Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

プロパティシートのコントロール表示のちらつき防止 #1424

Merged
merged 2 commits into from
Oct 11, 2020

Conversation

beru
Copy link
Contributor

@beru beru commented Oct 4, 2020

PR の目的

共通設定の画面において、主にリスト系のコントロールの内容を切り替える際にちらついて表示されるので、それがなるべく起きないようにするのが目的です。

カテゴリ

  • その他の問題

PR の背景

画面を操作していてコントロールの表示のちらつきが気になったので下記の対策を入れました。

  • リスト系のコントロールの表示内容を差し替える前後で WM_SETREDRAW メッセージを送る事で表示更新のタイミングを制御
    • コントロールに対して行ってもちらつきが減らなかった場合は親ウィンドウのダイアログ(プロパティシート)に対してメッセージを送るようにしましたが、その場合は後で InvalidateRect を実行しないと画面の表示が更新されないので、そうするようにしました。
  • リソースファイルのプロパティシートの拡張ウィンドウスタイルに WS_EX_COMPOSITED を付けてダブルバッファリングされるようにしました。これをやらないでもちらつきが特に気にならないレベルまで解消される場合もありますが、そうでない場合もあります。メカニズムがいまいち不明なのと、動作環境によって挙動が変わるかもしれません。

PR のメリット

表示のちらつきが減ります。

PR のデメリット (トレードオフとかあれば)

WS_EX_COMPOSITED スタイルを付けてダブルバッファリングしているのでメモリ使用量が増えます。ただし最近のPCであれば問題になるような増加量では無い筈です。

仕様・動作説明

テスト内容

画面表示を目視で確認しました。

リスト表示のちらつきが減ったかどうかの確認は、ドロップダウンリストの選択を切り替えるとリストの表示内容が切り替わるので、その際のちらつきが減ったかどうかを確認しました。

PR の影響範囲

下記の画面に手を入れました。

  • 共通設定
    • メインメニュー
    • ツールバー
    • タブバー
    • キー割り当て
    • カスタムメニュー
    • 強調キーワード

関連 issue, PR

参考資料

https://docs.microsoft.com/en-us/windows/win32/gdi/wm-setredraw
https://docs.microsoft.com/en-us/windows/win32/api/winuser/nf-winuser-invalidaterect
https://devblogs.microsoft.com/oldnewthing/20171018-00/?p=97245
https://devblogs.microsoft.com/oldnewthing/20110124-00/?p=11683

@AppVeyorBot
Copy link

Build sakura 1.0.3141 completed (commit cec4c67bcc by @beru)

Copy link
Contributor

@berryzplus berryzplus left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

ほぼいちゃもんですが ::SendMessageAny は使わないほうがよいと思います。

request changes な指摘はこれだけです。

PRの目的である「ちらつきを抑えたい」は、動かしてみてもちらつきが減ったかぼくには判断できないので、他の方にお任せします。

sakura_core/prop/CPropComCustmenu.cpp Outdated Show resolved Hide resolved
@@ -694,6 +695,8 @@ void CPropCustmenu::SetDataMenuList(HWND hwndDlg, int nIdx)
::DlgItem_SetText( hwndDlg, IDC_EDIT_MENUNAME, m_Common.m_sCustomMenu.m_szCustMenuNameArr[nIdx] );

CheckDlgButtonBool( hwndDlg, IDC_CHECK_SUBMENU, m_Common.m_sCustomMenu.m_bCustMenuPopupArr[nIdx] );
::SendMessageAny( hwndDlg, WM_SETREDRAW, TRUE, 0 );
::InvalidateRect( hwndDlg, NULL, FALSE );
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

ん?これは必要ですか・・・?
(たぶん、必要だから入れてると思いますが念のための確認です。)

window描画の仕組み

  1. 外部から WM_UPDATE を送る。
  2. WM_UPDATE を受け取ったウインドウは、クライアント領域に更新領域があれば自身に WM_PAINT を送る。
  3. WM_PAINT を受け取ったウインドウは、クライアント領域を描画する。

