From 54386c82fd5ca3079f60d1758bb1d25651a329c9 Mon Sep 17 00:00:00 2001 From: Sergey Ivanov Date: Thu, 23 Sep 2021 14:34:30 +0300 Subject: [PATCH] Merge pull request #20727 from sivanov-work:merge_vpl_accel_impl G-API: oneVPL (simplification) added CPU, DX11(fallback CPU) accels & surface pool * Add CPU, DX11(fallback CPU) accels & surface pool * Fix build for surface_pool * Apply some comments * Fix indentation --- modules/gapi/CMakeLists.txt | 6 + .../onevpl/accelerators/accel_policy_cpu.cpp | 225 ++++++++++++++++++ .../onevpl/accelerators/accel_policy_cpu.hpp | 55 +++++ .../onevpl/accelerators/accel_policy_dx11.cpp | 114 +++++++++ .../onevpl/accelerators/accel_policy_dx11.hpp | 67 ++++++ .../accelerators/surface/surface_pool.cpp | 69 ++++++ .../accelerators/surface/surface_pool.hpp | 45 ++++ .../gapi_streaming_vpl_core_test.cpp | 161 ++++++++++++- 8 files changed, 741 insertions(+), 1 deletion(-) create mode 100644 modules/gapi/src/streaming/onevpl/accelerators/accel_policy_cpu.cpp create mode 100644 modules/gapi/src/streaming/onevpl/accelerators/accel_policy_cpu.hpp create mode 100644 modules/gapi/src/streaming/onevpl/accelerators/accel_policy_dx11.cpp create mode 100644 modules/gapi/src/streaming/onevpl/accelerators/accel_policy_dx11.hpp create mode 100644 modules/gapi/src/streaming/onevpl/accelerators/surface/surface_pool.cpp create mode 100644 modules/gapi/src/streaming/onevpl/accelerators/surface/surface_pool.hpp diff --git a/modules/gapi/CMakeLists.txt b/modules/gapi/CMakeLists.txt index 56d051cf0f..380f1d7d84 100644 --- a/modules/gapi/CMakeLists.txt +++ b/modules/gapi/CMakeLists.txt @@ -171,6 +171,9 @@ set(gapi_srcs src/streaming/onevpl/data_provider_interface_exception.cpp src/streaming/onevpl/accelerators/surface/cpu_frame_adapter.cpp src/streaming/onevpl/accelerators/surface/surface.cpp + src/streaming/onevpl/accelerators/surface/surface_pool.cpp + src/streaming/onevpl/accelerators/accel_policy_cpu.cpp + src/streaming/onevpl/accelerators/accel_policy_dx11.cpp # Utils (ITT tracing) src/utils/itt.cpp @@ -247,6 +250,9 @@ 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(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}) diff --git a/modules/gapi/src/streaming/onevpl/accelerators/accel_policy_cpu.cpp b/modules/gapi/src/streaming/onevpl/accelerators/accel_policy_cpu.cpp new file mode 100644 index 0000000000..0a5f68ae4e --- /dev/null +++ b/modules/gapi/src/streaming/onevpl/accelerators/accel_policy_cpu.cpp @@ -0,0 +1,225 @@ +// 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 +#include + +#include "streaming/onevpl/accelerators/accel_policy_cpu.hpp" +#include "streaming/onevpl/accelerators/surface/cpu_frame_adapter.hpp" +#include "streaming/onevpl/accelerators/surface/surface.hpp" +#include "logger.hpp" + +#ifdef _WIN32 + #include + #include +#endif +namespace cv { +namespace gapi { +namespace wip { + +VPLCPUAccelerationPolicy::VPLCPUAccelerationPolicy() { + GAPI_LOG_INFO(nullptr, "created"); +} + +VPLCPUAccelerationPolicy::~VPLCPUAccelerationPolicy() { + for (auto& pair : pool_table) { + pair.second.clear(); + // do not free key here: last surface will release it + } + GAPI_LOG_INFO(nullptr, "destroyed"); +} + +void VPLCPUAccelerationPolicy::init(session_t session) { + GAPI_LOG_INFO(nullptr, "initialize session: " << session); +} + +void VPLCPUAccelerationPolicy::deinit(session_t session) { + GAPI_LOG_INFO(nullptr, "deinitialize session: " << session); +} + +VPLCPUAccelerationPolicy::pool_key_t +VPLCPUAccelerationPolicy::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); + + // create empty pool + pool_t pool; + pool.reserve(pool_size); + + // allocate workplace memory area + size_t preallocated_raw_bytes = pool_size * surface_size_bytes; + size_t page_size_bytes = 4 * 1024; + void *preallocated_pool_memory_ptr = nullptr; + +#ifdef _WIN32 + SYSTEM_INFO sysInfo; + GetSystemInfo(&sysInfo); + page_size_bytes = sysInfo.dwPageSize; + + GAPI_LOG_DEBUG(nullptr, "page size: " << page_size_bytes << ", preallocated_raw_bytes: " << preallocated_raw_bytes); + preallocated_pool_memory_ptr = _aligned_malloc(preallocated_raw_bytes, page_size_bytes); +#else + GAPI_Assert(false && "Compatibility is not tested for systems differ than \"_WIN32\". " + "Please feel free to set it up under OPENCV contribution policy"); +#endif + + if (!preallocated_pool_memory_ptr) { + throw std::runtime_error("VPLCPUAccelerationPolicy::create_surface_pool - failed: not enough memory." + "Requested surface count: " + std::to_string(pool_size) + + ", surface bytes: " + std::to_string(surface_size_bytes)); + } + + // fill pool with surfaces + std::shared_ptr workspace_mem_owner (preallocated_pool_memory_ptr, [] (void *ptr){ + GAPI_LOG_INFO(nullptr, "Free workspace memory: " << ptr); +#ifdef _WIN32 + _aligned_free(ptr); + GAPI_LOG_INFO(nullptr, "Released workspace memory: " << ptr); + ptr = nullptr; +#else + GAPI_Assert(false && "Not implemented for systems differ than \"_WIN32\". " + "Please feel free to set it up under OPENCV contribution policy"); +#endif + + }); + size_t i = 0; + try { + for (; i < pool_size; i++) { + size_t preallocated_mem_offset = static_cast(i) * surface_size_bytes; + + surface_ptr_t surf_ptr = creator(workspace_mem_owner, + preallocated_mem_offset, + preallocated_raw_bytes); + pool.push_back(std::move(surf_ptr)); + } + } catch (const std::exception& ex) { + throw std::runtime_error(std::string("VPLCPUAccelerationPolicy::create_surface_pool - ") + + "cannot construct surface index: " + std::to_string(i) + ", error: " + + ex.what() + + "Requested surface count: " + std::to_string(pool_size) + + ", surface bytes: " + std::to_string(surface_size_bytes)); + } + + // remember pool by key + GAPI_LOG_INFO(nullptr, "New pool allocated, key: " << preallocated_pool_memory_ptr << + ", surface count: " << pool.size() << + ", surface size bytes: " << surface_size_bytes); + if (!pool_table.emplace(preallocated_pool_memory_ptr, std::move(pool)).second) { + GAPI_LOG_WARNING(nullptr, "Cannot insert pool, table size: " + std::to_string(pool_table.size()) << + ", key: " << preallocated_pool_memory_ptr << " exists"); + GAPI_Assert(false && "Cannot create pool in VPLCPUAccelerationPolicy"); + } + + return preallocated_pool_memory_ptr; +} + +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()) { + throw std::runtime_error("VPLCPUAccelerationPolicy::get_free_surface - " + "key is not found, table size: " + + std::to_string(pool_table.size())); + } + + pool_t& requested_pool = pool_it->second; +#ifdef TEST_PERF + return requested_pool.find_free(); +#else // TEST_PERF + auto it = + std::find_if(requested_pool.begin(), requested_pool.end(), + [](const surface_ptr_t& val) { + GAPI_DbgAssert(val && "Pool contains empty surface"); + return !val->get_locks_count(); + }); + + // Limitation realloc pool might be a future extension + if (it == requested_pool.end()) { + std::stringstream ss; + ss << "cannot get free surface from pool, key: " << key << ", size: " << requested_pool.size(); + const std::string& str = ss.str(); + GAPI_LOG_WARNING(nullptr, str); + throw std::runtime_error(std::string(__FUNCTION__) + " - " + str); + } + + return *it; +#endif // TEST_PERF +} + +size_t VPLCPUAccelerationPolicy::get_free_surface_count(pool_key_t key) const { + auto pool_it = pool_table.find(key); + if (pool_it == pool_table.end()) { + GAPI_LOG_WARNING(nullptr, "key is not found: " << key << + ", table size: " << pool_table.size()); + return 0; + } +#ifdef TEST_PERF + return 0; +#else // TEST_PERF + const pool_t& requested_pool = pool_it->second; + size_t free_surf_count = + std::count_if(requested_pool.begin(), requested_pool.end(), + [](const surface_ptr_t& val) { + GAPI_Assert(val && "Pool contains empty surface"); + return !val->get_locks_count(); + }); + return free_surf_count; +#endif // TEST_PERF +} + +size_t VPLCPUAccelerationPolicy::get_surface_count(pool_key_t key) const { + auto pool_it = pool_table.find(key); + if (pool_it == pool_table.end()) { + GAPI_LOG_DEBUG(nullptr, "key is not found: " << key << + ", table size: " << pool_table.size()); + return 0; + } +#ifdef TEST_PERF + return 0; +#else // TEST_PERF + return pool_it->second.size(); +#endif // TEST_PERF +} + +cv::MediaFrame::AdapterPtr VPLCPUAccelerationPolicy::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); + } + + pool_t& requested_pool = pool_it->second; +#ifdef TEST_PERF + return cv::MediaFrame::AdapterPtr{new VPLMediaFrameCPUAdapter(requested_pool.find_by_handle(surface))}; +#else // TEST_PERF + auto it = + std::find_if(requested_pool.begin(), requested_pool.end(), + [surface](const surface_ptr_t& val) { + GAPI_DbgAssert(val && "Pool contains empty surface"); + return val->get_handle() == surface; + }); + + // Limitation realloc pool might be a future extension + if (it == requested_pool.end()) { + std::stringstream ss; + ss << "cannot get requested surface from pool, key: " << key << ", surf: " + << surface << ", pool size: " << requested_pool.size(); + const std::string& str = ss.str(); + GAPI_LOG_WARNING(nullptr, str); + throw std::runtime_error(std::string(__FUNCTION__) + " - " + str); + } + + return cv::MediaFrame::AdapterPtr{new VPLMediaFrameCPUAdapter(*it)}; +#endif // TEST_PERF +} +} // namespace wip +} // namespace gapi +} // namespace cv +#endif // HAVE_ONEVPL diff --git a/modules/gapi/src/streaming/onevpl/accelerators/accel_policy_cpu.hpp b/modules/gapi/src/streaming/onevpl/accelerators/accel_policy_cpu.hpp new file mode 100644 index 0000000000..cfe3057315 --- /dev/null +++ b/modules/gapi/src/streaming/onevpl/accelerators/accel_policy_cpu.hpp @@ -0,0 +1,55 @@ +// 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_ACCEL_POLICY_CPU_HPP +#define GAPI_STREAMING_ONEVPL_ACCELERATORS_ACCEL_POLICY_CPU_HPP + +#include +#include + +#include "opencv2/gapi/own/exports.hpp" // GAPI_EXPORTS + +#ifdef HAVE_ONEVPL +#include +#include "streaming/onevpl/accelerators/accel_policy_interface.hpp" +#ifdef TEST_PERF +#include "streaming/onevpl/accelerators/surface/surface_pool.hpp" +#endif // TEST_PERF + +namespace cv { +namespace gapi { +namespace wip { + +// GAPI_EXPORTS for tests +struct GAPI_EXPORTS VPLCPUAccelerationPolicy final : public VPLAccelerationPolicy +{ + VPLCPUAccelerationPolicy(); + ~VPLCPUAccelerationPolicy(); +#ifdef TEST_PERF + using pool_t = CachedPool; +#else // TEST_PERF + using pool_t = std::vector; +#endif // TEST_PERF + + 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; + 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; + +private: + std::map pool_table; +}; +} // namespace wip +} // namespace gapi +} // namespace cv + +#endif // HAVE_ONEVPL +#endif // GAPI_STREAMING_ONEVPL_ACCELERATORS_ACCEL_POLICY_CPU_HPP diff --git a/modules/gapi/src/streaming/onevpl/accelerators/accel_policy_dx11.cpp b/modules/gapi/src/streaming/onevpl/accelerators/accel_policy_dx11.cpp new file mode 100644 index 0000000000..348b864a15 --- /dev/null +++ b/modules/gapi/src/streaming/onevpl/accelerators/accel_policy_dx11.cpp @@ -0,0 +1,114 @@ +// 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/accel_policy_dx11.hpp" +#include "streaming/onevpl/accelerators/surface/cpu_frame_adapter.hpp" +#include "streaming/onevpl/accelerators/surface/surface.hpp" +#include "logger.hpp" + +#ifdef HAVE_DIRECTX +#ifdef HAVE_D3D11 +#pragma comment(lib,"d3d11.lib") + +#define D3D11_NO_HELPERS +#include +#include +#include +#include "opencv2/core/directx.hpp" + +#ifdef HAVE_OPENCL +#include +#endif + +namespace cv { +namespace gapi { +namespace wip { +VPLDX11AccelerationPolicy::VPLDX11AccelerationPolicy() +{ +#ifdef CPU_ACCEL_ADAPTER + adapter.reset(new VPLCPUAccelerationPolicy); +#endif +} + +VPLDX11AccelerationPolicy::~VPLDX11AccelerationPolicy() +{ + if (hw_handle) + { + GAPI_LOG_INFO(nullptr, "VPLDX11AccelerationPolicy release ID3D11Device"); + hw_handle->Release(); + } +} + +void VPLDX11AccelerationPolicy::init(session_t session) { + mfxStatus sts = MFXVideoCORE_GetHandle(session, MFX_HANDLE_D3D11_DEVICE, reinterpret_cast(&hw_handle)); + if (sts != MFX_ERR_NONE) + { + throw std::logic_error("Cannot create VPLDX11AccelerationPolicy, MFXVideoCORE_GetHandle error"); + } + + GAPI_LOG_INFO(nullptr, "VPLDX11AccelerationPolicy initialized, session: " << session); +} + +void VPLDX11AccelerationPolicy::deinit(session_t session) { + GAPI_LOG_INFO(nullptr, "deinitialize session: " << 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); + +#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"); +} + +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"); +} + +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_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"); +} + +cv::MediaFrame::AdapterPtr VPLDX11AccelerationPolicy::create_frame_adapter(pool_key_t key, + mfxFrameSurface1* surface) { + +#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"); +} +} // namespace wip +} // namespace gapi +} // namespace cv +#endif // HAVE_D3D11 +#endif // HAVE_DIRECTX +#endif // HAVE_ONEVPL diff --git a/modules/gapi/src/streaming/onevpl/accelerators/accel_policy_dx11.hpp b/modules/gapi/src/streaming/onevpl/accelerators/accel_policy_dx11.hpp new file mode 100644 index 0000000000..04970432c5 --- /dev/null +++ b/modules/gapi/src/streaming/onevpl/accelerators/accel_policy_dx11.hpp @@ -0,0 +1,67 @@ +// 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_ACCEL_POLICY_DX11_HPP +#define GAPI_STREAMING_ONEVPL_ACCELERATORS_ACCEL_POLICY_DX11_HPP + +#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 + +#ifdef HAVE_DIRECTX +#ifdef HAVE_D3D11 +#define D3D11_NO_HELPERS +#include +#include +#include "opencv2/core/directx.hpp" +#ifdef HAVE_OPENCL +#include +#endif + +namespace cv { +namespace gapi { +namespace wip { + +// GAPI_EXPORTS for tests +struct GAPI_EXPORTS VPLDX11AccelerationPolicy final: public VPLAccelerationPolicy +{ + // GAPI_EXPORTS for tests + VPLDX11AccelerationPolicy(); + ~VPLDX11AccelerationPolicy(); + + 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; + 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; + +private: + ID3D11Device *hw_handle; + +#ifdef CPU_ACCEL_ADAPTER + std::unique_ptr adapter; +#endif +}; +} // namespace wip +} // namespace gapi +} // namespace cv +#endif // HAVE_D3D11 +#endif // HAVE_DIRECTX + +#endif // HAVE_ONEVPL +#endif // GAPI_STREAMING_ONEVPL_ACCELERATORS_ACCEL_POLICY_DX11_HPP diff --git a/modules/gapi/src/streaming/onevpl/accelerators/surface/surface_pool.cpp b/modules/gapi/src/streaming/onevpl/accelerators/surface/surface_pool.cpp new file mode 100644 index 0000000000..8ead7965b4 --- /dev/null +++ b/modules/gapi/src/streaming/onevpl/accelerators/surface/surface_pool.cpp @@ -0,0 +1,69 @@ +#include "streaming/onevpl/accelerators/surface/surface_pool.hpp" +#include "streaming/onevpl/accelerators/surface/surface.hpp" +#include "logger.hpp" + +#ifdef HAVE_ONEVPL + +namespace cv { +namespace gapi { +namespace wip { + +void CachedPool::reserve(size_t size) { + surfaces.reserve(size); +} + +size_t CachedPool::size() const { + return surfaces.size(); +} + +void CachedPool::clear() { + surfaces.clear(); + next_free_it = surfaces.begin(); + cache.clear(); +} + +void CachedPool::push_back(surface_ptr_t &&surf) { + cache.insert(std::make_pair(surf->get_handle(), surf)); + surfaces.push_back(std::move(surf)); + next_free_it = surfaces.begin(); +} + +CachedPool::surface_ptr_t CachedPool::find_free() { + auto it = + std::find_if(next_free_it, surfaces.end(), + [](const surface_ptr_t& val) { + GAPI_DbgAssert(val && "Pool contains empty surface"); + return !val->get_locks_count(); + }); + + // Limitation realloc pool might be a future extension + if (it == surfaces.end()) { + it = std::find_if(surfaces.begin(), next_free_it, + [](const surface_ptr_t& val) { + GAPI_DbgAssert(val && "Pool contains empty surface"); + return !val->get_locks_count(); + }); + if (it == next_free_it) { + std::stringstream ss; + ss << "cannot get free surface from pool, size: " << surfaces.size(); + const std::string& str = ss.str(); + GAPI_LOG_WARNING(nullptr, str); + throw std::runtime_error(std::string(__FUNCTION__) + " - " + str); + } + } + + next_free_it = it; + ++next_free_it; + + return *it; +} + +CachedPool::surface_ptr_t CachedPool::find_by_handle(mfxFrameSurface1* handle) { + auto it = cache.find(handle); + GAPI_Assert(it != cache.end() && "Cannot find cached surface from pool. Data corruption is possible"); + return it->second; +} +} // namespace wip +} // namespace gapi +} // namespace cv +#endif // HAVE_ONEVPL diff --git a/modules/gapi/src/streaming/onevpl/accelerators/surface/surface_pool.hpp b/modules/gapi/src/streaming/onevpl/accelerators/surface/surface_pool.hpp new file mode 100644 index 0000000000..2059e9b27e --- /dev/null +++ b/modules/gapi/src/streaming/onevpl/accelerators/surface/surface_pool.hpp @@ -0,0 +1,45 @@ +#ifndef GAPI_STREAMING_ONEVPL_SURFACE_SURFACE_POOL_HPP +#define GAPI_STREAMING_ONEVPL_SURFACE_SURFACE_POOL_HPP + +#include +#include +#include + +#include "opencv2/gapi/own/exports.hpp" // GAPI_EXPORTS + +#ifdef HAVE_ONEVPL +#if (MFX_VERSION >= 2000) +#include +#endif + +#include + +namespace cv { +namespace gapi { +namespace wip { + +class Surface; +// GAPI_EXPORTS for tests +class GAPI_EXPORTS CachedPool { +public: + using surface_ptr_t = std::shared_ptr; + using surface_container_t = std::vector; + using free_surface_iterator_t = typename surface_container_t::iterator; + using cached_surface_container_t = std::map; + + void push_back(surface_ptr_t &&surf); + void reserve(size_t size); + size_t size() const; + void clear(); + surface_ptr_t find_free(); + surface_ptr_t find_by_handle(mfxFrameSurface1* handle); +private: + surface_container_t surfaces; + free_surface_iterator_t next_free_it; + cached_surface_container_t cache; +}; +} // namespace wip +} // namespace gapi +} // namespace cv +#endif // HAVE_ONEVPL +#endif // GAPI_STREAMING_ONEVPL_SURFACE_SURFACE_POOL_HPP 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 be905029ba..5a36a2befb 100644 --- a/modules/gapi/test/streaming/gapi_streaming_vpl_core_test.cpp +++ b/modules/gapi/test/streaming/gapi_streaming_vpl_core_test.cpp @@ -9,6 +9,7 @@ #include "../common/gapi_tests_common.hpp" +#include #include #include @@ -30,11 +31,18 @@ #ifdef HAVE_ONEVPL #include "streaming/onevpl/accelerators/surface/surface.hpp" #include "streaming/onevpl/accelerators/surface/cpu_frame_adapter.hpp" +#include "streaming/onevpl/accelerators/accel_policy_cpu.hpp" namespace opencv_test { namespace { +cv::gapi::wip::surface_ptr_t create_test_surface(std::shared_ptr out_buf_ptr, + size_t, size_t) { + std::unique_ptr handle(new mfxFrameSurface1{}); + return cv::gapi::wip::Surface::create_surface(std::move(handle), out_buf_ptr); +} + TEST(OneVPL_Source_Surface, InitSurface) { using namespace cv::gapi::wip; @@ -160,7 +168,7 @@ TEST(OneVPL_Source_Surface, MemoryLifeTime) EXPECT_TRUE(preallocated_memory_ptr.get() == nullptr); } -TEST(OneVPL_Source_CPUFrameAdapter, InitFrameAdapter) +TEST(OneVPL_Source_CPU_FrameAdapter, InitFrameAdapter) { using namespace cv::gapi::wip; @@ -180,6 +188,157 @@ TEST(OneVPL_Source_CPUFrameAdapter, InitFrameAdapter) } EXPECT_EQ(surf->get_locks_count(), 0); } + +TEST(OneVPL_Source_CPU_Accelerator, InitDestroy) +{ + using cv::gapi::wip::VPLCPUAccelerationPolicy; + using cv::gapi::wip::VPLAccelerationPolicy; + + auto acceleration_policy = std::make_shared(); + + size_t surface_count = 10; + size_t surface_size_bytes = 1024; + size_t pool_count = 3; + std::vector pool_export_keys; + pool_export_keys.reserve(pool_count); + + // create several pools + for (size_t i = 0; i < pool_count; i++) + { + VPLAccelerationPolicy::pool_key_t key = + acceleration_policy->create_surface_pool(surface_count, + surface_size_bytes, + create_test_surface); + // check consistency + EXPECT_EQ(acceleration_policy->get_surface_count(key), surface_count); + EXPECT_EQ(acceleration_policy->get_free_surface_count(key), surface_count); + + pool_export_keys.push_back(key); + } + + EXPECT_NO_THROW(acceleration_policy.reset()); +} + +TEST(OneVPL_Source_CPU_Accelerator, PoolProduceConsume) +{ + using cv::gapi::wip::VPLCPUAccelerationPolicy; + using cv::gapi::wip::VPLAccelerationPolicy; + using cv::gapi::wip::Surface; + + auto acceleration_policy = std::make_shared(); + + size_t surface_count = 10; + size_t surface_size_bytes = 1024; + + VPLAccelerationPolicy::pool_key_t key = + acceleration_policy->create_surface_pool(surface_count, + surface_size_bytes, + create_test_surface); + // check consistency + EXPECT_EQ(acceleration_policy->get_surface_count(key), surface_count); + EXPECT_EQ(acceleration_policy->get_free_surface_count(key), surface_count); + + // consume available surfaces + std::vector> surfaces; + surfaces.reserve(surface_count); + for (size_t i = 0; i < surface_count; i++) { + std::shared_ptr surf = acceleration_policy->get_free_surface(key).lock(); + EXPECT_TRUE(surf.get() != nullptr); + EXPECT_EQ(surf->obtain_lock(), 0); + surfaces.push_back(std::move(surf)); + } + + // check consistency (no free surfaces) + EXPECT_EQ(acceleration_policy->get_surface_count(key), surface_count); + EXPECT_EQ(acceleration_policy->get_free_surface_count(key), 0); + + // fail consume non-free surfaces + for (size_t i = 0; i < surface_count; i++) { + EXPECT_THROW(acceleration_policy->get_free_surface(key), std::runtime_error); + } + + // release surfaces + for (auto& surf : surfaces) { + EXPECT_EQ(surf->release_lock(), 1); + } + surfaces.clear(); + + // check consistency + EXPECT_EQ(acceleration_policy->get_surface_count(key), surface_count); + EXPECT_EQ(acceleration_policy->get_free_surface_count(key), surface_count); + + //check availability after release + for (size_t i = 0; i < surface_count; i++) { + std::shared_ptr surf = acceleration_policy->get_free_surface(key).lock(); + EXPECT_TRUE(surf.get() != nullptr); + EXPECT_EQ(surf->obtain_lock(), 0); + } +} + +TEST(OneVPL_Source_CPU_Accelerator, PoolProduceConcurrentConsume) +{ + using cv::gapi::wip::VPLCPUAccelerationPolicy; + using cv::gapi::wip::VPLAccelerationPolicy; + using cv::gapi::wip::Surface; + + auto acceleration_policy = std::make_shared(); + + size_t surface_count = 10; + size_t surface_size_bytes = 1024; + + VPLAccelerationPolicy::pool_key_t key = + acceleration_policy->create_surface_pool(surface_count, + surface_size_bytes, + create_test_surface); + + // check consistency + EXPECT_EQ(acceleration_policy->get_surface_count(key), surface_count); + EXPECT_EQ(acceleration_policy->get_free_surface_count(key), surface_count); + + // consume available surfaces + std::vector> surfaces; + surfaces.reserve(surface_count); + for (size_t i = 0; i < surface_count; i++) { + std::shared_ptr surf = acceleration_policy->get_free_surface(key).lock(); + EXPECT_TRUE(surf.get() != nullptr); + EXPECT_EQ(surf->obtain_lock(), 0); + surfaces.push_back(std::move(surf)); + } + + std::promise launch_promise; + std::future sync = launch_promise.get_future(); + std::promise surface_released_promise; + std::future released_result = surface_released_promise.get_future(); + std::thread worker_thread([&launch_promise, &surface_released_promise, &surfaces] () { + launch_promise.set_value(); + + // concurrent release surfaces + size_t surfaces_count = surfaces.size(); + for (auto& surf : surfaces) { + EXPECT_EQ(surf->release_lock(), 1); + std::this_thread::sleep_for(std::chrono::seconds(1)); + } + surfaces.clear(); + + surface_released_promise.set_value(surfaces_count); + }); + sync.wait(); + + // check free surface concurrently + std::future_status status; + size_t free_surface_count = 0; + size_t free_surface_count_prev = 0; + do { + status = released_result.wait_for(std::chrono::seconds(1)); + free_surface_count = acceleration_policy->get_free_surface_count(key); + EXPECT_TRUE(free_surface_count >= free_surface_count_prev); + free_surface_count_prev = free_surface_count; + } while (status != std::future_status::ready); + std::cerr<< "Ready" << std::endl; + free_surface_count = acceleration_policy->get_free_surface_count(key); + worker_thread.join(); + EXPECT_TRUE(free_surface_count >= free_surface_count_prev); +} } } // namespace opencv_test #endif // HAVE_ONEVPL