Game Boy 研修(2)

今日は,エミュレーターの実装の進め方について紹介します。

基本的には…
MJHDというのブログ記事の「Rustでゲームボーイエミュレータを自作した話」に沿った手順で実装するのが良いと思います。
また,どのよう実装するかイメージがつかめない方は「脱・初級者のための自作GBエミュレーター開発」というスライドを参考にするものよいかもしれません。

Step.1 ROMをデコードする
Step.2 CPU処理を実装する
Step.3 PPU処理を実装する
Step.4 デバッガを実装する
Step.5 CPUテストROMを通す
Step.6 機能追加・改善を考え,実装する。

ROMをデコードする

まずは,ゲームボーイのソフト。つまりカートリッジデータが読み込みできなければ話になりません。
…なので,まずはカードリッジデータを読み込めるようにしましょう。カードリッジデータが読み込めれば,ゲームで使用する命令列などが読み取れるようになるはずです。
カートリッジのデータフォーマットなどについては,Pan Docs にまとまっているようなので,どういうデータ構造になっているのか?についてはドキュメントを読みましょう。
どんなプログラマーもドキュメントが読み解けるようにならなければいけません。特に,コンソールプラットフォームなどは秘密事項の塊で,インターネットに情報が出ることはめったになく,公式ドキュメントを見なければ何も出来ないということが多々あります。
そのため,ドキュメントを見ながら実装力をつける訓練の最初の一歩としてやってみましょう。

CPU処理を実装する

続いて,絵を出す前にCPU処理をエミュレートした方が良いでしょう。
ロード命令やジャンプ命令・加算・減算・ビット演算など基本的な命令を実現できるようになりましょう。
また,基本的な命令の実装を通じて,低レベルで何が行われるのか?を知ることが研修の目的の一つでもあります。

PPU処理を実装する

グラフィックスプログラマーを目指すものであれば,絵が出せなければいけません。また,やっぱりゲームは絵が出てこそ!というところもあるので,絵を出しましょう。
また、PPU(Picture Processing Unit)で何が行われているかを知りましょう。これを知るためには,PPU処理を実装するのが一番です。
PPU処理の実装が出来れば,一番処理にGame Boyプログラムで作った「Hello World」の文字列表示プログラムが動かせるようになるはずです。

デバッガを実装する

PPU処理まで実装出来たら,あとはひたすら精度・品質を上げていくだけです。
そのためには,デバッガの実装は必須でしょう。
また,デバッガを実装するためにどのようなことをしなければないのか,普段我々がVisual Studio何気なくつかっているブレークポイントなどの機能はどのようにしておこなわれているのか?などデバッガの仕組みを身をもって体験してください。

CPUテストROMを通す

最後の仕上げです。
実機で動作保証がされているCPUテストと呼ばれるROMを実行し,自作のエミュレーターで正常動作すること確認してください。
テストのROMは以下にあります。
https://gbdev.gg8.se/files/roms/blargg-gb-tests/
正常動作しない場合は,エミュレーターの実装に問題があるということです。
特に会社等では,テスト駆動開発を実施している会社もあるのと思うので,ここでテストの重要さ,製品の品質保証など,クオリティーを上げる大事さを学んでください。

機能追加・改善を考え,実装する

言われた仕様,あるいはお題に通りに作れば最低ラインは超えたことになります。少なくとも一般的な会社では言われたこと通りものを作ることが出来れば,きちんと給料は支払われるはずです。
ここから先は,「他とどう差別化するか?」という話です。同じ機能でも見た目をカッコよくできるのであれば,付加価値はあがるでしょう?また,他社や他人には出来ない機能追加をするのもよいかもしれません。あるいは,思い切って機能を削ることによる分かりやすさの改善・動作安定性なども差別化が図れるでしょう。
余力がある人は,自分なりのカラーを出せるように改造・改変してみてください。
特に昨今はKFCの事例のように何も考えない会社が作るとUX(User Experience:ユーザー体験またはユーザー体感)が激悪化しやすいです。また,どことはいいませんが解約手続きの面倒さなど,もう二度と使うもんか!と思うような酷いものあったりします。ユーザーにとって何か使いやすいのか?何が嬉しいのか?については我々は常に思考を巡らせる必要があります。特に日本の大手会社(例えば電機メーカーなど)などはこうした考えが非常に弱く,そのせいで海外メーカーにシェアを取られるというひどい状況に陥っています。携帯電話のUXの酷さなどはそのもっともたる例のひとつになるかもしれません。
我々はグローバルに戦っていく必要があります。そのためは,常にユーザーないし,使う側の立場にとって良いものを提供する必要があると思います。
そこで,余力がある人は作ったアプリに対して,ユーザーにとって有益であるような機能追加・機能改善を実行してみてください。
恐らく基本となる考え方は「自分だったら嬉しいか・喜ぶか」がベースになるのではないかと思います。そこから,平均と比べて自分がどのぐらいズレがあるのかを推測して補正していくと良いでしょう。

