CreateProcessで環境変数を設定してアプリケーションを起動できる。
テスト用の環境変数を表示するプログラム。
// プログラム1 環境変数を表示 // display_myenvs.exe #include <iostream> #include <windows.h> int main() { char EnvVal[32767]; size_t limit = sizeof(EnvVal) / sizeof(EnvVal[0]); std::cout << "------------------------" << std::endl; // 環境変数を取得 GetEnvironmentVariableA("MY_ENV_STR", EnvVal, limit); std::cout << "MY_ENV_STR: " << EnvVal << std::endl; std::cout << "------------------------" << std::endl; GetEnvironmentVariableA("MY_ENV_INT",EnvVal, limit); std::cout << "MY_ENV_INT: " << EnvVal << std::endl; }
// プログラム2 環境変数設定してアプリケーションを起動 #include <iostream> #include <windows.h> int main() { //////////////////////////////////////////////////// // 独自の環境変数設定 // 環境変数作成 std::wstring envVar1 = L"MY_ENV_STR=HelloWorld"; std::wstring envVar2 = L"MY_ENV_INT=12345"; // 環境変数を設定するときは各変数を\0文字で区切り、終端を\0\0にする // "VAL1=value1\0VAL2=value2\0\0" std::wstring envVars; envVars += envVar1; envVars.push_back(L'\0'); // 環境変数の区切り envVars += envVar2; envVars.push_back(L'\0'); // 環境変数の区切り // 最後は\0\0で終端 envVars.push_back(L'\0'); envVars.push_back(L'\0'); // アプリ起動 STARTUPINFOW si = { sizeof(si) }; PROCESS_INFORMATION pi; std::wstring apppath = L"display_myenvs.exe"; BOOL ret = CreateProcessW( nullptr, &apppath[0], // 破壊的パースがされるため非constで渡す nullptr, nullptr, FALSE, CREATE_UNICODE_ENVIRONMENT, (void*)envVars.c_str(), nullptr, &si, &pi ); // エラー処理 if (!ret) { std::cerr << "CreateProcessA failed with error: " << GetLastError() << std::endl; return 1; } }
やってできないことはない。実用性があるかどうかは別。これを使うと引数を与える順番を考えなくてよくなるのと、引数の意味が呼び出し側でわかるようになるので若干可読性が上がるかもしれない。
#include <iostream> #include <type_traits>
// 型TがArgs...に含まれているかどうかを判定するメタ関数 template <typename T, typename... Args> constexpr bool is_contains_type = (std::is_same_v<T, Args> || ...);
// 可変引数テンプレートから特定の型Tで与えられた変数を取得する template<typename T, typename First, typename... Rest> decltype(auto) get_value(First&& first, Rest&&... rest) { if constexpr (std::is_same_v<T, std::decay_t<First> > == true) { return std::forward<First>(first); } // sizeof...(rest)で残りの引数の数をチェック else if constexpr (sizeof...(rest) > 0) { // 残りの引数restから T を探す return get_value<T>( std::forward<Rest>(rest)...); } else { static_assert(sizeof...(rest) > 0, "No matching type found in arguments."); } }
template<int ID> // IDを与えてusing時にそれぞれを違う型として認識されるようにする struct ArgString { std::string value; ArgString& operator=(const std::string& str) { value = str; return *this; } }; template<int ID> struct ArgInt { int value; ArgInt& operator=(int i) { value = i; return *this; } }; using Text = ArgString<0>; using Title = ArgString<1>; using Index = ArgInt<0>; using Count = ArgInt<1>; // グローバル引数として、関数に渡す時に指定する引数名を定義 Index index; Count count; Text text; Title title;
// print関数の定義 template <class... Args> void print(Args&&... args) {// argsにはindex,count,text,titleのいずれかが入っている Index index; Count count; Text text; Title title; if constexpr (is_contains_type<Index, std::decay_t<Args>...>) { auto&& arg = get_value<Index>( std::forward<Args>(args)... ); index = arg; // 引数の値をIndexに設定 } if constexpr (is_contains_type<Count, std::decay_t<Args>...>) { auto&& arg = get_value<Count>( std::forward<Args>(args)... ); count = arg; // 引数の値をCountに設定 } if constexpr (is_contains_type<Text, std::decay_t<Args>...>) { auto&& arg = get_value<Text>( std::forward<Args>(args)... ); text = arg; // 引数の値をTextに設定 } if constexpr (is_contains_type<Title, std::decay_t<Args>...>) { auto&& arg = get_value<Title>( std::forward<Args>(args)... ); title = arg; // 引数の値をTitleに設定 } // 各値を使用 std::cout << "index: " << index.value << std::endl; std::cout << "count: " << count.value << std::endl; std::cout << "text: " << text.value << std::endl; std::cout << "title: " << title.value << std::endl; }
int main() { print( index = 5, count = 10, text = "Hello, World!", title = "My Title" ); }
実行結果
optixRaycastingのプロジェクトにどのファイルが必要かを確認する
ソースコードの場所:
C:\ProgramData\NVIDIA Corporation\OptiX SDK 9.0.0\SDK\optixRaycasting\
に、以下のファイルが存在
プロジェクトの作成の前に、nvccで以下を生成しておく。
cl.exeも呼び出されるので、x64 Native Tools Command Prompt等で実行する。
本体のC++プロジェクトがMTかMDかでビルドオプションを変える
MT
MD
で、VC++で、C++プロジェクトを作成。
ぱっと見いらないものなどを消して、依存関係やらを整頓する。
・サンプルをビルドしたディレクトリ内にある sutil_7_sdk.lib が必要。
・gladはこの段階では取り除けなかった。
・sutil::loadSceneがモデルの他にシーン情報も設定している
・optixRaycasting_generated_optixRaycastingKernels.cu.obj optixRaycasting_generated_optixRaycastingKernels_dlink.obj をリンカー → 入力 → 追加の依存ファイルに追記しておく
/* * SPDX-FileCopyrightText: Copyright (c) 2019 - 2024 NVIDIA CORPORATION & AFFILIATES. All rights reserved. * SPDX-License-Identifier: BSD-3-Clause * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are met: * * 1. Redistributions of source code must retain the above copyright notice, this * list of conditions and the following disclaimer. * * 2. Redistributions in binary form must reproduce the above copyright notice, * this list of conditions and the following disclaimer in the documentation * and/or other materials provided with the distribution. * * 3. Neither the name of the copyright holder nor the names of its * contributors may be used to endorse or promote products derived from * this software without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ #include <cuda_runtime.h> #include <optix.h> #include <optix_function_table_definition.h> #include <optix_stack_size.h> #include <optix_stubs.h> /* // Include ディレクトリ C:\ProgramData\NVIDIA Corporation\OptiX SDK 9.0.0\include C:\ProgramData\NVIDIA Corporation\OptiX SDK 9.0.0\SDK\cuda C:\ProgramData\NVIDIA Corporation\OptiX SDK 9.0.0\SDK C:\Program Files\NVIDIA GPU Computing Toolkit\CUDA\v12.8\include C:\ProgramData\NVIDIA Corporation\OptiX SDK 9.0.0\SDK\support\imgui\.. */ /* // nvccでビルドして作成する各種関連ファイル: optixRaycasting_generated_optixRaycastingKernels.cu.obj optixRaycasting_generated_optixRaycastingKernels_dlink.obj */ /* プリプロセッサ _USE_MATH_DEFINES NOMINMAX GLAD_GLAPI_EXPORT */ // D:\tmp\optixbuild ここでインクルードしなくてもよい //#include <sampleConfig.h> #include <fstream> // getInputDataの代わりにifstreamを使う #include "cuda/whitted.h" #include <sutil/CUDAOutputBuffer.h> #include <sutil/Matrix.h> #include <sutil/Record.h> #include <sutil/Scene.h> #include <sutil/sutil.h> #include "optixRaycasting.h" #include "optixRaycastingKernels.h" #include <iomanip> #pragma comment(lib, "D:\\tmp\\optixbuild\\lib\\Release\\glad.lib") #pragma comment(lib, "D:\\tmp\\optixbuild\\lib\\Release\\sutil_7_sdk.lib") #pragma comment(lib, "C:\\Program Files\\NVIDIA GPU Computing Toolkit\\CUDA\\v12.8\\lib\\x64\\cudart_static.lib") #pragma comment(lib, "C:\\Program Files\\NVIDIA GPU Computing Toolkit\\CUDA\\v12.8\\lib\\x64\\cudadevrt.lib") struct RaycastingState { int width = 0; int height = 0; OptixDeviceContext context = 0; sutil::Scene scene = {}; OptixPipelineCompileOptions pipeline_compile_options = {}; OptixModule ptx_module = 0; OptixPipeline pipeline_1 = 0; OptixPipeline pipeline_2 = 0; OptixProgramGroup raygen_prog_group = 0; OptixProgramGroup miss_prog_group = 0; OptixProgramGroup hit_prog_group = 0; Params params = {}; Params params_translated = {}; OptixShaderBindingTable sbt = {}; sutil::Texture mask = {}; }; typedef sutil::Record<whitted::HitGroupData> HitGroupRecord; void createModule( RaycastingState& state ) { OptixModuleCompileOptions module_compile_options = {}; #if OPTIX_DEBUG_DEVICE_CODE module_compile_options.optLevel = OPTIX_COMPILE_OPTIMIZATION_LEVEL_0; module_compile_options.debugLevel = OPTIX_COMPILE_DEBUG_LEVEL_FULL; #else module_compile_options.optLevel = OPTIX_COMPILE_OPTIMIZATION_DEFAULT; module_compile_options.debugLevel = OPTIX_COMPILE_DEBUG_LEVEL_MINIMAL; #endif state.pipeline_compile_options.usesMotionBlur = false; state.pipeline_compile_options.traversableGraphFlags = OPTIX_TRAVERSABLE_GRAPH_FLAG_ALLOW_SINGLE_LEVEL_INSTANCING; state.pipeline_compile_options.numPayloadValues = 4; state.pipeline_compile_options.numAttributeValues = 2; state.pipeline_compile_options.exceptionFlags = OPTIX_EXCEPTION_FLAG_NONE; state.pipeline_compile_options.pipelineLaunchParamsVariableName = "params"; size_t inputSize = 0; // 使いにくいので書き換え #if 0 const char* input = sutil::getInputData( OPTIX_SAMPLE_NAME, OPTIX_SAMPLE_DIR, "optixRaycasting.cu", inputSize ); #else
std::string filepath = "optixRaycasting_generated_optixRaycasting.cu.optixir"; std::ifstream file(filepath, std::ios::binary | std::ios::ate); if (!file) throw std::runtime_error("Failed to open file: " + filepath); std::streamsize size = file.tellg(); // ファイルサイズ file.seekg(0, std::ios::beg); // 先頭へ移動 std::vector<char> buffer; buffer.resize(size); if (!file.read(buffer.data(), size)) throw std::runtime_error("Failed to read file: " + filepath); const char* input = buffer.data(); inputSize = size;
#endif // optixModuleCreateに.optixirを与える OPTIX_CHECK_LOG( optixModuleCreate( state.context, &module_compile_options, &state.pipeline_compile_options, input, inputSize, LOG, &LOG_SIZE, &state.ptx_module ) ); } void createProgramGroups( RaycastingState& state ) { OptixProgramGroupOptions program_group_options = {}; OptixProgramGroupDesc raygen_prog_group_desc = {}; raygen_prog_group_desc.kind = OPTIX_PROGRAM_GROUP_KIND_RAYGEN; raygen_prog_group_desc.raygen.module = state.ptx_module; raygen_prog_group_desc.raygen.entryFunctionName = "__raygen__from_buffer"; OPTIX_CHECK_LOG( optixProgramGroupCreate( state.context, &raygen_prog_group_desc, 1, // num program groups &program_group_options, LOG, &LOG_SIZE, &state.raygen_prog_group ) ); OptixProgramGroupDesc miss_prog_group_desc = {}; miss_prog_group_desc.kind = OPTIX_PROGRAM_GROUP_KIND_MISS; miss_prog_group_desc.miss.module = state.ptx_module; miss_prog_group_desc.miss.entryFunctionName = "__miss__buffer_miss"; OPTIX_CHECK_LOG( optixProgramGroupCreate( state.context, &miss_prog_group_desc, 1, // num program groups &program_group_options, LOG, &LOG_SIZE, &state.miss_prog_group ) ); OptixProgramGroupDesc hit_prog_group_desc = {}; hit_prog_group_desc.kind = OPTIX_PROGRAM_GROUP_KIND_HITGROUP; hit_prog_group_desc.hitgroup.moduleAH = state.ptx_module; hit_prog_group_desc.hitgroup.entryFunctionNameAH = "__anyhit__texture_mask"; hit_prog_group_desc.hitgroup.moduleCH = state.ptx_module; hit_prog_group_desc.hitgroup.entryFunctionNameCH = "__closesthit__buffer_hit"; OPTIX_CHECK_LOG( optixProgramGroupCreate( state.context, &hit_prog_group_desc, 1, // num program groups &program_group_options, LOG, &LOG_SIZE, &state.hit_prog_group ) ); } void createPipelines( RaycastingState& state ) { const uint32_t max_trace_depth = 1; OptixProgramGroup program_groups[3] = {state.raygen_prog_group, state.miss_prog_group, state.hit_prog_group}; OptixPipelineLinkOptions pipeline_link_options = {}; pipeline_link_options.maxTraceDepth = max_trace_depth; OPTIX_CHECK_LOG( optixPipelineCreate( state.context, &state.pipeline_compile_options, &pipeline_link_options, program_groups, sizeof( program_groups ) / sizeof( program_groups[0] ), LOG, &LOG_SIZE, &state.pipeline_1 ) ); OPTIX_CHECK_LOG( optixPipelineCreate( state.context, &state.pipeline_compile_options, &pipeline_link_options, program_groups, sizeof( program_groups ) / sizeof( program_groups[0] ), LOG, &LOG_SIZE, &state.pipeline_2 ) ); OptixStackSizes stack_sizes_1 = {}; OptixStackSizes stack_sizes_2 = {}; for( auto& prog_group : program_groups ) { OPTIX_CHECK( optixUtilAccumulateStackSizes( prog_group, &stack_sizes_1, state.pipeline_1 ) ); OPTIX_CHECK( optixUtilAccumulateStackSizes( prog_group, &stack_sizes_2, state.pipeline_2 ) ); } uint32_t direct_callable_stack_size_from_traversal; uint32_t direct_callable_stack_size_from_state; uint32_t continuation_stack_size; OPTIX_CHECK( optixUtilComputeStackSizes( &stack_sizes_1, max_trace_depth, 0, // maxCCDepth 0, // maxDCDEpth &direct_callable_stack_size_from_traversal, &direct_callable_stack_size_from_state, &continuation_stack_size ) ); OPTIX_CHECK( optixPipelineSetStackSize( state.pipeline_1, direct_callable_stack_size_from_traversal, direct_callable_stack_size_from_state, continuation_stack_size, 2 // maxTraversableDepth ) ); OPTIX_CHECK( optixUtilComputeStackSizes( &stack_sizes_2, max_trace_depth, 0, // maxCCDepth 0, // maxDCDEpth &direct_callable_stack_size_from_traversal, &direct_callable_stack_size_from_state, &continuation_stack_size ) ); OPTIX_CHECK( optixPipelineSetStackSize( state.pipeline_2, direct_callable_stack_size_from_traversal, direct_callable_stack_size_from_state, continuation_stack_size, 2 // maxTraversableDepth ) ); } void createSBT( RaycastingState& state ) { // raygen CUdeviceptr d_raygen_record = 0; const size_t raygen_record_size = sizeof( sutil::EmptyRecord ); CUDA_CHECK( cudaMalloc( reinterpret_cast<void**>( &d_raygen_record ), raygen_record_size ) ); sutil::EmptyRecord rg_record; OPTIX_CHECK( optixSbtRecordPackHeader( state.raygen_prog_group, &rg_record ) ); CUDA_CHECK( cudaMemcpy( reinterpret_cast<void*>( d_raygen_record ), &rg_record, raygen_record_size, cudaMemcpyHostToDevice ) ); // miss CUdeviceptr d_miss_record = 0; const size_t miss_record_size = sizeof( sutil::EmptyRecord ); CUDA_CHECK( cudaMalloc( reinterpret_cast<void**>( &d_miss_record ), miss_record_size ) ); sutil::EmptyRecord ms_record; OPTIX_CHECK( optixSbtRecordPackHeader( state.miss_prog_group, &ms_record ) ); CUDA_CHECK( cudaMemcpy( reinterpret_cast<void*>( d_miss_record ), &ms_record, miss_record_size, cudaMemcpyHostToDevice ) ); // hit group std::vector<HitGroupRecord> hitgroup_records; for( const auto& mesh : state.scene.meshes() ) { for( size_t i = 0; i < mesh->material_idx.size(); ++i ) { HitGroupRecord rec = {}; OPTIX_CHECK( optixSbtRecordPackHeader( state.hit_prog_group, &rec ) ); GeometryData::TriangleMesh triangle_mesh = {}; triangle_mesh.positions = mesh->positions[i]; triangle_mesh.normals = mesh->normals[i]; for( size_t j = 0; j < GeometryData::num_texcoords; ++j ) triangle_mesh.texcoords[j] = mesh->texcoords[j][i]; triangle_mesh.indices = mesh->indices[i]; rec.data.geometry_data.setTriangleMesh( triangle_mesh ); rec.data.material_data = state.scene.materials()[mesh->material_idx[i]]; hitgroup_records.push_back( rec ); } } CUdeviceptr d_hitgroup_record = 0; const size_t hitgroup_record_size = sizeof( HitGroupRecord ); CUDA_CHECK( cudaMalloc( reinterpret_cast<void**>( &d_hitgroup_record ), hitgroup_record_size * hitgroup_records.size() ) ); CUDA_CHECK( cudaMemcpy( reinterpret_cast<void*>( d_hitgroup_record ), hitgroup_records.data(), hitgroup_record_size * hitgroup_records.size(), cudaMemcpyHostToDevice ) ); state.sbt.raygenRecord = d_raygen_record; state.sbt.missRecordBase = d_miss_record; state.sbt.missRecordStrideInBytes = static_cast<uint32_t>( miss_record_size ); state.sbt.missRecordCount = RAY_TYPE_COUNT; state.sbt.hitgroupRecordBase = d_hitgroup_record; state.sbt.hitgroupRecordStrideInBytes = static_cast<uint32_t>( hitgroup_record_size ); state.sbt.hitgroupRecordCount = static_cast<int>( hitgroup_records.size() ); } void bufferRays( RaycastingState& state ) { // Create CUDA buffers for rays and hits sutil::Aabb aabb = state.scene.aabb(); aabb.invalidate(); for( const auto& instance : state.scene.instances() ) aabb.include( instance->world_aabb ); const float3 bbox_span = aabb.extent(); state.height = static_cast<int>( state.width * bbox_span.y / bbox_span.x ); Ray* rays_d = 0; Ray* translated_rays_d = 0; size_t rays_size_in_bytes = sizeof( Ray ) * state.width * state.height; CUDA_CHECK( cudaMalloc( &rays_d, rays_size_in_bytes ) ); CUDA_CHECK( cudaMalloc( &translated_rays_d, rays_size_in_bytes ) ); createRaysOrthoOnDevice( rays_d, state.width, state.height, aabb.m_min, aabb.m_max, 0.05f ); CUDA_CHECK( cudaGetLastError() ); CUDA_CHECK( cudaMemcpy( translated_rays_d, rays_d, rays_size_in_bytes, cudaMemcpyDeviceToDevice ) ); translateRaysOnDevice( translated_rays_d, state.width * state.height, bbox_span * make_float3( 0.2f, 0, 0 ) ); CUDA_CHECK( cudaGetLastError() ); Hit* hits_d = 0; Hit* translated_hits_d = 0; size_t hits_size_in_bytes = sizeof( Hit ) * state.width * state.height; CUDA_CHECK( cudaMalloc( &hits_d, hits_size_in_bytes ) ); CUDA_CHECK( cudaMalloc( &translated_hits_d, hits_size_in_bytes ) ); state.params = {state.scene.traversableHandle(), rays_d, hits_d}; state.params_translated = {state.scene.traversableHandle(), translated_rays_d, translated_hits_d}; } void launch( RaycastingState& state ) { CUstream stream_1 = 0; CUstream stream_2 = 0; CUDA_CHECK( cudaStreamCreate( &stream_1 ) ); CUDA_CHECK( cudaStreamCreate( &stream_2 ) ); Params* d_params = 0; Params* d_params_translated = 0; CUDA_CHECK( cudaMalloc( reinterpret_cast<void**>( &d_params ), sizeof( Params ) ) ); CUDA_CHECK( cudaMemcpyAsync( reinterpret_cast<void*>( d_params ), &state.params, sizeof( Params ), cudaMemcpyHostToDevice, stream_1 ) ); OPTIX_CHECK( optixLaunch( state.pipeline_1, stream_1, reinterpret_cast<CUdeviceptr>( d_params ), sizeof( Params ), &state.sbt, state.width, state.height, 1 ) ); // Translated CUDA_CHECK( cudaMalloc( reinterpret_cast<void**>( &d_params_translated ), sizeof( Params ) ) ); CUDA_CHECK( cudaMemcpyAsync( reinterpret_cast<void*>( d_params_translated ), &state.params_translated, sizeof( Params ), cudaMemcpyHostToDevice, stream_2 ) ); OPTIX_CHECK( optixLaunch( state.pipeline_2, stream_2, reinterpret_cast<CUdeviceptr>( d_params_translated ), sizeof( Params ), &state.sbt, state.width, state.height, 1 ) ); CUDA_SYNC_CHECK(); CUDA_CHECK( cudaFree( reinterpret_cast<void*>( d_params ) ) ); CUDA_CHECK( cudaFree( reinterpret_cast<void*>( d_params_translated ) ) ); } void shadeHits( RaycastingState& state, const std::string& outfile ) { sutil::CUDAOutputBufferType output_buffer_type = sutil::CUDAOutputBufferType::CUDA_DEVICE; sutil::CUDAOutputBuffer<float3> output_buffer( output_buffer_type, state.width, state.height ); sutil::ImageBuffer buffer; buffer.width = state.width; buffer.height = state.height; buffer.pixel_format = sutil::BufferImageFormat::FLOAT3; // Original shadeHitsOnDevice( output_buffer.map(), state.width * state.height, state.params.hits ); CUDA_CHECK( cudaGetLastError() ); output_buffer.unmap(); std::string ppmfile = outfile + ".ppm"; buffer.data = output_buffer.getHostPointer(); sutil::saveImage( ppmfile.c_str(), buffer, false ); std::cerr << "Wrote image to " << ppmfile << std::endl; // Translated shadeHitsOnDevice( output_buffer.map(), state.width * state.height, state.params_translated.hits ); CUDA_CHECK( cudaGetLastError() ); output_buffer.unmap(); ppmfile = outfile + "_translated.ppm"; buffer.data = output_buffer.getHostPointer(); sutil::saveImage( ppmfile.c_str(), buffer, false ); std::cerr << "Wrote translated image to " << ppmfile << std::endl; } void cleanup( RaycastingState& state ) { OPTIX_CHECK( optixPipelineDestroy( state.pipeline_1 ) ); OPTIX_CHECK( optixPipelineDestroy( state.pipeline_2 ) ); OPTIX_CHECK( optixProgramGroupDestroy( state.raygen_prog_group ) ); OPTIX_CHECK( optixProgramGroupDestroy( state.miss_prog_group ) ); OPTIX_CHECK( optixProgramGroupDestroy( state.hit_prog_group ) ); OPTIX_CHECK( optixModuleDestroy( state.ptx_module ) ); CUDA_CHECK( cudaFree( reinterpret_cast<void*>( state.params.rays ) ) ); CUDA_CHECK( cudaFree( reinterpret_cast<void*>( state.params.hits ) ) ); CUDA_CHECK( cudaFree( reinterpret_cast<void*>( state.params_translated.rays ) ) ); CUDA_CHECK( cudaFree( reinterpret_cast<void*>( state.params_translated.hits ) ) ); CUDA_CHECK( cudaFree( reinterpret_cast<void*>( state.sbt.raygenRecord ) ) ); CUDA_CHECK( cudaFree( reinterpret_cast<void*>( state.sbt.missRecordBase ) ) ); CUDA_CHECK( cudaFree( reinterpret_cast<void*>( state.sbt.hitgroupRecordBase ) ) ); CUDA_CHECK( cudaDestroyTextureObject( state.mask.texture ) ); CUDA_CHECK( cudaFreeArray( state.mask.array ) ); } int main( int argc, char** argv ) { std::string infile, outfile; RaycastingState state; state.width = 640; // .gltfはモデルとシーン情報を含む // データの場所: // C:\ProgramData\NVIDIA Corporation\OptiX SDK 9.0.0\SDK\data infile = sutil::sampleDataFilePath("Duck/DuckHole.gltf"); outfile = "output"; try { sutil::loadScene( infile.c_str(), state.scene ); state.scene.createContext(); state.scene.buildMeshAccels(); state.scene.buildInstanceAccel( RAY_TYPE_COUNT ); state.context = state.scene.context(); OPTIX_CHECK( optixInit() ); // Need to initialize function table createModule( state ); createProgramGroups( state ); createPipelines( state ); createSBT( state ); bufferRays( state ); launch( state ); shadeHits( state, outfile ); cleanup( state ); } catch( std::exception& e ) { std::cerr << "Caught exception: " << e.what() << std::endl; return 1; } return 0; }
Windows 11 + Visual C++ 2022 + CUDA 12.8 でOptiX 9.0を試す。
以下からドライバとCUDAを最新にしておく
NVIDIA Driver 今回使用:576.02
https://www.nvidia.com/ja-jp/drivers
CUDA Toolkit 今回使用:12.8
https://developer.nvidia.com/cuda-downloads
以下からOptiX 9.0をダウンロード・インストール
OptiX
https://developer.nvidia.com/designworks/optix/download
以下にインストールされている:
C:/ProgramData/NVIDIA Corporation/OptiX SDK 9.0.0/SDK
ここにCMakeLists.txtも入っているので、CMake-GUI で、Where is the source codeに指定。
Where to build the binaries: にビルド用のディレクトリを入れる。
インストールは行わないので、これでConfigure Generate Open Project する。
VC++でALL_BUILDすると、D:\tmp\optixbuild\bin\Release にサンプルプログラムが入っている。
C++17でFold expressionという機能が追加されたらしい。
なんでも
とすると、
と展開される。
#include <iostream>
template<typename... Args> auto add1(Args... val) { // Fold expression // val1 + val2 + val3 + ... return (val + ...); }
template<int... Args> int add2() { // Fold expression // args1 + args2 + args3 + ... return (Args + ...); }
int main() { auto ret1 = add1(1.0, 2.2, 3.f, 4, 5); std::cout << "Sum: " << ret1 << "\n"; int ret2 = add2<1, 2, 3, 4, 5>(); std::cout << "Sum: " << ret2 << "\n"; }
可変引数テンプレートもそうだが、if constexprやtype_traits、型推論に右辺参照もあるのでかなり簡単に書けるようになっている。
#include <iostream> #include <type_traits>
// 型TがArgs...に含まれているかどうかを判定するメタ関数 template <typename T, typename... Args> constexpr bool is_contains_type = (std::is_same_v<T, Args> || ...);
template<typename T, typename First, typename... Rest> decltype(auto) get_value(First&& first, Rest&&... rest) { if constexpr (std::is_same_v<T, std::decay_t<First> > == true) { return std::forward<First>(first); } // sizeof...(rest)で残りの引数の数をチェック else if constexpr (sizeof...(rest) > 0) { // 残りの引数restから T を探す return get_value<T>( std::forward<Rest>(rest)...); } else { static_assert(sizeof...(rest) > 0, "No matching type found in arguments."); } }
template <class... Args> void print(Args&&... args) { if constexpr (is_contains_type<int, std::decay_t<Args>...>) { auto&& arg_int = get_value<int>( std::forward<Args>(args)... ); std::cout << "int is " << arg_int << "\n"; } if constexpr (is_contains_type<double, std::decay_t<Args>...>) { auto&& arg_double = get_value<double>(std::forward<Args>(args)... ); std::cout << "double is "<< arg_double << "\n"; } if constexpr (is_contains_type<float, std::decay_t<Args>...>) { auto&& arg_float = get_value<float>(std::forward<Args>(args)... ); std::cout << "float is " << arg_float << "\n"; } }
int main() { print(1, 2.5, 5.f); // 引数が右辺値の場合 int K = 10; print(K); // 引数が左辺値の場合 std::cout << "K is " << K << "\n"; }
compile()関数を使うとPythonスクリプトのSyntax Errorを検出できる。スクリプトとその情報を与えてQwen3でバグフィクスをさせてみた。
from langchain_ollama import OllamaLLM from langchain_ollama import OllamaEmbeddings from langchain_community.vectorstores import FAISS from langchain_community.document_loaders import TextLoader from langchain.text_splitter import RecursiveCharacterTextSplitter from langchain.chains import RetrievalQA import re base_question="""あなたはPythonのコード修正アシスタントです。 あなたの仕事は、PythonコードのSyntax Errorを修正することです。 ・Syntax Errorの内容はPythonのcompile()関数により取得したものが与えられる。 ・Syntax Errorの修正以外は、オリジナルのコードを維持すること。 ・修正したスクリプトを出力せよ。 ・出力するスクリプトは ```python と ``` で囲うこと。 //////////////////////////////////////// Syntax Errorの内容: {SyntaxError} //////////////////////////////////////// 修正すべきスクリプト: {MyScript} """ ############################################################################################ ############################################################################################ ############################################################################################ ############################################################################################ ############################################################################################ ############################################################################################ def extract_python_code_blocks(text): """ 与えられた文字列から、 ```python ~ ``` で囲まれた範囲のコードをリストで返す。 """ pattern = r"```python\s*(.*?)```" match = re.search(pattern, text, re.DOTALL) return match.group(1) if match else None def check_syntax(code_str): try: compile(code_str, "<string>", "exec") print("構文エラーはありません。") return None except SyntaxError as e: print(f"構文エラーが発生しました: {e}") error_message = ( f"構文エラー: {e.msg}\n" f"ファイル名: {e.filename}\n" f"行番号: {e.lineno}\n" f"エラー発生位置: {e.offset}\n" f"エラー箇所: {e.text.strip() if e.text else '不明'}" ) return error_message ############################################################################################ ############################################################################################ ############################################################################################ ############################################################################################ ############################################################################################ ############################################################################################ def trycode(source): llm = OllamaLLM(model="qwen3:4b") code = source Loop = True while Loop: print("trycode===============================") print(code) print("=========================================") # 構文が間違っていたらもう一度生成し直し err = check_syntax(code) if err == None: break else: print("Syntax Error ============================") print(err) print("=========================================") # 問い合わせ作成 q1 = base_question.format(SyntaxError=err, MyScript=code) # 問い合わせ実行 response = llm.invoke(q1) code = re.sub(r"<think>.*?</think>", "", response, flags=re.DOTALL) # ```pythonを外す code = extract_python_code_blocks(code) return code ############################################################################################ ############################################################################################ ############################################################################################ ############################################################################################ ############################################################################################ ############################################################################################ # スクリプトファイル読み込み # myscript_path = "test.py" # パスを引数から取得 import sys if len(sys.argv) < 2: print("Usage: python auto_debugger.py <script_path>") sys.exit(1) myscript_path = sys.argv[1] if not myscript_path.endswith(".py"): print("Error: The script path must end with '.py'") sys.exit(1) myscript = "" with open(myscript_path, "r",encoding="utf-8") as f: myscript = f.read() myscript = trycode(myscript)# 自動Syntax Error修復 print(myscript) # スクリプトファイル書き込み with open(myscript_path + "_fix.py", "w",encoding="utf-8") as f: f.write(myscript)
import numpy as np import cv2 def generate_white_noise(width, height): # 0〜255のランダムな値を持つ画像を生成 noise = np.random.randint(0, 256, (height, width, 3), dtype=np.uint8) return noise def save_image(image, filename): cv2.imwrite(filename, image) if __name__ == "__main__": width = 640* height = 480 noise_image = generate_white_noise(width, height save_image(noise_image, "white_noise.png")
修正前
import numpy as np import cv2 def generate_white_noise(width, height): # 0〜255のランダムな値を持つ画像を生成 noise = np.random.randint(0, 256, (height, width, 3), dtype=np.uint8) return noise def save_image(image, filename): cv2.imwrite(filename, image) if __name__ == "__main__": width = 640 height = 480 noise_image = generate_white_noise(width, height) save_image(noise_image, "white_noise.png")
修正後
vtkDoubleArrayでSetArrayをすると、他ライブラリで管理している頂点データやカラーデータをVTKにコピーせずに使用できる(かもしれない)。
データ構造がXYZの配列のようにVTKの管理形式と同じである必要がある。
ただ、vtkCellArray(可視化する際に必要な頂点IDの配列)はいずれにせよ作成しなければいけないので、メモリ効率が劇的によくなるかというとそうでもない。
#include <iostream> //VTK_MODULE_INITに必要 #include <vtkAutoInit.h> #include <vtkSmartPointer.h> #include <vtkRenderer.h> #include <vtkRenderWindow.h> #include <vtkRenderWindowInteractor.h> //円筒とその表示に必要 #include <vtkPolyDataMapper.h> #include <vtkDoubleArray.h> #include <vtkPointData.h> #include <vtkActor.h> #include <vtkeigen/eigen/Dense> #pragma comment(lib,"opengl32.lib") #pragma comment(lib,"psapi.lib") #pragma comment(lib,"dbghelp.lib") #pragma comment(lib,"ws2_32.lib") //必須 VTK_MODULE_INIT(vtkRenderingOpenGL2); VTK_MODULE_INIT(vtkInteractionStyle); void create(std::vector<Eigen::Vector3d>& position, std::vector<Eigen::Vector3d>& color) { for (size_t i = 0; i < 100000; i++) { double x = (double)rand() / (double)RAND_MAX; double y = (double)rand() / (double)RAND_MAX; double z = (double)rand() / (double)RAND_MAX; position.push_back(Eigen::Vector3d(x, y, z)); color.push_back(Eigen::Vector3d(x, y, z)); // 色も同じ値にする } }
vtkSmartPointer<vtkActor> createActor(double* const position,double* const color,size_t count) { /////////////////////////////////////////////////// // 頂点データをVTKでラップ auto vtk_array_points = vtkSmartPointer<vtkDoubleArray>::New(); vtk_array_points->SetNumberOfComponents(3); vtk_array_points->SetArray(position, count * 3, 1); // save=1の時、double_ptrの管理を移譲しない /////////////////////////////////////////////////// // カラーデータをVTKでラップ auto vtk_array_color = vtkSmartPointer<vtkDoubleArray>::New(); vtk_array_color->SetNumberOfComponents(3); vtk_array_color->SetName("Colors"); vtk_array_color->SetArray(color, count * 3, 1); // save=1の時、double_ptrの管理を移譲しない /////////////////////////////////////////////////// // 頂点インデクスの配列 auto vertices = vtkSmartPointer<vtkCellArray>::New(); for (vtkIdType i = 0; i < count; ++i) { vertices->InsertNextCell(1, &i); } /////////////////////////////////////////////////// auto vtk_points = vtkSmartPointer<vtkPoints>::New(); vtk_points->SetData(vtk_array_points); // --- vtkPolyData作成 --- auto polydata = vtkSmartPointer<vtkPolyData>::New(); polydata->SetPoints(vtk_points); polydata->SetVerts(vertices); polydata->GetPointData()->SetScalars(vtk_array_color); // --- actor作成 --- auto mapper = vtkSmartPointer<vtkPolyDataMapper>::New(); mapper->SetInputData(polydata); vtkSmartPointer<vtkActor> actor = vtkSmartPointer<vtkActor>::New(); actor->SetMapper(mapper); return actor; }
int main(int /*argc*/, char** /*argv*/) { std::vector<Eigen::Vector3d> position; std::vector<Eigen::Vector3d> color; create(position, color); auto actor = createActor( reinterpret_cast<double*>(position.data()), reinterpret_cast<double*>(color.data()), position.size() ); auto renderer = vtkSmartPointer<vtkRenderer>::New(); renderer->AddActor(actor); renderer->ResetCamera(); ////////////////////////////////////// auto interactor = vtkSmartPointer<vtkRenderWindowInteractor>::New(); ////////////////////////////////////// auto renderWindow = vtkSmartPointer<vtkRenderWindow>::New(); renderWindow->AddRenderer(renderer); renderWindow->SetInteractor(interactor); renderWindow->Render(); interactor->Start(); //イベントループへ入る return 0; }
前にやったときはコールバック関数を直接呼び出していたが今度はクラスのインスタンスを作成してExecuteメンバを呼び出す。
VTKのタイマーは一つのコールバック用のクラスインスタンスのExecuteを各タイミングで呼び出す。
タイマーの停止はDestroyTimerで行う。
#include <iostream> //VTK_MODULE_INITに必要 #include <vtkAutoInit.h> #include <vtkSmartPointer.h> #include <vtkRenderer.h> #include <vtkRenderWindow.h> #include <vtkRenderWindowInteractor.h> //円筒とその表示に必要 #include <vtkCylinderSource.h> #include <vtkPolyDataMapper.h> #pragma comment(lib,"opengl32.lib") #pragma comment(lib,"psapi.lib") #pragma comment(lib,"dbghelp.lib") #pragma comment(lib,"ws2_32.lib") //必須 VTK_MODULE_INIT(vtkRenderingOpenGL2); VTK_MODULE_INIT(vtkInteractionStyle);
class MyTimerCallback : public vtkCommand { public: static MyTimerCallback* New() { return new MyTimerCallback; } int counter100 = 0; int counter500 = 0; void Execute(vtkObject* caller, unsigned long eventId, void* callData) override { if (eventId == vtkCommand::TimerEvent) { int timerId = *(int*)(callData); if (timerId == this->TimerId100) { // TimerId100の処理 counter100++; std::cout << "Timer100 triggered!" << counter100 <<std::endl; // タイマーを停止 if(counter100 >= 10) { std::cout << "Stopping Timer100" << std::endl; Interactor->DestroyTimer(timerId); } } else if (timerId == this->TimerId500) { // TimerId500の処理 counter500++; std::cout << "Timer500 triggered!" << counter500 << std::endl; } } } vtkSmartPointer<vtkRenderWindowInteractor> Interactor; int TimerId100 = -1; // タイマーの識別子 int TimerId500 = -1; };
int main(int /*argc*/, char** /*argv*/) { ////////////////////////////////////// // Create a vtkCylinderSource vtkSmartPointer<vtkCylinderSource> cylinderSource = vtkSmartPointer<vtkCylinderSource>::New(); cylinderSource->SetCenter(0.0, 0.0, 0.0); cylinderSource->SetRadius(5.0); cylinderSource->SetHeight(7.0); cylinderSource->SetResolution(100); ////////////////////////////////////// // Create a mapper and actor vtkSmartPointer<vtkPolyDataMapper> mapper = vtkSmartPointer<vtkPolyDataMapper>::New(); mapper->SetInputConnection(cylinderSource->GetOutputPort()); vtkSmartPointer<vtkActor> actor = vtkSmartPointer<vtkActor>::New(); actor->SetMapper(mapper); ////////////////////////////////////// auto renderer = vtkSmartPointer<vtkRenderer>::New(); renderer->AddActor(actor); renderer->ResetCamera(); ////////////////////////////////////// auto interactor = vtkSmartPointer<vtkRenderWindowInteractor>::New(); ////////////////////////////////////// auto renderWindow = vtkSmartPointer<vtkRenderWindow>::New(); renderWindow->AddRenderer(renderer); renderWindow->SetInteractor(interactor); renderWindow->Render(); ////////////////////////////////////// // タイマーコールバックの設定 vtkSmartPointer<MyTimerCallback> timerCallback = vtkSmartPointer<MyTimerCallback>::New(); // タイマーを作成。戻り値は識別子 timerCallback->TimerId100 = interactor->CreateRepeatingTimer(100); timerCallback->TimerId500 = interactor->CreateRepeatingTimer(500); // DestroyTimerのためにinteractorを保持 timerCallback->Interactor = interactor; interactor->AddObserver(vtkCommand::TimerEvent, timerCallback); interactor->SetRenderWindow(renderer->GetRenderWindow()); interactor->Initialize(); ////////////////////////////////////// interactor->Start(); //イベントループへ入る return 0; }
#include <iostream> //VTK_MODULE_INITに必要 #include <vtkAutoInit.h> #include <vtkSmartPointer.h> #include <vtkRenderer.h> #include <vtkRenderWindow.h> #include <vtkRenderWindowInteractor.h> //円筒とその表示に必要 #include <vtkCylinderSource.h> #include <vtkPolyDataMapper.h> #pragma comment(lib,"opengl32.lib") #pragma comment(lib,"psapi.lib") #pragma comment(lib,"dbghelp.lib") #pragma comment(lib,"ws2_32.lib") //必須 VTK_MODULE_INIT(vtkRenderingOpenGL2); VTK_MODULE_INIT(vtkInteractionStyle); #include <vtkInteractorStyleTrackballCamera.h> #include <vtkCamera.h> #include <vtkPropPicker.h> #include <vtkAppendPolyData.h> #include <vtkLineSource.h> #include <vtkProperty.h> // 注視点を表示するためのアクタを作成 vtkSmartPointer<vtkActor> Create3DPlusActor( double length = 1.0, double width = 2.0 );
// マウスイベントをカスタマイズするためのクラス class MouseInteractorStyle2 : public vtkInteractorStyleTrackballCamera { public: // 注視点を表示するためのアクタ vtkSmartPointer<vtkActor> focal_mark; static MouseInteractorStyle2* New(); vtkTypeMacro(MouseInteractorStyle2, vtkInteractorStyleTrackballCamera); virtual void OnLeftButtonDown() { int ctrl = this->Interactor->GetControlKey(); if (ctrl) { int* clickPos = this->GetInteractor()->GetEventPosition(); // ピック下オブジェクトを取得 auto picker = vtkSmartPointer<vtkPropPicker>::New(); picker->Pick(clickPos[0], clickPos[1], 0, this->GetDefaultRenderer()); // オブジェクトがクリックされていたら位置とアドレスを出力 if (picker->GetActor() != nullptr) { double* pos = picker->GetPickPosition(); auto camera = this->GetDefaultRenderer()->GetActiveCamera(); camera->SetFocalPoint(pos); // カメラの焦点をクリック位置に設定 focal_mark->SetPosition(pos); // クリック位置に移動 } } // Forward events vtkInteractorStyleTrackballCamera::OnLeftButtonDown(); } private: };
vtkStandardNewMacro(MouseInteractorStyle2); int main(int /*argc*/, char** /*argv*/) { ////////////////////////////////////// // Create a vtkCylinderSource vtkSmartPointer<vtkCylinderSource> cylinderSource = vtkSmartPointer<vtkCylinderSource>::New(); cylinderSource->SetCenter(0.0, 0.0, 0.0); cylinderSource->SetRadius(5.0); cylinderSource->SetHeight(7.0); cylinderSource->SetResolution(100); ////////////////////////////////////// // Create a mapper and actor vtkSmartPointer<vtkPolyDataMapper> mapper = vtkSmartPointer<vtkPolyDataMapper>::New(); mapper->SetInputConnection(cylinderSource->GetOutputPort()); vtkSmartPointer<vtkActor> actor = vtkSmartPointer<vtkActor>::New(); actor->SetMapper(mapper); ////////////////////////////////////// auto renderer = vtkSmartPointer<vtkRenderer>::New(); renderer->AddActor(actor); renderer->ResetCamera(); ////////////////////////////////////// auto interactor = vtkSmartPointer<vtkRenderWindowInteractor>::New(); ////////////////////////////////////// auto renderWindow = vtkSmartPointer<vtkRenderWindow>::New(); renderWindow->AddRenderer(renderer); renderWindow->SetInteractor(interactor); renderWindow->Render();
////////////////////////////////////// // マウスイベントを設定. auto style = vtkSmartPointer<MouseInteractorStyle2>::New(); style->SetDefaultRenderer(renderer); interactor->SetInteractorStyle(style); ////////////////////////////////////// vtkCamera* cam = renderer->GetActiveCamera(); double focal[3]; cam->GetFocalPoint(focal); auto axes = Create3DPlusActor(1, 1); axes->SetPosition(focal); renderer->AddActor(axes); // 軸をレンダラーに追加 style->focal_mark = axes;
////////////////////////////////////// interactor->Start(); //イベントループへ入る return 0; } // 線分の長さ、太さ、色を指定できる vtkSmartPointer<vtkActor> Create3DPlusActor( double length, double width ) { double center[3]= { 0.0, 0.0, 0.0 }; double color[3] = { 1.0, 1.0, 1.0 }; // 各軸の線分を作成 auto appendFilter = vtkSmartPointer<vtkAppendPolyData>::New(); // x軸の線 auto xLine = vtkSmartPointer<vtkLineSource>::New(); xLine->SetPoint1(center[0] - length / 2.0, center[1], center[2]); xLine->SetPoint2(center[0] + length / 2.0, center[1], center[2]); xLine->Update(); appendFilter->AddInputData(xLine->GetOutput()); // y軸の線 auto yLine = vtkSmartPointer<vtkLineSource>::New(); yLine->SetPoint1(center[0], center[1] - length / 2.0, center[2]); yLine->SetPoint2(center[0], center[1] + length / 2.0, center[2]); yLine->Update(); appendFilter->AddInputData(yLine->GetOutput()); // z軸の線 auto zLine = vtkSmartPointer<vtkLineSource>::New(); zLine->SetPoint1(center[0], center[1], center[2] - length / 2.0); zLine->SetPoint2(center[0], center[1], center[2] + length / 2.0); zLine->Update(); appendFilter->AddInputData(zLine->GetOutput()); appendFilter->Update(); // マッパーとアクター auto mapper = vtkSmartPointer<vtkPolyDataMapper>::New(); mapper->SetInputConnection(appendFilter->GetOutputPort()); auto actor = vtkSmartPointer<vtkActor>::New(); actor->SetMapper(mapper); actor->GetProperty()->SetLineWidth(width); actor->GetProperty()->SetColor(color); return actor; }
昔書いた内容かどうか思い出せない。コードを書いた記憶はあるが記事が見つからなかった。
やったことはあるかもしれないが、きっと当時よりはよいコードになっている。