世界を動かす技術を、日本語で。

古代X11スケーリング技術

概要

  • X11 での DPIスケーリングマルチモニター対応 の不可能説への挑戦
  • OpenGL を用いた ピクセル単位のスケーリング制御 の実験
  • 複数ディスプレイ 間での物理サイズ維持の実証
  • Xサーバ から取得した情報を用いた 動的サイズ調整
  • 結論: 「できない」と言われても実際には可能 であるという実体験

X11のDPIスケーリングとマルチモニター対応の実態

  • 世間ではX11はDPIスケーリングや複数モニター対応ができない という意見が多い
  • 実際に検証するために、複数の異なるサイズ・解像度のディスプレイを用意
  • xrandrコマンド で各ディスプレイの物理サイズと解像度を取得
    • eDP (ラップトップ)、DisplayPort-0 (モニター)、DisplayPort-1 (TV)の構成
  • OpenGLとGLX でウィンドウとシェーダーを用意し、 常に2インチの円 を描画することを目標
  • シェーダー内のradius変数 が円の半径(ピクセル数)を制御

実装アプローチ

  • XサーバのConfigureNotifyイベント でウィンドウ位置やサイズの変化を検知
  • 各モニターの物理幅(mm)と仮想幅(ピクセル) をXRRScreenResourcesから取得
    • XRROutputInfo で物理幅を、 XRRCrtcInfo でピクセル幅・位置を取得
  • ピクセル幅/物理幅からDPIを計算し、目的の物理サイズに合わせてradiusを調整
  • モニター間でウィンドウを移動すると、常に同じ物理サイズの円が描画される ことを確認

実験結果と考察

  • 各ディスプレイで2インチの円が表示されることを実証
    • TVでは4K解像度にも関わらず、物理サイズは維持
  • 計測誤差(TVの実測サイズとスペック表記の違い) による微小なズレも経験
  • プログラムはリモートのルーターで実行しても問題なく動作
  • X11+OpenGL+MIT-MAGIC-COOKIE-1でのリモート描画の有効性 も確認

「できない」と言われる理由と反論

  • 「X11ではDPIスケーリングやマルチモニターはできない」という通説 は必ずしも正しくない
  • 必要な情報はXサーバから取得可能 で、プログラム側で適切に処理すれば実現可能
  • 「できない」と言われても、自分で試してみることの重要性
  • 実際に動作する例を示すことで、誤解を払拭

応用例とまとめ

  • Shape Extensionを使ったオンスクリーン定規 も作成
  • 端末の行高さ(1/8インチ)も正確に測定可能
  • 「できない」と言われていることも、実際には技術的に可能
  • 実験と検証による知見の重要性

Hackerたちの意見

この投稿大好き!前の同僚のGを思い出すな。彼はまさにこの態度で、やりたいことをほとんど成果に結びつけてたんだよね。

実際、これはちょっと悪質で無知な投稿だね。もしくは、無知な人たちの主張を否定するための間違い(知っててやったのかどうかは不明)かもしれない。空間をしっかり理解してる人は、X11で物理ディスプレイサイズを取得して、それをレンダリングするピクセル数にマッピングするのが不可能だなんて言わないよ。これは数十年前から可能だったし、完全に簡単ではないけど、良いUIスケーリングをする上での難しい部分ではない。良いUIスケーリングには、動的なスケーリング変更のためのインフラ、同じシーン内でのディスプレイごとの異なるスケールファクター、どんなスケールファクターでもシャープなラインを保証するための仕組みが必要なんだ。これらの問題は、X11でも追加の努力で解決できたはずで、一部は部分的な解決策として実現されてた。コミュニティは、Waylandスタックにエネルギーを注ぐことを選んだんだよ。

今日はWaylandでのスケーリングよりもずっと良さそうだね。ターゲット解像度で直接レンダリングするから、OSXが広めた「2倍スケールで描いてから縮小」っていうやり方をしなくて済むし。あの方法だと、パフォーマンスが落ちるし、出力がぼやけちゃうからね。コンポジタがカバーすべき特別なケースは、クライアントが2つの出力にまたがっているときだけ。それでも、高いサイズでレンダリングして、1つの出力では完璧な出力が得られるし、もう1つではぼやけるという同じ欠点があるから、やっぱりこっちの方がいいよね。Waylandが最初からこうしなかったのは不思議だな。クライアントにほとんどのことを任せる哲学なのに。任意のスケーリングをするためには、アプリに「M×Nピクセルのバッファにレンダリングしてて、合成される出力のスケーリングファクターはX.Yだよ」って伝えるだけでいいんだよね。その後は、クライアントが実際の座標でイベントを処理して、特定のコンテキストに最適な方法でスケールできる。ブラウザやPDFビューア、画像処理アプリなんかで、任意の解像度でレンダリングできないのは、品質とパフォーマンスを求めるときにすごくイライラするよね。Waylandでもやっとそれが実現できるといいな。

