GPU プラグインのリモート Tensor API¶
ov::RemoteContext
インターフェイスと ov::RemoteTensor
インターフェイスの GPU プラグイン実装は、ビデオメモリーの共有と、OpenCL*、Microsoft DirectX*、VAAPI など既存のネイティブ API との相互運用性を必要とする GPU パイプラインの開発者をサポートします。
ov::RemoteContext
および ov::RemoteTensor
インターフェイスの実装は、メモリー共有の必要性と、OpenCL*、Microsoft DirectX*、VAAPI など既存のネイティブ API との相互運用性を対象としています。これにより、OpenVINO™ 推論を既存の GPU パイプラインに接続する際のメモリーコピーのオーバーヘッドを回避できます。また、OpenCL* カーネルがパイプラインに参加して、OpenVINO™ 推論のネイティブバッファーの消費者または生産者になることも可能です。
リモート Tensor API でサポートされる相互運用性シナリオは 2 つあります。
GPU プラグイン・コンテキストとメモリー・オブジェクトは、低レベルのデバイス、ディスプレイ、またはメモリーハンドルから構築でき、OpenVINO™
ov::CompiledModel
またはov::Tensor
オブジェクトの作成に使用できます。OpenCL コンテキストまたはバッファーハンドルは、既存の GPU プラグイン・オブジェクトから取得でき、アプリケーションの OpenCL* 処理で使用できます。
API のクラスと関数の宣言は、次のファイルで定義されます。
Windows* –
openvino/runtime/intel_gpu/ocl/ocl.hpp
およびopenvino/runtime/intel_gpu/ocl/dx.hpp
Linux* –
openvino/runtime/intel_gpu/ocl/ocl.hpp
およびopenvino/runtime/intel_gpu/ocl/va.hpp
アプリケーションとリモート Tensor API の対話を可能にする一般的な方法は、ネイティブハンドルを直接消費または生産するユーザーのユーティリティー・クラスと関数を使用することです。
アプリケーションと GPU プラグイン間のコンテキスト共有¶
ov::RemoteContext
インターフェイスを実装する GPU プラグインクラスは、コンテキストの共有を担当します。コンテキスト・オブジェクトを取得するのは、パイプライン・オブジェクトを共有する最初のステップです。GPU プラグインのコンテキスト・オブジェクトは OpenCL* コンテキストを直接ラップし、ov::CompiledModel
オブジェクトと ov::RemoteTensor
オブジェクトを共有するスコープを設定します。ov::RemoteContext
オブジェクトは、ネイティブ API から既存のハンドル上に作成することも、GPU プラグインから取得することもできます。
コンテキストを取得したら、それを使用して新しい ov::CompiledModel
をコンパイルしたり、ov::RemoteTensor
オブジェクトを作成したりできます。ネットワーク・コンパイルの場合は、追加パラメーターとしてコンテキストを受け入れる専用の ov::Core::compile_model()
フレーバーを使用します。
ネイティブハンドルから RemoteContext を作成¶
ユーザー・コンテキストの ov::RemoteContext
オブジェクトを作成するには、ov::RemoteContext
派生クラスのコンストラクターを使用して、プラグインにコンテキストを明示的に提供します。
cl_context ctx = get_cl_context();
ov::intel_gpu::ocl::ClContext gpu_context(core, ctx);
cl_command_queue queue = get_cl_queue();
ov::intel_gpu::ocl::ClContext gpu_context(core, queue);
ID3D11Device* device = get_d3d_device();
ov::intel_gpu::ocl::D3DContext gpu_context(core, device);
cl_context cl_context = get_cl_context();
ov_core_create_context(core,
"GPU",
4,
&gpu_context,
ov_property_key_intel_gpu_context_type,
"OCL",
ov_property_key_intel_gpu_ocl_context,
cl_context);
cl_command_queue cl_queue = get_cl_queue();
cl_context cl_context = get_cl_context();
ov_core_create_context(core,
"GPU",
6,
&gpu_context,
ov_property_key_intel_gpu_context_type,
"OCL",
ov_property_key_intel_gpu_ocl_context,
cl_context,
ov_property_key_intel_gpu_ocl_queue,
cl_queue);
ID3D11Device* device = get_d3d_device();
ov_core_create_context(core,
"GPU",
4,
&gpu_context,
ov_property_key_intel_gpu_context_type,
"VA_SHARED",
ov_property_key_intel_gpu_va_device,
device);
cl_context ctx = get_cl_context();
ov::intel_gpu::ocl::ClContext gpu_context(core, ctx);
cl_command_queue queue = get_cl_queue();
ov::intel_gpu::ocl::ClContext gpu_context(core, queue);
VADisplay display = get_va_display();
ov::intel_gpu::ocl::VAContext gpu_context(core, display);
cl_context cl_context = get_cl_context();
ov_core_create_context(core,
"GPU",
4,
&gpu_context,
ov_property_key_intel_gpu_context_type,
"OCL",
ov_property_key_intel_gpu_ocl_context,
cl_context);
cl_command_queue cl_queue = get_cl_queue();
cl_context cl_context = get_cl_context();
ov_core_create_context(core,
"GPU",
6,
&gpu_context,
ov_property_key_intel_gpu_context_type,
"OCL",
ov_property_key_intel_gpu_ocl_context,
cl_context,
ov_property_key_intel_gpu_ocl_queue,
cl_queue);
VADisplay display = get_va_display();
ov_core_create_context(core,
"GPU",
4,
&gpu_context,
ov_property_key_intel_gpu_context_type,
"VA_SHARED",
ov_property_key_intel_gpu_va_device,
display);
プラグインから RemoteContext を取得¶
ユーザー・コンテキストを省略すると、プラグインはデフォルトの内部コンテキストを使用します。プラグインは、プラグインオプションが同じである限り、同じ内部コンテキスト・オブジェクトを使用します。したがって、この間に作成されたすべての ov::CompiledModel
オブジェクトは同じコンテキストを共有します。プラグインオプションが変更されると、内部コンテキストは新しいコンテキストに置き換えられます。
プラグインの現在のデフォルト・コンテキストをリクエストするには、次のいずれかの方法を使用します。
auto gpu_context = core.get_default_context("GPU").as<ov::intel_gpu::ocl::ClContext>();
// Extract ocl context handle from RemoteContext
cl_context context_handle = gpu_context.get();
auto gpu_context = compiled_model.get_context().as<ov::intel_gpu::ocl::ClContext>();
// Extract ocl context handle from RemoteContext
cl_context context_handle = gpu_context.get();
ov_core_get_default_context(core, "GPU", &gpu_context);
// Extract ocl context handle from RemoteContext
size_t size = 0;
char* params = nullptr;
// params is format like: "CONTEXT_TYPE OCL OCL_CONTEXT 0x5583b2ec7b40 OCL_QUEUE 0x5583b2e98ff0"
// You need parse it.
ov_remote_context_get_params(gpu_context, &size, ¶ms);
ov_compiled_model_get_context(compiled_model, &gpu_context);
// Extract ocl context handle from RemoteContext
size_t size = 0;
char* params = nullptr;
// params is format like: "CONTEXT_TYPE OCL OCL_CONTEXT 0x5583b2ec7b40 OCL_QUEUE 0x5583b2e98ff0"
// You need parse it.
ov_remote_context_get_params(gpu_context, &size, ¶ms);
アプリケーションと GPU プラグイン間のメモリー共有¶
ov::RemoteTensor
インターフェイスを実装するクラスは、ネイティブ API メモリーハンドル (いつでも取得可能) のラッパーです。
ネイティブ・メモリー・ハンドルから共有テンソルを作成するには、ov::RemoteContext
サブクラスの専用の create_tensor
または create_tensor_nv12
メソッドを使用します。ov::intel_gpu::ocl::ClContext
には create_tensor
メソッドの複数のオーバーロードがあり、事前に割り当てられたネイティブハンドルを ov::RemoteTensor
オブジェクトでラップしたり、プラグインに特定のデバイスメモリーを割り当てることを要求したりできます。C++ API と同じことを行う C API も提供されています。詳細については、以下を参照してください。
void* shared_buffer = allocate_usm_buffer(input_size);
auto remote_tensor = gpu_context.create_tensor(in_element_type, in_shape, shared_buffer);
cl_mem shared_buffer = allocate_cl_mem(input_size);
auto remote_tensor = gpu_context.create_tensor(in_element_type, in_shape, shared_buffer);
cl::Buffer shared_buffer = allocate_buffer(input_size);
auto remote_tensor = gpu_context.create_tensor(in_element_type, in_shape, shared_buffer);
cl::Image2D shared_buffer = allocate_image(input_size);
auto remote_tensor = gpu_context.create_tensor(in_element_type, in_shape, shared_buffer);
cl::Image2D y_plane_surface = allocate_image(y_plane_size);
cl::Image2D uv_plane_surface = allocate_image(uv_plane_size);
auto remote_tensor = gpu_context.create_tensor_nv12(y_plane_surface, uv_plane_surface);
auto y_tensor = remote_tensor.first;
auto uv_tensor = remote_tensor.second;
ov::intel_gpu::ocl::USMTensor remote_tensor = gpu_context.create_usm_host_tensor(in_element_type, in_shape);
// Extract raw usm pointer from remote tensor
void* usm_ptr = remote_tensor.get();
auto remote_tensor = gpu_context.create_usm_device_tensor(in_element_type, in_shape);
// Extract raw usm pointer from remote tensor
void* usm_ptr = remote_tensor.get();
ov::RemoteTensor remote_tensor = gpu_context.create_tensor(in_element_type, in_shape);
// Cast from base to derived class and extract ocl memory handle
auto buffer_tensor = remote_tensor.as<ov::intel_gpu::ocl::ClBufferTensor>();
cl_mem handle = buffer_tensor.get();
void* shared_buffer = allocate_usm_buffer(input_size);
ov_remote_context_create_tensor(gpu_context,
input_type,
input_shape,
4,
&remote_tensor,
ov_property_key_intel_gpu_shared_mem_type,
"USM_USER_BUFFER",
ov_property_key_intel_gpu_mem_handle,
shared_buffer);
cl_mem shared_buffer = allocate_cl_mem(input_size);
ov_remote_context_create_tensor(gpu_context,
input_type,
input_shape,
4,
&remote_tensor,
ov_property_key_intel_gpu_shared_mem_type,
"OCL_BUFFER",
ov_property_key_intel_gpu_mem_handle,
shared_buffer);
cl::Buffer shared_buffer = allocate_buffer(input_size);
ov_remote_context_create_tensor(gpu_context,
input_type,
input_shape,
4,
&remote_tensor,
ov_property_key_intel_gpu_shared_mem_type,
"OCL_BUFFER",
ov_property_key_intel_gpu_mem_handle,
shared_buffer.get());
cl::Image2D shared_buffer = allocate_image(input_size);
ov_remote_context_create_tensor(gpu_context,
input_type,
input_shape,
4,
&remote_tensor,
ov_property_key_intel_gpu_shared_mem_type,
"OCL_IMAGE2D",
ov_property_key_intel_gpu_mem_handle,
shared_buffer.get());
cl::Image2D y_plane_surface = allocate_image(y_plane_size);
cl::Image2D uv_plane_surface = allocate_image(uv_plane_size);
ov_remote_context_create_tensor(gpu_context,
input_type,
shape_y,
4,
&remote_tensor_y,
ov_property_key_intel_gpu_shared_mem_type,
"OCL_IMAGE2D",
ov_property_key_intel_gpu_mem_handle,
y_plane_surface.get());
ov_remote_context_create_tensor(gpu_context,
input_type,
shape_uv,
4,
&remote_tensor_uv,
ov_property_key_intel_gpu_shared_mem_type,
"OCL_IMAGE2D",
ov_property_key_intel_gpu_mem_handle,
uv_plane_surface.get());
ov_tensor_free(remote_tensor_y);
ov_tensor_free(remote_tensor_uv);
ov_shape_free(&shape_y);
ov_shape_free(&shape_uv);
ov_remote_context_create_tensor(gpu_context,
input_type,
input_shape,
2,
&remote_tensor,
ov_property_key_intel_gpu_shared_mem_type,
"USM_HOST_BUFFER");
// Extract raw usm pointer from remote tensor
void* usm_ptr = NULL;
ov_tensor_data(remote_tensor, &usm_ptr);
ov_remote_context_create_tensor(gpu_context,
input_type,
input_shape,
2,
&remote_tensor,
ov_property_key_intel_gpu_shared_mem_type,
"USM_USER_BUFFER");
// Extract raw usm pointer from remote tensor
void* usm_ptr = NULL;
ov_tensor_data(remote_tensor, &usm_ptr);
ov::intel_gpu::ocl::D3DContext
クラスと ov::intel_gpu::ocl::VAContext
クラスは、ov::intel_gpu::ocl::ClContext
から派生します。したがって、上記の機能を提供し、それを拡張して、それぞれ ID3D11Buffer
、ID3D11Texture2D
ポインター、または VASurfaceID
ハンドルから ov::RemoteTensor
オブジェクトを作成できるようにします。
NV12 ビデオサーフェスの直接入力¶
ハードウェア・ビデオ・デコーダー出力の直接利用をサポートするため、GPU プラグインは以下を受け入れます。
2 プレーン NV12 ビデオサーフェス入力 -
create_tensor_nv12()
関数を呼び出すと、Y プレーンと UV プレーンを表すov::RemoteTensor
オブジェクトのペアが作成されます。シングルプレーン NV12 ビデオサーフェス入力 -
create_tensor()
関数を呼び出すと、Y プレーンと UV プレーンを一度に表す 1 つのov::RemoteTensor
オブジェクトが作成されます (Y 要素は UV 要素の前にあります)。NV12 からグレー・ビデオ・サーフェイス入力への変換 -
create_tensor()
関数を呼び出すと、Y 平面のみを表す 1 つのov::RemoteTensor
オブジェクトが作成されます。
プラグインが正しい実行グラフを生成できるように、モデルのコンパイル前に静的前処理を追加する必要があります。
using namespace ov::preprocess;
auto p = PrePostProcessor(model);
p.input().tensor().set_element_type(ov::element::u8)
.set_color_format(ov::preprocess::ColorFormat::NV12_TWO_PLANES, {"y", "uv"})
.set_memory_type(ov::intel_gpu::memory_type::surface);
p.input().preprocess().convert_color(ov::preprocess::ColorFormat::BGR);
p.input().model().set_layout("NCHW");
auto model_with_preproc = p.build();
ov_preprocess_prepostprocessor_create(model, &preprocess);
ov_preprocess_prepostprocessor_get_input_info(preprocess, &preprocess_input_info);
ov_preprocess_input_info_get_tensor_info(preprocess_input_info, &preprocess_input_tensor_info);
ov_preprocess_input_tensor_info_set_element_type(preprocess_input_tensor_info, ov_element_type_e::U8);
ov_preprocess_input_tensor_info_set_color_format_with_subname(preprocess_input_tensor_info,
ov_color_format_e::NV12_TWO_PLANES,
2,
"y",
"uv");
ov_preprocess_input_tensor_info_set_memory_type(preprocess_input_tensor_info, "GPU_SURFACE");
ov_preprocess_input_tensor_info_set_spatial_static_shape(preprocess_input_tensor_info, height, width);
ov_preprocess_input_info_get_preprocess_steps(preprocess_input_info, &preprocess_input_steps);
ov_preprocess_preprocess_steps_convert_color(preprocess_input_steps, ov_color_format_e::BGR);
ov_preprocess_preprocess_steps_resize(preprocess_input_steps, RESIZE_LINEAR);
ov_preprocess_input_info_get_model_info(preprocess_input_info, &preprocess_input_model_info);
ov_layout_create("NCHW", &layout);
ov_preprocess_input_model_info_set_layout(preprocess_input_model_info, layout);
ov_preprocess_prepostprocessor_build(preprocess, &model_with_preproc);
using namespace ov::preprocess;
auto p = PrePostProcessor(model);
p.input().tensor().set_element_type(ov::element::u8)
.set_color_format(ColorFormat::NV12_SINGLE_PLANE)
.set_memory_type(ov::intel_gpu::memory_type::surface);
p.input().preprocess().convert_color(ov::preprocess::ColorFormat::BGR);
p.input().model().set_layout("NCHW");
auto model_with_preproc = p.build();
using namespace ov::preprocess;
auto p = PrePostProcessor(model);
p.input().tensor().set_element_type(ov::element::u8)
.set_layout("NHWC")
.set_memory_type(ov::intel_gpu::memory_type::surface);
p.input().model().set_layout("NCHW");
auto model_with_preproc = p.build();
ov::intel_gpu::ocl::ClImage2DTensor
とその派生クラスはバッチ化されたサーフェスをサポートしていないため、バッチ化とサーフェス共有が同時に必要な場合は、ov::InferRequest::set_tensors
メソッドで各プレーンの共有サーフェスのベクトルを指定して入力を設定する必要があります。
auto input0 = model_with_preproc->get_parameters().at(0);
auto input1 = model_with_preproc->get_parameters().at(1);
ov::intel_gpu::ocl::ClImage2DTensor y_tensor = get_y_tensor();
ov::intel_gpu::ocl::ClImage2DTensor uv_tensor = get_uv_tensor();
infer_request.set_tensor(input0->get_friendly_name(), y_tensor);
infer_request.set_tensor(input1->get_friendly_name(), uv_tensor);
infer_request.infer();
ov_model_const_input_by_index(model, 0, &input_port0);
ov_model_const_input_by_index(model, 1, &input_port1);
ov_port_get_any_name(input_port0, &input_name0);
ov_port_get_any_name(input_port1, &input_name1);
ov_shape_t shape_y, shape_uv;
ov_tensor_t* remote_tensor_y = NULL;
ov_tensor_t* remote_tensor_uv = NULL;
ov_const_port_get_shape(input_port0, &shape_y);
ov_const_port_get_shape(input_port1, &shape_uv);
cl::Image2D image_y = get_y_image();
cl::Image2D image_uv = get_uv_image();
ov_remote_context_create_tensor(gpu_context,
ov_element_type_e::U8,
shape_y,
4,
&remote_tensor_y,
ov_property_key_intel_gpu_shared_mem_type,
"OCL_IMAGE2D",
ov_property_key_intel_gpu_mem_handle,
image_y.get());
ov_remote_context_create_tensor(gpu_context,
ov_element_type_e::U8,
shape_uv,
4,
&remote_tensor_y,
ov_property_key_intel_gpu_shared_mem_type,
"OCL_IMAGE2D",
ov_property_key_intel_gpu_mem_handle,
image_uv.get());
ov_infer_request_set_tensor(infer_request, input_name0, remote_tensor_y);
ov_infer_request_set_tensor(infer_request, input_name1, remote_tensor_uv);
ov_infer_request_infer(infer_request);
auto input_yuv = model_with_preproc->input(0);
ov::intel_gpu::ocl::ClImage2DTensor yuv_tensor = get_yuv_tensor();
infer_request.set_tensor(input_yuv.get_any_name(), yuv_tensor);
infer_request.infer();
cl::Image2D img_y_plane;
auto input_y = model_with_preproc->input(0);
auto remote_y_tensor = remote_context.create_tensor(input_y.get_element_type(), input.get_shape(), img_y_plane);
infer_request.set_tensor(input_y.get_any_name(), remote_y_tensor);
infer_request.infer();
auto input0 = model_with_preproc->get_parameters().at(0);
auto input1 = model_with_preproc->get_parameters().at(1);
std::vector<ov::Tensor> y_tensors = {y_tensor_0, y_tensor_1};
std::vector<ov::Tensor> uv_tensors = {uv_tensor_0, uv_tensor_1};
infer_request.set_tensors(input0->get_friendly_name(), y_tensors);
infer_request.set_tensors(input1->get_friendly_name(), uv_tensors);
infer_request.infer();
auto input_yuv = model_with_preproc->input(0);
std::vector<ov::Tensor> yuv_tensors = {yuv_tensor_0, yuv_tensor_1};
infer_request.set_tensors(input_yuv.get_any_name(), yuv_tensors);
infer_request.infer();
cl::Image2D img_y_plane_0, img_y_plane_l;
auto input_y = model_with_preproc->input(0);
auto remote_y_tensor_0 = remote_context.create_tensor(input_y.get_element_type(), input.get_shape(), img_y_plane_0);
auto remote_y_tensor_1 = remote_context.create_tensor(input_y.get_element_type(), input.get_shape(), img_y_plane_l);
std::vector<ov::Tensor> y_tensors = {remote_y_tensor_0, remote_y_tensor_1};
infer_request.set_tensors(input_y.get_any_name(), y_tensors);
infer_request.infer();
I420 カラー形式も同様の方法で処理できます。
コンテキスト & キューの共有¶
GPU プラグインは、cl_command_queue
ハンドルから共有コンテキストを作成することをサポートします。その場合、opencl
コンテキスト・ハンドルは OpenCL* API を介して指定されたキューから抽出され、キュー自体は推論プリミティブをさらに実行するためプラグイン内で使用されます。キューを共有することで、ov::InferRequest::start_async()
メソッドの動作が変更され、呼び出しスレッドに制御を戻す前に、指定されたキューへの推論プリミティブの送信完了が保証されます。
この共有メカニズムにより、アプリ側でパイプライン同期を実行し、推論の完了を待機する際のホストスレッドのブロックを回避できます。疑似コードは次のようになります。
キューとコンテキスト共有の例
// ...
// initialize the core and read the model
ov::Core core;
auto model = core.read_model("model.xml");
// get opencl queue object
cl::CommandQueue queue = get_ocl_queue();
cl::Context cl_context = get_ocl_context();
// share the queue with GPU plugin and compile model
auto remote_context = ov::intel_gpu::ocl::ClContext(core, queue.get());
auto exec_net_shared = core.compile_model(model, remote_context);
auto input = model->get_parameters().at(0);
auto input_size = ov::shape_size(input->get_shape());
auto output = model->get_results().at(0);
auto output_size = ov::shape_size(output->get_shape());
cl_int err;
// create the OpenCL buffers within the context
cl::Buffer shared_in_buffer(cl_context, CL_MEM_READ_WRITE, input_size, NULL, &err);
cl::Buffer shared_out_buffer(cl_context, CL_MEM_READ_WRITE, output_size, NULL, &err);
// wrap in and out buffers into RemoteTensor and set them to infer request
auto shared_in_blob = remote_context.create_tensor(input->get_element_type(), input->get_shape(), shared_in_buffer);
auto shared_out_blob = remote_context.create_tensor(output->get_element_type(), output->get_shape(), shared_out_buffer);
auto infer_request = exec_net_shared.create_infer_request();
infer_request.set_tensor(input, shared_in_blob);
infer_request.set_tensor(output, shared_out_blob);
// ...
// execute user kernel
cl::Program program;
cl::Kernel kernel_preproc(program, "user_kernel_preproc");
kernel_preproc.setArg(0, shared_in_buffer);
queue.enqueueNDRangeKernel(kernel_preproc,
cl::NDRange(0),
cl::NDRange(input_size),
cl::NDRange(1),
nullptr,
nullptr);
// Blocking clFinish() call is not required, but this barrier is added to the queue to guarantee that user kernel is finished
// before any inference primitive is started
queue.enqueueBarrierWithWaitList(nullptr, nullptr);
// ...
// pass results to the inference
// since the remote context is created with queue sharing, start_async() guarantees that scheduling is finished
infer_request.start_async();
// execute some postprocessing kernel.
// infer_request.wait() is not called, synchonization between inference and post-processing is done via
// enqueueBarrierWithWaitList call.
cl::Kernel kernel_postproc(program, "user_kernel_postproc");
kernel_postproc.setArg(0, shared_out_buffer);
queue.enqueueBarrierWithWaitList(nullptr, nullptr);
queue.enqueueNDRangeKernel(kernel_postproc,
cl::NDRange(0),
cl::NDRange(output_size),
cl::NDRange(1),
nullptr,
nullptr);
// Wait for pipeline completion
queue.finish();
制限事項¶
GPU プラグインの一部のプリミティブは、カーネルをコマンドキューに追加する前に、前のプリミティブを待機してホストスレッドをブロックする場合があります。そのような場合、
ov::InferRequest::start_async()
呼び出しは、ネットワークの (部分的または完全な) 完了を内部で待機するため、呼び出し元のスレッドに制御を返すまでにさらに時間を要します。
操作の例: Loop、TensorIterator、DetectionOutput、NonMaxSuppression共有キュー内の前後処理ジョブと推論パイプラインの同期はユーザーの責任です。
キュー共有が使用されている場合、スループット・モードは使用できません。つまり、コンパイルされたモデルごとに 1 つのストリームのみを使用できます。
RemoteContext および RemoteTensor 作成向けの低レベルメソッド¶
前述した高レベルのラッパーは、ネイティブ API への直接依存関係をユーザー・プログラムにもたらします。依存関係を回避したい場合でも、ov::Core::create_context()
、ov::RemoteContext::create_tensor()
、および ov::RemoteContext::get_params()
メソッドを直接使用できます。このレベルでは、ネイティブハンドルは void ポインターとして再解釈され、すべての引数は std::string と ov::Any のペアで満たされた ov::AnyMap
コンテナで渡されます。記述子とコンテナという 2 種類のマップエントリーが可能です。記述子はマップの予期される構造と可能なパラメーター値を設定します。
使用可能な低レベルのプロパティーとその説明については、ヘッダーファイル (remote_properties.hpp) を参照してください。
例¶
使用例の疑似コードについては、以下のセクションを参照してください。
注
低レベルのパラメーターの使用例については、上記のインクルード・ファイルのユーザー側ラッパーのソースコードを参照してください。
共有バッファー上での OpenCL* カーネルを実行
この例では、コンパイルされたモデル・オブジェクトから取得した OpenCL* コンテキストを使用します。
// ...
// initialize the core and load the network
ov::Core core;
auto model = core.read_model("model.xml");
auto compiled_model = core.compile_model(model, "GPU");
auto infer_request = compiled_model.create_infer_request();
// obtain the RemoteContext from the compiled model object and cast it to ClContext
auto gpu_context = compiled_model.get_context().as<ov::intel_gpu::ocl::ClContext>();
// obtain the OpenCL context handle from the RemoteContext,
// get device info and create a queue
cl::Context cl_context = gpu_context;
cl::Device device = cl::Device(cl_context.getInfo<CL_CONTEXT_DEVICES>()[0].get(), true);
cl_command_queue_properties props = CL_QUEUE_OUT_OF_ORDER_EXEC_MODE_ENABLE;
cl::CommandQueue queue = cl::CommandQueue(cl_context, device, props);
// create the OpenCL buffer within the obtained context
auto input = model->get_parameters().at(0);
auto input_size = ov::shape_size(input->get_shape());
cl_int err;
cl::Buffer shared_buffer(cl_context, CL_MEM_READ_WRITE, input_size, NULL, &err);
// wrap the buffer into RemoteBlob
auto shared_blob = gpu_context.create_tensor(input->get_element_type(), input->get_shape(), shared_buffer);
// ...
// execute user kernel
cl::Program program;
cl::Kernel kernel(program, "user_kernel");
kernel.setArg(0, shared_buffer);
queue.enqueueNDRangeKernel(kernel,
cl::NDRange(0),
cl::NDRange(input_size),
cl::NDRange(1),
nullptr,
nullptr);
queue.finish();
// ...
// pass results to the inference
infer_request.set_tensor(input, shared_blob);
infer_request.infer();
ユーザー指定の共有コンテキスト内での GPU プラグイン推論の実行
cl::Context ctx = get_ocl_context();
ov::Core core;
auto model = core.read_model("model.xml");
// share the context with GPU plugin and compile ExecutableNetwork
auto remote_context = ov::intel_gpu::ocl::ClContext(core, ctx.get());
auto exec_net_shared = core.compile_model(model, remote_context);
auto inf_req_shared = exec_net_shared.create_infer_request();
// ...
// do OpenCL processing stuff
// ...
// run the inference
inf_req_shared.infer();
Linux* での NV12 VAAPI ビデオ・デコーダー・サーフェスの直接使用
// ...
using namespace ov::preprocess;
auto p = PrePostProcessor(model);
p.input().tensor().set_element_type(ov::element::u8)
.set_color_format(ov::preprocess::ColorFormat::NV12_TWO_PLANES, {"y", "uv"})
.set_memory_type(ov::intel_gpu::memory_type::surface);
p.input().preprocess().convert_color(ov::preprocess::ColorFormat::BGR);
p.input().model().set_layout("NCHW");
model = p.build();
VADisplay disp = get_va_display();
// create the shared context object
auto shared_va_context = ov::intel_gpu::ocl::VAContext(core, disp);
// compile model within a shared context
auto compiled_model = core.compile_model(model, shared_va_context);
auto input0 = model->get_parameters().at(0);
auto input1 = model->get_parameters().at(1);
auto shape = input0->get_shape();
auto width = shape[1];
auto height = shape[2];
// execute decoding and obtain decoded surface handle
VASurfaceID va_surface = decode_va_surface();
// ...
//wrap decoder output into RemoteBlobs and set it as inference input
auto nv12_blob = shared_va_context.create_tensor_nv12(height, width, va_surface);
auto infer_request = compiled_model.create_infer_request();
infer_request.set_tensor(input0->get_friendly_name(), nv12_blob.first);
infer_request.set_tensor(input1->get_friendly_name(), nv12_blob.second);
infer_request.start_async();
infer_request.wait();
// ...
ov_preprocess_prepostprocessor_create(model, &preprocess);
ov_preprocess_prepostprocessor_get_input_info(preprocess, &preprocess_input_info);
ov_preprocess_input_info_get_tensor_info(preprocess_input_info, &preprocess_input_tensor_info);
ov_preprocess_input_tensor_info_set_element_type(preprocess_input_tensor_info, U8);
ov_preprocess_input_tensor_info_set_color_format_with_subname(preprocess_input_tensor_info,
NV12_TWO_PLANES,
2,
"y",
"uv");
ov_preprocess_input_tensor_info_set_memory_type(preprocess_input_tensor_info, "GPU_SURFACE");
ov_preprocess_input_tensor_info_set_spatial_static_shape(preprocess_input_tensor_info, height, width);
ov_preprocess_input_info_get_preprocess_steps(preprocess_input_info, &preprocess_input_steps);
ov_preprocess_preprocess_steps_convert_color(preprocess_input_steps, BGR);
ov_preprocess_preprocess_steps_resize(preprocess_input_steps, RESIZE_LINEAR);
ov_preprocess_input_info_get_model_info(preprocess_input_info, &preprocess_input_model_info);
ov_layout_create("NCHW", &layout);
ov_preprocess_input_model_info_set_layout(preprocess_input_model_info, layout);
ov_preprocess_prepostprocessor_build(preprocess, &new_model);
VADisplay display = get_va_display();
// create the shared context object
ov_core_create_context(core,
"GPU",
4,
&shared_va_context,
ov_property_key_intel_gpu_context_type,
"VA_SHARED",
ov_property_key_intel_gpu_va_device,
display);
// compile model within a shared context
ov_core_compile_model_with_context(core, new_model, shared_va_context, 0, &compiled_model);
ov_output_const_port_t* port_0 = NULL;
char* input_name_0 = NULL;
ov_model_const_input_by_index(new_model, 0, &port_0);
ov_port_get_any_name(port_0, &input_name_0);
ov_output_const_port_t* port_1 = NULL;
char* input_name_1 = NULL;
ov_model_const_input_by_index(new_model, 1, &port_1);
ov_port_get_any_name(port_1, &input_name_1);
ov_shape_t shape_y = {0, NULL};
ov_shape_t shape_uv = {0, NULL};
ov_const_port_get_shape(port_0, &shape_y);
ov_const_port_get_shape(port_1, &shape_uv);
// execute decoding and obtain decoded surface handle
VASurfaceID va_surface = decode_va_surface();
// ...
//wrap decoder output into RemoteBlobs and set it as inference input
ov_tensor_t* remote_tensor_y = NULL;
ov_tensor_t* remote_tensor_uv = NULL;
ov_remote_context_create_tensor(shared_va_context,
U8,
shape_y,
6,
&remote_tensor_y,
ov_property_key_intel_gpu_shared_mem_type,
"VA_SURFACE",
ov_property_key_intel_gpu_dev_object_handle,
va_surface,
ov_property_key_intel_gpu_va_plane,
0);
ov_remote_context_create_tensor(shared_va_context,
U8,
shape_uv,
6,
&remote_tensor_uv,
ov_property_key_intel_gpu_shared_mem_type,
"VA_SURFACE",
ov_property_key_intel_gpu_dev_object_handle,
va_surface,
ov_property_key_intel_gpu_va_plane,
1);
ov_compiled_model_create_infer_request(compiled_model, &infer_request);
ov_infer_request_set_tensor(infer_request, input_name_0, remote_tensor_y);
ov_infer_request_set_tensor(infer_request, input_name_1, remote_tensor_uv);
ov_infer_request_infer(infer_request);