ラベル High Sierra の投稿を表示しています。 すべての投稿を表示
ラベル High Sierra の投稿を表示しています。 すべての投稿を表示

2017-10-07

AppSummoner 〜Road to High Sierra 10.13 ready〜 その2

毎日夜中の細かい時間の積み重ねで、やっとある程度の形になったので今回の経緯と今時点どう回避しようとしているかの備忘。

先週確認したreleasenotesにあったように、High Sierra(macOS 10.13)ではlayerの描画作法が変わったようで、CALayerをいじって表現しているところでおかしな挙動を示していた。

その後releasenotesのその先も読んではみたが、確かに今までNSViewに頼っていた抽象操作を、いくらかCALayerレベルまで具象層に落として書き直しが必要だというのは判明したのだけれど、アルファの再描画で確信を得られず。NSViewのbooleanフラグが多すぎてもはや2^n 順列組み合わせで挙動がふらついたり、その説明はついぞ見つからなかったり、おかしな挙動から抜本的な脱出はできなかった。

で、自分でundocumentedなところを調べてるうちに、ふと前回upした動画のオススメ欄を見てみると、こんな動画が。。。


(投稿者にお礼のコメントは入れたけど、埋め込みの許可はまだ得られてないので、後日このブログからは☝️消えるかもしれません)
たしかにこの現象は少なくともうちの10.13環境でも再現する。

ということはつまり、AFPSが絡んでるかもしれないとはいえ、そうそう支配的には見えず、FinderやらDockの描画つまりベンダーが提供するappのグラフィック処理ですでにエグいバグが潜んでいるのではないかと。

ここで僕はNSImageに関連する再描画でアルファが悪さをしているというpivotを立てることにした。
まずview hierarchy図でいうとAppSumonerはざっくりこういう構造をとっている
viewの②以降は素材であろうとplaceholderであろうと、すべてアルファ 0.0で透過させている。

前回の1本目の動画では、②が半透明にしてあって、③以降が①に更新されないまま描き残っていた(全ての軌跡が黒っぽくなってた。これはwantsUpdateLayer + なんらかのアトリビュートtrue/false組み合わせ実装で回避できていた)。
前回2本目の動画は①描き残しはfixしたものの、今度は③内のアルファブレンディングの再描画ができず③の中で描き残しが発生し続けていた。
これも②描き残し対応施策で回避できるかと前回の結論として思ってたが、そう甘くはなく、NSImageViewは実はNSViewとは関係のないキメラのような存在なので、NSImageViewではなく、NSViewのlayerにNSImage、CGImageを直接view, layerのsetNeedsDisplayからのメッセージからdraw系で描き込んだりしても目に見えた変化は見られなかった。
つまり②以降のview全てをsubclassレベルで完全に合わせ込んでも期待値とはほど遠く、やけになってやってみたのがこれ。


よほどcrazyな絵面だが、②のbgColorを赤、アルファを1にした。
②も③も描き残しは見られない。

これはCocoa(AppKit)のソースは公開されていないんで全くの憶測だけど、調査過程で発見した動画や、CGImage再描画でも回避できないというのはどうやらアルファの重ね合わせでなんらか持ってるんではないかと。。。
eGPU対応とかで、前提が崩れた?NSNextStepFrameのbgColorとか絡んできてた時点で。。。いやわかんない。全て推測の域は決して超えない独り言。

これは放っておけば、ある日のアップデートで昔のコードが期待通りに動くようになるかもしれないし、もうNS\w+Viewでのグラフィックサポートは諦めて、macOSはmakeBackingLayerとLayer hierarchyと各種drawでやりくりするか、どっぷりそれなりのグラフィックフレームワークLayer作れといっているのか。Apple自身がいままさに考え中なのか。

もちろん僕のやり方ではないやり方で、AppKitとして"正しい"方法があるのかもしれないことは今日時点棄却できない。


ということで、iOS, macOS developerプログラム費用をAppleに払わせるという背徳感のためだけに存在するアプリは、いったん👇くらいまでは10.12と等価に見えるところまでアレンジできました。


あとタイミングとか画像の切り替わり調整、テストしたらHigh Sierra Readyと書けるか、書けるといいな、もう結構な時間を使っているので是が非でも書きたい。
May be  Coming soon.

2017-10-01

AppSummoner 〜Road to High Sierra 10.13 ready〜

なかなかまとまった時間がとれず遅々として進んでないのだけれども、このところMASに公開しているAppSumoner のHigh Sierra対応に時間の捻出も含め四苦八苦している。