ツールキット(Motif、Tk、Gtk、Qtなど)は fractional scaling に対応できなかったから、Waylandが簡単な道を選んでたら、すべてのアプリが壊れちゃうところだったよ。

"2倍スケールで描いてから縮小"っていうやり方を広めたのはOSXだよね。元々OS Xは、2倍スケールで描いてから縮小することなく描画するのがデフォルトだったんだ。ハードウェアが2倍スケールに必要なピクセル数を持つように設計されてたからね。例えば、2012年の初代Retina MacBook Proは、前の非Retina MacBook Proの幅と高さがどちらも2倍だった。結局、ハードウェアのコストが高くなりすぎて、これが難しくなったんだと思う。例えば、27インチの5K LCDパネルと27インチの4KパネルのSKUはどれだけ違うのか?でもAppleが整数スケーリングファクターを採用して縮小することを決める前に、もっと伝統的なアプローチを試してたんだ。TigerやLeopardなどの初期のOS Xリリースでそれが見られるよ。問題は、Apple自身が自社アプリに実装するのにかなりの労力がかかって、サードパーティアプリの採用が低くなるのを知ってたことだね。このLeopardのHiDPIレンダリングの例を見てみて:https://cdn.arstechnica.net/wp-content/uploads/archive/revie... これはAppleのTextEditアプリで、バグがあったんだ。スケーリングファクターを非整数に変更するためのいいUIもあったよ:https://superuser.com/a/13675

OS Xのグラフィックスって、ずっとDisplay Postscript/PDF技術に基づいてるんじゃなかったっけ?なんで2倍でレンダリングしてダウンサンプリングしなきゃいけないの?ベクターベースのプリミティブをネイティブ解像度でそのままレンダリングすればいいのに。

OSXで流行った「2倍スケールで描いてから縮小する」ダンスをやってるけど、Linuxはそれをやってない。 > Waylandが最初からこうしなかったのは変だね。最初は整数スケールファクター用にやってたし、後からは分数スケールファクターにも対応したけど、一部のWaylandベースの環境は早くからそれを実装してたよ。

Waylandは2022年からX11スタイルの分数スケーリングをサポートしてるよ: https://wayland.app/protocols/fractional-scale-v1 。QtとGTKの両方がWaylandでの分数スケーリングをサポートしてる。

最初の画像が十分大きければ、ダウンスケーリングによるぼやけはそれほど悪くないよ。例えば、1.3ピクセル対10.1ピクセルみたいな感じ。

Windowsはずっとこれを試してたけど、実際にちゃんと動くアプリはなかったよ。Excelがデバイスに依存しないピクセルでまともに表示できるようにするのに、何年もかけたけど、やっぱり人は生のピクセルで考えちゃうのが難しいんだよね。

