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, &params);
    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, &params);

アプリケーションと 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 から派生します。したがって、上記の機能を提供し、それを拡張して、それぞれ ID3D11BufferID3D11Texture2D ポインター、または 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::stringov::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);