Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
10 changes: 10 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,16 @@
## [Unreleased]

### Добавлено
- **Загрузка GPU в «Мониторе»** — новый ряд между CPU и RAM: процент + график, под ним частота и
мощность (`2450 МГц · 21.4 Вт`), в мини-виде только процент. Данные — через **Intel IGCL**,
user-mode API графического драйвера (`ControlLib.dll` приезжает со штатным драйвером Intel и лежит
в System32): driver-free, прав администратора для чтения не нужно, опрос стоит 47 мкс и живёт в том
же таймере 1 Гц, что и остальные графики. Загрузка сверена с собственным счётчиком Windows
(`\GPU Engine(*)\Utilization Percentage`) — сходится. Температуру и обороты вентиляторов IGCL на
встроенном GPU не даёт (пути только для дискретных Arc), поэтому в ряду их нет. Инициализация
ленивая: пока виджет не открыт, IGCL в процесс не подгружается. Не Intel (в т.ч. AMD-модели) —
ряд просто не появляется, виджет остаётся прежним. Разбор раскладки телеметрии и замеры —
[docs/09-power-monitoring.md](docs/09-power-monitoring.md) (XIC-18).
- **`OwlIgnoreDisplay` в config.json** — «Режим совы» умеет держать бодрой только систему, отпуская
экран: с `true` он больше не выставляет `ES_DISPLAY_REQUIRED`, и экран гаснет по обычному таймауту
схемы питания. Для сценария «комп не должен уснуть ради удалённого доступа», где подсвеченная
Expand Down
7 changes: 5 additions & 2 deletions CLAUDE.md
Original file line number Diff line number Diff line change
Expand Up @@ -141,7 +141,7 @@ WMI-событий). `Program.cs`: single-instance mutex → DI-контейне
`TravelChargeMonitor` (SystemIntegration).
- `src/Ui/` остальное: `QuickPanelForm` (панель по удержанию Mi / клику по трею — чистый view
над контроллером, навигация с клавиатуры; ширина фиксированная — при скрытых режимах
растягиваются ячейки), `OsdForm` (всплывашки), `MonitorForm` (виджет Вт/CPU/RAM/°C),
растягиваются ячейки), `OsdForm` (всплывашки), `MonitorForm` (виджет Вт/CPU/GPU/RAM/°C),
`FlyoutForm` + `FlyoutPalette` (общая база флайаутов: borderless tool-window, Region, Esc;
палитра — единственный источник тёмных цветов флайаутов), `FormChrome` (DWM-тёмный
заголовок + WM_SETREDRAW), `ModeUi` (режим → ключ локализации / вид OSD / акцент),
Expand Down Expand Up @@ -170,7 +170,10 @@ WMI-событий). `Program.cs`: single-instance mutex → DI-контейне
`AutoStart` (задача планировщика + самопочинка пути), `AwakeMode` («режим совы»: всегда
`ES_SYSTEM_REQUIRED` + крышка на AC, экран держится `ES_DISPLAY_REQUIRED` — кроме
`OwlIgnoreDisplay: true` в config.json, тогда только «не спать»),
`MicControl`, `KeyActions`, `Sound` (WAV-джинглы), `BatteryInfo`, `PowerDraw`;
`MicControl`, `KeyActions`, `Sound` (WAV-джинглы), `BatteryInfo`, `PowerDraw`,
`GpuTelemetry` (загрузка/ватты/частота iGPU через **Intel IGCL** — user-mode API драйвера Intel,
`ControlLib.dll` из System32; driver-free, без админа, ленивая инициализация; не Intel → ряд GPU
в «Мониторе» просто не появляется, см. `docs/09`);
**HTTP API (XIC-13, opt-in)** — `HttpApi` (хост на `HttpListener`/http.sys, без ASP.NET Core;
создаётся только при включённой фиче → выключено = 0 CPU), `ApiRouter` (авторизация Bearer+SHA-256
constant-time и белый список маршрутов — тестируется на фейках), `ApiSettings`/`ApiSettingsStore`
Expand Down
14 changes: 9 additions & 5 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -27,18 +27,18 @@ auto refresh rate and "owl mode" (stay awake).*
<img src="docs/img/monitor.png" width="440" alt="Monitor window">
</p>