InvalidateRect(hwnd, NULL, FALSE) は、クライアント領域の全域更新を指示する命令です。WM_SETREDRAW で再描画フラグを戻しただけだけだと更新されない仕様だと思うんですが、ここに要るなら他にもいるような気がして、何故ここだけ?と思いました。

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

コントロールのウィンドウの変更に対して WM_SETREDRAW する場合は後で InvalidateRect を呼ばないでも表示が更新されてたんですが、親のダイアログの場合は呼ばないと表示が更新されませんでした。理屈は分かっていません。

//キーワード数を表示する。
DispKeywordCount( hwndDlg );

::SendMessageAny( hwndDlg, WM_SETREDRAW, TRUE, 0 );
InvalidateRect(hwndDlg, NULL, FALSE);
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

ここにはありますね・・・

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

ここも親ダイアログに対しての場合ですね。

@@ -1187,7 +1187,7 @@ BEGIN
LTEXT "閉じるボタン(&X)", IDC_TextTabClose, 124, 116, 48, 8
COMBOBOX IDC_CHECK_DispTabClose, 173, 113, 46, 80, CBS_DROPDOWNLIST | WS_VSCROLL | WS_TABSTOP
PUSHBUTTON "フォント(&F)...", IDC_BUTTON_TABFONT, 232, 113, 51, 14
RTEXT "Font", IDC_STATIC_TABFONT, 102, 127, 180, 17, SS_RIGHT
RTEXT "Font", IDC_STATIC_TABFONT, 102, 127, 180, 12, SS_RIGHT
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

これは必要です? or NOT?

必要なら入れたらよいと思います。
右寄せラベルの高さが大きくなってるのを文字に合わせる変更に見えていて、ちらつき防止と関係あるようには思えないのですが・・・。

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

WS_EX_COMPOSITED を付けるとこの変更入れないと表示に問題がありました。

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

image

高さを調整しないとこのように表示されてしまいます。

Copy link
Contributor Author

@beru beru Oct 5, 2020

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

https://docs.microsoft.com/en-us/windows/win32/winmsg/extended-window-styles

WS_EX_COMPOSITED の解説ですが

Paints all descendants of a window in bottom-to-top painting order using double-buffering. Bottom-to-top painting order allows a descendent window to have translucency (alpha) and transparency (color-key) effects, but only if the descendent window also has the WS_EX_TRANSPARENT bit set. Double-buffering allows the window and its descendents to be painted without flicker. This cannot be used if the window has a class style of either CS_OWNDC or CS_CLASSDC.
Windows 2000: This style is not supported.

と書かれています。bottom-to-top painting order というのが画面のXYのYの上下の事なのかZ-orderの上下の事なのかわかっていませんが(多分後者)この事が関わっていそうです。

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

bottom-to-top painting order というのが画面のXYのYの上下の事なのかZ-orderの上下の事なのかわかっていませんが(多分後者)この事が関わっていそうです。

画面の下から上の順番でコントロールを描画していくってことでしょうね。
通常は上から下の順番で描画されていると考えると、下方向にはみ出したコントロールがこれまで問題にならず、拡張スタイルの追加でおかしくなった理由を説明できると思います。従来ははみ出した部分を下側にあるコントロールで上書きしていたから問題にならず、変更により描画済みのコントロールの上にはみ出して描画するようになったっちゅうことです。

@beru
Copy link
Contributor Author

beru commented Oct 5, 2020

PRの目的である「ちらつきを抑えたい」は、動かしてみてもちらつきが減ったかぼくには判断できないので、他の方にお任せします。

これについてですが変更前と変更後でアプリを両方とも起動して交互に操作して見比べるのを繰り返さないとなかなか判断出来ないと思います。

@AppVeyorBot
Copy link

