diff --git a/modules/gapi/CMakeLists.txt b/modules/gapi/CMakeLists.txt index 855ce27088..cc83606694 100644 --- a/modules/gapi/CMakeLists.txt +++ b/modules/gapi/CMakeLists.txt @@ -174,10 +174,13 @@ set(gapi_srcs src/streaming/onevpl/utils.cpp src/streaming/onevpl/data_provider_interface_exception.cpp src/streaming/onevpl/accelerators/surface/cpu_frame_adapter.cpp + src/streaming/onevpl/accelerators/surface/dx11_frame_adapter.cpp src/streaming/onevpl/accelerators/surface/surface.cpp src/streaming/onevpl/accelerators/surface/surface_pool.cpp + src/streaming/onevpl/accelerators/utils/shared_lock.cpp src/streaming/onevpl/accelerators/accel_policy_cpu.cpp src/streaming/onevpl/accelerators/accel_policy_dx11.cpp + src/streaming/onevpl/accelerators/dx11_alloc_resource.cpp src/streaming/onevpl/engine/engine_session.cpp src/streaming/onevpl/engine/processing_engine_base.cpp src/streaming/onevpl/engine/decode/decode_engine_legacy.cpp @@ -282,12 +285,16 @@ if(HAVE_GAPI_ONEVPL) if(TARGET opencv_test_gapi) ocv_target_compile_definitions(opencv_test_gapi PRIVATE -DHAVE_ONEVPL) ocv_target_link_libraries(opencv_test_gapi PRIVATE ${VPL_IMPORTED_TARGETS}) + if(MSVC) + target_compile_options(opencv_test_gapi PUBLIC "/wd4201") + endif() if(HAVE_D3D11 AND HAVE_OPENCL) ocv_target_include_directories(opencv_test_gapi SYSTEM PRIVATE ${OPENCL_INCLUDE_DIRS}) endif() endif() ocv_target_compile_definitions(${the_module} PRIVATE -DHAVE_ONEVPL) ocv_target_link_libraries(${the_module} PRIVATE ${VPL_IMPORTED_TARGETS}) + if(HAVE_D3D11 AND HAVE_OPENCL) ocv_target_include_directories(${the_module} SYSTEM PRIVATE ${OPENCL_INCLUDE_DIRS}) endif() diff --git a/modules/gapi/include/opencv2/gapi/streaming/onevpl/cfg_params.hpp b/modules/gapi/include/opencv2/gapi/streaming/onevpl/cfg_params.hpp index 9dc5ead7d7..0f35200f49 100644 --- a/modules/gapi/include/opencv2/gapi/streaming/onevpl/cfg_params.hpp +++ b/modules/gapi/include/opencv2/gapi/streaming/onevpl/cfg_params.hpp @@ -46,9 +46,72 @@ struct GAPI_EXPORTS CfgParam { double_t, void*, std::string>; + /** + * @brief frames_pool_size_name + * + * Special configuration parameter name for onevp::GSource: + * + * @note frames_pool_size_name allows to allocate surfaces pool appropriate size to keep + * decoded frames in accelerator memory ready before + * they would be consumed by onevp::GSource::pull operation. If you see + * a lot of WARNING about lack of free surface then it's time to increase + * frames_pool_size_name but be aware of accelerator free memory volume. + * If not set then MFX implementation use + * mfxFrameAllocRequest::NumFrameSuggested behavior + * + */ + static constexpr const char *frames_pool_size_name() { return "frames_pool_size"; } + static CfgParam create_frames_pool_size(uint64_t value); + + /** + * @brief acceleration_mode_name + * + * Special configuration parameter names for onevp::GSource: + * + * @note acceleration_mode_name allows to activate hardware acceleration & + * device memory management. + * Supported values: + * - MFX_ACCEL_MODE_VIA_D3D11 Will activate DX11 acceleration and will produces + * MediaFrames with data allocated in DX11 device memory + * + * If not set then MFX implementation will use default acceleration behavior: + * all decoding operation uses default GPU resources but MediaFrame produces + * data allocated by using host RAM + * + */ + static constexpr const char *acceleration_mode_name() { return "mfxImplDescription.AccelerationMode"; } + static CfgParam create_acceleration_mode(uint32_t value); + static CfgParam create_acceleration_mode(const char* value); + + /** + * @brief decoder_id_name + * + * Special configuration parameter names for onevp::GSource: + * + * @note decoder_id_name allows to specify VPL decoder type which MUST present + * in case of RAW video input data and MUST NOT present as CfgParam if video + * stream incapsulated into container(*.mp4, *.mkv and so on). In latter case + * onevp::GSource will determine it automatically + * Supported values: + * - MFX_CODEC_AVC + * - MFX_CODEC_HEVC + * - MFX_CODEC_MPEG2 + * - MFX_CODEC_VC1 + * - MFX_CODEC_CAPTURE + * - MFX_CODEC_VP9 + * - MFX_CODEC_AV1 + * + */ + static constexpr const char *decoder_id_name() { return "mfxImplDescription.mfxDecoderDescription.decoder.CodecID"; } + static CfgParam create_decoder_id(uint32_t value); + static CfgParam create_decoder_id(const char* value); + + static constexpr const char *implementation_name() { return "mfxImplDescription.Impl"; } + static CfgParam create_implementation(uint32_t value); + static CfgParam create_implementation(const char* value); /** - * Create onevp::GSource configuration parameter. + * Create generic onevp::GSource configuration parameter. * *@param name name of parameter. *@param value value of parameter. diff --git a/modules/gapi/perf/streaming/gapi_streaming_source_perf_tests.cpp b/modules/gapi/perf/streaming/gapi_streaming_source_perf_tests.cpp index ffefa24259..7d06ad068b 100644 --- a/modules/gapi/perf/streaming/gapi_streaming_source_perf_tests.cpp +++ b/modules/gapi/perf/streaming/gapi_streaming_source_perf_tests.cpp @@ -18,16 +18,19 @@ using namespace perf; const std::string files[] = { "highgui/video/big_buck_bunny.h265", "highgui/video/big_buck_bunny.h264", + "highgui/video/sample_322x242_15frames.yuv420p.libx265.mp4", }; const std::string codec[] = { "MFX_CODEC_HEVC", - "MFX_CODEC_AVC" + "MFX_CODEC_AVC", + "", }; using source_t = std::string; using codec_t = std::string; -using source_description_t = std::tuple; +using accel_mode_t = std::string; +using source_description_t = std::tuple; class OneVPLSourcePerfTest : public TestPerfParams {}; class VideoCapSourcePerfTest : public TestPerfParams {}; @@ -39,12 +42,20 @@ PERF_TEST_P_(OneVPLSourcePerfTest, TestPerformance) const auto params = GetParam(); source_t src = findDataFile(get<0>(params)); codec_t type = get<1>(params); + accel_mode_t mode = get<2>(params); std::vector cfg_params { - CfgParam::create("mfxImplDescription.Impl", "MFX_IMPL_TYPE_HARDWARE"), - CfgParam::create("mfxImplDescription.mfxDecoderDescription.decoder.CodecID", type), + CfgParam::create_implementation("MFX_IMPL_TYPE_HARDWARE"), }; + if (!type.empty()) { + cfg_params.push_back(CfgParam::create_decoder_id(type.c_str())); + } + + if (!mode.empty()) { + cfg_params.push_back(CfgParam::create_acceleration_mode(mode.c_str())); + } + auto source_ptr = cv::gapi::wip::make_onevpl_src(src, cfg_params); cv::gapi::wip::Data out; @@ -72,12 +83,17 @@ PERF_TEST_P_(VideoCapSourcePerfTest, TestPerformance) } INSTANTIATE_TEST_CASE_P(Streaming, OneVPLSourcePerfTest, - Values(source_description_t(files[0], codec[0]), - source_description_t(files[1], codec[1]))); + Values(source_description_t(files[0], codec[0], ""), + source_description_t(files[0], codec[0], "MFX_ACCEL_MODE_VIA_D3D11"), + source_description_t(files[1], codec[1], ""), + source_description_t(files[1], codec[1], "MFX_ACCEL_MODE_VIA_D3D11"), + source_description_t(files[2], codec[2], ""), + source_description_t(files[2], codec[2], "MFX_ACCEL_MODE_VIA_D3D11"))); INSTANTIATE_TEST_CASE_P(Streaming, VideoCapSourcePerfTest, Values(files[0], - files[1])); + files[1], + files[2])); } // namespace opencv_test #endif // HAVE_ONEVPL diff --git a/modules/gapi/samples/onevpl_infer_single_roi.cpp b/modules/gapi/samples/onevpl_infer_single_roi.cpp index fec0f0d043..1ab06de739 100644 --- a/modules/gapi/samples/onevpl_infer_single_roi.cpp +++ b/modules/gapi/samples/onevpl_infer_single_roi.cpp @@ -38,12 +38,14 @@ const std::string about = "This is an OpenCV-based version of oneVPLSource decoder example"; const std::string keys = - "{ h help | | Print this help message }" - "{ input | | Path to the input demultiplexed video file }" - "{ output | | Path to the output RAW video file. Use .avi extension }" - "{ facem | face-detection-adas-0001.xml | Path to OpenVINO IE face detection model (.xml) }" - "{ faced | CPU | Target device for face detection model (e.g. CPU, GPU, VPU, ...) }" - "{ cfg_params | :;: | Semicolon separated list of oneVPL mfxVariants which is used for configuring source (see `MFXSetConfigFilterProperty` by https://spec.oneapi.io/versions/latest/elements/oneVPL/source/index.html) }"; + "{ h help | | Print this help message }" + "{ input | | Path to the input demultiplexed video file }" + "{ output | | Path to the output RAW video file. Use .avi extension }" + "{ facem | face-detection-adas-0001.xml | Path to OpenVINO IE face detection model (.xml) }" + "{ faced | AUTO | Target device for face detection model (e.g. AUTO, GPU, VPU, ...) }" + "{ cfg_params | :;: | Semicolon separated list of oneVPL mfxVariants which is used for configuring source (see `MFXSetConfigFilterProperty` by https://spec.oneapi.io/versions/latest/elements/oneVPL/source/index.html) }" + "{ streaming_queue_capacity | 1 | Streaming executor queue capacity. Calculated automaticaly if 0 }" + "{ frames_pool_size | 0 | OneVPL source applies this parameter as preallocated frames pool size}"; namespace { @@ -194,6 +196,8 @@ int main(int argc, char *argv[]) { std::string file_path = cmd.get("input"); const std::string output = cmd.get("output"); const auto face_model_path = cmd.get("facem"); + const auto streaming_queue_capacity = cmd.get("streaming_queue_capacity"); + const auto source_queue_capacity = cmd.get("frames_pool_size"); // check ouput file extension if (!output.empty()) { @@ -217,6 +221,10 @@ int main(int argc, char *argv[]) { return -1; } + if (source_queue_capacity != 0) { + source_cfgs.push_back(cv::gapi::wip::onevpl::CfgParam::create_frames_pool_size(source_queue_capacity)); + } + const std::string& device_id = cmd.get("faced"); auto face_net = cv::gapi::ie::Params { face_model_path, // path to topology IR @@ -298,6 +306,10 @@ int main(int argc, char *argv[]) { < custom::OCVLocateROI , custom::OCVBBoxes>(); auto networks = cv::gapi::networks(face_net); + auto face_detection_args = cv::compile_args(networks, kernels); + if (streaming_queue_capacity != 0) { + face_detection_args += cv::compile_args(cv::gapi::streaming::queue_capacity{ streaming_queue_capacity }); + } // Create source cv::Ptr cap; @@ -331,7 +343,7 @@ int main(int argc, char *argv[]) { cv::GStreamingCompiled pipeline; try { pipeline = cv::GComputation(cv::GIn(in), cv::GOut(out)) - .compileStreaming(cv::compile_args(kernels, networks)); + .compileStreaming(std::move(face_detection_args)); } catch (const std::exception& ex) { std::cerr << "Exception occured during pipeline construction: " << ex.what() << std::endl; return -1; diff --git a/modules/gapi/src/backends/render/grenderocv.cpp b/modules/gapi/src/backends/render/grenderocv.cpp index 2652284668..da0e5831a1 100644 --- a/modules/gapi/src/backends/render/grenderocv.cpp +++ b/modules/gapi/src/backends/render/grenderocv.cpp @@ -128,15 +128,12 @@ GAPI_OCV_KERNEL_ST(RenderFrameOCVImpl, cv::gapi::wip::draw::GRenderFrame, Render out = in; auto desc = out.desc(); - auto w_out = out.access(cv::MediaFrame::Access::W); - - auto out_y = cv::Mat(desc.size, CV_8UC1, w_out.ptr[0], w_out.stride[0]); - auto out_uv = cv::Mat(desc.size / 2, CV_8UC2, w_out.ptr[1], w_out.stride[1]); - - auto r_in = in.access(cv::MediaFrame::Access::R); + cv::Mat upsample_uv, yuv; + { + auto r_in = in.access(cv::MediaFrame::Access::R); - auto in_y = cv::Mat(desc.size, CV_8UC1, r_in.ptr[0], r_in.stride[0]); - auto in_uv = cv::Mat(desc.size / 2, CV_8UC2, r_in.ptr[1], r_in.stride[1]); + auto in_y = cv::Mat(desc.size, CV_8UC1, r_in.ptr[0], r_in.stride[0]); + auto in_uv = cv::Mat(desc.size / 2, CV_8UC2, r_in.ptr[1], r_in.stride[1]); /* FIXME How to render correctly on NV12 format ? * @@ -157,19 +154,26 @@ GAPI_OCV_KERNEL_ST(RenderFrameOCVImpl, cv::gapi::wip::draw::GRenderFrame, Render * */ - // NV12 -> YUV - cv::Mat upsample_uv, yuv; - cv::resize(in_uv, upsample_uv, in_uv.size() * 2, cv::INTER_LINEAR); - cv::merge(std::vector{in_y, upsample_uv}, yuv); + // NV12 -> YUV + cv::resize(in_uv, upsample_uv, in_uv.size() * 2, cv::INTER_LINEAR); + cv::merge(std::vector{in_y, upsample_uv}, yuv); + } cv::gapi::wip::draw::drawPrimitivesOCVYUV(yuv, prims, state.ftpr); // YUV -> NV12 - cv::Mat out_u, out_v, uv_plane; - std::vector chs = { out_y, out_u, out_v }; - cv::split(yuv, chs); - cv::merge(std::vector{chs[1], chs[2]}, uv_plane); - cv::resize(uv_plane, out_uv, uv_plane.size() / 2, cv::INTER_LINEAR); + { + auto w_out = out.access(cv::MediaFrame::Access::W); + + auto out_y = cv::Mat(desc.size, CV_8UC1, w_out.ptr[0], w_out.stride[0]); + auto out_uv = cv::Mat(desc.size / 2, CV_8UC2, w_out.ptr[1], w_out.stride[1]); + + cv::Mat out_u, out_v, uv_plane; + std::vector chs = { out_y, out_u, out_v }; + cv::split(yuv, chs); + cv::merge(std::vector{chs[1], chs[2]}, uv_plane); + cv::resize(uv_plane, out_uv, uv_plane.size() / 2, cv::INTER_LINEAR); + } } static void setup(const cv::GFrameDesc& /* in_nv12 */, diff --git a/modules/gapi/src/streaming/onevpl/accelerators/accel_policy_cpu.cpp b/modules/gapi/src/streaming/onevpl/accelerators/accel_policy_cpu.cpp index 033fb9eb15..2cdf1c2b44 100644 --- a/modules/gapi/src/streaming/onevpl/accelerators/accel_policy_cpu.cpp +++ b/modules/gapi/src/streaming/onevpl/accelerators/accel_policy_cpu.cpp @@ -21,8 +21,100 @@ namespace cv { namespace gapi { namespace wip { namespace onevpl { +namespace utils { +mfxU32 GetSurfaceSize_(mfxU32 FourCC, mfxU32 width, mfxU32 height) { + mfxU32 nbytes = 0; -VPLCPUAccelerationPolicy::VPLCPUAccelerationPolicy() { + mfxU32 half_width = width / 2; + mfxU32 half_height = height / 2; + switch (FourCC) { + case MFX_FOURCC_I420: + case MFX_FOURCC_NV12: + nbytes = width * height + 2 * half_width * half_height; + break; + case MFX_FOURCC_I010: + case MFX_FOURCC_P010: + nbytes = width * height + 2 * half_width * half_height; + nbytes *= 2; + break; + case MFX_FOURCC_RGB4: + nbytes = width * height * 4; + break; + default: + break; + } + + return nbytes; +} + +surface_ptr_t create_surface_RGB4_(mfxFrameInfo frameInfo, + std::shared_ptr out_buf_ptr, + size_t out_buf_ptr_offset, + size_t out_buf_size) +{ + mfxU8* buf = reinterpret_cast(out_buf_ptr.get()); + mfxU16 surfW = frameInfo.Width * 4; + mfxU16 surfH = frameInfo.Height; + (void)surfH; + + // TODO more intelligent check + if (out_buf_size <= out_buf_ptr_offset) { + GAPI_LOG_WARNING(nullptr, "Not enough buffer, ptr: " << out_buf_ptr << + ", size: " << out_buf_size << + ", offset: " << out_buf_ptr_offset << + ", W: " << surfW << + ", H: " << surfH); + GAPI_Assert(false && "Invalid offset"); + } + + std::unique_ptr handle(new mfxFrameSurface1); + memset(handle.get(), 0, sizeof(mfxFrameSurface1)); + + handle->Info = frameInfo; + handle->Data.B = buf + out_buf_ptr_offset; + handle->Data.G = handle->Data.B + 1; + handle->Data.R = handle->Data.B + 2; + handle->Data.A = handle->Data.B + 3; + handle->Data.Pitch = surfW; + + return Surface::create_surface(std::move(handle), out_buf_ptr); +} + +surface_ptr_t create_surface_other_(mfxFrameInfo frameInfo, + std::shared_ptr out_buf_ptr, + size_t out_buf_ptr_offset, + size_t out_buf_size) +{ + mfxU8* buf = reinterpret_cast(out_buf_ptr.get()); + mfxU16 surfH = frameInfo.Height; + mfxU16 surfW = (frameInfo.FourCC == MFX_FOURCC_P010) ? frameInfo.Width * 2 : frameInfo.Width; + + // TODO more intelligent check + if (out_buf_size <= + out_buf_ptr_offset + (surfW * surfH) + ((surfW / 2) * (surfH / 2))) { + GAPI_LOG_WARNING(nullptr, "Not enough buffer, ptr: " << out_buf_ptr << + ", size: " << out_buf_size << + ", offset: " << out_buf_ptr_offset << + ", W: " << surfW << + ", H: " << surfH); + GAPI_Assert(false && "Invalid offset"); + } + + std::unique_ptr handle(new mfxFrameSurface1); + memset(handle.get(), 0, sizeof(mfxFrameSurface1)); + + handle->Info = frameInfo; + handle->Data.Y = buf + out_buf_ptr_offset; + handle->Data.U = buf + out_buf_ptr_offset + (surfW * surfH); + handle->Data.V = handle->Data.U + ((surfW / 2) * (surfH / 2)); + handle->Data.Pitch = surfW; + + return Surface::create_surface(std::move(handle), out_buf_ptr); +} +} // namespace utils + +VPLCPUAccelerationPolicy::VPLCPUAccelerationPolicy(device_selector_ptr_t selector) : + VPLAccelerationPolicy(selector) { GAPI_LOG_INFO(nullptr, "created"); } @@ -117,6 +209,37 @@ VPLCPUAccelerationPolicy::create_surface_pool(size_t pool_size, size_t surface_s return preallocated_pool_memory_ptr; } +VPLCPUAccelerationPolicy::pool_key_t +VPLCPUAccelerationPolicy::create_surface_pool(const mfxFrameAllocRequest& alloc_request, mfxVideoParam& param) { + + // External (application) allocation of decode surfaces + GAPI_LOG_DEBUG(nullptr, "Query mfxFrameAllocRequest.NumFrameSuggested: " << alloc_request.NumFrameSuggested << + ", mfxFrameAllocRequest.Type: " << alloc_request.Type); + + mfxU32 singleSurfaceSize = utils::GetSurfaceSize_(param.mfx.FrameInfo.FourCC, + param.mfx.FrameInfo.Width, + param.mfx.FrameInfo.Height); + if (!singleSurfaceSize) { + throw std::runtime_error("Cannot determine surface size for: fourCC: " + + std::to_string(param.mfx.FrameInfo.FourCC) + + ", width: " + std::to_string(param.mfx.FrameInfo.Width) + + ", height: " + std::to_string(param.mfx.FrameInfo.Height)); + } + + const auto &frameInfo = param.mfx.FrameInfo; + auto surface_creator = + [&frameInfo] (std::shared_ptr out_buf_ptr, size_t out_buf_ptr_offset, + size_t out_buf_size) -> surface_ptr_t { + return (frameInfo.FourCC == MFX_FOURCC_RGB4) ? + utils::create_surface_RGB4_(frameInfo, out_buf_ptr, out_buf_ptr_offset, + out_buf_size) : + utils::create_surface_other_(frameInfo, out_buf_ptr, out_buf_ptr_offset, + out_buf_size);}; + + return create_surface_pool(alloc_request.NumFrameSuggested, + singleSurfaceSize, surface_creator); +} + VPLCPUAccelerationPolicy::surface_weak_ptr_t VPLCPUAccelerationPolicy::get_free_surface(pool_key_t key) { auto pool_it = pool_table.find(key); if (pool_it == pool_table.end()) { diff --git a/modules/gapi/src/streaming/onevpl/accelerators/accel_policy_cpu.hpp b/modules/gapi/src/streaming/onevpl/accelerators/accel_policy_cpu.hpp index 35165a55c6..fdc0afd4bf 100644 --- a/modules/gapi/src/streaming/onevpl/accelerators/accel_policy_cpu.hpp +++ b/modules/gapi/src/streaming/onevpl/accelerators/accel_policy_cpu.hpp @@ -13,7 +13,6 @@ #include "opencv2/gapi/own/exports.hpp" // GAPI_EXPORTS #ifdef HAVE_ONEVPL -#include #include "streaming/onevpl/accelerators/accel_policy_interface.hpp" #include "streaming/onevpl/accelerators/surface/surface_pool.hpp" @@ -25,14 +24,15 @@ namespace onevpl { // GAPI_EXPORTS for tests struct GAPI_EXPORTS VPLCPUAccelerationPolicy final : public VPLAccelerationPolicy { - VPLCPUAccelerationPolicy(); + VPLCPUAccelerationPolicy(device_selector_ptr_t selector); ~VPLCPUAccelerationPolicy(); using pool_t = CachedPool; void init(session_t session) override; void deinit(session_t session) override; - pool_key_t create_surface_pool(size_t pool_size, size_t surface_size_bytes, surface_ptr_ctr_t creator) override; + pool_key_t create_surface_pool(size_t pool_size, size_t surface_size_bytes, surface_ptr_ctr_t creator); + pool_key_t create_surface_pool(const mfxFrameAllocRequest& alloc_request, mfxVideoParam& param) override; surface_weak_ptr_t get_free_surface(pool_key_t key) override; size_t get_free_surface_count(pool_key_t key) const override; size_t get_surface_count(pool_key_t key) const override; diff --git a/modules/gapi/src/streaming/onevpl/accelerators/accel_policy_dx11.cpp b/modules/gapi/src/streaming/onevpl/accelerators/accel_policy_dx11.cpp index 8365dd20e3..f528190ad5 100644 --- a/modules/gapi/src/streaming/onevpl/accelerators/accel_policy_dx11.cpp +++ b/modules/gapi/src/streaming/onevpl/accelerators/accel_policy_dx11.cpp @@ -5,8 +5,10 @@ // Copyright (C) 2021 Intel Corporation #ifdef HAVE_ONEVPL +#include + #include "streaming/onevpl/accelerators/accel_policy_dx11.hpp" -#include "streaming/onevpl/accelerators/surface/cpu_frame_adapter.hpp" +#include "streaming/onevpl/accelerators/surface/dx11_frame_adapter.hpp" #include "streaming/onevpl/accelerators/surface/surface.hpp" #include "streaming/onevpl/utils.hpp" #include "logger.hpp" @@ -30,28 +32,60 @@ namespace gapi { namespace wip { namespace onevpl { -VPLDX11AccelerationPolicy::VPLDX11AccelerationPolicy() : - hw_handle(nullptr) +VPLDX11AccelerationPolicy::VPLDX11AccelerationPolicy(device_selector_ptr_t selector) : + VPLAccelerationPolicy(selector), + hw_handle(), + device_context(), + allocator() { -#ifdef CPU_ACCEL_ADAPTER - adapter.reset(new VPLCPUAccelerationPolicy); -#endif + // setup dx11 device + IDeviceSelector::DeviceScoreTable devices = get_device_selector()->select_devices(); + GAPI_Assert(devices.size() == 1 && "Multiple(or zero) acceleration devices case is unsupported"); + AccelType accel_type = devices.begin()->second.get_type(); + GAPI_Assert(accel_type == AccelType::DX11 && + "Unexpected device AccelType while is waiting AccelType::DX11"); + + hw_handle = reinterpret_cast(devices.begin()->second.get_ptr()); + + // setup dx11 context + IDeviceSelector::DeviceContexts contexts = get_device_selector()->select_context(); + GAPI_Assert(contexts.size() == 1 && "Multiple(or zero) acceleration context case is unsupported"); + accel_type = contexts.begin()->get_type(); + GAPI_Assert(accel_type == AccelType::DX11 && + "Unexpected context AccelType while is waiting AccelType::DX11"); + device_context = reinterpret_cast(contexts.begin()->get_ptr()); + + // setup dx11 allocator + memset(&allocator, 0, sizeof(mfxFrameAllocator)); + allocator.Alloc = alloc_cb; + allocator.Lock = lock_cb; + allocator.Unlock = unlock_cb; + allocator.GetHDL = get_hdl_cb; + allocator.Free = free_cb; + allocator.pthis = this; } VPLDX11AccelerationPolicy::~VPLDX11AccelerationPolicy() { - if (hw_handle) - { - GAPI_LOG_INFO(nullptr, "VPLDX11AccelerationPolicy release ID3D11Device"); - hw_handle->Release(); + for (auto& allocation_pair : allocation_table) { + allocation_pair.second.reset(); } + GAPI_LOG_INFO(nullptr, "destroyed"); } void VPLDX11AccelerationPolicy::init(session_t session) { - mfxStatus sts = MFXVideoCORE_GetHandle(session, MFX_HANDLE_D3D11_DEVICE, reinterpret_cast(&hw_handle)); + mfxStatus sts = MFXVideoCORE_SetHandle(session, MFX_HANDLE_D3D11_DEVICE, + static_cast(hw_handle)); if (sts != MFX_ERR_NONE) { - throw std::logic_error("Cannot create VPLDX11AccelerationPolicy, MFXVideoCORE_GetHandle error: " + + throw std::logic_error("Cannot create VPLDX11AccelerationPolicy, MFXVideoCORE_SetHandle error: " + + mfxstatus_to_string(sts)); + } + + sts = MFXVideoCORE_SetFrameAllocator(session, &allocator); + if (sts != MFX_ERR_NONE) + { + throw std::logic_error("Cannot create VPLDX11AccelerationPolicy, MFXVideoCORE_SetFrameAllocator error: " + mfxstatus_to_string(sts)); } @@ -63,53 +97,289 @@ void VPLDX11AccelerationPolicy::deinit(session_t session) { } VPLDX11AccelerationPolicy::pool_key_t -VPLDX11AccelerationPolicy::create_surface_pool(size_t pool_size, size_t surface_size_bytes, - surface_ptr_ctr_t creator) { - GAPI_LOG_DEBUG(nullptr, "pool size: " << pool_size << ", surface size bytes: " << surface_size_bytes); +VPLDX11AccelerationPolicy::create_surface_pool(const mfxFrameAllocRequest& alloc_req, + mfxVideoParam& param) { + param.IOPattern = MFX_IOPATTERN_OUT_VIDEO_MEMORY; -#ifdef CPU_ACCEL_ADAPTER - return adapter->create_surface_pool(pool_size, surface_size_bytes, creator); -#endif - (void)pool_size; - (void)surface_size_bytes; - (void)creator; - throw std::runtime_error("VPLDX11AccelerationPolicy::create_surface_pool() is not implemented"); + // allocate textures by explicit request + mfxFrameAllocResponse mfxResponse; + mfxStatus sts = on_alloc(&alloc_req, &mfxResponse); + if (sts != MFX_ERR_NONE) + { + throw std::logic_error("Cannot create allocated memory for surfaces, error: " + + mfxstatus_to_string(sts)); + } + + // get reference pointer + auto table_it = allocation_table.find(alloc_req.AllocId); + GAPI_DbgAssert (allocation_table.end() != table_it); + + mfxU16 numSurfaces = alloc_req.NumFrameSuggested; + + // NB: create pool with numSurfaces reservation + pool_t pool(numSurfaces); + for (int i = 0; i < numSurfaces; i++) { + std::unique_ptr handle(new mfxFrameSurface1 {}); + handle->Info = param.mfx.FrameInfo; + handle->Data.MemId = mfxResponse.mids[i]; + + pool.push_back(Surface::create_surface(std::move(handle), table_it->second)); + } + + // remember pool by key + pool_key_t key = reinterpret_cast(table_it->second.get()); + GAPI_LOG_INFO(nullptr, "New pool allocated, key: " << key << + ", surface count: " << pool.total_size()); + try { + if (!pool_table.emplace(key, std::move(pool)).second) { + throw std::runtime_error(std::string("VPLDX11AccelerationPolicy::create_surface_pool - ") + + "cannot insert pool, table size: " + std::to_string(pool_table.size())); + } + } catch (const std::exception&) { + throw; + } + return key; } -VPLDX11AccelerationPolicy::surface_weak_ptr_t VPLDX11AccelerationPolicy::get_free_surface(pool_key_t key) -{ -#ifdef CPU_ACCEL_ADAPTER - return adapter->get_free_surface(key); -#endif - (void)key; - throw std::runtime_error("VPLDX11AccelerationPolicy::get_free_surface() is not implemented"); +VPLDX11AccelerationPolicy::surface_weak_ptr_t VPLDX11AccelerationPolicy::get_free_surface(pool_key_t key) { + auto pool_it = pool_table.find(key); + if (pool_it == pool_table.end()) { + std::stringstream ss; + ss << "key is not found: " << key << ", table size: " << pool_table.size(); + const std::string& str = ss.str(); + GAPI_LOG_WARNING(nullptr, str); + throw std::runtime_error(std::string(__FUNCTION__) + " - " + str); + } + + pool_t& requested_pool = pool_it->second; + return requested_pool.find_free(); } -size_t VPLDX11AccelerationPolicy::get_free_surface_count(pool_key_t key) const { -#ifdef CPU_ACCEL_ADAPTER - return adapter->get_free_surface_count(key); -#endif - (void)key; - throw std::runtime_error("get_free_surface_count() is not implemented"); +size_t VPLDX11AccelerationPolicy::get_free_surface_count(pool_key_t) const { + GAPI_Assert(false && "get_free_surface_count() is not implemented"); } -size_t VPLDX11AccelerationPolicy::get_surface_count(pool_key_t key) const { -#ifdef CPU_ACCEL_ADAPTER - return adapter->get_surface_count(key); -#endif - (void)key; - throw std::runtime_error("VPLDX11AccelerationPolicy::get_surface_count() is not implemented"); +size_t VPLDX11AccelerationPolicy::get_surface_count(pool_key_t) const { + GAPI_Assert(false && "VPLDX11AccelerationPolicy::get_surface_count() is not implemented"); } cv::MediaFrame::AdapterPtr VPLDX11AccelerationPolicy::create_frame_adapter(pool_key_t key, mfxFrameSurface1* surface) { + auto pool_it = pool_table.find(key); + if (pool_it == pool_table.end()) { + std::stringstream ss; + ss << "key is not found: " << key << ", table size: " << pool_table.size(); + const std::string& str = ss.str(); + GAPI_LOG_WARNING(nullptr, str); + throw std::runtime_error(std::string(__FUNCTION__) + " - " + str); + } -#ifdef CPU_ACCEL_ADAPTER - return adapter->create_frame_adapter(key, surface); -#endif - (void)key; - (void)surface; - throw std::runtime_error("VPLDX11AccelerationPolicy::create_frame_adapter() is not implemented"); + pool_t& requested_pool = pool_it->second; + return cv::MediaFrame::AdapterPtr{new VPLMediaFrameDX11Adapter(requested_pool.find_by_handle(surface))}; +} + +mfxStatus VPLDX11AccelerationPolicy::alloc_cb(mfxHDL pthis, mfxFrameAllocRequest *request, + mfxFrameAllocResponse *response) { + if (!pthis) { + return MFX_ERR_MEMORY_ALLOC; + } + + VPLDX11AccelerationPolicy *self = static_cast(pthis); + + return self->on_alloc(request, response); +} + +mfxStatus VPLDX11AccelerationPolicy::lock_cb(mfxHDL pthis, mfxMemId mid, mfxFrameData *ptr) { + VPLDX11AccelerationPolicy *self = static_cast(pthis); + GAPI_LOG_DEBUG(nullptr, "called from: " << self ? "Policy" : "Resource"); + cv::util::suppress_unused_warning(self); + return on_lock(mid, ptr); +} + +mfxStatus VPLDX11AccelerationPolicy::unlock_cb(mfxHDL pthis, mfxMemId mid, mfxFrameData *ptr) { + VPLDX11AccelerationPolicy *self = static_cast(pthis); + GAPI_LOG_DEBUG(nullptr, "called from: " << self ? "Policy" : "Resource"); + cv::util::suppress_unused_warning(self); + return on_unlock(mid, ptr); +} + +mfxStatus VPLDX11AccelerationPolicy::get_hdl_cb(mfxHDL pthis, mfxMemId mid, mfxHDL *handle) { + VPLDX11AccelerationPolicy *self = static_cast(pthis); + + GAPI_LOG_DEBUG(nullptr, "called from: " << self ? "Policy" : "Resource"); + cv::util::suppress_unused_warning(self); + return on_get_hdl(mid, handle); +} + +mfxStatus VPLDX11AccelerationPolicy::free_cb(mfxHDL pthis, mfxFrameAllocResponse *response) { + if (!pthis) { + return MFX_ERR_MEMORY_ALLOC; + } + + VPLDX11AccelerationPolicy *self = static_cast(pthis); + return self->on_free(response); +} + +mfxStatus VPLDX11AccelerationPolicy::on_alloc(const mfxFrameAllocRequest *request, + mfxFrameAllocResponse *response) { + GAPI_LOG_DEBUG(nullptr, "Requested allocation id: " << std::to_string(request->AllocId) << + ", type: " << ext_mem_frame_type_to_cstr(request->Type) << + ", size: " << request->Info.Width << "x" << request->Info.Height << + ", frames minimum count: " << request->NumFrameMin << + ", frames suggested count: " << request->NumFrameSuggested); + auto table_it = allocation_table.find(request->AllocId); + if (allocation_table.end() != table_it) { + GAPI_LOG_WARNING(nullptr, "Allocation already exists, id: " + std::to_string(request->AllocId) + + ". Total allocation size: " + std::to_string(allocation_table.size())); + + // TODO cache + allocation_t &resources_array = table_it->second; + response->AllocId = request->AllocId; + GAPI_DbgAssert(static_cast(std::numeric_limits::max()) > resources_array->size() && + "Invalid num frames: overflow"); + response->NumFrameActual = static_cast(resources_array->size()); + response->mids = reinterpret_cast(resources_array->data()); + + return MFX_ERR_NONE; + } + + DXGI_FORMAT colorFormat = VPLMediaFrameDX11Adapter::get_dx11_color_format(request->Info.FourCC); + + if (DXGI_FORMAT_UNKNOWN == colorFormat || colorFormat != DXGI_FORMAT_NV12) { + GAPI_LOG_WARNING(nullptr, "Unsupported fourcc :" << request->Info.FourCC); + return MFX_ERR_UNSUPPORTED; + } + + D3D11_TEXTURE2D_DESC desc = { 0 }; + + desc.Width = request->Info.Width; + desc.Height = request->Info.Height; + + desc.MipLevels = 1; + // single texture with subresources + desc.ArraySize = request->NumFrameSuggested; + desc.Format = colorFormat; + desc.SampleDesc.Count = 1; + desc.Usage = D3D11_USAGE_DEFAULT; + desc.MiscFlags = D3D11_RESOURCE_MISC_SHARED; + desc.BindFlags = D3D11_BIND_DECODER; + + if (request->Type & MFX_MEMTYPE_SHARED_RESOURCE) { + desc.BindFlags |= D3D11_BIND_SHADER_RESOURCE; + desc.MiscFlags = D3D11_RESOURCE_MISC_SHARED; + } + + ComPtrGuard main_texture = createCOMPtrGuard(); + HRESULT err = S_OK; + { + ID3D11Texture2D *pTexture2D = nullptr; + err = hw_handle->CreateTexture2D(&desc, nullptr, &pTexture2D); + if (FAILED(err)) { + GAPI_LOG_WARNING(nullptr, "Cannot create texture, error: " + std::to_string(HRESULT_CODE(err))); + return MFX_ERR_MEMORY_ALLOC; + } + main_texture.reset(pTexture2D); + } + + // create staging texture to read it from + desc.ArraySize = 1; + desc.Usage = D3D11_USAGE_STAGING; + desc.CPUAccessFlags = D3D11_CPU_ACCESS_READ | D3D11_CPU_ACCESS_WRITE; + desc.BindFlags = 0; + desc.MiscFlags = 0; + std::vector> staging_textures; + staging_textures.reserve(request->NumFrameSuggested); + for (int i = 0; i < request->NumFrameSuggested; i++ ) { + ID3D11Texture2D *staging_texture_2d = nullptr; + err = hw_handle->CreateTexture2D(&desc, NULL, &staging_texture_2d); + if (FAILED(err)) { + GAPI_LOG_WARNING(nullptr, "Cannot create staging texture, error: " + std::to_string(HRESULT_CODE(err))); + return MFX_ERR_MEMORY_ALLOC; + } + staging_textures.push_back(createCOMPtrGuard(staging_texture_2d)); + } + + // for multiple subresources initialize allocation array + auto cand_resource_it = allocation_table.end(); + { + // insert into global table + auto inserted_it = + allocation_table.emplace(request->AllocId, + DX11AllocationRecord::create(request->NumFrameSuggested, + device_context, + allocator, + std::move(main_texture), + std::move(staging_textures))); + if (!inserted_it.second) { + GAPI_LOG_WARNING(nullptr, "Cannot assign allocation by id: " + std::to_string(request->AllocId) + + " - aldeady exist. Total allocation size: " + std::to_string(allocation_table.size())); + return MFX_ERR_MEMORY_ALLOC; + } + + GAPI_LOG_DEBUG(nullptr, "allocation by id: " << request->AllocId << + " was created, total allocations count: " << allocation_table.size()); + cand_resource_it = inserted_it.first; + } + + // fill out response + GAPI_DbgAssert(cand_resource_it != allocation_table.end() && "Invalid cand_resource_it"); + + allocation_t &resources_array = cand_resource_it->second; + response->AllocId = request->AllocId; + response->NumFrameActual = request->NumFrameSuggested; + response->mids = reinterpret_cast(resources_array->data()); + + return MFX_ERR_NONE; +} + +mfxStatus VPLDX11AccelerationPolicy::on_lock(mfxMemId mid, mfxFrameData *ptr) { + DX11AllocationRecord::AllocationId data = reinterpret_cast(mid); + if (!data) { + GAPI_LOG_WARNING(nullptr, "Allocation record is empty"); + return MFX_ERR_LOCK_MEMORY; + } + + return data->acquire_access(ptr); +} + +mfxStatus VPLDX11AccelerationPolicy::on_unlock(mfxMemId mid, mfxFrameData *ptr) { + DX11AllocationRecord::AllocationId data = reinterpret_cast(mid); + if (!data) { + return MFX_ERR_LOCK_MEMORY; + } + + return data->release_access(ptr); +} + +mfxStatus VPLDX11AccelerationPolicy::on_get_hdl(mfxMemId mid, mfxHDL *handle) { + DX11AllocationRecord::AllocationId data = reinterpret_cast(mid); + if (!data) { + return MFX_ERR_INVALID_HANDLE; + } + + mfxHDLPair *pPair = reinterpret_cast(handle); + + pPair->first = data->get_texture_ptr(); + pPair->second = static_cast(reinterpret_cast( + static_cast(data->get_subresource()))); + + GAPI_LOG_DEBUG(nullptr, "texture : " << pPair->first << ", sub id: " << pPair->second); + return MFX_ERR_NONE; +} + +mfxStatus VPLDX11AccelerationPolicy::on_free(mfxFrameAllocResponse *response) { + GAPI_LOG_DEBUG(nullptr, "Allocations count before: " << allocation_table.size() << + ", requested id: " << response->AllocId); + + auto table_it = allocation_table.find(response->AllocId); + if (allocation_table.end() == table_it) { + GAPI_LOG_WARNING(nullptr, "Cannot find allocation id: " + std::to_string(response->AllocId) + + ". Total allocation size: " + std::to_string(allocation_table.size())); + return MFX_ERR_MEMORY_ALLOC; + } + + allocation_table.erase(table_it); + return MFX_ERR_NONE; } } // namespace onevpl } // namespace wip diff --git a/modules/gapi/src/streaming/onevpl/accelerators/accel_policy_dx11.hpp b/modules/gapi/src/streaming/onevpl/accelerators/accel_policy_dx11.hpp index 946640d886..e053089587 100644 --- a/modules/gapi/src/streaming/onevpl/accelerators/accel_policy_dx11.hpp +++ b/modules/gapi/src/streaming/onevpl/accelerators/accel_policy_dx11.hpp @@ -6,18 +6,14 @@ #ifndef GAPI_STREAMING_ONEVPL_ACCELERATORS_ACCEL_POLICY_DX11_HPP #define GAPI_STREAMING_ONEVPL_ACCELERATORS_ACCEL_POLICY_DX11_HPP +#include #include "opencv2/gapi/own/exports.hpp" // GAPI_EXPORTS -//TODO Remove the next MACRO right after DX11 implementation -#define CPU_ACCEL_ADAPTER #ifdef HAVE_ONEVPL -#include #include "streaming/onevpl/accelerators/accel_policy_interface.hpp" - -#ifdef CPU_ACCEL_ADAPTER -#include "streaming/onevpl/accelerators/accel_policy_cpu.hpp" -#endif +#include "streaming/onevpl/accelerators/surface/surface_pool.hpp" +#include "streaming/onevpl/accelerators/dx11_alloc_resource.hpp" #ifdef HAVE_DIRECTX #ifdef HAVE_D3D11 @@ -39,30 +35,52 @@ namespace onevpl { struct GAPI_EXPORTS VPLDX11AccelerationPolicy final: public VPLAccelerationPolicy { // GAPI_EXPORTS for tests - VPLDX11AccelerationPolicy(); + VPLDX11AccelerationPolicy(device_selector_ptr_t selector); ~VPLDX11AccelerationPolicy(); + using pool_t = CachedPool; + void init(session_t session) override; void deinit(session_t session) override; - pool_key_t create_surface_pool(size_t pool_size, size_t surface_size_bytes, surface_ptr_ctr_t creator) override; + pool_key_t create_surface_pool(const mfxFrameAllocRequest& alloc_request, + mfxVideoParam& param) override; surface_weak_ptr_t get_free_surface(pool_key_t key) override; size_t get_free_surface_count(pool_key_t key) const override; size_t get_surface_count(pool_key_t key) const override; cv::MediaFrame::AdapterPtr create_frame_adapter(pool_key_t key, - mfxFrameSurface1* surface) override; - + mfxFrameSurface1* surface) override; private: ID3D11Device *hw_handle; + ID3D11DeviceContext* device_context; -#ifdef CPU_ACCEL_ADAPTER - std::unique_ptr adapter; -#endif + mfxFrameAllocator allocator; + static mfxStatus MFX_CDECL alloc_cb(mfxHDL pthis, + mfxFrameAllocRequest *request, + mfxFrameAllocResponse *response); + static mfxStatus MFX_CDECL lock_cb(mfxHDL pthis, mfxMemId mid, mfxFrameData *ptr); + static mfxStatus MFX_CDECL unlock_cb(mfxHDL pthis, mfxMemId mid, mfxFrameData *ptr); + static mfxStatus MFX_CDECL get_hdl_cb(mfxHDL pthis, mfxMemId mid, mfxHDL *handle); + static mfxStatus MFX_CDECL free_cb(mfxHDL pthis, mfxFrameAllocResponse *response); + + virtual mfxStatus on_alloc(const mfxFrameAllocRequest *request, + mfxFrameAllocResponse *response); + static mfxStatus on_lock(mfxMemId mid, mfxFrameData *ptr); + static mfxStatus on_unlock(mfxMemId mid, mfxFrameData *ptr); + static mfxStatus on_get_hdl(mfxMemId mid, mfxHDL *handle); + virtual mfxStatus on_free(mfxFrameAllocResponse *response); + + using alloc_id_t = mfxU32; + using allocation_t = std::shared_ptr; + std::map allocation_table; + + std::map pool_table; }; } // namespace onevpl } // namespace wip } // namespace gapi } // namespace cv +#undef NOMINMAX #endif // HAVE_D3D11 #endif // HAVE_DIRECTX diff --git a/modules/gapi/src/streaming/onevpl/accelerators/accel_policy_interface.hpp b/modules/gapi/src/streaming/onevpl/accelerators/accel_policy_interface.hpp index 31ee91535c..a9059c29ef 100644 --- a/modules/gapi/src/streaming/onevpl/accelerators/accel_policy_interface.hpp +++ b/modules/gapi/src/streaming/onevpl/accelerators/accel_policy_interface.hpp @@ -12,9 +12,10 @@ #include #include +#include #ifdef HAVE_ONEVPL -#include +#include "streaming/onevpl/onevpl_export.hpp" namespace cv { namespace gapi { @@ -24,7 +25,10 @@ namespace onevpl { class Surface; struct VPLAccelerationPolicy { - virtual ~VPLAccelerationPolicy() {} + using device_selector_ptr_t = std::shared_ptr; + + VPLAccelerationPolicy(device_selector_ptr_t selector) : device_selector(selector) {} + virtual ~VPLAccelerationPolicy() = default; using pool_key_t = void*; @@ -36,6 +40,13 @@ struct VPLAccelerationPolicy size_t out_buf_ptr_offset, size_t out_buf_ptr_size)>; + device_selector_ptr_t get_device_selector() { + return device_selector; + } + const device_selector_ptr_t get_device_selector() const { + return device_selector; + } + virtual void init(session_t session) = 0; virtual void deinit(session_t session) = 0; @@ -43,7 +54,7 @@ struct VPLAccelerationPolicy // for existing workspace in existing pool (see realloc) // thus it is not implemented, // PLEASE provide initial memory area large enough - virtual pool_key_t create_surface_pool(size_t pool_size, size_t surface_size_bytes, surface_ptr_ctr_t creator) = 0; + virtual pool_key_t create_surface_pool(const mfxFrameAllocRequest& alloc_request, mfxVideoParam& param) = 0; virtual surface_weak_ptr_t get_free_surface(pool_key_t key) = 0; virtual size_t get_free_surface_count(pool_key_t key) const = 0; @@ -51,6 +62,8 @@ struct VPLAccelerationPolicy virtual cv::MediaFrame::AdapterPtr create_frame_adapter(pool_key_t key, mfxFrameSurface1* surface) = 0; +private: + device_selector_ptr_t device_selector; }; } // namespace onevpl } // namespace wip diff --git a/modules/gapi/src/streaming/onevpl/accelerators/dx11_alloc_resource.cpp b/modules/gapi/src/streaming/onevpl/accelerators/dx11_alloc_resource.cpp new file mode 100644 index 0000000000..3bbfb25b0a --- /dev/null +++ b/modules/gapi/src/streaming/onevpl/accelerators/dx11_alloc_resource.cpp @@ -0,0 +1,404 @@ +// This file is part of OpenCV project. +// It is subject to the license terms in the LICENSE file found in the top-level directory +// of this distribution and at http://opencv.org/license.html. +// +// Copyright (C) 2021 Intel Corporation + +#ifdef HAVE_ONEVPL +#include "streaming/onevpl/accelerators/dx11_alloc_resource.hpp" +#include "streaming/onevpl/accelerators/utils/shared_lock.hpp" +#include "logger.hpp" + +#ifdef HAVE_DIRECTX +#ifdef HAVE_D3D11 + +namespace cv { +namespace gapi { +namespace wip { +namespace onevpl { + +LockAdapter::LockAdapter(mfxFrameAllocator origin_allocator) : + lockable_allocator(origin_allocator), + impl() { + GAPI_DbgAssert((lockable_allocator.Lock && lockable_allocator.Unlock) && + "Cannot create LockAdapter for empty origin allocator"); + + // abandon unusable c-allocator interfaces + // because LockAdapter requires Lock & Unlock only + lockable_allocator.Alloc = nullptr; + lockable_allocator.Free = nullptr; + lockable_allocator.pthis = nullptr; +} + +size_t LockAdapter::read_lock(mfxMemId mid, mfxFrameData &data) { + size_t prev_lock_count = 0; + if (impl) { + prev_lock_count = impl->shared_lock(); + } + + // dispatch to VPL allocator using READ access mode + mfxStatus sts = MFX_ERR_LOCK_MEMORY; + try { + sts = lockable_allocator.Lock(nullptr, mid, &data); + } catch(...) { + } + + // adapter will throw error if VPL frame allocator fails + if (sts != MFX_ERR_NONE) { + impl->unlock_shared(); + GAPI_Assert(false && "Cannot lock frame on READ using VPL allocator"); + } + + return prev_lock_count; +} + +size_t LockAdapter::unlock_read(mfxMemId mid, mfxFrameData &data) { + GAPI_DbgAssert(!impl || !is_write_acquired() && + "Reject `unlock_read` in `write_lock` state"); + lockable_allocator.Unlock(nullptr, mid, &data); + return impl ? impl->unlock_shared() : 0; +} + +void LockAdapter::write_lock(mfxMemId mid, mfxFrameData &data) { + if (impl) { + // TODO consider using `try_lock` in loop with limited iteration count + // to prevent dead-lock with WARN at least notification + impl->lock(); + } + + // dispatch to VPL allocator using READ access mode + mfxStatus sts = MFX_ERR_LOCK_MEMORY; + try { + sts = lockable_allocator.Lock(nullptr, mid, &data); + } catch(...) { + } + + // adapter will throw error if VPL frame allocator fails + if (sts != MFX_ERR_NONE) { + impl->unlock(); + GAPI_Assert(false && "Cannot lock frame on WRITE using VPL allocator"); + } +} + +bool LockAdapter::is_write_acquired() { + if(!impl) return true; + return impl->owns(); +} + +void LockAdapter::unlock_write(mfxMemId mid, mfxFrameData &data) { + GAPI_DbgAssert(is_write_acquired() && + "Reject `unlock_write` for unlocked state"); + lockable_allocator.Unlock(nullptr, mid, &data); + if (impl) { + impl->unlock(); + } +} + +SharedLock* LockAdapter::set_adaptee(SharedLock* new_impl) { + SharedLock* old_impl = impl; + impl = new_impl; + return old_impl; +} + +SharedLock* LockAdapter::get_adaptee() { + return impl; +} + +NativeHandleAdapter::NativeHandleAdapter(mfxFrameAllocator origin_allocator) : + native_handle_getter(origin_allocator) { + GAPI_DbgAssert(native_handle_getter.GetHDL && + "Cannot create NativeHandleAdapter for empty origin allocator"); + + // abandon unusable c-allocator interfaces + // because NativeHandleAdapter requires `GetHDL` only + native_handle_getter.Alloc = nullptr; + native_handle_getter.Free = nullptr; + native_handle_getter.Lock = nullptr; + native_handle_getter.Unlock = nullptr; + native_handle_getter.pthis = nullptr; +} + +void NativeHandleAdapter::get_handle(mfxMemId mid, mfxHDL& out) { + if (native_handle_getter.GetHDL(nullptr, mid, &out) != MFX_ERR_NONE) { + GAPI_Assert(nullptr && "Cannot get native handle for resourse by mid"); + } +} + +DX11AllocationItem::DX11AllocationItem(std::weak_ptr parent, + ID3D11DeviceContext* origin_ctx, + mfxFrameAllocator origin_allocator, + ComSharedPtrGuard tex_ptr, + subresource_id_t subtex_id, + ComPtrGuard&& staging_tex_ptr) : + LockAdapter(origin_allocator), + NativeHandleAdapter(origin_allocator), + shared_device_context(origin_ctx), + texture_ptr(tex_ptr), + subresource_id(subtex_id), + staging_texture_ptr(std::move(staging_tex_ptr)), + observer(parent) { + GAPI_DbgAssert(texture_ptr && + "Cannot create DX11AllocationItem for empty texture"); + GAPI_DbgAssert(staging_texture_ptr && + "Cannot create DX11AllocationItem for empty staging texture"); + GAPI_DbgAssert(observer.lock() && + "Cannot create DX11AllocationItem for empty parent"); + + shared_device_context->AddRef(); +} + +DX11AllocationItem::~DX11AllocationItem() { + release(); + observer.reset(); + if (shared_device_context) { + shared_device_context->Release(); + } +} + +void DX11AllocationItem::release() { + auto parent = observer.lock(); + GAPI_LOG_DEBUG(nullptr, "texture: " << texture_ptr << + ", subresource id: " << subresource_id << + ", parent: " << parent.get()); + cv::util::suppress_unused_warning(parent); +} + +ID3D11Texture2D* DX11AllocationItem::get_texture_ptr() { + return texture_ptr.get(); +} + +ID3D11Texture2D* DX11AllocationItem::get_staging_texture_ptr() { + return staging_texture_ptr.get(); +} + +DX11AllocationItem::subresource_id_t DX11AllocationItem::get_subresource() const { + return subresource_id; +} + +ID3D11DeviceContext* DX11AllocationItem::get_device_ctx_ptr() { + return shared_device_context;//shared_device_context.get(); +} + +void DX11AllocationItem::on_first_in_impl(mfxFrameData *ptr) { + D3D11_MAP mapType = D3D11_MAP_READ; + UINT mapFlags = D3D11_MAP_FLAG_DO_NOT_WAIT; + + shared_device_context->CopySubresourceRegion(get_staging_texture_ptr(), 0, + 0, 0, 0, + get_texture_ptr(), + get_subresource(), + nullptr); + HRESULT err = S_OK; + D3D11_MAPPED_SUBRESOURCE lockedRect {}; + do { + err = shared_device_context->Map(get_staging_texture_ptr(), 0, mapType, mapFlags, &lockedRect); + if (S_OK != err && DXGI_ERROR_WAS_STILL_DRAWING != err) { + GAPI_LOG_WARNING(nullptr, "Cannot Map staging texture in device context, error: " << std::to_string(HRESULT_CODE(err))); + GAPI_Assert(false && "Cannot Map staging texture in device context"); + } + } while (DXGI_ERROR_WAS_STILL_DRAWING == err); + + if (FAILED(err)) { + GAPI_LOG_WARNING(nullptr, "Cannot lock frame"); + GAPI_Assert(false && "Cannot lock frame"); + return ; + } + + D3D11_TEXTURE2D_DESC desc {}; + get_texture_ptr()->GetDesc(&desc); + switch (desc.Format) { + case DXGI_FORMAT_NV12: + ptr->Pitch = (mfxU16)lockedRect.RowPitch; + ptr->Y = (mfxU8 *)lockedRect.pData; + ptr->UV = (mfxU8 *)lockedRect.pData + desc.Height * lockedRect.RowPitch; + + GAPI_Assert(ptr->Y && ptr->UV && "DXGI_FORMAT_NV12 locked frame data is nullptr"); + break; + default: + GAPI_LOG_WARNING(nullptr, "Unknown DXGI format: " << desc.Format); + return; + } +} + +void DX11AllocationItem::on_last_out_impl(mfxFrameData *ptr) { + shared_device_context->Unmap(get_staging_texture_ptr(), 0); + if (ptr) { + ptr->Pitch = 0; + ptr->U = ptr->V = ptr->Y = 0; + ptr->A = ptr->R = ptr->G = ptr->B = 0; + } +} + +mfxStatus DX11AllocationItem::acquire_access(mfxFrameData *ptr) { + if (is_write_acquired()) { + return exclusive_access_acquire_unsafe(ptr); + } + return shared_access_acquire_unsafe(ptr); +} + +mfxStatus DX11AllocationItem::release_access(mfxFrameData *ptr) { + if (is_write_acquired()) { + return exclusive_access_release_unsafe(ptr); + } + return shared_access_release_unsafe(ptr); +} + +mfxStatus DX11AllocationItem::shared_access_acquire_unsafe(mfxFrameData *ptr) { + GAPI_LOG_DEBUG(nullptr, "acquire READ lock: " << this); + GAPI_LOG_DEBUG(nullptr, "texture: " << get_texture_ptr() << + ", sub id: " << get_subresource()); + // shared access requires elastic barrier + // first-in visited thread uses resource mapping on host memory + // subsequent threads reuses mapped memory + // + // exclusive access is prohibited while any one shared access has been obtained + visit_in(ptr); + + if (!(ptr->Y && (ptr->UV || (ptr->U && ptr->V)))) { + GAPI_LOG_WARNING(nullptr, "No any data obtained: " << this); + return MFX_ERR_LOCK_MEMORY; + } + GAPI_LOG_DEBUG(nullptr, "READ access granted: " << this); + return MFX_ERR_NONE; +} + +mfxStatus DX11AllocationItem::shared_access_release_unsafe(mfxFrameData *ptr) { + GAPI_LOG_DEBUG(nullptr, "releasing READ lock: " << this); + GAPI_LOG_DEBUG(nullptr, "texture: " << get_texture_ptr() << + ", sub id: " << get_subresource()); + // releasing shared access requires elastic barrier + // last-out thread must make memory unmapping then and only then no more + // read access is coming. If another read-access goes into critical section + // (or waiting for acees) we must drop off unmapping procedure + visit_out(ptr); + + GAPI_LOG_DEBUG(nullptr, "access on READ released: " << this); + return MFX_ERR_NONE; +} + +mfxStatus DX11AllocationItem::exclusive_access_acquire_unsafe(mfxFrameData *ptr) { + GAPI_LOG_DEBUG(nullptr, "acquire WRITE lock: " << this); + GAPI_LOG_DEBUG(nullptr, "texture: " << get_texture_ptr() << + ", sub id: " << get_subresource()); + D3D11_MAP mapType = D3D11_MAP_WRITE; + UINT mapFlags = D3D11_MAP_FLAG_DO_NOT_WAIT; + + HRESULT err = S_OK; + D3D11_MAPPED_SUBRESOURCE lockedRect {}; + do { + err = get_device_ctx_ptr()->Map(get_staging_texture_ptr(), 0, mapType, mapFlags, &lockedRect); + if (S_OK != err && DXGI_ERROR_WAS_STILL_DRAWING != err) { + GAPI_LOG_WARNING(nullptr, "Cannot Map staging texture in device context, error: " << std::to_string(HRESULT_CODE(err))); + return MFX_ERR_LOCK_MEMORY; + } + } while (DXGI_ERROR_WAS_STILL_DRAWING == err); + + if (FAILED(err)) { + GAPI_LOG_WARNING(nullptr, "Cannot lock frame"); + return MFX_ERR_LOCK_MEMORY; + } + + D3D11_TEXTURE2D_DESC desc {}; + get_texture_ptr()->GetDesc(&desc); + switch (desc.Format) { + case DXGI_FORMAT_NV12: + ptr->Pitch = (mfxU16)lockedRect.RowPitch; + ptr->Y = (mfxU8 *)lockedRect.pData; + ptr->UV = (mfxU8 *)lockedRect.pData + desc.Height * lockedRect.RowPitch; + if (!ptr->Y || !ptr->UV) { + GAPI_LOG_WARNING(nullptr, "DXGI_FORMAT_NV12 locked frame data is nullptr"); + return MFX_ERR_LOCK_MEMORY; + } + break; + default: + GAPI_LOG_WARNING(nullptr, "Unknown DXGI format: " << desc.Format); + return MFX_ERR_LOCK_MEMORY; + } + + GAPI_LOG_DEBUG(nullptr, "WRITE access granted: " << this); + return MFX_ERR_NONE; +} + +mfxStatus DX11AllocationItem::exclusive_access_release_unsafe(mfxFrameData *ptr) { + GAPI_LOG_DEBUG(nullptr, "releasing WRITE lock: " << this); + GAPI_LOG_DEBUG(nullptr, "texture: " << get_texture_ptr() << + ", sub id: " << get_subresource()); + + get_device_ctx_ptr()->Unmap(get_staging_texture_ptr(), 0); + + get_device_ctx_ptr()->CopySubresourceRegion(get_texture_ptr(), + get_subresource(), + 0, 0, 0, + get_staging_texture_ptr(), 0, + nullptr); + + if (ptr) { + ptr->Pitch = 0; + ptr->U = ptr->V = ptr->Y = 0; + ptr->A = ptr->R = ptr->G = ptr->B = 0; + } + GAPI_LOG_DEBUG(nullptr, "access on WRITE released: " << this); + return MFX_ERR_NONE; +} + +DX11AllocationRecord::DX11AllocationRecord() = default; + +DX11AllocationRecord::~DX11AllocationRecord() { + GAPI_LOG_DEBUG(nullptr, "record: " << this << + ", subresources count: " << resources.size()); + + for (AllocationId id : resources) { + delete id; + } + resources.clear(); + + GAPI_LOG_DEBUG(nullptr, "release final referenced texture: " << texture_ptr.get()); +} + +void DX11AllocationRecord::init(unsigned int items, + ID3D11DeviceContext* origin_ctx, + mfxFrameAllocator origin_allocator, + ComPtrGuard&& texture, + std::vector> &&staging_textures) { + GAPI_DbgAssert(items != 0 && "Cannot create DX11AllocationRecord with empty items"); + GAPI_DbgAssert(items == staging_textures.size() && "Allocation items count and staging size are not equal"); + GAPI_DbgAssert(origin_ctx && + "Cannot create DX11AllocationItem for empty origin_ctx"); + auto shared_allocator_copy = origin_allocator; + GAPI_DbgAssert((shared_allocator_copy.Lock && shared_allocator_copy.Unlock) && + "Cannot create DX11AllocationItem for empty origin allocator"); + + // abandon unusable c-allocator interfaces + shared_allocator_copy.Alloc = nullptr; + shared_allocator_copy.Free = nullptr; + shared_allocator_copy.pthis = nullptr; + + + GAPI_LOG_DEBUG(nullptr, "subresources count: " << items << ", text: " << texture.get()); + resources.reserve(items); + // no AddRef here, because DX11AllocationRecord receive ownership it here + texture_ptr = createCOMSharedPtrGuard(std::move(texture)); + for(unsigned int i = 0; i < items; i++) { + resources.emplace_back(new DX11AllocationItem(get_ptr(), origin_ctx, shared_allocator_copy, + texture_ptr, i, std::move(staging_textures[i]))); + } +} + +DX11AllocationRecord::Ptr DX11AllocationRecord::get_ptr() { + return shared_from_this(); +} + +DX11AllocationRecord::AllocationId* DX11AllocationRecord::data() { + return resources.data(); +} + +size_t DX11AllocationRecord::size() const { + return resources.size(); +} +} // namespace onevpl +} // namespace wip +} // namespace gapi +} // namespace cv +#endif // HAVE_D3D11 +#endif // HAVE_DIRECTX +#endif // HAVE_ONEVPL diff --git a/modules/gapi/src/streaming/onevpl/accelerators/dx11_alloc_resource.hpp b/modules/gapi/src/streaming/onevpl/accelerators/dx11_alloc_resource.hpp new file mode 100644 index 0000000000..46ddff86a4 --- /dev/null +++ b/modules/gapi/src/streaming/onevpl/accelerators/dx11_alloc_resource.hpp @@ -0,0 +1,151 @@ +#ifndef GAPI_STREAMING_ONEVPL_ACCEL_DX11_ALLOC_RESOURCE_HPP +#define GAPI_STREAMING_ONEVPL_ACCEL_DX11_ALLOC_RESOURCE_HPP + +#include + +#include "opencv2/gapi/own/exports.hpp" // GAPI_EXPORTS +#include + +#ifdef HAVE_ONEVPL +#include "streaming/onevpl/onevpl_export.hpp" +#include "streaming/onevpl/accelerators/utils/elastic_barrier.hpp" +#include "streaming/onevpl/utils.hpp" + +#ifdef HAVE_DIRECTX +#ifdef HAVE_D3D11 +#pragma comment(lib,"d3d11.lib") + +#define D3D11_NO_HELPERS +#define NOMINMAX +#include +#include +#include +#include "opencv2/core/directx.hpp" +#ifdef HAVE_OPENCL +#include +#endif // HAVE_OPENCL +#undef D3D11_NO_HELPERS +#undef NOMINMAX + +namespace cv { +namespace gapi { +namespace wip { +namespace onevpl { + +class SharedLock; +// GAPI_EXPORTS for tests +struct GAPI_EXPORTS LockAdapter { + LockAdapter(mfxFrameAllocator origin_allocator); + + size_t read_lock(mfxMemId mid, mfxFrameData &data); + size_t unlock_read(mfxMemId mid, mfxFrameData &data); + + void write_lock(mfxMemId mid, mfxFrameData &data); + bool is_write_acquired(); + void unlock_write(mfxMemId mid, mfxFrameData &data); + + SharedLock* set_adaptee(SharedLock* new_impl); + SharedLock* get_adaptee(); +private: + LockAdapter(const LockAdapter&) = delete; + LockAdapter(LockAdapter&&) = delete; + LockAdapter& operator= (const LockAdapter&) = delete; + LockAdapter& operator= (LockAdapter&&) = delete; + + mfxFrameAllocator lockable_allocator; + SharedLock* impl; +}; + +struct GAPI_EXPORTS NativeHandleAdapter { + NativeHandleAdapter(mfxFrameAllocator origin_allocator); + + void get_handle(mfxMemId mid, mfxHDL& out); +private: + mfxFrameAllocator native_handle_getter; +}; + +struct DX11AllocationRecord; +struct DX11AllocationItem : public LockAdapter, + public NativeHandleAdapter, + public elastic_barrier { + using subresource_id_t = unsigned int; + + friend struct DX11AllocationRecord; + friend class elastic_barrier; + ~DX11AllocationItem(); + + void release(); + ID3D11Texture2D* get_texture_ptr(); + ID3D11Texture2D* get_staging_texture_ptr(); + DX11AllocationItem::subresource_id_t get_subresource() const; + + ID3D11DeviceContext* get_device_ctx_ptr(); + + // public transactional access to resources. + // implements dispatching through different access acquisition modes. + // current acquisition mode determined by `LockAdapter` with `is_write_acquired()` + mfxStatus acquire_access(mfxFrameData *ptr); + mfxStatus release_access(mfxFrameData *ptr); +private: + DX11AllocationItem(std::weak_ptr parent, + ID3D11DeviceContext* origin_ctx, + mfxFrameAllocator origin_allocator, + ComSharedPtrGuard texture_ptr, + subresource_id_t subresource_id, + ComPtrGuard&& staging_tex_ptr); + + // elastic barrier interface impl + void on_first_in_impl(mfxFrameData *ptr); + void on_last_out_impl(mfxFrameData *ptr); + + mfxStatus shared_access_acquire_unsafe(mfxFrameData *ptr); + mfxStatus shared_access_release_unsafe(mfxFrameData *ptr); + mfxStatus exclusive_access_acquire_unsafe(mfxFrameData *ptr); + mfxStatus exclusive_access_release_unsafe(mfxFrameData *ptr); + + ID3D11DeviceContext* shared_device_context; + + ComSharedPtrGuard texture_ptr; + subresource_id_t subresource_id = 0; + ComPtrGuard staging_texture_ptr; + std::weak_ptr observer; +}; + +struct DX11AllocationRecord : public std::enable_shared_from_this { + + using Ptr = std::shared_ptr; + + ~DX11AllocationRecord(); + + template + static Ptr create(Args&& ...args) { + std::shared_ptr record(new DX11AllocationRecord); + record->init(std::forward(args)...); + return record; + } + + Ptr get_ptr(); + + // Raw ptr is required as a part of VPL `Mid` c-interface + // which requires contiguous memory + using AllocationId = DX11AllocationItem*; + AllocationId* data(); + size_t size() const; +private: + DX11AllocationRecord(); + void init(unsigned int items, ID3D11DeviceContext* origin_ctx, + mfxFrameAllocator origin_allocator, + ComPtrGuard&& texture, std::vector> &&staging_textures); + + std::vector resources; + ComSharedPtrGuard texture_ptr; +}; + +} // namespace onevpl +} // namespace wip +} // namespace gapi +} // namespace cv +#endif // HAVE_D3D11 +#endif // HAVE_DIRECTX +#endif // HAVE_ONEVPL +#endif // GAPI_STREAMING_ONEVPL_ACCEL_DX11_ALLOC_RESOURCE_HPP diff --git a/modules/gapi/src/streaming/onevpl/accelerators/surface/cpu_frame_adapter.cpp b/modules/gapi/src/streaming/onevpl/accelerators/surface/cpu_frame_adapter.cpp index d3020ab168..39094c9bc3 100644 --- a/modules/gapi/src/streaming/onevpl/accelerators/surface/cpu_frame_adapter.cpp +++ b/modules/gapi/src/streaming/onevpl/accelerators/surface/cpu_frame_adapter.cpp @@ -9,12 +9,7 @@ #include "logger.hpp" #ifdef HAVE_ONEVPL - -#if (MFX_VERSION >= 2000) -#include -#endif - -#include +#include "streaming/onevpl/onevpl_export.hpp" namespace cv { namespace gapi { @@ -25,24 +20,10 @@ VPLMediaFrameCPUAdapter::VPLMediaFrameCPUAdapter(std::shared_ptr surfac parent_surface_ptr(surface) { GAPI_Assert(parent_surface_ptr && "Surface is nullptr"); - parent_surface_ptr->obtain_lock(); - GAPI_LOG_DEBUG(nullptr, "surface: " << parent_surface_ptr->get_handle() << ", w: " << parent_surface_ptr->get_info().Width << ", h: " << parent_surface_ptr->get_info().Height << ", p: " << parent_surface_ptr->get_data().Pitch); -} - -VPLMediaFrameCPUAdapter::~VPLMediaFrameCPUAdapter() { - - // Each VPLMediaFrameCPUAdapter releases mfx surface counter - // The last VPLMediaFrameCPUAdapter releases shared Surface pointer - // The last surface pointer releases workspace memory - parent_surface_ptr->release_lock(); -} - -cv::GFrameDesc VPLMediaFrameCPUAdapter::meta() const { - GFrameDesc desc; const Surface::info_t& info = parent_surface_ptr->get_info(); switch(info.FourCC) { @@ -50,14 +31,26 @@ cv::GFrameDesc VPLMediaFrameCPUAdapter::meta() const { throw std::runtime_error("MediaFrame doesn't support I420 type"); break; case MFX_FOURCC_NV12: - desc.fmt = MediaFormat::NV12; + frame_desc.fmt = MediaFormat::NV12; break; default: throw std::runtime_error("MediaFrame unknown 'fmt' type: " + std::to_string(info.FourCC)); } - desc.size = cv::Size{info.Width, info.Height}; - return desc; + frame_desc.size = cv::Size{info.Width, info.Height}; + parent_surface_ptr->obtain_lock(); +} + +VPLMediaFrameCPUAdapter::~VPLMediaFrameCPUAdapter() { + + // Each VPLMediaFrameCPUAdapter releases mfx surface counter + // The last VPLMediaFrameCPUAdapter releases shared Surface pointer + // The last surface pointer releases workspace memory + parent_surface_ptr->release_lock(); +} + +cv::GFrameDesc VPLMediaFrameCPUAdapter::meta() const { + return frame_desc; } MediaFrame::View VPLMediaFrameCPUAdapter::access(MediaFrame::Access) { diff --git a/modules/gapi/src/streaming/onevpl/accelerators/surface/cpu_frame_adapter.hpp b/modules/gapi/src/streaming/onevpl/accelerators/surface/cpu_frame_adapter.hpp index 04a9bdc275..1c51ad7473 100644 --- a/modules/gapi/src/streaming/onevpl/accelerators/surface/cpu_frame_adapter.hpp +++ b/modules/gapi/src/streaming/onevpl/accelerators/surface/cpu_frame_adapter.hpp @@ -33,6 +33,7 @@ public: void deserialize(cv::gapi::s11n::IIStream&) override; private: std::shared_ptr parent_surface_ptr; + GFrameDesc frame_desc; }; } // namespace onevpl } // namespace wip diff --git a/modules/gapi/src/streaming/onevpl/accelerators/surface/dx11_frame_adapter.cpp b/modules/gapi/src/streaming/onevpl/accelerators/surface/dx11_frame_adapter.cpp new file mode 100644 index 0000000000..04cf10c8d7 --- /dev/null +++ b/modules/gapi/src/streaming/onevpl/accelerators/surface/dx11_frame_adapter.cpp @@ -0,0 +1,232 @@ +// This file is part of OpenCV project. +// It is subject to the license terms in the LICENSE file found in the top-level directory +// of this distribution and at http://opencv.org/license.html. +// +// Copyright (C) 2021 Intel Corporation + +#include "streaming/onevpl/accelerators/surface/dx11_frame_adapter.hpp" +#include "streaming/onevpl/accelerators/dx11_alloc_resource.hpp" +#include "streaming/onevpl/accelerators/surface/surface.hpp" +#include "logger.hpp" + +#ifdef HAVE_ONEVPL +#include "streaming/onevpl/onevpl_export.hpp" + +#ifdef HAVE_INF_ENGINE +// For IE classes (ParamMap, etc) +#include +#endif // HAVE_INF_ENGINE + +namespace cv { +namespace gapi { +namespace wip { +namespace onevpl { + +void lock_mid(mfxMemId mid, mfxFrameData &data, MediaFrame::Access mode) { + LockAdapter* alloc_data = reinterpret_cast(mid); + if (mode == MediaFrame::Access::R) { + alloc_data->read_lock(mid, data); + } else { + alloc_data->write_lock(mid, data); + } +} + +void unlock_mid(mfxMemId mid, mfxFrameData &data, MediaFrame::Access mode) { + LockAdapter* alloc_data = reinterpret_cast(data.MemId); + if (mode == MediaFrame::Access::R) { + alloc_data->unlock_read(mid, data); + } else { + alloc_data->unlock_write(mid, data); + } +} + +VPLMediaFrameDX11Adapter::VPLMediaFrameDX11Adapter(std::shared_ptr surface): + parent_surface_ptr(surface) { + GAPI_Assert(parent_surface_ptr && "Surface is nullptr"); + + const Surface::info_t& info = parent_surface_ptr->get_info(); + Surface::data_t& data = parent_surface_ptr->get_data(); + GAPI_LOG_DEBUG(nullptr, "surface: " << parent_surface_ptr->get_handle() << + ", w: " << info.Width << ", h: " << info.Height << + ", p: " << data.Pitch); + switch(info.FourCC) + { + case MFX_FOURCC_I420: + throw std::runtime_error("MediaFrame doesn't support I420 type"); + break; + case MFX_FOURCC_NV12: + frame_desc.fmt = MediaFormat::NV12; + break; + default: + throw std::runtime_error("MediaFrame unknown 'fmt' type: " + std::to_string(info.FourCC)); + } + frame_desc.size = cv::Size{info.Width, info.Height}; + + LockAdapter* alloc_data = reinterpret_cast(data.MemId); + alloc_data->set_adaptee(this); + + parent_surface_ptr->obtain_lock(); +} + +VPLMediaFrameDX11Adapter::~VPLMediaFrameDX11Adapter() { + // Each VPLMediaFrameDX11Adapter releases mfx surface counter + // The last VPLMediaFrameDX11Adapter releases shared Surface pointer + // The last surface pointer releases workspace memory + Surface::data_t& data = parent_surface_ptr->get_data(); + LockAdapter* alloc_data = reinterpret_cast(data.MemId); + alloc_data->set_adaptee(nullptr); + + parent_surface_ptr->release_lock(); +} + +cv::GFrameDesc VPLMediaFrameDX11Adapter::meta() const { + return frame_desc; +} + +MediaFrame::View VPLMediaFrameDX11Adapter::access(MediaFrame::Access mode) { + Surface::data_t& data = parent_surface_ptr->get_data(); + const Surface::info_t& info = parent_surface_ptr->get_info(); + void* frame_id = reinterpret_cast(this); + + GAPI_LOG_DEBUG(nullptr, "START lock frame in surface: " << parent_surface_ptr->get_handle() << + ", frame id: " << frame_id); + + // lock MT + lock_mid(data.MemId, data, mode); + + GAPI_LOG_DEBUG(nullptr, "FINISH lock frame in surface: " << parent_surface_ptr->get_handle() << + ", frame id: " << frame_id); + using stride_t = typename cv::MediaFrame::View::Strides::value_type; + stride_t pitch = static_cast(data.Pitch); + + // NB: make copy for some copyable object, because access release may be happened + // after source/pool destruction, so we need a copy + auto parent_surface_ptr_copy = parent_surface_ptr; + switch(info.FourCC) { + case MFX_FOURCC_I420: + { + GAPI_Assert(data.Y && data.U && data.V && "MFX_FOURCC_I420 frame data is nullptr"); + cv::MediaFrame::View::Ptrs pp = { data.Y, data.U, data.V, nullptr }; + cv::MediaFrame::View::Strides ss = { pitch, pitch / 2, pitch / 2, 0u }; + return cv::MediaFrame::View(std::move(pp), std::move(ss), + [parent_surface_ptr_copy, + frame_id, mode] () { + parent_surface_ptr_copy->obtain_lock(); + + auto& data = parent_surface_ptr_copy->get_data(); + GAPI_LOG_DEBUG(nullptr, "START unlock frame in surface: " << parent_surface_ptr_copy->get_handle() << + ", frame id: " << frame_id); + unlock_mid(data.MemId, data, mode); + + GAPI_LOG_DEBUG(nullptr, "FINISH unlock frame in surface: " << parent_surface_ptr_copy->get_handle() << + ", frame id: " << frame_id); + + parent_surface_ptr_copy->release_lock(); + }); + } + case MFX_FOURCC_NV12: + { + if (!data.Y || !data.UV) { + GAPI_LOG_WARNING(nullptr, "Empty data detected!!! for surface: " << parent_surface_ptr->get_handle() << + ", frame id: " << frame_id); + } + GAPI_Assert(data.Y && data.UV && "MFX_FOURCC_NV12 frame data is nullptr"); + cv::MediaFrame::View::Ptrs pp = { data.Y, data.UV, nullptr, nullptr }; + cv::MediaFrame::View::Strides ss = { pitch, pitch, 0u, 0u }; + return cv::MediaFrame::View(std::move(pp), std::move(ss), + [parent_surface_ptr_copy, + frame_id, mode] () { + parent_surface_ptr_copy->obtain_lock(); + + auto& data = parent_surface_ptr_copy->get_data(); + GAPI_LOG_DEBUG(nullptr, "START unlock frame in surface: " << parent_surface_ptr_copy->get_handle() << + ", frame id: " << frame_id); + unlock_mid(data.MemId, data, mode); + + GAPI_LOG_DEBUG(nullptr, "FINISH unlock frame in surface: " << parent_surface_ptr_copy->get_handle() << + ", frame id: " << frame_id); + parent_surface_ptr_copy->release_lock(); + }); + } + break; + default: + throw std::runtime_error("MediaFrame unknown 'fmt' type: " + std::to_string(info.FourCC)); + } +} + +cv::util::any VPLMediaFrameDX11Adapter::blobParams() const { +#ifdef HAVE_INF_ENGINE + GAPI_Assert(false && "VPLMediaFrameDX11Adapter::blobParams() is not fully operable " + "in G-API streaming. Please waiting for future PRs"); + + Surface::data_t& data = parent_surface_ptr->get_data(); + NativeHandleAdapter* native_handle_getter = reinterpret_cast(data.MemId); + + mfxHDLPair handle{}; + native_handle_getter->get_handle(data.MemId, reinterpret_cast(handle)); + + InferenceEngine::ParamMap params{{"SHARED_MEM_TYPE", "VA_SURFACE"}, + {"DEV_OBJECT_HANDLE", handle.first}, + {"COLOR_FORMAT", InferenceEngine::ColorFormat::NV12}, + {"VA_PLANE", + static_cast( + reinterpret_cast( + reinterpret_cast( + handle.second)))}};//, + const Surface::info_t& info = parent_surface_ptr->get_info(); + InferenceEngine::TensorDesc tdesc({InferenceEngine::Precision::U8, + {1, 3, static_cast(info.Height), + static_cast(info.Width)}, + InferenceEngine::Layout::NCHW}); + return std::make_pair(tdesc, params); +#else + GAPI_Assert(false && "VPLMediaFrameDX11Adapter::blobParams() is not implemented"); +#endif // HAVE_INF_ENGINE +} + +void VPLMediaFrameDX11Adapter::serialize(cv::gapi::s11n::IOStream&) { + GAPI_Assert(false && "VPLMediaFrameDX11Adapter::serialize() is not implemented"); +} + +void VPLMediaFrameDX11Adapter::deserialize(cv::gapi::s11n::IIStream&) { + GAPI_Assert(false && "VPLMediaFrameDX11Adapter::deserialize() is not implemented"); +} + +DXGI_FORMAT VPLMediaFrameDX11Adapter::get_dx11_color_format(uint32_t mfx_fourcc) { + switch (mfx_fourcc) { + case MFX_FOURCC_NV12: + return DXGI_FORMAT_NV12; + + case MFX_FOURCC_YUY2: + return DXGI_FORMAT_YUY2; + + case MFX_FOURCC_RGB4: + return DXGI_FORMAT_B8G8R8A8_UNORM; + + case MFX_FOURCC_P8: + case MFX_FOURCC_P8_TEXTURE: + return DXGI_FORMAT_P8; + + case MFX_FOURCC_ARGB16: + case MFX_FOURCC_ABGR16: + return DXGI_FORMAT_R16G16B16A16_UNORM; + + case MFX_FOURCC_P010: + return DXGI_FORMAT_P010; + + case MFX_FOURCC_A2RGB10: + return DXGI_FORMAT_R10G10B10A2_UNORM; + + case DXGI_FORMAT_AYUV: + case MFX_FOURCC_AYUV: + return DXGI_FORMAT_AYUV; + + default: + return DXGI_FORMAT_UNKNOWN; + } +} +} // namespace onevpl +} // namespace wip +} // namespace gapi +} // namespace cv +#endif // HAVE_ONEVPL diff --git a/modules/gapi/src/streaming/onevpl/accelerators/surface/dx11_frame_adapter.hpp b/modules/gapi/src/streaming/onevpl/accelerators/surface/dx11_frame_adapter.hpp new file mode 100644 index 0000000000..ca6602353b --- /dev/null +++ b/modules/gapi/src/streaming/onevpl/accelerators/surface/dx11_frame_adapter.hpp @@ -0,0 +1,63 @@ +// This file is part of OpenCV project. +// It is subject to the license terms in the LICENSE file found in the top-level directory +// of this distribution and at http://opencv.org/license.html. +// +// Copyright (C) 2021 Intel Corporation + +#ifndef GAPI_STREAMING_ONEVPL_ACCELERATORS_SURFACE_DX11_FRAME_ADAPTER_HPP +#define GAPI_STREAMING_ONEVPL_ACCELERATORS_SURFACE_DX11_FRAME_ADAPTER_HPP +#include + +#include +#include "opencv2/gapi/own/exports.hpp" // GAPI_EXPORTS + +#include "streaming/onevpl/accelerators/utils/shared_lock.hpp" +#ifdef HAVE_ONEVPL +#include "streaming/onevpl/onevpl_export.hpp" + +#ifdef HAVE_DIRECTX +#ifdef HAVE_D3D11 + #define D3D11_NO_HELPERS + #define NOMINMAX + #include + #include + #include "opencv2/core/directx.hpp" + #ifdef HAVE_OPENCL + #include + #endif + +namespace cv { +namespace gapi { +namespace wip { +namespace onevpl { + +class Surface; +class VPLMediaFrameDX11Adapter final: public cv::MediaFrame::IAdapter, + public SharedLock { +public: + // GAPI_EXPORTS for tests + GAPI_EXPORTS VPLMediaFrameDX11Adapter(std::shared_ptr assoc_surface); + GAPI_EXPORTS ~VPLMediaFrameDX11Adapter(); + cv::GFrameDesc meta() const override; + MediaFrame::View access(MediaFrame::Access) override; + + // The default implementation does nothing + cv::util::any blobParams() const override; + void serialize(cv::gapi::s11n::IOStream&) override; + void deserialize(cv::gapi::s11n::IIStream&) override; + + static DXGI_FORMAT get_dx11_color_format(uint32_t mfx_fourcc); +private: + std::shared_ptr parent_surface_ptr; + mfxFrameAllocator allocator; + GFrameDesc frame_desc; +}; +} // namespace onevpl +} // namespace wip +} // namespace gapi +} // namespace cv +#undef NOMINMAX +#endif // HAVE_D3D11 +#endif // HAVE_DIRECTX +#endif // HAVE_ONEVPL +#endif // GAPI_STREAMING_ONEVPL_ACCELERATORS_SURFACE_DX11_FRAME_ADAPTER_HPP diff --git a/modules/gapi/src/streaming/onevpl/accelerators/surface/surface.cpp b/modules/gapi/src/streaming/onevpl/accelerators/surface/surface.cpp index 3f5fd00305..c09dc80338 100644 --- a/modules/gapi/src/streaming/onevpl/accelerators/surface/surface.cpp +++ b/modules/gapi/src/streaming/onevpl/accelerators/surface/surface.cpp @@ -20,18 +20,20 @@ Surface::Surface(std::unique_ptr&& surf, std::shared_ptr associa mirrored_locked_count() { GAPI_Assert(mfx_surface && "Surface is nullptr"); - mirrored_locked_count.store(mfx_surface->Data.Locked); GAPI_LOG_DEBUG(nullptr, "create surface: " << mfx_surface << ", locked count: " << mfx_surface->Data.Locked); } Surface::~Surface() { GAPI_LOG_DEBUG(nullptr, "destroy surface: " << mfx_surface << - ", worspace memory counter: " << workspace_memory_ptr.use_count()); + ", worspace memory counter: " << + workspace_memory_ptr.use_count()); } std::shared_ptr Surface::create_surface(std::unique_ptr&& surf, std::shared_ptr accociated_memory) { + Surface::info_t& info = surf->Info; + info.FourCC = MFX_FOURCC_NV12; surface_ptr_t ret {new Surface(std::move(surf), accociated_memory)}; return ret; } @@ -48,14 +50,16 @@ const Surface::data_t& Surface::get_data() const { return mfx_surface->Data; } +Surface::data_t& Surface::get_data() { + return const_cast(static_cast(this)->get_data()); +} + size_t Surface::get_locks_count() const { - return mirrored_locked_count.load(); + return mirrored_locked_count.load() + mfx_surface->Data.Locked; } size_t Surface::obtain_lock() { size_t locked_count = mirrored_locked_count.fetch_add(1); - GAPI_Assert(locked_count < std::numeric_limits::max() && "Too many references "); - mfx_surface->Data.Locked = static_cast(locked_count + 1); GAPI_LOG_DEBUG(nullptr, "surface: " << mfx_surface.get() << ", locked times: " << locked_count + 1); return locked_count; // return preceding value @@ -63,9 +67,7 @@ size_t Surface::obtain_lock() { size_t Surface::release_lock() { size_t locked_count = mirrored_locked_count.fetch_sub(1); - GAPI_Assert(locked_count < std::numeric_limits::max() && "Too many references "); GAPI_Assert(locked_count && "Surface lock counter is invalid"); - mfx_surface->Data.Locked = static_cast(locked_count - 1); GAPI_LOG_DEBUG(nullptr, "surface: " << mfx_surface.get() << ", locked times: " << locked_count - 1); return locked_count; // return preceding value diff --git a/modules/gapi/src/streaming/onevpl/accelerators/surface/surface.hpp b/modules/gapi/src/streaming/onevpl/accelerators/surface/surface.hpp index 828e5cb1c7..4f93312e24 100644 --- a/modules/gapi/src/streaming/onevpl/accelerators/surface/surface.hpp +++ b/modules/gapi/src/streaming/onevpl/accelerators/surface/surface.hpp @@ -13,12 +13,7 @@ #include "opencv2/gapi/own/exports.hpp" // GAPI_EXPORTS #ifdef HAVE_ONEVPL -#if (MFX_VERSION >= 2000) -#include -#endif - -#include - +#include "streaming/onevpl/onevpl_export.hpp" namespace cv { namespace gapi { @@ -45,24 +40,21 @@ namespace onevpl { * - @ref Surface::obtain_lock() against @ref Surface::release_lock() * - @ref Surface::release_lock() against @ref Surface::release_lock() */ -class Surface { - using handle_t = mfxFrameSurface1; - - std::shared_ptr workspace_memory_ptr; - std::unique_ptr mfx_surface; - std::atomic mirrored_locked_count; +class GAPI_EXPORTS Surface final { // GAPI_EXPORTS for tests public: + using handle_t = mfxFrameSurface1; using info_t = mfxFrameInfo; using data_t = mfxFrameData; - // GAPI_EXPORTS for tests - GAPI_EXPORTS static std::shared_ptr create_surface(std::unique_ptr&& surf, - std::shared_ptr accociated_memory); - GAPI_EXPORTS ~Surface(); - GAPI_EXPORTS handle_t* get_handle() const; - GAPI_EXPORTS const info_t& get_info() const; - GAPI_EXPORTS const data_t& get_data() const; + static std::shared_ptr create_surface(std::unique_ptr&& surf, + std::shared_ptr accociated_memory); + ~Surface(); + + handle_t* get_handle() const; + const info_t& get_info() const; + const data_t& get_data() const; + data_t& get_data(); /** * Extract value thread-safe lock counter (see @ref Surface description). @@ -71,7 +63,7 @@ public: * * @return fetched locks count. */ - GAPI_EXPORTS size_t get_locks_count() const; + size_t get_locks_count() const; /** * Atomically increase value of thread-safe lock counter (see @ref Surface description). @@ -80,7 +72,7 @@ public: * * @return locks count just before its increasing. */ - GAPI_EXPORTS size_t obtain_lock(); + size_t obtain_lock(); /** * Atomically decrease value of thread-safe lock counter (see @ref Surface description). @@ -89,9 +81,13 @@ public: * * @return locks count just before its decreasing. */ - GAPI_EXPORTS size_t release_lock(); + size_t release_lock(); private: Surface(std::unique_ptr&& surf, std::shared_ptr accociated_memory); + + std::shared_ptr workspace_memory_ptr; + std::unique_ptr mfx_surface; + std::atomic mirrored_locked_count; }; using surface_ptr_t = std::shared_ptr; diff --git a/modules/gapi/src/streaming/onevpl/accelerators/surface/surface_pool.hpp b/modules/gapi/src/streaming/onevpl/accelerators/surface/surface_pool.hpp index a1b8d192bc..a5084a81ea 100644 --- a/modules/gapi/src/streaming/onevpl/accelerators/surface/surface_pool.hpp +++ b/modules/gapi/src/streaming/onevpl/accelerators/surface/surface_pool.hpp @@ -8,11 +8,7 @@ #include "opencv2/gapi/own/exports.hpp" // GAPI_EXPORTS #ifdef HAVE_ONEVPL -#if (MFX_VERSION >= 2000) -#include -#endif - -#include +#include "streaming/onevpl/onevpl_export.hpp" namespace cv { namespace gapi { diff --git a/modules/gapi/src/streaming/onevpl/accelerators/utils/elastic_barrier.hpp b/modules/gapi/src/streaming/onevpl/accelerators/utils/elastic_barrier.hpp new file mode 100644 index 0000000000..827392f8be --- /dev/null +++ b/modules/gapi/src/streaming/onevpl/accelerators/utils/elastic_barrier.hpp @@ -0,0 +1,296 @@ +// This file is part of OpenCV project. +// It is subject to the license terms in the LICENSE file found in the top-level directory +// of this distribution and at http://opencv.org/license.html. +// +// Copyright (C) 2021 Intel Corporation + +#ifndef GAPI_STREAMING_ONEVPL_ACCELERATORS_UTILS_ELASTIC_BARRIER_HPP +#define GAPI_STREAMING_ONEVPL_ACCELERATORS_UTILS_ELASTIC_BARRIER_HPP +#include + +namespace cv { +namespace gapi { +namespace wip { +namespace onevpl { + +template +class elastic_barrier { +public: + using self_t = Impl; + elastic_barrier() : + incoming_requests(), + outgoing_requests(), + pending_requests(), + reinit(false) { + } + + self_t* get_self() { + return static_cast(this); + } + + template + void visit_in (Args&& ...args) { + on_lock(std::forward(args)...); + } + + template + void visit_out (Args&& ...args) { + on_unlock(std::forward(args)...); + } + +protected: + ~elastic_barrier() = default; + +private: + std::atomic incoming_requests; + std::atomic outgoing_requests; + std::atomic pending_requests; + std::atomic reinit; + + template + void on_first_in(Args&& ...args) { + get_self()->on_first_in_impl(std::forward(args)...); + } + + template + void on_last_out(Args&& ...args) { + get_self()->on_last_out_impl(std::forward(args)...); + } + + template + void on_lock(Args&& ...args) { + // Read access is more complex + // each `incoming` request must check in before acquire resource + size_t thread_id = incoming_requests.fetch_add(1); + if (thread_id == 0) { + /* + * only one `incoming` request is allowable to init resource + * at first time + * let's filter out the first one by `thread_id` + * + * The first one `incoming` request becomes main `incoming` request + * */ + if (outgoing_requests.load() == 0) { + get_self()->on_first_in(std::forward(args)...); + /* + * The main `incoming` request finished resource initialization + * and became `outgoing` + * + * Non empty `outgoing` count means that + * other further `incoming` (or busy-wait) requests + * are getting on with its job without resource initialization, + * because main `incoming` request has already initialized it at here + * */ + outgoing_requests.fetch_add(1); + return; + } + return; + } else { + /* + * CASE 1) + * + * busy wait for others `incoming` requests for resource initialization + * besides main `incoming` request which are getting on + * resource initialization at this point + * + * */ + + // OR + + /* + * CASE 2) + * + * busy wait for ALL `incoming` request for resource initialization + * including main `incoming` request. It will happen if + * new `incoming` requests had came here while resource was getting on deinit + * in `on_unlock` in another processing thread. + * In this case no actual main `incoming` request is available and + * all `incoming` requests must be in busy-wait stare + * + * */ + + // Each `incoming` request became `busy-wait` request + size_t busy_thread_id = pending_requests.fetch_add(1); + + /* + * CASE 1) + * + * Non empty `outgoing` requests count means that other further `incoming` or + * `busy-wait` request are getting on with its job + * without resource initialization because + * main thread has already initialized it at here + * */ + while (outgoing_requests.load() == 0) { + + // OR + + /* + * CASE 2) + * + * In case of NO master `incoming `request is available and doesn't + * provide resource initialization. All `incoming` requests must be in + * busy-wait state. + * If it is not true then CASE 1) is going on + * + * OR + * + * `on_unlock` is in deinitialization phase in another thread. + * Both cases mean busy-wait state here + * */ + if (pending_requests.load() == incoming_requests.load()) { + /* + * CASE 2) ONLY + * + * It will happen if 'on_unlock` in another thread + * finishes its execution only + * + * `on_unlock` in another thread might finished with either + * deinitialization action or without deinitialization action + * (the call off deinitialization case) + * + * We must not continue at here (without reinit) + * if deinitialization happens in `on_unlock` in another thread. + * So try it on + * */ + + // only single `busy-wait` request must make sure about possible + // deinitialization. So first `busy-wait` request becomes + // main `busy-wait` request + if (busy_thread_id == 0) { + bool expected_reinit = true; + if (!reinit.compare_exchange_strong(expected_reinit, false)) { + /* + * deinitialization called off in `on_unlock` + * because new `incoming` request had appeared at here before + * `on_unlock` started deinit procedure in another thread. + * So no reinit required because no deinit had happended + * + * main `busy-wait` request must break busy-wait state + * and become `outgoing` request. + * Non empty `outgoing` count means that other + * further `incoming` requests or + * `busy-wait` requests are getting on with its job + * without resource initialization/reinitialization + * because no deinit happened in `on_unlock` + * in another thread + * */ + break; //just quit busy loop + } else { + /* Deinitialization had happened in `on_unlock` + * in another thread right before + * new `incoming` requests appeared. + * So main `busy-wait` request must start reinit procedure + */ + get_self()->on_first_in(std::forward(args)...); + + /* + * Main `busy-wait` request has finished reinit procedure + * and becomes `outgong` request. + * Non empty `outgoing` count means that other + * further `incoming` requests or + * `busy-wait` requests are getting on with its job + * without resource initialization because + * main `busy-wait` request + * has already re-initialized it at here + */ + outgoing_requests.fetch_add(1); + pending_requests.fetch_sub(1); + return; + } + } + } + } + + // All non main requests became `outgoing` and look at on initialized resource + outgoing_requests++; + + // Each `busy-wait` request are not busy-wait now + pending_requests.fetch_sub(1); + } + return; + } + + template + void on_unlock(Args&& ...args) { + // Read unlock + /* + * Each released `outgoing` request checks out to doesn't use resource anymore. + * The last `outgoing` request becomes main `outgoing` request and + * must deinitialize resource if no `incoming` or `busy-wait` requests + * are waiting for it + */ + size_t thread_id = outgoing_requests.fetch_sub(1); + if (thread_id == 1) { + /* + * Make sure that no another `incoming` (including `busy-wait) + * exists. + * But beforehand its must make sure that no `incoming` or `pending` + * requests are exist. + * + * The main `outgoing` request is an one of `incoming` request + * (it is the oldest one in the current `incoming` bunch) and still + * holds resource in initialized state (thus we compare with 1). + * We must not deinitialize resource before decrease + * `incoming` requests counter because + * after it has got 0 value in `on_lock` another thread + * will start initialize resource procedure which will get conflict + * with current deinitialize procedure + * + * From this point, all `on_lock` request in another thread would + * become `busy-wait` without reaching main `incoming` state (CASE 2) + * */ + if (incoming_requests.load() == 1) { + /* + * The main `outgoing` request is ready to deinit shared resource + * in unconflicting manner. + * + * This is a critical section for single thread for main `outgoing` + * request + * + * CASE 2 only available in `on_lock` thread + * */ + get_self()->on_last_out(std::forward(args)...); + + /* + * Before main `outgoinq` request become released it must notify + * subsequent `busy-wait` requests in `on_lock` in another thread + * that main `busy-wait` must start reinit resource procedure + * */ + reinit.store(true); + + /* + * Deinitialize procedure is finished and main `outgoing` request + * (it is the oldest one in `incoming` request) must become released + * + * Right after when we decrease `incoming` counter + * the condition for equality + * `busy-wait` and `incoming` counter will become true (CASE 2 only) + * in `on_lock` in another threads. After that + * a main `busy-wait` request would check `reinit` condition + * */ + incoming_requests.fetch_sub(1); + return; + } + + /* + * At this point we have guarantee that new `incoming` requests + * had became increased in `on_lock` in another thread right before + * current thread deinitialize resource. + * + * So call off deinitialization procedure here + * */ + } + incoming_requests.fetch_sub(1); + } + + elastic_barrier(const elastic_barrier&) = delete; + elastic_barrier(elastic_barrier&&) = delete; + elastic_barrier& operator() (const elastic_barrier&) = delete; + elastic_barrier& operator() (elastic_barrier&&) = delete; +}; +} // namespace onevpl +} // namespace wip +} // namespace gapi +} // namespace cv + +#endif // GAPI_STREAMING_ONEVPL_ACCELERATORS_UTILS_ELASTIC_BARRIER_HPP diff --git a/modules/gapi/src/streaming/onevpl/accelerators/utils/shared_lock.cpp b/modules/gapi/src/streaming/onevpl/accelerators/utils/shared_lock.cpp new file mode 100644 index 0000000000..ab3c48afd1 --- /dev/null +++ b/modules/gapi/src/streaming/onevpl/accelerators/utils/shared_lock.cpp @@ -0,0 +1,95 @@ +// This file is part of OpenCV project. +// It is subject to the license terms in the LICENSE file found in the top-level directory +// of this distribution and at http://opencv.org/license.html. +// +// Copyright (C) 2021 Intel Corporation + +#include +#include "streaming/onevpl/accelerators/utils/shared_lock.hpp" + +namespace cv { +namespace gapi { +namespace wip { +namespace onevpl { + +SharedLock::SharedLock() { + exclusive_lock.store(false); + shared_counter.store(0); +} + +size_t SharedLock::shared_lock() { + size_t prev = 0; + bool in_progress = false; + bool pred_excl = exclusive_lock.load(); + do { + if (!pred_excl) { + // if no exclusive lock then start shared lock transaction + prev = shared_counter.fetch_add(1); + in_progress = true; // transaction is in progress + } else { + if (in_progress) { + in_progress = false; + shared_counter.fetch_sub(1); + } + std::this_thread::yield(); + } + + // test if exclusive lock happened before + pred_excl = exclusive_lock.load(); + } while (pred_excl || !in_progress); + + return prev; +} + +size_t SharedLock::unlock_shared() { + return shared_counter.fetch_sub(1); +} + +void SharedLock::lock() { + bool in_progress = false; + size_t prev_shared = shared_counter.load(); + do { + if (prev_shared == 0) { + bool expected = false; + while (!exclusive_lock.compare_exchange_strong(expected, true)) { + expected = false; + std::this_thread::yield(); + } + in_progress = true; + } else { + if (in_progress) { + in_progress = false; + exclusive_lock.store(false); + } + std::this_thread::yield(); + } + prev_shared = shared_counter.load(); + } while (prev_shared != 0 || !in_progress); +} + +bool SharedLock::try_lock() { + if (shared_counter.load() != 0) { + return false; + } + + bool expected = false; + if (exclusive_lock.compare_exchange_strong(expected, true)) { + if (shared_counter.load() == 0) { + return true; + } else { + exclusive_lock.store(false); + } + } + return false; +} + +void SharedLock::unlock() { + exclusive_lock.store(false); +} +bool SharedLock::owns() const { + return exclusive_lock.load(); +} +} // namespace onevpl +} // namespace wip +} // namespace gapi +} // namespace cv diff --git a/modules/gapi/src/streaming/onevpl/accelerators/utils/shared_lock.hpp b/modules/gapi/src/streaming/onevpl/accelerators/utils/shared_lock.hpp new file mode 100644 index 0000000000..704a474761 --- /dev/null +++ b/modules/gapi/src/streaming/onevpl/accelerators/utils/shared_lock.hpp @@ -0,0 +1,47 @@ +// This file is part of OpenCV project. +// It is subject to the license terms in the LICENSE file found in the top-level directory +// of this distribution and at http://opencv.org/license.html. +// +// Copyright (C) 2021 Intel Corporation + +#ifndef GAPI_STREAMING_ONEVPL_ACCELERATORS_SURFACE_SHARED_LOCK_HPP +#define GAPI_STREAMING_ONEVPL_ACCELERATORS_SURFACE_SHARED_LOCK_HPP + +#include +#include + +#include "opencv2/gapi/own/exports.hpp" // GAPI_EXPORTS + +namespace cv { +namespace gapi { +namespace wip { +namespace onevpl { + +class GAPI_EXPORTS SharedLock { +public: + SharedLock(); + ~SharedLock() = default; + + size_t shared_lock(); + size_t unlock_shared(); + + void lock(); + bool try_lock(); + void unlock(); + + bool owns() const; +private: + SharedLock(const SharedLock&) = delete; + SharedLock& operator= (const SharedLock&) = delete; + SharedLock(SharedLock&&) = delete; + SharedLock& operator== (SharedLock&&) = delete; + + std::atomic exclusive_lock; + std::atomic shared_counter; +}; +} // namespace onevpl +} // namespace wip +} // namespace gapi +} // namespace cv + +#endif // GAPI_STREAMING_ONEVPL_ACCELERATORS_SURFACE_SHARED_LOCK_HPP diff --git a/modules/gapi/src/streaming/onevpl/cfg_param_device_selector.cpp b/modules/gapi/src/streaming/onevpl/cfg_param_device_selector.cpp index 64dc34329b..a4d85f2598 100644 --- a/modules/gapi/src/streaming/onevpl/cfg_param_device_selector.cpp +++ b/modules/gapi/src/streaming/onevpl/cfg_param_device_selector.cpp @@ -5,7 +5,7 @@ // Copyright (C) 2021 Intel Corporation #ifdef HAVE_ONEVPL -#include +#include "streaming/onevpl/onevpl_export.hpp" #include #include @@ -44,7 +44,7 @@ CfgParamDeviceSelector::CfgParamDeviceSelector(const CfgParams& cfg_params) : auto accel_mode_it = std::find_if(cfg_params.begin(), cfg_params.end(), [] (const CfgParam& value) { - return value.get_name() == "mfxImplDescription.AccelerationMode"; + return value.get_name() == CfgParam::acceleration_mode_name(); }); if (accel_mode_it == cfg_params.end()) { @@ -128,9 +128,11 @@ CfgParamDeviceSelector::CfgParamDeviceSelector(const CfgParams& cfg_params) : suggested_device = IDeviceSelector::create(hw_handle, "GPU", AccelType::DX11); suggested_context = IDeviceSelector::create(device_context, AccelType::DX11); #else - GAPI_LOG_WARNING(nullptr, "Unavailable \"mfxImplDescription.AccelerationMode: MFX_ACCEL_MODE_VIA_D3D11\"" + GAPI_LOG_WARNING(nullptr, "Unavailable \"" << CfgParam::acceleration_mode_name() << ": MFX_ACCEL_MODE_VIA_D3D11\"" "was chosen for current project configuration"); - throw std::logic_error("Unsupported \"mfxImplDescription.AccelerationMode: MFX_ACCEL_MODE_VIA_D3D11\""); + throw std::logic_error(std::string("Unsupported \"") + + CfgParam::acceleration_mode_name() + + ": MFX_ACCEL_MODE_VIA_D3D11\""); #endif // HAVE_DIRECTX #endif // HAVE_D3D11 break; @@ -140,7 +142,8 @@ CfgParamDeviceSelector::CfgParamDeviceSelector(const CfgParams& cfg_params) : break; } default: - throw std::logic_error("Unsupported \"mfxImplDescription.AccelerationMode\" requested: " + + throw std::logic_error(std::string("Unsupported \"") + + CfgParam::acceleration_mode_name() +"\" requested: " + std::to_string(accel_mode.Data.U32)); break; } @@ -154,14 +157,16 @@ CfgParamDeviceSelector::CfgParamDeviceSelector(Device::Ptr device_ptr, suggested_context(IDeviceSelector::create(nullptr, AccelType::HOST)) { auto accel_mode_it = std::find_if(cfg_params.begin(), cfg_params.end(), [] (const CfgParam& value) { - return value.get_name() == "mfxImplDescription.AccelerationMode"; + return value.get_name() == CfgParam::acceleration_mode_name(); }); if (accel_mode_it == cfg_params.end()) { GAPI_LOG_WARNING(nullptr, "Cannot deternime \"device_ptr\" type. " - "Make sure a param \"mfxImplDescription.AccelerationMode\" " + "Make sure a param \"" << CfgParam::acceleration_mode_name() << "\" " "presents in configurations and has correct value according to " "\"device_ptr\" type"); - throw std::logic_error("Missing \"mfxImplDescription.AccelerationMode\" param"); + throw std::logic_error(std::string("Missing \"") + + CfgParam::acceleration_mode_name() + + "\" param"); } GAPI_LOG_DEBUG(nullptr, "Turn on HW acceleration support for device: " << @@ -169,7 +174,7 @@ CfgParamDeviceSelector::CfgParamDeviceSelector(Device::Ptr device_ptr, ", context: " << ctx_ptr); if (!device_ptr) { GAPI_LOG_WARNING(nullptr, "Empty \"device_ptr\" is not allowed when " - "param \"mfxImplDescription.AccelerationMode\" existed"); + "param \"" << CfgParam::acceleration_mode_name() << "\" existed"); throw std::logic_error("Invalid param: \"device_ptr\""); } @@ -191,23 +196,36 @@ CfgParamDeviceSelector::CfgParamDeviceSelector(Device::Ptr device_ptr, suggested_context = IDeviceSelector::create(ctx_ptr, AccelType::DX11); ID3D11DeviceContext* dx_ctx_ptr = reinterpret_cast(suggested_context.get_ptr()); + + // oneVPL recommendation + { + ID3D11Multithread *pD11Multithread = nullptr; + dx_ctx_ptr->QueryInterface(IID_PPV_ARGS(&pD11Multithread)); + pD11Multithread->SetMultithreadProtected(true); + pD11Multithread->Release(); + } + dx_ctx_ptr->AddRef(); #else - GAPI_LOG_WARNING(nullptr, "Unavailable \"mfxImplDescription.AccelerationMode: MFX_ACCEL_MODE_VIA_D3D11\"" + GAPI_LOG_WARNING(nullptr, "Unavailable \"" << CfgParam::acceleration_mode_name() << + ": MFX_ACCEL_MODE_VIA_D3D11\"" "was chosen for current project configuration"); - throw std::logic_error("Unsupported \"mfxImplDescription.AccelerationMode: MFX_ACCEL_MODE_VIA_D3D11\""); + throw std::logic_error(std::string("Unsupported \"") + + CfgParam::acceleration_mode_name() + ": MFX_ACCEL_MODE_VIA_D3D11\""); #endif // HAVE_DIRECTX #endif // HAVE_D3D11 break; } case MFX_ACCEL_MODE_NA: { - GAPI_LOG_WARNING(nullptr, "Incompatible \"mfxImplDescription.AccelerationMode: MFX_ACCEL_MODE_NA\" with " + GAPI_LOG_WARNING(nullptr, "Incompatible \"" << CfgParam::acceleration_mode_name() << + ": MFX_ACCEL_MODE_NA\" with " "\"device_ptr\" and \"ctx_ptr\" arguments. " "You should not clarify these arguments with \"MFX_ACCEL_MODE_NA\" mode"); throw std::logic_error("Incompatible param: MFX_ACCEL_MODE_NA"); } default: - throw std::logic_error("Unsupported \"mfxImplDescription.AccelerationMode\" requested: " + + throw std::logic_error(std::string("Unsupported \"") + CfgParam::acceleration_mode_name() + + "\" requested: " + std::to_string(accel_mode.Data.U32)); break; } diff --git a/modules/gapi/src/streaming/onevpl/cfg_params.cpp b/modules/gapi/src/streaming/onevpl/cfg_params.cpp index 456725508f..97458faed3 100644 --- a/modules/gapi/src/streaming/onevpl/cfg_params.cpp +++ b/modules/gapi/src/streaming/onevpl/cfg_params.cpp @@ -86,6 +86,34 @@ CfgParam::CfgParam (const std::string& param_name, value_t&& param_value, bool i CfgParam::~CfgParam() = default; +CfgParam CfgParam::create_frames_pool_size(uint64_t value) { + return CfgParam::create(CfgParam::frames_pool_size_name(), value, false); +} + +CfgParam CfgParam::create_acceleration_mode(uint32_t value) { + return CfgParam::create(CfgParam::acceleration_mode_name(), value); +} + +CfgParam CfgParam::create_acceleration_mode(const char* value) { + return CfgParam::create(CfgParam::acceleration_mode_name(), std::string(value)); +} + +CfgParam CfgParam::create_decoder_id(uint32_t value) { + return CfgParam::create(CfgParam::decoder_id_name(), value); +} + +CfgParam CfgParam::create_decoder_id(const char* value) { + return CfgParam::create(CfgParam::decoder_id_name(), std::string(value)); +} + +CfgParam CfgParam::create_implementation(uint32_t value) { + return CfgParam::create(CfgParam::implementation_name(), value); +} + +CfgParam CfgParam::create_implementation(const char* value) { + return CfgParam::create(CfgParam::implementation_name(), std::string(value)); +} + CfgParam& CfgParam::operator=(const CfgParam& src) { if (this != &src) { m_priv = src.m_priv; diff --git a/modules/gapi/src/streaming/onevpl/cfg_params_parser.cpp b/modules/gapi/src/streaming/onevpl/cfg_params_parser.cpp index a683c7478e..07c639faa2 100644 --- a/modules/gapi/src/streaming/onevpl/cfg_params_parser.cpp +++ b/modules/gapi/src/streaming/onevpl/cfg_params_parser.cpp @@ -22,19 +22,16 @@ namespace onevpl { template <> struct ParamCreator { template - CfgParam create (const std::string& name, ValueType&& value) { + CfgParam create (const std::string& name, ValueType&& value, bool is_major_flag = false) { return CfgParam::create(name, std::forward(value), is_major_flag); } - bool is_major_flag = false; }; template <> struct ParamCreator { template - mfxVariant create (const std::string& name, ValueType&& value) { - static_assert(std::is_same::type, mfxU32>::value, - "ParamCreator supports mfxU32 at the moment. " - "Feel free to extend for more types"); + mfxVariant create (const std::string& name, ValueType&& value, bool is_major_flag = false) { + cv::util::suppress_unused_warning(is_major_flag); return create_impl(name, value); } private: @@ -44,6 +41,18 @@ private: ret.Data.U32 = value; return ret; } + mfxVariant create_impl(const std::string&, mfxI64 value) { + mfxVariant ret; + ret.Type = MFX_VARIANT_TYPE_I64; + ret.Data.I64 = value; + return ret; + } + mfxVariant create_impl(const std::string&, mfxU64 value) { + mfxVariant ret; + ret.Type = MFX_VARIANT_TYPE_U64; + ret.Data.U64 = value; + return ret; + } }; template @@ -67,14 +76,16 @@ std::vector get_params_from_string(const std::string& str) { std::string value = line.substr(name_endline_pos + 2); ParamCreator creator; - if (name == "mfxImplDescription.Impl") { + if (name == CfgParam::implementation_name()) { ret.push_back(creator.create(name, cstr_to_mfx_impl(value.c_str()))); - } else if (name == "mfxImplDescription.mfxDecoderDescription.decoder.CodecID") { + } else if (name == CfgParam::decoder_id_name()) { ret.push_back(creator.create(name, cstr_to_mfx_codec_id(value.c_str()))); - } else if (name == "mfxImplDescription.AccelerationMode") { + } else if (name == CfgParam::acceleration_mode_name()) { ret.push_back(creator.create(name, cstr_to_mfx_accel_mode(value.c_str()))); } else if (name == "mfxImplDescription.ApiVersion.Version") { ret.push_back(creator.create(name, cstr_to_mfx_version(value.c_str()))); + } else if (name == CfgParam::frames_pool_size_name()) { + ret.push_back(creator.create(name, strtoull_or_throw(value.c_str()), false)); } else { GAPI_LOG_DEBUG(nullptr, "Cannot parse configuration param, name: " << name << ", value: " << value); @@ -116,6 +127,32 @@ mfxVariant cfg_param_to_mfx_variant(const CfgParam& cfg_val) { }), cfg_val.get_value()); return ret; } + +size_t strtoull_or_throw(const char* str) { + char *end_ptr = nullptr; + errno = 0; + size_t ret = strtoull(str, &end_ptr, 10); + if ((end_ptr == str) || + ((ret == LONG_MAX || ret == LONG_MIN) && errno == ERANGE)) { + // nothing parsed from the string, handle errors or exit + GAPI_LOG_WARNING(nullptr, "strtoull failed for: " << str); + GAPI_Assert(false && "strtoull_or_throw"); + } + return ret; +} + +int64_t strtoll_or_throw(const char* str) { + char *end_ptr = nullptr; + errno = 0; + int64_t ret = strtoll(str, &end_ptr, 10); + if ((end_ptr == str) || + ((ret == LONG_MAX || ret == LONG_MIN) && errno == ERANGE)) { + // nothing parsed from the string, handle errors or exit + GAPI_LOG_WARNING(nullptr, "strtoll failed for: " << str); + GAPI_Assert(false && "strtoll_or_throw"); + } + return ret; +} } // namespace onevpl } // namespace wip } // namespace gapi diff --git a/modules/gapi/src/streaming/onevpl/cfg_params_parser.hpp b/modules/gapi/src/streaming/onevpl/cfg_params_parser.hpp index 6247eef916..c5e7685756 100644 --- a/modules/gapi/src/streaming/onevpl/cfg_params_parser.hpp +++ b/modules/gapi/src/streaming/onevpl/cfg_params_parser.hpp @@ -8,12 +8,7 @@ #define GAPI_STREAMING_ONEVPL_CFG_PARAM_PARSER_HPP #ifdef HAVE_ONEVPL -#if (MFX_VERSION >= 2000) -#include -#endif // MFX_VERSION - -#include -#include +#include "streaming/onevpl/onevpl_export.hpp" #include #include @@ -31,10 +26,14 @@ std::vector get_params_from_string(const std::string& str); template struct ParamCreator { template - ReturnType create(const std::string& name, ValueType&& value); + ReturnType create(const std::string& name, ValueType&& value, bool is_major = false); }; mfxVariant cfg_param_to_mfx_variant(const CfgParam& value); + +size_t strtoull_or_throw(const char* str); +int64_t strtoll_or_throw(const char* str); + } // namespace onevpl } // namespace wip } // namespace gapi diff --git a/modules/gapi/src/streaming/onevpl/data_provider_defines.hpp b/modules/gapi/src/streaming/onevpl/data_provider_defines.hpp index d31bece9fe..5a4c66fef2 100644 --- a/modules/gapi/src/streaming/onevpl/data_provider_defines.hpp +++ b/modules/gapi/src/streaming/onevpl/data_provider_defines.hpp @@ -2,8 +2,7 @@ #define GAPI_STREAMING_ONEVPL_DATA_PROVIDER_DEFINES_HPP #ifdef HAVE_ONEVPL -#include -#include +#include "streaming/onevpl/onevpl_export.hpp" #endif // HAVE_ONEVPL #include diff --git a/modules/gapi/src/streaming/onevpl/data_provider_dispatcher.cpp b/modules/gapi/src/streaming/onevpl/data_provider_dispatcher.cpp index bc712eb1a1..e900fad7c6 100644 --- a/modules/gapi/src/streaming/onevpl/data_provider_dispatcher.cpp +++ b/modules/gapi/src/streaming/onevpl/data_provider_dispatcher.cpp @@ -25,13 +25,14 @@ IDataProvider::Ptr DataProviderDispatcher::create(const std::string& file_path, // Look-up CodecId from input params // If set then raw data provider is preferred - GAPI_LOG_DEBUG(nullptr, "try find explicit cfg param\"mfxImplDescription.mfxDecoderDescription.decoder.CodecID\""); + GAPI_LOG_DEBUG(nullptr, "try find explicit cfg param \"" << + CfgParam::decoder_id_name() <<"\""); auto codec_it = std::find_if(cfg_params.begin(), cfg_params.end(), [] (const CfgParam& value) { - return value.get_name() == "mfxImplDescription.mfxDecoderDescription.decoder.CodecID"; + return value.get_name() == CfgParam::decoder_id_name(); }); if (codec_it != cfg_params.end()) { - GAPI_LOG_DEBUG(nullptr, "Dispatcher found \"mfxImplDescription.mfxDecoderDescription.decoder.CodecID\"" + GAPI_LOG_DEBUG(nullptr, "Dispatcher found \"" << CfgParam::decoder_id_name() << "\"" " so try on raw data provider at first"); try { diff --git a/modules/gapi/src/streaming/onevpl/data_provider_interface_exception.cpp b/modules/gapi/src/streaming/onevpl/data_provider_interface_exception.cpp index f30ab1cfad..18d51ebf2c 100644 --- a/modules/gapi/src/streaming/onevpl/data_provider_interface_exception.cpp +++ b/modules/gapi/src/streaming/onevpl/data_provider_interface_exception.cpp @@ -5,8 +5,7 @@ // Copyright (C) 2021 Intel Corporation #ifdef HAVE_ONEVPL -#include -#include +#include "streaming/onevpl/onevpl_export.hpp" #endif // HAVE_ONEVPL #include diff --git a/modules/gapi/src/streaming/onevpl/demux/async_mfp_demux_data_provider.hpp b/modules/gapi/src/streaming/onevpl/demux/async_mfp_demux_data_provider.hpp index 706d63dca7..f63593a46e 100644 --- a/modules/gapi/src/streaming/onevpl/demux/async_mfp_demux_data_provider.hpp +++ b/modules/gapi/src/streaming/onevpl/demux/async_mfp_demux_data_provider.hpp @@ -13,7 +13,7 @@ #include #ifdef HAVE_ONEVPL -#include +#include "streaming/onevpl/onevpl_export.hpp" #ifdef _WIN32 #define NOMINMAX diff --git a/modules/gapi/src/streaming/onevpl/engine/decode/decode_engine_legacy.cpp b/modules/gapi/src/streaming/onevpl/engine/decode/decode_engine_legacy.cpp index 0abca7f5a5..6707a401b1 100644 --- a/modules/gapi/src/streaming/onevpl/engine/decode/decode_engine_legacy.cpp +++ b/modules/gapi/src/streaming/onevpl/engine/decode/decode_engine_legacy.cpp @@ -16,6 +16,7 @@ #include "streaming/onevpl/engine/decode/decode_session.hpp" #include "streaming/onevpl/accelerators/accel_policy_interface.hpp" #include "streaming/onevpl/accelerators/surface/surface.hpp" +#include "streaming/onevpl/cfg_params_parser.hpp" #include "streaming/onevpl/utils.hpp" #include "logger.hpp" @@ -24,95 +25,6 @@ namespace cv { namespace gapi { namespace wip { namespace onevpl { -/* UTILS */ -mfxU32 GetSurfaceSize(mfxU32 FourCC, mfxU32 width, mfxU32 height) { - mfxU32 nbytes = 0; - - mfxU32 half_width = width / 2; - mfxU32 half_height = height / 2; - switch (FourCC) { - case MFX_FOURCC_I420: - case MFX_FOURCC_NV12: - nbytes = width * height + 2 * half_width * half_height; - break; - case MFX_FOURCC_I010: - case MFX_FOURCC_P010: - nbytes = width * height + 2 * half_width * half_height; - nbytes *= 2; - break; - case MFX_FOURCC_RGB4: - nbytes = width * height * 4; - break; - default: - GAPI_LOG_WARNING(nullptr, "Unsupported FourCC requested: " << FourCC); - GAPI_Assert(false && "Unsupported FourCC requested"); - break; - } - return nbytes; -} - -surface_ptr_t create_surface_RGB4(mfxFrameInfo frameInfo, - std::shared_ptr out_buf_ptr, - size_t out_buf_ptr_offset, - size_t out_buf_size) -{ - mfxU8* buf = reinterpret_cast(out_buf_ptr.get()); - mfxU16 surfW = frameInfo.Width * 4; - mfxU16 surfH = frameInfo.Height; - (void)surfH; - - // TODO more intelligent check - if (out_buf_size <= out_buf_ptr_offset) { - throw std::runtime_error(std::string("Insufficient buffer size: ") + - std::to_string(out_buf_size) + ", buffer offset: " + - std::to_string(out_buf_ptr_offset) + - ", expected surface width: " + std::to_string(surfW) + - ", height: " + std::to_string(surfH)); - } - - std::unique_ptr handle(new mfxFrameSurface1); - memset(handle.get(), 0, sizeof(mfxFrameSurface1)); - - handle->Info = frameInfo; - handle->Data.B = buf + out_buf_ptr_offset; - handle->Data.G = handle->Data.B + 1; - handle->Data.R = handle->Data.B + 2; - handle->Data.A = handle->Data.B + 3; - handle->Data.Pitch = surfW; - - return Surface::create_surface(std::move(handle), out_buf_ptr); -} - -surface_ptr_t create_surface_other(mfxFrameInfo frameInfo, - std::shared_ptr out_buf_ptr, - size_t out_buf_ptr_offset, - size_t out_buf_size) -{ - mfxU8* buf = reinterpret_cast(out_buf_ptr.get()); - mfxU16 surfH = frameInfo.Height; - mfxU16 surfW = (frameInfo.FourCC == MFX_FOURCC_P010) ? frameInfo.Width * 2 : frameInfo.Width; - - // TODO more intelligent check - if (out_buf_size <= - out_buf_ptr_offset + (surfW * surfH) + ((surfW / 2) * (surfH / 2))) { - throw std::runtime_error(std::string("Insufficient buffer size: ") + - std::to_string(out_buf_size) + ", buffer offset: " + - std::to_string(out_buf_ptr_offset) + - ", expected surface width: " + std::to_string(surfW) + - ", height: " + std::to_string(surfH)); - } - - std::unique_ptr handle(new mfxFrameSurface1); - memset(handle.get(), 0, sizeof(mfxFrameSurface1)); - - handle->Info = frameInfo; - handle->Data.Y = buf + out_buf_ptr_offset; - handle->Data.U = buf + out_buf_ptr_offset + (surfW * surfH); - handle->Data.V = handle->Data.U + ((surfW / 2) * (surfH / 2)); - handle->Data.Pitch = surfW; - - return Surface::create_surface(std::move(handle), out_buf_ptr); -} VPLLegacyDecodeEngine::VPLLegacyDecodeEngine(std::unique_ptr&& accel) : ProcessingEngineBase(std::move(accel)) { @@ -146,8 +58,9 @@ VPLLegacyDecodeEngine::VPLLegacyDecodeEngine(std::unique_ptrDataLength)) ? my_sess.stream.get() + : nullptr, /* No more data to read, start decode draining mode*/ my_sess.procesing_surface_ptr.lock()->get_handle(), &sync_pair.second, @@ -164,33 +77,30 @@ VPLLegacyDecodeEngine::VPLLegacyDecodeEngine(std::unique_ptrget_handle(), &sync_pair.second, &sync_pair.first); } catch (const std::runtime_error& ex) { + // NB: not an error, yield CPU ticks to check + // surface availability at a next phase. + // But print WARNING to notify user about pipeline stuck GAPI_LOG_WARNING(nullptr, "[" << my_sess.session << "] has no surface, reason: " << ex.what()); - // TODO it is supposed to place `break;` here - // to simulate `yield`-like behavior. - // Further DX11 intergation logic claims more strict rules - // for enqueue surfaces. If no free surface - // is available it had better to wait free one by checking - // for async result than waste time in spinning. - // - // Put it as-is at now to not break - // current compatibility and avoid further merge-conflicts + break; } } if (my_sess.last_status == MFX_ERR_NONE) { my_sess.sync_queue.emplace(sync_pair); } else if (my_sess.last_status != MFX_ERR_MORE_DATA) /* suppress MFX_ERR_MORE_DATA warning */ { - GAPI_LOG_WARNING(nullptr, "pending ops count: " << my_sess.sync_queue.size() << + GAPI_LOG_WARNING(nullptr, "decode pending ops count: " << + my_sess.sync_queue.size() << ", sync id: " << sync_pair.first << - ", status: " << mfxstatus_to_string(my_sess.last_status)); + ", status: " << + mfxstatus_to_string(my_sess.last_status)); } return ExecutionStatus::Continue; }, @@ -198,20 +108,26 @@ VPLLegacyDecodeEngine::VPLLegacyDecodeEngine(std::unique_ptr ExecutionStatus { LegacyDecodeSession& my_sess = static_cast(sess); - if (!my_sess.sync_queue.empty()) // FIFO: check the oldest async operation complete - { - LegacyDecodeSession::op_handle_t& pending_op = my_sess.sync_queue.front(); - sess.last_status = MFXVideoCORE_SyncOperation(sess.session, pending_op.first, 0); - - GAPI_LOG_DEBUG(nullptr, "pending ops count: " << my_sess.sync_queue.size() << - ", sync id: " << pending_op.first << - ", status: " << mfxstatus_to_string(my_sess.last_status)); - - // put frames in ready queue on success - if (MFX_ERR_NONE == sess.last_status) { - on_frame_ready(my_sess, pending_op.second); + do { + if (!my_sess.sync_queue.empty()) { // FIFO: check the oldest async operation complete + LegacyDecodeSession::op_handle_t& pending_op = my_sess.sync_queue.front(); + sess.last_status = MFXVideoCORE_SyncOperation(sess.session, pending_op.first, 0); + + GAPI_LOG_DEBUG(nullptr, "pending ops count: " << + my_sess.sync_queue.size() << + ", sync id: " << + pending_op.first << + ", surface: " << + pending_op.second << + ", status: " << + mfxstatus_to_string(my_sess.last_status)); + + // put frames in ready queue on success + if (MFX_ERR_NONE == sess.last_status) { + on_frame_ready(my_sess, pending_op.second); + } } - } + } while (MFX_ERR_NONE == sess.last_status && !my_sess.sync_queue.empty()); return ExecutionStatus::Continue; }, // 4) Falls back on generic status procesing @@ -222,45 +138,138 @@ VPLLegacyDecodeEngine::VPLLegacyDecodeEngine(std::unique_ptr provider) -{ - mfxFrameAllocRequest decRequest = {}; +ProcessingEngineBase::session_ptr +VPLLegacyDecodeEngine::initialize_session(mfxSession mfx_session, + const std::vector& cfg_params, + std::shared_ptr provider) { + GAPI_DbgAssert(provider && "Cannot create decoder, data provider is nullptr"); + + // init session + acceleration_policy->init(mfx_session); + + // Get codec ID from data provider + IDataProvider::mfx_codec_id_type decoder_id_name = provider->get_mfx_codec_id(); + + // Prepare video param + mfxVideoParam mfxDecParams {}; + mfxDecParams.mfx.CodecId = decoder_id_name; + + // set memory stream direction accroding to accelearion policy device type + IDeviceSelector::DeviceScoreTable devices = acceleration_policy->get_device_selector()->select_devices(); + GAPI_Assert(devices.size() == 1 && "Multiple(or zero) acceleration devices case is unsupported"); + AccelType accel_type = devices.begin()->second.get_type(); + if (accel_type == AccelType::DX11) { + mfxDecParams.IOPattern = MFX_IOPATTERN_OUT_VIDEO_MEMORY; + } else if (accel_type == AccelType::HOST) { + mfxDecParams.IOPattern = MFX_IOPATTERN_OUT_SYSTEM_MEMORY; + } else { + GAPI_Assert(false && "unsupported AccelType from device selector"); + } + + // try fetch & decode input data + mfxStatus sts = MFX_ERR_NONE; + std::shared_ptr bitstream{}; + bool can_fetch_data = false; + do { + can_fetch_data = provider->fetch_bitstream_data(bitstream); + if (!can_fetch_data) { + // must fetch data always because EOF critical at this point + GAPI_LOG_WARNING(nullptr, "cannot decode header from provider: " << provider.get() << + ". Unexpected EOF"); + throw std::runtime_error("Error reading bitstream: EOF"); + } + + sts = MFXVideoDECODE_DecodeHeader(mfx_session, bitstream.get(), &mfxDecParams); + if(MFX_ERR_NONE != sts && MFX_ERR_MORE_DATA != sts) { + throw std::runtime_error("Error decoding header, error: " + + mfxstatus_to_string(sts)); + } + } while (sts == MFX_ERR_MORE_DATA && !provider->empty()); + + if (MFX_ERR_NONE != sts) { + GAPI_LOG_WARNING(nullptr, "cannot decode header from provider: " << provider.get() + << ". Make sure data source is valid and/or " + "\"" << CfgParam::decoder_id_name() << "\"" + " has correct value in case of demultiplexed raw input"); + throw std::runtime_error("Error decode header, error: " + + mfxstatus_to_string(sts)); + } + mfxFrameAllocRequest decRequest {}; + // Query number required surfaces for decoder - MFXVideoDECODE_QueryIOSurf(mfx_session, &decoder_param.param, &decRequest); + MFXVideoDECODE_QueryIOSurf(mfx_session, &mfxDecParams, &decRequest); // External (application) allocation of decode surfaces GAPI_LOG_DEBUG(nullptr, "Query IOSurf for session: " << mfx_session << + ", mfxFrameAllocRequest.NumFrameMin: " << decRequest.NumFrameMin << ", mfxFrameAllocRequest.NumFrameSuggested: " << decRequest.NumFrameSuggested << ", mfxFrameAllocRequest.Type: " << decRequest.Type); - mfxU32 singleSurfaceSize = GetSurfaceSize(decoder_param.param.mfx.FrameInfo.FourCC, - decoder_param.param.mfx.FrameInfo.Width, - decoder_param.param.mfx.FrameInfo.Height); - if (!singleSurfaceSize) { - throw std::runtime_error("Cannot determine surface size for: fourCC" + - std::to_string(decoder_param.param.mfx.FrameInfo.FourCC) + - ", width: " + std::to_string(decoder_param.param.mfx.FrameInfo.Width) + - ", height: " + std::to_string(decoder_param.param.mfx.FrameInfo.Height)); + // NB: override NumFrameSuggested preallocation size (how many frames we can hold) + size_t preallocated_frames_count = decRequest.NumFrameSuggested; + // NB: if you see bunch of WARNING about "cannot get free surface from pool" + // and have abundant RAM size then increase `preallocated_frames_count` + // to keep more free surfaces in a round. Otherwise VPL decode pipeline will be waiting + // till application is freeing unusable surface on its side. + // + auto queue_capacity_it = std::find_if(cfg_params.begin(), cfg_params.end(), [] (const CfgParam& value) { + return value.get_name() == CfgParam::frames_pool_size_name(); + }); + if (queue_capacity_it != cfg_params.end()) { + cv::util::visit(cv::util::overload_lambdas( + [&preallocated_frames_count](uint8_t value) { preallocated_frames_count = static_cast(value); }, + [&preallocated_frames_count](int8_t value) { preallocated_frames_count = static_cast(value); }, + [&preallocated_frames_count](uint16_t value) { preallocated_frames_count = static_cast(value); }, + [&preallocated_frames_count](int16_t value) { preallocated_frames_count = static_cast(value); }, + [&preallocated_frames_count](uint32_t value) { preallocated_frames_count = static_cast(value); }, + [&preallocated_frames_count](int32_t value) { preallocated_frames_count = static_cast(value); }, + [&preallocated_frames_count](uint64_t value) { preallocated_frames_count = static_cast(value); }, + [&preallocated_frames_count](int64_t value) { preallocated_frames_count = static_cast(value); }, + [&preallocated_frames_count](float_t value) { preallocated_frames_count = static_cast(value); }, + [&preallocated_frames_count](double_t value) { preallocated_frames_count = static_cast(value); }, + [&preallocated_frames_count](void*) { GAPI_Assert(false && "`void*` is unsupported type"); }, + [&preallocated_frames_count](const std::string& value) { + preallocated_frames_count = strtoull_or_throw(value.c_str()); + }), + queue_capacity_it->get_value()); + + GAPI_LOG_INFO(nullptr, "Try to use CfgParam \"" << CfgParam::frames_pool_size_name() << "\": " << + preallocated_frames_count << ", for session: " << mfx_session); + + } + if (preallocated_frames_count < decRequest.NumFrameMin) { + GAPI_LOG_WARNING(nullptr, "Cannot proceed with CfgParam \"" << CfgParam::frames_pool_size_name() << "\": " << + preallocated_frames_count << ". It must be equal or greater than " + "mfxFrameAllocRequest.NumFrameMin: " << decRequest.NumFrameMin); + throw std::runtime_error(std::string("Invalid value of param: ") + + CfgParam::frames_pool_size_name() + ", underflow"); + } else { + if (static_cast(std::numeric_limits::max()) < preallocated_frames_count) { + GAPI_LOG_WARNING(nullptr, "Cannot proceed with CfgParam \"" << CfgParam::frames_pool_size_name() << "\": " << + preallocated_frames_count << ". It must not be equal than " << + std::numeric_limits::max()); + throw std::runtime_error(std::string("Invalid value of param: ") + + CfgParam::frames_pool_size_name() + ", overflow"); + } + decRequest.NumFrameSuggested = static_cast(preallocated_frames_count); + GAPI_LOG_DEBUG(nullptr, "mfxFrameAllocRequest overriden by user input for session: " << mfx_session << + ", mfxFrameAllocRequest.NumFrameMin: " << decRequest.NumFrameMin << + ", mfxFrameAllocRequest.NumFrameSuggested: " << decRequest.NumFrameSuggested << + ", mfxFrameAllocRequest.Type: " << decRequest.Type); } - const auto &frameInfo = decoder_param.param.mfx.FrameInfo; - auto surface_creator = - [&frameInfo] (std::shared_ptr out_buf_ptr, size_t out_buf_ptr_offset, - size_t out_buf_size) -> surface_ptr_t { - return (frameInfo.FourCC == MFX_FOURCC_RGB4) ? - create_surface_RGB4(frameInfo, out_buf_ptr, out_buf_ptr_offset, - out_buf_size) : - create_surface_other(frameInfo, out_buf_ptr, out_buf_ptr_offset, - out_buf_size);}; - - //TODO Configure preallocation size (how many frames we can hold) - const size_t preallocated_frames_count = 30; VPLAccelerationPolicy::pool_key_t decode_pool_key = - acceleration_policy->create_surface_pool(decRequest.NumFrameSuggested * preallocated_frames_count, - singleSurfaceSize, - surface_creator); + acceleration_policy->create_surface_pool(decRequest, mfxDecParams); + + // Input parameters finished, now initialize decode + // create decoder for session accoring to header recovered from source file + sts = MFXVideoDECODE_Init(mfx_session, &mfxDecParams); + if (MFX_ERR_NONE != sts) { + throw std::runtime_error("Error initializing Decode, error: " + + mfxstatus_to_string(sts)); + } + + DecoderParams decoder_param {bitstream, mfxDecParams}; // create session std::shared_ptr sess_ptr = @@ -271,6 +280,7 @@ void VPLLegacyDecodeEngine::initialize_session(mfxSession mfx_session, sess_ptr->init_surface_pool(decode_pool_key); // prepare working decode surface sess_ptr->swap_surface(*this); + return sess_ptr; } ProcessingEngineBase::ExecutionStatus VPLLegacyDecodeEngine::execute_op(operation_t& op, EngineSession& sess) { @@ -303,14 +313,13 @@ ProcessingEngineBase::ExecutionStatus VPLLegacyDecodeEngine::process_error(mfxSt sess.swap_surface(*this); return ExecutionStatus::Continue; } catch (const std::runtime_error& ex) { - GAPI_LOG_WARNING(nullptr, "[" << sess.session << "] error: " << ex.what() << - "Abort"); - // TODO it is supposed to be `break;` here in future PR + GAPI_LOG_WARNING(nullptr, "[" << sess.session << "] error: " << ex.what()); + return ExecutionStatus::Continue; // read more data } } case MFX_ERR_MORE_DATA: // The function requires more bitstream at input before decoding can proceed - if (!sess.data_provider || sess.data_provider->empty()) { - // No more data to drain from decoder, start encode draining mode + if (!(sess.data_provider || (sess.stream && sess.stream->DataLength))) { + // No more data to drain from decoder return ExecutionStatus::Processed; } else @@ -326,7 +335,7 @@ ProcessingEngineBase::ExecutionStatus VPLLegacyDecodeEngine::process_error(mfxSt return ExecutionStatus::Continue; } catch (const std::runtime_error& ex) { GAPI_LOG_WARNING(nullptr, "[" << sess.session << "] error: " << ex.what()); - // TODO it is supposed to be `break;` here in future PR + return ExecutionStatus::Continue; // read more data } break; } @@ -371,9 +380,8 @@ ProcessingEngineBase::ExecutionStatus VPLLegacyDecodeEngine::process_error(mfxSt sess.swap_surface(*this); return ExecutionStatus::Continue; } catch (const std::runtime_error& ex) { - GAPI_LOG_WARNING(nullptr, "[" << sess.session << "] error: " << ex.what() << - "Abort"); - // TODO it is supposed to be `break;` here in future PR + GAPI_LOG_WARNING(nullptr, "[" << sess.session << "] error: " << ex.what()); + return ExecutionStatus::Continue; } default: GAPI_LOG_WARNING(nullptr, "Unknown status code: " << mfxstatus_to_string(status) << diff --git a/modules/gapi/src/streaming/onevpl/engine/decode/decode_engine_legacy.hpp b/modules/gapi/src/streaming/onevpl/engine/decode/decode_engine_legacy.hpp index 3499d7f6df..f6a02db3db 100644 --- a/modules/gapi/src/streaming/onevpl/engine/decode/decode_engine_legacy.hpp +++ b/modules/gapi/src/streaming/onevpl/engine/decode/decode_engine_legacy.hpp @@ -12,10 +12,7 @@ #include "streaming/onevpl/engine/processing_engine_base.hpp" #ifdef HAVE_ONEVPL -#if (MFX_VERSION >= 2000) - #include -#endif -#include +#include "streaming/onevpl/onevpl_export.hpp" namespace cv { namespace gapi { @@ -31,8 +28,9 @@ class VPLLegacyDecodeEngine : public ProcessingEngineBase { public: VPLLegacyDecodeEngine(std::unique_ptr&& accel); - void initialize_session(mfxSession mfx_session, DecoderParams&& decoder_param, - std::shared_ptr provider) override; + session_ptr initialize_session(mfxSession mfx_session, + const std::vector& cfg_params, + std::shared_ptr provider) override; private: ExecutionStatus execute_op(operation_t& op, EngineSession& sess) override; diff --git a/modules/gapi/src/streaming/onevpl/engine/decode/decode_session.cpp b/modules/gapi/src/streaming/onevpl/engine/decode/decode_session.cpp index 871f588ffa..bbb1378767 100644 --- a/modules/gapi/src/streaming/onevpl/engine/decode/decode_session.cpp +++ b/modules/gapi/src/streaming/onevpl/engine/decode/decode_session.cpp @@ -52,8 +52,10 @@ void LegacyDecodeSession::swap_surface(VPLLegacyDecodeEngine& engine) { procesing_surface_ptr = cand; } catch (const std::runtime_error& ex) { - GAPI_LOG_WARNING(nullptr, "[" << session << "] error: " << ex.what() << - "Abort"); + GAPI_LOG_WARNING(nullptr, "[" << session << "] error: " << ex.what()); + + // Delegate exception processing on caller + throw; } } @@ -72,6 +74,10 @@ Data::Meta LegacyDecodeSession::generate_frame_meta() { }; return meta; } + +const mfxVideoParam& LegacyDecodeSession::get_video_param() const { + return mfx_decoder_param; +} } // namespace onevpl } // namespace wip } // namespace gapi diff --git a/modules/gapi/src/streaming/onevpl/engine/decode/decode_session.hpp b/modules/gapi/src/streaming/onevpl/engine/decode/decode_session.hpp index 58be858fdb..476a575172 100644 --- a/modules/gapi/src/streaming/onevpl/engine/decode/decode_session.hpp +++ b/modules/gapi/src/streaming/onevpl/engine/decode/decode_session.hpp @@ -15,10 +15,7 @@ #include "streaming/onevpl/engine/engine_session.hpp" #include "streaming/onevpl/accelerators/accel_policy_interface.hpp" #ifdef HAVE_ONEVPL -#if (MFX_VERSION >= 2000) - #include -#endif -#include +#include "streaming/onevpl/onevpl_export.hpp" namespace cv { namespace gapi { @@ -40,11 +37,11 @@ public: void swap_surface(VPLLegacyDecodeEngine& engine); void init_surface_pool(VPLAccelerationPolicy::pool_key_t key); - mfxVideoParam mfx_decoder_param; - std::shared_ptr data_provider; - Data::Meta generate_frame_meta(); + const mfxVideoParam& get_video_param() const override; private: + mfxVideoParam mfx_decoder_param; + std::shared_ptr data_provider; VPLAccelerationPolicy::pool_key_t decoder_pool_id; mfxFrameAllocRequest request; diff --git a/modules/gapi/src/streaming/onevpl/engine/engine_session.hpp b/modules/gapi/src/streaming/onevpl/engine/engine_session.hpp index e0c6d01f8b..67018d0fd7 100644 --- a/modules/gapi/src/streaming/onevpl/engine/engine_session.hpp +++ b/modules/gapi/src/streaming/onevpl/engine/engine_session.hpp @@ -18,7 +18,7 @@ #include #ifdef HAVE_ONEVPL -#include +#include "streaming/onevpl/onevpl_export.hpp" namespace cv { namespace gapi { @@ -40,6 +40,8 @@ struct GAPI_EXPORTS EngineSession { EngineSession(mfxSession sess, std::shared_ptr&& str); std::string error_code_to_str() const; virtual ~EngineSession(); + + virtual const mfxVideoParam& get_video_param() const = 0; }; } // namespace onevpl } // namespace wip diff --git a/modules/gapi/src/streaming/onevpl/engine/processing_engine_base.cpp b/modules/gapi/src/streaming/onevpl/engine/processing_engine_base.cpp index 9a4c4a7fb0..72f2f62fc4 100644 --- a/modules/gapi/src/streaming/onevpl/engine/processing_engine_base.cpp +++ b/modules/gapi/src/streaming/onevpl/engine/processing_engine_base.cpp @@ -23,7 +23,8 @@ ProcessingEngineBase::ProcessingEngineBase(std::unique_ptrerror_code_to_str() << ", " << ProcessingEngineBase::status_to_string(status) << @@ -57,6 +59,7 @@ ProcessingEngineBase::ExecutionStatus ProcessingEngineBase::process(mfxSession s } if (status == ExecutionStatus::Processed) { + GAPI_LOG_INFO(nullptr, "Processed [" << session << "]"); sessions.erase(sess_it); execution_table.erase(session); } @@ -90,6 +93,7 @@ void ProcessingEngineBase::get_frame(Data &data) { data = ready_frames.front(); ready_frames.pop(); + GAPI_LOG_DEBUG(nullptr, " elapsed ready frames count: " << ready_frames.size()); } const VPLAccelerationPolicy* ProcessingEngineBase::get_accel() const { diff --git a/modules/gapi/src/streaming/onevpl/engine/processing_engine_base.hpp b/modules/gapi/src/streaming/onevpl/engine/processing_engine_base.hpp index 4b34721d67..059ef963de 100644 --- a/modules/gapi/src/streaming/onevpl/engine/processing_engine_base.hpp +++ b/modules/gapi/src/streaming/onevpl/engine/processing_engine_base.hpp @@ -8,6 +8,7 @@ #define GAPI_STREAMING_ONEVPL_ENGINE_PROCESSING_ENGINE_BASE_HPP #include +#include #include "streaming/onevpl/engine/engine_session.hpp" #include "opencv2/gapi/own/exports.hpp" // GAPI_EXPORTS @@ -49,9 +50,9 @@ public: ProcessingEngineBase(std::unique_ptr&& accel); virtual ~ProcessingEngineBase(); - virtual void initialize_session(mfxSession mfx_session, - DecoderParams&& decoder_param, - std::shared_ptr provider) = 0; + virtual session_ptr initialize_session(mfxSession mfx_session, + const std::vector& cfg_params, + std::shared_ptr provider) = 0; ExecutionStatus process(mfxSession session); size_t get_ready_frames_count() const; diff --git a/modules/gapi/src/streaming/onevpl/file_data_provider.cpp b/modules/gapi/src/streaming/onevpl/file_data_provider.cpp index 020d471b55..a86d541904 100644 --- a/modules/gapi/src/streaming/onevpl/file_data_provider.cpp +++ b/modules/gapi/src/streaming/onevpl/file_data_provider.cpp @@ -28,14 +28,14 @@ FileDataProvider::FileDataProvider(const std::string& file_path, codec_params.size()); auto codec_it = std::find_if(codec_params.begin(), codec_params.end(), [] (const CfgParam& value) { - return value.get_name() == "mfxImplDescription.mfxDecoderDescription.decoder.CodecID"; + return value.get_name() == CfgParam::decoder_id_name(); }); if (codec_it == codec_params.end()) { GAPI_LOG_WARNING(nullptr, "[" << this << "] " << - "\"mfxImplDescription.mfxDecoderDescription.decoder.CodecID\" " + "\"" << CfgParam::decoder_id_name() << "\" " "is absent, total param count" << codec_params.size()); - throw DataProviderUnsupportedException("\"mfxImplDescription.mfxDecoderDescription.decoder.CodecID\" " + throw DataProviderUnsupportedException(std::string("\"") + CfgParam::decoder_id_name() + "\" " "is required for FileDataProvider"); } diff --git a/modules/gapi/src/streaming/onevpl/onevpl_export.hpp b/modules/gapi/src/streaming/onevpl/onevpl_export.hpp new file mode 100644 index 0000000000..44970ee7be --- /dev/null +++ b/modules/gapi/src/streaming/onevpl/onevpl_export.hpp @@ -0,0 +1,25 @@ +#ifndef GAPI_STREAMING_ONEVPL_EXPORT_HPP +#define GAPI_STREAMING_ONEVPL_EXPORT_HPP + +#if defined(_MSC_VER) +#pragma warning(push) +#pragma warning(disable : 4201) +#pragma warning(disable : 4302) +#pragma warning(disable : 4311) +#pragma warning(disable : 4312) +#endif // defined(_MSC_VER) + +#ifdef HAVE_ONEVPL +#if (MFX_VERSION >= 2000) +#include +#endif // MFX_VERSION + +#include +#include +#endif // HAVE_ONEVPL + +#if defined(_MSC_VER) +#pragma warning(pop) +#endif // defined(_MSC_VER) + +#endif // GAPI_STREAMING_ONEVPL_EXPORT_HPP diff --git a/modules/gapi/src/streaming/onevpl/source_priv.cpp b/modules/gapi/src/streaming/onevpl/source_priv.cpp index c5de2a6998..fd2a401957 100644 --- a/modules/gapi/src/streaming/onevpl/source_priv.cpp +++ b/modules/gapi/src/streaming/onevpl/source_priv.cpp @@ -53,14 +53,15 @@ GSource::Priv::Priv() : mfx_session(), description(), description_is_valid(false), - engine() + engine(), + consumed_frames_count() { GAPI_LOG_INFO(nullptr, "Initialized MFX handle: " << mfx_handle); } GSource::Priv::Priv(std::shared_ptr provider, const std::vector& params, - std::shared_ptr) : + std::shared_ptr device_selector) : GSource::Priv() { // Enable Config @@ -193,13 +194,9 @@ GSource::Priv::Priv(std::shared_ptr provider, GAPI_LOG_INFO(nullptr, "Initialized MFX session: " << mfx_session); - // initialize decoder - // Find codec ID from config - IDataProvider::mfx_codec_id_type decoder_id = provider->get_mfx_codec_id(); - // create session driving engine if required if (!engine) { - std::unique_ptr acceleration = initializeHWAccel(); + std::unique_ptr acceleration = initializeHWAccel(device_selector); // TODO Add factory static method in ProcessingEngineBase if (mfx_impl_description->ApiVersion.Major >= VPL_NEW_API_MAJOR_VERSION) { @@ -211,106 +208,54 @@ GSource::Priv::Priv(std::shared_ptr provider, } } - //create decoder for session accoring to header recovered from source file - DecoderParams decoder_param = create_decoder_from_file(decoder_id, provider); - // create engine session for processing mfx session pipeline - engine->initialize_session(mfx_session, std::move(decoder_param), - provider); - - //prepare session for processing - engine->process(mfx_session); -} - -GSource::Priv::~Priv() -{ - GAPI_LOG_INFO(nullptr, "Unload MFX implementation description: " << mfx_impl_description); - MFXDispReleaseImplDescription(mfx_handle, mfx_impl_description); - GAPI_LOG_INFO(nullptr, "Unload MFX handle: " << mfx_handle); - MFXUnload(mfx_handle); -} - -DecoderParams GSource::Priv::create_decoder_from_file(uint32_t decoder_id, - std::shared_ptr provider) -{ - GAPI_DbgAssert(provider && "Cannot create decoder, data provider is nullptr"); - - std::shared_ptr bitstream{}; - - // Retrieve the frame information from input stream - mfxVideoParam mfxDecParams {}; - mfxDecParams.mfx.CodecId = decoder_id; - mfxDecParams.IOPattern = MFX_IOPATTERN_OUT_SYSTEM_MEMORY;//MFX_IOPATTERN_OUT_VIDEO_MEMORY; - mfxStatus sts = MFX_ERR_NONE; - bool can_fetch_data = false; - do { - can_fetch_data = provider->fetch_bitstream_data(bitstream); - if (!can_fetch_data) { - // must fetch data always because EOF critical at this point - GAPI_LOG_WARNING(nullptr, "cannot decode header from provider: " << provider.get() << - ". Unexpected EOF"); - throw std::runtime_error("Error reading bitstream: EOF"); - } - - sts = MFXVideoDECODE_DecodeHeader(mfx_session, bitstream.get(), &mfxDecParams); - if(MFX_ERR_NONE != sts && MFX_ERR_MORE_DATA != sts) { - throw std::runtime_error("Error decoding header, error: " + - mfxstatus_to_string(sts)); - } - } while (sts == MFX_ERR_MORE_DATA && !provider->empty()); + auto engine_session_ptr = engine->initialize_session(mfx_session, cfg_params, + provider); - if (MFX_ERR_NONE != sts) { - GAPI_LOG_WARNING(nullptr, "cannot decode header from provider: " << provider.get() - << ". Make sure data source is valid and/or " - "\"mfxImplDescription.mfxDecoderDescription.decoder.CodecID\"" - " has correct value in case of demultiplexed raw input"); - throw std::runtime_error("Error decode header, error: " + - mfxstatus_to_string(sts)); - } - - // Input parameters finished, now initialize decode - sts = MFXVideoDECODE_Init(mfx_session, &mfxDecParams); - if (MFX_ERR_NONE != sts) { - throw std::runtime_error("Error initializing Decode, error: " + - mfxstatus_to_string(sts)); - } + const mfxVideoParam& video_param = engine_session_ptr->get_video_param(); // set valid description description.size = cv::Size { - mfxDecParams.mfx.FrameInfo.Width, - mfxDecParams.mfx.FrameInfo.Height}; - switch(mfxDecParams.mfx.FrameInfo.FourCC) { + video_param.mfx.FrameInfo.Width, + video_param.mfx.FrameInfo.Height}; + switch(video_param.mfx.FrameInfo.FourCC) { case MFX_FOURCC_I420: - GAPI_Assert(false && "Cannot create GMetaArg description: " - "MediaFrame doesn't support I420 type"); + throw std::runtime_error("Cannot parse GMetaArg description: MediaFrame doesn't support I420 type"); case MFX_FOURCC_NV12: description.fmt = cv::MediaFormat::NV12; break; default: - { - GAPI_LOG_WARNING(nullptr, "Cannot create GMetaArg description: " - "MediaFrame unknown 'fmt' type: " << - std::to_string(mfxDecParams.mfx.FrameInfo.FourCC)); - GAPI_Assert(false && "Cannot create GMetaArg description: invalid value"); - } + throw std::runtime_error("Cannot parse GMetaArg description: MediaFrame unknown 'fmt' type: " + + std::to_string(video_param.mfx.FrameInfo.FourCC)); } description_is_valid = true; - return {bitstream, mfxDecParams}; + //prepare session for processing + engine->process(mfx_session); +} + +GSource::Priv::~Priv() { + engine.reset(); + + GAPI_LOG_INFO(nullptr, "consumed frames count: " << consumed_frames_count); + GAPI_LOG_INFO(nullptr, "Unload MFX implementation description: " << mfx_impl_description); + MFXDispReleaseImplDescription(mfx_handle, mfx_impl_description); + GAPI_LOG_INFO(nullptr, "Unload MFX handle: " << mfx_handle); + MFXUnload(mfx_handle); } -std::unique_ptr GSource::Priv::initializeHWAccel() +std::unique_ptr GSource::Priv::initializeHWAccel(std::shared_ptr selector) { std::unique_ptr ret; auto accel_mode_it = std::find_if(cfg_params.begin(), cfg_params.end(), [] (const CfgParam& value) { - return value.get_name() == "mfxImplDescription.AccelerationMode"; + return value.get_name() == CfgParam::acceleration_mode_name(); }); if (accel_mode_it == cfg_params.end()) { GAPI_LOG_DEBUG(nullptr, "No HW Accel requested. Use CPU"); - ret.reset(new VPLCPUAccelerationPolicy); + ret.reset(new VPLCPUAccelerationPolicy(selector)); return ret; } @@ -320,13 +265,13 @@ std::unique_ptr GSource::Priv::initializeHWAccel() switch(accel_mode.Data.U32) { case MFX_ACCEL_MODE_VIA_D3D11: { - std::unique_ptr cand(new VPLDX11AccelerationPolicy); + std::unique_ptr cand(new VPLDX11AccelerationPolicy(selector)); ret = std::move(cand); break; } case MFX_ACCEL_MODE_NA: { - std::unique_ptr cand(new VPLCPUAccelerationPolicy); + std::unique_ptr cand(new VPLCPUAccelerationPolicy(selector)); ret = std::move(cand); break; } @@ -367,6 +312,7 @@ bool GSource::Priv::pull(cv::gapi::wip::Data& data) if (engine->get_ready_frames_count()) { engine->get_frame(data); + consumed_frames_count++; return true; } else { return false; diff --git a/modules/gapi/src/streaming/onevpl/source_priv.hpp b/modules/gapi/src/streaming/onevpl/source_priv.hpp index 07e1139191..b835850d35 100644 --- a/modules/gapi/src/streaming/onevpl/source_priv.hpp +++ b/modules/gapi/src/streaming/onevpl/source_priv.hpp @@ -17,14 +17,7 @@ #include #ifdef HAVE_ONEVPL -#if (MFX_VERSION >= 2000) -#include -#endif // MFX_VERSION - -#include - -#include - +#include "streaming/onevpl/onevpl_export.hpp" #include "streaming/onevpl/engine/processing_engine_base.hpp" namespace cv { @@ -49,9 +42,7 @@ struct GSource::Priv GMetaArg descr_of() const; private: Priv(); - DecoderParams create_decoder_from_file(uint32_t decoder_id, - std::shared_ptr provider); - std::unique_ptr initializeHWAccel(); + std::unique_ptr initializeHWAccel(std::shared_ptr selector); mfxLoader mfx_handle; mfxImplDescription *mfx_impl_description; @@ -64,6 +55,8 @@ private: bool description_is_valid; std::unique_ptr engine; + + size_t consumed_frames_count; }; } // namespace onevpl } // namespace wip diff --git a/modules/gapi/src/streaming/onevpl/utils.cpp b/modules/gapi/src/streaming/onevpl/utils.cpp index 6cbe0e7ea1..3ec0dea8ae 100644 --- a/modules/gapi/src/streaming/onevpl/utils.cpp +++ b/modules/gapi/src/streaming/onevpl/utils.cpp @@ -14,6 +14,18 @@ #include "streaming/onevpl/utils.hpp" #include "logger.hpp" +#define ONEVPL_STRINGIFY_CASE(value) \ + case value: return #value; + +#define ONEVPL_STRINGIFY_IF(value) \ + if (!strcmp(cstr, #value)) { \ + return value; \ + } + +#define APPEND_STRINGIFY_MASK_N_ERASE(value, pref, mask) \ + if (value & mask) { ss << pref << #mask; value ^= mask; } + + namespace cv { namespace gapi { namespace wip { @@ -21,153 +33,96 @@ namespace onevpl { const char* mfx_impl_to_cstr(const mfxIMPL impl) { switch (impl) { - case MFX_IMPL_TYPE_SOFTWARE: - return "MFX_IMPL_TYPE_SOFTWARE"; - case MFX_IMPL_TYPE_HARDWARE: - return "MFX_IMPL_TYPE_HARDWARE"; - default: - return "unknown mfxIMPL"; + ONEVPL_STRINGIFY_CASE(MFX_IMPL_TYPE_SOFTWARE); + ONEVPL_STRINGIFY_CASE(MFX_IMPL_TYPE_HARDWARE); + default: return "unknown mfxIMPL"; } } mfxIMPL cstr_to_mfx_impl(const char* cstr) { - if (!strcmp(cstr, "MFX_IMPL_TYPE_SOFTWARE")) { - return MFX_IMPL_TYPE_SOFTWARE; - } else if (!strcmp(cstr, "MFX_IMPL_TYPE_HARDWARE")) { - return MFX_IMPL_TYPE_HARDWARE; - } - - throw std::logic_error(std::string("Invalid \"mfxImplDescription.Impl\":") + cstr); + ONEVPL_STRINGIFY_IF(MFX_IMPL_TYPE_SOFTWARE) + ONEVPL_STRINGIFY_IF(MFX_IMPL_TYPE_HARDWARE) + throw std::logic_error(std::string("Invalid \"") + CfgParam::implementation_name() + + "\":" + cstr); } const char* mfx_accel_mode_to_cstr (const mfxAccelerationMode mode) { switch (mode) { - case MFX_ACCEL_MODE_NA: - return "MFX_ACCEL_MODE_NA"; - case MFX_ACCEL_MODE_VIA_D3D9: - return "MFX_ACCEL_MODE_VIA_D3D9"; - case MFX_ACCEL_MODE_VIA_D3D11: - return "MFX_ACCEL_MODE_VIA_D3D11"; - case MFX_ACCEL_MODE_VIA_VAAPI: - return "MFX_ACCEL_MODE_VIA_VAAPI"; - case MFX_ACCEL_MODE_VIA_VAAPI_DRM_MODESET: - return "MFX_ACCEL_MODE_VIA_VAAPI_DRM_MODESET"; - case MFX_ACCEL_MODE_VIA_VAAPI_GLX: - return "MFX_ACCEL_MODE_VIA_VAAPI_GLX"; - case MFX_ACCEL_MODE_VIA_VAAPI_X11: - return "MFX_ACCEL_MODE_VIA_VAAPI_X11"; - case MFX_ACCEL_MODE_VIA_VAAPI_WAYLAND: - return "MFX_ACCEL_MODE_VIA_VAAPI_WAYLAND"; - case MFX_ACCEL_MODE_VIA_HDDLUNITE: - return "MFX_ACCEL_MODE_VIA_HDDLUNITE"; - default: - return "unknown mfxAccelerationMode"; + ONEVPL_STRINGIFY_CASE(MFX_ACCEL_MODE_NA) + ONEVPL_STRINGIFY_CASE(MFX_ACCEL_MODE_VIA_D3D9) + ONEVPL_STRINGIFY_CASE(MFX_ACCEL_MODE_VIA_D3D11) + ONEVPL_STRINGIFY_CASE(MFX_ACCEL_MODE_VIA_VAAPI) + ONEVPL_STRINGIFY_CASE(MFX_ACCEL_MODE_VIA_VAAPI_DRM_MODESET) + ONEVPL_STRINGIFY_CASE(MFX_ACCEL_MODE_VIA_VAAPI_GLX) + ONEVPL_STRINGIFY_CASE(MFX_ACCEL_MODE_VIA_VAAPI_X11) + ONEVPL_STRINGIFY_CASE(MFX_ACCEL_MODE_VIA_VAAPI_WAYLAND) + ONEVPL_STRINGIFY_CASE(MFX_ACCEL_MODE_VIA_HDDLUNITE) + default: return "unknown mfxAccelerationMode"; } - return "unknown mfxAccelerationMode"; } mfxAccelerationMode cstr_to_mfx_accel_mode(const char* cstr) { - if (!strcmp(cstr, "MFX_ACCEL_MODE_NA")) { - return MFX_ACCEL_MODE_NA; - } else if (!strcmp(cstr, "MFX_ACCEL_MODE_VIA_D3D9")) { - return MFX_ACCEL_MODE_VIA_D3D9; - } else if (!strcmp(cstr, "MFX_ACCEL_MODE_VIA_D3D11")) { - return MFX_ACCEL_MODE_VIA_D3D11; - } else if (!strcmp(cstr, "MFX_ACCEL_MODE_VIA_VAAPI")) { - return MFX_ACCEL_MODE_VIA_VAAPI; - } else if (!strcmp(cstr, "MFX_ACCEL_MODE_VIA_VAAPI_DRM_MODESET")) { - return MFX_ACCEL_MODE_VIA_VAAPI_DRM_MODESET; - } else if (!strcmp(cstr, "MFX_ACCEL_MODE_VIA_VAAPI_GLX")) { - return MFX_ACCEL_MODE_VIA_VAAPI_GLX; - } else if (!strcmp(cstr, "MFX_ACCEL_MODE_VIA_VAAPI_X11")) { - return MFX_ACCEL_MODE_VIA_VAAPI_X11; - } else if (!strcmp(cstr, "MFX_ACCEL_MODE_VIA_VAAPI_WAYLAND")) { - return MFX_ACCEL_MODE_VIA_VAAPI_WAYLAND; - } else if (!strcmp(cstr, "MFX_ACCEL_MODE_VIA_HDDLUNITE")) { - return MFX_ACCEL_MODE_VIA_HDDLUNITE; - } - throw std::logic_error(std::string("Invalid \"mfxImplDescription.AccelerationMode\":") + cstr); + ONEVPL_STRINGIFY_IF(MFX_ACCEL_MODE_NA) + ONEVPL_STRINGIFY_IF(MFX_ACCEL_MODE_VIA_D3D9) + ONEVPL_STRINGIFY_IF(MFX_ACCEL_MODE_VIA_D3D11) + ONEVPL_STRINGIFY_IF(MFX_ACCEL_MODE_VIA_VAAPI) + ONEVPL_STRINGIFY_IF(MFX_ACCEL_MODE_VIA_VAAPI_DRM_MODESET) + ONEVPL_STRINGIFY_IF(MFX_ACCEL_MODE_VIA_VAAPI_GLX) + ONEVPL_STRINGIFY_IF(MFX_ACCEL_MODE_VIA_VAAPI_X11) + ONEVPL_STRINGIFY_IF(MFX_ACCEL_MODE_VIA_VAAPI_WAYLAND) + ONEVPL_STRINGIFY_IF(MFX_ACCEL_MODE_VIA_HDDLUNITE) + throw std::logic_error(std::string("Invalid \"") + + CfgParam::acceleration_mode_name() + + "\":" + cstr); } const char* mfx_resource_type_to_cstr (const mfxResourceType type) { switch (type) { - case MFX_RESOURCE_SYSTEM_SURFACE: - return "MFX_RESOURCE_SYSTEM_SURFACE"; - case MFX_RESOURCE_VA_SURFACE: - return "MFX_RESOURCE_VA_SURFACE"; - case MFX_RESOURCE_VA_BUFFER: - return "MFX_RESOURCE_VA_BUFFER"; - case MFX_RESOURCE_DX9_SURFACE: - return "MFX_RESOURCE_DX9_SURFACE"; - case MFX_RESOURCE_DX11_TEXTURE: - return "MFX_RESOURCE_DX11_TEXTURE"; - case MFX_RESOURCE_DX12_RESOURCE: - return "MFX_RESOURCE_DX12_RESOURCE"; - case MFX_RESOURCE_DMA_RESOURCE: - return "MFX_RESOURCE_DMA_RESOURCE"; - case MFX_RESOURCE_HDDLUNITE_REMOTE_MEMORY: - return "MFX_RESOURCE_HDDLUNITE_REMOTE_MEMORY"; - default: - return "unknown mfxResourceType"; + ONEVPL_STRINGIFY_CASE(MFX_RESOURCE_SYSTEM_SURFACE) + ONEVPL_STRINGIFY_CASE(MFX_RESOURCE_VA_SURFACE) + ONEVPL_STRINGIFY_CASE(MFX_RESOURCE_VA_BUFFER) + ONEVPL_STRINGIFY_CASE(MFX_RESOURCE_DX9_SURFACE) + ONEVPL_STRINGIFY_CASE(MFX_RESOURCE_DX11_TEXTURE) + ONEVPL_STRINGIFY_CASE(MFX_RESOURCE_DX12_RESOURCE) + ONEVPL_STRINGIFY_CASE(MFX_RESOURCE_DMA_RESOURCE) + ONEVPL_STRINGIFY_CASE(MFX_RESOURCE_HDDLUNITE_REMOTE_MEMORY) + default: return "unknown mfxResourceType"; } } mfxResourceType cstr_to_mfx_resource_type(const char* cstr) { - if (!strcmp(cstr, "MFX_RESOURCE_SYSTEM_SURFACE")) { - return MFX_RESOURCE_SYSTEM_SURFACE; - } else if (!strcmp(cstr, "MFX_RESOURCE_VA_SURFACE")) { - return MFX_RESOURCE_VA_SURFACE; - } else if (!strcmp(cstr, "MFX_RESOURCE_VA_BUFFER")) { - return MFX_RESOURCE_VA_BUFFER; - } else if (!strcmp(cstr, "MFX_RESOURCE_DX9_SURFACE")) { - return MFX_RESOURCE_DX9_SURFACE; - } else if (!strcmp(cstr, "MFX_RESOURCE_DX11_TEXTURE")) { - return MFX_RESOURCE_DX11_TEXTURE; - } else if (!strcmp(cstr, "MFX_RESOURCE_DX12_RESOURCE")) { - return MFX_RESOURCE_DX12_RESOURCE; - } else if (!strcmp(cstr, "MFX_RESOURCE_DMA_RESOURCE")) { - return MFX_RESOURCE_DMA_RESOURCE; - } else if (!strcmp(cstr, "MFX_RESOURCE_HDDLUNITE_REMOTE_MEMORY")) { - return MFX_RESOURCE_HDDLUNITE_REMOTE_MEMORY; - } + ONEVPL_STRINGIFY_IF(MFX_RESOURCE_SYSTEM_SURFACE) + ONEVPL_STRINGIFY_IF(MFX_RESOURCE_VA_SURFACE) + ONEVPL_STRINGIFY_IF(MFX_RESOURCE_VA_BUFFER) + ONEVPL_STRINGIFY_IF(MFX_RESOURCE_DX9_SURFACE) + ONEVPL_STRINGIFY_IF(MFX_RESOURCE_DX11_TEXTURE) + ONEVPL_STRINGIFY_IF(MFX_RESOURCE_DX12_RESOURCE) + ONEVPL_STRINGIFY_IF(MFX_RESOURCE_DMA_RESOURCE) + ONEVPL_STRINGIFY_IF(MFX_RESOURCE_HDDLUNITE_REMOTE_MEMORY) throw std::logic_error(std::string("Invalid \"decoder.Profiles.MemDesc.MemHandleType\":") + cstr); } mfxU32 cstr_to_mfx_codec_id(const char* cstr) { - if (!strcmp(cstr, "MFX_CODEC_AVC")) { - return MFX_CODEC_AVC; - } else if (!strcmp(cstr, "MFX_CODEC_HEVC")) { - return MFX_CODEC_HEVC; - } else if (!strcmp(cstr, "MFX_CODEC_MPEG2")) { - return MFX_CODEC_MPEG2; - } else if (!strcmp(cstr, "MFX_CODEC_VC1")) { - return MFX_CODEC_VC1; - } else if (!strcmp(cstr, "MFX_CODEC_CAPTURE")) { - return MFX_CODEC_CAPTURE; - } else if (!strcmp(cstr, "MFX_CODEC_VP9")) { - return MFX_CODEC_VP9; - } else if (!strcmp(cstr, "MFX_CODEC_AV1")) { - return MFX_CODEC_AV1; - } - throw std::logic_error(std::string("Cannot parse \"mfxImplDescription.mfxDecoderDescription.decoder.CodecID\" value: ") + cstr); + ONEVPL_STRINGIFY_IF(MFX_CODEC_AVC) + ONEVPL_STRINGIFY_IF(MFX_CODEC_HEVC) + ONEVPL_STRINGIFY_IF(MFX_CODEC_MPEG2) + ONEVPL_STRINGIFY_IF(MFX_CODEC_VC1) + ONEVPL_STRINGIFY_IF(MFX_CODEC_CAPTURE) + ONEVPL_STRINGIFY_IF(MFX_CODEC_VP9) + ONEVPL_STRINGIFY_IF(MFX_CODEC_AV1) + throw std::logic_error(std::string("Cannot parse \"") + CfgParam::decoder_id_name() + + "\" value: " + cstr); } const char* mfx_codec_id_to_cstr(mfxU32 mfx_id) { switch(mfx_id) { - case MFX_CODEC_AVC: - return "MFX_CODEC_AVC"; - case MFX_CODEC_HEVC: - return "MFX_CODEC_HEVC"; - case MFX_CODEC_MPEG2: - return "MFX_CODEC_MPEG2"; - case MFX_CODEC_VC1: - return "MFX_CODEC_VC1"; - case MFX_CODEC_VP9: - return "MFX_CODEC_VP9"; - case MFX_CODEC_AV1: - return "MFX_CODEC_AV1"; - case MFX_CODEC_JPEG: - return "MFX_CODEC_JPEG"; + ONEVPL_STRINGIFY_CASE(MFX_CODEC_AVC) + ONEVPL_STRINGIFY_CASE(MFX_CODEC_HEVC) + ONEVPL_STRINGIFY_CASE(MFX_CODEC_MPEG2) + ONEVPL_STRINGIFY_CASE(MFX_CODEC_VC1) + ONEVPL_STRINGIFY_CASE(MFX_CODEC_VP9) + ONEVPL_STRINGIFY_CASE(MFX_CODEC_AV1) + ONEVPL_STRINGIFY_CASE(MFX_CODEC_JPEG) default: return ""; } @@ -189,145 +144,92 @@ const char* mfx_codec_type_to_cstr(const mfxU32 fourcc, const mfxU32 type) { switch (fourcc) { case MFX_CODEC_JPEG: { switch (type) { - case MFX_PROFILE_UNKNOWN: - return "MFX_PROFILE_UNKNOWN"; - case MFX_PROFILE_JPEG_BASELINE: - return "MFX_PROFILE_JPEG_BASELINE"; - default: - return "(idesc.Version.Major) << "." << static_cast(idesc.Version.Minor) << std::endl; - out << "mfxImplDescription.Impl: " << mfx_impl_to_cstr(idesc.Impl) << std::endl; - out << "(*)mfxImplDescription.AccelerationMode: " << mfx_accel_mode_to_cstr(idesc.AccelerationMode) << std::endl; + out << "(*)" << CfgParam::implementation_name() << ": " << mfx_impl_to_cstr(idesc.Impl) << std::endl; + out << "(*)" << CfgParam::acceleration_mode_name() << ": " << mfx_accel_mode_to_cstr(idesc.AccelerationMode) << std::endl; out << "mfxImplDescription.ApiVersion: " << idesc.ApiVersion.Major << "." << idesc.ApiVersion.Minor << std::endl; out << "(*)mfxImplDescription.ApiVersion.Version: " << idesc.ApiVersion.Version << std::endl; out << "mfxImplDescription.ImplName: " << idesc.ImplName << std::endl; @@ -386,7 +288,7 @@ std::ostream& operator<< (std::ostream& out, const mfxImplDescription& idesc) << "." << static_cast(dec.Version.Minor) << std::endl; for (int codec = 0; codec < dec.NumCodecs; codec++) { auto cid = dec.Codecs[codec].CodecID; - out << "(*)mfxImplDescription.mfxDecoderDescription.decoder.CodecID: " << cid;//(cid & 0xff) << "." << (cid >> 8 & 0xff) << "." << (cid >> 16 & 0xff) << "." << (cid >> 24 & 0xff) << std::endl; + out << "(*)" << CfgParam::decoder_id_name() << ": " << cid;//(cid & 0xff) << "." << (cid >> 8 & 0xff) << "." << (cid >> 16 & 0xff) << "." << (cid >> 24 & 0xff) << std::endl; out << "mfxImplDescription.mfxDecoderDescription.decoder.MaxcodecLevel: " << dec.Codecs[codec].MaxcodecLevel << std::endl; for (int profile = 0; profile < dec.Codecs[codec].NumProfiles; profile++) { out << "mfxImplDescription.mfxDecoderDescription.decoder.Profiles: " @@ -422,69 +324,69 @@ std::string mfxstatus_to_string(int64_t err) { } std::string mfxstatus_to_string(mfxStatus err) { - switch(err) - { - case MFX_ERR_NONE: - return "MFX_ERR_NONE"; - case MFX_ERR_UNKNOWN: - return "MFX_ERR_UNKNOWN"; - case MFX_ERR_NULL_PTR: - return "MFX_ERR_NULL_PTR"; - case MFX_ERR_UNSUPPORTED: - return "MFX_ERR_UNSUPPORTED"; - case MFX_ERR_MEMORY_ALLOC: - return "MFX_ERR_MEMORY_ALLOC"; - case MFX_ERR_NOT_ENOUGH_BUFFER: - return "MFX_ERR_NOT_ENOUGH_BUFFER"; - case MFX_ERR_INVALID_HANDLE: - return "MFX_ERR_INVALID_HANDLE"; - case MFX_ERR_LOCK_MEMORY: - return "MFX_ERR_LOCK_MEMORY"; - case MFX_ERR_NOT_INITIALIZED: - return "MFX_ERR_NOT_INITIALIZED"; - case MFX_ERR_NOT_FOUND: - return "MFX_ERR_NOT_FOUND"; - case MFX_ERR_MORE_DATA: - return "MFX_ERR_MORE_DATA"; - case MFX_ERR_MORE_SURFACE: - return "MFX_ERR_MORE_SURFACE"; - case MFX_ERR_ABORTED: - return "MFX_ERR_ABORTED"; - case MFX_ERR_DEVICE_LOST: - return "MFX_ERR_DEVICE_LOST"; - case MFX_ERR_INCOMPATIBLE_VIDEO_PARAM: - return "MFX_ERR_INCOMPATIBLE_VIDEO_PARAM"; - case MFX_ERR_INVALID_VIDEO_PARAM: - return "MFX_ERR_INVALID_VIDEO_PARAM"; - case MFX_ERR_UNDEFINED_BEHAVIOR: - return "MFX_ERR_UNDEFINED_BEHAVIOR"; - case MFX_ERR_DEVICE_FAILED: - return "MFX_ERR_DEVICE_FAILED"; - case MFX_ERR_MORE_BITSTREAM: - return "MFX_ERR_MORE_BITSTREAM"; - case MFX_ERR_GPU_HANG: - return "MFX_ERR_GPU_HANG"; - case MFX_ERR_REALLOC_SURFACE: - return "MFX_ERR_REALLOC_SURFACE"; - case MFX_ERR_RESOURCE_MAPPED: - return "MFX_ERR_RESOURCE_MAPPED"; - case MFX_ERR_NOT_IMPLEMENTED: - return "MFX_ERR_NOT_IMPLEMENTED"; - case MFX_WRN_DEVICE_BUSY: - return "MFX_WRN_DEVICE_BUSY"; - case MFX_WRN_VIDEO_PARAM_CHANGED: - return "MFX_WRN_VIDEO_PARAM_CHANGED"; - case MFX_WRN_IN_EXECUTION: - return "MFX_WRN_IN_EXECUTION"; - - default: - break; + switch(err) { + ONEVPL_STRINGIFY_CASE(MFX_ERR_NONE) + ONEVPL_STRINGIFY_CASE(MFX_ERR_UNKNOWN) + ONEVPL_STRINGIFY_CASE(MFX_ERR_NULL_PTR) + ONEVPL_STRINGIFY_CASE(MFX_ERR_UNSUPPORTED) + ONEVPL_STRINGIFY_CASE(MFX_ERR_MEMORY_ALLOC) + ONEVPL_STRINGIFY_CASE(MFX_ERR_NOT_ENOUGH_BUFFER) + ONEVPL_STRINGIFY_CASE(MFX_ERR_INVALID_HANDLE) + ONEVPL_STRINGIFY_CASE(MFX_ERR_LOCK_MEMORY) + ONEVPL_STRINGIFY_CASE(MFX_ERR_NOT_INITIALIZED) + ONEVPL_STRINGIFY_CASE(MFX_ERR_NOT_FOUND) + ONEVPL_STRINGIFY_CASE(MFX_ERR_MORE_DATA) + ONEVPL_STRINGIFY_CASE(MFX_ERR_MORE_SURFACE) + ONEVPL_STRINGIFY_CASE(MFX_ERR_ABORTED) + ONEVPL_STRINGIFY_CASE(MFX_ERR_DEVICE_LOST) + ONEVPL_STRINGIFY_CASE(MFX_ERR_INCOMPATIBLE_VIDEO_PARAM) + ONEVPL_STRINGIFY_CASE(MFX_ERR_INVALID_VIDEO_PARAM) + ONEVPL_STRINGIFY_CASE(MFX_ERR_UNDEFINED_BEHAVIOR) + ONEVPL_STRINGIFY_CASE(MFX_ERR_DEVICE_FAILED) + ONEVPL_STRINGIFY_CASE(MFX_ERR_MORE_BITSTREAM) + ONEVPL_STRINGIFY_CASE(MFX_ERR_GPU_HANG) + ONEVPL_STRINGIFY_CASE(MFX_ERR_REALLOC_SURFACE) + ONEVPL_STRINGIFY_CASE(MFX_ERR_RESOURCE_MAPPED) + ONEVPL_STRINGIFY_CASE(MFX_ERR_NOT_IMPLEMENTED) + ONEVPL_STRINGIFY_CASE(MFX_WRN_DEVICE_BUSY) + ONEVPL_STRINGIFY_CASE(MFX_WRN_VIDEO_PARAM_CHANGED) + ONEVPL_STRINGIFY_CASE(MFX_WRN_IN_EXECUTION) + default: break; } std::string ret(""; return ret; } + +std::string ext_mem_frame_type_to_cstr(int type) { + std::stringstream ss; + APPEND_STRINGIFY_MASK_N_ERASE(type, "|", MFX_MEMTYPE_DXVA2_DECODER_TARGET); + APPEND_STRINGIFY_MASK_N_ERASE(type, "|", MFX_MEMTYPE_DXVA2_PROCESSOR_TARGET); + // NB: accoring to VPL source the commented MFX_* constane below are belong to the + // same actual integral value as condition abobe. So it is impossible + // to distinct them in condition branch. Just put this comment and possible + // constans here... + //APPEND_STRINGIFY_MASK_N_ERASE(type, "|", MFX_MEMTYPE_VIDEO_MEMORY_DECODER_TARGET); + //APPEND_STRINGIFY_MASK_N_ERASE(type, "|", MFX_MEMTYPE_VIDEO_MEMORY_PROCESSOR_TARGET); + APPEND_STRINGIFY_MASK_N_ERASE(type, "|", MFX_MEMTYPE_SYSTEM_MEMORY); + APPEND_STRINGIFY_MASK_N_ERASE(type, "|", MFX_MEMTYPE_RESERVED1); + APPEND_STRINGIFY_MASK_N_ERASE(type, "|", MFX_MEMTYPE_FROM_ENCODE); + APPEND_STRINGIFY_MASK_N_ERASE(type, "|", MFX_MEMTYPE_FROM_DECODE); + APPEND_STRINGIFY_MASK_N_ERASE(type, "|", MFX_MEMTYPE_FROM_VPPIN); + APPEND_STRINGIFY_MASK_N_ERASE(type, "|", MFX_MEMTYPE_FROM_VPPOUT); + APPEND_STRINGIFY_MASK_N_ERASE(type, "|", MFX_MEMTYPE_FROM_ENC); + APPEND_STRINGIFY_MASK_N_ERASE(type, "|", MFX_MEMTYPE_INTERNAL_FRAME); + APPEND_STRINGIFY_MASK_N_ERASE(type, "|", MFX_MEMTYPE_EXTERNAL_FRAME); + APPEND_STRINGIFY_MASK_N_ERASE(type, "|", MFX_MEMTYPE_EXPORT_FRAME); + //APPEND_STRINGIFY_MASK_N_ERASE(type, "|", MFX_MEMTYPE_SHARED_RESOURCE); + APPEND_STRINGIFY_MASK_N_ERASE(type, "|", MFX_MEMTYPE_VIDEO_MEMORY_ENCODER_TARGET); + + if (type != 0) { + ss << "(rest: " << std::to_string(type) << ")"; + } + return ss.str(); +} } // namespace onevpl } // namespace wip } // namespace gapi diff --git a/modules/gapi/src/streaming/onevpl/utils.hpp b/modules/gapi/src/streaming/onevpl/utils.hpp index 723ba16a98..36711bf9a0 100644 --- a/modules/gapi/src/streaming/onevpl/utils.hpp +++ b/modules/gapi/src/streaming/onevpl/utils.hpp @@ -8,12 +8,7 @@ #define GAPI_STREAMING_ONEVPL_ONEVPL_UTILS_HPP #ifdef HAVE_ONEVPL -#if (MFX_VERSION >= 2000) -#include -#endif // MFX_VERSION - -#include -#include +#include "streaming/onevpl/onevpl_export.hpp" #include #include @@ -42,11 +37,19 @@ void release(COMNonManageableType *ptr) { template using ComPtrGuard = std::unique_ptr)>; +template +using ComSharedPtrGuard = std::shared_ptr; + template ComPtrGuard createCOMPtrGuard(COMNonManageableType *ptr = nullptr) { return ComPtrGuard {ptr, &release}; } +template +ComSharedPtrGuard createCOMSharedPtrGuard(ComPtrGuard&& unique_guard) { + return ComSharedPtrGuard(std::move(unique_guard)); +} + const char* mfx_impl_to_cstr(const mfxIMPL impl); @@ -75,6 +78,7 @@ std::string mfxstatus_to_string(mfxStatus err); std::ostream& operator<< (std::ostream& out, const mfxImplDescription& idesc); +std::string ext_mem_frame_type_to_cstr(int type); } // namespace onevpl } // namespace wip } // namespace gapi diff --git a/modules/gapi/test/common/gapi_streaming_tests_common.hpp b/modules/gapi/test/common/gapi_streaming_tests_common.hpp new file mode 100644 index 0000000000..500371727e --- /dev/null +++ b/modules/gapi/test/common/gapi_streaming_tests_common.hpp @@ -0,0 +1,86 @@ +// This file is part of OpenCV project. +// It is subject to the license terms in the LICENSE file found in the top-level directory +// of this distribution and at http://opencv.org/license.html. +// +// Copyright (C) 2021 Intel Corporation + +#ifndef OPENCV_GAPI_STREAMING_TESTS_COMMON_HPP +#define OPENCV_GAPI_STREAMING_TESTS_COMMON_HPP + +#include "gapi_tests_common.hpp" +#include +#include +#include "streaming/onevpl/data_provider_defines.hpp" + +#ifdef HAVE_ONEVPL +#include "streaming/onevpl/onevpl_export.hpp" + +namespace opencv_test { +namespace streaming { +namespace onevpl { + +struct StreamDataProvider : public cv::gapi::wip::onevpl::IDataProvider { + + StreamDataProvider(std::istream& in) : data_stream (in) { + EXPECT_TRUE(in); + } + +mfx_codec_id_type get_mfx_codec_id() const override { + return MFX_CODEC_HEVC; + } + + bool fetch_bitstream_data(std::shared_ptr &out_bitstream) override { + if (empty()) { + return false; + } + + if (!out_bitstream) { + out_bitstream = std::make_shared(); + out_bitstream->MaxLength = 2000000; + out_bitstream->Data = (mfxU8 *)calloc(out_bitstream->MaxLength, sizeof(mfxU8)); + if(!out_bitstream->Data) { + throw std::runtime_error("Cannot allocate bitstream.Data bytes: " + + std::to_string(out_bitstream->MaxLength * sizeof(mfxU8))); + } + out_bitstream->CodecId = get_mfx_codec_id(); + } + + mfxU8 *p0 = out_bitstream->Data; + mfxU8 *p1 = out_bitstream->Data + out_bitstream->DataOffset; + EXPECT_FALSE(out_bitstream->DataOffset > out_bitstream->MaxLength - 1); + EXPECT_FALSE(out_bitstream->DataLength + out_bitstream->DataOffset > out_bitstream->MaxLength); + + std::copy_n(p1, out_bitstream->DataLength, p0); + + out_bitstream->DataOffset = 0; + out_bitstream->DataLength += static_cast(fetch_data(out_bitstream->MaxLength - out_bitstream->DataLength, + out_bitstream->Data + out_bitstream->DataLength)); + return out_bitstream->DataLength != 0; + } + + size_t fetch_data(size_t out_data_size, void* out_data_buf) { + data_stream.read(reinterpret_cast(out_data_buf), out_data_size); + return data_stream.gcount(); + } + bool empty() const override { + return data_stream.eof() || data_stream.bad(); + } +private: + std::istream& data_stream; +}; + +static const unsigned char hevc_header[] = { + 0x00, 0x00, 0x00, 0x01, 0x40, 0x01, 0x0C, 0x06, 0xFF, 0xFF, 0x01, 0x40, 0x00, + 0x00, 0x03, 0x00, 0x80, 0x00, 0x00, 0x03, 0x00, 0x00, 0x03, 0x00, 0x78, 0x00, + 0x00, 0x04, 0x02, 0x10, 0x30, 0x00, 0x00, 0x03, 0x00, 0x10, 0x00, 0x00, 0x03, + 0x01, 0xE5, 0x00, 0x00, 0x00, 0x01, 0x42, 0x01, 0x06, 0x01, 0x40, 0x00, 0x00, + 0x03, 0x00, 0x80, 0x00, 0x00, 0x03, 0x00, 0x00, 0x03, 0x00, 0x78, 0x00, 0x00, + 0xA0, 0x10, 0x20, 0x61, 0x63, 0x41, 0x00, 0x86, 0x49, 0x1B, 0x2B, 0x20, 0x00, + 0x00, 0x00, 0x01, 0x44, 0x01, 0xC0, 0x71, 0xC0, 0xD9, 0x20, 0x00, 0x00, 0x00, + 0x01, 0x26, 0x01, 0xAF, 0x0C +}; +} // namespace onevpl +} // namespace streaming +} // namespace opencv_test +#endif // HAVE_ONEVPL +#endif // OPENCV_GAPI_STREAMING_TESTS_HPP diff --git a/modules/gapi/test/streaming/gapi_streaming_tests.cpp b/modules/gapi/test/streaming/gapi_streaming_tests.cpp index fefd3e07e1..8cef807cd2 100644 --- a/modules/gapi/test/streaming/gapi_streaming_tests.cpp +++ b/modules/gapi/test/streaming/gapi_streaming_tests.cpp @@ -7,7 +7,7 @@ #include "../test_precomp.hpp" -#include "../common/gapi_tests_common.hpp" +#include "../common/gapi_streaming_tests_common.hpp" #include // sleep_for (Delay) @@ -24,18 +24,8 @@ #include #include #include +#include -#include -#include "streaming/onevpl/data_provider_defines.hpp" - -#ifdef HAVE_ONEVPL - -#if (MFX_VERSION >= 2000) -#include -#endif - -#include -#endif // HAVE_ONEVPL namespace opencv_test { @@ -116,7 +106,7 @@ struct GAPI_Streaming: public ::testing::TestWithParam &out_bitstream) override { - if (empty()) { - return false; - } - - if (!out_bitstream) { - out_bitstream = std::make_shared(); - out_bitstream->MaxLength = 2000000; - out_bitstream->Data = (mfxU8 *)calloc(out_bitstream->MaxLength, sizeof(mfxU8)); - if(!out_bitstream->Data) { - throw std::runtime_error("Cannot allocate bitstream.Data bytes: " + - std::to_string(out_bitstream->MaxLength * sizeof(mfxU8))); - } - out_bitstream->CodecId = get_mfx_codec_id(); - } - - mfxU8 *p0 = out_bitstream->Data; - mfxU8 *p1 = out_bitstream->Data + out_bitstream->DataOffset; - EXPECT_FALSE(out_bitstream->DataOffset > out_bitstream->MaxLength - 1); - EXPECT_FALSE(out_bitstream->DataLength + out_bitstream->DataOffset > out_bitstream->MaxLength); - - std::copy_n(p1, out_bitstream->DataLength, p0); - - out_bitstream->DataOffset = 0; - out_bitstream->DataLength += static_cast(fetch_data(out_bitstream->MaxLength - out_bitstream->DataLength, - out_bitstream->Data + out_bitstream->DataLength)); - return out_bitstream->DataLength != 0; - } - - size_t fetch_data(size_t out_data_size, void* out_data_buf) { - data_stream.read(reinterpret_cast(out_data_buf), out_data_size); - return (size_t)data_stream.gcount(); - } - bool empty() const override { - return data_stream.eof() || data_stream.bad(); - } -private: - std::istream& data_stream; -}; -#endif // HAVE_ONEVPL } // anonymous namespace TEST_P(GAPI_Streaming, SmokeTest_ConstInput_GMat) @@ -2244,31 +2183,21 @@ TEST(GAPI_Streaming, TestPythonAPI) } #ifdef HAVE_ONEVPL -const unsigned char hevc_header[] = { - 0x00, 0x00, 0x00, 0x01, 0x40, 0x01, 0x0C, 0x06, 0xFF, 0xFF, 0x01, 0x40, 0x00, - 0x00, 0x03, 0x00, 0x80, 0x00, 0x00, 0x03, 0x00, 0x00, 0x03, 0x00, 0x78, 0x00, - 0x00, 0x04, 0x02, 0x10, 0x30, 0x00, 0x00, 0x03, 0x00, 0x10, 0x00, 0x00, 0x03, - 0x01, 0xE5, 0x00, 0x00, 0x00, 0x01, 0x42, 0x01, 0x06, 0x01, 0x40, 0x00, 0x00, - 0x03, 0x00, 0x80, 0x00, 0x00, 0x03, 0x00, 0x00, 0x03, 0x00, 0x78, 0x00, 0x00, - 0xA0, 0x10, 0x20, 0x61, 0x63, 0x41, 0x00, 0x86, 0x49, 0x1B, 0x2B, 0x20, 0x00, - 0x00, 0x00, 0x01, 0x44, 0x01, 0xC0, 0x71, 0xC0, 0xD9, 0x20, 0x00, 0x00, 0x00, - 0x01, 0x26, 0x01, 0xAF, 0x0C -}; + TEST(OneVPL_Source, Init) { using CfgParam = cv::gapi::wip::onevpl::CfgParam; std::vector src_params; - src_params.push_back(CfgParam::create("mfxImplDescription.Impl", - MFX_IMPL_TYPE_HARDWARE)); - src_params.push_back(CfgParam::create("mfxImplDescription.AccelerationMode", - MFX_ACCEL_MODE_VIA_D3D11, false)); - src_params.push_back(CfgParam::create("mfxImplDescription.mfxDecoderDescription.decoder.CodecID", - MFX_CODEC_HEVC)); + src_params.push_back(CfgParam::create_implementation(MFX_IMPL_TYPE_HARDWARE)); + src_params.push_back(CfgParam::create_acceleration_mode(MFX_ACCEL_MODE_VIA_D3D11)); + src_params.push_back(CfgParam::create_decoder_id(MFX_CODEC_HEVC)); std::stringstream stream(std::ios_base::in | std::ios_base::out | std::ios_base::binary); - EXPECT_TRUE(stream.write(reinterpret_cast(const_cast(hevc_header)), - sizeof(hevc_header))); - auto stream_data_provider = std::make_shared(stream); + + EXPECT_TRUE(stream.write(reinterpret_cast(const_cast(streaming::onevpl::hevc_header)), + sizeof(streaming::onevpl::hevc_header))); + std::shared_ptr stream_data_provider = + std::make_shared(stream); cv::Ptr cap; bool cap_created = false; @@ -2285,7 +2214,7 @@ TEST(OneVPL_Source, Init) } EXPECT_TRUE(stream_data_provider->empty()); } -#endif +#endif // HAVE_ONEVPL TEST(GAPI_Streaming, TestDesyncRMat) { cv::GMat in; diff --git a/modules/gapi/test/streaming/gapi_streaming_utils_test.cpp b/modules/gapi/test/streaming/gapi_streaming_utils_test.cpp new file mode 100644 index 0000000000..5599b8826f --- /dev/null +++ b/modules/gapi/test/streaming/gapi_streaming_utils_test.cpp @@ -0,0 +1,348 @@ +// This file is part of OpenCV project. +// It is subject to the license terms in the LICENSE file found in the top-level directory +// of this distribution and at http://opencv.org/license.html. +// +// Copyright (C) 2021 Intel Corporation + + +#include "../test_precomp.hpp" + +#include "../common/gapi_streaming_tests_common.hpp" + +#include +#include + +#define private public +#include "streaming/onevpl/accelerators/utils/shared_lock.hpp" +#undef private + +#include "streaming/onevpl/accelerators/utils/elastic_barrier.hpp" + +namespace opencv_test +{ +namespace +{ +using cv::gapi::wip::onevpl::SharedLock; + +struct TestBarrier : public cv::gapi::wip::onevpl::elastic_barrier { + void on_first_in_impl(size_t visitor_id) { + + static std::atomic thread_counter{}; + thread_counter++; + EXPECT_EQ(thread_counter.load(), 1); + + visitors_in.insert(visitor_id); + last_visitor_id = visitor_id; + + thread_counter--; + EXPECT_EQ(thread_counter.load(), 0); + } + + void on_last_out_impl(size_t visitor_id) { + + static std::atomic thread_counter{}; + thread_counter++; + EXPECT_EQ(thread_counter.load(), 1); + + visitors_out.insert(visitor_id); + last_visitor_id = visitor_id; + + thread_counter--; + EXPECT_EQ(thread_counter.load(), 0); + } + + size_t last_visitor_id = 0; + std::set visitors_in; + std::set visitors_out; +}; + +TEST(OneVPL_SharedLock, Create) { + SharedLock lock; + EXPECT_EQ(lock.shared_counter.load(), size_t{0}); +} + +TEST(OneVPL_SharedLock, Read_SingleThread) +{ + SharedLock lock; + + const size_t single_thread_read_count = 100; + for(size_t i = 0; i < single_thread_read_count; i++) { + lock.shared_lock(); + EXPECT_FALSE(lock.owns()); + } + EXPECT_EQ(lock.shared_counter.load(), single_thread_read_count); + + for(size_t i = 0; i < single_thread_read_count; i++) { + lock.unlock_shared(); + EXPECT_FALSE(lock.owns()); + } + + EXPECT_EQ(lock.shared_counter.load(), size_t{0}); +} + +TEST(OneVPL_SharedLock, TryLock_SingleThread) +{ + SharedLock lock; + + EXPECT_TRUE(lock.try_lock()); + EXPECT_TRUE(lock.owns()); + + lock.unlock(); + EXPECT_FALSE(lock.owns()); + EXPECT_EQ(lock.shared_counter.load(), size_t{0}); +} + +TEST(OneVPL_SharedLock, Write_SingleThread) +{ + SharedLock lock; + + lock.lock(); + EXPECT_TRUE(lock.owns()); + + lock.unlock(); + EXPECT_FALSE(lock.owns()); + EXPECT_EQ(lock.shared_counter.load(), size_t{0}); +} + +TEST(OneVPL_SharedLock, TryLockTryLock_SingleThread) +{ + SharedLock lock; + + lock.try_lock(); + EXPECT_FALSE(lock.try_lock()); + lock.unlock(); + + EXPECT_FALSE(lock.owns()); +} + +TEST(OneVPL_SharedLock, ReadTryLock_SingleThread) +{ + SharedLock lock; + + lock.shared_lock(); + EXPECT_FALSE(lock.owns()); + EXPECT_FALSE(lock.try_lock()); + lock.unlock_shared(); + + EXPECT_TRUE(lock.try_lock()); + EXPECT_TRUE(lock.owns()); + lock.unlock(); +} + +TEST(OneVPL_SharedLock, WriteTryLock_SingleThread) +{ + SharedLock lock; + + lock.lock(); + EXPECT_TRUE(lock.owns()); + EXPECT_FALSE(lock.try_lock()); + lock.unlock(); + + EXPECT_TRUE(lock.try_lock()); + EXPECT_TRUE(lock.owns()); + lock.unlock(); +} + + +TEST(OneVPL_SharedLock, Write_MultiThread) +{ + SharedLock lock; + + std::promise barrier; + std::shared_future sync = barrier.get_future(); + + static const size_t inc_count = 10000000; + size_t shared_value = 0; + auto work = [&lock, &shared_value](size_t count) { + for (size_t i = 0; i < count; i ++) { + lock.lock(); + shared_value ++; + lock.unlock(); + } + }; + + std::thread worker_thread([&barrier, sync, work] () { + + std::thread sub_worker([&barrier, work] () { + barrier.set_value(); + work(inc_count); + }); + + sync.wait(); + work(inc_count); + sub_worker.join(); + }); + sync.wait(); + + work(inc_count); + worker_thread.join(); + + EXPECT_EQ(shared_value, inc_count * 3); +} + +TEST(OneVPL_SharedLock, ReadWrite_MultiThread) +{ + SharedLock lock; + + std::promise barrier; + std::future sync = barrier.get_future(); + + static const size_t inc_count = 10000000; + size_t shared_value = 0; + auto write_work = [&lock, &shared_value](size_t count) { + for (size_t i = 0; i < count; i ++) { + lock.lock(); + shared_value ++; + lock.unlock(); + } + }; + + auto read_work = [&lock, &shared_value](size_t count) { + + auto old_shared_value = shared_value; + for (size_t i = 0; i < count; i ++) { + lock.shared_lock(); + EXPECT_TRUE(shared_value >= old_shared_value); + old_shared_value = shared_value; + lock.unlock_shared(); + } + }; + + std::thread writer_thread([&barrier, write_work] () { + barrier.set_value(); + write_work(inc_count); + }); + sync.wait(); + + read_work(inc_count); + writer_thread.join(); + + EXPECT_EQ(shared_value, inc_count); +} + + +TEST(OneVPL_ElasticBarrier, single_thread_visit) +{ + TestBarrier barrier; + + const size_t max_visit_count = 10000; + size_t visit_id = 0; + for (visit_id = 0; visit_id < max_visit_count; visit_id++) { + barrier.visit_in(visit_id); + EXPECT_EQ(barrier.visitors_in.size(), size_t{1}); + } + EXPECT_EQ(barrier.last_visitor_id, size_t{0}); + EXPECT_EQ(barrier.visitors_out.size(), size_t{0}); + + for (visit_id = 0; visit_id < max_visit_count; visit_id++) { + barrier.visit_out(visit_id); + EXPECT_EQ(barrier.visitors_in.size(), size_t{1}); + } + EXPECT_EQ(barrier.last_visitor_id, visit_id - 1); + EXPECT_EQ(barrier.visitors_out.size(), size_t{1}); +} + + +TEST(OneVPL_ElasticBarrier, multi_thread_visit) +{ + TestBarrier tested_barrier; + + static const size_t max_visit_count = 10000000; + std::atomic visit_in_wait_counter{}; + std::promise start_sync_barrier; + std::shared_future start_sync = start_sync_barrier.get_future(); + std::promise phase_sync_barrier; + std::shared_future phase_sync = phase_sync_barrier.get_future(); + + auto visit_worker_job = [&tested_barrier, + &visit_in_wait_counter, + start_sync, + phase_sync] (size_t worker_id) { + + start_sync.wait(); + + // first phase + const size_t begin_range = worker_id * max_visit_count; + const size_t end_range = (worker_id + 1) * max_visit_count; + for (size_t visit_id = begin_range; visit_id < end_range; visit_id++) { + tested_barrier.visit_in(visit_id); + } + + // notify all worker first phase ready + visit_in_wait_counter.fetch_add(1); + + // wait main second phase + phase_sync.wait(); + + // second phase + for (size_t visit_id = begin_range; visit_id < end_range; visit_id++) { + tested_barrier.visit_out(visit_id); + } + }; + + auto visit_main_job = [&tested_barrier, + &visit_in_wait_counter, + &phase_sync_barrier] (size_t total_workers_count, + size_t worker_id) { + + const size_t begin_range = worker_id * max_visit_count; + const size_t end_range = (worker_id + 1) * max_visit_count; + for (size_t visit_id = begin_range; visit_id < end_range; visit_id++) { + tested_barrier.visit_in(visit_id); + } + + // wait all workers first phase done + visit_in_wait_counter.fetch_add(1); + while (visit_in_wait_counter.load() != total_workers_count) { + std::this_thread::yield(); + }; + + // TEST invariant: last_visitor_id MUST be one from any FIRST worker visitor_id + bool one_of_available_ids_matched = false; + for (size_t id = 0; id < total_workers_count; id ++) { + size_t expected_last_visitor_for_id = id * max_visit_count; + one_of_available_ids_matched |= + (tested_barrier.last_visitor_id == expected_last_visitor_for_id) ; + } + EXPECT_TRUE(one_of_available_ids_matched); + + // unblock all workers to work out second phase + phase_sync_barrier.set_value(); + + // continue second phase + for (size_t visit_id = begin_range; visit_id < end_range; visit_id++) { + tested_barrier.visit_out(visit_id); + } + }; + + size_t max_worker_count = std::thread::hardware_concurrency(); + if (max_worker_count < 2) { + max_worker_count = 2; // logical 2 threads required at least + } + std::vector workers; + workers.reserve(max_worker_count); + for (size_t worker_id = 1; worker_id < max_worker_count; worker_id++) { + workers.emplace_back(visit_worker_job, worker_id); + } + + // let's go for first phase + start_sync_barrier.set_value(); + + // utilize main thread as well + visit_main_job(max_worker_count, 0); + + // join all threads second phase + for (auto& w : workers) { + w.join(); + } + + // TEST invariant: last_visitor_id MUST be one from any LATTER worker visitor_id + bool one_of_available_ids_matched = false; + for (size_t id = 0; id < max_worker_count; id ++) { + one_of_available_ids_matched |= + (tested_barrier.last_visitor_id == ((id + 1) * max_visit_count - 1)) ; + } + EXPECT_TRUE(one_of_available_ids_matched); +} +} +} // opencv_test diff --git a/modules/gapi/test/streaming/gapi_streaming_vpl_core_test.cpp b/modules/gapi/test/streaming/gapi_streaming_vpl_core_test.cpp index a84f92fafc..c62f58eecf 100644 --- a/modules/gapi/test/streaming/gapi_streaming_vpl_core_test.cpp +++ b/modules/gapi/test/streaming/gapi_streaming_vpl_core_test.cpp @@ -7,7 +7,7 @@ #include "../test_precomp.hpp" -#include "../common/gapi_tests_common.hpp" +#include "../common/gapi_streaming_tests_common.hpp" #include #include @@ -27,14 +27,16 @@ #include #include -#include - #ifdef HAVE_ONEVPL #include +#include "streaming/onevpl/cfg_param_device_selector.hpp" #include "streaming/onevpl/accelerators/surface/surface.hpp" #include "streaming/onevpl/accelerators/surface/cpu_frame_adapter.hpp" #include "streaming/onevpl/accelerators/accel_policy_cpu.hpp" +#include "streaming/onevpl/accelerators/accel_policy_dx11.hpp" +#include "streaming/onevpl/accelerators/dx11_alloc_resource.hpp" +#include "streaming/onevpl/accelerators/utils/shared_lock.hpp" #include "streaming/onevpl/engine/processing_engine_base.hpp" #include "streaming/onevpl/engine/engine_session.hpp" @@ -60,6 +62,11 @@ struct TestProcessingSession : public cv::gapi::wip::onevpl::EngineSession { TestProcessingSession(mfxSession mfx_session) : EngineSession(mfx_session, {}) { } + + const mfxVideoParam& get_video_param() const override { + static mfxVideoParam empty; + return empty; + } }; struct TestProcessingEngine: public cv::gapi::wip::onevpl::ProcessingEngineBase { @@ -98,13 +105,65 @@ struct TestProcessingEngine: public cv::gapi::wip::onevpl::ProcessingEngineBase ); } - void initialize_session(mfxSession mfx_session, - cv::gapi::wip::onevpl::DecoderParams&&, - std::shared_ptr) override { + std::shared_ptr + initialize_session(mfxSession mfx_session, + const std::vector&, + std::shared_ptr) override { + + return register_session(mfx_session); + } +}; - register_session(mfx_session); +template +class TestLockableAllocator { +public : + using self_t = TestLockableAllocator; + mfxFrameAllocator get() { + return m_allocator; } +private: + TestLockableAllocator(mfxFrameAllocator allocator) : + m_allocator(allocator) { + } + + static mfxStatus MFX_CDECL lock_cb(mfxHDL, mfxMemId mid, mfxFrameData *ptr) { + auto it = lock_processor_table.find(mid); + EXPECT_TRUE(it != lock_processor_table.end()); + return it->second(mid, ptr); + } + static mfxStatus MFX_CDECL unlock_cb(mfxHDL, mfxMemId mid, mfxFrameData *ptr) { + auto it = unlock_processor_table.find(mid); + EXPECT_TRUE(it != unlock_processor_table.end()); + return it->second(mid, ptr); + } + + template + friend TestLockableAllocator create_test_allocator(mfxMemId, L, U); + + static std::map lock_processor_table; + static std::map unlock_processor_table; + + mfxFrameAllocator m_allocator; }; +template +std::map TestLockableAllocator::lock_processor_table {}; + +template +std::map TestLockableAllocator::unlock_processor_table {}; + +template +TestLockableAllocator +create_test_allocator(mfxMemId mid, LockProcessor lock_p, UnlockProcessor unlock_p) { + mfxFrameAllocator allocator {}; + + TestLockableAllocator::lock_processor_table[mid] = lock_p; + allocator.Lock = &TestLockableAllocator::lock_cb; + + TestLockableAllocator::unlock_processor_table[mid] = unlock_p; + allocator.Unlock = &TestLockableAllocator::unlock_cb; + + return TestLockableAllocator {allocator}; +} cv::gapi::wip::onevpl::surface_ptr_t create_test_surface(std::shared_ptr out_buf_ptr, size_t, size_t) { @@ -262,8 +321,10 @@ TEST(OneVPL_Source_CPU_Accelerator, InitDestroy) { using cv::gapi::wip::onevpl::VPLCPUAccelerationPolicy; using cv::gapi::wip::onevpl::VPLAccelerationPolicy; + using cv::gapi::wip::onevpl::CfgParamDeviceSelector; - auto acceleration_policy = std::make_shared(); + auto acceleration_policy = + std::make_shared(std::make_shared()); size_t surface_count = 10; size_t surface_size_bytes = 1024; @@ -292,9 +353,11 @@ TEST(OneVPL_Source_CPU_Accelerator, PoolProduceConsume) { using cv::gapi::wip::onevpl::VPLCPUAccelerationPolicy; using cv::gapi::wip::onevpl::VPLAccelerationPolicy; + using cv::gapi::wip::onevpl::CfgParamDeviceSelector; using cv::gapi::wip::onevpl::Surface; - auto acceleration_policy = std::make_shared(); + auto acceleration_policy = + std::make_shared(std::make_shared()); size_t surface_count = 10; size_t surface_size_bytes = 1024; @@ -348,9 +411,11 @@ TEST(OneVPL_Source_CPU_Accelerator, PoolProduceConcurrentConsume) { using cv::gapi::wip::onevpl::VPLCPUAccelerationPolicy; using cv::gapi::wip::onevpl::VPLAccelerationPolicy; + using cv::gapi::wip::onevpl::CfgParamDeviceSelector; using cv::gapi::wip::onevpl::Surface; - auto acceleration_policy = std::make_shared(); + auto acceleration_policy = + std::make_shared(std::make_shared()); size_t surface_count = 10; size_t surface_size_bytes = 1024; @@ -416,7 +481,7 @@ TEST(OneVPL_Source_ProcessingEngine, Init) TestProcessingEngine engine(std::move(accel)); mfxSession mfx_session{}; - engine.initialize_session(mfx_session, DecoderParams{}, std::shared_ptr{}); + engine.initialize_session(mfx_session, {}, std::shared_ptr{}); EXPECT_EQ(0, engine.get_ready_frames_count()); ProcessingEngineBase::ExecutionStatus ret = engine.process(mfx_session); @@ -444,6 +509,181 @@ TEST(OneVPL_Source_ProcessingEngine, Init) cv::gapi::wip::Data frame; engine.get_frame(frame); } + +#ifdef HAVE_DIRECTX +#ifdef HAVE_D3D11 +TEST(OneVPL_Source_DX11_Accel, Init) +{ + using namespace cv::gapi::wip::onevpl; + + std::vector cfg_params_w_dx11; + cfg_params_w_dx11.push_back(CfgParam::create_acceleration_mode(MFX_ACCEL_MODE_VIA_D3D11)); + VPLDX11AccelerationPolicy accel(std::make_shared(cfg_params_w_dx11)); + + mfxLoader mfx_handle = MFXLoad(); + + mfxConfig cfg_inst_0 = MFXCreateConfig(mfx_handle); + EXPECT_TRUE(cfg_inst_0); + mfxVariant mfx_param_0; + mfx_param_0.Type = MFX_VARIANT_TYPE_U32; + mfx_param_0.Data.U32 = MFX_IMPL_TYPE_HARDWARE; + EXPECT_EQ(MFXSetConfigFilterProperty(cfg_inst_0,(mfxU8 *)CfgParam::implementation_name(), + mfx_param_0), MFX_ERR_NONE); + + mfxConfig cfg_inst_1 = MFXCreateConfig(mfx_handle); + EXPECT_TRUE(cfg_inst_1); + mfxVariant mfx_param_1; + mfx_param_1.Type = MFX_VARIANT_TYPE_U32; + mfx_param_1.Data.U32 = MFX_ACCEL_MODE_VIA_D3D11; + EXPECT_EQ(MFXSetConfigFilterProperty(cfg_inst_1,(mfxU8 *)CfgParam::acceleration_mode_name(), + mfx_param_1), MFX_ERR_NONE); + + mfxConfig cfg_inst_2 = MFXCreateConfig(mfx_handle); + EXPECT_TRUE(cfg_inst_2); + mfxVariant mfx_param_2; + mfx_param_2.Type = MFX_VARIANT_TYPE_U32; + mfx_param_2.Data.U32 = MFX_CODEC_HEVC; + EXPECT_EQ(MFXSetConfigFilterProperty(cfg_inst_2,(mfxU8 *)CfgParam::decoder_id_name(), + mfx_param_2), MFX_ERR_NONE); + + // create session + mfxSession mfx_session{}; + mfxStatus sts = MFXCreateSession(mfx_handle, 0, &mfx_session); + EXPECT_EQ(MFX_ERR_NONE, sts); + + // assign acceleration + EXPECT_NO_THROW(accel.init(mfx_session)); + + // create proper bitstream + mfxBitstream bitstream{}; + const int BITSTREAM_BUFFER_SIZE = 2000000; + bitstream.MaxLength = BITSTREAM_BUFFER_SIZE; + bitstream.Data = (mfxU8 *)calloc(bitstream.MaxLength, sizeof(mfxU8)); + EXPECT_TRUE(bitstream.Data); + + // simulate read stream + bitstream.DataOffset = 0; + bitstream.DataLength = sizeof(streaming::onevpl::hevc_header) * sizeof(streaming::onevpl::hevc_header[0]); + memcpy(bitstream.Data, streaming::onevpl::hevc_header, bitstream.DataLength); + bitstream.CodecId = MFX_CODEC_HEVC; + + // prepare dec params + mfxVideoParam mfxDecParams {}; + mfxDecParams.mfx.CodecId = bitstream.CodecId; + mfxDecParams.IOPattern = MFX_IOPATTERN_OUT_VIDEO_MEMORY; + sts = MFXVideoDECODE_DecodeHeader(mfx_session, &bitstream, &mfxDecParams); + EXPECT_EQ(MFX_ERR_NONE, sts); + + mfxFrameAllocRequest request{}; + memset(&request, 0, sizeof(request)); + sts = MFXVideoDECODE_QueryIOSurf(mfx_session, &mfxDecParams, &request); + EXPECT_EQ(MFX_ERR_NONE, sts); + + // Allocate surfaces for decoder + VPLAccelerationPolicy::pool_key_t key = accel.create_surface_pool(request, + mfxDecParams); + auto cand_surface = accel.get_free_surface(key).lock(); + + sts = MFXVideoDECODE_Init(mfx_session, &mfxDecParams); + EXPECT_EQ(MFX_ERR_NONE, sts); + + MFXVideoDECODE_Close(mfx_session); + EXPECT_EQ(MFX_ERR_NONE, sts); + + EXPECT_NO_THROW(accel.deinit(mfx_session)); + MFXClose(mfx_session); + MFXUnload(mfx_handle); +} +#endif // HAVE_DIRECTX +#endif // HAVE_D3D11 + +TEST(OneVPL_Source_DX11_FrameLockable, LockUnlock_without_Adaptee) +{ + using namespace cv::gapi::wip::onevpl; + mfxMemId mid = 0; + int lock_counter = 0; + int unlock_counter = 0; + + std::function lock = + [&lock_counter] (mfxMemId, mfxFrameData *) { + lock_counter ++; + return MFX_ERR_NONE; + }; + std::function unlock = + [&unlock_counter] (mfxMemId, mfxFrameData *) { + unlock_counter++; + return MFX_ERR_NONE; + }; + + auto test_allocator = create_test_allocator(mid, lock, unlock); + LockAdapter adapter(test_allocator.get()); + + mfxFrameData data; + const int exec_count = 123; + for (int i = 0; i < exec_count; i ++) { + EXPECT_EQ(adapter.read_lock(mid, data), 0); + adapter.write_lock(mid, data); + EXPECT_EQ(adapter.unlock_read(mid, data), 0); + adapter.unlock_write(mid, data); + } + + EXPECT_EQ(lock_counter, exec_count * 2); + EXPECT_EQ(unlock_counter, exec_count * 2); +} + +TEST(OneVPL_Source_DX11_FrameLockable, LockUnlock_with_Adaptee) +{ + using namespace cv::gapi::wip::onevpl; + mfxMemId mid = 0; + int r_lock_counter = 0; + int r_unlock_counter = 0; + int w_lock_counter = 0; + int w_unlock_counter = 0; + + SharedLock adaptee; + std::function lock = + [&r_lock_counter, &w_lock_counter, &adaptee] (mfxMemId, mfxFrameData *) { + if (adaptee.owns()) { + w_lock_counter ++; + } else { + r_lock_counter ++; + } + return MFX_ERR_NONE; + }; + std::function unlock = + [&r_unlock_counter, &w_unlock_counter, &adaptee] (mfxMemId, mfxFrameData *) { + if (adaptee.owns()) { + w_unlock_counter ++; + } else { + r_unlock_counter ++; + } + return MFX_ERR_NONE; + }; + + auto test_allocator = create_test_allocator(mid, lock, unlock); + LockAdapter adapter(test_allocator.get()); + + adapter.set_adaptee(&adaptee); + + mfxFrameData data; + const int exec_count = 123; + for (int i = 0; i < exec_count; i ++) { + EXPECT_EQ(adapter.read_lock(mid, data), 0); + EXPECT_FALSE(adaptee.try_lock()); + + EXPECT_EQ(adapter.unlock_read(mid, data), 1); + EXPECT_TRUE(adaptee.try_lock()); + adaptee.unlock(); + + adapter.write_lock(mid, data); + adapter.unlock_write(mid, data); + } + + EXPECT_EQ(r_lock_counter, exec_count); + EXPECT_EQ(r_unlock_counter, exec_count); + EXPECT_EQ(w_lock_counter, exec_count); + EXPECT_EQ(w_unlock_counter, exec_count); +} } } // namespace opencv_test #endif // HAVE_ONEVPL diff --git a/modules/gapi/test/streaming/gapi_streaming_vpl_data_provider.cpp b/modules/gapi/test/streaming/gapi_streaming_vpl_data_provider.cpp index 4e797ae9ef..c8c27fa6a4 100644 --- a/modules/gapi/test/streaming/gapi_streaming_vpl_data_provider.cpp +++ b/modules/gapi/test/streaming/gapi_streaming_vpl_data_provider.cpp @@ -39,8 +39,6 @@ array_element_t files[] = { true, true, true}, array_element_t {"highgui/video/sample_322x242_15frames.yuv420p.libvpx-vp9.mp4", true, true, true}, - array_element_t {"highgui/video/sample_322x242_15frames.yuv420p.libx264.avi", - true, true, true}, array_element_t {"highgui/video/sample_322x242_15frames.yuv420p.libx264.mp4", true, true, true}, array_element_t {"highgui/video/sample_322x242_15frames.yuv420p.libx265.mp4", @@ -82,7 +80,7 @@ TEST_P(OneVPL_Source_MFPAsyncDispatcherTest, open_and_decode_file) mfxVariant mfx_param_0; mfx_param_0.Type = MFX_VARIANT_TYPE_U32; mfx_param_0.Data.U32 = provider_ptr->get_mfx_codec_id(); - EXPECT_EQ(MFXSetConfigFilterProperty(cfg_inst_0,(mfxU8 *)"mfxImplDescription.mfxDecoderDescription.decoder.CodecID", + EXPECT_EQ(MFXSetConfigFilterProperty(cfg_inst_0,(mfxU8 *)CfgParam::decoder_id_name(), mfx_param_0), MFX_ERR_NONE); // create MFX session @@ -135,7 +133,7 @@ TEST_P(OneVPL_Source_MFPAsyncDispatcherTest, choose_dmux_provider) EXPECT_FALSE(dd_result); provider_ptr = DataProviderDispatcher::create(path, { CfgParam::create( - "mfxImplDescription.mfxDecoderDescription.decoder.CodecID", + CfgParam::decoder_id_name(), "MFX_CODEC_HEVC") /* Doesn't matter what codec for RAW here*/}); EXPECT_TRUE(std::dynamic_pointer_cast(provider_ptr)); GTEST_SUCCEED(); diff --git a/modules/gapi/test/streaming/gapi_streaming_vpl_device_selector.cpp b/modules/gapi/test/streaming/gapi_streaming_vpl_device_selector.cpp index 2f42742b88..d484dcec75 100644 --- a/modules/gapi/test/streaming/gapi_streaming_vpl_device_selector.cpp +++ b/modules/gapi/test/streaming/gapi_streaming_vpl_device_selector.cpp @@ -30,7 +30,7 @@ #endif // HAVE_DIRECTX #ifdef HAVE_ONEVPL -#include +#include "streaming/onevpl/onevpl_export.hpp" #include "streaming/onevpl/cfg_param_device_selector.hpp" namespace opencv_test @@ -94,8 +94,7 @@ TEST(OneVPL_Source_Device_Selector_CfgParam, DefaultDeviceWithAccelNACfgParam) { using namespace cv::gapi::wip::onevpl; std::vector cfg_params_w_no_accel; - cfg_params_w_no_accel.push_back(CfgParam::create("mfxImplDescription.AccelerationMode", - MFX_ACCEL_MODE_NA)); + cfg_params_w_no_accel.push_back(CfgParam::create_acceleration_mode(MFX_ACCEL_MODE_NA)); CfgParamDeviceSelector selector(cfg_params_w_no_accel); IDeviceSelector::DeviceScoreTable devs = selector.select_devices(); EXPECT_EQ(devs.size(), 1); @@ -126,8 +125,7 @@ TEST(OneVPL_Source_Device_Selector_CfgParam, DefaultDeviceWithDX11AccelCfgParam_ { using namespace cv::gapi::wip::onevpl; std::vector cfg_params_w_dx11; - cfg_params_w_dx11.push_back(CfgParam::create("mfxImplDescription.AccelerationMode", - MFX_ACCEL_MODE_VIA_D3D11)); + cfg_params_w_dx11.push_back(CfgParam::create_acceleration_mode(MFX_ACCEL_MODE_VIA_D3D11)); std::unique_ptr selector_ptr; EXPECT_NO_THROW(selector_ptr.reset(new CfgParamDeviceSelector(cfg_params_w_dx11))); IDeviceSelector::DeviceScoreTable devs = selector_ptr->select_devices(); @@ -146,8 +144,7 @@ TEST(OneVPL_Source_Device_Selector_CfgParam, NULLDeviceWithDX11AccelCfgParam_DX1 { using namespace cv::gapi::wip::onevpl; std::vector cfg_params_w_dx11; - cfg_params_w_dx11.push_back(CfgParam::create("mfxImplDescription.AccelerationMode", - MFX_ACCEL_MODE_VIA_D3D11)); + cfg_params_w_dx11.push_back(CfgParam::create_acceleration_mode(MFX_ACCEL_MODE_VIA_D3D11)); Device::Ptr empty_device_ptr = nullptr; Context::Ptr empty_ctx_ptr = nullptr; EXPECT_THROW(CfgParamDeviceSelector sel(empty_device_ptr, "GPU", @@ -179,8 +176,7 @@ TEST(OneVPL_Source_Device_Selector_CfgParam, ExternalDeviceWithDX11AccelCfgParam std::unique_ptr selector_ptr; std::vector cfg_params_w_dx11; - cfg_params_w_dx11.push_back(CfgParam::create("mfxImplDescription.AccelerationMode", - MFX_ACCEL_MODE_VIA_D3D11)); + cfg_params_w_dx11.push_back(CfgParam::create_acceleration_mode(MFX_ACCEL_MODE_VIA_D3D11)); EXPECT_NO_THROW(selector_ptr.reset(new CfgParamDeviceSelector(device, "GPU", device_context, cfg_params_w_dx11))); @@ -205,8 +201,7 @@ TEST(OneVPL_Source_Device_Selector_CfgParam, DX11DeviceFromCfgParamWithDX11Disab { using namespace cv::gapi::wip::onevpl; std::vector cfg_params_w_non_existed_dx11; - cfg_params_w_not_existed_dx11.push_back(CfgParam::create("mfxImplDescription.AccelerationMode", - MFX_ACCEL_MODE_VIA_D3D11)); + cfg_params_w_not_existed_dx11.push_back(CfgParam::create_acceleration_mode(MFX_ACCEL_MODE_VIA_D3D11)); EXPECT_THROW(CfgParamDeviceSelector{cfg_params_w_non_existed_dx11}, std::logic_error); }