Screen Space Reflection関連の資料

実装用メモです。

SSRを実装すると,エッジ部分どうすんのよ?っていう問題が付きまとうのですが,下記の資料で解決方法の1つが示されています。
[Genin 2017] Remi Genin, “Screen Space Planer Reflections in Ghost Recon Wildlands”, https://remi-genin.github.io/posts/screen-space-planar-reflections-in-ghost-recon-wildlands/

SSRの実装自体は下記記事が参考になります。

また,ナイーブな実装だと,下に伸びるようなアーティファクトが発生することがあります。この問題への対策の一つとして,深度の厚みを考慮することによってある程度回避する方法が述べられています(Notes On Screen Space HIZ Tracing)。

また、上記のブログでは,深度によるアーティファクトの問題が指摘されていますが,GDC 2016のTHE RENDERING OF INSIDEの中で紹介されている隣接セルの深度を補間するのが良いでアイデアではないかと触れられています。

最適化については,“Screen Space Reflections in The Surge”の中で触れられているInterleave化を試すのが良いかもしれません。SSAOなどでは実際に実装してみた経験があり,かなり処理負荷削減できました。ただ、この手法はHi-Zには相性が悪いかもしれません。

色々と試して思うのは,HYBRID SCREEN-SPACE REFLECTIONSにあるように,近い点をSSRで解決してしまって,遠い箇所についてはレイトレしてしまった方が,良いかもしれないと思っていますが,実際にHi-Z使うよりもいい結果とパフォーマンスが得られるかどうかまでは試していないので,どこかで試してみるのはありかもしれないです。

また深度の厚みに関しては,”Screen Space Reflection Techniques”という文献の中で,Min-Max Hi-Zという手法が紹介されており,これを使うとパフォーマンスやアーティファクトの問題が解決できるかもしれないです。こちらもまだ試せていないです。

Game Boy 研修 (1)

こんにちわ。Pocolです。
今年は研修どうしようか悩んでいたんですが,今の若い人はあんまりハードのことを知らないのではないか?(当然、めっちゃ詳しい人もいますが…)
という考えが湧いてきたので,今年はGame Boyでプログラミングするというのを研修ネタとしてやってみようかなと思います。

目的

ハードウェアについて理解し,実際にゲームを作成し,プログラミング能力および開発能力を高める。
また、ポーティング作業を行うことでマルチプレットフォーム展開を行う際に、やってはいけないことを体感させ,開発効率改善および向上のための意識づけを行うことを目的とする。
エミュレーター開発を通じて各種APIへの理解を深める。

目標

  • 開発環境を自分で整えられるようになる。
  • Game Boy上で動くゲームを自力で開発する。
  • Game Boyエミュレーターを自作し,ハードウェアを理解する。
  • 指定されたプラットフォーム上で動作するように自作したGame Boyエミュレーターの移植作業を行う。

資料

● The Ultimage Game Boy Talk

● The Game Boy, a hardware astopsy – Part 1: the CPU

● The Game Boy, a hardware autopsy – Part 1.5: a few mistakes and register F

● The Game Boy, a hardware autopsy – Part2: Memory mapping

● GBDKの導入~ゲーム作成まで
https://qiita.com/BubbleBubble/items/9c28c57ed942c9b48843

● きるこ日記帳
https://www.dkrk-blog.net/category/GameBoy

● ゲームボーイのゲームを作ってみる
https://www.youtube.com/watch?v=xhUOhT7KKE4&list=PLbP78mBX9wpCH1SwnLix14rd1g47XJ5fO

