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
48 changes: 48 additions & 0 deletions .github/release-notes/v1.1.3.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,48 @@
<!-- prerelease -->
## ⚡ WinBat Lens v1.1.3 — 效能最佳化(預發行版)

純效能改動。**顯示的數值、更新頻率與所有文案完全沒有改變。**

這是一支常駐系統匣、每秒取樣一次的監測程式,先前大部分開銷花在重複計算不會改變的東西。這一版把那些重複工作拿掉。

### 每秒取樣路徑

- **GPU Engine 執行個體名稱只解析一次**。原本每秒對約 600 個執行個體做一次轉小寫加兩次字串切割,等於每秒產生約 1,800 個內容與上一秒完全相同的暫時字串。
- **顯示卡名稱只解析一次**:介面卡清單在執行期間不會變,原本卻每秒重新掃描。
- **彙總用的字典改為原地清空後重複使用**,不再每秒重建。
- **每秒只讀一次電池**:電壓路徑原本會重複發送三個裝置 IOCTL 去拿呼叫端已經有的讀值。
- **電池 IOCTL 改用釘選堆疊變數**:每秒省下 6 組原生記憶體配置與 5 次結構複製。

### 縮到系統匣時

- **硬體感測器掃描從 1 秒放寬到 5 秒**,與托盤取樣節奏一致。這是背景 CPU 最大的單一開銷,原本五次裡有四次的結果直接被丟棄。
- **工作集回收改由堆積成長觸發**(≥8 MB),不再每 60 秒無條件做一次會封鎖的 gen2 回收。
- **修正 handle 洩漏**:先前每次回收都會配置一個行程物件與一個 OS handle 且都沒有釋放。

### 畫面

- 系統匣提示文字只在內容改變時才更新(那是一次系統呼叫,不是單純寫欄位)。
- 波形圖的點集合改為原地寫入;Y 軸刻度只在刻度改變時重寫。

---

### ⚠️ 為什麼標記為預發行版

這一版改動的是硬體讀取路徑(電池 IOCTL、GPU 效能計數器解析、感測器節奏)。GitHub Actions 已在 Windows 上通過建置(含 `-warnaserror`,0 警告 0 錯誤)、單元測試與單檔發行封裝,**但 CI 機器沒有電池、沒有獨立顯示卡,也沒有 GPU Engine 效能計數器**,因此下列項目尚未在實機上驗證:

1. 獨顯負載百分比是否仍隨負載變動。
2. 電池電壓、溫度與充放電瓦數是否仍讀得到。
3. 縮到系統匣再還原後,獨顯瓦數是否立即恢復每秒更新。
4. 波形圖三條線是否正常繪製。

若你在實機上遇到上述任一項異常,請回報 issue,並可繼續使用 v1.1.2。

### 📥 下載

| 檔案 | 說明 |
|---|---|
| `WinBatLens_v1.1.3_Setup_x64.exe` | Inno Setup 安裝版,支援就地更新 |
| `WinBatLens_v1.1.3_Portable_x64.exe` | 單一可攜執行檔 |
| `WinBatLens_v1.1.3_Portable_x64.zip` | 含執行檔、README 與授權檔的 ZIP |

發行檔未經 Authenticode 簽署,可能觸發 Windows SmartScreen 提示。
105 changes: 105 additions & 0 deletions .github/workflows/release.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,105 @@
name: Release

# Builds the three published artifacts on a real Windows runner and attaches
# them to a GitHub Release. Triggered by pushing a version tag, e.g. v1.1.3.
#
# The build itself is build-release.ps1 — the same script used locally — so a
# release built here and one built by hand cannot drift apart.

on:
push:
tags: ["v*"]

permissions:
contents: write

jobs:
release:
runs-on: windows-latest
timeout-minutes: 45
steps:
- name: Checkout
uses: actions/checkout@v4

- name: Set up .NET 10
uses: actions/setup-dotnet@v4
with:
dotnet-version: 10.0.x

# build-release.ps1 skips the installer with a warning when ISCC.exe is
# absent, which would silently publish a release missing its Setup.exe.
- name: Install Inno Setup 6
run: choco install innosetup --no-progress -y