だから、ぼやけたスーパーレゾリューションに基づくダウンサンプリングのミームがどこから来たのか理解できない。もしそうなら、スーパーレゾリューションアンチエイリアシング[1]は何なの?ダウンサンプリングされた解像度よりも高い解像度でレンダリングされた画像は、通常、ダウンサンプリングされた解像度でレンダリングされた画像よりもシャープだよ。これは、高周波成分をよりよく保持するから。ダウンサンプリングに基づく他のアンチエイリアシング技術もいくつかあって、すべて信号対雑音比を向上させる。これってUIにも効くんじゃないの?ほとんどがベクターグラフィックスだし。ビットマップアイコンは更新が必要だけど、他のUI(テキスト)はシャープであるべきだよね。1ピクセルの線(完全に水平または垂直)について言及する人がいるのは知ってる。その後、1.25倍とかに掛け算して、「ああ、見て、0.25ピクセルは嘘だ、だから分数スケーリングはフェイクだ」って言う(スウェイのドキュメントは今でもこれを言及してる)。これは、非常にニッチなメンタルエクササイズ以外では実際には成立しないように思える。十分に高い解像度では、私たちが話しているディスプレイのケースでは、1ピクセルの線が本当に欲しいの?ほとんど見えないよ。今、Linuxでこの問題に直面してる。さらに、線がドラッグ可能な場合、クリックゾーンも小さくなりすぎる。物理的な寸法を持つ何かが欲しいだろうし、それはおそらく複数のピクセルを必要とするだろう。その時点で、見えないアンチエイリアスが必要になるだろう。さらに、1ピクセルの線は、プログラムが指定した色である必要はない。私の画面上の完全に水平で垂直な線のほとんどは灰色がかってる。AAのアーティファクトがあれば、色が少し変わるけど、実質的な影響はないと思う。そうだとしたら、スーパーレゾリューションはかなりうまく機能するはずだ。じゃあ、本当に欲しいのは次のようなものだね:1. ほとんどの「デスクトップ」アプリケーションに対するスーパーレゾリューションスケーリング。2. 一部のフルスクリーンアプリケーション(ゲーム、動画再生)にはネイティブ解像度を与え、動画再生のようなアプリには画面上の矩形のネイティブ解像度を与える。これにより、これらのアプリケーションで情報損失を引き起こす可能性のある高解像度でレンダリングしてからダウンサンプリングするのを避けられる。3. これをセッション単位ではなく、アプリケーション単位で行う。LinuxのDEはこれを実装していない。KDEはセッション単位を実装しているけど、柔軟性が足りない。起動時に各アプリケーションごとに設定する必要がある。 [1]: https://en.wikipedia.org/wiki/Supersampling

Linuxを使ってるけど、コンポジタのコードを書いたり、仕組みを詳しく知らない自分からすると、WaylandはX11よりも fractional scaling(小数スケーリング)に対応してるのが全然良いね。少なくとも、X11で1.5倍のスケールを設定するのは全くできなかった。いつもアドバイスは「使ってるアプリごとにフォントサイズを大きくすればいい」だったし。Waylandで fractional scalingを使うと、XWaylandのアプリはいつもすごくぼやけて見えるけど、Waylandネイティブのアプリはめっちゃ綺麗に見えるよ。

円を描くのはちょっとズルいよね。スケーリングの難しいところは、ラスタアイコンや1pxの線をぼやけずに描くことなんだよ。

スケーリングの難しいところは、ラスタアイコンや1pxの線をぼやけずに描くことなんだ。 しかも、OpenGLじゃなくてXを使ってそれを実現すること。

そうそう、まさにその通り。ディスプレイの物理的なジオメトリを決定するのが不可能だなんて誰も言ってないよね(でも、リモートXセッションでは難しいかもしれないし、そこでもうまくいくかはわからないけど)。

それに、異なるスケールの複数モニターにも対応してるし。誰もX11が異なるDPIをサポートしてないとは言わないよ。問題は、異なるピクセル密度のモニターを持っているときに発生するんだ。今のところ、Windowsだけがそのケースを完璧に処理してるし、macOSですらそうじゃない。Waylandは、ツールキットとコンポジタがオプションの分数スケーリングを実装すれば、二番手になるけど、Linuxデスクトップエコシステムがそこを正しくやるとは思えないな。サーバーサイドのデコレーションと分数スケーリングがオプション(つまり、コンポジタとツールキットからのランタイムのオプトインが必要)なのは、デスクトッププロトコルとしては失敗だと思う。これらの欠けている機能は、GNOMEと彼らのGTKや他のコアライブラリへの支配に直接起因してるんだ。

それは、アイコンをアップスケーリングする際にどんなフィルタリングを使うかによるよね。最新のリサンプリングフィルタを使うと、シャープな境界の近くに微妙な「油絵」や「水彩画」みたいな効果が出ることが多いけど、目に見えるぼやけはないよ。これらのフィルタは、画面全体をアップスケーリングする時はちょっと計算負荷がかかるかもしれないけど、小さいラスタアイコンや他のラスタ画像だけをアップスケーリングして、他はネイティブ解像度で描画すれば、その効果はほとんど無視できるよ。