Build sakura 1.0.3142 completed (commit 2f6454c6c2 by @beru)

@berryzplus berryzplus dismissed their stale review October 7, 2020 14:16

修正を確認したので。

Copy link
Contributor

@berryzplus berryzplus left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

コードで気になる点は解消したと思います。
変更したプロパティーシートでドロップダウンリストが表示できることも確認しました。

変更前の「ちらつく」について定量的な表現で事象を解説してもらえれば、改修確認のデータ取ってもよいです。目視チェックする能力はないので、目視がいい場合は他の人に頼んでください。

@beru
Copy link
Contributor Author

beru commented Oct 7, 2020

変更前の「ちらつく」について定量的な表現で事象を解説してもらえれば、改修確認のデータ取ってもよいです。目視チェックする能力はないので、目視がいい場合は他の人に頼んでください。

いつかHFR(ハイフレームレート) 撮影が出来るカメラを購入して撮影した動画を YouTube において確認出来るようにしようかと思います。

@berryzplus
Copy link
Contributor

変更前の「ちらつく」について定量的な表現で事象を解説してもらえれば、改修確認のデータ取ってもよいです。目視チェックする能力はないので、目視がいい場合は他の人に頼んでください。

いつかHFR(ハイフレームレート) 撮影が出来るカメラを購入して撮影した動画を YouTube において確認出来るようにしようかと思います。

超スロー再生にしたときに問題のある瞬間のコマがどう見えるか、
つまり、「ちらつく」がどういう不具合なのかを説明してもらえればいいっす。

@beru
Copy link
Contributor Author

beru commented Oct 9, 2020

超スロー再生にしたときに問題のある瞬間のコマがどう見えるか、
つまり、「ちらつく」がどういう不具合なのかを説明してもらえればいいっす。

百聞は一見にしかず、ということわざがあるぐらいなので言葉で説明してもきちんと伝わるか自信が無いですが書いてみます。

本来は表示する必要のない途中の絵が見えてしまうのがちらつきの原因です。例えばユーザーが操作をする前にリストボックスにアイテムがたくさん表示されているとして、操作後に下記の処理が行われるとします。

  • LB_RESETCONTENT : リストボックスのアイテムを全消去
  • for ループ
    • LB_ADDSTRING : リストボックスにアイテムを追加
  • LB_SETCURSEL : リストボックスの選択アイテムを設定し、選択アイテムが表示される位置にスクロールを調整
  • LB_SETTOPINDEX : リストボックスの指定アイテムが表示されるようにスクロール位置を調整

上記の処理の途中、例えばリストボックスのアイテムを全消去した後の内容が表示されると、そのコマの絵ではリストボックスのアイテムは空表示になります。上記の処理がすべて完了後にも描画が行われますが、そのコマではリストボックスにアイテムが入った状態の絵になります。

  1. 操作前:リストボックスにアイテムがたくさん表示されている
  2. アイテム消去後:リストボックスのアイテムを全消去された空表示
  3. 処理完了後 : 再度リストボックスにアイテムがたくさん表示されている

途中の 2 の絵は本来は表示する必要は無いもので、表示されるととても短い時間の間に 1 -> 2 -> 3 の3コマが表示されるのでその分画面の表示の切り替わりが頻繁になり、表示がちらついたような印象を受けます。

実際には3コマだけとも限らず、中間の状態のコマがもっとたくさん間に挟まって表示されてしまう事もあります。よく行われる対策としては、LB_ADDSTRING するループの前後で WM_SETREDRAW を送って再描画を抑制します。このPRでやっている事も基本的には同じです。

@berryzplus
Copy link
Contributor

超スロー再生にしたときに問題のある瞬間のコマがどう見えるか、
つまり、「ちらつく」がどういう不具合なのかを説明してもらえればいいっす。

百聞は一見にしかず、ということわざがあるぐらいなので言葉で説明してもきちんと伝わるか自信が無いですが書いてみます。

