void *Win32CallstackResolver::SendRecvPipeMessage(wstring message) { if(pdblocatePipe == NULL) return NULL; DWORD written = 0; DWORD msgLen = (DWORD)message.length()*sizeof(wchar_t); BOOL success = WriteFile(pdblocatePipe, message.c_str(), msgLen, &written, NULL); if(!success || written != msgLen) return NULL; byte *bufPtr = (byte *)pipeMessageBuf; DWORD bufSize = sizeof(pipeMessageBuf); do { DWORD read = 0; success = ReadFile(pdblocatePipe, bufPtr, bufSize, &read, NULL); RDCDEBUG("'%ls' -> %lu", message.c_str(), read); DWORD err = GetLastError(); if (!success && err != ERROR_MORE_DATA) { break; } bufPtr += read; bufSize -= read; } while (!success); RDCDEBUG("buf: %02x %02x %02x %02x", bufPtr[0], bufPtr[1], bufPtr[2], bufPtr[3]); return pipeMessageBuf; }
BOOL add_hooks() { wchar_t curFile[512]; GetModuleFileNameW(NULL, curFile, 512); wstring f = strlower(wstring(curFile)); // bail immediately if we're in a system process. We don't want to hook, log, anything - // this instance is being used for a shell extension. if(f.find(L"dllhost.exe") != wstring::npos || f.find(L"explorer.exe") != wstring::npos) { #ifndef _RELEASE OutputDebugStringA("Hosting renderdoc.dll in shell process\n"); #endif return TRUE; } if(f.find(L"renderdoccmd.exe") != wstring::npos || f.find(L"renderdocui.exe") != wstring::npos) { RDCDEBUG("Not creating hooks - in replay app"); RenderDoc::Inst().SetReplayApp(true); RenderDoc::Inst().Initialise(); return true; } RenderDoc::Inst().Initialise(); RDCLOG("Loading into %ls", curFile); const wchar_t *appFilter = NULL; //L"MyApplication"; if(appFilter != NULL) { if(wcsstr(curFile, appFilter) == 0) { RDCDEBUG("Not app I want. Exiting"); return TRUE; } } LibraryHooks::GetInstance().CreateHooks(); return TRUE; }
void WrappedID3D11DeviceContext::AttemptCapture() { m_State = WRITING_CAPFRAME; m_FailureReason = CaptureSucceeded; // deferred contexts are initially NOT successful unless empty. That's because we don't have the serialised // contents of whatever is in them up until now (could be anything). // Only after they have been through a Finish() and then in CAPFRAME mode are they considered // successful. if(GetType() == D3D11_DEVICE_CONTEXT_DEFERRED) { RDCDEBUG("Deferred Context %llu Attempting capture - initially %s, %s", GetResourceID(), m_SuccessfulCapture ? "successful" : "unsuccessful", m_EmptyCommandList ? "empty" : "non-empty"); m_SuccessfulCapture |= m_EmptyCommandList; if(m_SuccessfulCapture) m_FailureReason = CaptureSucceeded; else m_FailureReason = CaptureFailed_UncappedCmdlist; RDCDEBUG("Deferred Context %llu Attempting capture - now %s", GetResourceID(), m_SuccessfulCapture ? "successful" : "unsuccessful"); } else { RDCDEBUG("Immediate Context %llu Attempting capture", GetResourceID()); m_SuccessfulCapture = true; m_FailureReason = CaptureSucceeded; for(auto it=m_DeferredRecords.begin(); it != m_DeferredRecords.end(); ++it) (*it)->Delete(m_pDevice->GetResourceManager()); m_DeferredRecords.clear(); m_ContextRecord->LockChunks(); while(m_ContextRecord->HasChunks()) { Chunk *chunk = m_ContextRecord->GetLastChunk(); SAFE_DELETE(chunk); m_ContextRecord->PopChunk(); } m_ContextRecord->UnlockChunks(); m_ContextRecord->FreeParents(m_pDevice->GetResourceManager()); } }
bool ReplayRenderer::GetDrawcalls(uint32_t frameID, bool includeTimes, rdctype::array<FetchDrawcall> *draws) { if(frameID >= (uint32_t)m_FrameRecord.size() || draws == NULL) return false; if(includeTimes) { RDCDEBUG("Timing drawcalls..."); m_pDevice->TimeDrawcalls(m_FrameRecord[frameID].m_DrawCallList); } *draws = m_FrameRecord[frameID].m_DrawCallList; return true; }
void D3D12RenderState::ApplyGraphicsRootElements(ID3D12GraphicsCommandList *cmd) const { for(size_t i = 0; i < graphics.sigelems.size(); i++) { // just don't set tables that aren't in the descriptor heaps, since it's invalid and can crash // and is probably just from stale bindings that aren't going to be used if(graphics.sigelems[i].type != eRootTable || std::find(heaps.begin(), heaps.end(), graphics.sigelems[i].id) != heaps.end()) { graphics.sigelems[i].SetToGraphics(GetResourceManager(), cmd, (UINT)i); } else { RDCDEBUG("Skipping setting possibly stale graphics root table referring to heap %llu", graphics.sigelems[i].id); } } }
// DllMain equivalent void library_loaded() { string curfile; FileIO::GetExecutableFilename(curfile); if(curfile.find("/renderdoccmd") != string::npos || curfile.find("/renderdocui") != string::npos || curfile.find("/qrenderdoc") != string::npos) { RDCDEBUG("Not creating hooks - in replay app"); RenderDoc::Inst().SetReplayApp(true); RenderDoc::Inst().Initialise(); return; } else { RenderDoc::Inst().Initialise(); char *logfile = getenv("RENDERDOC_LOGFILE"); char *opts = getenv("RENDERDOC_CAPTUREOPTS"); if(opts) { string optstr = opts; CaptureOptions optstruct; readCapOpts(optstr.c_str(), &optstruct); RenderDoc::Inst().SetCaptureOptions(optstruct); } if(logfile) { RenderDoc::Inst().SetLogFile(logfile); } RDCLOG("Loading into %s", curfile.c_str()); LibraryHooks::GetInstance().CreateHooks(); } }
static BOOL add_hooks() { wchar_t curFile[512]; GetModuleFileNameW(NULL, curFile, 512); wstring f = strlower(wstring(curFile)); // bail immediately if we're in a system process. We don't want to hook, log, anything - // this instance is being used for a shell extension. if(f.find(L"dllhost.exe") != wstring::npos || f.find(L"explorer.exe") != wstring::npos) { #ifndef _RELEASE OutputDebugStringA("Hosting " STRINGIZE(RDOC_DLL_FILE) ".dll in shell process\n"); #endif return TRUE; } if(f.find(CONCAT(L, STRINGIZE(RDOC_DLL_FILE)) L"cmd.exe") != wstring::npos || f.find(CONCAT(L, STRINGIZE(RDOC_DLL_FILE)) L"ui.vshost.exe") != wstring::npos || f.find(L"q" CONCAT(L, STRINGIZE(RDOC_DLL_FILE)) L".exe") != wstring::npos || f.find(CONCAT(L, STRINGIZE(RDOC_DLL_FILE)) L"ui.exe") != wstring::npos) { RDCDEBUG("Not creating hooks - in replay app"); RenderDoc::Inst().SetReplayApp(true); RenderDoc::Inst().Initialise(); return true; } RenderDoc::Inst().Initialise(); RDCLOG("Loading into %ls", curFile); LibraryHooks::GetInstance().CreateHooks(); return TRUE; }
static BOOL add_hooks() { wchar_t curFile[512]; GetModuleFileNameW(NULL, curFile, 512); wstring f = strlower(wstring(curFile)); // bail immediately if we're in a system process. We don't want to hook, log, anything - // this instance is being used for a shell extension. if(f.find(L"dllhost.exe") != wstring::npos || f.find(L"explorer.exe") != wstring::npos) { #ifndef _RELEASE OutputDebugStringA("Hosting " STRINGIZE(RDOC_DLL_FILE) ".dll in shell process\n"); #endif return TRUE; } // search for an exported symbol with this name, typically renderdoc__replay__marker if(HOOKS_IDENTIFY(STRINGIZE(RDOC_DLL_FILE) "__replay__marker")) { RDCDEBUG("Not creating hooks - in replay app"); RenderDoc::Inst().SetReplayApp(true); RenderDoc::Inst().Initialise(); return true; } RenderDoc::Inst().Initialise(); RDCLOG("Loading into %ls", curFile); LibraryHooks::GetInstance().CreateHooks(); return TRUE; }
void *dlopen(const char *filename, int flag) { SCOPED_LOCK(libLock); if(realdlopen == NULL) realdlopen = (DLOPENPROC)dlsym(RTLD_NEXT, "dlopen"); void *ret = realdlopen(filename, flag); if(filename && ret) { for(auto it = libraryHooks.begin(); it != libraryHooks.end(); ++it) { if(strstr(filename, it->first.c_str())) { RDCDEBUG("Redirecting dlopen to ourselves for %s", filename); it->second(ret); ret = realdlopen("librenderdoc.so", flag); } } } return ret; }
GLXContext glXCreateContextAttribsARB(Display *dpy, GLXFBConfig config, GLXContext shareList, Bool direct, const int *attribList) { const int *attribs = attribList; vector<int> attribVec; // modify attribs to our liking { bool flagsNext = false; bool flagsFound = false; const int *a = attribList; while(*a) { int val = *a; if(flagsNext) { flagsNext = false; if(RenderDoc::Inst().GetCaptureOptions().DebugDeviceMode) val |= GLX_CONTEXT_DEBUG_BIT_ARB; else val &= ~GLX_CONTEXT_DEBUG_BIT_ARB; // remove NO_ERROR bit val &= ~GL_CONTEXT_FLAG_NO_ERROR_BIT_KHR; } if(val == GLX_CONTEXT_FLAGS_ARB) { flagsNext = true; flagsFound = true; } attribVec.push_back(val); a++; } if(!flagsFound && RenderDoc::Inst().GetCaptureOptions().DebugDeviceMode) { attribVec.push_back(GLX_CONTEXT_FLAGS_ARB); attribVec.push_back(GLX_CONTEXT_DEBUG_BIT_ARB); } attribVec.push_back(0); attribs = &attribVec[0]; } RDCDEBUG("glXCreateContextAttribsARB:"); bool core = false; int *a = (int *)attribs; while(*a) { RDCDEBUG("%x: %d", a[0], a[1]); a += 2; if(a[0] == GLX_CONTEXT_PROFILE_MASK_ARB) core = (a[1] & GLX_CONTEXT_CORE_PROFILE_BIT_ARB); } GLXContext ret = OpenGLHook::glhooks.glXCreateContextAttribsARB_real(dpy, config, shareList, direct, attribs); XVisualInfo *vis = OpenGLHook::glhooks.glXGetVisualFromFBConfig_real(dpy, config); GLInitParams init; init.width = 0; init.height = 0; int value = 0; Keyboard::CloneDisplay(dpy); OpenGLHook::glhooks.glXGetConfig_real(dpy, vis, GLX_BUFFER_SIZE, &value); init.colorBits = value; OpenGLHook::glhooks.glXGetConfig_real(dpy, vis, GLX_DEPTH_SIZE, &value); init.depthBits = value; OpenGLHook::glhooks.glXGetConfig_real(dpy, vis, GLX_STENCIL_SIZE, &value); init.stencilBits = value; value = 1; // default to srgb OpenGLHook::glhooks.glXGetConfig_real(dpy, vis, GLX_FRAMEBUFFER_SRGB_CAPABLE_ARB, &value); init.isSRGB = value; value = 1; OpenGLHook::glhooks.glXGetConfig_real(dpy, vis, GLX_SAMPLES_ARB, &value); init.isSRGB = RDCMAX(1, value); XFree(vis); GLWindowingData data; data.dpy = dpy; data.wnd = (GLXDrawable)NULL; data.ctx = ret; OpenGLHook::glhooks.GetDriver()->CreateContext(data, shareList, init, core, true); return ret; }
MemoryAllocation WrappedVulkan::AllocateMemoryForResource(bool buffer, VkMemoryRequirements mrq, MemoryScope scope, MemoryType type) { MemoryAllocation ret; ret.scope = scope; ret.type = type; ret.buffer = buffer; ret.size = AlignUp(mrq.size, mrq.alignment); RDCDEBUG("Allocating 0x%llx with alignment 0x%llx in 0x%x for a %s (%s in %s)", ret.size, mrq.alignment, mrq.memoryTypeBits, buffer ? "buffer" : "image", ToStr(type).c_str(), ToStr(scope).c_str()); std::vector<MemoryAllocation> &blockList = m_MemoryBlocks[(size_t)scope]; // first try to find a match int i = 0; for(MemoryAllocation &block : blockList) { RDCDEBUG( "Considering block %d: memory type %u and type %s. Total size 0x%llx, current offset " "0x%llx, last alloc was %s", i, block.memoryTypeIndex, ToStr(block.type).c_str(), block.size, block.offs, block.buffer ? "buffer" : "image"); i++; // skip this block if it's not the memory type we want if(ret.type != block.type || (mrq.memoryTypeBits & (1 << block.memoryTypeIndex)) == 0) { RDCDEBUG("block type %d or memory type %d is incompatible", block.type, block.memoryTypeIndex); continue; } // offs is where we can put our next sub-allocation VkDeviceSize offs = block.offs; // if we are on a buffer/image, account for any alignment we might have to do if(ret.buffer != block.buffer) offs = AlignUp(offs, m_PhysicalDeviceData.props.limits.bufferImageGranularity); // align as required by the resource offs = AlignUp(offs, mrq.alignment); if(offs > block.size) { RDCDEBUG("Next offset 0x%llx would be off the end of the memory (size 0x%llx).", offs, block.size); continue; } VkDeviceSize avail = block.size - offs; RDCDEBUG("At next offset 0x%llx, there's 0x%llx bytes available for 0x%llx bytes requested", offs, avail, ret.size); // if the allocation will fit, we've found our candidate. if(ret.size <= avail) { // update the block offset and buffer/image bit block.offs = offs + ret.size; block.buffer = ret.buffer; // update our return value ret.offs = offs; ret.mem = block.mem; RDCDEBUG("Allocating using this block: 0x%llx -> 0x%llx", ret.offs, block.offs); // stop searching break; } } if(ret.mem == VK_NULL_HANDLE) { RDCDEBUG("No available block found - allocating new block"); VkDeviceSize &allocSize = m_MemoryBlockSize[(size_t)scope]; // we start allocating 32M, then increment each time we need a new block. switch(allocSize) { case 0: allocSize = 32; break; case 32: allocSize = 64; break; case 64: allocSize = 128; break; case 128: case 256: allocSize = 256; break; default: RDCDEBUG("Unexpected previous allocation size 0x%llx bytes, allocating 256MB", allocSize); allocSize = 256; break; } uint32_t memoryTypeIndex = 0; switch(ret.type) { case MemoryType::Upload: memoryTypeIndex = GetUploadMemoryIndex(mrq.memoryTypeBits); break; case MemoryType::GPULocal: memoryTypeIndex = GetGPULocalMemoryIndex(mrq.memoryTypeBits); break; case MemoryType::Readback: memoryTypeIndex = GetReadbackMemoryIndex(mrq.memoryTypeBits); break; } VkMemoryAllocateInfo info = { VK_STRUCTURE_TYPE_MEMORY_ALLOCATE_INFO, NULL, allocSize * 1024 * 1024, memoryTypeIndex, }; if(ret.size > info.allocationSize) { // if we get an over-sized allocation, first try to immediately jump to the largest block // size. allocSize = 256; info.allocationSize = allocSize * 1024 * 1024; // if it's still over-sized, just allocate precisely enough and give it a dedicated allocation if(ret.size > info.allocationSize) { RDCDEBUG("Over-sized allocation for 0x%llx bytes", ret.size); info.allocationSize = ret.size; } } RDCDEBUG("Creating new allocation of 0x%llx bytes", info.allocationSize); MemoryAllocation chunk; chunk.buffer = ret.buffer; chunk.memoryTypeIndex = memoryTypeIndex; chunk.scope = scope; chunk.type = type; chunk.size = info.allocationSize; // the offset starts immediately after this allocation chunk.offs = ret.size; VkDevice d = GetDev(); // do the actual allocation VkResult vkr = ObjDisp(d)->AllocateMemory(Unwrap(d), &info, NULL, &chunk.mem); RDCASSERTEQUAL(vkr, VK_SUCCESS); GetResourceManager()->WrapResource(Unwrap(d), chunk.mem); // push the new chunk blockList.push_back(chunk); // return the first bytes in the new chunk ret.offs = 0; ret.mem = chunk.mem; } return ret; }
void WrappedID3D11DeviceContext::ReplayLog(LogState readType, uint32_t startEventID, uint32_t endEventID, bool partial) { m_State = readType; m_DoStateVerify = true; D3D11ChunkType header = (D3D11ChunkType)m_pSerialiser->PushContext(NULL, 1, false); RDCASSERT(header == CONTEXT_CAPTURE_HEADER); ResourceId id; m_pSerialiser->Serialise("context", id); WrappedID3D11DeviceContext *context = (WrappedID3D11DeviceContext *)m_pDevice->GetResourceManager()->GetLiveResource(id); RDCASSERT(WrappedID3D11DeviceContext::IsAlloc(context) && 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) { ClearMaps(); for(size_t i=0; i < m_pDevice->GetNumDeferredContexts(); i++) { WrappedID3D11DeviceContext *defcontext = m_pDevice->GetDeferredContext(i); defcontext->ClearMaps(); } } m_pDevice->GetResourceManager()->MarkInFrame(true); uint64_t startOffset = m_pSerialiser->GetOffset(); 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(); D3D11ChunkType chunktype = (D3D11ChunkType)m_pSerialiser->PushContext(NULL, 1, false); ProcessChunk(offset, chunktype, false); RenderDoc::Inst().SetProgress(FrameEventsRead, float(offset - startOffset)/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(chunktype == CONTEXT_CAPTURE_FOOTER) break; m_CurEventID++; } if(m_State == READING) { m_pDevice->GetFrameRecord().back().drawcallList = m_ParentDrawcall.Bake(); m_pDevice->GetFrameRecord().back().frameInfo.debugMessages = m_pDevice->GetDebugMessages(); int initialSkips = 0; for(auto it=WrappedID3D11Buffer::m_BufferList.begin(); it != WrappedID3D11Buffer::m_BufferList.end(); ++it) m_ResourceUses[it->first]; for(auto it=WrappedID3D11Texture1D::m_TextureList.begin(); it != WrappedID3D11Texture1D::m_TextureList.end(); ++it) m_ResourceUses[it->first]; for(auto it=WrappedID3D11Texture2D::m_TextureList.begin(); it != WrappedID3D11Texture2D::m_TextureList.end(); ++it) m_ResourceUses[it->first]; for(auto it=WrappedID3D11Texture3D::m_TextureList.begin(); it != WrappedID3D11Texture3D::m_TextureList.end(); ++it) m_ResourceUses[it->first]; // it's easier to remove duplicate usages here than check it as we go. // this means if textures are bound in multiple places in the same draw // we don't have duplicate uses for(auto it = m_ResourceUses.begin(); it != m_ResourceUses.end(); ++it) { vector<EventUsage> &v = it->second; std::sort(v.begin(), v.end()); v.erase( std::unique(v.begin(), v.end()), v.end() ); #if 0 ResourceId resid = m_pDevice->GetResourceManager()->GetOriginalID(it->first); if(m_pDevice->GetResourceManager()->GetInitialContents(resid).resource == NULL) continue; // code disabled for now as skipping these initial states // doesn't seem to produce any measurable improvement in any case // I've checked RDCDEBUG("Resource %llu", resid); if(v.empty()) { RDCDEBUG("Never used!"); initialSkips++; } else { bool written = false; for(auto usit = v.begin(); usit != v.end(); ++usit) { ResourceUsage u = usit->usage; if(u == eUsage_SO || (u >= eUsage_VS_RWResource && u <= eUsage_CS_RWResource) || u == eUsage_DepthStencilTarget || u == eUsage_ColourTarget) { written = true; break; } } if(written) { RDCDEBUG("Written in frame - needs initial state"); } else { RDCDEBUG("Never written to in the frame"); initialSkips++; } } #endif } //RDCDEBUG("Can skip %d initial states.", initialSkips); } m_pDevice->GetResourceManager()->MarkInFrame(false); m_State = READING; m_DoStateVerify = false; }
ReplayCreateStatus D3D12_CreateReplayDevice(const char *logfile, IReplayDriver **driver) { RDCDEBUG("Creating a D3D12 replay device"); WrappedIDXGISwapChain3::RegisterD3DDeviceCallback(GetD3D12DeviceIfAlloc); HMODULE lib = NULL; lib = LoadLibraryA("d3d12.dll"); if(lib == NULL) { RDCERR("Failed to load d3d12.dll"); return eReplayCreate_APIInitFailed; } lib = LoadLibraryA("dxgi.dll"); if(lib == NULL) { RDCERR("Failed to load dxgi.dll"); return eReplayCreate_APIInitFailed; } if(GetD3DCompiler() == NULL) { RDCERR("Failed to load d3dcompiler_??.dll"); return eReplayCreate_APIInitFailed; } D3D12InitParams initParams; RDCDriver driverFileType = RDC_D3D12; string driverName = "D3D12"; if(logfile) { auto status = RenderDoc::Inst().FillInitParams(logfile, driverFileType, driverName, (RDCInitParams *)&initParams); if(status != eReplayCreate_Success) return status; } // initParams.SerialiseVersion is guaranteed to be valid/supported since otherwise the // FillInitParams (which calls D3D12InitParams::Serialise) would have failed above, so no need to // check it here. if(initParams.MinimumFeatureLevel < D3D_FEATURE_LEVEL_11_0) initParams.MinimumFeatureLevel = D3D_FEATURE_LEVEL_11_0; ID3D12Device *dev = NULL; HRESULT hr = RENDERDOC_CreateWrappedD3D12Device(NULL, initParams.MinimumFeatureLevel, __uuidof(ID3D12Device), (void **)&dev); if(FAILED(hr)) { RDCERR("Couldn't create a d3d12 device :(."); return eReplayCreate_APIHardwareUnsupported; } WrappedID3D12Device *wrappedDev = (WrappedID3D12Device *)dev; if(logfile) wrappedDev->SetLogFile(logfile); wrappedDev->SetLogVersion(initParams.SerialiseVersion); RDCLOG("Created device."); D3D12Replay *replay = wrappedDev->GetReplay(); replay->SetProxy(logfile == NULL); *driver = (IReplayDriver *)replay; return eReplayCreate_Success; }
bool WrappedID3D12CommandQueue::Serialise_ExecuteCommandLists(UINT NumCommandLists, ID3D12CommandList *const *ppCommandLists) { SERIALISE_ELEMENT(UINT, numCmds, NumCommandLists); vector<ResourceId> cmdIds; ID3D12CommandList **cmds = m_State >= WRITING ? NULL : new ID3D12CommandList *[numCmds]; if(m_State >= WRITING) { for(UINT i = 0; i < numCmds; i++) { D3D12ResourceRecord *record = GetRecord(ppCommandLists[i]); RDCASSERT(record->bakedCommands); if(record->bakedCommands) cmdIds.push_back(record->bakedCommands->GetResourceID()); } } m_pSerialiser->Serialise("ppCommandLists", cmdIds); if(m_State < WRITING) { for(UINT i = 0; i < numCmds; i++) { cmds[i] = cmdIds[i] != ResourceId() ? Unwrap(GetResourceManager()->GetLiveAs<ID3D12CommandList>(cmdIds[i])) : NULL; } } const string desc = m_pSerialiser->GetDebugStr(); D3D12NOTIMP("Serialise_DebugMessages"); if(m_State == READING) { m_pReal->ExecuteCommandLists(numCmds, cmds); for(uint32_t i = 0; i < numCmds; i++) { ResourceId cmd = GetResourceManager()->GetLiveID(cmdIds[i]); m_pDevice->ApplyBarriers(m_Cmd.m_BakedCmdListInfo[cmd].barriers); } m_Cmd.AddEvent(EXECUTE_CMD_LISTS, desc); // we're adding multiple events, need to increment ourselves m_Cmd.m_RootEventID++; string basename = "ExecuteCommandLists(" + ToStr::Get(numCmds) + ")"; for(uint32_t c = 0; c < numCmds; c++) { string name = StringFormat::Fmt("=> %s[%u]: ID3D12CommandList(%s)", basename.c_str(), c, ToStr::Get(cmdIds[c]).c_str()); // add a fake marker FetchDrawcall draw; draw.name = name; draw.flags |= eDraw_SetMarker; m_Cmd.AddEvent(SET_MARKER, name); m_Cmd.AddDrawcall(draw, true); m_Cmd.m_RootEventID++; BakedCmdListInfo &cmdBufInfo = m_Cmd.m_BakedCmdListInfo[cmdIds[c]]; // insert the baked command list in-line into this list of notes, assigning new event and // drawIDs m_Cmd.InsertDrawsAndRefreshIDs(cmdBufInfo.draw->children); for(size_t e = 0; e < cmdBufInfo.draw->executedCmds.size(); e++) { vector<uint32_t> &submits = m_Cmd.m_Partial[D3D12CommandData::Secondary].cmdListExecs[cmdBufInfo.draw->executedCmds[e]]; for(size_t s = 0; s < submits.size(); s++) submits[s] += m_Cmd.m_RootEventID; } D3D12NOTIMP("Debug Messages"); /* for(size_t i = 0; i < cmdBufInfo.debugMessages.size(); i++) { m_DebugMessages.push_back(cmdBufInfo.debugMessages[i]); m_DebugMessages.back().eventID += m_RootEventID; } */ // only primary command lists can be submitted m_Cmd.m_Partial[D3D12CommandData::Primary].cmdListExecs[cmdIds[c]].push_back( m_Cmd.m_RootEventID); m_Cmd.m_RootEventID += cmdBufInfo.eventCount; m_Cmd.m_RootDrawcallID += cmdBufInfo.drawCount; name = StringFormat::Fmt("=> %s[%u]: Close(%s)", basename.c_str(), c, ToStr::Get(cmdIds[c]).c_str()); draw.name = name; m_Cmd.AddEvent(SET_MARKER, name); m_Cmd.AddDrawcall(draw, true); m_Cmd.m_RootEventID++; } // account for the outer loop thinking we've added one event and incrementing, // since we've done all the handling ourselves this will be off by one. m_Cmd.m_RootEventID--; } else if(m_State == EXECUTING) { // account for the queue submit event m_Cmd.m_RootEventID++; uint32_t startEID = m_Cmd.m_RootEventID; // advance m_CurEventID to match the events added when reading for(uint32_t c = 0; c < numCmds; c++) { // 2 extra for the virtual labels around the command list m_Cmd.m_RootEventID += 2 + m_Cmd.m_BakedCmdListInfo[cmdIds[c]].eventCount; m_Cmd.m_RootDrawcallID += 2 + m_Cmd.m_BakedCmdListInfo[cmdIds[c]].drawCount; } // same accounting for the outer loop as above m_Cmd.m_RootEventID--; if(numCmds == 0) { // do nothing, don't bother with the logic below } else if(m_Cmd.m_LastEventID <= startEID) { #ifdef VERBOSE_PARTIAL_REPLAY RDCDEBUG("Queue Submit no replay %u == %u", m_Cmd.m_LastEventID, startEID); #endif } else if(m_Cmd.m_DrawcallCallback && m_Cmd.m_DrawcallCallback->RecordAllCmds()) { #ifdef VERBOSE_PARTIAL_REPLAY RDCDEBUG("Queue Submit re-recording from %u", m_Cmd.m_RootEventID); #endif vector<ID3D12CommandList *> rerecordedCmds; for(uint32_t c = 0; c < numCmds; c++) { ID3D12CommandList *cmd = m_Cmd.RerecordCmdList(cmdIds[c]); ResourceId rerecord = GetResID(cmd); #ifdef VERBOSE_PARTIAL_REPLAY RDCDEBUG("Queue Submit fully re-recorded replay of %llu, using %llu", cmdIds[c], rerecord); #endif rerecordedCmds.push_back(Unwrap(cmd)); m_pDevice->ApplyBarriers(m_Cmd.m_BakedCmdListInfo[rerecord].barriers); } m_pReal->ExecuteCommandLists((UINT)rerecordedCmds.size(), &rerecordedCmds[0]); } else if(m_Cmd.m_LastEventID > startEID && m_Cmd.m_LastEventID < m_Cmd.m_RootEventID) { #ifdef VERBOSE_PARTIAL_REPLAY RDCDEBUG("Queue Submit partial replay %u < %u", m_Cmd.m_LastEventID, m_Cmd.m_RootEventID); #endif uint32_t eid = startEID; vector<ResourceId> trimmedCmdIds; vector<ID3D12CommandList *> trimmedCmds; for(uint32_t c = 0; c < numCmds; c++) { // account for the virtual label at the start of the events here // so it matches up to baseEvent eid++; uint32_t end = eid + m_Cmd.m_BakedCmdListInfo[cmdIds[c]].eventCount; if(eid == m_Cmd.m_Partial[D3D12CommandData::Primary].baseEvent) { ID3D12GraphicsCommandList *list = m_Cmd.RerecordCmdList(cmdIds[c], D3D12CommandData::Primary); ResourceId partial = GetResID(list); #ifdef VERBOSE_PARTIAL_REPLAY RDCDEBUG("Queue Submit partial replay of %llu at %u, using %llu", cmdIds[c], eid, partial); #endif trimmedCmdIds.push_back(partial); trimmedCmds.push_back(Unwrap(list)); } else if(m_Cmd.m_LastEventID >= end) { #ifdef VERBOSE_PARTIAL_REPLAY RDCDEBUG("Queue Submit full replay %llu", cmdIds[c]); #endif trimmedCmdIds.push_back(cmdIds[c]); trimmedCmds.push_back(Unwrap(GetResourceManager()->GetLiveAs<ID3D12CommandList>(cmdIds[c]))); } else { #ifdef VERBOSE_PARTIAL_REPLAY RDCDEBUG("Queue not submitting %llu", cmdIds[c]); #endif } // 1 extra to account for the virtual end command list label (begin is accounted for // above) eid += 1 + m_Cmd.m_BakedCmdListInfo[cmdIds[c]].eventCount; } RDCASSERT(trimmedCmds.size() > 0); m_pReal->ExecuteCommandLists((UINT)trimmedCmds.size(), &trimmedCmds[0]); for(uint32_t i = 0; i < trimmedCmdIds.size(); i++) { ResourceId cmd = trimmedCmdIds[i]; m_pDevice->ApplyBarriers(m_Cmd.m_BakedCmdListInfo[cmd].barriers); } } else { #ifdef VERBOSE_PARTIAL_REPLAY RDCDEBUG("Queue Submit full replay %u >= %u", m_Cmd.m_LastEventID, m_Cmd.m_RootEventID); #endif m_pReal->ExecuteCommandLists(numCmds, cmds); for(uint32_t i = 0; i < numCmds; i++) { ResourceId cmd = GetResourceManager()->GetLiveID(cmdIds[i]); m_pDevice->ApplyBarriers(m_Cmd.m_BakedCmdListInfo[cmd].barriers); } } } SAFE_DELETE_ARRAY(cmds); return true; }
void ReceiveMessage(RemoteMessage *msg) { if(m_Socket == NULL) { msg->Type = eRemoteMsg_Disconnected; return; } if(!m_Socket->IsRecvDataWaiting()) { if(!m_Socket->Connected()) { SAFE_DELETE(m_Socket); msg->Type = eRemoteMsg_Disconnected; } else { Threading::Sleep(2); msg->Type = eRemoteMsg_Noop; } return; } PacketType type; Serialiser *ser = NULL; GetPacket(type, ser); if(m_Socket == NULL) { SAFE_DELETE(ser); msg->Type = eRemoteMsg_Disconnected; return; } else { if(type == ePacket_Noop) { SAFE_DELETE(ser); RDCDEBUG("Got a no-op"); msg->Type = eRemoteMsg_Noop; return; } else if(type == ePacket_Busy) { wstring existingClient; ser->Serialise("", existingClient); SAFE_DELETE(ser); SAFE_DELETE(m_Socket); RDCLOG("Got busy signal: '%ls", existingClient.c_str()); msg->Type = eRemoteMsg_Busy; msg->Busy.ClientName = existingClient; return; } else if(type == ePacket_CopyCapture) { msg->Type = eRemoteMsg_CaptureCopied; ser->Serialise("", msg->NewCapture.ID); SAFE_DELETE(ser); msg->NewCapture.localpath = m_CaptureCopies[msg->NewCapture.ID]; if(!RecvChunkedFile(m_Socket, ePacket_CopyCapture, msg->NewCapture.localpath.elems, ser, NULL)) { SAFE_DELETE(ser); SAFE_DELETE(m_Socket); msg->Type = eRemoteMsg_Disconnected; return; } m_CaptureCopies.erase(msg->NewCapture.ID); SAFE_DELETE(ser); return; } else if(type == ePacket_NewCapture) { msg->Type = eRemoteMsg_NewCapture; ser->Serialise("", msg->NewCapture.ID); ser->Serialise("", msg->NewCapture.timestamp); wstring path; ser->Serialise("", path); msg->NewCapture.localpath = path; if(!m_Local) msg->NewCapture.localpath = L""; uint32_t thumblen = 0; ser->Serialise("", thumblen); create_array_uninit(msg->NewCapture.thumbnail, thumblen); size_t l = 0; byte *buf = &msg->NewCapture.thumbnail[0]; ser->SerialiseBuffer("", buf, l); RDCLOG("Got a new capture: %d (time %llu) %d byte thumbnail", msg->NewCapture.ID, msg->NewCapture.timestamp, thumblen); SAFE_DELETE(ser); return; } else if(type == ePacket_RegisterAPI) { msg->Type = eRemoteMsg_RegisterAPI; ser->Serialise("", m_API); msg->RegisterAPI.APIName = m_API; RDCLOG("Used API: %ls", m_API.c_str()); SAFE_DELETE(ser); return; } } SAFE_DELETE(ser); msg->Type = eRemoteMsg_Noop; }
bool WrappedVulkan::Serialise_vkCreateDevice( Serialiser* localSerialiser, VkPhysicalDevice physicalDevice, const VkDeviceCreateInfo* pCreateInfo, const VkAllocationCallbacks* pAllocator, VkDevice* pDevice) { SERIALISE_ELEMENT(ResourceId, physId, GetResID(physicalDevice)); SERIALISE_ELEMENT(VkDeviceCreateInfo, serCreateInfo, *pCreateInfo); SERIALISE_ELEMENT(ResourceId, devId, GetResID(*pDevice)); if(m_State == READING) { // we must make any modifications locally, so the free of pointers // in the serialised VkDeviceCreateInfo don't double-free VkDeviceCreateInfo createInfo = serCreateInfo; std::vector<string> Extensions; for(uint32_t i=0; i < createInfo.enabledExtensionCount; i++) { // don't include the debug marker extension if(strcmp(createInfo.ppEnabledExtensionNames[i], VK_EXT_DEBUG_MARKER_EXTENSION_NAME)) Extensions.push_back(createInfo.ppEnabledExtensionNames[i]); } std::vector<string> Layers; for(uint32_t i=0; i < createInfo.enabledLayerCount; i++) Layers.push_back(createInfo.ppEnabledLayerNames[i]); StripUnwantedLayers(Layers); AddRequiredExtensions(false, Extensions); #if defined(FORCE_VALIDATION_LAYERS) Layers.push_back("VK_LAYER_LUNARG_standard_validation"); #endif createInfo.enabledLayerCount = (uint32_t)Layers.size(); const char **layerArray = NULL; if(!Layers.empty()) { layerArray = new const char *[createInfo.enabledLayerCount]; for(uint32_t i=0; i < createInfo.enabledLayerCount; i++) layerArray[i] = Layers[i].c_str(); createInfo.ppEnabledLayerNames = layerArray; } createInfo.enabledExtensionCount = (uint32_t)Extensions.size(); const char **extArray = NULL; if(!Extensions.empty()) { extArray = new const char *[createInfo.enabledExtensionCount]; for(uint32_t i=0; i < createInfo.enabledExtensionCount; i++) extArray[i] = Extensions[i].c_str(); createInfo.ppEnabledExtensionNames = extArray; } physicalDevice = GetResourceManager()->GetLiveHandle<VkPhysicalDevice>(physId); VkDevice device; uint32_t qCount = 0; ObjDisp(physicalDevice)->GetPhysicalDeviceQueueFamilyProperties(Unwrap(physicalDevice), &qCount, NULL); VkQueueFamilyProperties *props = new VkQueueFamilyProperties[qCount]; ObjDisp(physicalDevice)->GetPhysicalDeviceQueueFamilyProperties(Unwrap(physicalDevice), &qCount, props); bool found = false; uint32_t qFamilyIdx = 0; VkQueueFlags search = (VK_QUEUE_GRAPHICS_BIT); // for queue priorities, if we need it float one = 1.0f; // if we need to change the requested queues, it will point to this VkDeviceQueueCreateInfo *modQueues = NULL; for(uint32_t i=0; i < createInfo.queueCreateInfoCount; i++) { uint32_t idx = createInfo.pQueueCreateInfos[i].queueFamilyIndex; RDCASSERT(idx < qCount); // this requested queue is one we can use too if((props[idx].queueFlags & search) == search && createInfo.pQueueCreateInfos[i].queueCount > 0) { qFamilyIdx = idx; found = true; break; } } // if we didn't find it, search for which queue family we should add a request for if(!found) { RDCDEBUG("App didn't request a queue family we can use - adding our own"); for(uint32_t i=0; i < qCount; i++) { if((props[i].queueFlags & search) == search) { qFamilyIdx = i; found = true; break; } } if(!found) { SAFE_DELETE_ARRAY(props); RDCERR("Can't add a queue with required properties for RenderDoc! Unsupported configuration"); } else { // we found the queue family, add it modQueues = new VkDeviceQueueCreateInfo[createInfo.queueCreateInfoCount + 1]; for(uint32_t i=0; i < createInfo.queueCreateInfoCount; i++) modQueues[i] = createInfo.pQueueCreateInfos[i]; modQueues[createInfo.queueCreateInfoCount].queueFamilyIndex = qFamilyIdx; modQueues[createInfo.queueCreateInfoCount].queueCount = 1; modQueues[createInfo.queueCreateInfoCount].pQueuePriorities = &one; createInfo.pQueueCreateInfos = modQueues; createInfo.queueCreateInfoCount++; } } SAFE_DELETE_ARRAY(props); VkPhysicalDeviceFeatures enabledFeatures = {0}; if(createInfo.pEnabledFeatures != NULL) enabledFeatures = *createInfo.pEnabledFeatures; createInfo.pEnabledFeatures = &enabledFeatures; VkPhysicalDeviceFeatures availFeatures = {0}; ObjDisp(physicalDevice)->GetPhysicalDeviceFeatures(Unwrap(physicalDevice), &availFeatures); if(availFeatures.fillModeNonSolid) enabledFeatures.fillModeNonSolid = true; else RDCWARN("fillModeNonSolid = false, wireframe overlay will be solid"); if(availFeatures.robustBufferAccess) enabledFeatures.robustBufferAccess = true; else RDCWARN("robustBufferAccess = false, out of bounds access due to bugs in application or RenderDoc may cause crashes"); if(availFeatures.vertexPipelineStoresAndAtomics) enabledFeatures.vertexPipelineStoresAndAtomics = true; else RDCWARN("vertexPipelineStoresAndAtomics = false, output mesh data will not be available"); uint32_t numExts = 0; VkResult vkr = ObjDisp(physicalDevice)->EnumerateDeviceExtensionProperties(Unwrap(physicalDevice), NULL, &numExts, NULL); RDCASSERTEQUAL(vkr, VK_SUCCESS); VkExtensionProperties *exts = new VkExtensionProperties[numExts]; vkr = ObjDisp(physicalDevice)->EnumerateDeviceExtensionProperties(Unwrap(physicalDevice), NULL, &numExts, exts); RDCASSERTEQUAL(vkr, VK_SUCCESS); for(uint32_t i=0; i < numExts; i++) RDCLOG("Ext %u: %s (%u)", i, exts[i].extensionName, exts[i].specVersion); SAFE_DELETE_ARRAY(exts); // PORTABILITY check that extensions and layers supported in capture (from createInfo) are supported in replay vkr = GetDeviceDispatchTable(NULL)->CreateDevice(Unwrap(physicalDevice), &createInfo, NULL, &device); RDCASSERTEQUAL(vkr, VK_SUCCESS); GetResourceManager()->WrapResource(device, device); GetResourceManager()->AddLiveResource(devId, device); InitDeviceReplayTables(Unwrap(device)); RDCASSERT(m_Device == VK_NULL_HANDLE); // MULTIDEVICE m_PhysicalDevice = physicalDevice; m_Device = device; m_QueueFamilyIdx = qFamilyIdx; if(m_InternalCmds.cmdpool == VK_NULL_HANDLE) { VkCommandPoolCreateInfo poolInfo = { VK_STRUCTURE_TYPE_COMMAND_POOL_CREATE_INFO, NULL, VK_COMMAND_POOL_CREATE_RESET_COMMAND_BUFFER_BIT, qFamilyIdx }; vkr = ObjDisp(device)->CreateCommandPool(Unwrap(device), &poolInfo, NULL, &m_InternalCmds.cmdpool); RDCASSERTEQUAL(vkr, VK_SUCCESS); GetResourceManager()->WrapResource(Unwrap(device), m_InternalCmds.cmdpool); } ObjDisp(physicalDevice)->GetPhysicalDeviceProperties(Unwrap(physicalDevice), &m_PhysicalDeviceData.props); ObjDisp(physicalDevice)->GetPhysicalDeviceMemoryProperties(Unwrap(physicalDevice), &m_PhysicalDeviceData.memProps); ObjDisp(physicalDevice)->GetPhysicalDeviceFeatures(Unwrap(physicalDevice), &m_PhysicalDeviceData.features); for(int i=VK_FORMAT_BEGIN_RANGE+1; i < VK_FORMAT_END_RANGE; i++) ObjDisp(physicalDevice)->GetPhysicalDeviceFormatProperties(Unwrap(physicalDevice), VkFormat(i), &m_PhysicalDeviceData.fmtprops[i]); m_PhysicalDeviceData.readbackMemIndex = m_PhysicalDeviceData.GetMemoryIndex(~0U, VK_MEMORY_PROPERTY_HOST_VISIBLE_BIT, 0); m_PhysicalDeviceData.uploadMemIndex = m_PhysicalDeviceData.GetMemoryIndex(~0U, VK_MEMORY_PROPERTY_HOST_VISIBLE_BIT, 0); m_PhysicalDeviceData.GPULocalMemIndex = m_PhysicalDeviceData.GetMemoryIndex(~0U, VK_MEMORY_PROPERTY_DEVICE_LOCAL_BIT, VK_MEMORY_PROPERTY_HOST_VISIBLE_BIT); for(size_t i=0; i < m_PhysicalDevices.size(); i++) { if(physicalDevice == m_PhysicalDevices[i]) { m_PhysicalDeviceData.memIdxMap = m_MemIdxMaps[i]; break; } } m_DebugManager = new VulkanDebugManager(this, device); SAFE_DELETE_ARRAY(modQueues); SAFE_DELETE_ARRAY(layerArray); SAFE_DELETE_ARRAY(extArray); } return true; }
ReplayCreateStatus GL_CreateReplayDevice(const wchar_t *logfile, IReplayDriver **driver) { RDCDEBUG("Creating an OpenGL replay device"); if(glXCreateContextAttribsProc == NULL) { glXGetFuncProc = (PFNGLXGETPROCADDRESSPROC)dlsym(RTLD_NEXT, "glXGetProcAddress"); glXDestroyCtxProc = (PFNGLXDESTROYCONTEXTPROC)dlsym(RTLD_NEXT, "glXDestroyContext"); glXSwapProc = (PFNGLXSWAPBUFFERSPROC)dlsym(RTLD_NEXT, "glXSwapBuffers"); glXChooseFBConfigProc = (PFNGLXCHOOSEFBCONFIGPROC)dlsym(RTLD_NEXT, "glXChooseFBConfig"); glXCreatePbufferProc = (PFNGLXCREATEPBUFFERPROC)dlsym(RTLD_NEXT, "glXCreatePbuffer"); glXDestroyPbufferProc = (PFNGLXDESTROYPBUFFERPROC)dlsym(RTLD_NEXT, "glXDestroyPbuffer"); glXQueryDrawableProc = (PFNGLXQUERYDRAWABLEPROC)dlsym(RTLD_NEXT, "glXQueryDrawable"); if(glXGetFuncProc == NULL || glXDestroyCtxProc == NULL || glXSwapProc == NULL || glXChooseFBConfigProc == NULL || glXCreatePbufferProc == NULL || glXDestroyPbufferProc == NULL || glXQueryDrawableProc == NULL) { RDCERR("Couldn't find required entry points, glXGetProcAddress glXDestroyContext glXSwapBuffers"); return eReplayCreate_APIInitFailed; } glXCreateContextAttribsProc = (PFNGLXCREATECONTEXTATTRIBSARBPROC)glXGetFuncProc((const GLubyte*)"glXCreateContextAttribsARB"); glXMakeContextCurrentProc = (PFNGLXMAKECONTEXTCURRENTPROC)glXGetFuncProc((const GLubyte*)"glXMakeContextCurrent"); if(glXCreateContextAttribsProc == NULL || glXMakeContextCurrentProc == NULL) { RDCERR("Couldn't get glx function addresses, glXCreateContextAttribsARB glXMakeContextCurrent"); return eReplayCreate_APIInitFailed; } } GLInitParams initParams; RDCDriver driverType = RDC_OpenGL; wstring driverName = L"OpenGL"; if(logfile) RenderDoc::Inst().FillInitParams(logfile, driverType, driverName, (RDCInitParams *)&initParams); if(initParams.SerialiseVersion != GLInitParams::GL_SERIALISE_VERSION) { RDCERR("Incompatible OpenGL serialise version, expected %d got %d", GLInitParams::GL_SERIALISE_VERSION, initParams.SerialiseVersion); return eReplayCreate_APIIncompatibleVersion; } int attribs[64] = {0}; int i=0; attribs[i++] = GLX_CONTEXT_MAJOR_VERSION_ARB; attribs[i++] = 4; attribs[i++] = GLX_CONTEXT_MINOR_VERSION_ARB; attribs[i++] = 3; attribs[i++] = GLX_CONTEXT_FLAGS_ARB; attribs[i++] = GLX_CONTEXT_DEBUG_BIT_ARB; Display *dpy = XOpenDisplay(NULL); if(dpy == NULL) { RDCERR("Couldn't open default X display"); return eReplayCreate_APIInitFailed; } // don't need to care about the fb config as we won't be using the default framebuffer (backbuffer) static int visAttribs[] = { 0 }; int numCfgs = 0; GLXFBConfig *fbcfg = glXChooseFBConfigProc(dpy, DefaultScreen(dpy), visAttribs, &numCfgs); if(fbcfg == NULL) { XCloseDisplay(dpy); RDCERR("Couldn't choose default framebuffer config"); return eReplayCreate_APIInitFailed; } GLXContext ctx = glXCreateContextAttribsProc(dpy, fbcfg[0], 0, true, attribs); if(ctx == NULL) { XCloseDisplay(dpy); RDCERR("Couldn't create 4.3 context - RenderDoc requires OpenGL 4.3 availability"); return eReplayCreate_APIHardwareUnsupported; } // don't care about pbuffer properties for same reason as backbuffer int pbAttribs[] = { GLX_PBUFFER_WIDTH, 32, GLX_PBUFFER_HEIGHT, 32, 0 }; GLXPbuffer pbuffer = glXCreatePbufferProc(dpy, fbcfg[0], pbAttribs); XFree(fbcfg); Bool res = glXMakeContextCurrentProc(dpy, pbuffer, pbuffer, ctx); if(!res) { glXDestroyPbufferProc(dpy, pbuffer); glXDestroyCtxProc(dpy, ctx); XCloseDisplay(dpy); RDCERR("Couldn't make pbuffer & context current"); return eReplayCreate_APIInitFailed; } WrappedOpenGL *gl = new WrappedOpenGL(logfile, GetRealFunctions()); gl->Initialise(initParams); RDCLOG("Created device."); GLReplay *replay = gl->GetReplay(); replay->SetProxy(logfile == NULL); GLWindowingData data; data.dpy = dpy; data.ctx = ctx; data.wnd = pbuffer; replay->SetReplayData(data); *driver = (IReplayDriver *)replay; return eReplayCreate_Success; }
bool WrappedVulkan::Serialise_vkQueueSubmit(Serialiser *localSerialiser, VkQueue queue, uint32_t submitCount, const VkSubmitInfo *pSubmits, VkFence fence) { SERIALISE_ELEMENT(ResourceId, queueId, GetResID(queue)); SERIALISE_ELEMENT(ResourceId, fenceId, fence != VK_NULL_HANDLE ? GetResID(fence) : ResourceId()); SERIALISE_ELEMENT(uint32_t, numCmds, pSubmits->commandBufferCount); vector<ResourceId> cmdIds; VkCommandBuffer *cmds = m_State >= WRITING ? NULL : new VkCommandBuffer[numCmds]; for(uint32_t i = 0; i < numCmds; i++) { ResourceId bakedId; if(m_State >= WRITING) { VkResourceRecord *record = GetRecord(pSubmits->pCommandBuffers[i]); RDCASSERT(record->bakedCommands); if(record->bakedCommands) bakedId = record->bakedCommands->GetResourceID(); } SERIALISE_ELEMENT(ResourceId, id, bakedId); if(m_State < WRITING) { cmdIds.push_back(id); cmds[i] = id != ResourceId() ? Unwrap(GetResourceManager()->GetLiveHandle<VkCommandBuffer>(id)) : NULL; } } if(m_State < WRITING) { queue = GetResourceManager()->GetLiveHandle<VkQueue>(queueId); if(fenceId != ResourceId()) fence = GetResourceManager()->GetLiveHandle<VkFence>(fenceId); else fence = VK_NULL_HANDLE; } // we don't serialise semaphores at all, just whether we waited on any. // For waiting semaphores, since we don't track state we have to just conservatively // wait for queue idle. Since we do that, there's equally no point in signalling semaphores SERIALISE_ELEMENT(uint32_t, numWaitSems, pSubmits->waitSemaphoreCount); if(m_State < WRITING && numWaitSems > 0) ObjDisp(queue)->QueueWaitIdle(Unwrap(queue)); VkSubmitInfo submitInfo = { VK_STRUCTURE_TYPE_SUBMIT_INFO, NULL, 0, NULL, NULL, // wait semaphores numCmds, cmds, // command buffers 0, NULL, // signal semaphores }; const string desc = localSerialiser->GetDebugStr(); Serialise_DebugMessages(localSerialiser, true); if(m_State == READING) { // don't submit the fence, since we have nothing to wait on it being signalled, and we might // not have it correctly in the unsignalled state. ObjDisp(queue)->QueueSubmit(Unwrap(queue), 1, &submitInfo, VK_NULL_HANDLE); for(uint32_t i = 0; i < numCmds; i++) { ResourceId cmd = GetResourceManager()->GetLiveID(cmdIds[i]); GetResourceManager()->ApplyBarriers(m_BakedCmdBufferInfo[cmd].imgbarriers, m_ImageLayouts); } AddEvent(QUEUE_SUBMIT, desc); // we're adding multiple events, need to increment ourselves m_RootEventID++; string basename = "vkQueueSubmit(" + ToStr::Get(numCmds) + ")"; for(uint32_t c = 0; c < numCmds; c++) { string name = StringFormat::Fmt("=> %s[%u]: vkBeginCommandBuffer(%s)", basename.c_str(), c, ToStr::Get(cmdIds[c]).c_str()); // add a fake marker FetchDrawcall draw; draw.name = name; draw.flags |= eDraw_SetMarker; AddEvent(SET_MARKER, name); AddDrawcall(draw, true); m_RootEventID++; BakedCmdBufferInfo &cmdBufInfo = m_BakedCmdBufferInfo[cmdIds[c]]; // insert the baked command buffer in-line into this list of notes, assigning new event and // drawIDs InsertDrawsAndRefreshIDs(cmdBufInfo.draw->children); for(size_t e = 0; e < cmdBufInfo.draw->executedCmds.size(); e++) { vector<uint32_t> &submits = m_Partial[Secondary].cmdBufferSubmits[cmdBufInfo.draw->executedCmds[e]]; for(size_t s = 0; s < submits.size(); s++) submits[s] += m_RootEventID; } for(size_t i = 0; i < cmdBufInfo.debugMessages.size(); i++) { m_DebugMessages.push_back(cmdBufInfo.debugMessages[i]); m_DebugMessages.back().eventID += m_RootEventID; } // only primary command buffers can be submitted m_Partial[Primary].cmdBufferSubmits[cmdIds[c]].push_back(m_RootEventID); m_RootEventID += cmdBufInfo.eventCount; m_RootDrawcallID += cmdBufInfo.drawCount; name = StringFormat::Fmt("=> %s[%u]: vkEndCommandBuffer(%s)", basename.c_str(), c, ToStr::Get(cmdIds[c]).c_str()); draw.name = name; AddEvent(SET_MARKER, name); AddDrawcall(draw, true); m_RootEventID++; } // account for the outer loop thinking we've added one event and incrementing, // since we've done all the handling ourselves this will be off by one. m_RootEventID--; } else if(m_State == EXECUTING) { // account for the queue submit event m_RootEventID++; uint32_t startEID = m_RootEventID; // advance m_CurEventID to match the events added when reading for(uint32_t c = 0; c < numCmds; c++) { // 2 extra for the virtual labels around the command buffer m_RootEventID += 2 + m_BakedCmdBufferInfo[cmdIds[c]].eventCount; m_RootDrawcallID += 2 + m_BakedCmdBufferInfo[cmdIds[c]].drawCount; } // same accounting for the outer loop as above m_RootEventID--; if(numCmds == 0) { // do nothing, don't bother with the logic below } else if(m_LastEventID <= startEID) { #ifdef VERBOSE_PARTIAL_REPLAY RDCDEBUG("Queue Submit no replay %u == %u", m_LastEventID, startEID); #endif } else if(m_DrawcallCallback && m_DrawcallCallback->RecordAllCmds()) { #ifdef VERBOSE_PARTIAL_REPLAY RDCDEBUG("Queue Submit re-recording from %u", m_RootEventID); #endif vector<VkCommandBuffer> rerecordedCmds; for(uint32_t c = 0; c < numCmds; c++) { VkCommandBuffer cmd = RerecordCmdBuf(cmdIds[c]); ResourceId rerecord = GetResID(cmd); #ifdef VERBOSE_PARTIAL_REPLAY RDCDEBUG("Queue Submit fully re-recorded replay of %llu, using %llu", cmdIds[c], rerecord); #endif rerecordedCmds.push_back(Unwrap(cmd)); GetResourceManager()->ApplyBarriers(m_BakedCmdBufferInfo[rerecord].imgbarriers, m_ImageLayouts); } submitInfo.commandBufferCount = (uint32_t)rerecordedCmds.size(); submitInfo.pCommandBuffers = &rerecordedCmds[0]; // don't submit the fence, since we have nothing to wait on it being signalled, and we might // not have it correctly in the unsignalled state. ObjDisp(queue)->QueueSubmit(Unwrap(queue), 1, &submitInfo, VK_NULL_HANDLE); } else if(m_LastEventID > startEID && m_LastEventID < m_RootEventID) { #ifdef VERBOSE_PARTIAL_REPLAY RDCDEBUG("Queue Submit partial replay %u < %u", m_LastEventID, m_RootEventID); #endif uint32_t eid = startEID; vector<ResourceId> trimmedCmdIds; vector<VkCommandBuffer> trimmedCmds; for(uint32_t c = 0; c < numCmds; c++) { // account for the virtual vkBeginCommandBuffer label at the start of the events here // so it matches up to baseEvent eid++; uint32_t end = eid + m_BakedCmdBufferInfo[cmdIds[c]].eventCount; if(eid == m_Partial[Primary].baseEvent) { ResourceId partial = GetResID(RerecordCmdBuf(cmdIds[c], Primary)); #ifdef VERBOSE_PARTIAL_REPLAY RDCDEBUG("Queue Submit partial replay of %llu at %u, using %llu", cmdIds[c], eid, partial); #endif trimmedCmdIds.push_back(partial); trimmedCmds.push_back(Unwrap(RerecordCmdBuf(cmdIds[c], Primary))); } else if(m_LastEventID >= end) { #ifdef VERBOSE_PARTIAL_REPLAY RDCDEBUG("Queue Submit full replay %llu", cmdIds[c]); #endif trimmedCmdIds.push_back(cmdIds[c]); trimmedCmds.push_back( Unwrap(GetResourceManager()->GetLiveHandle<VkCommandBuffer>(cmdIds[c]))); } else { #ifdef VERBOSE_PARTIAL_REPLAY RDCDEBUG("Queue not submitting %llu", cmdIds[c]); #endif } // 1 extra to account for the virtual end command buffer label (begin is accounted for // above) eid += 1 + m_BakedCmdBufferInfo[cmdIds[c]].eventCount; } RDCASSERT(trimmedCmds.size() > 0); submitInfo.commandBufferCount = (uint32_t)trimmedCmds.size(); submitInfo.pCommandBuffers = &trimmedCmds[0]; // don't submit the fence, since we have nothing to wait on it being signalled, and we might // not have it correctly in the unsignalled state. ObjDisp(queue)->QueueSubmit(Unwrap(queue), 1, &submitInfo, VK_NULL_HANDLE); for(uint32_t i = 0; i < trimmedCmdIds.size(); i++) { ResourceId cmd = trimmedCmdIds[i]; GetResourceManager()->ApplyBarriers(m_BakedCmdBufferInfo[cmd].imgbarriers, m_ImageLayouts); } } else { #ifdef VERBOSE_PARTIAL_REPLAY RDCDEBUG("Queue Submit full replay %u >= %u", m_LastEventID, m_RootEventID); #endif // don't submit the fence, since we have nothing to wait on it being signalled, and we might // not have it correctly in the unsignalled state. ObjDisp(queue)->QueueSubmit(Unwrap(queue), 1, &submitInfo, VK_NULL_HANDLE); for(uint32_t i = 0; i < numCmds; i++) { ResourceId cmd = GetResourceManager()->GetLiveID(cmdIds[i]); GetResourceManager()->ApplyBarriers(m_BakedCmdBufferInfo[cmd].imgbarriers, m_ImageLayouts); } } } SAFE_DELETE_ARRAY(cmds); return true; }
HRESULT Create_Internal(IUnknown *pAdapter, D3D_FEATURE_LEVEL MinimumFeatureLevel, REFIID riid, void **ppDevice) { // if we're already inside a wrapped create i.e. this function, then DON'T do anything // special. Just grab the trampolined function and call it. if(m_InsideCreate) { PFN_D3D12_CREATE_DEVICE createFunc = NULL; // shouldn't ever get in here if we're in the case without hooks but let's be safe. if(m_HasHooks) { createFunc = CreateDevice(); } else { HMODULE d3d12 = GetModuleHandleA("d3d12.dll"); if(d3d12) { createFunc = (PFN_D3D12_CREATE_DEVICE)GetProcAddress(d3d12, "D3D12CreateDevice"); } else { RDCERR("Something went seriously wrong, d3d12.dll couldn't be loaded!"); return E_UNEXPECTED; } } return createFunc(pAdapter, MinimumFeatureLevel, riid, ppDevice); } m_InsideCreate = true; if(riid != __uuidof(ID3D12Device)) { RDCERR("Unsupported UUID %s for D3D12CreateDevice", ToStr::Get(riid).c_str()); return E_NOINTERFACE; } RDCDEBUG("Call to Create_Internal Feature Level %x", MinimumFeatureLevel, ToStr::Get(riid).c_str()); bool reading = RenderDoc::Inst().IsReplayApp(); if(reading) { RDCDEBUG("In replay app"); } const bool EnableDebugLayer = #if 1 // toggle on/off if you want debug layer during replay RenderDoc::Inst().IsReplayApp() || #endif (m_EnabledHooks && !reading && RenderDoc::Inst().GetCaptureOptions().APIValidation); if(EnableDebugLayer) { PFN_D3D12_GET_DEBUG_INTERFACE getfn = GetDebugInterface(); if(getfn == NULL) getfn = (PFN_D3D12_GET_DEBUG_INTERFACE)GetProcAddress(GetModuleHandleA("d3d12.dll"), "D3D12GetDebugInterface"); if(getfn) { ID3D12Debug *debug = NULL; HRESULT hr = getfn(__uuidof(ID3D12Debug), (void **)&debug); if(SUCCEEDED(hr) && debug) debug->EnableDebugLayer(); else RDCERR("Couldn't enable debug layer: %x", hr); SAFE_RELEASE(debug); } else { RDCERR("Couldn't find D3D12GetDebugInterface!"); } } RDCDEBUG("Calling real createdevice..."); PFN_D3D12_CREATE_DEVICE createFunc = (PFN_D3D12_CREATE_DEVICE)GetProcAddress(GetModuleHandleA("d3d12.dll"), "D3D12CreateDevice"); if(createFunc == NULL) createFunc = CreateDevice(); // shouldn't ever get here, we should either have it from procaddress or the trampoline, but // let's be safe. if(createFunc == NULL) { RDCERR("Something went seriously wrong with the hooks!"); m_InsideCreate = false; return E_UNEXPECTED; } HRESULT ret = createFunc(pAdapter, MinimumFeatureLevel, riid, ppDevice); RDCDEBUG("Called real createdevice... 0x%08x", ret); if(SUCCEEDED(ret) && m_EnabledHooks && ppDevice) { RDCDEBUG("succeeded and hooking."); if(!WrappedID3D12Device::IsAlloc(*ppDevice)) { D3D12InitParams params; params.MinimumFeatureLevel = MinimumFeatureLevel; ID3D12Device *dev = (ID3D12Device *)*ppDevice; WrappedID3D12Device *wrap = new WrappedID3D12Device(dev, ¶ms); RDCDEBUG("created wrapped device."); *ppDevice = (ID3D12Device *)wrap; } } else if(SUCCEEDED(ret)) { RDCLOG("Created wrapped D3D12 device."); } else { RDCDEBUG("failed. 0x%08x", ret); } m_InsideCreate = false; return ret; }
Socket *CreateClientSocket(const wchar_t *host, uint16_t port, int timeoutMS) { char portstr[7] = {0}; StringFormat::snprintf(portstr, 6, "%d", port); addrinfo hints; RDCEraseEl(hints); hints.ai_family = AF_INET; hints.ai_socktype = SOCK_STREAM; hints.ai_protocol = IPPROTO_TCP; wstring hostWide = wstring(host); string hoststr = narrow(hostWide); if(widen(hoststr) != hostWide) RDCWARN("Unicode hostname truncated: %S", hostWide.c_str()); addrinfo *result = NULL; getaddrinfo(hoststr.c_str(), portstr, &hints, &result); for(addrinfo *ptr = result; ptr != NULL; ptr = ptr->ai_next) { int s = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP); if(s == -1) return NULL; int flags = fcntl(s, F_GETFL, 0); fcntl(s, F_SETFL, flags | O_NONBLOCK); int result = connect(s, ptr->ai_addr, (int)ptr->ai_addrlen); if(result == -1) { fd_set set; FD_ZERO(&set); FD_SET(s, &set); int err = errno; if(err == EWOULDBLOCK) { timeval timeout; timeout.tv_sec = (timeoutMS/1000); timeout.tv_usec = (timeoutMS%1000)*1000; result = select(0, NULL, &set, NULL, &timeout); if(result <= 0) { RDCDEBUG("connect timed out"); close(s); continue; } else { RDCDEBUG("connect before timeout"); } } else { RDCDEBUG("problem other than blocking"); close(s); continue; } } else { RDCDEBUG("connected immediately"); } int nodelay = 1; setsockopt(s, IPPROTO_TCP, TCP_NODELAY, (char *)&nodelay, sizeof(nodelay)); return new Socket((ptrdiff_t)s); } RDCWARN("Failed to connect to %S:%d", host, port); return NULL; }
void D3D12RenderState::ApplyState(ID3D12GraphicsCommandList *cmd) { if(pipe != ResourceId()) cmd->SetPipelineState(GetResourceManager()->GetCurrentAs<ID3D12PipelineState>(pipe)); if(!views.empty()) cmd->RSSetViewports((UINT)views.size(), &views[0]); if(!scissors.empty()) cmd->RSSetScissorRects((UINT)scissors.size(), &scissors[0]); if(topo != D3D_PRIMITIVE_TOPOLOGY_UNDEFINED) cmd->IASetPrimitiveTopology(topo); cmd->OMSetStencilRef(stencilRef); cmd->OMSetBlendFactor(blendFactor); if(ibuffer.buf != ResourceId()) { D3D12_INDEX_BUFFER_VIEW ib; ID3D12Resource *res = GetResourceManager()->GetCurrentAs<ID3D12Resource>(ibuffer.buf); if(res) ib.BufferLocation = res->GetGPUVirtualAddress() + ibuffer.offs; else ib.BufferLocation = 0; ib.Format = (ibuffer.bytewidth == 2 ? DXGI_FORMAT_R16_UINT : DXGI_FORMAT_R32_UINT); ib.SizeInBytes = ibuffer.size; cmd->IASetIndexBuffer(&ib); } for(size_t i = 0; i < vbuffers.size(); i++) { D3D12_VERTEX_BUFFER_VIEW vb; ID3D12Resource *res = GetResourceManager()->GetCurrentAs<ID3D12Resource>(vbuffers[i].buf); if(res) vb.BufferLocation = res->GetGPUVirtualAddress() + vbuffers[i].offs; else vb.BufferLocation = 0; vb.StrideInBytes = vbuffers[i].stride; vb.SizeInBytes = vbuffers[i].size; cmd->IASetVertexBuffers((UINT)i, 1, &vb); } std::vector<ID3D12DescriptorHeap *> descHeaps; descHeaps.resize(heaps.size()); for(size_t i = 0; i < heaps.size(); i++) descHeaps[i] = GetResourceManager()->GetCurrentAs<ID3D12DescriptorHeap>(heaps[i]); if(!descHeaps.empty()) cmd->SetDescriptorHeaps((UINT)descHeaps.size(), &descHeaps[0]); if(!rts.empty() || dsv.heap != ResourceId()) { D3D12_CPU_DESCRIPTOR_HANDLE rtHandles[8]; D3D12_CPU_DESCRIPTOR_HANDLE dsvHandle = CPUHandleFromPortableHandle(GetResourceManager(), dsv); UINT numActualHandles = rtSingle ? 1 : (UINT)rts.size(); for(UINT i = 0; i < numActualHandles; i++) rtHandles[i] = CPUHandleFromPortableHandle(GetResourceManager(), rts[i]); // need to unwrap here, as FromPortableHandle unwraps too. Unwrap(cmd)->OMSetRenderTargets((UINT)rts.size(), rtHandles, rtSingle ? TRUE : FALSE, dsv.heap != ResourceId() ? &dsvHandle : NULL); } if(graphics.rootsig != ResourceId()) { cmd->SetGraphicsRootSignature( GetResourceManager()->GetCurrentAs<ID3D12RootSignature>(graphics.rootsig)); for(size_t i = 0; i < graphics.sigelems.size(); i++) { // just don't set tables that aren't in the descriptor heaps, since it's invalid and can crash // and is probably just from stale bindings that aren't going to be used if(graphics.sigelems[i].type == eRootTable || std::find(heaps.begin(), heaps.end(), graphics.sigelems[i].id) != heaps.end()) { graphics.sigelems[i].SetToGraphics(GetResourceManager(), cmd, (UINT)i); } else { RDCDEBUG("Skipping setting possibly stale graphics root table referring to heap %llu", graphics.sigelems[i].id); } } } if(compute.rootsig != ResourceId()) { cmd->SetComputeRootSignature( GetResourceManager()->GetCurrentAs<ID3D12RootSignature>(compute.rootsig)); for(size_t i = 0; i < compute.sigelems.size(); i++) { // just don't set tables that aren't in the descriptor heaps, since it's invalid and can crash // and is probably just from stale bindings that aren't going to be used if(compute.sigelems[i].type != eRootTable || std::find(heaps.begin(), heaps.end(), compute.sigelems[i].id) != heaps.end()) { compute.sigelems[i].SetToCompute(GetResourceManager(), cmd, (UINT)i); } else { RDCDEBUG("Skipping setting possibly stale compute root table referring to heap %llu", compute.sigelems[i].id); } } } }
ReplayCreateStatus GL_CreateReplayDevice(const char *logfile, IReplayDriver **driver) { RDCDEBUG("Creating an OpenGL replay device"); HMODULE lib = NULL; lib = LoadLibraryA("opengl32.dll"); if(lib == NULL) { RDCERR("Failed to load opengl32.dll"); return eReplayCreate_APIInitFailed; } GLInitParams initParams; RDCDriver driverType = RDC_OpenGL; string driverName = "OpenGL"; uint64_t machineIdent = 0; if(logfile) { auto status = RenderDoc::Inst().FillInitParams(logfile, driverType, driverName, machineIdent, (RDCInitParams *)&initParams); if(status != eReplayCreate_Success) return status; } PIXELFORMATDESCRIPTOR pfd = {0}; if(wglGetProc == NULL) { wglGetProc = (WGLGETPROCADDRESSPROC)GetProcAddress(lib, "wglGetProcAddress"); wglCreateRC = (WGLCREATECONTEXTPROC)GetProcAddress(lib, "wglCreateContext"); wglMakeCurrentProc = (WGLMAKECURRENTPROC)GetProcAddress(lib, "wglMakeCurrent"); wglDeleteRC = (WGLDELETECONTEXTPROC)GetProcAddress(lib, "wglDeleteContext"); if(wglGetProc == NULL || wglCreateRC == NULL || wglMakeCurrentProc == NULL || wglDeleteRC == NULL) { RDCERR("Couldn't get wgl function addresses"); return eReplayCreate_APIInitFailed; } WNDCLASSEX wc; RDCEraseEl(wc); wc.style = CS_OWNDC; wc.cbSize = sizeof(WNDCLASSEX); wc.lpfnWndProc = DefWindowProc; wc.hInstance = GetModuleHandle(NULL); wc.hCursor = LoadCursor(NULL, IDC_ARROW); wc.lpszClassName = L"renderdocGLclass"; if(!RegisterClassEx(&wc)) { RDCERR("Couldn't register GL window class"); return eReplayCreate_APIInitFailed; } RDCEraseEl(pfd); pfd.nSize = sizeof(PIXELFORMATDESCRIPTOR); pfd.nVersion = 1; pfd.dwFlags = PFD_DRAW_TO_WINDOW | PFD_SUPPORT_OPENGL | PFD_DOUBLEBUFFER; pfd.iLayerType = PFD_MAIN_PLANE; pfd.iPixelType = PFD_TYPE_RGBA; pfd.cColorBits = 32; pfd.cDepthBits = 24; pfd.cStencilBits = 0; } HWND w = CreateWindowEx(WS_EX_CLIENTEDGE, L"renderdocGLclass", L"", WS_OVERLAPPEDWINDOW, CW_USEDEFAULT, CW_USEDEFAULT, CW_USEDEFAULT, CW_USEDEFAULT, NULL, NULL, GetModuleHandle(NULL), NULL); HDC dc = GetDC(w); int pf = ChoosePixelFormat(dc, &pfd); if(pf == 0) { RDCERR("Couldn't choose pixel format"); return eReplayCreate_APIInitFailed; } BOOL res = SetPixelFormat(dc, pf, &pfd); if(res == FALSE) { RDCERR("Couldn't set pixel format"); return eReplayCreate_APIInitFailed; } HGLRC rc = wglCreateRC(dc); if(rc == NULL) { RDCERR("Couldn't create simple RC"); return eReplayCreate_APIInitFailed; } res = wglMakeCurrentProc(dc, rc); if(res == FALSE) { RDCERR("Couldn't make simple RC current"); return eReplayCreate_APIInitFailed; } createContextAttribs = (PFNWGLCREATECONTEXTATTRIBSARBPROC)wglGetProc("wglCreateContextAttribsARB"); getPixelFormatAttrib = (PFNWGLGETPIXELFORMATATTRIBIVARBPROC)wglGetProc("wglGetPixelFormatAttribivARB"); if(createContextAttribs == NULL || getPixelFormatAttrib == NULL) { RDCERR("RenderDoc requires WGL_ARB_create_context and WGL_ARB_pixel_format"); return eReplayCreate_APIHardwareUnsupported; } wglMakeCurrentProc(NULL, NULL); wglDeleteRC(rc); ReleaseDC(w, dc); DestroyWindow(w); GLReplay::PreContextInitCounters(); // we don't use the default framebuffer (backbuffer) for anything, so we make it // tiny and with no depth/stencil bits pfd.iPixelType = PFD_TYPE_RGBA; pfd.cColorBits = 24; pfd.cDepthBits = 0; pfd.cStencilBits = 0; w = CreateWindowEx(WS_EX_CLIENTEDGE, L"renderdocGLclass", L"RenderDoc replay window", WS_OVERLAPPEDWINDOW, CW_USEDEFAULT, CW_USEDEFAULT, 32, 32, NULL, NULL, GetModuleHandle(NULL), NULL); dc = GetDC(w); pf = ChoosePixelFormat(dc, &pfd); if(pf == 0) { RDCERR("Couldn't choose pixel format"); ReleaseDC(w, dc); GLReplay::PostContextShutdownCounters(); return eReplayCreate_APIInitFailed; } res = SetPixelFormat(dc, pf, &pfd); if(res == FALSE) { RDCERR("Couldn't set pixel format"); ReleaseDC(w, dc); GLReplay::PostContextShutdownCounters(); return eReplayCreate_APIInitFailed; } int attribs[64] = {0}; int i = 0; attribs[i++] = WGL_CONTEXT_MAJOR_VERSION_ARB; int &major = attribs[i]; attribs[i++] = 0; attribs[i++] = WGL_CONTEXT_MINOR_VERSION_ARB; int &minor = attribs[i]; attribs[i++] = 0; attribs[i++] = WGL_CONTEXT_FLAGS_ARB; #if ENABLED(RDOC_DEVEL) attribs[i++] = WGL_CONTEXT_DEBUG_BIT_ARB; #else attribs[i++] = 0; #endif attribs[i++] = WGL_CONTEXT_PROFILE_MASK_ARB; attribs[i++] = WGL_CONTEXT_CORE_PROFILE_BIT_ARB; // try to create all versions from 4.5 down to 3.2 in order to get the // highest versioned context we can struct { int major; int minor; } versions[] = { {4, 5}, {4, 4}, {4, 3}, {4, 2}, {4, 1}, {4, 0}, {3, 3}, {3, 2}, }; rc = NULL; for(size_t v = 0; v < ARRAY_COUNT(versions); v++) { major = versions[v].major; minor = versions[v].minor; rc = createContextAttribs(dc, NULL, attribs); if(rc) break; } if(rc == NULL) { RDCERR("Couldn't create 3.2 RC - RenderDoc requires OpenGL 3.2 availability"); ReleaseDC(w, dc); GLReplay::PostContextShutdownCounters(); return eReplayCreate_APIHardwareUnsupported; } GLCoreVersion = major * 10 + minor; res = wglMakeCurrentProc(dc, rc); if(res == FALSE) { RDCERR("Couldn't make 3.2 RC current"); wglMakeCurrentProc(NULL, NULL); wglDeleteRC(rc); ReleaseDC(w, dc); GLReplay::PostContextShutdownCounters(); return eReplayCreate_APIInitFailed; } PFNGLGETINTEGERVPROC getInt = (PFNGLGETINTEGERVPROC)GetProcAddress(lib, "glGetIntegerv"); PFNGLGETSTRINGPROC getStr = (PFNGLGETSTRINGPROC)GetProcAddress(lib, "glGetString"); PFNGLGETSTRINGIPROC getStri = (PFNGLGETSTRINGIPROC)wglGetProc("glGetStringi"); if(getInt == NULL || getStr == NULL || getStri == NULL) { RDCERR("Couldn't get glGetIntegerv (%p), glGetString (%p) or glGetStringi (%p) entry points", getInt, getStr, getStri); wglMakeCurrentProc(NULL, NULL); wglDeleteRC(rc); ReleaseDC(w, dc); GLReplay::PostContextShutdownCounters(); return eReplayCreate_APIInitFailed; } bool missingExt = CheckReplayContext(getStr, getInt, getStri); if(missingExt) { wglMakeCurrentProc(NULL, NULL); wglDeleteRC(rc); ReleaseDC(w, dc); GLReplay::PostContextShutdownCounters(); return eReplayCreate_APIInitFailed; } const GLHookSet &real = GetRealGLFunctions(); bool extensionsValidated = ValidateFunctionPointers(real); if(!extensionsValidated) { wglMakeCurrentProc(NULL, NULL); wglDeleteRC(rc); ReleaseDC(w, dc); GLReplay::PostContextShutdownCounters(); return eReplayCreate_APIInitFailed; } WrappedOpenGL *gl = new WrappedOpenGL(logfile, real, GetGLPlatform()); gl->Initialise(initParams); if(gl->GetSerialiser()->HasError()) { delete gl; return eReplayCreate_FileIOFailed; } RDCLOG("Created device."); GLReplay *replay = gl->GetReplay(); replay->SetProxy(logfile == NULL); GLWindowingData data; data.DC = dc; data.ctx = rc; data.wnd = w; replay->SetReplayData(data); *driver = (IReplayDriver *)replay; return eReplayCreate_Success; }
VkResult WrappedVulkan::vkQueueSubmit(VkQueue queue, uint32_t submitCount, const VkSubmitInfo *pSubmits, VkFence fence) { SCOPED_DBG_SINK(); size_t tempmemSize = sizeof(VkSubmitInfo) * submitCount; // need to count how many semaphore and command buffer arrays to allocate for for(uint32_t i = 0; i < submitCount; i++) { tempmemSize += pSubmits[i].commandBufferCount * sizeof(VkCommandBuffer); tempmemSize += pSubmits[i].signalSemaphoreCount * sizeof(VkSemaphore); tempmemSize += pSubmits[i].waitSemaphoreCount * sizeof(VkSemaphore); } byte *memory = GetTempMemory(tempmemSize); VkSubmitInfo *unwrappedSubmits = (VkSubmitInfo *)memory; VkSemaphore *unwrappedWaitSems = (VkSemaphore *)(unwrappedSubmits + submitCount); for(uint32_t i = 0; i < submitCount; i++) { RDCASSERT(pSubmits[i].sType == VK_STRUCTURE_TYPE_SUBMIT_INFO && pSubmits[i].pNext == NULL); unwrappedSubmits[i] = pSubmits[i]; unwrappedSubmits[i].pWaitSemaphores = unwrappedSubmits[i].waitSemaphoreCount ? unwrappedWaitSems : NULL; for(uint32_t o = 0; o < unwrappedSubmits[i].waitSemaphoreCount; o++) unwrappedWaitSems[o] = Unwrap(pSubmits[i].pWaitSemaphores[o]); unwrappedWaitSems += unwrappedSubmits[i].waitSemaphoreCount; VkCommandBuffer *unwrappedCommandBuffers = (VkCommandBuffer *)unwrappedWaitSems; unwrappedSubmits[i].pCommandBuffers = unwrappedSubmits[i].commandBufferCount ? unwrappedCommandBuffers : NULL; for(uint32_t o = 0; o < unwrappedSubmits[i].commandBufferCount; o++) unwrappedCommandBuffers[o] = Unwrap(pSubmits[i].pCommandBuffers[o]); unwrappedCommandBuffers += unwrappedSubmits[i].commandBufferCount; VkSemaphore *unwrappedSignalSems = (VkSemaphore *)unwrappedCommandBuffers; unwrappedSubmits[i].pSignalSemaphores = unwrappedSubmits[i].signalSemaphoreCount ? unwrappedSignalSems : NULL; for(uint32_t o = 0; o < unwrappedSubmits[i].signalSemaphoreCount; o++) unwrappedSignalSems[o] = Unwrap(pSubmits[i].pSignalSemaphores[o]); } VkResult ret = ObjDisp(queue)->QueueSubmit(Unwrap(queue), submitCount, unwrappedSubmits, Unwrap(fence)); bool capframe = false; set<ResourceId> refdIDs; for(uint32_t s = 0; s < submitCount; s++) { for(uint32_t i = 0; i < pSubmits[s].commandBufferCount; i++) { ResourceId cmd = GetResID(pSubmits[s].pCommandBuffers[i]); VkResourceRecord *record = GetRecord(pSubmits[s].pCommandBuffers[i]); { SCOPED_LOCK(m_ImageLayoutsLock); GetResourceManager()->ApplyBarriers(record->bakedCommands->cmdInfo->imgbarriers, m_ImageLayouts); } // need to lock the whole section of code, not just the check on // m_State, as we also need to make sure we don't check the state, // start marking dirty resources then while we're doing so the // state becomes capframe. // the next sections where we mark resources referenced and add // the submit chunk to the frame record don't have to be protected. // Only the decision of whether we're inframe or not, and marking // dirty. { SCOPED_LOCK(m_CapTransitionLock); if(m_State == WRITING_CAPFRAME) { for(auto it = record->bakedCommands->cmdInfo->dirtied.begin(); it != record->bakedCommands->cmdInfo->dirtied.end(); ++it) GetResourceManager()->MarkPendingDirty(*it); capframe = true; } else { for(auto it = record->bakedCommands->cmdInfo->dirtied.begin(); it != record->bakedCommands->cmdInfo->dirtied.end(); ++it) GetResourceManager()->MarkDirtyResource(*it); } } if(capframe) { // for each bound descriptor set, mark it referenced as well as all resources currently // bound to it for(auto it = record->bakedCommands->cmdInfo->boundDescSets.begin(); it != record->bakedCommands->cmdInfo->boundDescSets.end(); ++it) { GetResourceManager()->MarkResourceFrameReferenced(GetResID(*it), eFrameRef_Read); VkResourceRecord *setrecord = GetRecord(*it); for(auto refit = setrecord->descInfo->bindFrameRefs.begin(); refit != setrecord->descInfo->bindFrameRefs.end(); ++refit) { refdIDs.insert(refit->first); GetResourceManager()->MarkResourceFrameReferenced(refit->first, refit->second.second); if(refit->second.first & DescriptorSetData::SPARSE_REF_BIT) { VkResourceRecord *sparserecord = GetResourceManager()->GetResourceRecord(refit->first); GetResourceManager()->MarkSparseMapReferenced(sparserecord->sparseInfo); } } } for(auto it = record->bakedCommands->cmdInfo->sparse.begin(); it != record->bakedCommands->cmdInfo->sparse.end(); ++it) GetResourceManager()->MarkSparseMapReferenced(*it); // pull in frame refs from this baked command buffer record->bakedCommands->AddResourceReferences(GetResourceManager()); record->bakedCommands->AddReferencedIDs(refdIDs); // ref the parent command buffer by itself, this will pull in the cmd buffer pool GetResourceManager()->MarkResourceFrameReferenced(record->GetResourceID(), eFrameRef_Read); for(size_t sub = 0; sub < record->bakedCommands->cmdInfo->subcmds.size(); sub++) { record->bakedCommands->cmdInfo->subcmds[sub]->bakedCommands->AddResourceReferences( GetResourceManager()); record->bakedCommands->cmdInfo->subcmds[sub]->bakedCommands->AddReferencedIDs(refdIDs); GetResourceManager()->MarkResourceFrameReferenced( record->bakedCommands->cmdInfo->subcmds[sub]->GetResourceID(), eFrameRef_Read); record->bakedCommands->cmdInfo->subcmds[sub]->bakedCommands->AddRef(); } GetResourceManager()->MarkResourceFrameReferenced(GetResID(queue), eFrameRef_Read); if(fence != VK_NULL_HANDLE) GetResourceManager()->MarkResourceFrameReferenced(GetResID(fence), eFrameRef_Read); { SCOPED_LOCK(m_CmdBufferRecordsLock); m_CmdBufferRecords.push_back(record->bakedCommands); for(size_t sub = 0; sub < record->bakedCommands->cmdInfo->subcmds.size(); sub++) m_CmdBufferRecords.push_back(record->bakedCommands->cmdInfo->subcmds[sub]->bakedCommands); } record->bakedCommands->AddRef(); } record->cmdInfo->dirtied.clear(); } } if(capframe) { vector<VkResourceRecord *> maps; { SCOPED_LOCK(m_CoherentMapsLock); maps = m_CoherentMaps; } for(auto it = maps.begin(); it != maps.end(); ++it) { VkResourceRecord *record = *it; MemMapState &state = *record->memMapState; // potential persistent map if(state.mapCoherent && state.mappedPtr && !state.mapFlushed) { // only need to flush memory that could affect this submitted batch of work if(refdIDs.find(record->GetResourceID()) == refdIDs.end()) { RDCDEBUG("Map of memory %llu not referenced in this queue - not flushing", record->GetResourceID()); continue; } size_t diffStart = 0, diffEnd = 0; bool found = true; // enabled as this is necessary for programs with very large coherent mappings // (> 1GB) as otherwise more than a couple of vkQueueSubmit calls leads to vast // memory allocation. There might still be bugs lurking in here though #if 1 // this causes vkFlushMappedMemoryRanges call to allocate and copy to refData // from serialised buffer. We want to copy *precisely* the serialised data, // otherwise there is a gap in time between serialising out a snapshot of // the buffer and whenever we then copy into the ref data, e.g. below. // during this time, data could be written to the buffer and it won't have // been caught in the serialised snapshot, and if it doesn't change then // it *also* won't be caught in any future FindDiffRange() calls. // // Likewise once refData is allocated, the call below will also update it // with the data serialised out for the same reason. // // Note: it's still possible that data is being written to by the // application while it's being serialised out in the snapshot below. That // is OK, since the application is responsible for ensuring it's not writing // data that would be needed by the GPU in this submit. As long as the // refdata we use for future use is identical to what was serialised, we // shouldn't miss anything state.needRefData = true; // if we have a previous set of data, compare. // otherwise just serialise it all if(state.refData) found = FindDiffRange((byte *)state.mappedPtr, state.refData, (size_t)state.mapSize, diffStart, diffEnd); else #endif diffEnd = (size_t)state.mapSize; if(found) { // MULTIDEVICE should find the device for this queue. // MULTIDEVICE only want to flush maps associated with this queue VkDevice dev = GetDev(); { RDCLOG("Persistent map flush forced for %llu (%llu -> %llu)", record->GetResourceID(), (uint64_t)diffStart, (uint64_t)diffEnd); VkMappedMemoryRange range = {VK_STRUCTURE_TYPE_MAPPED_MEMORY_RANGE, NULL, (VkDeviceMemory)(uint64_t)record->Resource, state.mapOffset + diffStart, diffEnd - diffStart}; vkFlushMappedMemoryRanges(dev, 1, &range); state.mapFlushed = false; } GetResourceManager()->MarkPendingDirty(record->GetResourceID()); } else { RDCDEBUG("Persistent map flush not needed for %llu", record->GetResourceID()); } } } { CACHE_THREAD_SERIALISER(); for(uint32_t s = 0; s < submitCount; s++) { SCOPED_SERIALISE_CONTEXT(QUEUE_SUBMIT); Serialise_vkQueueSubmit(localSerialiser, queue, 1, &pSubmits[s], fence); m_FrameCaptureRecord->AddChunk(scope.Get()); for(uint32_t sem = 0; sem < pSubmits[s].waitSemaphoreCount; sem++) GetResourceManager()->MarkResourceFrameReferenced( GetResID(pSubmits[s].pWaitSemaphores[sem]), eFrameRef_Read); for(uint32_t sem = 0; sem < pSubmits[s].signalSemaphoreCount; sem++) GetResourceManager()->MarkResourceFrameReferenced( GetResID(pSubmits[s].pSignalSemaphores[sem]), eFrameRef_Read); } } } return ret; }
Socket *CreateClientSocket(const char *host, uint16_t port, int timeoutMS) { wchar_t portwstr[7] = {0}; { char buf[7] = {0}; int n = StringFormat::snprintf(buf, 6, "%d", port); for(int i=0; i < n && i < 6; i++) portwstr[i] = (wchar_t)buf[i]; } addrinfoW hints; RDCEraseEl(hints); hints.ai_family = AF_INET; hints.ai_socktype = SOCK_STREAM; hints.ai_protocol = IPPROTO_TCP; std::wstring whost = StringFormat::UTF82Wide(string(host)); addrinfoW *addrResult = NULL; GetAddrInfoW(whost.c_str(), portwstr, &hints, &addrResult); for(addrinfoW *ptr = addrResult; ptr != NULL; ptr = ptr->ai_next) { SOCKET s = WSASocket(AF_INET, SOCK_STREAM, IPPROTO_TCP, NULL, 0, WSA_FLAG_NO_HANDLE_INHERIT); if(s == INVALID_SOCKET) return NULL; u_long enable = 1; ioctlsocket(s, FIONBIO, &enable); int result = connect(s, ptr->ai_addr, (int)ptr->ai_addrlen); if(result == SOCKET_ERROR) { fd_set set; FD_ZERO(&set); // macro FD_SET contains the do { } while(0) idiom, which warns #pragma warning(push) #pragma warning(disable : 4127) // conditional expression is constant FD_SET(s, &set); #pragma warning(pop) int err = WSAGetLastError(); if(err == WSAEWOULDBLOCK) { timeval timeout; timeout.tv_sec = (timeoutMS/1000); timeout.tv_usec = (timeoutMS%1000)*1000; result = select(0, NULL, &set, NULL, &timeout); if(result <= 0) { RDCDEBUG("connect timed out"); closesocket(s); continue; } else { RDCDEBUG("connect before timeout"); } } else { RDCDEBUG("problem other than blocking"); closesocket(s); continue; } } else { RDCDEBUG("connected immediately"); } BOOL nodelay = TRUE; setsockopt(s, IPPROTO_TCP, TCP_NODELAY, (const char *)&nodelay, sizeof(nodelay)); return new Socket((ptrdiff_t)s); } RDCWARN("Failed to connect to %s:%d", host, port); return NULL; }
VkResult WrappedVulkan::vkCreateDevice( VkPhysicalDevice physicalDevice, const VkDeviceCreateInfo* pCreateInfo, const VkAllocationCallbacks* pAllocator, VkDevice* pDevice) { VkDeviceCreateInfo createInfo = *pCreateInfo; uint32_t qCount = 0; VkResult vkr = VK_SUCCESS; ObjDisp(physicalDevice)->GetPhysicalDeviceQueueFamilyProperties(Unwrap(physicalDevice), &qCount, NULL); VkQueueFamilyProperties *props = new VkQueueFamilyProperties[qCount]; ObjDisp(physicalDevice)->GetPhysicalDeviceQueueFamilyProperties(Unwrap(physicalDevice), &qCount, props); // find a queue that supports all capabilities, and if one doesn't exist, add it. bool found = false; uint32_t qFamilyIdx = 0; VkQueueFlags search = (VK_QUEUE_GRAPHICS_BIT); // for queue priorities, if we need it float one = 1.0f; // if we need to change the requested queues, it will point to this VkDeviceQueueCreateInfo *modQueues = NULL; for(uint32_t i=0; i < createInfo.queueCreateInfoCount; i++) { uint32_t idx = createInfo.pQueueCreateInfos[i].queueFamilyIndex; RDCASSERT(idx < qCount); // this requested queue is one we can use too if((props[idx].queueFlags & search) == search && createInfo.pQueueCreateInfos[i].queueCount > 0) { qFamilyIdx = idx; found = true; break; } } // if we didn't find it, search for which queue family we should add a request for if(!found) { RDCDEBUG("App didn't request a queue family we can use - adding our own"); for(uint32_t i=0; i < qCount; i++) { if((props[i].queueFlags & search) == search) { qFamilyIdx = i; found = true; break; } } if(!found) { SAFE_DELETE_ARRAY(props); RDCERR("Can't add a queue with required properties for RenderDoc! Unsupported configuration"); return VK_ERROR_INITIALIZATION_FAILED; } // we found the queue family, add it modQueues = new VkDeviceQueueCreateInfo[createInfo.queueCreateInfoCount + 1]; for(uint32_t i=0; i < createInfo.queueCreateInfoCount; i++) modQueues[i] = createInfo.pQueueCreateInfos[i]; modQueues[createInfo.queueCreateInfoCount].queueFamilyIndex = qFamilyIdx; modQueues[createInfo.queueCreateInfoCount].queueCount = 1; modQueues[createInfo.queueCreateInfoCount].pQueuePriorities = &one; createInfo.pQueueCreateInfos = modQueues; createInfo.queueCreateInfoCount++; } SAFE_DELETE_ARRAY(props); m_QueueFamilies.resize(createInfo.queueCreateInfoCount); for(size_t i=0; i < createInfo.queueCreateInfoCount; i++) { uint32_t family = createInfo.pQueueCreateInfos[i].queueFamilyIndex; uint32_t count = createInfo.pQueueCreateInfos[i].queueCount; m_QueueFamilies.resize(RDCMAX(m_QueueFamilies.size(), size_t(family+1))); m_QueueFamilies[family] = new VkQueue[count]; for(uint32_t q=0; q < count; q++) m_QueueFamilies[family][q] = VK_NULL_HANDLE; } VkLayerDeviceCreateInfo *layerCreateInfo = (VkLayerDeviceCreateInfo *)pCreateInfo->pNext; // step through the chain of pNext until we get to the link info while(layerCreateInfo && (layerCreateInfo->sType != VK_STRUCTURE_TYPE_LOADER_DEVICE_CREATE_INFO || layerCreateInfo->function != VK_LAYER_LINK_INFO) ) { layerCreateInfo = (VkLayerDeviceCreateInfo *)layerCreateInfo->pNext; } RDCASSERT(layerCreateInfo); PFN_vkGetDeviceProcAddr gdpa = layerCreateInfo->u.pLayerInfo->pfnNextGetDeviceProcAddr; PFN_vkGetInstanceProcAddr gipa = layerCreateInfo->u.pLayerInfo->pfnNextGetInstanceProcAddr; // move chain on for next layer layerCreateInfo->u.pLayerInfo = layerCreateInfo->u.pLayerInfo->pNext; PFN_vkCreateDevice createFunc = (PFN_vkCreateDevice)gipa(VK_NULL_HANDLE, "vkCreateDevice"); // now search again through for the loader data callback (if it exists) layerCreateInfo = (VkLayerDeviceCreateInfo *)pCreateInfo->pNext; // step through the chain of pNext while(layerCreateInfo && (layerCreateInfo->sType != VK_STRUCTURE_TYPE_LOADER_DEVICE_CREATE_INFO || layerCreateInfo->function != VK_LOADER_DATA_CALLBACK) ) { layerCreateInfo = (VkLayerDeviceCreateInfo *)layerCreateInfo->pNext; } // if we found one (we might not - on old loaders), then store the func ptr for // use instead of SetDispatchTableOverMagicNumber if(layerCreateInfo) { RDCASSERT(m_SetDeviceLoaderData == layerCreateInfo->u.pfnSetDeviceLoaderData || m_SetDeviceLoaderData == NULL, m_SetDeviceLoaderData, layerCreateInfo->u.pfnSetDeviceLoaderData); m_SetDeviceLoaderData = layerCreateInfo->u.pfnSetDeviceLoaderData; } VkResult ret = createFunc(Unwrap(physicalDevice), &createInfo, pAllocator, pDevice); // don't serialise out any of the pNext stuff for layer initialisation // (note that we asserted above that there was nothing else in the chain) createInfo.pNext = NULL; if(ret == VK_SUCCESS) { InitDeviceTable(*pDevice, gdpa); ResourceId id = GetResourceManager()->WrapResource(*pDevice, *pDevice); if(m_State >= WRITING) { Chunk *chunk = NULL; { CACHE_THREAD_SERIALISER(); SCOPED_SERIALISE_CONTEXT(CREATE_DEVICE); Serialise_vkCreateDevice(localSerialiser, physicalDevice, &createInfo, NULL, pDevice); chunk = scope.Get(); } VkResourceRecord *record = GetResourceManager()->AddResourceRecord(*pDevice); RDCASSERT(record); record->AddChunk(chunk); record->memIdxMap = GetRecord(physicalDevice)->memIdxMap; record->instDevInfo = new InstanceDeviceInfo(); #undef CheckExt #define CheckExt(name) record->instDevInfo->name = GetRecord(m_Instance)->instDevInfo->name; // inherit extension enablement from instance, that way GetDeviceProcAddress can check // for enabled extensions for instance functions CheckInstanceExts(); #undef CheckExt #define CheckExt(name) if(!strcmp(createInfo.ppEnabledExtensionNames[i], STRINGIZE(name))) { record->instDevInfo->name = true; } for(uint32_t i=0; i < createInfo.enabledExtensionCount; i++) { CheckDeviceExts(); } InitDeviceExtensionTables(*pDevice); GetRecord(m_Instance)->AddParent(record); } else { GetResourceManager()->AddLiveResource(id, *pDevice); } VkDevice device = *pDevice; RDCASSERT(m_Device == VK_NULL_HANDLE); // MULTIDEVICE m_PhysicalDevice = physicalDevice; m_Device = device; m_QueueFamilyIdx = qFamilyIdx; if(m_InternalCmds.cmdpool == VK_NULL_HANDLE) { VkCommandPoolCreateInfo poolInfo = { VK_STRUCTURE_TYPE_COMMAND_POOL_CREATE_INFO, NULL, VK_COMMAND_POOL_CREATE_RESET_COMMAND_BUFFER_BIT, qFamilyIdx }; vkr = ObjDisp(device)->CreateCommandPool(Unwrap(device), &poolInfo, NULL, &m_InternalCmds.cmdpool); RDCASSERTEQUAL(vkr, VK_SUCCESS); GetResourceManager()->WrapResource(Unwrap(device), m_InternalCmds.cmdpool); } ObjDisp(physicalDevice)->GetPhysicalDeviceProperties(Unwrap(physicalDevice), &m_PhysicalDeviceData.props); ObjDisp(physicalDevice)->GetPhysicalDeviceMemoryProperties(Unwrap(physicalDevice), &m_PhysicalDeviceData.memProps); ObjDisp(physicalDevice)->GetPhysicalDeviceFeatures(Unwrap(physicalDevice), &m_PhysicalDeviceData.features); m_PhysicalDeviceData.readbackMemIndex = m_PhysicalDeviceData.GetMemoryIndex(~0U, VK_MEMORY_PROPERTY_HOST_VISIBLE_BIT, 0); m_PhysicalDeviceData.uploadMemIndex = m_PhysicalDeviceData.GetMemoryIndex(~0U, VK_MEMORY_PROPERTY_HOST_VISIBLE_BIT, 0); m_PhysicalDeviceData.GPULocalMemIndex = m_PhysicalDeviceData.GetMemoryIndex(~0U, VK_MEMORY_PROPERTY_DEVICE_LOCAL_BIT, VK_MEMORY_PROPERTY_HOST_VISIBLE_BIT); m_PhysicalDeviceData.fakeMemProps = GetRecord(physicalDevice)->memProps; m_DebugManager = new VulkanDebugManager(this, device); } SAFE_DELETE_ARRAY(modQueues); return ret; }
void STDMETHODCALLTYPE WrappedID3D12CommandQueue::ExecuteCommandLists( UINT NumCommandLists, ID3D12CommandList *const *ppCommandLists) { ID3D12CommandList **unwrapped = m_pDevice->GetTempArray<ID3D12CommandList *>(NumCommandLists); for(UINT i = 0; i < NumCommandLists; i++) unwrapped[i] = Unwrap(ppCommandLists[i]); m_pReal->ExecuteCommandLists(NumCommandLists, unwrapped); if(m_State >= WRITING) { SCOPED_LOCK(m_Lock); SCOPED_LOCK(m_pDevice->GetCapTransitionLock()); bool capframe = (m_State == WRITING_CAPFRAME); set<ResourceId> refdIDs; for(UINT i = 0; i < NumCommandLists; i++) { D3D12ResourceRecord *record = GetRecord(ppCommandLists[i]); if(record->ContainsExecuteIndirect) m_QueueRecord->ContainsExecuteIndirect = true; m_pDevice->ApplyBarriers(record->bakedCommands->cmdInfo->barriers); // need to lock the whole section of code, not just the check on // m_State, as we also need to make sure we don't check the state, // start marking dirty resources then while we're doing so the // state becomes capframe. // the next sections where we mark resources referenced and add // the submit chunk to the frame record don't have to be protected. // Only the decision of whether we're inframe or not, and marking // dirty. if(capframe) { for(auto it = record->bakedCommands->cmdInfo->dirtied.begin(); it != record->bakedCommands->cmdInfo->dirtied.end(); ++it) GetResourceManager()->MarkPendingDirty(*it); } else { for(auto it = record->bakedCommands->cmdInfo->dirtied.begin(); it != record->bakedCommands->cmdInfo->dirtied.end(); ++it) GetResourceManager()->MarkDirtyResource(*it); } if(capframe) { // any descriptor copies or writes could reference new resources not in the // bound descs list yet. So we take all of those referenced descriptors and // include them to see if we need to flush std::vector<D3D12Descriptor> dynDescRefs; m_pDevice->GetDynamicDescriptorReferences(dynDescRefs); for(size_t d = 0; d < dynDescRefs.size(); d++) { ResourceId id, id2; FrameRefType ref = eFrameRef_Read; dynDescRefs[d].GetRefIDs(id, id2, ref); if(id != ResourceId()) { refdIDs.insert(id); GetResourceManager()->MarkResourceFrameReferenced(id, ref); } if(id2 != ResourceId()) { refdIDs.insert(id2); GetResourceManager()->MarkResourceFrameReferenced(id2, ref); } } // for each bound descriptor table, mark it referenced as well as all resources currently // bound to it for(auto it = record->bakedCommands->cmdInfo->boundDescs.begin(); it != record->bakedCommands->cmdInfo->boundDescs.end(); ++it) { D3D12Descriptor *desc = *it; ResourceId id, id2; FrameRefType ref = eFrameRef_Read; desc->GetRefIDs(id, id2, ref); if(id != ResourceId()) { refdIDs.insert(id); GetResourceManager()->MarkResourceFrameReferenced(id, ref); } if(id2 != ResourceId()) { refdIDs.insert(id2); GetResourceManager()->MarkResourceFrameReferenced(id2, ref); } } // pull in frame refs from this baked command list record->bakedCommands->AddResourceReferences(GetResourceManager()); record->bakedCommands->AddReferencedIDs(refdIDs); // reference all executed bundles as well for(size_t b = 0; b < record->bakedCommands->cmdInfo->bundles.size(); b++) { record->bakedCommands->cmdInfo->bundles[b]->bakedCommands->AddResourceReferences( GetResourceManager()); record->bakedCommands->cmdInfo->bundles[b]->bakedCommands->AddReferencedIDs(refdIDs); GetResourceManager()->MarkResourceFrameReferenced( record->bakedCommands->cmdInfo->bundles[b]->GetResourceID(), eFrameRef_Read); record->bakedCommands->cmdInfo->bundles[b]->bakedCommands->AddRef(); } { m_CmdListRecords.push_back(record->bakedCommands); for(size_t sub = 0; sub < record->bakedCommands->cmdInfo->bundles.size(); sub++) m_CmdListRecords.push_back(record->bakedCommands->cmdInfo->bundles[sub]->bakedCommands); } record->bakedCommands->AddRef(); } record->cmdInfo->dirtied.clear(); } if(capframe) { vector<MapState> maps = m_pDevice->GetMaps(); for(auto it = maps.begin(); it != maps.end(); ++it) { WrappedID3D12Resource *res = it->res; UINT subres = it->subres; size_t size = (size_t)it->totalSize; // only need to flush memory that could affect this submitted batch of work if(refdIDs.find(res->GetResourceID()) == refdIDs.end()) { RDCDEBUG("Map of memory %llu not referenced in this queue - not flushing", res->GetResourceID()); continue; } size_t diffStart = 0, diffEnd = 0; bool found = true; byte *ref = res->GetShadow(subres); byte *data = res->GetMap(subres); if(ref) found = FindDiffRange(data, ref, size, diffStart, diffEnd); else diffEnd = size; if(found) { RDCLOG("Persistent map flush forced for %llu (%llu -> %llu)", res->GetResourceID(), (uint64_t)diffStart, (uint64_t)diffEnd); D3D12_RANGE range = {diffStart, diffEnd}; m_pDevice->MapDataWrite(res, subres, data, range); if(ref == NULL) { res->AllocShadow(subres, size); ref = res->GetShadow(subres); } // update comparison shadow for next time memcpy(ref, res->GetMap(subres), size); GetResourceManager()->MarkPendingDirty(res->GetResourceID()); } else { RDCDEBUG("Persistent map flush not needed for %llu", res->GetResourceID()); } } for(UINT i = 0; i < NumCommandLists; i++) { SCOPED_SERIALISE_CONTEXT(EXECUTE_CMD_LISTS); Serialise_ExecuteCommandLists(1, ppCommandLists + i); m_QueueRecord->AddChunk(scope.Get()); } } } }
static BOOL WINAPI Hook_CreateProcessW( PFN_CREATE_PROCESS_W realFunc, __in_opt LPCWSTR lpApplicationName, __inout_opt LPWSTR lpCommandLine, __in_opt LPSECURITY_ATTRIBUTES lpProcessAttributes, __in_opt LPSECURITY_ATTRIBUTES lpThreadAttributes, __in BOOL bInheritHandles, __in DWORD dwCreationFlags, __in_opt LPVOID lpEnvironment, __in_opt LPCWSTR lpCurrentDirectory, __in LPSTARTUPINFOW lpStartupInfo, __out LPPROCESS_INFORMATION lpProcessInformation) { PROCESS_INFORMATION dummy; RDCEraseEl(dummy); // not sure if this is valid, but I need the PID so I'll fill in my own struct to ensure that. if(lpProcessInformation == NULL) { lpProcessInformation = &dummy; } else { *lpProcessInformation = dummy; } dwCreationFlags |= CREATE_SUSPENDED; BOOL ret = realFunc(lpApplicationName, lpCommandLine, lpProcessAttributes, lpThreadAttributes, bInheritHandles, dwCreationFlags, lpEnvironment, lpCurrentDirectory, lpStartupInfo, lpProcessInformation); if(ret && RenderDoc::Inst().GetCaptureOptions().HookIntoChildren) { RDCDEBUG("Intercepting CreateProcessW"); bool inject = true; // sanity check to make sure we're not going to go into an infinity loop injecting into // ourselves. if(lpApplicationName) { wstring app = lpApplicationName; app = strlower(app); if(app.find(L"renderdoccmd.exe") != wstring::npos || app.find(L"renderdocui.vshost.exe") != wstring::npos || app.find(L"qrenderdoc.exe") != string::npos || app.find(L"renderdocui.exe") != wstring::npos) { inject = false; } } if(lpCommandLine) { wstring cmd = lpCommandLine; cmd = strlower(cmd); if(cmd.find(L"renderdoccmd.exe") != wstring::npos || cmd.find(L"renderdocui.vshost.exe") != wstring::npos || cmd.find(L"qrenderdoc.exe") != wstring::npos || cmd.find(L"renderdocui.exe") != wstring::npos) { inject = false; } } if(inject) { // inherit logfile and capture options uint32_t ident = RENDERDOC_InjectIntoProcess(lpProcessInformation->dwProcessId, NULL, RenderDoc::Inst().GetLogFile(), &RenderDoc::Inst().GetCaptureOptions(), false); RenderDoc::Inst().AddChildProcess((uint32_t)lpProcessInformation->dwProcessId, ident); } } ResumeThread(lpProcessInformation->hThread); // ensure we clean up after ourselves if(dummy.dwProcessId != 0) { CloseHandle(dummy.hProcess); CloseHandle(dummy.hThread); } return ret; }
bool WrappedID3D12CommandQueue::Serialise_ExecuteCommandLists(UINT NumCommandLists, ID3D12CommandList *const *ppCommandLists) { SERIALISE_ELEMENT(ResourceId, queueId, GetResourceID()); SERIALISE_ELEMENT(UINT, numCmds, NumCommandLists); vector<ResourceId> cmdIds; ID3D12GraphicsCommandList **cmds = m_State >= WRITING ? NULL : new ID3D12GraphicsCommandList *[numCmds]; if(m_State >= WRITING) { for(UINT i = 0; i < numCmds; i++) { D3D12ResourceRecord *record = GetRecord(ppCommandLists[i]); RDCASSERT(record->bakedCommands); if(record->bakedCommands) cmdIds.push_back(record->bakedCommands->GetResourceID()); } } m_pSerialiser->Serialise("ppCommandLists", cmdIds); if(m_State < WRITING) { for(UINT i = 0; i < numCmds; i++) { cmds[i] = cmdIds[i] != ResourceId() ? GetResourceManager()->GetLiveAs<ID3D12GraphicsCommandList>(cmdIds[i]) : NULL; } } const string desc = m_pSerialiser->GetDebugStr(); // debug messages { vector<DebugMessage> debugMessages; if(m_State == WRITING_CAPFRAME) debugMessages = m_pDevice->GetDebugMessages(); SERIALISE_ELEMENT(uint32_t, NumMessages, (uint32_t)debugMessages.size()); for(uint32_t i = 0; i < NumMessages; i++) { ScopedContext msgscope(m_pSerialiser, "DebugMessage", "DebugMessage", 0, false); string msgDesc; if(m_State >= WRITING) msgDesc = debugMessages[i].description.elems; SERIALISE_ELEMENT(uint32_t, Category, debugMessages[i].category); SERIALISE_ELEMENT(uint32_t, Severity, debugMessages[i].severity); SERIALISE_ELEMENT(uint32_t, ID, debugMessages[i].messageID); SERIALISE_ELEMENT(string, Description, msgDesc); if(m_State == READING) { DebugMessage msg; msg.source = eDbgSource_API; msg.category = (DebugMessageCategory)Category; msg.severity = (DebugMessageSeverity)Severity; msg.messageID = ID; msg.description = Description; m_Cmd.m_EventMessages.push_back(msg); } } } ID3D12CommandQueue *real = NULL; if(m_State <= EXECUTING) real = Unwrap(GetResourceManager()->GetLiveAs<ID3D12CommandQueue>(queueId)); if(m_State == READING) { for(uint32_t i = 0; i < numCmds; i++) { if(m_Cmd.m_BakedCmdListInfo[cmdIds[i]].executeEvents.empty() || m_Cmd.m_BakedCmdListInfo[cmdIds[i]].executeEvents[0].patched) { ID3D12CommandList *list = Unwrap(cmds[i]); real->ExecuteCommandLists(1, &list); #if ENABLED(SINGLE_FLUSH_VALIDATE) m_pDevice->GPUSync(); #endif } else { BakedCmdListInfo &info = m_Cmd.m_BakedCmdListInfo[cmdIds[i]]; // execute the first half of the cracked list ID3D12CommandList *list = Unwrap(info.crackedLists[0]); real->ExecuteCommandLists(1, &list); for(size_t c = 1; c < info.crackedLists.size(); c++) { m_pDevice->GPUSync(); // readback the patch buffer and perform patching m_ReplayList->PatchExecuteIndirect(info, uint32_t(c - 1)); // execute next list with this indirect. list = Unwrap(info.crackedLists[c]); real->ExecuteCommandLists(1, &list); } #if ENABLED(SINGLE_FLUSH_VALIDATE) m_pDevice->GPUSync(); #endif } } for(uint32_t i = 0; i < numCmds; i++) { ResourceId cmd = GetResourceManager()->GetLiveID(cmdIds[i]); m_pDevice->ApplyBarriers(m_Cmd.m_BakedCmdListInfo[cmd].barriers); } m_Cmd.AddEvent(desc); // we're adding multiple events, need to increment ourselves m_Cmd.m_RootEventID++; string basename = "ExecuteCommandLists(" + ToStr::Get(numCmds) + ")"; for(uint32_t c = 0; c < numCmds; c++) { string name = StringFormat::Fmt("=> %s[%u]: ID3D12CommandList(%s)", basename.c_str(), c, ToStr::Get(cmdIds[c]).c_str()); // add a fake marker FetchDrawcall draw; draw.name = name; draw.flags |= eDraw_SetMarker; m_Cmd.AddEvent(name); m_Cmd.AddDrawcall(draw, true); m_Cmd.m_RootEventID++; BakedCmdListInfo &cmdBufInfo = m_Cmd.m_BakedCmdListInfo[cmdIds[c]]; // insert the baked command list in-line into this list of notes, assigning new event and // drawIDs m_Cmd.InsertDrawsAndRefreshIDs(cmdIds[c], cmdBufInfo.draw->children); for(size_t e = 0; e < cmdBufInfo.draw->executedCmds.size(); e++) { vector<uint32_t> &submits = m_Cmd.m_Partial[D3D12CommandData::Secondary].cmdListExecs[cmdBufInfo.draw->executedCmds[e]]; for(size_t s = 0; s < submits.size(); s++) submits[s] += m_Cmd.m_RootEventID; } for(size_t i = 0; i < cmdBufInfo.debugMessages.size(); i++) { DebugMessage msg = cmdBufInfo.debugMessages[i]; msg.eventID += m_Cmd.m_RootEventID; m_pDevice->AddDebugMessage(msg); } // only primary command lists can be submitted m_Cmd.m_Partial[D3D12CommandData::Primary].cmdListExecs[cmdIds[c]].push_back( m_Cmd.m_RootEventID); m_Cmd.m_RootEventID += cmdBufInfo.eventCount; m_Cmd.m_RootDrawcallID += cmdBufInfo.drawCount; name = StringFormat::Fmt("=> %s[%u]: Close(%s)", basename.c_str(), c, ToStr::Get(cmdIds[c]).c_str()); draw.name = name; m_Cmd.AddEvent(name); m_Cmd.AddDrawcall(draw, true); m_Cmd.m_RootEventID++; } // account for the outer loop thinking we've added one event and incrementing, // since we've done all the handling ourselves this will be off by one. m_Cmd.m_RootEventID--; } else if(m_State == EXECUTING) { // account for the queue submit event m_Cmd.m_RootEventID++; uint32_t startEID = m_Cmd.m_RootEventID; // advance m_CurEventID to match the events added when reading for(uint32_t c = 0; c < numCmds; c++) { // 2 extra for the virtual labels around the command list m_Cmd.m_RootEventID += 2 + m_Cmd.m_BakedCmdListInfo[cmdIds[c]].eventCount; m_Cmd.m_RootDrawcallID += 2 + m_Cmd.m_BakedCmdListInfo[cmdIds[c]].drawCount; } // same accounting for the outer loop as above m_Cmd.m_RootEventID--; if(numCmds == 0) { // do nothing, don't bother with the logic below } else if(m_Cmd.m_LastEventID <= startEID) { #if ENABLED(VERBOSE_PARTIAL_REPLAY) RDCDEBUG("Queue Submit no replay %u == %u", m_Cmd.m_LastEventID, startEID); #endif } else if(m_Cmd.m_DrawcallCallback && m_Cmd.m_DrawcallCallback->RecordAllCmds()) { #if ENABLED(VERBOSE_PARTIAL_REPLAY) RDCDEBUG("Queue Submit re-recording from %u", m_Cmd.m_RootEventID); #endif vector<ID3D12CommandList *> rerecordedCmds; for(uint32_t c = 0; c < numCmds; c++) { ID3D12CommandList *cmd = m_Cmd.RerecordCmdList(cmdIds[c]); ResourceId rerecord = GetResID(cmd); #if ENABLED(VERBOSE_PARTIAL_REPLAY) RDCDEBUG("Queue Submit fully re-recorded replay of %llu, using %llu", cmdIds[c], rerecord); #endif rerecordedCmds.push_back(Unwrap(cmd)); m_pDevice->ApplyBarriers(m_Cmd.m_BakedCmdListInfo[rerecord].barriers); } #if ENABLED(SINGLE_FLUSH_VALIDATE) for(size_t i = 0; i < rerecordedCmds.size(); i++) { real->ExecuteCommandLists(1, &rerecordedCmds[i]); m_pDevice->GPUSync(); } #else real->ExecuteCommandLists((UINT)rerecordedCmds.size(), &rerecordedCmds[0]); #endif } else if(m_Cmd.m_LastEventID > startEID && m_Cmd.m_LastEventID < m_Cmd.m_RootEventID) { #if ENABLED(VERBOSE_PARTIAL_REPLAY) RDCDEBUG("Queue Submit partial replay %u < %u", m_Cmd.m_LastEventID, m_Cmd.m_RootEventID); #endif uint32_t eid = startEID; vector<ResourceId> trimmedCmdIds; vector<ID3D12CommandList *> trimmedCmds; for(uint32_t c = 0; c < numCmds; c++) { // account for the virtual label at the start of the events here // so it matches up to baseEvent eid++; uint32_t end = eid + m_Cmd.m_BakedCmdListInfo[cmdIds[c]].eventCount; if(eid == m_Cmd.m_Partial[D3D12CommandData::Primary].baseEvent) { ID3D12GraphicsCommandList *list = m_Cmd.RerecordCmdList(cmdIds[c], D3D12CommandData::Primary); ResourceId partial = GetResID(list); #if ENABLED(VERBOSE_PARTIAL_REPLAY) RDCDEBUG("Queue Submit partial replay of %llu at %u, using %llu", cmdIds[c], eid, partial); #endif trimmedCmdIds.push_back(partial); trimmedCmds.push_back(Unwrap(list)); } else if(m_Cmd.m_LastEventID >= end) { #if ENABLED(VERBOSE_PARTIAL_REPLAY) RDCDEBUG("Queue Submit full replay %llu", cmdIds[c]); #endif trimmedCmdIds.push_back(cmdIds[c]); trimmedCmds.push_back(Unwrap(GetResourceManager()->GetLiveAs<ID3D12CommandList>(cmdIds[c]))); } else { #if ENABLED(VERBOSE_PARTIAL_REPLAY) RDCDEBUG("Queue not submitting %llu", cmdIds[c]); #endif } // 1 extra to account for the virtual end command list label (begin is accounted for // above) eid += 1 + m_Cmd.m_BakedCmdListInfo[cmdIds[c]].eventCount; } RDCASSERT(trimmedCmds.size() > 0); #if ENABLED(SINGLE_FLUSH_VALIDATE) for(size_t i = 0; i < trimmedCmds.size(); i++) { real->ExecuteCommandLists(1, &trimmedCmds[i]); m_pDevice->GPUSync(); } #else real->ExecuteCommandLists((UINT)trimmedCmds.size(), &trimmedCmds[0]); #endif for(uint32_t i = 0; i < trimmedCmdIds.size(); i++) { ResourceId cmd = trimmedCmdIds[i]; m_pDevice->ApplyBarriers(m_Cmd.m_BakedCmdListInfo[cmd].barriers); } } else { #if ENABLED(VERBOSE_PARTIAL_REPLAY) RDCDEBUG("Queue Submit full replay %u >= %u", m_Cmd.m_LastEventID, m_Cmd.m_RootEventID); #endif ID3D12CommandList **unwrapped = new ID3D12CommandList *[numCmds]; for(uint32_t i = 0; i < numCmds; i++) unwrapped[i] = Unwrap(cmds[i]); #if ENABLED(SINGLE_FLUSH_VALIDATE) for(UINT i = 0; i < numCmds; i++) { real->ExecuteCommandLists(1, &unwrapped[i]); m_pDevice->GPUSync(); } #else real->ExecuteCommandLists(numCmds, unwrapped); #endif SAFE_DELETE_ARRAY(unwrapped); for(uint32_t i = 0; i < numCmds; i++) { ResourceId cmd = GetResourceManager()->GetLiveID(cmdIds[i]); m_pDevice->ApplyBarriers(m_Cmd.m_BakedCmdListInfo[cmd].barriers); } } } SAFE_DELETE_ARRAY(cmds); return true; }
ReplayCreateStatus GL_CreateReplayDevice(const char *logfile, IReplayDriver **driver) { RDCDEBUG("Creating an OpenGL replay device"); if(glXCreateContextAttribsProc == NULL) { glXGetFuncProc = (PFNGLXGETPROCADDRESSPROC)dlsym(RTLD_NEXT, "glXGetProcAddress"); glXDestroyCtxProc = (PFNGLXDESTROYCONTEXTPROC)dlsym(RTLD_NEXT, "glXDestroyContext"); glXSwapProc = (PFNGLXSWAPBUFFERSPROC)dlsym(RTLD_NEXT, "glXSwapBuffers"); glXChooseFBConfigProc = (PFNGLXCHOOSEFBCONFIGPROC)dlsym(RTLD_NEXT, "glXChooseFBConfig"); glXCreatePbufferProc = (PFNGLXCREATEPBUFFERPROC)dlsym(RTLD_NEXT, "glXCreatePbuffer"); glXDestroyPbufferProc = (PFNGLXDESTROYPBUFFERPROC)dlsym(RTLD_NEXT, "glXDestroyPbuffer"); glXQueryDrawableProc = (PFNGLXQUERYDRAWABLEPROC)dlsym(RTLD_NEXT, "glXQueryDrawable"); if(glXGetFuncProc == NULL || glXDestroyCtxProc == NULL || glXSwapProc == NULL || glXChooseFBConfigProc == NULL || glXCreatePbufferProc == NULL || glXDestroyPbufferProc == NULL || glXQueryDrawableProc == NULL) { RDCERR("Couldn't find required entry points, glXGetProcAddress glXDestroyContext glXSwapBuffers"); return eReplayCreate_APIInitFailed; } glXCreateContextAttribsProc = (PFNGLXCREATECONTEXTATTRIBSARBPROC)glXGetFuncProc((const GLubyte*)"glXCreateContextAttribsARB"); glXMakeContextCurrentProc = (PFNGLXMAKECONTEXTCURRENTPROC)glXGetFuncProc((const GLubyte*)"glXMakeContextCurrent"); if(glXCreateContextAttribsProc == NULL || glXMakeContextCurrentProc == NULL) { RDCERR("Couldn't get glx function addresses, glXCreateContextAttribsARB glXMakeContextCurrent"); return eReplayCreate_APIInitFailed; } } GLInitParams initParams; RDCDriver driverType = RDC_OpenGL; string driverName = "OpenGL"; if(logfile) RenderDoc::Inst().FillInitParams(logfile, driverType, driverName, (RDCInitParams *)&initParams); if(initParams.SerialiseVersion != GLInitParams::GL_SERIALISE_VERSION) { RDCERR("Incompatible OpenGL serialise version, expected %d got %d", GLInitParams::GL_SERIALISE_VERSION, initParams.SerialiseVersion); return eReplayCreate_APIIncompatibleVersion; } int attribs[64] = {0}; int i=0; GLReplay::PreContextInitCounters(); attribs[i++] = GLX_CONTEXT_MAJOR_VERSION_ARB; attribs[i++] = 4; attribs[i++] = GLX_CONTEXT_MINOR_VERSION_ARB; attribs[i++] = 3; attribs[i++] = GLX_CONTEXT_FLAGS_ARB; attribs[i++] = GLX_CONTEXT_DEBUG_BIT_ARB; attribs[i++] = GLX_CONTEXT_PROFILE_MASK_ARB; attribs[i++] = GLX_CONTEXT_CORE_PROFILE_BIT_ARB; Display *dpy = XOpenDisplay(NULL); if(dpy == NULL) { RDCERR("Couldn't open default X display"); return eReplayCreate_APIInitFailed; } // don't need to care about the fb config as we won't be using the default framebuffer (backbuffer) static int visAttribs[] = { 0 }; int numCfgs = 0; GLXFBConfig *fbcfg = glXChooseFBConfigProc(dpy, DefaultScreen(dpy), visAttribs, &numCfgs); if(fbcfg == NULL) { XCloseDisplay(dpy); GLReplay::PostContextShutdownCounters(); RDCERR("Couldn't choose default framebuffer config"); return eReplayCreate_APIInitFailed; } GLXContext ctx = glXCreateContextAttribsProc(dpy, fbcfg[0], 0, true, attribs); if(ctx == NULL) { XFree(fbcfg); XCloseDisplay(dpy); GLReplay::PostContextShutdownCounters(); RDCERR("Couldn't create 4.3 context - RenderDoc requires OpenGL 4.3 availability"); return eReplayCreate_APIHardwareUnsupported; } // don't care about pbuffer properties for same reason as backbuffer int pbAttribs[] = { GLX_PBUFFER_WIDTH, 32, GLX_PBUFFER_HEIGHT, 32, 0 }; GLXPbuffer pbuffer = glXCreatePbufferProc(dpy, fbcfg[0], pbAttribs); XFree(fbcfg); Bool res = glXMakeContextCurrentProc(dpy, pbuffer, pbuffer, ctx); if(!res) { glXDestroyPbufferProc(dpy, pbuffer); glXDestroyCtxProc(dpy, ctx); XFree(fbcfg); XCloseDisplay(dpy); GLReplay::PostContextShutdownCounters(); RDCERR("Couldn't make pbuffer & context current"); return eReplayCreate_APIInitFailed; } PFNGLGETINTEGERVPROC getInt = (PFNGLGETINTEGERVPROC)glXGetFuncProc((const GLubyte *)"glGetIntegerv"); PFNGLGETSTRINGIPROC getStr = (PFNGLGETSTRINGIPROC)glXGetFuncProc((const GLubyte *)"glGetStringi"); if(getInt == NULL || getStr == NULL) { RDCERR("Couldn't get glGetIntegerv (%p) or glGetStringi (%p) entry points", getInt, getStr); glXDestroyPbufferProc(dpy, pbuffer); glXDestroyCtxProc(dpy, ctx); XFree(fbcfg); XCloseDisplay(dpy); GLReplay::PostContextShutdownCounters(); return eReplayCreate_APIInitFailed; } else { // eventually we want to emulate EXT_dsa on replay if it isn't present, but for // now we just require it. bool dsa = false; bool bufstorage = false; if(getStr) RDCLOG("Running GL replay on: %s / %s / %s", getStr(eGL_VENDOR), getStr(eGL_RENDERER), getStr(eGL_VERSION)); GLint numExts = 0; getInt(eGL_NUM_EXTENSIONS, &numExts); for(GLint e=0; e < numExts; e++) { const char *ext = (const char *)getStri(eGL_EXTENSIONS, (GLuint)e); RDCLOG("Extension % 3d: %s", e, ext); if(!strcmp(ext, "GL_EXT_direct_state_access")) dsa = true; if(!strcmp(ext, "GL_ARB_buffer_storage")) bufstorage = true; } if(!dsa) RDCERR("RenderDoc requires EXT_direct_state_access availability, and it is not reported. Try updating your drivers."); if(!bufstorage) RDCERR("RenderDoc requires ARB_buffer_storage availability, and it is not reported. Try updating your drivers."); if(!dsa || !bufstorage) { glXDestroyPbufferProc(dpy, pbuffer); glXDestroyCtxProc(dpy, ctx); XFree(fbcfg); XCloseDisplay(dpy); GLReplay::PostContextShutdownCounters(); return eReplayCreate_APIHardwareUnsupported; } } WrappedOpenGL *gl = new WrappedOpenGL(logfile, GetRealGLFunctions()); gl->Initialise(initParams); RDCLOG("Created device."); GLReplay *replay = gl->GetReplay(); replay->SetProxy(logfile == NULL); GLWindowingData data; data.dpy = dpy; data.ctx = ctx; data.wnd = pbuffer; replay->SetReplayData(data); *driver = (IReplayDriver *)replay; return eReplayCreate_Success; }
void WrappedID3D11DeviceContext::ReplayLog(LogState readType, uint32_t startEventID, uint32_t endEventID, bool partial) { m_State = readType; m_DoStateVerify = true; D3D11ChunkType header = (D3D11ChunkType)m_pSerialiser->PushContext(NULL, 1, false); RDCASSERT(header == CONTEXT_CAPTURE_HEADER); ResourceId id; m_pSerialiser->Serialise("context", id); WrappedID3D11DeviceContext *context = (WrappedID3D11DeviceContext *)m_pDevice->GetResourceManager()->GetLiveResource(id); RDCASSERT(WrappedID3D11DeviceContext::IsAlloc(context) && 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) { ClearMaps(); for(size_t i=0; i < m_pDevice->GetNumDeferredContexts(); i++) { WrappedID3D11DeviceContext *context = m_pDevice->GetDeferredContext(i); context->ClearMaps(); } } m_pDevice->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(); D3D11ChunkType context = (D3D11ChunkType)m_pSerialiser->PushContext(NULL, 1, false); ProcessChunk(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) { m_pDevice->GetFrameRecord().back().drawcallList = m_ParentDrawcall.Bake(); m_ParentDrawcall.children.clear(); int initialSkips = 0; for(auto it=WrappedID3D11Buffer::m_BufferList.begin(); it != WrappedID3D11Buffer::m_BufferList.end(); ++it) m_ResourceUses[it->first]; for(auto it=WrappedID3D11Texture1D::m_TextureList.begin(); it != WrappedID3D11Texture1D::m_TextureList.end(); ++it) m_ResourceUses[it->first]; for(auto it=WrappedID3D11Texture2D::m_TextureList.begin(); it != WrappedID3D11Texture2D::m_TextureList.end(); ++it) m_ResourceUses[it->first]; for(auto it=WrappedID3D11Texture3D::m_TextureList.begin(); it != WrappedID3D11Texture3D::m_TextureList.end(); ++it) m_ResourceUses[it->first]; for(auto it = m_ResourceUses.begin(); it != m_ResourceUses.end(); ++it) { ResourceId id = m_pDevice->GetResourceManager()->GetOriginalID(it->first); if(m_pDevice->GetResourceManager()->GetInitialContents(id) == NULL) continue; RDCDEBUG("Resource %llu", id); if(it->second.empty()) { RDCDEBUG("Never used!"); initialSkips++; } else { bool written = false; for(auto usit = it->second.begin(); usit != it->second.end(); ++usit) { ResourceUsage u = usit->usage; if(u == eUsage_SO || u == eUsage_CS_UAV || u == eUsage_PS_UAV || u == eUsage_OM_DSV || u == eUsage_OM_RTV) { written = true; break; } } if(written) { RDCDEBUG("Written in frame - needs initial state"); } else { RDCDEBUG("Never written to in the frame"); initialSkips++; } } } RDCDEBUG("Can skip %d initial states.", initialSkips); } m_pDevice->GetResourceManager()->MarkInFrame(false); m_State = READING; m_DoStateVerify = false; }