# A tag that disagrees with the version baked into the binaries would
# produce assets whose names do not match the release. Fail before the
# build rather than after publishing.
- name: Verify tag matches project version
shell: pwsh
run: |
$tag = $env:GITHUB_REF_NAME
$scriptVersion = (Select-String -Path build-release.ps1 -Pattern '^\$Version = "(.+)"').Matches[0].Groups[1].Value
$csprojVersion = ([xml](Get-Content WinBatLens.csproj)).Project.PropertyGroup.Version | Where-Object { $_ }
$issVersion = (Select-String -Path installer/WinBatLens.iss -Pattern '#define MyAppVersion "(.+)"').Matches[0].Groups[1].Value

Write-Host "tag=$tag script=$scriptVersion csproj=$csprojVersion iss=$issVersion"

if ($tag -ne "v$scriptVersion") { throw "Tag $tag does not match build-release.ps1 version $scriptVersion" }
if ($csprojVersion -ne $scriptVersion) { throw "WinBatLens.csproj version $csprojVersion does not match $scriptVersion" }
if ($issVersion -ne $scriptVersion) { throw "installer/WinBatLens.iss version $issVersion does not match $scriptVersion" }

- name: Build release packages
shell: pwsh
run: ./build-release.ps1

- name: Verify all three artifacts exist
shell: pwsh
run: |
$version = $env:GITHUB_REF_NAME.TrimStart('v')
$expected = @(
"dist/WinBatLens_v${version}_Setup_x64.exe",
"dist/WinBatLens_v${version}_Portable_x64.exe",
"dist/WinBatLens_v${version}_Portable_x64.zip"
)
foreach ($file in $expected) {
if (-not (Test-Path $file)) { throw "Expected release artifact is missing: $file" }
$mb = [math]::Round((Get-Item $file).Length / 1MB, 2)
Write-Host "$file ($mb MB)"
}

