再現性 - sipo/gipo GitHub Wiki

GipoフレームワークではSectionからLogicへのinputは全てHookを通して伝えられます。また、Logicでは不確定な処理は排除されています。このため、Hookで入力を保存することで、完全にプログラムの動きを再現することができます。

再現性はフレーム単位で処理が行われます。この時、実際の処理時間とはズレが発生することがあるため、処理時間の保証はされません。

Operation

再現性を担当するのはReproduceというクラスですが、再現を行う時、あるいは行っている時に、その外側から開発者の指示を受ける必要があります。この範囲をOperationと呼びます。OperationはLSH構造を持つため、Gipoフレームワークの全体構造は以下のようになります。

Operationは状況に応じて作り変えます。サンプルでは右上に表示される単純なボタンとして実装してあります。

Operationによる操作は再現性の対象外になります。

input

SectionからのLogicへの入力をinputと呼びます。基本的に、Sectionは入力を受けたものを自分で処理せず、処理内容をLogicに委ねます。Sectionが自身で処理を行ってしまうと、いわゆる「Smart UI アンチパターン」に陥るだけでなく、その動作が再現できなくなってしまいます。

inputにはinstantとreadyの2種類があります。

instant

Section側に副作用を伴わず、状況によって結果が変化する入力です。多くの入力処理がこちらになります。ユーザーの入力、日付やランダムシードの取得、サーバーからのデータ取得などです。必要なデータはEnumの引数に含みます。

instantは再現時に発生しても、Hookによって止められます。つまり、再現時にユーザー操作があったとしても無視されます。

ready

処理を追える時間が不明で、Section側に副作用を伴い、しかし結果は同一のものです。例えば、画像のGPUへのアップロードなど描画を行うためにどうしても必要な準備処理です。

再現時のreadyの挙動は少し特殊です。プログラムの実際の動作と、再現時のフレーム数を合わせる必要があるからです。

再現データのフレーム数が来ても、Section側の準備が終わっていない場合、このままプログラムが進行してしまうとエラーが発生してしまうので、Logicのフレーム進行を待つように指示します。SectionからreadyInputがHookに届くと、処理が再開されます。

ユーザーの入力など、再現時にはinputが届かないものをreadyに指定してしまうと、入力を待ってプログラムが止まり続けてしまうので注意してください。

Snapshot

長い実行を再現するのは非常に大変な場合がありますので、途中から再開出来るようにするためにはSnapshotを使用します。

SnapshotはLogic側からHookに送ります。SnapshotにはLogicが必要とするデータ全てが含まれます。HookからSnapshotを受け取った場合、Logicは今までの情報を全て捨てて、Snapshotに対応するよう実装します。

使用例

ReproducePhase

再現時はフレームだけを合わせても、同じフレームでの処理の順序が合わなければ正しい再現になりません。このフレーム内での処理順序を区切るものをReproducePhaseと呼びます。

reproduce.startInFramePhase()を使用して、フェーズを指定します。

フレーム外のフェーズにはreproduce.startOutFramePhase()を使用します。

reproduce.endPhase();を使用してフェーズを終了します。再現時にはこのタイミングで保存されていたinputがLogicに伝えられます。

使用例