C++ ときどき ごはん、わりとてぃーぶれいく☆

USAGI.NETWORKのなかのひとのブログ。主にC++。

UE4/C++, Blueprint: UAsyncTaskDownloadImage を拡張した実装を使いたい場合にリンクが必要となる RHI と RenderCore のメモ

状況

UAsyncTaskDownloadImage はブループリントでは癖の強めの非同期処理ノード。何かを For などでループしながら DownloadImage ノードを使い OnSuccess にテクスチャーデータだけでなく何らかのインデックスなどの付加情報を引っ掛けた処理を行わせようとすると、結果的にはループの最後の状態の値が非同期処理の実行時には採用され、意図しない処理結果となる事がある。

例えば次のような具合のノードをブループリントで組んだ場合、

f:id:USAGI-WRP:20180911185745p:plain

DownloadImage ノードの左側の同期処理される赤文字出力の PrintText では 1, 2, 3, ... が出力されるが、 DownloadImage ノードの右側の非同期処理されえる青文字出力の PrintText では 8, 8, 8, ... が出力される。内部的に IHttpRequest の非同期処理を使うため DownloadImageOnSuccess あるいは OnFail デリゲートのブロードキャストから実行される非同期処理の実行時には ForLoop は完了した最後の状態で Index8 になっている。( DownloadImage 実行時にその後の非同期処理で必要になる変数をキャプチャーしてくれる機能は無いが、ブループリントの実装都合 Index が未定義で落ちる事は無い。 )

そこで、必要に応じた変数のキャプチャーと通知の機能を仕込んだ UAsyncTaskDownloadImage の拡張を実装したくなる。幸い UE4オープンソースソフトウェアで実装を自分のプロジェクトへコピー&ペーストして、必要に応じて UAsyncTaskDownloadImageMyCustom などクラス名を変え、DownloadImage 関数の引数にキャプチャーしたい変数を追加しメンバー変数として保持しつつ、 OnSuccess あるいは OnFail デリゲートの定義を DECLARE_DYNAMIC_MULTICAST_DELEGATE_TwoParams など使って必要なだけ引数を拡張し、それらのデリゲートの Broadcast の呼び出し時にメンバー変数へ保持しておいた値を実引数として与えるようにすればよい。

よい、のじゃが、これだけだとビルドできない。たぶん↓のようなリンクエラーに襲われる事になる。