- name: Upload build artifacts
uses: actions/upload-artifact@v4
with:
name: winbatlens-${{ github.ref_name }}
path: dist/*
if-no-files-found: error

# Release notes live in .github/release-notes/<tag>.md so the published
# text is reviewed in the same pull request as the code it describes.
# Without one, fall back to GitHub's generated changelog.
#
# A release is marked pre-release when its tag carries a suffix
# (v1.2.0-rc1) or when its notes file opts in with a "<!-- prerelease -->"
# marker on any line. Everything else publishes as a normal release, so
# this workflow does not quietly hold future versions back.
- name: Publish GitHub Release
shell: pwsh
env:
GH_TOKEN: ${{ github.token }}
run: |
$tag = $env:GITHUB_REF_NAME
$notes = ".github/release-notes/$tag.md"

$isPrerelease = $tag -match '^v[0-9.]+-'
if ((Test-Path $notes) -and (Select-String -Path $notes -Pattern '<!-- prerelease -->' -Quiet)) {
$isPrerelease = $true
}

$ghArgs = @("release", "create", $tag, "--title", "WinBat Lens $tag", "--verify-tag")
if ($isPrerelease) { $ghArgs += "--prerelease" }
if (Test-Path $notes) { $ghArgs += @("--notes-file", $notes) }
else { $ghArgs += "--generate-notes" }
$ghArgs += (Get-ChildItem dist -File | ForEach-Object { $_.FullName })

Write-Host "prerelease=$isPrerelease"
gh @ghArgs
27 changes: 27 additions & 0 deletions AI_HANDOFF.md
Original file line number Diff line number Diff line change
@@ -1,5 +1,32 @@
# Project State & Handoff

## v1.1.3 release pipeline

Release artifacts are now built by GitHub Actions rather than only by hand.
`.github/workflows/release.yml` fires on a `v*` tag, runs the very same
`build-release.ps1` on `windows-latest` (with Inno Setup installed via choco so
the installer is never silently skipped), and attaches all three artifacts to a
GitHub Release.

Guards, because a bad release is expensive to retract:
- The tag, `build-release.ps1`, `WinBatLens.csproj` and `installer/WinBatLens.iss`
versions must all agree, checked **before** the build starts.
- All three expected artifact filenames must exist before anything is published.
- Release notes come from `.github/release-notes/<tag>.md`, so the published
text is reviewed in the same pull request as the code it describes.
- A release is marked pre-release when the tag carries a suffix (`v1.2.0-rc1`)
or its notes file contains a `<!-- prerelease -->` marker. Future normal tags
publish as normal releases; the flag is not hardcoded.

v1.1.3 itself is tagged pre-release on purpose: it is the memory/CPU pass
below, which reworks hardware-read paths that CI cannot exercise (the runner has
no battery, no discrete GPU and no GPU Engine counters). Promote it to latest
from the GitHub UI once it has been confirmed on real hardware.

Version bumped to 1.1.3 in `WinBatLens.csproj`, `build-release.ps1`,
`installer/WinBatLens.iss` and `README.md`.


## Memory + CPU optimization pass (latest)

Goal: cut what this always-on tray monitor costs per tick, in both allocations
Expand Down
19 changes: 16 additions & 3 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,19 @@

Windows 電池健康度與即時功耗監測儀表板,使用 WPF 顯示 `powercfg /batteryreport`、電池驅動資料與可取得的硬體感測值。

## v1.1.3 變更重點

純效能最佳化,顯示的數值、更新頻率與文案都沒有改變。

- GPU Engine 執行個體名稱只解析一次並快取,不再每秒對約 600 個執行個體重做字串切割。
- 顯示卡名稱、彙總字典改為只算一次或重複使用;每秒只讀一次電池,不再重複發送 IOCTL。
- 電池 IOCTL 改用釘選堆疊變數,輪詢路徑上不再配置原生記憶體。
- 縮到系統匣時,硬體感測器掃描從 1 秒放寬到 5 秒,與托盤取樣節奏一致。
- 工作集回收改由堆積成長觸發,不再每分鐘無條件做一次會封鎖的 GC;並修正每次回收洩漏一個行程 handle 的問題。
- 系統匣提示文字、波形圖點集合與 Y 軸刻度改為只在內容真的改變時才更新。

此版本標記為預發行版:上述改動動到硬體讀取路徑,CI 已在 Windows 上通過建置與測試,但尚未在具備電池與獨立顯示卡的實機上完整驗證。

## v1.1.2 變更重點

- 升級至 .NET 10 LTS。
Expand Down Expand Up @@ -36,9 +49,9 @@ Windows 可提供外接電源是否足夠的判定(Adequate/Inadequate/Not

最新版本請至 [GitHub Releases](https://github.com/nojackno2-ctrl/WinBat-Lens/releases) 下載:

- `WinBatLens_v1.1.2_Setup_x64.exe`:Inno Setup 安裝版。
- `WinBatLens_v1.1.2_Portable_x64.exe`:單一可攜執行檔。
- `WinBatLens_v1.1.2_Portable_x64.zip`:含執行檔、README 與授權檔的 ZIP。
- `WinBatLens_v1.1.3_Setup_x64.exe`:Inno Setup 安裝版。
- `WinBatLens_v1.1.3_Portable_x64.exe`:單一可攜執行檔。
- `WinBatLens_v1.1.3_Portable_x64.zip`:含執行檔、README 與授權檔的 ZIP。

目前公開發行檔的簽章需由發行者在本機提供 Authenticode 憑證或簽章服務後完成;沒有簽章的檔案可能觸發 Windows SmartScreen 提示。

Expand Down
6 changes: 3 additions & 3 deletions WinBatLens.csproj
Original file line number Diff line number Diff line change
Expand Up @@ -13,9 +13,9 @@
<Company>WinBat Lens</Company>
<Product>WinBat Lens</Product>
<Description>Windows 電池健康度視覺化儀表板與 powercfg /batteryreport 解析工具</Description>
<Version>1.1.2</Version>
<AssemblyVersion>1.1.2.0</AssemblyVersion>
<FileVersion>1.1.2.0</FileVersion>
<Version>1.1.3</Version>
<AssemblyVersion>1.1.3.0</AssemblyVersion>
<FileVersion>1.1.3.0</FileVersion>

<!-- Memory tuning for a long-running background tray app -->
<!-- Workstation (non-server) GC keeps a single small heap instead of one per CPU core -->
Expand Down
2 changes: 1 addition & 1 deletion build-release.ps1
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@ $ErrorActionPreference = "Stop"
$ProjectRoot = $PSScriptRoot
Set-Location $ProjectRoot

$Version = "1.1.2"
$Version = "1.1.3"
$DistDir = Join-Path $ProjectRoot "dist"
$PortableDirName = "WinBatLens_v$Version`_Portable_x64"
$PortableDir = Join-Path $DistDir $PortableDirName
Expand Down
2 changes: 1 addition & 1 deletion installer/WinBatLens.iss
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
; WinBat Lens Inno Setup Script
#define MyAppName "WinBat Lens"
#define MyAppVersion "1.1.2"
#define MyAppVersion "1.1.3"
#define MyAppPublisher "WinBat Lens Team"
#define MyAppExeName "WinBatLens.exe"
#define MyAppId "{{D2B3F0E1-8E4B-4D2A-9A2C-5F1B3E7A902A}"
Expand Down
40 changes: 40 additions & 0 deletions release_notes.md
Original file line number Diff line number Diff line change
@@ -1,3 +1,43 @@
## ⚡ WinBat Lens v1.1.3 — 效能最佳化(預發行版)

純效能改動。**顯示的數值、更新頻率與所有文案完全沒有改變。**

這是一支常駐系統匣、每秒取樣一次的監測程式,先前大部分開銷花在重複計算不會改變的東西。這一版把那些重複工作拿掉。

### 每秒取樣路徑

- **GPU Engine 執行個體名稱只解析一次**。原本每秒對約 600 個執行個體做一次轉小寫加兩次字串切割,等於每秒產生約 1,800 個內容與上一秒完全相同的暫時字串。
- **顯示卡名稱只解析一次**:介面卡清單在執行期間不會變,原本卻每秒重新掃描。
- **彙總用的字典改為原地清空後重複使用**,不再每秒重建。
- **每秒只讀一次電池**:電壓路徑原本會重複發送三個裝置 IOCTL 去拿呼叫端已經有的讀值。
- **電池 IOCTL 改用釘選堆疊變數**:每秒省下 6 組原生記憶體配置與 5 次結構複製。

### 縮到系統匣時

- **硬體感測器掃描從 1 秒放寬到 5 秒**,與托盤取樣節奏一致。這是背景 CPU 最大的單一開銷,原本五次裡有四次的結果直接被丟棄。
- **工作集回收改由堆積成長觸發**(≥8 MB),不再每 60 秒無條件做一次會封鎖的 gen2 回收。
- **修正 handle 洩漏**:先前每次回收都會配置一個行程物件與一個 OS handle 且都沒有釋放。

### 畫面

- 系統匣提示文字只在內容改變時才更新(那是一次系統呼叫,不是單純寫欄位)。
- 波形圖的點集合改為原地寫入;Y 軸刻度只在刻度改變時重寫。

---

### ⚠️ 為什麼標記為預發行版

這一版改動的是硬體讀取路徑(電池 IOCTL、GPU 效能計數器解析、感測器節奏)。GitHub Actions 已在 Windows 上通過建置(含 `-warnaserror`,0 警告 0 錯誤)、單元測試與單檔發行封裝,**但 CI 機器沒有電池、沒有獨立顯示卡,也沒有 GPU Engine 效能計數器**,因此下列項目尚未在實機上驗證:

1. 獨顯負載百分比是否仍隨負載變動。
2. 電池電壓、溫度與充放電瓦數是否仍讀得到。
3. 縮到系統匣再還原後,獨顯瓦數是否立即恢復每秒更新。
4. 波形圖三條線是否正常繪製。

若你在實機上遇到上述任一項異常,請回報 issue,並可繼續使用 v1.1.2。

---

## 📈 WinBat Lens v1.1.2

### 工程可靠性與效能
Expand Down
Loading