● Game Boy Development community
https://gbdev.io/

● Open Game Boy Documentation Project
https://mgba-emu.github.io/gbdoc/

スケジュール

● 第1週目
お題:指定されたライブラリを利用した開発環境を整えてください。そして,第3者が開発環境を整えられるように手順書を作成してください。
アドバンスなお題:Visual Studio Codeで開発できるようにしてください。

● 第2~3週目
お題:サンプルアプリを作ってください。
  ・Hello, Worldの表示
  ・スプライト描画
  ・十字キー入力でキャラを動かす
  ・音を鳴らす (Option)
  ・説明書を作る (Option)

● 第4週~第5週目
お題:Game Boyエミュレータを自作して,サンプルアプリを動かしてください。ただし、描画APIを利用する場合はDirectX12を使用してください。
※6週目~7週目を見越してラッパーAPIを作っておくのは可とします。
アドバンスなお題:
  ・ゲームパッド対応を行ってください。
  ・どこでもセーブできる機能を作ってください。
  ・アップスケール対応を行ってください(Option) [ex]Hq3xなど…
  ・可能であれば次のSIGGRAPH論文を実装してしてください(Advanced)
    Johannes Kopf, Dani Lischinski, “Depixeling Pixel Art”, SIGGRAPH 2011 Technical Papers, https://johanneskopf.de/publications/pixelart/
    実装参考として,次のPythonコードを参照するのは可とします。https://github.com/vvanirudh/Pixel-Art
補足:動作確認には自作のアプリとhttps://gbdev.gg8.se/files/roms/blargg-gb-tests/のROMが動作することを確認してください。

● 第6週~第7週目
お題:指定プラットフォーム上で,自作Game Boyエミュレータを動作するように移植作業を行い,サンプルアプリを動かしてください。

Recreating Naniteの記事

JGLRXAVPOKさんという方のブログで,Recreating Naniteという記事があります。
先日紹介した,MeshoptimizerとMEITZを使ってLODを生成するような話も載っています。

Recreating Nanite: The Plan
Recreating Nanite: Visibility buffer
Recreating Nanite: Cluster rendering
Recreating Nanite: LOD generation
Recreating Nanite: A basic material pass
Recreating Nanite: LOD generation -faster, better, simpler
Recreating Nanite: Runtime LOD selection
Recreating Nanite: Mesh shader time

FrameGraph関連の資料

実装用のネタ帳です。

● FrameGraph: Extensible Rendering Architecture in Frostbite
https://www.gdcvault.com/play/1024612/FrameGraph-Extensible-Rendering-Architecture-in

● Advanced Graphics Tech: Moving to DirectX12: Lessons Learned
https://www.gdcvault.com/play/1024656/Advanced-Graphics-Tech-Moving-to

● fg
https://github.com/acdemiralp/fg

● Why Talking About Render Graphs
https://logins.github.io/graphics/2021/05/31/RenderGraphs.html

● Task Graph Renderer At Activision
https://research.activision.com/publications/2023/06/Task-Graph-Renderer-at-Activision

● Organizing GPU Work with Directed Acyclic Graphs
https://levelup.gitconnected.com/organizing-gpu-work-with-directed-acyclic-graphs-f3fd5f2c2af3

● Render graphs and Vulkan – a deep dive
https://themaister.net/blog/2017/08/15/render-graphs-and-vulkan-a-deep-dive/

● Unreal Engine 5 – Render Dependency Graph
https://docs.unrealengine.com/5.3/en-US/render-dependency-graph-in-unreal-engine/

● GPU synchronization in Godot 4.3 is getting a major upgrade
https://godotengine.org/article/rendering-acyclic-graph/

● Render Graph 101
https://blog.traverseresearch.nl/render-graph-101-f42646255636

● An update to our Render Graph
https://blog.traverseresearch.nl/an-update-to-our-render-graph-17ca4154fd23

● EmbarkStudios kajiya-rg
https://github.com/EmbarkStudios/kajiya/tree/main/crates/lib/kajiya-rg

● Mastering Graphics Programming with Vulkan
https://github.com/PacktPublishing/Mastering-Graphics-Programming-with-Vulkan

● Render graphs
https://apoorvaj.io/render-graphs-1/

Virtual Texture関連の資料

Virtual Texture関連の私的メモです。