ハハ、円の話をするなんて面白いね!X11でアークや円を正しく塗りつぶしたり描いたりするのは本当に楽しいんだ。馬の口から: https://archive.org/details/xlibprogrammingm01adri/page/144/... XlibプログラミングマニュアルとXlibリファレンスマニュアル、セクション6.1.4、pp 144: >より正確に言うと、矩形ルーチンの塗りつぶしと描画バージョンは、同じ引数を与えても同じアウトラインを描かない。 >矩形を塗りつぶすルーチンは、アウトラインを描くだけのルーチンよりも幅と高さが1ピクセル短いアウトラインを描く。図6-2に示されているように。矩形呼び出しの引数を調整するのは簡単で、1をxとyに加えて、幅と高さから1を引くだけで、アウトラインの中にきれいに収まる。けれど、アークの場合は、これははるかに難しい提案だ(おそらくポータブルな方法では不可能)。 https://news.ycombinator.com/item?id=11484148 DonHopkinsが2016年4月12日に投稿 | 親 | コンテキスト | お気に入り | NeWS – ネットワーク拡張ウィンドウシステムについて >Xがアンチエイリアスを行う方法は、根本からの再設計なしには不可能だ。レンダリングルールは、どのピクセルがどのように触れられるかについて非常に厳密に定義されている。 >X11の離散的な半開きピクセル指向のレンダリングモデルと、PostScriptの連続的なステンシル/ペイントPorter/Duffイメージングモデルとの間には、根本的に調和しない哲学的かつ数学的な違いがある。 >X11グラフィックスは、塗りつぶしとストロークで異なる丸めを行い、ストロークを任意の座標変換での塗りつぶしとして定義し、右下に重力を持つ「半開き」ピクセルに関するもので、ジオメトリック領域のピクセルカバレッジとは異なり、アンチエイリアスが定義される方法だ。 >X11は車輪の上のラスタオプスだ。多くのアプリケーション開発者がX11の方法でピクセルや座標を考えるのを楽しんでいなかったことが判明した。ディスプレイは常に正方形のピクセルを持っているわけではなく、ラスタオプスを効率的にサポートするハードウェア(咳 Microvaxフレームバッファ)はすでに古く、レンダリングはハードウェア最適化の余地を許さないように厳密に定義されており、開発者は今やコンピュータが十分に速くなったので、より高レベルのステンシル/ペイントやスケーラブルなグラフィックスを使いたがっている。 >Unix-HatersのX-Windows Disaster章で問題を説明しようとしたことがある: >形状を塗りつぶしたりストロークしたりするような簡単なタスクが、Xの奇妙なピクセル指向のイメージングルールのためにかなり複雑だ。XFillRectangleで10x10の正方形を塗りつぶすと、期待通りの100ピクセルが塗りつぶされる。でも、同じ引数をXDrawRectangleに渡すと、実際には11x11の正方形が描かれ、1ピクセル下と右に飛び出す「ボーナスピクセル」が得られる!!!これが信じられないなら、自分でXマニュアルを調べてみて。第1巻、セクション6.1.4。マニュアルは、塗りつぶされた矩形のxとyの位置に1を加え、幅と高さから1を引くのがどれだけ簡単かを上から目線で説明している。そうすれば、アウトラインの中にきれいに収まる。それから、「ただし、アークの場合は、これははるかに難しい提案だ(おそらくポータブルな方法では不可能)」と指摘している。つまり、X Window Systemを使って、重なりや隙間を残さずに任意にスケーリングされたアークを塗りつぶしたりストロークしたりするのは、解決不可能な問題だってこと。考えてみて。厚いアウトラインを持つ適切な矩形すら描けないんだ。線の幅はスケーリングされていないピクセル単位で指定されるから、ディスプレイが長方形のピクセルを持っていると、縦と横の線は厚さが異なることになる。たとえ矩形のコーナー座標をアスペクト比を補正するためにスケーリングしてもね。 [1] X-Windows Disaster: http://www.art.net/~hopkins/Don/unix-haters/x-windows/disast...

たぶん最もエキサイティングなタスクではないけど、他のスケーリングの課題と同じだと思う。 そして、古いGUIライブラリのエコシステム全体でこれをやるの? アイコンやテキスト、画面に線を描く方法の違いに対処するの? そこが本当に大変なところだよね。

そうだね、X11をWaylandに同時に再設計するのはずっと簡単だよね :).

面白い記事だね。最初にタイトルを見たときは、別の「スケーリング」のことを考えてたんだ。つまり、X11のクライアント/サーバーのデカップリングね。SSH経由のX11フォワーディングって、すごくクールであまり注目されてない機能だと思う。最近はあまりやらない理由がたくさんあるのは分かるけど、サーバーアプリのUIをローカルで動かすのが役立った経験もあるよ。(まあ、役立つというより楽しかったけどね。)

確かにすごく便利だね。僕は仕事の半分をSSH経由のX11でやってるけど、LAN上ではまあまあうまく動いてるよ(少なくともemacsやプロットなどはね)。

昨日、HNの人たちがGLXはネットワーク越しには動かないって言ってた気がする。

$ ssh glxgearsは問題なく動くよ!

