diff --git a/.github/release-notes/v1.1.3.md b/.github/release-notes/v1.1.3.md new file mode 100644 index 0000000..f376b91 --- /dev/null +++ b/.github/release-notes/v1.1.3.md @@ -0,0 +1,48 @@ + +## ⚡ 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 提示。 diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml new file mode 100644 index 0000000..6cced3d --- /dev/null +++ b/.github/workflows/release.yml @@ -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/.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 "" + # 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 '' -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 diff --git a/AI_HANDOFF.md b/AI_HANDOFF.md index 661965f..868f9a6 100644 --- a/AI_HANDOFF.md +++ b/AI_HANDOFF.md @@ -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/.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 `` 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 diff --git a/README.md b/README.md index c218dc1..2273e7a 100644 --- a/README.md +++ b/README.md @@ -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。 @@ -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 提示。 diff --git a/WinBatLens.csproj b/WinBatLens.csproj index a679c50..eaa7495 100644 --- a/WinBatLens.csproj +++ b/WinBatLens.csproj @@ -13,9 +13,9 @@ WinBat Lens WinBat Lens Windows 電池健康度視覺化儀表板與 powercfg /batteryreport 解析工具 - 1.1.2 - 1.1.2.0 - 1.1.2.0 + 1.1.3 + 1.1.3.0 + 1.1.3.0 diff --git a/build-release.ps1 b/build-release.ps1 index 3b74eb9..b6c5378 100644 --- a/build-release.ps1 +++ b/build-release.ps1 @@ -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 diff --git a/installer/WinBatLens.iss b/installer/WinBatLens.iss index 96f79a5..a04b03c 100644 --- a/installer/WinBatLens.iss +++ b/installer/WinBatLens.iss @@ -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}" diff --git a/release_notes.md b/release_notes.md index c95f222..a98c6cd 100644 --- a/release_notes.md +++ b/release_notes.md @@ -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 ### 工程可靠性與效能