*Monitor window: live graphs of power draw (W), CPU and RAM usage.*
*Monitor window: live graphs of power draw (W), CPU, GPU and RAM usage.*

<p align="center">
<img src="docs/img/monitor-mini.png" width="380" alt="Monitor — compact view">
&nbsp;&nbsp;
<img src="docs/img/monitor-watts.png" width="120" alt="Monitor — watts only">
</p>

*In full view — live graphs: power draw (W), CPU, RAM, **hotspot temperature**
(hot zone in cherry red) and the connected adapter's wattage. Collapses into a compact line
(Power / CPU / RAM) or a single watts readout — via the "view" button or a double-click;
current direction is shown by color (charging green / discharging orange).*
*In full view — live graphs: power draw (W), CPU, **GPU** (clock and watts under the percentage),
RAM, **hotspot temperature** (hot zone in cherry red) and the connected adapter's wattage. Collapses
into a compact line (Power / CPU / GPU / RAM) or a single watts readout — via the "view" button or a
double-click; current direction is shown by color (charging green / discharging orange).*

*The tray icon changes with the active mode:*

Expand Down Expand Up @@ -411,6 +411,10 @@ None of this runs or spends resources while the API is off (the server simply is
- The feature set depends on the model: firmware telemetry (fan RPM) is unsupported on the tested
machine. **Temperature** is shown anyway — not from the firmware, but via Intel DPTF
(WMI `EsifDeviceInformation`), as a line graph in the Monitor.
- **GPU usage** comes from Intel IGCL — the graphics driver's user-mode API (`ControlLib.dll` in
System32, installed with the Intel driver; no administrator rights needed). On machines without
Intel graphics the GPU row simply does not appear. This channel exposes no temperature or fan RPM
for integrated GPUs — only usage, power and clock.

## How it works

Expand Down
22 changes: 13 additions & 9 deletions README.ru.md
Original file line number Diff line number Diff line change
Expand Up @@ -17,29 +17,29 @@
**Никакого WinRing0**, никаких сторонних драйверов и прямого доступа к EC.

<p align="center">
<img src="docs/img/quick-panel.png" width="560" alt="Панель быстрых настроек">
<img src="docs/img/quick-panel.ru.png" width="560" alt="Панель быстрых настроек">
</p>

*Панель быстрых настроек (удержание кнопки Mi): пять режимов производительности,
лимит заряда, «В дорогу» (разовый заряд до 100%), тачпад и сенсорный экран вкл/выкл,
авто-герцовка и «режим совы» (не спать).*

<p align="center">
<img src="docs/img/monitor.png" width="440" alt="Окно «Монитор»">
<img src="docs/img/monitor.ru.png" width="440" alt="Окно «Монитор»">
</p>

*Окно «Монитор»: живые графики потребления (Вт), загрузки CPU и RAM.*
*Окно «Монитор»: живые графики потребления (Вт), загрузки CPU, GPU и RAM.*

<p align="center">
<img src="docs/img/monitor-mini.png" width="380" alt="«Монитор» — компактный вид">
<img src="docs/img/monitor-mini.ru.png" width="380" alt="«Монитор» — компактный вид">
&nbsp;&nbsp;
<img src="docs/img/monitor-watts.png" width="120" alt="«Монитор» — только ватты">
<img src="docs/img/monitor-watts.ru.png" width="120" alt="«Монитор» — только ватты">
</p>

*В полном виде — живые графики: потребление (Вт), CPU, RAM, **температура** «горячей точки»
(горячая зона — вишнёвым) и мощность подключённого адаптера. Сворачивается в компактную строку
(Power / CPU / RAM) или в один показатель ватт — кнопкой «вид» или двойным кликом; направление
тока — цветом (заряд зелёный / разряд оранжевый).*
*В полном виде — живые графики: потребление (Вт), CPU, **GPU** (под процентом — частота и ватты),
RAM, **температура** «горячей точки» (горячая зона — вишнёвым) и мощность подключённого адаптера.
Сворачивается в компактную строку (Power / CPU / GPU / RAM) или в один показатель ватт — кнопкой
«вид» или двойным кликом; направление тока — цветом (заряд зелёный / разряд оранжевый).*