なんどやっても変更前 455a7ff の挙動を「ちらつく」と感じませんでした。

共通設定 ダイアログ
 メインメニュー タブ
  「種別」を「ファイル操作系」から「編集系」に変える

目視の範疇では 不具合が再現しない です。
百聞は一見に如かず に従うと、何言ってるんだこいつは? になるです。
(別な理由で、正しい対策なんだろうな、と思っていますが。)

本当に目視で「ちらつき」を感じていますか?
発端となった「問題のある操作」を明示してもらえたら、こちらでも再現できるのかもです。

@berryzplus
Copy link
Contributor

本来は表示する必要のない途中の絵が見えてしまうのがちらつきの原因です。

と書かれているので 「ちらつく」がどんな現象であるか? に齟齬はないように思いました。

「ちらつく」は科学的に説明できる現象で、C/C++特有の よくある不具合 です。

  • 超スロー再生にしたときに問題のある瞬間のコマがどう見えるか
    消えて見えるか、不完全な描画状態に見えます。
  • 「ちらつく」がどういう不具合なのか
    再描画の途中過程が(意図せず)見えてしまう不具合です。

よくある不具合なので対策手法の定石があります。

描画途中が見えてしまうことが問題ならば、見せなきゃよいのです。

修正内容は一定区間で発生する描画命令を抑制させるので、定石に照らして適切である、と予想できます。

問題は、実際の不具合事象を確認できていないのに理屈のみを以て approve してよいか? ということで、
ダメなんじゃないかと思ったので「ちらつき」を感知する能力のある人による表示確認を提案した次第です。

@beru
Copy link
Contributor Author

beru commented Oct 10, 2020

変更前

455a7fff55759ba3058a210fa9ca62d1f8eb8baa_IDD_PROP_KEYWORD

変更後

079929f9b9ad367e29b69b029d70b82561c3c9fb_IDD_PROP_KEYWORD

@beru
Copy link
Contributor Author

beru commented Oct 10, 2020

上記のコメントに ScreenToGif でキャプチャして保存したアニメーションGIFファイルを貼り付けました。
等速再生なので違いがわかりにくいかもしれません。

下記のページでは同じ動画を 1/10 の速度で再生しています。
https://beru.github.io/flickering/test.html

@berryzplus
Copy link
Contributor

#1424 (comment)
これは確かにちらついてますね。
(ここまでハッキリちらついてたらさすがに見えるっす。)

よく考えてみたらPRの事象確認には結構な描画性能を持つマシンを使っていて、他の端末での確認は行っていませんでした。
(GeForce GTX 1070 Ti 相当を積んだノートで、実際 #1424 (comment) の操作を試しても再現しませんでした。)

発生メカニズムからして「人の能力」ではなくて「マシンの能力」が影響する可能性に気付きました。

無駄な再描画(=クリアして描画し直し)を繰り返し行なう「不適切なコード」があったとして、
「マシンの能力」が十分であれば即時に再描画が完了するため途中が見えてしまう事態が発生しません。

これをもって改善効果を確認できたとしてよいような気がしますがどうでしょう?

@beru
Copy link
Contributor Author

beru commented Oct 10, 2020

マシンの能力も関係すると思います。ところで一般的にモニタのリフレッシュレートは 60Hzですが、240Hzのゲーミングモニターを使えば(マシンパワーが十分にあれば)中間のコマが更に表示される可能性は増えますね。無駄な中間のコマを表示すると計算量も増えてしまうので、リスト系のコントロールに大量にアイテムを登録する場合には WM_SETREDRAW を使って再描画を抑える事が推奨されています。

Copy link
Contributor

@berryzplus berryzplus left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

問題ないと思います。

@beru
Copy link
Contributor Author

beru commented Oct 11, 2020

レビューありがとうございます。Mergeします。
もし問題が見つかったらあとで修正します。

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

3 participants