From b4a3fb449050f8b2234ae102a64ef7e0f3c0f4cc Mon Sep 17 00:00:00 2001 From: baldurk Date: Tue, 27 Aug 2019 15:27:53 +0100 Subject: [PATCH] Fetch debug messages at replay time if option is enabled --- renderdoc/driver/d3d11/d3d11_context_wrap.cpp | 10 + renderdoc/driver/d3d11/d3d11_device.cpp | 34 ++- renderdoc/driver/d3d11/d3d11_device.h | 1 + renderdoc/driver/d3d11/d3d11_replay.cpp | 38 ++- .../driver/d3d12/d3d12_command_queue_wrap.cpp | 4 + renderdoc/driver/d3d12/d3d12_commands.cpp | 5 + renderdoc/driver/d3d12/d3d12_device.cpp | 39 ++- renderdoc/driver/d3d12/d3d12_device.h | 1 + renderdoc/driver/d3d12/d3d12_replay.cpp | 26 +- renderdoc/driver/gl/cgl_platform.cpp | 2 +- renderdoc/driver/gl/egl_platform.cpp | 15 +- renderdoc/driver/gl/gl_common.h | 4 +- renderdoc/driver/gl/gl_driver.cpp | 27 +- renderdoc/driver/gl/gl_replay.cpp | 2 +- renderdoc/driver/gl/glx_platform.cpp | 13 +- renderdoc/driver/gl/wgl_platform.cpp | 13 +- renderdoc/driver/vulkan/vk_common.h | 14 -- renderdoc/driver/vulkan/vk_core.cpp | 231 +++++++++--------- renderdoc/driver/vulkan/vk_core.h | 3 + renderdoc/driver/vulkan/vk_manager.cpp | 8 +- .../vulkan/wrappers/vk_device_funcs.cpp | 35 ++- 21 files changed, 348 insertions(+), 177 deletions(-) diff --git a/renderdoc/driver/d3d11/d3d11_context_wrap.cpp b/renderdoc/driver/d3d11/d3d11_context_wrap.cpp index a73f6974f..8edf71a63 100644 --- a/renderdoc/driver/d3d11/d3d11_context_wrap.cpp +++ b/renderdoc/driver/d3d11/d3d11_context_wrap.cpp @@ -3762,6 +3762,16 @@ void WrappedID3D11DeviceContext::Serialise_DebugMessages(SerialiserType &ser) SERIALISE_ELEMENT(DebugMessages); + // if we're using replay-time API validation, fetch messages at replay time and ignore any + // serialised ones + if(ser.IsReading() && IsLoading(m_State) && m_pDevice->GetReplayOptions().apiValidation) + { + if(GetType() == D3D11_DEVICE_CONTEXT_IMMEDIATE) + DebugMessages = m_pDevice->GetDebugMessages(); + else + DebugMessages.clear(); + } + // hide empty sets of messages. if(ser.IsReading() && DebugMessages.empty()) ser.Hidden(); diff --git a/renderdoc/driver/d3d11/d3d11_device.cpp b/renderdoc/driver/d3d11/d3d11_device.cpp index 9033778ae..cd80e1133 100644 --- a/renderdoc/driver/d3d11/d3d11_device.cpp +++ b/renderdoc/driver/d3d11/d3d11_device.cpp @@ -786,13 +786,26 @@ std::vector WrappedID3D11Device::GetDebugMessages() { std::vector ret; - // if reading, m_DebugMessages will contain all the messages (we - // don't try and fetch anything from the API). If writing, - // m_DebugMessages will contain any manually-added messages. - ret.swap(m_DebugMessages); + if(IsActiveReplaying(m_State)) + { + // once we're active replaying, m_DebugMessages will contain all the messages from loading + // (either from the captured serialised messages, or from ourselves during replay). + ret.swap(m_DebugMessages); - if(IsReplayMode(m_State)) return ret; + } + + if(IsCaptureMode(m_State)) + { + // add any manually-added messages before fetching those from the API + ret.swap(m_DebugMessages); + } + + // during loading only try and fetch messages if we're doing that deliberately during replay + if(IsLoading(m_State) && !m_ReplayOptions.apiValidation) + return ret; + + // during capture, and during loading if the option is enabled, we fetch messages from the API if(!m_pInfoQueue) return ret; @@ -1158,8 +1171,19 @@ ReplayStatus WrappedID3D11Device::ReadLogInitialisation(RDCFile *rdc, bool store m_pImmediateContext->SetFrameReader(new StreamReader(reader, frameDataSize)); if(!IsStructuredExporting(m_State)) + { + std::vector savedDebugMessages; + + // save any debug messages we built up + savedDebugMessages.swap(m_DebugMessages); + GetResourceManager()->ApplyInitialContents(); + // restore saved messages - which implicitly discards any generated while applying initial + // contents + savedDebugMessages.swap(m_DebugMessages); + } + ReplayStatus status = m_pImmediateContext->ReplayLog(m_State, 0, 0, false); if(status != ReplayStatus::Succeeded) diff --git a/renderdoc/driver/d3d11/d3d11_device.h b/renderdoc/driver/d3d11/d3d11_device.h index db5526f77..1c38b7eb7 100644 --- a/renderdoc/driver/d3d11/d3d11_device.h +++ b/renderdoc/driver/d3d11/d3d11_device.h @@ -401,6 +401,7 @@ public: m_SectionVersion = sectionVersion; m_ReplayOptions = opts; } + const ReplayOptions &GetReplayOptions() { return m_ReplayOptions; } uint64_t GetLogVersion() { return m_SectionVersion; } virtual ~WrappedID3D11Device(); diff --git a/renderdoc/driver/d3d11/d3d11_replay.cpp b/renderdoc/driver/d3d11/d3d11_replay.cpp index 893f3010e..2b6df5863 100644 --- a/renderdoc/driver/d3d11/d3d11_replay.cpp +++ b/renderdoc/driver/d3d11/d3d11_replay.cpp @@ -3846,12 +3846,18 @@ ReplayStatus D3D11_CreateReplayDevice(RDCFile *rdc, const ReplayOptions &opts, I UINT flags = initParams.Flags; + // we control the debug flag ourselves + flags &= ~D3D11_CREATE_DEVICE_DEBUG; + #if ENABLED(RDOC_DEVEL) // in development builds, always enable debug layer during replay flags |= D3D11_CREATE_DEVICE_DEBUG; #else - // in release builds, never enable it - flags &= ~D3D11_CREATE_DEVICE_DEBUG; + // in release builds, only enable it if forced by replay options + if(opts.apiValidation) + flags |= D3D11_CREATE_DEVICE_DEBUG; + else + flags &= ~D3D11_CREATE_DEVICE_DEBUG; #endif // we should now be set up to try creating feature level 11 devices either with a selected @@ -3862,6 +3868,7 @@ ReplayStatus D3D11_CreateReplayDevice(RDCFile *rdc, const ReplayOptions &opts, I // try first with the adapter, then if we were using a specific adapter try without, then last try // with WARP // within that, try to create a device at descending feature levels + // for each attempt, if the debug layer is enabled try without it for(int adapterPass = 0; adapterPass < 3; adapterPass++) { @@ -3908,9 +3915,32 @@ ReplayStatus D3D11_CreateReplayDevice(RDCFile *rdc, const ReplayOptions &opts, I if(SUCCEEDED(hr) && device) break; - RDCLOG("Device creation failed, %s", ToStr(hr).c_str()); - SAFE_RELEASE(device); + +// in release try to fall back to a non-debug device +#if ENABLED(RDOC_RELEASE) + if(flags & D3D11_CREATE_DEVICE_DEBUG) + { + UINT noDebugFlags = flags & ~D3D11_CREATE_DEVICE_DEBUG; + + HRESULT hr2 = CreateDeviceAndSwapChain(adapter, driverType, NULL, noDebugFlags, + featureLevelSubset, numFeatureLevels, + D3D11_SDK_VERSION, NULL, NULL, &device, NULL, NULL); + + // if we can manage to create it without debug active, do that since it's extremely unlikely + // that any other configuration will have better luck with debug active. + if(SUCCEEDED(hr2) && device) + { + RDCLOG( + "Device creation failed with validation active - check that you have the " + "SDK installed or Windows feature enabled to get the D3D debug layers."); + + break; + } + } +#endif + + RDCLOG("Device creation failed, %s", ToStr(hr).c_str()); } if(SUCCEEDED(hr) && device) diff --git a/renderdoc/driver/d3d12/d3d12_command_queue_wrap.cpp b/renderdoc/driver/d3d12/d3d12_command_queue_wrap.cpp index d33c09b91..fd312e746 100644 --- a/renderdoc/driver/d3d12/d3d12_command_queue_wrap.cpp +++ b/renderdoc/driver/d3d12/d3d12_command_queue_wrap.cpp @@ -92,6 +92,10 @@ bool WrappedID3D12CommandQueue::Serialise_ExecuteCommandLists(SerialiserType &se if(ser.IsReading() && IsLoading(m_State)) { + // if we're using replay-time API validation, ignore messages from capture time + if(m_pDevice->GetReplayOptions().apiValidation) + DebugMessages.clear(); + for(const DebugMessage &msg : DebugMessages) m_Cmd.m_EventMessages.push_back(msg); } diff --git a/renderdoc/driver/d3d12/d3d12_commands.cpp b/renderdoc/driver/d3d12/d3d12_commands.cpp index d4d7fa473..3a9d99c73 100644 --- a/renderdoc/driver/d3d12/d3d12_commands.cpp +++ b/renderdoc/driver/d3d12/d3d12_commands.cpp @@ -1415,6 +1415,11 @@ void D3D12CommandData::AddEvent() apievent.callstack = m_ChunkMetadata.callstack; + // if we're using replay-time debug messages, fetch them now since we can do better to correlate + // to events on replay + if(m_pDevice->GetReplayOptions().apiValidation) + m_EventMessages = m_pDevice->GetDebugMessages(); + for(size_t i = 0; i < m_EventMessages.size(); i++) m_EventMessages[i].eventId = apievent.eventId; diff --git a/renderdoc/driver/d3d12/d3d12_device.cpp b/renderdoc/driver/d3d12/d3d12_device.cpp index c424afea4..91ccfbe06 100644 --- a/renderdoc/driver/d3d12/d3d12_device.cpp +++ b/renderdoc/driver/d3d12/d3d12_device.cpp @@ -2072,13 +2072,26 @@ std::vector WrappedID3D12Device::GetDebugMessages() { std::vector ret; - // if reading, m_DebugMessages will contain all the messages (we - // don't try and fetch anything from the API). If writing, - // m_DebugMessages will contain any manually-added messages. - ret.swap(m_DebugMessages); + if(IsActiveReplaying(m_State)) + { + // once we're active replaying, m_DebugMessages will contain all the messages from loading + // (either from the captured serialised messages, or from ourselves during replay). + ret.swap(m_DebugMessages); - if(IsReplayMode(m_State)) return ret; + } + + if(IsCaptureMode(m_State)) + { + // add any manually-added messages before fetching those from the API + ret.swap(m_DebugMessages); + } + + // during loading only try and fetch messages if we're doing that deliberately during replay + if(IsLoading(m_State) && !m_ReplayOptions.apiValidation) + return ret; + + // during capture, and during loading if the option is enabled, we fetch messages from the API if(!m_pInfoQueue) return ret; @@ -2144,7 +2157,10 @@ std::vector WrappedID3D12Device::GetDebugMessages() msg.messageID = (uint32_t)message->ID; msg.description = std::string(message->pDescription); - ret.push_back(msg); + // during capture add all messages. Otherwise only add this message if it's different to the + // last one - due to our replay with real and cracked lists we get many duplicated messages + if(!IsLoading(m_State) || ret.empty() || !(ret.back() == msg)) + ret.push_back(msg); SAFE_DELETE_ARRAY(msgbuf); } @@ -2928,8 +2944,19 @@ ReplayStatus WrappedID3D12Device::ReadLogInitialisation(RDCFile *rdc, bool store m_Queue->SetFrameReader(new StreamReader(reader, frameDataSize)); if(!IsStructuredExporting(m_State)) + { + std::vector savedDebugMessages; + + // save any debug messages we built up + savedDebugMessages.swap(m_DebugMessages); + ApplyInitialContents(); + // restore saved messages - which implicitly discards any generated while applying initial + // contents + savedDebugMessages.swap(m_DebugMessages); + } + ReplayStatus status = m_Queue->ReplayLog(m_State, 0, 0, false); if(status != ReplayStatus::Succeeded) diff --git a/renderdoc/driver/d3d12/d3d12_device.h b/renderdoc/driver/d3d12/d3d12_device.h index 014fa6cf4..37373b0d2 100644 --- a/renderdoc/driver/d3d12/d3d12_device.h +++ b/renderdoc/driver/d3d12/d3d12_device.h @@ -513,6 +513,7 @@ public: m_SectionVersion = sectionVersion; m_ReplayOptions = opts; } + const ReplayOptions &GetReplayOptions() { return m_ReplayOptions; } uint64_t GetLogVersion() { return m_SectionVersion; } CaptureState GetState() { return m_State; } D3D12Replay *GetReplay() { return &m_Replay; } diff --git a/renderdoc/driver/d3d12/d3d12_replay.cpp b/renderdoc/driver/d3d12/d3d12_replay.cpp index 4e070cbcc..52f599d72 100644 --- a/renderdoc/driver/d3d12/d3d12_replay.cpp +++ b/renderdoc/driver/d3d12/d3d12_replay.cpp @@ -3757,20 +3757,30 @@ ReplayStatus D3D12_CreateReplayDevice(RDCFile *rdc, const ReplayOptions &opts, I ChooseBestMatchingAdapter(GraphicsAPI::D3D12, factory, initParams.AdapterDesc, opts, NULL, &adapter); - bool EnableDebugLayer = false; + bool debugLayerEnabled = false; RDCLOG("Creating D3D12 replay device, minimum feature level %s", ToStr(initParams.MinimumFeatureLevel).c_str()); -#if ENABLED(RDOC_DEVEL) - // in development builds, always enable debug layer during replay - EnableDebugLayer = EnableD3D12DebugLayer(); + bool shouldEnableDebugLayer = opts.apiValidation; - RDCLOG( - "Development RenderDoc builds require D3D debug layers available, " - "ensure you have the windows SDK or windows feature needed."); +// in development builds, always enable debug layer during replay +#if ENABLED(RDOC_DEVEL) + shouldEnableDebugLayer = true; #endif + if(shouldEnableDebugLayer) + { + debugLayerEnabled = EnableD3D12DebugLayer(); + + if(!debugLayerEnabled) + { + RDCLOG( + "Enabling the D3D debug layers failed, " + "ensure you have the windows SDK or windows feature needed."); + } + } + ID3D12Device *dev = NULL; hr = createDevice(adapter, initParams.MinimumFeatureLevel, __uuidof(ID3D12Device), (void **)&dev); @@ -3795,7 +3805,7 @@ ReplayStatus D3D12_CreateReplayDevice(RDCFile *rdc, const ReplayOptions &opts, I return ReplayStatus::APIHardwareUnsupported; } - WrappedID3D12Device *wrappedDev = new WrappedID3D12Device(dev, initParams, EnableDebugLayer); + WrappedID3D12Device *wrappedDev = new WrappedID3D12Device(dev, initParams, debugLayerEnabled); wrappedDev->SetInitParams(initParams, ver, opts); RDCLOG("Created device."); diff --git a/renderdoc/driver/gl/cgl_platform.cpp b/renderdoc/driver/gl/cgl_platform.cpp index f9b4bd61c..dd528fe31 100644 --- a/renderdoc/driver/gl/cgl_platform.cpp +++ b/renderdoc/driver/gl/cgl_platform.cpp @@ -212,7 +212,7 @@ class CGLPlatform : public GLPlatform return ret; } - ReplayStatus InitialiseAPI(GLWindowingData &replayContext, RDCDriver api) + ReplayStatus InitialiseAPI(GLWindowingData &replayContext, RDCDriver api, bool debug) { RDCASSERT(api == RDCDriver::OpenGL); diff --git a/renderdoc/driver/gl/egl_platform.cpp b/renderdoc/driver/gl/egl_platform.cpp index 176c35445..71753c1a8 100644 --- a/renderdoc/driver/gl/egl_platform.cpp +++ b/renderdoc/driver/gl/egl_platform.cpp @@ -134,12 +134,16 @@ class EGLPlatform : public GLPlatform EGLDisplay eglDisplay = EGL.GetDisplay(EGL_DEFAULT_DISPLAY); RDCASSERT(eglDisplay); - return CreateWindowingData(eglDisplay, share_context.ctx, win); + return CreateWindowingData(eglDisplay, share_context.ctx, win, false); } GLWindowingData CreateWindowingData(EGLDisplay eglDisplay, EGLContext share_ctx, - EGLNativeWindowType window) + EGLNativeWindowType window, bool debug) { +#if ENABLED(RDOC_DEVEL) + debug = true; +#endif + GLWindowingData ret; ret.egl_dpy = eglDisplay; ret.egl_ctx = NULL; @@ -176,6 +180,9 @@ class EGLPlatform : public GLPlatform // first we try with the debug bit set, then if that fails we try without debug for(int debugPass = 0; debugPass < 2; debugPass++) { + if(!debug && debugPass == 0) + continue; + // don't change this ar ray without changing indices in the loop below EGLint verAttribs[] = { EGL_CONTEXT_MAJOR_VERSION_KHR, @@ -271,7 +278,7 @@ class EGLPlatform : public GLPlatform } bool PopulateForReplay() { return EGL.PopulateForReplay(); } - ReplayStatus InitialiseAPI(GLWindowingData &replayContext, RDCDriver api) + ReplayStatus InitialiseAPI(GLWindowingData &replayContext, RDCDriver api, bool debug) { // we only support replaying GLES through EGL RDCASSERT(api == RDCDriver::OpenGLES); @@ -288,7 +295,7 @@ class EGLPlatform : public GLPlatform int major, minor; EGL.Initialize(eglDisplay, &major, &minor); - replayContext = CreateWindowingData(eglDisplay, EGL_NO_CONTEXT, 0); + replayContext = CreateWindowingData(eglDisplay, EGL_NO_CONTEXT, 0, debug); if(!replayContext.ctx) { diff --git a/renderdoc/driver/gl/gl_common.h b/renderdoc/driver/gl/gl_common.h index e6fbdf83b..2f5729ab7 100644 --- a/renderdoc/driver/gl/gl_common.h +++ b/renderdoc/driver/gl/gl_common.h @@ -289,7 +289,7 @@ struct GLPlatform // for initialisation at replay time virtual bool CanCreateGLESContext() = 0; virtual bool PopulateForReplay() = 0; - virtual ReplayStatus InitialiseAPI(GLWindowingData &replayContext, RDCDriver api) = 0; + virtual ReplayStatus InitialiseAPI(GLWindowingData &replayContext, RDCDriver api, bool debug) = 0; virtual void *GetReplayFunction(const char *funcname) = 0; }; @@ -313,7 +313,7 @@ class GLDummyPlatform : public GLPlatform // for initialisation at replay time virtual bool CanCreateGLESContext() { return true; } virtual bool PopulateForReplay() { return true; } - virtual ReplayStatus InitialiseAPI(GLWindowingData &replayContext, RDCDriver api) + virtual ReplayStatus InitialiseAPI(GLWindowingData &replayContext, RDCDriver api, bool debug) { return ReplayStatus::Succeeded; } diff --git a/renderdoc/driver/gl/gl_driver.cpp b/renderdoc/driver/gl/gl_driver.cpp index c4862ba43..1bb1f263f 100644 --- a/renderdoc/driver/gl/gl_driver.cpp +++ b/renderdoc/driver/gl/gl_driver.cpp @@ -2629,7 +2629,15 @@ bool WrappedOpenGL::Serialise_BeginCaptureFrame(SerialiserType &ser) if(IsReplayingAndReading()) { + std::vector savedDebugMessages; + + // save any debug messages we built up + savedDebugMessages.swap(m_DebugMessages); + state.ApplyState(this); + + // restore saved messages - which implicitly discards any generated while applying state + savedDebugMessages.swap(m_DebugMessages); } return true; @@ -2702,6 +2710,14 @@ void WrappedOpenGL::Serialise_DebugMessages(SerialiserType &ser) SERIALISE_ELEMENT(DebugMessages); + // if we're using replay-time API validation, fetch messages at replay time and ignore any + // serialised ones + if(ser.IsReading() && IsLoading(m_State) && m_ReplayOptions.apiValidation) + { + DebugMessages = m_DebugMessages; + m_DebugMessages.clear(); + } + // hide empty sets of messages. if(ser.IsReading() && DebugMessages.empty()) ser.Hidden(); @@ -2767,7 +2783,7 @@ void WrappedOpenGL::DebugSnoop(GLenum source, GLenum type, GLuint id, GLenum sev RDCLOG("Debug Message context: \"%s\"", m_DebugMsgContext.c_str()); } - if(IsActiveCapturing(m_State)) + if(IsActiveCapturing(m_State) || (IsLoading(m_State) && m_ReplayOptions.apiValidation)) { DebugMessage msg; @@ -2955,8 +2971,17 @@ ReplayStatus WrappedOpenGL::ReadLogInitialisation(RDCFile *rdc, bool storeStruct m_FrameReader = new StreamReader(reader, frameDataSize); + std::vector savedDebugMessages; + + // save any debug messages we built up + savedDebugMessages.swap(m_DebugMessages); + GetResourceManager()->ApplyInitialContents(); + // restore saved messages - which implicitly discards any generated while applying initial + // contents + savedDebugMessages.swap(m_DebugMessages); + ReplayStatus status = ContextReplayLog(m_State, 0, 0, false); if(status != ReplayStatus::Succeeded) diff --git a/renderdoc/driver/gl/gl_replay.cpp b/renderdoc/driver/gl/gl_replay.cpp index cad654a31..3cebad570 100644 --- a/renderdoc/driver/gl/gl_replay.cpp +++ b/renderdoc/driver/gl/gl_replay.cpp @@ -3415,7 +3415,7 @@ ReplayStatus CreateReplayDevice(RDCDriver rdcdriver, RDCFile *rdc, const ReplayO GLWindowingData data = {}; - ReplayStatus status = platform.InitialiseAPI(data, rdcdriver); + ReplayStatus status = platform.InitialiseAPI(data, rdcdriver, opts.apiValidation); // any errors will be already printed, just pass the error up if(status != ReplayStatus::Succeeded) diff --git a/renderdoc/driver/gl/glx_platform.cpp b/renderdoc/driver/gl/glx_platform.cpp index d11debc43..2558fa656 100644 --- a/renderdoc/driver/gl/glx_platform.cpp +++ b/renderdoc/driver/gl/glx_platform.cpp @@ -326,8 +326,13 @@ class GLXPlatform : public GLPlatform } bool PopulateForReplay() { return GLX.PopulateForReplay(); } - ReplayStatus InitialiseAPI(GLWindowingData &replayContext, RDCDriver api) + ReplayStatus InitialiseAPI(GLWindowingData &replayContext, RDCDriver api, bool debug) { +// force debug in development builds +#if ENABLED(RDOC_DEVEL) + debug = true; +#endif + RDCASSERT(api == RDCDriver::OpenGL || api == RDCDriver::OpenGLES); int attribs[64] = {0}; @@ -340,11 +345,7 @@ class GLXPlatform : public GLPlatform int &minor = attribs[i]; attribs[i++] = 0; attribs[i++] = GLX_CONTEXT_FLAGS_ARB; -#if ENABLED(RDOC_DEVEL) - attribs[i++] = GLX_CONTEXT_DEBUG_BIT_ARB; -#else - attribs[i++] = 0; -#endif + attribs[i++] = debug ? GLX_CONTEXT_DEBUG_BIT_ARB : 0; attribs[i++] = GLX_CONTEXT_PROFILE_MASK_ARB; attribs[i++] = api == RDCDriver::OpenGLES ? GLX_CONTEXT_ES2_PROFILE_BIT_EXT : GLX_CONTEXT_CORE_PROFILE_BIT_ARB; diff --git a/renderdoc/driver/gl/wgl_platform.cpp b/renderdoc/driver/gl/wgl_platform.cpp index 453265195..c5e3f0e59 100644 --- a/renderdoc/driver/gl/wgl_platform.cpp +++ b/renderdoc/driver/gl/wgl_platform.cpp @@ -291,8 +291,13 @@ class WGLPlatform : public GLPlatform } bool PopulateForReplay() { return WGL.PopulateForReplay(); } - ReplayStatus InitialiseAPI(GLWindowingData &replayContext, RDCDriver api) + ReplayStatus InitialiseAPI(GLWindowingData &replayContext, RDCDriver api, bool debug) { +// force debug in development builds +#if ENABLED(RDOC_DEVEL) + debug = true; +#endif + RDCASSERT(api == RDCDriver::OpenGL || api == RDCDriver::OpenGLES); bool success = RegisterClass(); @@ -370,11 +375,7 @@ class WGLPlatform : public GLPlatform 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++] = debug ? WGL_CONTEXT_DEBUG_BIT_ARB : 0; attribs[i++] = WGL_CONTEXT_PROFILE_MASK_ARB; attribs[i++] = api == RDCDriver::OpenGLES ? WGL_CONTEXT_ES2_PROFILE_BIT_EXT : WGL_CONTEXT_CORE_PROFILE_BIT_ARB; diff --git a/renderdoc/driver/vulkan/vk_common.h b/renderdoc/driver/vulkan/vk_common.h index bf61d188b..6c37b04bc 100644 --- a/renderdoc/driver/vulkan/vk_common.h +++ b/renderdoc/driver/vulkan/vk_common.h @@ -71,20 +71,6 @@ // happening #define VERBOSE_PARTIAL_REPLAY OPTION_OFF -// enable this to enable validation layers on replay, useful for debugging -// problems with new replay code -#define FORCE_VALIDATION_LAYERS OPTION_OFF - -// enable this to send replay-time validation layer messages to the UI. -// By default we only display saved validation layer messages from capture, and then any runtime -// messages we generate ourselves. With this option, every time the catpure is replayed, any -// messages will be bubbled up and added to the list in the UI - there is no deduplication so this -// will be an ever-growing list. -// This is independent of FORCE_VALIDATION_LAYERS above. We will listen to debug report if it's -// available whether or not we enabled the validation layers, and output any messages. This allows -// the ICD to generate messages for display -#define DISPLAY_RUNTIME_DEBUG_MESSAGES OPTION_OFF - ResourceFormat MakeResourceFormat(VkFormat fmt); VkFormat MakeVkFormat(ResourceFormat fmt); Topology MakePrimitiveTopology(VkPrimitiveTopology Topo, uint32_t patchControlPoints); diff --git a/renderdoc/driver/vulkan/vk_core.cpp b/renderdoc/driver/vulkan/vk_core.cpp index eec095e5b..614419d39 100644 --- a/renderdoc/driver/vulkan/vk_core.cpp +++ b/renderdoc/driver/vulkan/vk_core.cpp @@ -2087,6 +2087,10 @@ ReplayStatus WrappedVulkan::ReadLogInitialisation(RDCFile *rdc, bool storeStruct uint64_t frameDataSize = 0; + ScopedDebugMessageSink *sink = NULL; + if(m_ReplayOptions.apiValidation) + sink = new ScopedDebugMessageSink(this); + for(;;) { PerformanceTimer timer; @@ -2145,6 +2149,8 @@ ReplayStatus WrappedVulkan::ReadLogInitialisation(RDCFile *rdc, bool storeStruct break; } + SAFE_DELETE(sink); + #if ENABLED(RDOC_DEVEL) for(auto it = chunkInfos.begin(); it != chunkInfos.end(); ++it) { @@ -2236,10 +2242,16 @@ ReplayStatus WrappedVulkan::ContextReplayLog(CaptureState readType, uint32_t sta // (not undefined) if(IsLoading(m_State)) { + // temporarily disable the debug message sink, to ignore messages from initial contents apply + ScopedDebugMessageSink *sink = GetDebugMessageSink(); + SetDebugMessageSink(NULL); + ApplyInitialContents(); SubmitCmds(); FlushQ(); + + SetDebugMessageSink(sink); } m_RootEvents.clear(); @@ -2470,28 +2482,7 @@ bool WrappedVulkan::ContextProcessChunk(ReadSerialiser &ser, VulkanChunk chunk) { m_AddedDrawcall = false; - bool success = false; - -#if ENABLED(DISPLAY_RUNTIME_DEBUG_MESSAGES) - // see the definition of DISPLAY_RUNTIME_DEBUG_MESSAGES for more information. During replay, add a - // debug sink to catch any replay-time messages - { - ScopedDebugMessageSink sink(this); - - success = ProcessChunk(ser, chunk); - - if(IsActiveReplaying(m_State)) - { - std::vector DebugMessages; - DebugMessages.swap(sink.msgs); - - for(const DebugMessage &msg : DebugMessages) - AddDebugMessage(msg); - } - } -#else - success = ProcessChunk(ser, chunk); -#endif + bool success = ProcessChunk(ser, chunk); if(!success) return false; @@ -3223,98 +3214,16 @@ void WrappedVulkan::Serialise_DebugMessages(SerialiserType &ser) if(sink) DebugMessages.swap(sink->msgs); - // if we have the unique objects layer we can assume all objects have a unique ID, and replace - // any text that looks like an object reference (0xHEX[NAME]). - if(m_LayersEnabled[VkCheckLayer_unique_objects]) - { - for(DebugMessage &msg : DebugMessages) - { - if(strstr(msg.description.c_str(), "0x")) - { - std::string desc = msg.description; - - size_t offs = desc.find("0x"); - while(offs != std::string::npos) - { - // if we're on a word boundary - if(offs == 0 || !isalnum(desc[offs - 1])) - { - size_t end = offs + 2; - - uint64_t val = 0; - - // consume all hex chars - while(end < desc.length()) - { - if(desc[end] >= '0' && desc[end] <= '9') - { - val <<= 4; - val += (desc[end] - '0'); - end++; - } - else if(desc[end] >= 'A' && desc[end] <= 'F') - { - val <<= 4; - val += (desc[end] - 'A') + 0xA; - end++; - } - else if(desc[end] >= 'a' && desc[end] <= 'f') - { - val <<= 4; - val += (desc[end] - 'a') + 0xA; - end++; - } - else - { - break; - } - } - - // we now expect a [NAME]. Look for matched set of []s - if(desc[end] == '[') - { - int depth = 1; - end++; - - while(end < desc.length() && depth) - { - if(desc[end] == '[') - depth++; - else if(desc[end] == ']') - depth--; - - end++; - } - - // unique objects layer implies this is a unique search so we don't have to worry - // about type aliases - ResourceId id = GetResourceManager()->GetFirstIDForHandle(val); - - if(id != ResourceId()) - { - std::string idstr = ToStr(id); - - desc.erase(offs, end - offs); - - desc.insert(offs, idstr.c_str()); - - offs = desc.find("0x", offs + idstr.length()); - continue; - } - } - } - - offs = desc.find("0x", offs + 1); - } - - msg.description = desc; - } - } - } + for(DebugMessage &msg : DebugMessages) + ProcessDebugMessage(msg); } SERIALISE_ELEMENT(DebugMessages); + // if we're using debug messages from replay, discard any from the capture + if(ser.IsReading() && IsLoading(m_State) && m_ReplayOptions.apiValidation) + DebugMessages.clear(); + // hide empty sets of messages. if(ser.IsReading() && DebugMessages.empty()) ser.Hidden(); @@ -3329,6 +3238,95 @@ void WrappedVulkan::Serialise_DebugMessages(SerialiserType &ser) template void WrappedVulkan::Serialise_DebugMessages(WriteSerialiser &ser); template void WrappedVulkan::Serialise_DebugMessages(ReadSerialiser &ser); +void WrappedVulkan::ProcessDebugMessage(DebugMessage &msg) +{ + // if we have the unique objects layer we can assume all objects have a unique ID, and replace + // any text that looks like an object reference (0xHEX[NAME]). + if(m_LayersEnabled[VkCheckLayer_unique_objects]) + { + if(strstr(msg.description.c_str(), "0x")) + { + std::string desc = msg.description; + + size_t offs = desc.find("0x"); + while(offs != std::string::npos) + { + // if we're on a word boundary + if(offs == 0 || !isalnum(desc[offs - 1])) + { + size_t end = offs + 2; + + uint64_t val = 0; + + // consume all hex chars + while(end < desc.length()) + { + if(desc[end] >= '0' && desc[end] <= '9') + { + val <<= 4; + val += (desc[end] - '0'); + end++; + } + else if(desc[end] >= 'A' && desc[end] <= 'F') + { + val <<= 4; + val += (desc[end] - 'A') + 0xA; + end++; + } + else if(desc[end] >= 'a' && desc[end] <= 'f') + { + val <<= 4; + val += (desc[end] - 'a') + 0xA; + end++; + } + else + { + break; + } + } + + // we now expect a [NAME]. Look for matched set of []s + if(desc[end] == '[') + { + int depth = 1; + end++; + + while(end < desc.length() && depth) + { + if(desc[end] == '[') + depth++; + else if(desc[end] == ']') + depth--; + + end++; + } + + // unique objects layer implies this is a unique search so we don't have to worry + // about type aliases + ResourceId id = GetResourceManager()->GetFirstIDForHandle(val); + + if(id != ResourceId()) + { + std::string idstr = ToStr(id); + + desc.erase(offs, end - offs); + + desc.insert(offs, idstr.c_str()); + + offs = desc.find("0x", offs + idstr.length()); + continue; + } + } + } + + offs = desc.find("0x", offs + 1); + } + + msg.description = desc; + } + } +} + std::vector WrappedVulkan::GetDebugMessages() { std::vector ret; @@ -3396,7 +3394,18 @@ VkBool32 WrappedVulkan::DebugCallback(MessageSeverity severity, MessageCategory msg.eventId = it->eventId; } - sink->msgs.push_back(msg); + // function calls are replayed after the call to Serialise_DebugMessages() so we don't have a + // sync point to gather together all the messages from the sink. But instead we can just push + // them directly into the list since we're linearised + if(IsReplayMode(m_State)) + { + ProcessDebugMessage(msg); + AddDebugMessage(msg); + } + else + { + sink->msgs.push_back(msg); + } } } diff --git a/renderdoc/driver/vulkan/vk_core.h b/renderdoc/driver/vulkan/vk_core.h index 8ba9bb307..d5dc73a1c 100644 --- a/renderdoc/driver/vulkan/vk_core.h +++ b/renderdoc/driver/vulkan/vk_core.h @@ -246,6 +246,9 @@ private: std::vector m_DebugMessages; template void Serialise_DebugMessages(SerialiserType &ser); + + void ProcessDebugMessage(DebugMessage &DebugMessages); + std::vector GetDebugMessages(); void AddDebugMessage(DebugMessage msg); void AddDebugMessage(MessageCategory c, MessageSeverity sv, MessageSource src, std::string d); diff --git a/renderdoc/driver/vulkan/vk_manager.cpp b/renderdoc/driver/vulkan/vk_manager.cpp index 6b9486d55..b79988df7 100644 --- a/renderdoc/driver/vulkan/vk_manager.cpp +++ b/renderdoc/driver/vulkan/vk_manager.cpp @@ -747,9 +747,9 @@ void VulkanResourceManager::ApplyBarriers(uint32_t queueFamilyIndex, ResourceId VulkanResourceManager::GetFirstIDForHandle(uint64_t handle) { - for(auto it = m_ResourceRecords.begin(); it != m_ResourceRecords.end(); ++it) + for(auto it = m_CurrentResourceMap.begin(); it != m_CurrentResourceMap.end(); ++it) { - WrappedVkRes *res = it->second->Resource; + WrappedVkRes *res = it->second; if(!res) continue; @@ -758,13 +758,13 @@ ResourceId VulkanResourceManager::GetFirstIDForHandle(uint64_t handle) { WrappedVkDispRes *disp = (WrappedVkDispRes *)res; if(disp->real.handle == handle) - return disp->id; + return IsReplayMode(m_State) ? GetOriginalID(disp->id) : disp->id; } else { WrappedVkNonDispRes *nondisp = (WrappedVkNonDispRes *)res; if(nondisp->real.handle == handle) - return nondisp->id; + return IsReplayMode(m_State) ? GetOriginalID(nondisp->id) : nondisp->id; } } diff --git a/renderdoc/driver/vulkan/wrappers/vk_device_funcs.cpp b/renderdoc/driver/vulkan/wrappers/vk_device_funcs.cpp index 9ba3787bf..8fe9c7590 100644 --- a/renderdoc/driver/vulkan/wrappers/vk_device_funcs.cpp +++ b/renderdoc/driver/vulkan/wrappers/vk_device_funcs.cpp @@ -180,10 +180,6 @@ ReplayStatus WrappedVulkan::Initialise(VkInitParams ¶ms, uint64_t sectionVer StripUnwantedLayers(params.Layers); StripUnwantedExtensions(params.Extensions); -#if ENABLED(FORCE_VALIDATION_LAYERS) && DISABLED(RDOC_ANDROID) - params.Layers.push_back("VK_LAYER_LUNARG_standard_validation"); -#endif - std::set supportedLayers; { @@ -199,6 +195,29 @@ ReplayStatus WrappedVulkan::Initialise(VkInitParams ¶ms, uint64_t sectionVer SAFE_DELETE_ARRAY(props); } + if(m_ReplayOptions.apiValidation) + { + const char KhronosValidation[] = "VK_LAYER_KHRONOS_validation"; + const char LunarGValidation[] = "VK_LAYER_LUNARG_standard_validation"; + + if(supportedLayers.find(KhronosValidation) != supportedLayers.end()) + { + RDCLOG("Enabling %s layer for API validation", KhronosValidation); + params.Layers.push_back(KhronosValidation); + m_LayersEnabled[VkCheckLayer_unique_objects] = true; + } + else if(supportedLayers.find(LunarGValidation) != supportedLayers.end()) + { + RDCLOG("Enabling %s layer for API validation", LunarGValidation); + params.Layers.push_back(LunarGValidation); + m_LayersEnabled[VkCheckLayer_unique_objects] = true; + } + else + { + RDCLOG("API validation layers are not available, check you have the Vulkan SDK installed"); + } + } + // complain about any missing layers, but remove them from the list and continue for(auto it = params.Layers.begin(); it != params.Layers.end();) { @@ -614,6 +633,7 @@ VkResult WrappedVulkan::vkCreateInstance(const VkInstanceCreateInfo *pCreateInfo for(uint32_t i = 0; i < modifiedCreateInfo.enabledLayerCount; i++) { if(!strcmp(modifiedCreateInfo.ppEnabledLayerNames[i], "VK_LAYER_LUNARG_standard_validation") || + !strcmp(modifiedCreateInfo.ppEnabledLayerNames[i], "VK_LAYER_KHRONOS_validation") || !strcmp(modifiedCreateInfo.ppEnabledLayerNames[i], "VK_LAYER_GOOGLE_unique_objects")) { m_LayersEnabled[VkCheckLayer_unique_objects] = true; @@ -2603,11 +2623,18 @@ bool WrappedVulkan::Serialise_vkCreateDevice(SerialiserType &ser, VkPhysicalDevi APIProps.vendor = GetDriverInfo().Vendor(); + // temporarily disable the debug message sink, to ignore any false positive messages from our + // init + ScopedDebugMessageSink *sink = GetDebugMessageSink(); + SetDebugMessageSink(NULL); + m_ShaderCache = new VulkanShaderCache(this); m_DebugManager = new VulkanDebugManager(this); m_Replay.CreateResources(); + + SetDebugMessageSink(sink); } return true;