kuina / Kuin

Kuin Programming Language
265 stars 19 forks source link

DirectX11, Direct2Dの導入について #109

Open haruo-wakakusa opened 6 years ago

haruo-wakakusa commented 6 years ago

現在Kuin言語ではDirectX 10が使用されています。ただ、現状DirectX 10はプロジェクト内、draw.cpp内でのみ使われているのでDirectX 11への変更は容易であると考えます。また、2Dグラフィックスの拡充に向けてDirect 2Dを導入いたしたく。こちらのほうは私のほうで実装実験を行っていませんがとりあえずissueのほうにあげさせていただきました。

lib_wndのdraw.cppをDirect 10からDirect 11へ修正したソースコードを置いておきます。 https://gist.github.com/haruo-wakakusa/e590ef1cd5d860f1581c1abcbc9429ee

DirectXの使用経験はないのでバグがあるかもしれません。そこを精査したうえでDirectXのバージョンアップをしていただきたく思っています。あえてpull requestではなくissueのほうに投稿させていただきます。よろしくお願いいたします。

kuina commented 6 years ago

もともと DirectX10 を採用した経緯は、Windows Vista (DirectX10 がプリインストールされている) をサポートするためでしたが、 現状では Windows7 以降を対象としているので、DirectX11 に乗り換えても良いかもしれませんね。 (とはいえ、DirectX11 で新規追加された機能を使わなければ GPU パフォーマンス等にそれほど違いは出ないと思います。)

Direct2D は、具体的にはどのような機能を使いたいでしょうか。 2D グラフィックスでも、3D の機能で描画したほうが、ハードウェアが Direct2D に対応していなかったときのパフォーマンス低下などを考える必要がなくなるため、可能なら代替の実装を検討します。

haruo-wakakusa commented 6 years ago

Direct2Dに対応していなかったときのパフォーマンス低下 ちょっと事例を把握しそこねています。ソースがあればご教示いただければと。

・2D図形の和・差を求める ・任意の図形に対してテクスチャの切り出し、アルファブレンド、グラデーションをかける ・ベジエ曲線を描画する ・正しい連結領域を表示させたい。しかも直線と曲線が入り混じったものが望ましい

といった用途を考えています。Direct 2DはDirect 3Dの上層であると把握していますが、レイヤー付加がパフォーマンスの低下に繋がるのであれば導入しないほうがいいのかもしれません。ただ、ブラウザのレンダリングをターゲットにしているのでくいなちゃんがフルスクラッチでコーディングするよりも機能拡張が容易なのではないかと思った次第です。

haruo-wakakusa commented 6 years ago

一日だけDirect3Dのコードを見させていただきました。テクスチャ、バッファといったものに対してシェーダー演算を行うことで図形描画処理を高速化できるかもしれないとなんとなく理解できたように思います。Direct3DでGDI+相当のもの、しかも汎用性を排除して高速化する仕組みを容易に構築できるのかもしれないとは思いました。

ただ、応急処置としてDirect2D/GDI+のAPIをKuinの私家版(つまり私のレポジトリ)に実装することはありうると思います。APIの模索・提示を行うということです。

kuina commented 6 years ago

ちょっと事例を把握しそこねています。ソースがあればご教示いただければと。

すみません、わたしが MSDN の説明を誤解していました。 Direct2D に対応したビデオカードが必要なのかと思い込んでいましたが、そうではないようですね。

ただ、ブラウザのレンダリングをターゲットにしているのでくいなちゃんがフルスクラッチでコーディングするよりも機能拡張が容易なのではないかと思った次第です。

確かに機能拡張が容易そうですね、検討してみます!

haruo-wakakusa commented 6 years ago

dcライブラリの実装検討を上げさせていただきました。README.mdにいろいろ寝言のようなことも書いてありますのでよろしくお願いします。 https://github.com/haruo-wakakusa/Kuin/tree/DC-from-Oct17

haruo-wakakusa commented 6 years ago

大事なことをアナウンスするのを忘れておりました。Direct2DとDirect3Dを相互連携する場合、Direct 3Dは10でも11でもよかったと思います。DXGIという仕組みを使うらしいのです。ただ最新の情報を把握しているわけではないのですが、Windows7におけるDirect2DとDirect3D11の相性は悪いらしく、Direct2DとDirect3D 10.1の組み合わせが推奨されていました。

kuina commented 6 years ago

ふむふむ。

haruo-wakakusa commented 6 years ago

デバッグサンプルは別プロジェクトで管理することといたしました。 https://github.com/haruo-wakakusa/Kuin-dev-misc/tree/master/draw2d_sample