現在公開しているものをHigh Sierraでいきなり確認すると実はこうなる



バックが黒く透過されているのは、デバッグモードだからなのだけど、Sierraの最後ではこんなことはなかった。描画済みのlayerがfillで更新されず、思い切り描き残っている。
これはこれでマウスポインタの軌跡が履歴で見れてUI/UXとして新たな可能性も感じるけど、客観的にアプリケーションとして見るとてんでいけてない。

最初は10.13のバグで、放っておけばそのうち直るのを期待していたのだけど、よくよくreleasenote見返したらまったくそうではないらしい。

で、releasenotesのこの辺をざっくり読んでわかったつもりで今日時点対応してみたのがこれ



よく見ると魔方陣のNSRect再描画でまだ対応途中なのだけど、ここまでreleasenotesの備忘録。

まずreleasenotesにはNSViewのbacking-layerで変更があったのだと書いてある。
その記述の先頭が↓

Stricter allocation of view-backing layers

macOS 10.13 is stricter in some cases about when it allocates view-backing layers for certain views. As before, if your view relies on having a layer (e.g. for the purpose of setting layer properties), it should receive [view wantsLayer:YES] and should probably also return YES from -wantsUpdateLayer.
"some cases"というのがどのケースなのかよくわからないが、どうやら僕のやり方はこのsome casesのひとつを引き当てたらしい。
でも、macOSのアニメーションはやりかたがたくさんありすぎて、どれがmain streamでどれがfringeなのかが明確に公言されてないところに、Stricterもない気はするけど。

ソフトウェアは否定しようと思えばいくらでも否定できるし、肯定しようと思えばいくらでも肯定できるのが数学と違うところ。
心を無にして...たしかにCALayerバリ使ってる。

まず、この記述によると、NSViewクラスインスタンスでwantsLayerにTrue立てろと。
これはSierra以前からもやっているし、少なくとも10.11時点でview.layer使うならこれやらないと使えなかったと記憶している。
ただし、今回は続きがあって、wantsUpdateLayerもTrueを立てろと。
で、NSViewのヘッダを見ると、wantsUpdateLayerは
    @available(OSX 10.8, *)
    open var wantsUpdateLayer: Bool { get }
とreadonlyなので、wantsLayerのように生成したインスタンスへ乱暴にtrueは放り込めない。
ObjCから脱却しきれないのは仕方ないのか、端から見てるとclangから脱却するためにうまいことswift取り込んだのに、やっぱりまだObjCが低水準レイヤに居座ってるんだと思いつつ、どうせdrawもoverrideしないといけないので、さくっとこうした。
class InvisibleView: NSView {
    override var wantsUpdateLayer: Bool { return true }...
やりたいことだけに注目すれば、objCではよく知られたsnippetsだけど、protocol extensionでできたりするのかな?
ぱっとやってみた感じ、NSViewのprotocolにこのIF持ってるやつがいなかったので、今回はすぐ諦めたけど。
メモリ空間シェアしちゃだめだよ、ちゃんとクラス立ててクリーンに動かせよという設計意図なのかもしれない。

ちなみに乗っ取ったScreen上の再描画はこれだけで、multiScreenのケースも含め蛇の張った跡みたいなやつは消えました。
問題は魔方陣の方で、ImageViewをpsdっぽくレイヤー重ね合わせで表現してるところ。
noteからすると、次項の"Ownership of view-backing layer properties"...ってもう答えが出てる気もするけど、とりあえず今日は時間切れ。

Cocoaの設計だとImageViewって、(できるけど)パーツごとの透過Image重ねるような使い方してほしくないんだよな...既存IFの建て付けと一般的にアルファブレンドの再描画コスト考えると。

やられて困ることはドキュメント読んだりやら、いちいち推論なんて立てなくても、はなからコードでできない設計にしてほしいとは思うが、Appleのアーキテクトではないので素直に逃げ道に従うか、別アプローチを考えよう。

今頃バタバタしてるのは、機材の関係でここ数年ベータ見てないのが悪いんだけど、ほんといつになったらHigh Serra readyと書いてMASに出せるのやら。

なお、CoolerCotrollerの方も、このあおりで一カ所起動パーツが参照でview渡してたところのCoreAnimationが中心座標ずれて回転してるのもわかっちゃいるのだけれども。主要機能をブロックする訳ではないから後回しにしてたり。
シリアルポートは9600でつながってたのでArduinoが反応してるのは確認した。