これを信じてる人っているの?間接GLXが何か分かってるのかな?XQuartzをサーバーにして、Linuxのボックスをクライアントにするのは、私にはいつも完璧に動いてるけど、GLXも含めてね。

2025年のLinuxで、カスタム解像度を簡単に出力できないなんて驚きだよ。今、君はxrandrのことで何か返事を書いてると思うけど、絶対にうまくいかないよ。普通の解像度内で画面をスケーリングすることすらできないんだから。でも、NVIDIAのGPUを使えばWindowsではできるんだよ。おかしいよね。

今、xrandrのナンセンスでレスポンスを打ってるんだろうけど、絶対にうまくいかないよ。スキルの問題だね。キーボードを持つのを間違えたんじゃない?シンプルなxrandrコマンドは、何十年も前から問題なく動いてるから。(もちろんWaylandに移行してたら、誰にも分からないけどね。)

スケーリングやセンタリングを伴うカスタムフレームバッファを作るのにxrandrを使う必要はないけど、できることはできるよ。Gamescope(https://wiki.archlinux.org/title/Gamescope)も使えるし、X11とWayland、どんなGPUでも動くから。通常はフルスクリーンアプリ、特にゲームを起動するために使われるけど、デスクトップセッションをカスタム解像度でカスタムスケーリングやレターボックスを使いたいなら、ウィンドウマネージャを起動することもできるよ。

はぁ。もう十分時間が経ったから、みんなこの問題が不十分だった理由をすっかり忘れて、デスクトップ環境やツールキットの開発者が単にバカだと思うだろうね。(重要なことに、アプリケーションは一時期これをデフォルトでやってたんだよ。これのせいで変なnvidia-xsettingsを見たのを覚えてる。)X11が本当に欠けているもの(少なくとも最も重要なの)はDPIの仮想化なんだ。UIスケーリングはほとんどのディスプレイサーバーが実装していない機能だし、ほとんどのディスプレイサーバーは実際のUI部分を実装していないからね。DPIの仮想化がないのは問題なんだ。なぜなら、ウィンドウが入力と出力の座標を論理的にスケーリングする方法を自力で考えなきゃいけないから。さらに悪いことに、モニターごとにそれをやらなきゃいけないし、異なるスケーリングの2つのディスプレイにウィンドウが重なると、部分的にウィンドウが変に見えることをどうしようもないんだ。もし何かがこれをうまくやらなかったり、少し違ったやり方をしたら、見た目が変になってしまうし、ユーザーは環境変数やXプロパティを探すくらいしか手段がない。これを説明するのは、X11がディスプレイスケーリングのサポートが悪いと言うよりも難しいよ。「UI/ディスプレイスケーリングをサポートしていない」と言うのはちょっと誤解を招く表現だけど、問題の本質ではないんだ。

異なるスケーリングの2つのディスプレイにウィンドウが重なると、部分的にウィンドウが変に見えることをどうしようもないのはおかしいよね。人々がこれについて文句を言い続けるのは馬鹿らしい。これは非常に小さな影響で、原則的にはすべてを純粋なベクターレンダリングに移行することで解決できるものだよ。一般的に、ウィンドウは一つの画面にしかまたがらないからね。ウィンドウを別のモニターにドラッグできるのは便利だけど、その重なりをワークフローの恒常的な機能にするのはちょっと狂ってるよ。 > X11が本当に欠けているもの(少なくとも最も重要なの)はDPIの仮想化だ。そんなDPIの仮想化は、Xサーバーやプロトコルよりもツールキットの方が気にすべきことじゃないの?Xがハードウェアから正確なDPI情報を取得してクライアントに報告している限り、他に何が必要なんだろう?

Fresco(以前はBerlin、InterViewsにインスパイアされた)には成功してほしかったな。彼らのモデルでは、UIツールキットがサーバーサイドで、本当にアプリが動いてる間に変更できるから。抽象的なデバイスをターゲットにしてたから(1200dpiのプリンターと72dpiのディスプレイを同時に扱える)その特性を無料で得られたんだよ。

DPIが異なる2つのディスプレイが隣り合っていて、その真ん中にウィンドウが置かれた場合、これに対応できるの?

「ウィンドウに画面のDPIを提供できる?」っていう話じゃなくて、「異なるDPIの2つのスクリーンにまたがって、アプリに透過的にウィンドウを描画できる?」ってことなんだよ。

X11でも全然できるけど、クライアント側でやらなきゃいけないから、誰も気にしないんだよね。