diff --git a/gfx/thebes/d3dkmtQueryStatistics.h b/gfx/thebes/d3dkmtQueryStatistics.h index c3491f8a06e4..d00b70696688 100644 --- a/gfx/thebes/d3dkmtQueryStatistics.h +++ b/gfx/thebes/d3dkmtQueryStatistics.h @@ -19,8 +19,9 @@ typedef struct _D3DKMTQS_COUNTER { typedef struct _D3DKMTQS_ADAPTER_INFO { ULONG NbSegments; + ULONG NodeCount; - ULONG Filler[4]; + ULONG Filler[3]; ULONGLONG Filler2[2]; // Assumed sizeof(LONGLONG) = sizeof(ULONGLONG) struct { ULONG Filler[14]; @@ -121,11 +122,20 @@ typedef struct _D3DKMTQS_PROCESS_SEGMENT_INFO { ULONG64 Reserved[8]; } D3DKMTQS_PROCESS_SEGMENT_INFO; +typedef struct _D3DKMTQS_PROCESS_NODE_INFO { + LARGE_INTEGER RunningTime; // 100ns + ULONG ContextSwitch; + ULONG PreemptionStatistics[16]; + ULONG PacketStatistics[32]; + ULONG64 Reserved[8]; +} D3DKMTQS_PROCESS_NODE_INFO; + typedef enum _D3DKMTQS_TYPE { D3DKMTQS_ADAPTER = 0, D3DKMTQS_PROCESS = 1, D3DKMTQS_SEGMENT = 3, D3DKMTQS_PROCESS_SEGMENT = 4, + D3DKMTQS_PROCESS_NODE = 6, } D3DKMTQS_TYPE; typedef union _D3DKMTQS_RESULT { @@ -134,12 +144,17 @@ typedef union _D3DKMTQS_RESULT { D3DKMTQS_SEGMENT_INFO_WIN8 SegmentInfoWin8; D3DKMTQS_PROCESS_INFO ProcessInfo; D3DKMTQS_PROCESS_SEGMENT_INFO ProcessSegmentInfo; + D3DKMTQS_PROCESS_NODE_INFO ProcessNodeInformation; } D3DKMTQS_RESULT; typedef struct _D3DKMTQS_QUERY_SEGMENT { ULONG SegmentId; } D3DKMTQS_QUERY_SEGMENT; +typedef struct _D3DKMTQS_QUERY_NODE { + ULONG NodeId; +} D3DKMTQS_QUERY_NODE; + typedef struct _D3DKMTQS { D3DKMTQS_TYPE Type; LUID AdapterLuid; @@ -149,6 +164,7 @@ typedef struct _D3DKMTQS { union { D3DKMTQS_QUERY_SEGMENT QuerySegment; D3DKMTQS_QUERY_SEGMENT QueryProcessSegment; + D3DKMTQS_QUERY_NODE QueryProcessNode; }; } D3DKMTQS; diff --git a/gfx/thebes/gfxWindowsPlatform.cpp b/gfx/thebes/gfxWindowsPlatform.cpp index 3a81c2bde9df..4b582830f809 100644 --- a/gfx/thebes/gfxWindowsPlatform.cpp +++ b/gfx/thebes/gfxWindowsPlatform.cpp @@ -313,6 +313,69 @@ void gfxWindowsPlatform::InitMemoryReportersForGPUProcess() { RegisterStrongMemoryReporter(new D3DSharedTexturesReporter()); } +/* static */ +nsresult gfxWindowsPlatform::GetGpuTimeSinceProcessStartInMs( + uint64_t* aResult) { + RefPtr d3d11Device; + if (!(d3d11Device = mozilla::gfx::Factory::GetDirect3D11Device())) { + // If we don't have a D3DDevice, we likely didn't use any GPU time. + *aResult = 0; + return NS_OK; + } + + nsModuleHandle module(LoadLibrary(L"gdi32.dll")); + if (!module) { + return NS_ERROR_NOT_AVAILABLE; + } + + PFND3DKMTQS queryD3DKMTStatistics = + (PFND3DKMTQS)GetProcAddress(module, "D3DKMTQueryStatistics"); + if (!queryD3DKMTStatistics) { + return NS_ERROR_NOT_AVAILABLE; + } + + RefPtr dxgiDevice; + if (d3d11Device->QueryInterface(__uuidof(IDXGIDevice), + getter_AddRefs(dxgiDevice)) != S_OK) { + return NS_ERROR_FAILURE; + } + + IDXGIAdapter* DXGIAdapter; + if (dxgiDevice->GetAdapter(&DXGIAdapter) != S_OK) { + return NS_ERROR_FAILURE; + } + + DXGI_ADAPTER_DESC adapterDesc; + DXGIAdapter->GetDesc(&adapterDesc); + DXGIAdapter->Release(); + + D3DKMTQS queryStatistics; + memset(&queryStatistics, 0, sizeof(D3DKMTQS)); + queryStatistics.Type = D3DKMTQS_ADAPTER; + queryStatistics.AdapterLuid = adapterDesc.AdapterLuid; + if (!NT_SUCCESS(queryD3DKMTStatistics(&queryStatistics))) { + return NS_ERROR_FAILURE; + } + + uint64_t result = 0; + ULONG nodeCount = queryStatistics.QueryResult.AdapterInfo.NodeCount; + for (ULONG i = 0; i < nodeCount; ++i) { + memset(&queryStatistics, 0, sizeof(D3DKMTQS)); + queryStatistics.Type = D3DKMTQS_PROCESS_NODE; + queryStatistics.AdapterLuid = adapterDesc.AdapterLuid; + queryStatistics.hProcess = GetCurrentProcess(); + queryStatistics.QueryProcessNode.NodeId = i; + if (NT_SUCCESS(queryD3DKMTStatistics(&queryStatistics))) { + result += queryStatistics.QueryResult.ProcessNodeInformation.RunningTime + .QuadPart * + 100 / PR_NSEC_PER_MSEC; + } + } + + *aResult = result; + return NS_OK; +} + static void UpdateANGLEConfig() { if (!gfxConfig::IsEnabled(Feature::D3D11_COMPOSITING)) { gfxConfig::Disable(Feature::D3D11_HW_ANGLE, FeatureStatus::Disabled, diff --git a/gfx/thebes/gfxWindowsPlatform.h b/gfx/thebes/gfxWindowsPlatform.h index a439d2303d35..51f61a3ee823 100644 --- a/gfx/thebes/gfxWindowsPlatform.h +++ b/gfx/thebes/gfxWindowsPlatform.h @@ -6,12 +6,6 @@ #ifndef GFX_WINDOWS_PLATFORM_H #define GFX_WINDOWS_PLATFORM_H -/** - * XXX to get CAIRO_HAS_DWRITE_FONT - * and cairo_win32_scaled_font_select_font - */ -#include "cairo-win32.h" - #include "gfxCrashReporterUtils.h" #include "gfxFontUtils.h" #include "gfxWindowsSurface.h" @@ -177,6 +171,8 @@ class gfxWindowsPlatform final : public gfxPlatform { } public: + static nsresult GetGpuTimeSinceProcessStartInMs(uint64_t* aResult); + bool DwmCompositionEnabled(); mozilla::layers::ReadbackManagerD3D11* GetReadbackManager(); diff --git a/toolkit/components/glean/ipc/FOGIPC.cpp b/toolkit/components/glean/ipc/FOGIPC.cpp index 1d07eb71752e..90898a910978 100644 --- a/toolkit/components/glean/ipc/FOGIPC.cpp +++ b/toolkit/components/glean/ipc/FOGIPC.cpp @@ -21,8 +21,8 @@ using FlushFOGDataPromise = mozilla::dom::ContentParent::FlushFOGDataPromise; namespace mozilla { namespace glean { -static void RecordCpuTime() { - static uint64_t previousCpuTime = 0; +static void RecordPowerMetrics() { + static uint64_t previousCpuTime = 0, previousGpuTime = 0; uint64_t cpuTime; if (NS_FAILED(GetCpuTimeSinceProcessStartInMs(&cpuTime))) { @@ -38,6 +38,16 @@ static void RecordCpuTime() { // This should be fine for now, but may overflow in the future. power::total_cpu_time_ms.Add(int32_t(newCpuTime)); } + + uint64_t gpuTime; + if (NS_SUCCEEDED(GetGpuTimeSinceProcessStartInMs(&gpuTime))) { + uint64_t newGpuTime = gpuTime - previousGpuTime; + previousGpuTime += newGpuTime; + + if (newGpuTime) { + power::total_gpu_time_ms.Add(int32_t(newGpuTime)); + } + } } /** @@ -48,8 +58,8 @@ static void RecordCpuTime() { * serialized payload that the Rust impl hands you. */ void FlushFOGData(std::function&& aResolver) { - // Record the CPU time right before data is sent to the parent. - RecordCpuTime(); + // Record power metrics right before data is sent to the parent. + RecordPowerMetrics(); ByteBuf buf; uint32_t ipcBufferSize = impl::fog_serialize_ipc_buf(); @@ -130,8 +140,9 @@ void SendFOGData(ipc::ByteBuf&& buf) { * sending it all down into Rust to be used. */ RefPtr FlushAndUseFOGData() { - // Record CPU time on the parent before sending requests to child processes. - RecordCpuTime(); + // Record power metrics on the parent before sending requests to child + // processes. + RecordPowerMetrics(); RefPtr ret = new GenericPromise::Private(__func__); std::function &&)> resolver = diff --git a/toolkit/components/processtools/ProcInfo.h b/toolkit/components/processtools/ProcInfo.h index 220b0a0569e0..180328854b71 100644 --- a/toolkit/components/processtools/ProcInfo.h +++ b/toolkit/components/processtools/ProcInfo.h @@ -27,6 +27,13 @@ class GeckoChildProcessHost; */ nsresult GetCpuTimeSinceProcessStartInMs(uint64_t* aResult); +/** + * Return the number of milliseconds of GPU time used since process start. + * + * @return NS_OK on success. + */ +nsresult GetGpuTimeSinceProcessStartInMs(uint64_t* aResult); + // Process types. When updating this enum, please make sure to update // WebIDLProcType, ChromeUtils::RequestProcInfo and ProcTypeToWebIDL to // mirror the changes. diff --git a/toolkit/components/processtools/ProcInfo.mm b/toolkit/components/processtools/ProcInfo.mm index a954928be02d..ecc20e620ab1 100644 --- a/toolkit/components/processtools/ProcInfo.mm +++ b/toolkit/components/processtools/ProcInfo.mm @@ -31,6 +31,19 @@ nsresult GetCpuTimeSinceProcessStartInMs(uint64_t* aResult) { return NS_OK; } +nsresult GetGpuTimeSinceProcessStartInMs(uint64_t* aResult) { + task_power_info_v2_data_t task_power_info; + mach_msg_type_number_t count = TASK_POWER_INFO_V2_COUNT; + kern_return_t kr = + task_info(mach_task_self(), TASK_POWER_INFO_V2, (task_info_t)&task_power_info, &count); + if (kr != KERN_SUCCESS) { + return NS_ERROR_FAILURE; + } + + *aResult = task_power_info.gpu_energy.task_gpu_utilisation / PR_NSEC_PER_MSEC; + return NS_OK; +} + RefPtr GetProcInfo(nsTArray&& aRequests) { auto holder = MakeUnique>(); RefPtr promise = holder->Ensure(__func__); diff --git a/toolkit/components/processtools/ProcInfo_linux.cpp b/toolkit/components/processtools/ProcInfo_linux.cpp index 08f2edd134b4..92e9ffd3a1da 100644 --- a/toolkit/components/processtools/ProcInfo_linux.cpp +++ b/toolkit/components/processtools/ProcInfo_linux.cpp @@ -196,6 +196,10 @@ nsresult GetCpuTimeSinceProcessStartInMs(uint64_t* aResult) { return NS_OK; } +nsresult GetGpuTimeSinceProcessStartInMs(uint64_t* aResult) { + return NS_ERROR_NOT_IMPLEMENTED; +} + RefPtr GetProcInfo(nsTArray&& aRequests) { auto holder = MakeUnique>(); RefPtr promise = holder->Ensure(__func__); diff --git a/toolkit/components/processtools/ProcInfo_win.cpp b/toolkit/components/processtools/ProcInfo_win.cpp index 33eae39da91d..2e013ebb8bd2 100644 --- a/toolkit/components/processtools/ProcInfo_win.cpp +++ b/toolkit/components/processtools/ProcInfo_win.cpp @@ -7,6 +7,7 @@ #include "mozilla/ProcInfo.h" #include "mozilla/ipc/GeckoChildProcessHost.h" #include "mozilla/SSE.h" +#include "gfxWindowsPlatform.h" #include "nsMemoryReporterManager.h" #include "nsNetCID.h" #include "nsWindowsHelpers.h" @@ -85,6 +86,10 @@ nsresult GetCpuTimeSinceProcessStartInMs(uint64_t* aResult) { return NS_OK; } +nsresult GetGpuTimeSinceProcessStartInMs(uint64_t* aResult) { + return gfxWindowsPlatform::GetGpuTimeSinceProcessStartInMs(aResult); +} + RefPtr GetProcInfo(nsTArray&& aRequests) { auto holder = MakeUnique>(); RefPtr promise = holder->Ensure(__func__); diff --git a/toolkit/components/processtools/metrics.yaml b/toolkit/components/processtools/metrics.yaml index 951d075e91e0..c341479f7524 100644 --- a/toolkit/components/processtools/metrics.yaml +++ b/toolkit/components/processtools/metrics.yaml @@ -26,3 +26,19 @@ power: notification_emails: - florian@mozilla.com expires: never + + total_gpu_time_ms: + type: counter + description: > + Total GPU time used by all processes in ms. + bugs: + - https://bugzilla.mozilla.org/show_bug.cgi?id=1743176 + data_reviews: + - https://bugzilla.mozilla.org/show_bug.cgi?id=1743176 + data_sensitivity: + - technical + notification_emails: + - florian@mozilla.com + expires: never + no_lint: + - COMMON_PREFIX