From: Steinar H. Gunderson Date: Fri, 29 May 2020 20:01:10 +0000 (+0200) Subject: Move VAResourcePool into a shared class between MJPEGEncoder in Nageru and the VA... X-Git-Tag: 2.0.0~20 X-Git-Url: https://git.sesse.net/?p=nageru;a=commitdiff_plain;h=8850f9468d6a1727e7be302d2a9b4360f69210cc Move VAResourcePool into a shared class between MJPEGEncoder in Nageru and the VA-API JPEG decoder in Futatabi. --- diff --git a/futatabi/vaapi_jpeg_decoder.cpp b/futatabi/vaapi_jpeg_decoder.cpp index 0540db7..8fe1ac8 100644 --- a/futatabi/vaapi_jpeg_decoder.cpp +++ b/futatabi/vaapi_jpeg_decoder.cpp @@ -6,6 +6,7 @@ #include "pbo_pool.h" #include "shared/memcpy_interleaved.h" #include "shared/va_display.h" +#include "shared/va_resource_pool.h" #include #include @@ -29,30 +30,9 @@ using namespace std; static unique_ptr va_dpy; -static VAConfigID config_id; -static VAImageFormat uyvy_format; -bool vaapi_jpeg_decoding_usable = false; - -struct VAResources { - unsigned width, height; - VASurfaceID surface; - VAContextID context; - VAImage image; -}; -static list va_resources_freelist; -static mutex va_resources_mutex; - -#define CHECK_VASTATUS(va_status, func) \ - if (va_status != VA_STATUS_SUCCESS) { \ - fprintf(stderr, "%s:%d (%s) failed with %d\n", __func__, __LINE__, func, va_status); \ - abort(); \ - } +static unique_ptr va_pool; -#define CHECK_VASTATUS_RET(va_status, func) \ - if (va_status != VA_STATUS_SUCCESS) { \ - fprintf(stderr, "%s:%d (%s) failed with %d\n", __func__, __LINE__, func, va_status); \ - return nullptr; \ - } +bool vaapi_jpeg_decoding_usable = false; // From libjpeg (although it's of course identical between implementations). static const int jpeg_natural_order[DCTSIZE2] = { @@ -66,85 +46,24 @@ static const int jpeg_natural_order[DCTSIZE2] = { 53, 60, 61, 54, 47, 55, 62, 63, }; -VAResources get_va_resources(unsigned width, unsigned height) -{ - { - lock_guard lock(va_resources_mutex); - for (auto it = va_resources_freelist.begin(); it != va_resources_freelist.end(); ++it) { - if (it->width == width && it->height == height) { - VAResources ret = *it; - va_resources_freelist.erase(it); - return ret; - } - } - } - - VAResources ret; - - ret.width = width; - ret.height = height; - - VAStatus va_status = vaCreateSurfaces(va_dpy->va_dpy, VA_RT_FORMAT_YUV422, - width, height, - &ret.surface, 1, nullptr, 0); - CHECK_VASTATUS(va_status, "vaCreateSurfaces"); - - va_status = vaCreateContext(va_dpy->va_dpy, config_id, width, height, 0, &ret.surface, 1, &ret.context); - CHECK_VASTATUS(va_status, "vaCreateContext"); - - va_status = vaCreateImage(va_dpy->va_dpy, &uyvy_format, width, height, &ret.image); - CHECK_VASTATUS(va_status, "vaCreateImage"); - - return ret; -} - -void release_va_resources(VAResources resources) +static unique_ptr try_open_va_mjpeg(const string &va_display) { - lock_guard lock(va_resources_mutex); - if (va_resources_freelist.size() > 10) { - auto it = va_resources_freelist.end(); - --it; - - VAStatus va_status = vaDestroyImage(va_dpy->va_dpy, it->image.image_id); - CHECK_VASTATUS(va_status, "vaDestroyImage"); - - va_status = vaDestroyContext(va_dpy->va_dpy, it->context); - CHECK_VASTATUS(va_status, "vaDestroyContext"); - - va_status = vaDestroySurfaces(va_dpy->va_dpy, &it->surface, 1); - CHECK_VASTATUS(va_status, "vaDestroySurfaces"); + VAConfigID config_id_422, config_id_420; + VAImageFormat uyvy_format, nv12_format; - va_resources_freelist.erase(it); - } - - va_resources_freelist.push_front(resources); -} - -// RAII wrapper to release VAResources on return (even on error). -class ReleaseVAResources { -public: - ReleaseVAResources(const VAResources &resources) - : resources(resources) {} - ~ReleaseVAResources() - { - if (!committed) { - release_va_resources(resources); - } + // Seemingly VA_FOURCC_422H is no good for vaGetImage(). :-/ + unique_ptr va_dpy = + try_open_va(va_display, { VAProfileJPEGBaseline }, VAEntrypointVLD, + { { "4:2:2", VA_RT_FORMAT_YUV422, VA_FOURCC_UYVY, &config_id_422, &uyvy_format }, + { "4:2:0", VA_RT_FORMAT_YUV420, VA_FOURCC_NV12, &config_id_420, &nv12_format } }, + /*chosen_profile=*/nullptr, /*error=*/nullptr); + if (va_dpy == nullptr) { + return va_dpy; } - void commit() { committed = true; } - -private: - const VAResources &resources; - bool committed = false; -}; + va_pool.reset(new VAResourcePool(va_dpy->va_dpy, uyvy_format, nv12_format, config_id_422, config_id_420, /*with_data_buffer=*/false)); -static unique_ptr try_open_va_mjpeg(const string &va_display) -{ - // Seemingly VA_FOURCC_422H is no good for vaGetImage(). :-/ - return try_open_va(va_display, { VAProfileJPEGBaseline }, VAEntrypointVLD, - { { "4:2:2", VA_RT_FORMAT_YUV422, VA_FOURCC_UYVY, &config_id, &uyvy_format } }, - /*chosen_profile=*/nullptr, /*error=*/nullptr); + return va_dpy; } string get_usable_va_display() @@ -280,8 +199,8 @@ shared_ptr decode_jpeg_vaapi(const string &jpeg) pic_param.color_space = 0; // YUV. pic_param.rotation = VA_ROTATION_NONE; - VAResources resources = get_va_resources(dinfo.image_width, dinfo.image_height); - ReleaseVAResources release(resources); + VAResourcePool::VAResources resources = va_pool->get_va_resources(dinfo.image_width, dinfo.image_height, VA_FOURCC_UYVY); + ReleaseVAResources release(va_pool.get(), resources); VABufferID pic_param_buffer; VAStatus va_status = vaCreateBuffer(va_dpy->va_dpy, resources.context, VAPictureParameterBufferType, sizeof(pic_param), 1, &pic_param, &pic_param_buffer); diff --git a/nageru/mjpeg_encoder.cpp b/nageru/mjpeg_encoder.cpp index 2cd1607..26eb9a8 100644 --- a/nageru/mjpeg_encoder.cpp +++ b/nageru/mjpeg_encoder.cpp @@ -221,6 +221,7 @@ MJPEGEncoder::MJPEGEncoder(HTTPD *httpd, const string &va_display) add_stream(HTTPD::StreamID{ HTTPD::MULTICAM_STREAM, 0 }); // Initialize VA-API. + VAConfigID config_id_422, config_id_420; string error; va_dpy = try_open_va(va_display, { VAProfileJPEGBaseline }, VAEntrypointEncPicture, { @@ -233,6 +234,8 @@ MJPEGEncoder::MJPEGEncoder(HTTPD *httpd, const string &va_display) fprintf(stderr, "Could not initialize VA-API for MJPEG encoding: %s. JPEGs will be encoded in software if needed.\n", error.c_str()); } + va_pool.reset(new VAResourcePool(va_dpy->va_dpy, uyvy_format, nv12_format, config_id_422, config_id_420, /*with_data_buffer=*/true)); + encoder_thread = thread(&MJPEGEncoder::encoder_thread_func, this); if (va_dpy != nullptr) { va_receiver_thread = thread(&MJPEGEncoder::va_receiver_thread_func, this); @@ -449,85 +452,6 @@ private: VABufferID buf; }; -MJPEGEncoder::VAResources MJPEGEncoder::get_va_resources(unsigned width, unsigned height, uint32_t fourcc) -{ - { - lock_guard lock(va_resources_mutex); - for (auto it = va_resources_freelist.begin(); it != va_resources_freelist.end(); ++it) { - if (it->width == width && it->height == height && it->fourcc == fourcc) { - VAResources ret = *it; - va_resources_freelist.erase(it); - return ret; - } - } - } - - VAResources ret; - - ret.width = width; - ret.height = height; - ret.fourcc = fourcc; - - VASurfaceAttrib attrib; - attrib.flags = VA_SURFACE_ATTRIB_SETTABLE; - attrib.type = VASurfaceAttribPixelFormat; - attrib.value.type = VAGenericValueTypeInteger; - attrib.value.value.i = fourcc; - - VAStatus va_status; - VAConfigID config_id; - if (fourcc == VA_FOURCC_UYVY) { - va_status = vaCreateSurfaces(va_dpy->va_dpy, VA_RT_FORMAT_YUV422, width, height, &ret.surface, 1, &attrib, 1); - config_id = config_id_422; - } else { - assert(fourcc == VA_FOURCC_NV12); - va_status = vaCreateSurfaces(va_dpy->va_dpy, VA_RT_FORMAT_YUV420, width, height, &ret.surface, 1, &attrib, 1); - config_id = config_id_420; - } - - va_status = vaCreateContext(va_dpy->va_dpy, config_id, width, height, 0, &ret.surface, 1, &ret.context); - CHECK_VASTATUS(va_status, "vaCreateContext"); - - va_status = vaCreateBuffer(va_dpy->va_dpy, ret.context, VAEncCodedBufferType, width * height * 3 + 8192, 1, nullptr, &ret.data_buffer); - CHECK_VASTATUS(va_status, "vaCreateBuffer"); - - if (fourcc == VA_FOURCC_UYVY) { - va_status = vaCreateImage(va_dpy->va_dpy, &uyvy_format, width, height, &ret.image); - CHECK_VASTATUS(va_status, "vaCreateImage"); - } else { - assert(fourcc == VA_FOURCC_NV12); - va_status = vaCreateImage(va_dpy->va_dpy, &nv12_format, width, height, &ret.image); - CHECK_VASTATUS(va_status, "vaCreateImage"); - } - - return ret; -} - -void MJPEGEncoder::release_va_resources(MJPEGEncoder::VAResources resources) -{ - lock_guard lock(va_resources_mutex); - if (va_resources_freelist.size() > 50) { - auto it = va_resources_freelist.end(); - --it; - - VAStatus va_status = vaDestroyBuffer(va_dpy->va_dpy, it->data_buffer); - CHECK_VASTATUS(va_status, "vaDestroyBuffer"); - - va_status = vaDestroyContext(va_dpy->va_dpy, it->context); - CHECK_VASTATUS(va_status, "vaDestroyContext"); - - va_status = vaDestroySurfaces(va_dpy->va_dpy, &it->surface, 1); - CHECK_VASTATUS(va_status, "vaDestroySurfaces"); - - va_status = vaDestroyImage(va_dpy->va_dpy, it->image.image_id); - CHECK_VASTATUS(va_status, "vaDestroyImage"); - - va_resources_freelist.erase(it); - } - - va_resources_freelist.push_front(resources); -} - namespace { void push16(uint16_t val, string *str) @@ -787,7 +711,7 @@ void MJPEGEncoder::encode_jpeg_va(QueuedFrame &&qf) unsigned width = qf.video_format.width; unsigned height = qf.video_format.height; - VAResources resources; + VAResourcePool::VAResources resources; ReleaseVAResources release; if (userdata->data_copy_current_src == PBOFrameAllocator::Userdata::FROM_VA_API) { assert(is_uyvy(qf.frame)); @@ -796,12 +720,12 @@ void MJPEGEncoder::encode_jpeg_va(QueuedFrame &&qf) } else { assert(userdata->data_copy_current_src == PBOFrameAllocator::Userdata::FROM_MALLOC); if (is_uyvy(qf.frame)) { - resources = get_va_resources(width, height, VA_FOURCC_UYVY); + resources = va_pool->get_va_resources(width, height, VA_FOURCC_UYVY); } else { assert(is_i420(qf.frame)); - resources = get_va_resources(width, height, VA_FOURCC_NV12); + resources = va_pool->get_va_resources(width, height, VA_FOURCC_NV12); } - release = ReleaseVAResources(this, resources); + release = ReleaseVAResources(va_pool.get(), resources); } int y_h_samp_factor, y_v_samp_factor; diff --git a/nageru/mjpeg_encoder.h b/nageru/mjpeg_encoder.h index 330784b..5f19246 100644 --- a/nageru/mjpeg_encoder.h +++ b/nageru/mjpeg_encoder.h @@ -4,6 +4,7 @@ #include "defs.h" #include "shared/ffmpeg_raii.h" #include "shared/httpd.h" +#include "shared/va_resource_pool.h" #include "ref_counted_frame.h" extern "C" { @@ -44,62 +45,11 @@ public: bool using_vaapi() const { return va_dpy != nullptr; } bool should_encode_mjpeg_for_card(unsigned card_index); + VAResourcePool *get_va_pool() const { return va_pool.get(); } private: static constexpr int quality = 90; - struct VAResources { - unsigned width, height; - uint32_t fourcc; - VASurfaceID surface; - VAContextID context; - VABufferID data_buffer; - VAImage image; - }; - - // RAII wrapper to release VAResources on return (even on error). - class ReleaseVAResources { - public: - ReleaseVAResources() : committed(true) {} - - ReleaseVAResources(MJPEGEncoder *mjpeg, const VAResources &resources) - : mjpeg(mjpeg), resources(resources) {} - - ReleaseVAResources(ReleaseVAResources &) = delete; - - ReleaseVAResources(ReleaseVAResources &&other) - : mjpeg(other.mjpeg), resources(other.resources), committed(other.committed) { - other.commit(); - } - - ReleaseVAResources &operator= (ReleaseVAResources &) = delete; - - ReleaseVAResources &operator= (ReleaseVAResources &&other) { - if (!committed) { - mjpeg->release_va_resources(resources); - } - mjpeg = other.mjpeg; - resources = std::move(other.resources); - committed = other.committed; - other.commit(); - return *this; - } - - ~ReleaseVAResources() - { - if (!committed) { - mjpeg->release_va_resources(resources); - } - } - - void commit() { committed = true; } - - private: - MJPEGEncoder *mjpeg = nullptr; - VAResources resources; - bool committed = false; - }; - struct QueuedFrame { int64_t pts; unsigned card_index; @@ -110,7 +60,7 @@ private: movit::RGBTriplet white_balance; // Only for frames in the process of being encoded by VA-API. - VAResources resources; + VAResourcePool::VAResources resources; ReleaseVAResources resource_releaser; }; @@ -153,7 +103,7 @@ private: bool running = false; std::unique_ptr va_dpy; - VAConfigID config_id_422, config_id_420; + std::unique_ptr va_pool; struct VAKey { unsigned width, height, y_h_samp_factor, y_v_samp_factor; @@ -185,11 +135,6 @@ private: std::map va_data_for_parameters; VAData get_va_data_for_parameters(unsigned width, unsigned height, unsigned y_h_samp_factor, unsigned y_v_samp_factor, const movit::RGBTriplet &white_balance); - std::list va_resources_freelist; - std::mutex va_resources_mutex; - VAResources get_va_resources(unsigned width, unsigned height, uint32_t fourcc); - void release_va_resources(VAResources resources); - uint8_t *tmp_y, *tmp_cbcr, *tmp_cb, *tmp_cr; // Private to the encoder thread. Used by the libjpeg backend only. std::atomic metric_mjpeg_frames_zero_size_dropped{0}; diff --git a/nageru/pbo_frame_allocator.cpp b/nageru/pbo_frame_allocator.cpp index 37ce2a5..d7204f2 100644 --- a/nageru/pbo_frame_allocator.cpp +++ b/nageru/pbo_frame_allocator.cpp @@ -328,8 +328,8 @@ bmusb::FrameAllocator::Frame PBOFrameAllocator::create_frame(size_t width, size_ mjpeg_encoder->should_encode_mjpeg_for_card(card_index)) { if (mjpeg_encoder->using_vaapi()) { VADisplay va_dpy = mjpeg_encoder->va_dpy->va_dpy; - MJPEGEncoder::VAResources resources = mjpeg_encoder->get_va_resources(width, height, VA_FOURCC_UYVY); // Only used by DeckLinkCapture, so always 4:2:2. - MJPEGEncoder::ReleaseVAResources release(mjpeg_encoder, resources); + VAResourcePool::VAResources resources = mjpeg_encoder->get_va_pool()->get_va_resources(width, height, VA_FOURCC_UYVY); // Only used by DeckLinkCapture, so always 4:2:2. + ReleaseVAResources release(mjpeg_encoder->get_va_pool(), resources); if (resources.image.pitches[0] == stride) { userdata->va_resources = move(resources); @@ -393,8 +393,8 @@ void PBOFrameAllocator::release_frame(Frame frame) { // In case we never got to upload the frame to MJPEGEncoder. Userdata *userdata = (Userdata *)frame.userdata; - MJPEGEncoder::VAResources resources __attribute__((unused)) = move(userdata->va_resources); - MJPEGEncoder::ReleaseVAResources release = move(userdata->va_resources_release); + VAResourcePool::VAResources resources __attribute__((unused)) = move(userdata->va_resources); + ReleaseVAResources release = move(userdata->va_resources_release); if (frame.data_copy != nullptr && userdata->data_copy_current_src == Userdata::FROM_VA_API) { VADisplay va_dpy = mjpeg_encoder->va_dpy->va_dpy; diff --git a/nageru/pbo_frame_allocator.h b/nageru/pbo_frame_allocator.h index eee25ce..43ae6cb 100644 --- a/nageru/pbo_frame_allocator.h +++ b/nageru/pbo_frame_allocator.h @@ -89,8 +89,8 @@ public: // from there into VA-API buffers (by MJPEGEncoder) if needed. enum { FROM_MALLOC, FROM_VA_API } data_copy_current_src; uint8_t *data_copy_malloc; - MJPEGEncoder::VAResources va_resources; - MJPEGEncoder::ReleaseVAResources va_resources_release; + VAResourcePool::VAResources va_resources; + ReleaseVAResources va_resources_release; int generation; }; diff --git a/shared/meson.build b/shared/meson.build index 28586c6..c27d13f 100644 --- a/shared/meson.build +++ b/shared/meson.build @@ -21,7 +21,7 @@ proto_generated = gen.process(['midi_mapping.proto']) protobuf_lib = static_library('protobufs', proto_generated, dependencies: [protobufdep]) protobuf_hdrs = declare_dependency(sources: proto_generated) -srcs = ['memcpy_interleaved.cpp', 'metacube2.cpp', 'ffmpeg_raii.cpp', 'mux.cpp', 'metrics.cpp', 'context.cpp', 'httpd.cpp', 'disk_space_estimator.cpp', 'read_file.cpp', 'text_proto.cpp', 'midi_device.cpp', 'ref_counted_texture.cpp', 'va_display.cpp'] +srcs = ['memcpy_interleaved.cpp', 'metacube2.cpp', 'ffmpeg_raii.cpp', 'mux.cpp', 'metrics.cpp', 'context.cpp', 'httpd.cpp', 'disk_space_estimator.cpp', 'read_file.cpp', 'text_proto.cpp', 'midi_device.cpp', 'ref_counted_texture.cpp', 'va_display.cpp', 'va_resource_pool.cpp'] srcs += proto_generated # Qt objects. diff --git a/shared/va_resource_pool.cpp b/shared/va_resource_pool.cpp new file mode 100644 index 0000000..f23aca0 --- /dev/null +++ b/shared/va_resource_pool.cpp @@ -0,0 +1,92 @@ + +#include + +#include "shared/va_resource_pool.h" + +using namespace std; + +VAResourcePool::VAResources VAResourcePool::get_va_resources(unsigned width, unsigned height, uint32_t fourcc) +{ + { + lock_guard lock(mu); + for (auto it = freelist.begin(); it != freelist.end(); ++it) { + if (it->width == width && it->height == height && it->fourcc == fourcc) { + VAResources ret = *it; + freelist.erase(it); + return ret; + } + } + } + + VAResources ret; + + ret.width = width; + ret.height = height; + ret.fourcc = fourcc; + + VASurfaceAttrib attrib; + attrib.flags = VA_SURFACE_ATTRIB_SETTABLE; + attrib.type = VASurfaceAttribPixelFormat; + attrib.value.type = VAGenericValueTypeInteger; + attrib.value.value.i = fourcc; + + VAStatus va_status; + VAConfigID config_id; + if (fourcc == VA_FOURCC_UYVY) { + va_status = vaCreateSurfaces(va_dpy, VA_RT_FORMAT_YUV422, width, height, &ret.surface, 1, &attrib, 1); + config_id = config_id_422; + } else { + assert(fourcc == VA_FOURCC_NV12); + va_status = vaCreateSurfaces(va_dpy, VA_RT_FORMAT_YUV420, width, height, &ret.surface, 1, &attrib, 1); + config_id = config_id_420; + } + + va_status = vaCreateContext(va_dpy, config_id, width, height, 0, &ret.surface, 1, &ret.context); + CHECK_VASTATUS(va_status, "vaCreateContext"); + + if (with_data_buffer) { + va_status = vaCreateBuffer(va_dpy, ret.context, VAEncCodedBufferType, width * height * 3 + 8192, 1, nullptr, &ret.data_buffer); + CHECK_VASTATUS(va_status, "vaCreateBuffer"); + } + + if (fourcc == VA_FOURCC_UYVY) { + va_status = vaCreateImage(va_dpy, &uyvy_format, width, height, &ret.image); + CHECK_VASTATUS(va_status, "vaCreateImage"); + } else { + assert(fourcc == VA_FOURCC_NV12); + va_status = vaCreateImage(va_dpy, &nv12_format, width, height, &ret.image); + CHECK_VASTATUS(va_status, "vaCreateImage"); + } + + return ret; +} + +void VAResourcePool::release_va_resources(VAResourcePool::VAResources resources) +{ + lock_guard lock(mu); + if (freelist.size() > 50) { + auto it = freelist.end(); + --it; + + VAStatus va_status; + + if (with_data_buffer) { + va_status = vaDestroyBuffer(va_dpy, it->data_buffer); + CHECK_VASTATUS(va_status, "vaDestroyBuffer"); + } + + va_status = vaDestroyContext(va_dpy, it->context); + CHECK_VASTATUS(va_status, "vaDestroyContext"); + + va_status = vaDestroySurfaces(va_dpy, &it->surface, 1); + CHECK_VASTATUS(va_status, "vaDestroySurfaces"); + + va_status = vaDestroyImage(va_dpy, it->image.image_id); + CHECK_VASTATUS(va_status, "vaDestroyImage"); + + freelist.erase(it); + } + + freelist.push_front(resources); +} + diff --git a/shared/va_resource_pool.h b/shared/va_resource_pool.h new file mode 100644 index 0000000..e8f276f --- /dev/null +++ b/shared/va_resource_pool.h @@ -0,0 +1,96 @@ +#ifndef _VA_RESOURCE_POOL +#define _VA_RESOURCE_POOL 1 + +#include +#include + +#include +#include + +#define CHECK_VASTATUS(va_status, func) \ + if (va_status != VA_STATUS_SUCCESS) { \ + fprintf(stderr, "%s:%d (%s) failed: %s\n", __func__, __LINE__, func, vaErrorStr(va_status)); \ + exit(1); \ + } + +#define CHECK_VASTATUS_RET(va_status, func) \ + if (va_status != VA_STATUS_SUCCESS) { \ + fprintf(stderr, "%s:%d (%s) failed with %d\n", __func__, __LINE__, func, va_status); \ + return nullptr; \ + } + +class VAResourcePool { +public: + struct VAResources { + unsigned width, height; + uint32_t fourcc; + VASurfaceID surface; + VAContextID context; + VABufferID data_buffer; + VAImage image; + }; + + VAResourcePool(VADisplay va_dpy, VAImageFormat uyvy_format, VAImageFormat nv12_format, VAConfigID config_id_422, VAConfigID config_id_420, bool with_data_buffer) + : va_dpy(va_dpy), + uyvy_format(uyvy_format), + nv12_format(nv12_format), + config_id_422(config_id_422), + config_id_420(config_id_420), + with_data_buffer(with_data_buffer) {} + VAResources get_va_resources(unsigned width, unsigned height, uint32_t fourcc); + void release_va_resources(VAResources resources); + +private: + const VADisplay va_dpy; + VAImageFormat uyvy_format, nv12_format; + const VAConfigID config_id_422, config_id_420; + const bool with_data_buffer; + + std::mutex mu; + std::list freelist; // Under mu. +}; + +// RAII wrapper to release VAResources on return (even on error). +class ReleaseVAResources { +public: + ReleaseVAResources() : committed(true) {} + + ReleaseVAResources(VAResourcePool *pool, const VAResourcePool::VAResources &resources) + : pool(pool), resources(resources) {} + + ReleaseVAResources(ReleaseVAResources &) = delete; + + ReleaseVAResources(ReleaseVAResources &&other) + : pool(other.pool), resources(other.resources), committed(other.committed) { + other.commit(); + } + + ReleaseVAResources &operator= (ReleaseVAResources &) = delete; + + ReleaseVAResources &operator= (ReleaseVAResources &&other) { + if (!committed) { + pool->release_va_resources(resources); + } + pool = other.pool; + resources = std::move(other.resources); + committed = other.committed; + other.commit(); + return *this; + } + + ~ReleaseVAResources() + { + if (!committed) { + pool->release_va_resources(resources); + } + } + + void commit() { committed = true; } + +private: + VAResourcePool *pool = nullptr; + VAResourcePool::VAResources resources; + bool committed = false; +}; + +#endif // !defined(_VA_RESOURCE_POOL)