たすブログ

近況:4 告知:4 技術ブログ:2 な割合です

【UE4】インラインアセンブラを使えるかどうかについて

この記事はUnrealEngine 4(UE4)AdventCalender 2020の17日目の投稿記事です。 

qiita.com

 

箸休め記事のつもりで書いています。

 題材は他に「BPはテキストエディタを介してコピペできるのだから

頑張ってソースからBPにするコンバータ作れないかチャレンジ」と

悩みましたが「C++にしたほうが早い」と思ったのでこっちにしました。

 

 さて、UE4で開発をする際に

「昔のゲームロジックやソースの一部分だけでも有効活用したい」

ということがあるかと思いますが

何かの拍子でそれがアセンブラだったらどうしよう、といった場合を想定して

インラインアセンブラを動かす調査をしてみました。

 

結論

先に書いておきます。

  • C++ソースにインラインアセンブラ直埋めは出来ない(出来るならすみません調査漏れです)
  • どうしてもやりたいなら外部ライブラリに入れる
  • 別の方法で解決したほうが多分いい

となります。

ここから先は上記結論を出すためにいろいろした内容の共有ですので、

UE4インラインアセンブラを触らなければならなくなりそうなときは

「避けた方がいいっすよ」という材料としていただければ。

 

前提

実際問題、アセンブラのソースを移植する必要が出てきた場合は

書き方の違いや特にMC68000系だと逆、ということがありますし

今時のコンパイラを超える速度を手書きは現実味がなく、むしろ遅くなる可能性と

描画周りはそのままはまず無理なので作り直すかと思います。

 

ですので、ロジック部分だけの移植になりますがそれでも

  • エミュレーションさせる(ちゃんと作ればちゃんとそのまま動くはず)
  • ソースを読み取って書き直す(オーバーヘッドをつぶしながら進められるので処理速度が確保できる)
  • ソースをコンバートする(ベースを有効活用したまま拡張性がある)

あたりが第一候補で、

インラインアセンブラでなんとかする、は選択肢としては

あまり出ないと思うのですが、それはそれ、というお話です。

 

UE4C++ソースにインラインアセンブラを入れられるのか

まず大前提としてUE4C++ソースにインラインアセンブラ

埋め込んで走ってくれるのか、という部分ですが、うまく動いてくれませんでした。

(もし動く方法あれば教えていただけると嬉しいです)

 

以下、調査結果。

まず、VisualStudioでx64でビルドしようとした場合、

インラインアセンブラは使えません。

なので、__asmを使用することは諦めてx64用のアセンブラ対応にします。

 

ちなみにWin32にしてビルドするとどうなるのかなあとしてみた結果、

ソース無視されてビルド通りました。

f:id:tasuya:20201216021145p:plain

どうみてもダメなaがいるのにビルドが通るの図

 

 ということで、ごにょごにょ頑張るよりはもうこの時点で

素直にx64用のインラインアセンブラ対応に切り替えます。

対応方法は以下を参考にすると良さそうです。

docs.microsoft.com

 

 ソリューションエクスプローラーでプロジェクトを選択して、
メニュー>プロジェクト>ビルドのカスタマイズを開いて、masmをオンに。

f:id:tasuya:20201216191904p:plain

ちなみにここでチェックを入れてもGenerateVisualStudioProjectFileをすると

プロジェクトファイル作り直されるのでリセットされます。

 

次に新しい項目の追加からC++ファイルを選択して

拡張子をtest.asmのように拡張子をasm指定して作成。 

f:id:tasuya:20201216021857p:plain

masmをオンにしてから作成すると自動で項目の種類が変更されます

で、あとは.asmをがりがり書いていきましょう。

今回は通すことが目的なのでシンプルに引き算です。 

.code
public asmtest
asmtest proc
sub ecx,edx
mov eax,ecx
ret
asmtest endp
end

 

呼び出し側は、適当にUObjectとかUActorでクラスを作成して

extern "C" int asmtest(int a, int b);

を追加、asmtest(3,2,);とかをどこかで呼び出してやると1が返るはずという想定です。

 

ということで、この状態でインラインアセンブラが準備できたのでビルドします。

すると、関数未解決エラーが出ます。

 asmファイルに適当にaとかを入れてビルドしてもそこで引っかからずに

同じ結果になるので

asmファイルをそもそも解決してくれないようです。

 

asmファイルを組み込むようにごにょごにょ出来ないか

解決方法を探したんですがすぐには見つけきれなかったので、

可能性は残りますが、これは一旦直接呼べないという形で次へ行きます。

 

外部ライブラリで呼ぶ

ビルドが通らないなら通ったものを渡せばいい、ということで

次に外部ライブラリでインラインアセンブラを埋め込んで

それを呼び出すとどうなるか、を試してみました。

 

外部ライブラリの作成方法はまめおさんの昨年のアドカレ記事が詳しいです。
papersloth.hatenablog.com

 

この手順で外部ライブラリを作成します。

今回は、静的ライブラリで試しましたので

  1. 静的ライブラリの手順でプロジェクトを準備
  2. メニュー>プロジェクト>ビルドのカスタマイズから、masmをオン
  3. 新しい項目の追加からC++ファイルを選択してAdCalAsmTest.asmを作成し、内容を上記asmtest命令に
  4. ヘッダーとソース準備
// header.h
#pragma once
#include <sdkddkver.h>
#define WIN32_LERN_AND_MEAN
namespace AdCalLib
{
 class AsmLib
 {
  public:
  AsmLib();
  virtual ~AsmLib();
  int CallAsmTest(int a, int b);
 };
}
//Source.cpp
#include "Header.h"
extern "C"  int asmtest(int a, int b);\\Asmコード
using namespace AdCalLib;

AsmLib::AsmLib(){}
AsmLib::~AsmLib(){}

int AsmLib::CallAsmTest(int a, int b)
{
 return asmtest(a , b);
}

で、ビルドするとインラインアセンブラの入ったLibが作成できます。

 

そして出来たlibと作成したhファイルをUE4プロジェクトに入れてC++

呼び出せるようにします。

今回は適当にActorを作ってBeginPlayに入れました。

#include "AsmTestActor.h"
#if PLATFORM_WINDOWS #include "Header.h" #include <memory> #endif AAsmTestActor::AAsmTestActor() {  PrimaryActorTick.bCanEverTick = true; } void AAsmTestActor::BeginPlay() {  Super::BeginPlay(); #if PLATFORM_WINDOWS  int a = 3, b = 2;  const auto asmLib(std::make_shared<AdCalLib::AsmLib>());  UE_LOG(LogTemp, Log, TEXT("AsmTest : %d - %d = %d"), a,b,asmLib->CallAsmTest(a,b)); #endif }

asmtestの呼び出しはライブラリ側で行っているので

普通に外部ライブラリ命令を呼び出す形でOKです。

 

で、これをビルドして、BPの親にしてレベルに配置して呼び出してやると、

 

f:id:tasuya:20201216192423p:plain

うごいた


ということで、

asmファイルを入れた外部ライブラリでは、ひとまず動く

という形で今回は決着としたいです。

 

まとめ

速度面や調整面を考えず実際呼び出せるかどうか、

という点だけをみれば出来るのは出来ます

今回ベンチマーク的なものとかは取れていないですが、

どうしても処理が細かく大量に呼ばれる作りにはなってしまうので、

どうしても使う必要がある時だけにとどめるのがよさそうです。

 

直接UE4のC++への埋め込みは何か見落としてそうな気がすごいするので

引き続き調査は続けます。あと「直接BPにコンバートチャレンジ」も。

 

それでは皆様良い年末年始になりますよう。