普通のdll作成と何一つ変わらないので記事にするか迷ったのだが、一カ所だけわかりにくかったので念のためにまとめておく。
環境は Visual C++ 2017 + CUDA 9.2
手順は以下の通りである。
#include "cuda_runtime.h" #include "device_launch_parameters.h" #include <stdio.h> #include <cstring> #include "cudadll.h" __global__ void hello(char *c) { c[0] = 'h'; c[1] = 'e'; c[2] = 'l'; c[3] = 'l'; c[4] = 'o'; c[5] = '\0'; } //この関数の名前はmainではなく、呼び出す際に適切な名前にする void func_hello(char **c_cpu) { const size_t LEN = 6;// LEN = strlen("hello") + 1; char *c_gpu; cudaMalloc((void**)&c_gpu, LEN);//GPU側にメモリを確保 hello << <1, 1 >> >(c_gpu);//GPU側の関数を呼出 cudaMemcpy(c_cpu, c_gpu, LEN, cudaMemcpyDeviceToHost);//GPU側から実行結果を取得 cudaFree(c_gpu);//GPU側のメモリを解放 }
ソースコードに、_declspec(dllexport) , _declspec(dllimport)の分岐プリプロセッサを書き、先ほどのcall_cuda_funcの先頭につける。
cudadll.h
#ifdef __DLL_EXPORT_DO #define DLL_PORT extern "C" _declspec(dllexport) #else #define DLL_PORT extern "C" _declspec(dllimport) #endif DLL_PORT void func_hello(char **c_cpu);
#include "../cudadll/cudadll.h" #pragma comment(lib,"cudadll.lib") int main() { char c_cpu[12]; func_hello( (char**)&c_cpu); puts(c_cpu); getchar(); return 0; }
基本的に、普通のDLL生成と全く同じである。違いは、
A. dll側のプロジェクトのファイル名が、 (上記②,③)
B. _declspecの分岐のためのプリプロセッサを定義する場所が、 (上記⑤)
の二点である。
Cyclesは半透明な物を描くにあたり、透過処理を行う回数の上限を定めています。
この上限は[Render]→[Light Paths]→[Transparency]→Maxの値で変更できます。
レンダリングしたいシーンに対してこの値が小さすぎるとVolumeやα値を持つオブジェクトを正常に描画できません。
C言語系の関数は例外に対応していない。
個人的に例外は機能は好きだが構文は嫌いだ。それでも使った方がすっきりかける場合もある。
一番簡単なのはラップしてしまう事だろうがつまらないのでもうちょっと自己満足のいくものを考える。
以下のようなクラスを作る。(改めてみるとクラス名がひどい)
template<typename T> class MakeThrow{ T verr; int err_id; public: void ErrorValue(const T val){ verr = val; } void operator=(const T& v){ if( v == verr){ throw err_id; } }; MakeThrow<T>& operator()(const int id){ err_id=id; return *this; } };
そして以下のように使う
int _tmain(int argc, _TCHAR* argv[]) { MakeThrow<FILE*> rets; rets.ErrorValue(nullptr); try{ rets(5) = fopen("abc","r"); } catch(int e){ std::cout << e; } getchar(); return 0; }
retsの=をオーバーロードし、C言語系の関数の戻り値を受け取れるようにする。
=の中では、受け取った値を比較し、エラー値なら例外を出す。この時エラー番号を投げておくと、どこでエラーがあったかがよりわかりやすくなる(多分)ので、operator()でエラー番号を指定すると、その数値をcatchすることができる。
まあ、普通はまず使わないだろうが、XmlLite プログラミングを読んでいると、命令の一つ一つでif(FAILED(...)を行っていて、たとえそれが合理的で完膚なきまでに正しい行為でも、もう少し何とかならんのかと思ってしまったのである。
if(FAILED(pWriter->WriteStartElement(NULL, L"requirement", NULL))){ MessageBox(NULL, _T("WriteStartElement失敗"), _T("警告"), MB_OK | MB_ICONWARNING); return; } if(FAILED(pWriter->WriteElementString(NULL, L"type", NULL, L"ヘッダ"))){ MessageBox(NULL, _T("WriteElementString失敗"), _T("警告"), MB_OK | MB_ICONWARNING); return; } if(FAILED(pWriter->WriteElementString(NULL, L"name", NULL, L"XmlLite.h"))){ MessageBox(NULL, _T("WriteElementString失敗"), _T("警告"), MB_OK | MB_ICONWARNING); return; } if(FAILED(pWriter->WriteFullEndElement())){ MessageBox(NULL, _T("WriteFullEndElement失敗"), _T("警告"), MB_OK | MB_ICONWARNING); return; }
Noiseノードの設定。Propertiesで、[3D]を選ぶ。これをしないと2Dテクスチャが伸びたようなフォグになる。
Noiseノードの設定。Propertiesで、[3D]を選ぶ。これをしないと2Dテクスチャが伸びたようなフォグになる。
BlenderでASCII FBX files are not supportedと言われたとき。
どうやらASCII FBXに非対応らしいBlender。Autodesk公式からコンバータをダウンロードする必要がある。
FBX Converter Archivesへ行き、FBX Converter Archivesの自分のOSに合ったものを選ぶ。
インストーラー がダウンロードできるので、インストールする。
画面左側の領域にBlenderがエラーを出したfbxファイルをドロップすると、右側に出力予定のファイルパスとファイル名が表示される。
Change destination folder... ・・・保存先のフォルダを指定。ただし、下のRename selected fileでも変更可
Rename selected file... ・・・出力ファイル名を変更。ただし、フォルダ変更もできる
FBX Save Mode ・・・ここはBinaryであることを確認
Convert ・・・変換実行
EDITコントロールのサイズを変えるのはたやすいが、フォントサイズの指定はCreateFontでフォントを作成し、WM_SETFONTをSendMessageしなければならない。
下のプログラムは、CreateWindowで指定するときのコントロールの高さに応じて、相応のフォントサイズを計算している。
実際にこの方法を使うと、高さ50ピクセルくらいの時はいいが、100ピクセルなど巨大なテキストボックスのサイズに対してやや小さい文字となる。
そのため結果をさらに1.1~1.3倍程度するとよりちょうどよくなる(経験則)
/////////////////////////////////////////////////////////////////////////// // ウィンドウプロシージャ。今回はいじらない LRESULT CALLBACK WndProc(HWND hwnd, UINT msg, WPARAM wp, LPARAM lp) { switch (msg) { case WM_DESTROY: DestroyWindow(hwnd); PostQuitMessage(0); return 0; } return DefWindowProc(hwnd, msg, wp, lp); } int WINAPI WinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance, PSTR lpCmdLine, int nCmdShow) { HWND hwnd_parent; MSG msg; WNDCLASS winc; /////////////////////////////////////////////////////////////////////////// // ウィンドウクラスの登録。特別なことはしない winc.style = CS_HREDRAW | CS_VREDRAW; winc.lpfnWndProc = WndProc; winc.cbClsExtra = winc.cbWndExtra = 0; winc.hInstance = hInstance; winc.hIcon = LoadIcon(NULL, IDI_APPLICATION); winc.hCursor = LoadCursor(NULL, IDC_ARROW); winc.hbrBackground = (HBRUSH)GetStockObject(WHITE_BRUSH); winc.lpszMenuName = NULL; winc.lpszClassName = TEXT("MYWINDOW"); if (!RegisterClass(&winc)) return 0; /////////////////////////////////////////////////////////////////////////// // メインウィンドウの作成。特別なことはしない hwnd_parent = CreateWindow( TEXT("MYWINDOW"), TEXT("my window"), WS_OVERLAPPEDWINDOW | WS_VISIBLE, 100, 100, 200, 200, NULL, NULL, hInstance, NULL ); if (hwnd_parent == NULL) return 0; /////////////////////////////////////////////////////////////////////////// // エディットボックスの作成。内容的に普通。 int edit_height = 300;//高さを変数指定する。この高さを基準にフォントサイズを決定するため。 HWND hedit = CreateWindow( TEXT("EDIT"), TEXT("edit box"), WS_CHILD | WS_VISIBLE | WS_BORDER | ES_LEFT, 0, 0, 500, edit_height, hwnd_parent, (HMENU)1, hInstance, NULL ); //http://d.hatena.ne.jp/It_lives_vainly/20100331/1270002338 //${フォントのポイント数} × ${利用ディスプレイのDPI} ÷ 72 = ${フォント表示のドット数(高さ)} // → ${フォントのポイント数} = ${フォント表示のドット数(高さ)} * 72 ÷ ${利用ディスプレイのDPI} double wdpi, hdpi,sdpi; GetDPI(hedit, &wdpi, &hdpi,&sdpi); //DPIを求める関数(自作)を呼び出し int pt = edit_height * 72 / hdpi; // フォントサイズをポイントで求める // DPIを計算すると大体1.33になるので、 pt = edit_height / 1.33;でもいい //////////////////////////////////////////// //フォント作成 ///////////////////////////// /////////////////////////////////////////// HDC editdc = GetDC(hedit); int lfHeight = pt; HFONT hEditFont = CreateFont(lfHeight, 0, 0, 0, FW_NORMAL, FALSE, FALSE, 0, SHIFTJIS_CHARSET, OUT_DEFAULT_PRECIS, CLIP_DEFAULT_PRECIS, DEFAULT_QUALITY, DEFAULT_PITCH, "MS Pゴシック" ); ReleaseDC(hedit,editdc); ////////////////////////////////////////// //フォント変更のメッセージを送信 ////////// ////////////////////////////////////////// SendMessage(hedit, WM_SETFONT, (WPARAM)hEditFont, MAKELPARAM(TRUE, 0)); while (GetMessage(&msg, NULL, 0, 0)) { TranslateMessage(&msg); DispatchMessage(&msg); } DeleteObject(hEditFont); return msg.wParam; }
GetDPIの中身はこのようになる。
//参考↓ //https://gist.github.com/skitaoka/842b8d6cbb00bb54d9ef430dd082e822 void GetDPI(HWND hwnd,double *width_DPI,double* height_DPI,double* size_DPI) { HDC hdc = GetDC(hwnd); // 画面解像度を取得 const int width_pix = GetDeviceCaps(hdc, HORZRES); const int height_pix = GetDeviceCaps(hdc, VERTRES); const double size_pix = std::sqrt(width_pix * width_pix + height_pix * height_pix); // 物理サイズを取得 (単位は mm) const int width_mm = GetDeviceCaps(hdc, HORZSIZE); const int height_mm = GetDeviceCaps(hdc, VERTSIZE); // インチサイズに換算 const double width_inch = width_mm / 25.4; const double height_inch = height_mm / 25.4; const double size_inch = std::sqrt(width_inch * width_inch + height_inch * height_inch); // DPI を算出 const double width_dpi = width_pix / width_inch; const double height_dpi = height_pix / height_inch; const double size_dpi = size_pix / size_inch; *width_DPI = width_dpi; *height_DPI = height_dpi; *size_DPI = size_dpi; ReleaseDC(hwnd, hdc); }
EditBox高さ30 px
EditBox高さ60 px
EditBox高さ100 px
Visual C++ 2017でプロジェクトを作る際、exe本体の他にlib,dllのプロジェクトを作ることがある。
このとき、プロジェクト設定を例えば以下のようにしておくと、何かと便利である。オープンソース系のプロジェクトを日常的にコンパイルするならその慣例にならえば良いと思う。
また、pdbはデバッグ時にはあっていいがReleaseにはいらないので、Releaseビルドの時には出力されないようにする。
NewSolution
+--ExeProject //ソースコードが入る
+--DllProject //ソースコードが入る
+--Libs
+--x64
+--Release // lib,expが入る
+--Debug // lib,exp
+--Builds
+--x64
+--Release //exe,dllが入る
+--Debug //exe,dll,pdb が入る
DLLProjectを右クリック
● → プロパティページ → リンカー → デバッグ →
(構成をReleaseにしてから) デバッグ情報の生成 => 「いいえ」
さらに、iobj,ipdbが出力される場合は、
● → プロパティページ → リンカー → 最適化 →
(構成をReleaseにしてから) リンク時のコード生成 =>「既定」
● → プロパティページ → リンカー → 詳細設定 → インポートライブラリ
変更前 : $(OutDir)$(TargetName).lib
変更後 : $(SolutionDir)Libs\$(Platform)\$(Configuration)\$(TargetName).lib
これで、NewSolution\Libs\x64\Release\ にlibとexpが出力されるようになる。
最後に、libをリンクしたいexeのプロジェクトで、上記libファイル出力先を参照先に指定する
● → プロパティページ → リンカー → 全般 → 追加のライブラリディレクトリ
追記 : $(SolutionDir)Libs\$(Platform)\$(Configuration)
exe側、dll側同様に、
● → プロパティページ → 全般 → 出力ディレクトリ
変更前 : $(SolutionDir)$(Platform)\$(Configuration)\
変更後 : $(SolutionDir)Builds\$(Platform)\$(Configuration)\
詳しくは以下を参照:
https://msdn.microsoft.com/ja-jp/library/c02as0cs.aspx
今回使用したのは、
$(SolutionDir) | slnファイルのあるディレクトリへのパスが入る。最後に’¥’がつく |
$(Platform) | x64,win32などが入る。最後に’¥’はつかない。→ ![]() |
$(Configuration) | Release,Debugなどが入る。最後に’¥’はつかない。→ ![]() |
一番外側のdivの前後はhtmlやheadやbodyやnaviを入れる領域です。このコードを囲むループは存在しません。
<div class="contents"> <?php $cat_info = get_category( get_query_var( 'cat' ) ); //現在のページのカテゴリ情報を取得 $my_paged = get_query_var( 'paged', 0 ); //現在のページのページ番号を取得 ?> <?php // 記事一覧 $args = array( 'post_type' => array('post'), 'post_status' => array('publish'), 'order'=>'desc', 'orderby'=>'post_date', 'paged' => $my_paged, 'posts_per_page' => 20, 'category_name' => $cat_info->slug ); $query = new WP_Query($args);// $argsで指定した条件に合う記事群を取得 /* タイトル表示 */ echo '<h1>' . esc_html( $cat_info->name ); echo '('; echo (0 == $my_paged ? 1 : get_query_var( 'paged', 1 )) . '/'; echo $query->max_num_pages; echo ')'; echo '</h1>'; // 記事表示用ループ if ( $query->have_posts() ) : while ( $query->have_posts() ) : $query->the_post(); ?> <div> <time><?php the_modified_date('Y/m/d'); ?></time> <!-- 更新日 --> <p><a href="<?php the_permalink(); ?>"><?php esc_html( the_title() ); ?></a></p> <!-- タイトル --> </div> <div> <div><?php /* the_content(); */ /*本文を表示する場合は左のコメントを外す*/ ?></div> </div> <?php endwhile; endif; wp_reset_postdata(); ?> <?php //ページネーション $big = 999999999; $links = paginate_links(array( 'base' => str_replace($big, '%#%', esc_url(get_pagenum_link($big))), 'show_all' => true, 'type' => 'array', 'format' => '?paged=%#%', 'current' => max(1, $my_paged ), 'total' => $query->max_num_pages, 'prev_text' => '前へ', 'next_text' => '次へ', )); ?> <!-- ページネーション表示 --> <?php if( $links){ ?> <div> <?php foreach($links as $lnk){ ?> <span><?php echo $lnk ?></span> <?php }} ?> </div> </div>