*Значок в трее меняется по активному режиму:*

Expand Down Expand Up @@ -412,6 +412,10 @@ curl -X POST http://192.168.1.50:58125/travel \
- Набор функций зависит от модели: прошивочная телеметрия (обороты вентиляторов) на проверенной
машине не поддерживается. **Температуру** при этом показываем — не из прошивки, а через Intel DPTF
(WMI `EsifDeviceInformation`), строкой-графиком в «Мониторе».
- **Загрузка GPU** идёт через Intel IGCL — user-mode API графического драйвера (`ControlLib.dll`
из System32, ставится вместе с драйвером Intel; прав администратора не требует). На машинах без
графики Intel ряд GPU в «Мониторе» просто не появляется. Температуру и обороты вентиляторов этот
канал на встроенном GPU не отдаёт — только загрузку, мощность и частоту.

## Как это работает

Expand Down
11 changes: 10 additions & 1 deletion ROADMAP.md
Original file line number Diff line number Diff line change
Expand Up @@ -55,6 +55,11 @@
через Intel DPTF: WMI `root\wmi\EsifDeviceInformation`, `.Temperature` уже в °C, берём максимум
среди активных доменов. Ранее числился закрытым как «невозможно» — переоткрыт на проверенном
сигнале (см. ниже). Горячая зона графика подсвечивается вишнёвым (порог от ACPI `CriticalTripPoint`) (unreleased).
- **Загрузка GPU в «Мониторе»** — ряд между CPU и RAM: процент + график, под ним частота и мощность
(`2450 МГц · 21.4 Вт`); в мини-виде только процент. Driver-free через **Intel IGCL** — user-mode API
графического драйвера (`ControlLib.dll` из System32, прав администратора не нужно даже на чтение):
один `ctlPowerTelemetryGet`, загрузка и ватты считаются как приращения накопительных счётчиков.
Не Intel → ряда просто нет. Разбор и замеры — [docs/09-power-monitoring.md](docs/09-power-monitoring.md) (XIC-18, unreleased).
- **Сенсорный экран вкл/выкл** — то же, что тачпад, для тач-экрана ноутбука: действие для клавиш +
ячейка в панели (левее тачпада, только если тачскрин есть) + OSD. Общая база `HidNodeToggle`
(тачпад `U:0005`, тачскрин `U:0004`). Вклад контрибьютора (PR #4) (unreleased).
Expand Down Expand Up @@ -120,7 +125,11 @@
- Ручной цикл частоты экрана по клавише — не нужна: висела «если попросят», запросов не было,
авто-герцовка по питанию покрывает сценарий (XIC-2 отменена; вернёмся, если попросят тестеры).
- ICC-профили, splash-скины, служба + named pipe + protobuf (архитектура MIControl).
- GPU-режим, обороты вентиляторов — WMI на нашей модели не отдаёт (температуры — берём через DPTF, см. «Сделано»).
- **Обороты вентиляторов** — WMI на нашей модели не отдаёт, EC закрыт своим драйвером; в Intel IGCL
`ctlEnumFans` на встроенном GPU тоже отказ (это путь для дискретных Arc, как и `ctlEnumTemperatureSensors`).
GPU-**режим** (переключение/профили самого GPU) — тоже нет. При этом **телеметрия** iGPU
✅ **сделана** driver-free через IGCL (загрузка/ватты/частота, см. «Сделано»), а температуры — через
DPTF; так что «GPU не отдаётся» больше не верно целиком.
- WinRing0 / прямой доступ к EC — цель проекта в том, чтобы обойтись без них.
- Lottie-анимации — заменены алгоритмическими (+10 МБ и второй рендер-стек не окупаются).
- NFC-фичи (метки → действия) — модуль есть в железе, но в Windows как устройство не выведен
Expand Down
53 changes: 53 additions & 0 deletions docs/09-power-monitoring.md
Original file line number Diff line number Diff line change
Expand Up @@ -83,3 +83,56 @@ Driver-free (только чтение), на нём построены:
Строка-график «горячей точки» в «Мониторе» — driver-free через **Intel DPTF** (WMI
`root\wmi\EsifDeviceInformation`, `.Temperature` в °C, максимум активных доменов). Тема раньше
числилась закрытой как «невозможно», переоткрыта на этом сигнале — см. ROADMAP и `05-open-questions.md`.

## GPU: загрузка, мощность, частота (Intel IGCL)

Ряд GPU берётся из **Intel IGCL** — user-mode API графического драйвера. `ControlLib.dll` лежит в
`System32` и приезжает со штатным драйвером Intel: **мы ничего не устанавливаем, и права
администратора для чтения не нужны** — тот же driver-free принцип, что и WMI. Реализация —
`src/SystemIntegration/GpuTelemetry.cs`, инициализация ленивая (пока виджет не открыли, IGCL в
процесс не подгружается).

Всё берётся одним вызовом `ctlPowerTelemetryGet`. Раскладка `ctl_power_telemetry_t` (проверено на
TM2424, `ControlLib 1.2.291.0`, адаптер `0x8086:0xB080`): структура 512 байт, внутри цепочка
элементов `ctl_oc_telemetry_item_t` = `uint bSupported; uint units; uint type; pad; double value`.

| смещение | поле | единицы | есть? |
|---|---|---|---|
| 8 | `timeStamp` | секунды | да |
| 32 | `gpuEnergyCounter` | джоули | да |
| 56 | `gpuVoltage` | — | **нет** |
| 80 | `gpuCurrentClockFrequency` | МГц | да |
| 104 | `gpuCurrentTemperature` | — | **нет** |
| 128 | `globalActivityCounter` | секунды активности | да |
| 152 | `renderComputeActivityCounter` | секунды активности | да |
| 176 | `mediaActivityCounter` | секунды активности | да |

Счётчики **накопительные**, поэтому загрузка и ватты — приращения:
`загрузка % = Δactivity / Δtime · 100`, `Вт = Δjoules / Δtime`. Первая выборка после открытия
виджета только набирает базу и даёт `NaN` — график начинается со второй точки. При повторном
открытии зовём `Reset()`, иначе первое значение размажется по времени, что виджет был закрыт.

Тонкости и границы:

- `ctl_version_info_t` — это **простой `uint32`** (`major << 16 | minor`), а не структура из двух
`uint16`. `ctl_init_args_t` = 36 байт, `AppVersion = 1.0`; реализация рапортует 1.1.
- На старте сверяем `bSupported`/`units`/`type` по всем четырём используемым смещениям. Не совпало —
выключаемся навсегда: лучше не показать ряд, чем показать мусор.
- **Температуры и оборотов вентиляторов IGCL на встроенном GPU не даёт**: все `ctlEnum*`
(`TemperatureSensors`, `Fans`, `PowerDomains`, `EngineGroups`, `FrequencyDomains`) отвечают
отказом — это пути для дискретных Arc. Температуры мы и так берём через DPTF (выше).
- `renderCompute` почти дублирует `global` (разница ~1.3 п.п. под нагрузкой), `media` под 3D-нагрузкой
всегда 0 — в UI выведен только `global`.
- AMD-модели: IGCL там нет (`ctlInit` не поднимется, либо `ControlLib.dll` вообще отсутствует и
P/Invoke бросит `DllNotFoundException`) — ряд просто не появится. Аналог у AMD — `atiadlxx.dll`,
отдельная история.

### Проверено на железе (TM2424, 2026-07-29)

Прогон бенчмарка, 829 выборок / 216 с: загрузка 0.9 → 99.7 %, мощность 0.01 → **24 Вт**,
частота 900 → **2500 МГц** (под длительной нагрузкой оседает на 2390–2440 — троттлинг),
корреляция загрузки и мощности **r = 0.988**. Сверка загрузки с собственным счётчиком Windows
`\GPU Engine(*)\Utilization Percentage`: IGCL ~42.5 % против Windows ~45.4 % — сходится.
Цена вызова: **медиана 47 мкс**, p95 77 мкс (кадр при 60 Гц — 16666 мкс), поэтому опрос живёт
в том же UI-таймере 1 Гц, без воркера. `ctlInit` + enumerate — 8.9 мс однократно при первом показе;
приватная память не растёт (`ControlLib.dll` 296 КБ и `IntelControlLib.dll` 712 КБ мапятся как образы).
Loading
Loading