● Software Virtual Textures
https://mrelusive.com/publications/pubs_bytype.html
https://studiopixl.com/2022-04-27/sparse-virtual-textures

● Using Virtual Texturing to Handle Massive Texture Data
https://www.nvidia.com/content/GTC-2010/pdfs/2152_GTC2010.pdf

● Sparse Virtual Texturing
https://silverspaceship.com/src/svt/

● Advanced Virual Textures Topics
https://pdfs.semanticscholar.org/32f7/49c984e1ebd97e85f90d96b8ee5ed35c143a.pdf
https://dokumen.tips/documents/chapter02-mittring-advanced-virtual-texture-topics.html

● Virtual Texturing in Software and Hardware
https://mrelusive.com/publications/presentations/2012_siggraph/Virtual_Texturing_in_Software_and_Hardware_final.pdf

● Adaptive Virtual Texture Rendering in Far Cry 4
https://www.gdcvault.com/play/1021761/Adaptive-Virtual-Texture-Rendering-in

● Terrain Rendering in ‘Far Cry5’
https://www.gdcvault.com/play/1025480/Terrain-Rendering-in-Far-Cry

● Sampler Feedback
https://microsoft.github.io/DirectX-Specs/d3d/SamplerFeedback.html

1-Phase Occlusion Culling

完全に私的な実装メモです。
通常,オクルージョンカリングを実装する際は,GPU Zenやもんしょさんのサイトに載っているように2-Phaseでジオメトリ描画して実装するのが普通かと思います。
少し前ですが,GDC 2021の“Samurai Landscapes: Building and Rendering Tsushima Island on PS4”というセッションの,43:45あたりから,Occlusion-Cullingについての説明があり,Ghost of Tsushimaの実装では,前フレームの深度バッファと,それらから保守的(conservative)に作成したミップレベル,エピポラーサーチを用いて,現在フレームの深度バッファを復元し,1回のジオメトリ描画でオクルージョンカリングを実装する方法が紹介されています。
説明が分かりやすいので,アルゴリズムについては元動画を参照してください。
馬鹿まじめに線形探索をせずに,ミップマップを使って検索するのがアルゴリズムのキモみたいです。

一応実装コードが紹介されていますが,動画の品質が低すぎて全然良く見えませんw。
そこで,それっぽい感じに見えるコードを自分で推測しながら書いてみました。
推測で書いているのと,きちんと動作検証もしていないので,バグっている可能性があるので,まんまコピペで使用して不都合・不利益が発生しても何ら責任は負いませんのでご注意ください。
もし、「ここはこうじゃね?」とかアドバイスあればコメントください。

Depth Reprojection

// Forward-project last frame's depth to this frame's space
{
    float zPrev = pSrt->m_texIn[dispatchThreadId.xy];
    float zDepthPrev = DepthFromWorld(zPrev, pSrt->m_vecRecoryHypebolicDepth);

    float4 posClipPrev = float4(VecClipFromUv(uvThread), zDepthPrev, 1.0f);
    posClipPrev *= zPrev; // This ensure that zCur will end up in posClip.w;

    float4 posClip = mul(posClipPrev, pSrt->m_matClipPrevToClipCur);
    posClip.xyz /= posClip.w;

    float2 uv = UvFromVecClip(posClip.xy);

    if (all(uv >= pSrc->m_rectScissor.xy) && all(uv >= pSrc->m_rectScissor.zw))
    {
        // NOTE : z ends up in posClip.w so we can use that directly.
        float z = posClip.w;
        
        if (z < pSrt->m_zNear)
            z = pSrt->m_zFar;

        // NOTE : Add a small bias to resolve self-occlusion artifacts
        z += pSrt->m_dsBias;

        if (z > pSrt->m_zFar || zPrev >= pSrt->m_zFar)
            z = pSrt->m_zFar;

        float2 xy = floor(uv * pSrt->m_texture.m_dXy); // 多分、テクスチャサイズを乗算してウィンドウサイズに戻している。
  
        AtomicMax(pSrt->m_texOut[xy], z);
    }
}