haruo-wakakusa commented 6 years ago

くいなちゃんにマージしていただいたコードで1つ疑問点が。私が提出したコードでは、 struct SLinearGradientBrush { SBrush Parent; ID2D1LinearGradientBrush Brush; }; としていたと思いますが、マージ後のコードでは、 struct SBrushLinearGradient { SClass Class; ID2D1LinearGradientBrush Brush; }; となっていました。私がリニアグラディエントブラシクラスの定義内にブラシクラスの構造体を含めているのは、ID2D1LinearGradientBrushのポインタのほかにID2D1Brushのポインタも保持したいと考えているためです。というのも、COMインターフェースはC++の継承関係とは異なる実装なのでID2D1LinearGradientBrushオブジェクトをID2D1Brushにreinterpret_castして使うのは方法的に間違っているためです。たまたま動いているのかもしれませんが本来であればUUIDが異なるのでSBrushLinearGradient内にSBrushを含めたく。COMの本体が恐らくC++で実装されているであろうからあえてQueryInterfaceを使わずにC++の方式でキャストしてみてうまくいくようであればポインタの2重管理はしないというスタンスであればそれでもいいです。ご見解をお伺いいたしたく。

kuina commented 6 years ago

なるほど、QueryInterfaceを使わないといけなかったのですね…勉強になりました>< 二重持ちになるのが気になっていましたが、仕方がないですね。

haruo-wakakusa commented 6 years ago

多分Microsoftのほうでも非公式には親クラスオブジェクトの使用に関してはreinterpret_castで対応できるようにしているのかもしれません。そのほうが速度が稼げますので。COMに関して言えることは、COMオブジェクトはGUIDを用いて生成し、COMオブジェクトは関数ポインタの配列に対応しているということだけだったと思います。性能等で問題にならないと思うので、私が行っていた、ポインタを別々に持つ実装方式に戻しておきたいと思います。よろしくお願いいたします。

tatt61880 commented 5 years ago

すみません質問です。 下記のコードのように、draw@~ での描画と draw2d@~ での描画を混ぜたときに、 関数の実行順序によらず draw@~ がすべて描画されたあとに、 draw2d@~ が描画されるようなのですが、これはそういう仕様でしょうか?

func main()
    var wndMain: wnd@Wnd :: wnd@makeWnd(null, %aspect, 400, 200, "Title")
    var drawMain: wnd@Draw :: wnd@makeDraw(wndMain, 0, 0, 400, 200, %scale, %scale, false)
    do drawMain.onPaint :: @onPaint
    do drawMain.paint()

    while(wnd@act())
    end while
end func

func onPaint(wnd: wnd@WndBase, wid: int, hei: int)
    do draw2d@circle(100.0, 100.0, 100.0, 100.0, 0xFFFF0000)
    do draw@rect(100.0, 0.0, 200.0, 200.0, 0xFF00FF00)
    do draw2d@circle(300.0, 100.0, 100.0, 100.0, 0xFF0000FF)
    do draw@render(0)
end func

赤色の円が緑色の四角の下に描画されることを意図しましたが、実際には以下の添付画像のように緑色の四角が先に描画されています。 image

kuina commented 5 years ago

それは気づきませんでしたっ。 どうなっているのか確認してみます。

kuina commented 5 years ago

あ、多分ですが、draw2d のほうは CurRenderTarget->BeginDraw(); CurRenderTarget->EndDraw(); で挟んだ部分に描画処理を描くという内部的な仕様があり、EndDraw のタイミングで描画コールがまとめて積まれているのかもしれませんね。

かといって、draw2d@circle などの中で BeginDraw/EndDraw を呼び出すとパフォーマンスに影響が出そうなので、 draw2d の描画順は必ず最後になる挙動は仕様とせざるをえないかもしれませんね…。

tatt61880 commented 5 years ago

EndDraw のタイミングか Flush したタイミングで描画されるようですね。 ( https://msdn.microsoft.com/ja-jp/library/windows/desktop/dd371768.aspx )

オーバーヘッドがどれくらいあるかはわからないですが、下記のコードを lib_draw2d\main.cpp_circle 関数内の最後に追加したら、私が試しに書いた先述のコードに関しては描画順が正しくなりました。

    CurRenderTarget->Flush();

image

kuina commented 5 years ago

なるほど…、仕様としても分かりやすいほうが良いため、 FlushなりEndDrawなりを逐一行うように変更しようと思います。

kuina commented 5 years ago

Flushするようにしてpushしました。

tatt61880 commented 5 years ago

ありがとうございます!