CompilerResultsLog: Error: AsyncTaskDownloadMap.cpp.obj : error LNK2019: unresolved external symbol "__declspec(dllimport) public: static bool __cdecl FRHIResource::Bypass(void)" (__imp_?Bypass@FRHIResource@@SA_NXZ) referenced in function "void __cdecl WriteRawToTexture_RenderThread(class FTexture2DDynamicResource *,class TArray<unsigned char,class FDefaultA
llocator> const &,bool)" (?WriteRawToTexture_RenderThread@@YAXPEAVFTexture2DDynamicResource@@AEBV?$TArray@EVFDefaultAllocator@@@@_N@Z)
CompilerResultsLog: Error: AsyncTaskDownloadMap.cpp.obj : error LNK2019: unresolved external symbol "__declspec(dllimport) public: unsigned int __cdecl FRHITexture2D::GetSizeX(void)const " (__imp_?GetSizeX@FRHITexture2D@@QEBAIXZ) referenced in function "void __cdecl WriteRawToTexture_RenderThread(class FTexture2DDynamicResource *,class TArray<unsigned char,c
lass FDefaultAllocator> const &,bool)" (?WriteRawToTexture_RenderThread@@YAXPEAVFTexture2DDynamicResource@@AEBV?$TArray@EVFDefaultAllocator@@@@_N@Z)
CompilerResultsLog: Error: AsyncTaskDownloadMap.cpp.obj : error LNK2019: unresolved external symbol "__declspec(dllimport) public: unsigned int __cdecl FRHITexture2D::GetSizeY(void)const " (__imp_?GetSizeY@FRHITexture2D@@QEBAIXZ) referenced in function "void __cdecl WriteRawToTexture_RenderThread(class FTexture2DDynamicResource *,class TArray<unsigned char,c
lass FDefaultAllocator> const &,bool)" (?WriteRawToTexture_RenderThread@@YAXPEAVFTexture2DDynamicResource@@AEBV?$TArray@EVFDefaultAllocator@@@@_N@Z)
CompilerResultsLog: Error: AsyncTaskDownloadMap.cpp.obj : error LNK2019: unresolved external symbol "__declspec(dllimport) public: static enum ENamedThreads::Type __cdecl FRenderCommand::GetDesiredThread(void)" (__imp_?GetDesiredThread@FRenderCommand@@SA?AW4Type@ENamedThreads@@XZ) referenced in function "private: void __cdecl TGraphTask<class `private: void 
__cdecl UAsyncTaskDownloadMap::HandleMapRequest(class TSharedPtr<class IHttpRequest,0>,class TSharedPtr<class IHttpResponse,1>,bool)'::`16'::EURCMacro_FWriteRawDataToTexture>::SetupPrereqs(class TArray<class TRefCountPtr<class FGraphEvent>,class TInlineAllocator<4,class FDefaultAllocator> > const *,enum ENamedThreads::Type,bool)" (?SetupPrereqs@?$TGraphTask@
VEURCMacro_FWriteRawDataToTexture@?BA@??HandleMapRequest@UAsyncTaskDownloadMap@@AEAAXV?$TSharedPtr@VIHttpRequest@@$0A@@@V?$TSharedPtr@VIHttpResponse@@$00@@_N@Z@@@AEAAXPEBV?$TArray@V?$TRefCountPtr@VFGraphEvent@@@@V?$TInlineAllocator@$03VFDefaultAllocator@@@@@@W4Type@ENamedThreads@@_N@Z)
CompilerResultsLog: Error: AsyncTaskDownloadMap.cpp.obj : error LNK2019: unresolved external symbol "__declspec(dllimport) public: static enum ESubsequentsMode::Type __cdecl FRenderCommand::GetSubsequentsMode(void)" (__imp_?GetSubsequentsMode@FRenderCommand@@SA?AW4Type@ESubsequentsMode@@XZ) referenced in function "public: static class TGraphTask<class `priva
te: void __cdecl UAsyncTaskDownloadMap::HandleMapRequest(class TSharedPtr<class IHttpRequest,0>,class TSharedPtr<class IHttpResponse,1>,bool)'::`16'::EURCMacro_FWriteRawDataToTexture>::FConstructor __cdecl TGraphTask<class `private: void __cdecl UAsyncTaskDownloadMap::HandleMapRequest(class TSharedPtr<class IHttpRequest,0>,class TSharedPtr<class IHttpRespons
e,1>,bool)'::`16'::EURCMacro_FWriteRawDataToTexture>::CreateTask(class TArray<class TRefCountPtr<class FGraphEvent>,class TInlineAllocator<4,class FDefaultAllocator> > const *,enum ENamedThreads::Type)" (?CreateTask@?$TGraphTask@VEURCMacro_FWriteRawDataToTexture@?BA@??HandleMapRequest@UAsyncTaskDownloadMap@@AEAAXV?$TSharedPtr@VIHttpRequest@@$0A@@@V?$TSharedP
tr@VIHttpResponse@@$00@@_N@Z@@@SA?AVFConstructor@1@PEBV?$TArray@V?$TRefCountPtr@VFGraphEvent@@@@V?$TInlineAllocator@$03VFDefaultAllocator@@@@@@W4Type@ENamedThreads@@@Z)
CompilerResultsLog: Error: AsyncTaskDownloadMap.cpp.obj : error LNK2019: unresolved external symbol "__declspec(dllimport) class FRHICommandListImmediate & __cdecl GetImmediateCommandList_ForRenderCommand(void)" (__imp_?GetImmediateCommandList_ForRenderCommand@@YAAEAVFRHICommandListImmediate@@XZ) referenced in function "private: virtual void __cdecl TGraphTa
sk<class `private: void __cdecl UAsyncTaskDownloadMap::HandleMapRequest(class TSharedPtr<class IHttpRequest,0>,class TSharedPtr<class IHttpResponse,1>,bool)'::`16'::EURCMacro_FWriteRawDataToTexture>::ExecuteTask(class TArray<class FBaseGraphTask *,class FDefaultAllocator> &,enum ENamedThreads::Type)" (?ExecuteTask@?$TGraphTask@VEURCMacro_FWriteRawDataToTextu
re@?BA@??HandleMapRequest@UAsyncTaskDownloadMap@@AEAAXV?$TSharedPtr@VIHttpRequest@@$0A@@@V?$TSharedPtr@VIHttpResponse@@$00@@_N@Z@@@EEAAXAEAV?$TArray@PEAVFBaseGraphTask@@VFDefaultAllocator@@@@W4Type@ENamedThreads@@@Z)
CompilerResultsLog: Error: AsyncTaskDownloadMap.cpp.obj : error LNK2001: unresolved external symbol "__declspec(dllimport) bool GRHINeedsExtraDeletionLatency" (__imp_?GRHINeedsExtraDeletionLatency@@3_NA)
CompilerResultsLog: Error: AsyncTaskDownloadMap.cpp.obj : error LNK2001: unresolved external symbol "__declspec(dllimport) private: static class TLockFreePointerListUnordered<class FRHIResource,128> FRHIResource::PendingDeletes" (__imp_?PendingDeletes@FRHIResource@@0V?$TLockFreePointerListUnordered@VFRHIResource@@$0IA@@@A)
CompilerResultsLog: Error: AsyncTaskDownloadMap.cpp.obj : error LNK2001: unresolved external symbol "__declspec(dllimport) class FDynamicRHI * GDynamicRHI" (__imp_?GDynamicRHI@@3PEAVFDynamicRHI@@EA)
CompilerResultsLog: Error: AsyncTaskDownloadMap.cpp.obj : error LNK2001: unresolved external symbol "__declspec(dllimport) class FRHICommandListExecutor GRHICommandList" (__imp_?GRHICommandList@@3VFRHICommandListExecutor@@A)
CompilerResultsLog: Error: AsyncTaskDownloadMap.cpp.obj : error LNK2001: unresolved external symbol "__declspec(dllimport) bool GIsThreadedRendering" (__imp_?GIsThreadedRendering@@3_NA)
CompilerResultsLog: Error: AsyncTaskDownloadMap.cpp.obj : error LNK2001: unresolved external symbol "__declspec(dllimport) class TAtomic<bool> GMainThreadBlockedOnRenderThread" (__imp_?GMainThreadBlockedOnRenderThread@@3V?$TAtomic@_N@@A)

リンクエラーの解消法

MyProjeect.Build.csPublicDependencyModuleNamesRHIRenderCore を追加する。

    PublicDependencyModuleNames.AddRange
      ( new string[]
        { "RHI", "RenderCore"

ちなみに、 RHI は Rendering Hardware Interface の事らしい。プラットフォームごとに D3D, OGL などを抽象化したレンダリングハードウェアのインターフェースらしいが、私もその実装まではまだ確認していない。

参考