]> git.sesse.net Git - nageru/commitdiff
Move VAResourcePool into a shared class between MJPEGEncoder in Nageru and the VA...
authorSteinar H. Gunderson <sgunderson@bigfoot.com>
Fri, 29 May 2020 20:01:10 +0000 (22:01 +0200)
committerSteinar H. Gunderson <sgunderson@bigfoot.com>
Fri, 29 May 2020 20:09:29 +0000 (22:09 +0200)
futatabi/vaapi_jpeg_decoder.cpp
nageru/mjpeg_encoder.cpp
nageru/mjpeg_encoder.h
nageru/pbo_frame_allocator.cpp
nageru/pbo_frame_allocator.h
shared/meson.build
shared/va_resource_pool.cpp [new file with mode: 0644]
shared/va_resource_pool.h [new file with mode: 0644]

index 0540db7da82061ff314ad26bdba2c76cb2cdca5a..8fe1ac88f7fc46f4ddb0af10895115bd52a05dd7 100644 (file)
@@ -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 <X11/Xlib.h>
 #include <assert.h>
 using namespace std;
 
 static unique_ptr<VADisplayWithCleanup> 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<VAResources> 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<VAResourcePool> 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<mutex> 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<VADisplayWithCleanup> try_open_va_mjpeg(const string &va_display)
 {
-       lock_guard<mutex> 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<VADisplayWithCleanup> 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<VADisplayWithCleanup> 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<Frame> 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);
index 2cd1607bac6e1f6055439e2f62a3407135341a10..26eb9a868f70ddeb6bbf23349318f7435d73565e 100644 (file)
@@ -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<mutex> 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<mutex> 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;
index 330784bd94a30121cf30f9b3c7a5bfb1d48d2883..5f19246841dd8e5f5e0bb6c6e41981ec4e71ecdc 100644 (file)
@@ -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<VADisplayWithCleanup> va_dpy;
-       VAConfigID config_id_422, config_id_420;
+       std::unique_ptr<VAResourcePool> va_pool;
 
        struct VAKey {
                unsigned width, height, y_h_samp_factor, y_v_samp_factor;
@@ -185,11 +135,6 @@ private:
        std::map<VAKey, VAData> 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<VAResources> 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<int64_t> metric_mjpeg_frames_zero_size_dropped{0};
index 37ce2a5a45463b026873df3453be8eceeb3ec14e..d7204f283927426b18e6c831174dd101c8dd5d6a 100644 (file)
@@ -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;
index eee25ce906d443f6656935c33e3f9cfda476af9b..43ae6cbf622d1d43f66a49166cc38cb7671245ae 100644 (file)
@@ -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;
        };
index 28586c61267582f8dabdb6c9046f506637080c1f..c27d13fa62bea6f522739f188e88d378277d0dc8 100644 (file)
@@ -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 (file)
index 0000000..f23aca0
--- /dev/null
@@ -0,0 +1,92 @@
+
+#include <assert.h>
+
+#include "shared/va_resource_pool.h"
+
+using namespace std;
+
+VAResourcePool::VAResources VAResourcePool::get_va_resources(unsigned width, unsigned height, uint32_t fourcc)
+{
+       {
+               lock_guard<mutex> 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<mutex> 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 (file)
index 0000000..e8f276f
--- /dev/null
@@ -0,0 +1,96 @@
+#ifndef _VA_RESOURCE_POOL
+#define _VA_RESOURCE_POOL 1
+
+#include <inttypes.h>
+#include <va/va.h>
+
+#include <list>
+#include <mutex>
+
+#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<VAResources> 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)