ReplayCreateStatus ReplayRenderer::CreateDevice(const wchar_t *logfile) { RDCLOG("Creating replay device for %ls", logfile); RDCDriver driverType = RDC_Unknown; wstring driverName = L""; auto status = RenderDoc::Inst().FillInitParams(logfile, driverType, driverName, NULL); if(driverType == RDC_Unknown || driverName == L"" || status != eReplayCreate_Success) { RDCERR("Couldn't get device type from log"); return status; } IReplayDriver *driver = NULL; status = RenderDoc::Inst().CreateReplayDriver(driverType, logfile, &driver); if(driver && status == eReplayCreate_Success) { RDCLOG("Created replay driver."); return PostCreateInit(driver); } RDCERR("Couldn't create a replay device :(."); return status; }
TargetControl(Network::Socket *sock, string clientName, bool forceConnection, bool localhost) : m_Socket(sock), m_Local(localhost) { PacketType type; vector<byte> payload; m_PID = 0; { Serialiser ser("", Serialiser::WRITING, false); ser.SerialiseString("", clientName); ser.Serialise("", forceConnection); if(!SendPacket(m_Socket, ePacket_Handshake, ser)) { SAFE_DELETE(m_Socket); return; } } Serialiser *ser = NULL; GetPacket(type, ser); // failed handshaking if(m_Socket == NULL || ser == NULL) return; RDCASSERT(type == ePacket_Handshake || type == ePacket_Busy); if(type == ePacket_Handshake) { ser->Serialise("", m_Target); ser->Serialise("", m_API); ser->Serialise("", m_PID); RDCLOG("Got remote handshake: %s (%s) [%u]", m_Target.c_str(), m_API.c_str(), m_PID); } else if(type == ePacket_Busy) { ser->Serialise("", m_Target); ser->Serialise("", m_API); ser->Serialise("", m_BusyClient); RDCLOG("Got remote busy signal: %s (%s) owned by %s", m_Target.c_str(), m_API.c_str(), m_BusyClient.c_str()); } SAFE_DELETE(ser); }
extern "C" RENDERDOC_API int RENDERDOC_CC RENDERDOC_GetAPI(RENDERDOC_Version version, void **outAPIPointers) { if(outAPIPointers == NULL) { RDCERR("Invalid call to RENDERDOC_GetAPI with NULL outAPIPointers"); return 0; } int ret = 0; int major = 0, minor = 0, patch = 0; #define API_VERSION_HANDLE(enumver, actualver) \ if(version == CONCAT(eRENDERDOC_API_Version_, enumver)) \ { \ CONCAT(Init_, actualver)(); \ *outAPIPointers = &CONCAT(api_, actualver); \ CONCAT(api_, actualver).GetAPIVersion(&major, &minor, &patch); \ ret = 1; \ } API_VERSION_HANDLE(1_0_0, 1_0_1); API_VERSION_HANDLE(1_0_1, 1_0_1); #undef API_VERSION_HANDLE if(ret) { RDCLOG("Initialising RenderDoc API version %d.%d.%d for requested version %d", major, minor, patch, version); return 1; } RDCERR("Unrecognised API version '%d'", version); return 0; }
extern "C" RENDERDOC_API void RENDERDOC_CC RENDERDOC_TriggerExceptionHandler(void *exceptionPtrs, bool32 crashed) { if(RenderDoc::Inst().GetCrashHandler() == NULL) return; if(exceptionPtrs) { RenderDoc::Inst().GetCrashHandler()->WriteMinidump(exceptionPtrs); } else { if(!crashed) { RDCLOG("Writing crash log"); } RenderDoc::Inst().GetCrashHandler()->WriteMinidump(); if(!crashed) { RenderDoc::Inst().RecreateCrashHandler(); } } }
ReplayCreateStatus ReplayRenderer::SetDevice(IReplayDriver *device) { if(device) { RDCLOG("Got replay driver."); return PostCreateInit(device); } RDCERR("Given invalid replay driver."); return eReplayCreate_InternalError; }
static void GPA_LoggingCallback(GPA_Logging_Type messageType, const char *pMessage) { if(messageType == GPA_LOGGING_ERROR) { RDCWARN(pMessage); } else { RDCLOG(pMessage); } }
static int WSAAPI WSACleanup_hook() { // don't let the application murder our sockets with a mismatched WSACleanup() call if(syshooks.m_WSARefCount == 1) { RDCLOG("WSACleanup called with (to the application) no WSAStartup! Ignoring."); SetLastError(WSANOTINITIALISED); return SOCKET_ERROR; } // decrement refcount and call the real thing syshooks.m_WSARefCount--; return syshooks.WSACleanup()(); }
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; }
// 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; }
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; }
bool WrappedVulkan::Serialise_vkEnumeratePhysicalDevices( Serialiser* localSerialiser, VkInstance instance, uint32_t* pPhysicalDeviceCount, VkPhysicalDevice* pPhysicalDevices) { SERIALISE_ELEMENT(ResourceId, inst, GetResID(instance)); SERIALISE_ELEMENT(uint32_t, physIndex, *pPhysicalDeviceCount); SERIALISE_ELEMENT(ResourceId, physId, GetResID(*pPhysicalDevices)); uint32_t memIdxMap[32] = {0}; if(m_State >= WRITING) memcpy(memIdxMap, GetRecord(*pPhysicalDevices)->memIdxMap, sizeof(memIdxMap)); localSerialiser->SerialisePODArray<32>("memIdxMap", memIdxMap); // not used at the moment but useful for reference and might be used // in the future VkPhysicalDeviceProperties physProps; VkPhysicalDeviceMemoryProperties memProps; VkPhysicalDeviceFeatures physFeatures; if(m_State >= WRITING) { ObjDisp(instance)->GetPhysicalDeviceProperties(Unwrap(*pPhysicalDevices), &physProps); ObjDisp(instance)->GetPhysicalDeviceMemoryProperties(Unwrap(*pPhysicalDevices), &memProps); ObjDisp(instance)->GetPhysicalDeviceFeatures(Unwrap(*pPhysicalDevices), &physFeatures); } localSerialiser->Serialise("physProps", physProps); localSerialiser->Serialise("memProps", memProps); localSerialiser->Serialise("physFeatures", physFeatures); VkPhysicalDevice pd = VK_NULL_HANDLE; if(m_State >= WRITING) { pd = *pPhysicalDevices; } else { uint32_t count; VkPhysicalDevice *devices; instance = GetResourceManager()->GetLiveHandle<VkInstance>(inst); VkResult vkr = ObjDisp(instance)->EnumeratePhysicalDevices(Unwrap(instance), &count, NULL); RDCASSERTEQUAL(vkr, VK_SUCCESS); RDCASSERT(count > physIndex); devices = new VkPhysicalDevice[count]; if(physIndex >= m_PhysicalDevices.size()) { m_PhysicalDevices.resize(physIndex+1); m_MemIdxMaps.resize(physIndex+1); } vkr = ObjDisp(instance)->EnumeratePhysicalDevices(Unwrap(instance), &count, devices); RDCASSERTEQUAL(vkr, VK_SUCCESS); // PORTABILITY match up physical devices to those available on replay pd = devices[physIndex]; for(size_t i=0; i < m_PhysicalDevices.size(); i++) { // physical devices might be re-created inside EnumeratePhysicalDevices every time, so // we need to re-wrap any previously enumerated physical devices if(m_PhysicalDevices[i] != VK_NULL_HANDLE) { RDCASSERTNOTEQUAL(i, physIndex); GetWrapped(m_PhysicalDevices[i])->RewrapObject(devices[i]); } } SAFE_DELETE_ARRAY(devices); GetResourceManager()->WrapResource(instance, pd); GetResourceManager()->AddLiveResource(physId, pd); m_PhysicalDevices[physIndex] = pd; uint32_t *storedMap = new uint32_t[32]; memcpy(storedMap, memIdxMap, sizeof(memIdxMap)); m_MemIdxMaps[physIndex] = storedMap; RDCLOG("Captured log describes physical device %u:", physIndex); RDCLOG(" - %s (ver %x) - %04x:%04x", physProps.deviceName, physProps.driverVersion, physProps.vendorID, physProps.deviceID); ObjDisp(pd)->GetPhysicalDeviceProperties(Unwrap(pd), &physProps); ObjDisp(pd)->GetPhysicalDeviceMemoryProperties(Unwrap(pd), &memProps); ObjDisp(pd)->GetPhysicalDeviceFeatures(Unwrap(pd), &physFeatures); RDCLOG("Replaying on physical device %u:", physIndex); RDCLOG(" - %s (ver %x) - %04x:%04x", physProps.deviceName, physProps.driverVersion, physProps.vendorID, physProps.deviceID); } return true; }
VkResult WrappedVulkan::vkCreateInstance( const VkInstanceCreateInfo* pCreateInfo, const VkAllocationCallbacks* pAllocator, VkInstance* pInstance) { RDCASSERT(pCreateInfo); // don't support any extensions for this createinfo RDCASSERT(pCreateInfo->pApplicationInfo == NULL || pCreateInfo->pApplicationInfo->pNext == NULL); VkLayerInstanceCreateInfo *layerCreateInfo = (VkLayerInstanceCreateInfo *)pCreateInfo->pNext; // step through the chain of pNext until we get to the link info while(layerCreateInfo && (layerCreateInfo->sType != VK_STRUCTURE_TYPE_LOADER_INSTANCE_CREATE_INFO || layerCreateInfo->function != VK_LAYER_LINK_INFO) ) { layerCreateInfo = (VkLayerInstanceCreateInfo *)layerCreateInfo->pNext; } RDCASSERT(layerCreateInfo); PFN_vkGetInstanceProcAddr gpa = layerCreateInfo->u.pLayerInfo->pfnNextGetInstanceProcAddr; // move chain on for next layer layerCreateInfo->u.pLayerInfo = layerCreateInfo->u.pLayerInfo->pNext; PFN_vkCreateInstance createFunc = (PFN_vkCreateInstance)gpa(VK_NULL_HANDLE, "vkCreateInstance"); VkInstanceCreateInfo modifiedCreateInfo; modifiedCreateInfo = *pCreateInfo; const char **addedExts = new const char *[modifiedCreateInfo.enabledExtensionCount+1]; for(uint32_t i=0; i < modifiedCreateInfo.enabledExtensionCount; i++) addedExts[i] = modifiedCreateInfo.ppEnabledExtensionNames[i]; if(RenderDoc::Inst().GetCaptureOptions().APIValidation) addedExts[modifiedCreateInfo.enabledExtensionCount++] = VK_EXT_DEBUG_REPORT_EXTENSION_NAME; modifiedCreateInfo.ppEnabledExtensionNames = addedExts; VkResult ret = createFunc(&modifiedCreateInfo, pAllocator, pInstance); m_Instance = *pInstance; InitInstanceTable(m_Instance, gpa); GetResourceManager()->WrapResource(m_Instance, m_Instance); *pInstance = m_Instance; // should only be called during capture RDCASSERT(m_State >= WRITING); m_InitParams.Set(pCreateInfo, GetResID(m_Instance)); VkResourceRecord *record = GetResourceManager()->AddResourceRecord(m_Instance); record->instDevInfo = new InstanceDeviceInfo(); #undef CheckExt #define CheckExt(name) if(!strcmp(modifiedCreateInfo.ppEnabledExtensionNames[i], STRINGIZE(name))) { record->instDevInfo->name = true; } for(uint32_t i=0; i < modifiedCreateInfo.enabledExtensionCount; i++) { CheckInstanceExts(); } delete[] addedExts; InitInstanceExtensionTables(m_Instance); RenderDoc::Inst().AddDeviceFrameCapturer(LayerDisp(m_Instance), this); m_DbgMsgCallback = VK_NULL_HANDLE; m_PhysicalDevice = VK_NULL_HANDLE; m_Device = VK_NULL_HANDLE; m_QueueFamilyIdx = ~0U; m_Queue = VK_NULL_HANDLE; m_InternalCmds.Reset(); if(RenderDoc::Inst().GetCaptureOptions().APIValidation && ObjDisp(m_Instance)->CreateDebugReportCallbackEXT) { VkDebugReportCallbackCreateInfoEXT debugInfo = {}; debugInfo.sType = VK_STRUCTURE_TYPE_DEBUG_REPORT_CREATE_INFO_EXT; debugInfo.pNext = NULL; debugInfo.pfnCallback = &DebugCallbackStatic; debugInfo.pUserData = this; debugInfo.flags = VK_DEBUG_REPORT_WARNING_BIT_EXT|VK_DEBUG_REPORT_PERFORMANCE_WARNING_BIT_EXT|VK_DEBUG_REPORT_ERROR_BIT_EXT; ObjDisp(m_Instance)->CreateDebugReportCallbackEXT(Unwrap(m_Instance), &debugInfo, NULL, &m_DbgMsgCallback); } if(ret == VK_SUCCESS) { RDCLOG("Initialised capture layer in Vulkan instance."); } return ret; }
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; }
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; }
extern "C" RENDERDOC_API void RENDERDOC_CC RENDERDOC_LogText(const char *text) { RDCLOG("%s", text); }
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; }
static bool InitDbgHelp() { static bool doinit = true; static bool ret = false; if(!doinit) return ret; doinit = false; HMODULE module = NULL; // can't reliably co-exist with dbghelp already being used in the process if(GetModuleHandleA("dbghelp.dll") != NULL) { RDCLOG( "dbghelp.dll is already loaded, can't guarantee thread-safety against application use. " "Callstack collection disabled"); ret = false; return false; } else { wchar_t path[MAX_PATH] = {0}; GetModuleFileNameW(GetModuleHandleA(STRINGIZE(RDOC_DLL_FILE) ".dll"), path, MAX_PATH - 1); wchar_t *slash = wcsrchr(path, '\\'); if(slash) { *slash = 0; } else { slash = wcsrchr(path, '/'); if(slash == 0) { ret = false; return false; } *slash = 0; } #if ENABLED(RDOC_X64) wcscat_s(path, L"/dbghelp.dll"); #else wcscat_s(path, L"/dbghelp.dll"); #endif module = LoadLibraryW(path); } if(!module) { RDCWARN("Couldn't open dbghelp.dll"); ret = false; return false; } dynSymInitializeW = (PSYMINITIALIZEW)GetProcAddress(module, "SymInitializeW"); dynSymEnumerateModules64W = (PSYMENUMERATEMODULES64W)GetProcAddress(module, "SymEnumerateModulesW64"); dynSymRefreshModuleList = (PSYMREFRESHMODULELIST)GetProcAddress(module, "SymRefreshModuleList"); dynSymGetModuleInfo64W = (PSYMGETMODULEINFO64W)GetProcAddress(module, "SymGetModuleInfoW64"); dynSymFindFileInPathW = (PSYMFINDFILEINPATHW)GetProcAddress(module, "SymFindFileInPathW"); if(!dynSymInitializeW || !dynSymRefreshModuleList || !dynSymEnumerateModules64W || !dynSymGetModuleInfo64W) { RDCERR("Couldn't get some dbghelp function"); ret = false; return ret; } dynSymInitializeW(GetCurrentProcess(), L".", TRUE); HMODULE hModule = NULL; GetModuleHandleEx( GET_MODULE_HANDLE_EX_FLAG_FROM_ADDRESS | GET_MODULE_HANDLE_EX_FLAG_UNCHANGED_REFCOUNT, (LPCTSTR)&dllLocator, &hModule); if(hModule != NULL) { MODULEINFO modinfo = {0}; BOOL result = GetModuleInformation(GetCurrentProcess(), hModule, &modinfo, sizeof(modinfo)); if(result != FALSE) { renderdocBase = modinfo.lpBaseOfDll; renderdocSize = modinfo.SizeOfImage; } } if(RenderDoc::Inst().IsReplayApp()) { DIA2::Init(); } ret = true; return ret; }
static void SetLogFilePathTemplate(const char *logfile) { RDCLOG("Using logfile %s", logfile); RenderDoc::Inst().SetLogFile(logfile); }
Win32CallstackResolver::Win32CallstackResolver(char *moduleDB, size_t DBSize, string pdbSearchPaths, volatile bool *killSignal) { wstring configPath = StringFormat::UTF82Wide(FileIO::GetAppFolderFilename("config.ini")); { FILE *f = NULL; _wfopen_s(&f, configPath.c_str(), L"a"); if(f) fclose(f); } wchar_t inputBuf[2048]; GetPrivateProfileStringW(L"renderdoc", L"ignores", NULL, &inputBuf[0], 2048, configPath.c_str()); wstring ignores = inputBuf; split(ignores, pdbIgnores, L';'); wstring widepdbsearch = StringFormat::UTF82Wide(pdbSearchPaths); split(widepdbsearch, pdbRememberedPaths, L';'); pdblocateProcess = NULL; pdblocatePipe = NULL; OpenPdblocateHandle(); if(memcmp(moduleDB, "WN32CALL", 8)) { RDCWARN("Can't load callstack resolve for this log. Possibly from another platform?"); return; } char *chunks = moduleDB + 8; char *end = chunks + DBSize - 8; EnumModChunk *chunk = (EnumModChunk *)(chunks); WCHAR *modName = (WCHAR *)(chunks+sizeof(EnumModChunk)); if(pdblocatePipe == NULL) return; // loop over all our modules for(; chunks < end; chunks += sizeof(EnumModChunk)+(chunk->imageNameLen)*sizeof(WCHAR) ) { chunk = (EnumModChunk *)chunks; modName = (WCHAR *)(chunks+sizeof(EnumModChunk)); if(killSignal && *killSignal) break; Module m; m.name = modName; m.base = chunk->base; m.size = chunk->size; m.moduleId = 0; if(find(pdbIgnores.begin(), pdbIgnores.end(), m.name) != pdbIgnores.end()) { RDCWARN("Not attempting to get symbols for %ls", m.name.c_str()); modules.push_back(m); continue; } // get default pdb (this also looks up symbol server etc) // relies on pdblocate. Always done in unicode std::wstring defaultPdb = LookupModule(modName, chunk->guid, chunk->age); // strip newline if(defaultPdb != L"" && defaultPdb[defaultPdb.length()-1] == '\n') defaultPdb.pop_back(); // if we didn't even get a default pdb we'll have to prompt first time through bool failed = false; if(defaultPdb == L"") { defaultPdb = strlower(basename(m.name)); size_t it = defaultPdb.find(L".dll"); if(it != wstring::npos) { defaultPdb[it+1] = L'p'; defaultPdb[it+2] = L'd'; defaultPdb[it+3] = L'b'; } it = defaultPdb.find(L".exe"); if(it != wstring::npos) { defaultPdb[it+1] = L'p'; defaultPdb[it+2] = L'd'; defaultPdb[it+3] = L'b'; } failed = true; } std::wstring pdbName = defaultPdb; int fallbackIdx = -1; while(m.moduleId == 0) { if(failed) { fallbackIdx++; // try one of the folders we've been given, just in case the symbols // are there if(fallbackIdx < (int)pdbRememberedPaths.size()) { pdbName = pdbRememberedPaths[fallbackIdx] + L"\\" + basename(pdbName); } else { pdbName = dirname(defaultPdb) + L"\\" + basename(defaultPdb); // prompt for new pdbName, unless it's renderdoc or dbghelp if(pdbName.find(L"renderdoc.") != wstring::npos || pdbName.find(L"dbghelp.") != wstring::npos) pdbName = L""; else pdbName = pdbBrowse(pdbName); // user cancelled, just don't load this pdb if(pdbName == L"") break; } failed = false; } m.moduleId = GetModuleID(pdbName, chunk->guid, chunk->age); if(m.moduleId == 0) { failed = true; } else { if(fallbackIdx >= (int)pdbRememberedPaths.size()) { wstring dir = dirname(pdbName); if(find(pdbRememberedPaths.begin(), pdbRememberedPaths.end(), dir) == pdbRememberedPaths.end()) { pdbRememberedPaths.push_back(dir); } } } } // didn't load the pdb? go to the next module. if (m.moduleId == 0) { modules.push_back(m); // still add the module, with 0 module id RDCWARN("Couldn't get symbols for %ls", m.name.c_str()); // silently ignore renderdoc.dll and dbghelp.dll without asking to permanently ignore if(m.name.find(L"renderdoc") != wstring::npos || m.name.find(L"dbghelp") != wstring::npos) continue; wchar_t text[1024]; wsprintf(text, L"Do you want to permanently ignore this file?\nPath: %ls", m.name.c_str()); int ret = MessageBoxW(NULL, text, L"Ignore this pdb?", MB_YESNO); if(ret == IDYES) pdbIgnores.push_back(m.name); continue; } SetModuleBaseAddress(m.moduleId, chunk->base); RDCLOG("Loaded Symbols for %ls", m.name.c_str()); modules.push_back(m); } sort( pdbIgnores.begin(), pdbIgnores.end() ); pdbIgnores.erase( unique( pdbIgnores.begin(), pdbIgnores.end() ), pdbIgnores.end() ); merge(pdbIgnores, ignores, L';'); WritePrivateProfileStringW(L"renderdoc", L"ignores", ignores.c_str(), configPath.c_str()); }
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; }
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; }
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); msg->Type = eRemoteMsg_Noop; return; } else if(type == ePacket_Busy) { string existingClient; ser->Serialise("", existingClient); SAFE_DELETE(ser); SAFE_DELETE(m_Socket); RDCLOG("Got busy signal: '%s", 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_NewChild) { msg->Type = eRemoteMsg_NewChild; ser->Serialise("", msg->NewChild.PID); ser->Serialise("", msg->NewChild.ident); RDCLOG("Got a new child process: %u %u", msg->NewChild.PID, msg->NewChild.ident); SAFE_DELETE(ser); return; } else if(type == ePacket_NewCapture) { msg->Type = eRemoteMsg_NewCapture; ser->Serialise("", msg->NewCapture.ID); ser->Serialise("", msg->NewCapture.timestamp); string path; ser->Serialise("", path); msg->NewCapture.localpath = path; if(!m_Local) msg->NewCapture.localpath = ""; 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: %s", m_API.c_str()); SAFE_DELETE(ser); return; } } SAFE_DELETE(ser); msg->Type = eRemoteMsg_Noop; }
extern "C" RENDERDOC_API void RENDERDOC_CC RENDERDOC_SetLogFile(const char *logfile) { RDCLOG("Using logfile %s", logfile); RenderDoc::Inst().SetLogFile(logfile); }
void VulkanReplay::GetOutputWindowDimensions(uint64_t id, int32_t &w, int32_t &h) { w = 500; h = 500; // FIXME RDCLOG("VulkanReplay::GetOutputWindowDimensions: %i, %i", w, h); }
extern "C" RENDERDOC_API void RENDERDOC_CC RENDERDOC_SetCaptureOptions(const CaptureOptions *opts) { RDCLOG("Setting capture options"); RenderDoc::Inst().SetCaptureOptions(opts); }
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 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()); } } } }