From d0f76bfd612a0a3f1add2565ba0cd89d3086f625 Mon Sep 17 00:00:00 2001 From: AyeZeeBB Date: Sat, 17 Oct 2026 01:44:22 -0500 Subject: [PATCH] GPU diagnostics: a resource creation that takes 20 ms or more is said in the log With what was made (heap type and flags, size, format, flags) and the game code that asked, at most 40 times. Small buffers and textures took 120 to 170 ms each, two threads at the same moment, which showed the 5 s stall was the GPU or driver held up by something outside the game, not the size of what the game made. --- Extension/UI/Overlay/gpu_diagnostics.cpp | 37 ++++++++++++++++++++++++ 1 file changed, 37 insertions(+) diff --git a/Extension/UI/Overlay/gpu_diagnostics.cpp b/Extension/UI/Overlay/gpu_diagnostics.cpp index f90e831..358fe7d 100644 --- a/Extension/UI/Overlay/gpu_diagnostics.cpp +++ b/Extension/UI/Overlay/gpu_diagnostics.cpp @@ -367,9 +367,40 @@ std::atomic original_placed1{}; std::atomic original_committed3{}; std::atomic original_placed2{}; +// A resource creation that holds up its thread (the driver waiting on the GPU inside it): said with +// what was made and which game code asked, at most 40 times. A 170 ms stall every 5 s was one of +// these (2026-10-17): the game's batched GPU uploads (Skate 0x1446b3f20) making their upload buffer. +std::uint64_t creation_clock() noexcept { + return static_cast(std::chrono::duration_cast( + std::chrono::steady_clock::now().time_since_epoch()).count()); +} +template +void note_slow_creation(std::uint64_t started, const D3D12_HEAP_PROPERTIES* properties, D3D12_HEAP_FLAGS heap_flags, const Desc* desc, + void* caller) noexcept { + const auto ms = static_cast(creation_clock() - started) / 1e6; + if (ms < 20) return; + static std::atomic said{}; + if (said.fetch_add(1, std::memory_order_relaxed) >= 40) return; + try { + const auto image = reinterpret_cast(GetModuleHandleW(nullptr)); + const auto at = reinterpret_cast(caller); + const auto where = at > image && at - image < 0x10000000 ? std::format("Skate 0x{:x}", 0x140000000 + (at - image)) + : std::format("0x{:x}", at); + static constexpr const char* dimensions[]{"unknown", "buffer", "1D texture", "2D texture", "3D texture"}; + const auto dimension = desc && static_cast(desc->Dimension) < 5 ? dimensions[desc->Dimension] : "unknown"; + dingosdk::logging::log(dingosdk::logging::Level::warning, dingosdk::logging::Channel::graphics, + "CreateCommittedResource took {:.1f} ms: heap type {}, heap flags {:#x}; a {} {} x {} x {}, {} mips, format {}, " + "flags {:#x}; asked for by {}.", ms, properties ? static_cast(properties->Type) : -1, static_cast(heap_flags), + dimension, desc ? desc->Width : 0, desc ? desc->Height : 0, desc ? desc->DepthOrArraySize : 0, desc ? desc->MipLevels : 0, + desc ? static_cast(desc->Format) : 0, desc ? static_cast(desc->Flags) : 0u, where); + } catch (...) {} +} + HRESULT STDMETHODCALLTYPE hooked_committed(ID3D12Device* device, const D3D12_HEAP_PROPERTIES* properties, D3D12_HEAP_FLAGS flags, const D3D12_RESOURCE_DESC* desc, D3D12_RESOURCE_STATES state, const D3D12_CLEAR_VALUE* clear, REFIID riid, void** out) { + const auto started = creation_clock(); const auto result = original_committed.load()(device, properties, flags, desc, state, clear, riid, out); + note_slow_creation(started, properties, flags, desc, _ReturnAddress()); if (SUCCEEDED(result)) note_resource(out, nullptr, 0); return result; } @@ -387,7 +418,9 @@ HRESULT STDMETHODCALLTYPE hooked_placed(ID3D12Device* device, ID3D12Heap* heap, HRESULT STDMETHODCALLTYPE hooked_committed1(ID3D12Device* device, const D3D12_HEAP_PROPERTIES* properties, D3D12_HEAP_FLAGS flags, const D3D12_RESOURCE_DESC* desc, D3D12_RESOURCE_STATES state, const D3D12_CLEAR_VALUE* clear, ID3D12ProtectedResourceSession* session, REFIID riid, void** out) { + const auto started = creation_clock(); const auto result = original_committed1.load()(device, properties, flags, desc, state, clear, session, riid, out); + note_slow_creation(started, properties, flags, desc, _ReturnAddress()); if (SUCCEEDED(result)) note_resource(out, nullptr, 0); return result; } @@ -400,7 +433,9 @@ HRESULT STDMETHODCALLTYPE hooked_heap1(ID3D12Device* device, const D3D12_HEAP_DE HRESULT STDMETHODCALLTYPE hooked_committed2(ID3D12Device* device, const D3D12_HEAP_PROPERTIES* properties, D3D12_HEAP_FLAGS flags, const D3D12_RESOURCE_DESC1* desc, D3D12_RESOURCE_STATES state, const D3D12_CLEAR_VALUE* clear, ID3D12ProtectedResourceSession* session, REFIID riid, void** out) { + const auto started = creation_clock(); const auto result = original_committed2.load()(device, properties, flags, desc, state, clear, session, riid, out); + note_slow_creation(started, properties, flags, desc, _ReturnAddress()); if (SUCCEEDED(result)) note_resource(out, nullptr, 0); return result; } @@ -413,7 +448,9 @@ HRESULT STDMETHODCALLTYPE hooked_placed1(ID3D12Device* device, ID3D12Heap* heap, HRESULT STDMETHODCALLTYPE hooked_committed3(ID3D12Device* device, const D3D12_HEAP_PROPERTIES* properties, D3D12_HEAP_FLAGS flags, const D3D12_RESOURCE_DESC1* desc, D3D12_BARRIER_LAYOUT layout, const D3D12_CLEAR_VALUE* clear, ID3D12ProtectedResourceSession* session, UINT32 formats, const DXGI_FORMAT* castable, REFIID riid, void** out) { + const auto started = creation_clock(); const auto result = original_committed3.load()(device, properties, flags, desc, layout, clear, session, formats, castable, riid, out); + note_slow_creation(started, properties, flags, desc, _ReturnAddress()); if (SUCCEEDED(result)) note_resource(out, nullptr, 0); return result; }