{
     float4 posClip = float4(VecClipFromUv(uvThread), 0.0f, 1.0f);

     float4 posClipPrev = mul(posClip, pSrt->matClipCurToClipPrev);

     if (any(abs(posClipPrev.xy) >= abs(posClipPrev.w))
     {
          float2 uv = UvFromVecClip(posClipPrev.xy / posClipPrev.w);

          float z = pSrt->m_texIn->SampleLOD(pSrt->m_sampler, uv, 0) * pSrt->m_dsBias;

          AtomicMax(pSrt->m_texOut[dispatchThreadId.xy], z);
     }
}

Hole-Filling

float2 uv = (dispatchThreadId.xy + 0.5f.xx) * pSrt->m_texture.m_div;
float zMax = pSrt->m_texIn.SampleLOD(pSrt->m_sampler, uv, 0);

if (zMax == -FLT_MAX)
{
    float2 normalEpipole = normalize(pSrt->m_uvEpipole * uv);
    float2 dUvStep = pSrt->m_texture.m_div * normalEpipole;

    for(uint iMip = 1; iMip <= pSrt->m_iMipLast; ++iMip)
    {
        float2 uvSearch = uv - dUvStep;
        dUvStep *= 2.0;

        float z = pSrt->m_texIn.SampleLOD(pSrt->m_sampler, uvSearch, iMip);

        if (z != -FLT_MAX)
        {
            z = max(z, pSrt->m_texOut[dispatchThreadId.xy]);
            z *= pSrt->m_dsBias;
            z = min(z, pSrt->m_zFar);
            pSrt->m_texOut[dispatchThreadId.xy] = z;

            return;
        }
    }

    if (zMax < pSrt->m_zNear)
        zMax = pSrt->m_zFar;

    zMax = min(zMax, pSrt->m_zFar);

    pSrt->m_texOut[dispatchThreadId.xy] = zMax;
}

GPUで実行するテレイン上へのプロシージャルな配置の話

先日、Twitter(現X)を見ていたら,Unity engine上でテレイン上に配置する奴のツイート(リポスト)が来たので,忘れないようにメモとして残しておこうと思います。

1つ目は,GPU Run-time Procedural Placement on Terrain というやつです。
Horizon Zero-Dawnの資料を元に実装しているっぽく、どこぞの会社とおなじようなものっぽいです。

2つ目は、上記記事の続編と思われる Efficient GPU Rendering for Dynamic Instances in Game Development です。

いわゆるテレインを使用するオープンワールドゲームを作る際には役に立つかと思います。

premake5覚書

こんねね~。Pocolです。
premake5の覚書をしておこうと思います。

XBox系のプラットフォームの場合は,Billzard社がpremake-consolesというGithubリポジトリを公開しているので,これを使えばよさそうです。
https://github.com/Blizzard/premake-consoles

で,青いプラットフォームの場合は,これまたBillzard社がソースコードをフォーラムにて公開しているので,それを使えばいいです。

問題はこれらが使えない場合です。既にpremake作っちゃっているやつとか,これから出てくる新規プラットフォームの場合とかですね。
自力で実装する必要があります。ほとんどの場合はSDKがC++だと思われるので,C++のコンパイルは問題なく対応できるでしょう。
じゃ、何が問題か?
そうです。前回やったシェーダ設定ですね。恐らく問題になるでしょう。
SDKでVS拡張が提供されている場合は,プロジェクトファイルに記述すればいいので,premakeを拡張していけばいいです。

やり方としては,
https://github.com/premake/premake-core/blob/master/modules/vstudio/vs2010_vcxproj.lua
のFxCompileおよびfxCompileで記載されている箇所を参考にします。

premake用に公開するAPIは,
https://github.com/premake/premake-core/blob/master/modules/vstudio/_preload.lua
のshadertypeとか,shadermodel,shaderincludedirsあたりを参考にLuaに実装を付け足します。

先ほど言った、VS拡張が提供されている場合はこんな感じでいいのですが,じゃ、提供されていない場合はどうすればいいでしょうか?
残念ながら,自分で作るしかないですね。

作り方については,以前に書いた「カスタムビルドルール!」「Visual Studioと格闘中…。」の記事で紹介しているので,これを参考に実装すれば良いでしょう。

…ということでコンソールプラットフォーム用のpremakeのシェーダビルド対応について,覚書を書いてみました。
殆どの人には,全く役に立たないんですが,自分だけには役立つというしょうもない記事でした。