From 72ee4688cbaef0832eb44dee78063bbc0df97bf8 Mon Sep 17 00:00:00 2001 From: Carl Hetherington Date: Sun, 21 Nov 2021 14:32:45 +0100 Subject: [PATCH] Add fastvideo J2K encoding backend. --- run/dcpomatic | 2 +- run/dcpomatic_cli | 2 +- run/dcpomatic_player | 2 +- run/tests | 2 +- src/lib/barrier.cc | 53 ++++++ src/lib/barrier.h | 43 +++++ src/lib/config.cc | 6 + src/lib/config.h | 14 ++ src/lib/exceptions.cc | 9 + src/lib/exceptions.h | 17 ++ src/lib/j2k_encoder.cc | 75 ++++++-- src/lib/j2k_encoder.h | 6 + src/lib/j2k_encoder_cpu_backend.h | 3 - src/lib/j2k_encoder_fastvideo_backend.cc | 211 +++++++++++++++++++++++ src/lib/j2k_encoder_fastvideo_backend.h | 63 +++++++ src/lib/j2k_encoder_remote_backend.cc | 11 -- src/lib/j2k_encoder_remote_backend.h | 2 - src/lib/wscript | 4 + src/tools/wscript | 2 + src/wx/full_config_dialog.cc | 24 +++ src/wx/wscript | 2 + test/barrier_test.cc | 98 +++++++++++ test/fastvideo_test.cc | 73 ++++++++ test/wscript | 6 + wscript | 13 ++ 25 files changed, 708 insertions(+), 35 deletions(-) create mode 100644 src/lib/barrier.cc create mode 100644 src/lib/barrier.h create mode 100644 src/lib/j2k_encoder_fastvideo_backend.cc create mode 100644 src/lib/j2k_encoder_fastvideo_backend.h create mode 100644 test/barrier_test.cc create mode 100644 test/fastvideo_test.cc diff --git a/run/dcpomatic b/run/dcpomatic index 6de2989ae..c1d7b98a4 100755 --- a/run/dcpomatic +++ b/run/dcpomatic @@ -2,7 +2,7 @@ DIR="$( cd "$( dirname "${BASH_SOURCE[0]}" )" >/dev/null 2>&1 && pwd )" build=$DIR/../build -export LD_LIBRARY_PATH=$build/src/lib:$build/src/wx:$build/src/asdcplib/src:$LD_LIBRARY_PATH +export LD_LIBRARY_PATH=$build/src/lib:$build/src/wx:$build/src/asdcplib/src:/home/carl/fastvideo/current/fastvideo_sdk/lib:/home/carl/fastvideo/current/bin:$LD_LIBRARY_PATH export DYLD_LIBRARY_PATH=$build/src/lib:$build/src/wx:$build/src/asdcplib/src:/Users/ci/osx-environment/x86_64/10.10/lib export DCPOMATIC_GRAPHICS=$DIR/../graphics binary=$build/src/tools/dcpomatic2 diff --git a/run/dcpomatic_cli b/run/dcpomatic_cli index b24090db9..511084de1 100755 --- a/run/dcpomatic_cli +++ b/run/dcpomatic_cli @@ -2,7 +2,7 @@ cd "$( cd "$( dirname "${BASH_SOURCE[0]}" )" && pwd )"/.. -export LD_LIBRARY_PATH=build/src/lib:build/src:/home/c.hetherington/lib:$LD_LIBRARY_PATH +export LD_LIBRARY_PATH=build/src/lib:build/src:/home/carl/fastvideo/current/fastvideo_sdk/lib:/home/carl/fastvideo/current/bin:$LD_LIBRARY_PATH if [ "$1" == "--debug" ]; then shift gdb --args build/src/tools/dcpomatic2_cli "$@" diff --git a/run/dcpomatic_player b/run/dcpomatic_player index 168fa9bb1..dfc1ccaf8 100755 --- a/run/dcpomatic_player +++ b/run/dcpomatic_player @@ -1,6 +1,6 @@ #!/bin/bash -export LD_LIBRARY_PATH=build/src/lib:build/src/wx:build/src/asdcplib/src:$LD_LIBRARY_PATH +export LD_LIBRARY_PATH=build/src/lib:build/src:/home/carl/fastvideo/current/fastvideo_sdk/lib:/home/carl/fastvideo/current/bin:$LD_LIBRARY_PATH export DYLD_LIBRARY_PATH=$build/src/lib:$build/src/wx:$build/src/asdcplib/src:/Users/ci/osx-environment/x86_64/10.10/lib export DCPOMATIC_GRAPHICS=graphics if [ "$1" == "--debug" ]; then diff --git a/run/tests b/run/tests index 0c5c8c0c3..73a3f5992 100755 --- a/run/tests +++ b/run/tests @@ -3,7 +3,7 @@ # e.g. --run_tests=foo if [ "$(uname)" == "Linux" ]; then - export LD_LIBRARY_PATH=build/src/lib:$LD_LIBRARY_PATH + export LD_LIBRARY_PATH=build/src/lib:build/src:/home/carl/fastvideo/current/fastvideo_sdk/lib:/home/carl/fastvideo/current/bin:$LD_LIBRARY_PATH rm -f build/test/dcpomatic2_openssl # This must be our patched openssl or tests will fail if [ ! -f build/test/dcpomatic2_openssl ]; then diff --git a/src/lib/barrier.cc b/src/lib/barrier.cc new file mode 100644 index 000000000..c00fdf0f0 --- /dev/null +++ b/src/lib/barrier.cc @@ -0,0 +1,53 @@ +/* + Copyright (C) 2021 Carl Hetherington + + This file is part of DCP-o-matic. + + DCP-o-matic is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + DCP-o-matic is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with DCP-o-matic. If not, see . + +*/ + + +#include "barrier.h" + + +Barrier::Barrier (int count) + : _count(count) +{ + +} + + +void +Barrier::wait () +{ + boost::mutex::scoped_lock lm (_mutex); + ++_waiting; + if (_waiting >= _count) { + _condition.notify_all(); + _waiting = 0; + } else { + _condition.wait(lm); + } +} + + +void +Barrier::lower () +{ + boost::mutex::scoped_lock lm (_mutex); + _count = 0; + _condition.notify_all(); +} + diff --git a/src/lib/barrier.h b/src/lib/barrier.h new file mode 100644 index 000000000..3d05c7d5a --- /dev/null +++ b/src/lib/barrier.h @@ -0,0 +1,43 @@ +/* + Copyright (C) 2021 Carl Hetherington + + This file is part of DCP-o-matic. + + DCP-o-matic is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + DCP-o-matic is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with DCP-o-matic. If not, see . + +*/ + + +#include +#include + + +class Barrier +{ +public: + Barrier (int count); + Barrier (Barrier const&) = delete; + Barrier& operator= (Barrier const&) = delete; + + void wait (); + void lower (); + +private: + boost::mutex _mutex; + boost::condition _condition; + int _count; + int _waiting = 0; +}; + + diff --git a/src/lib/config.cc b/src/lib/config.cc index 78f35e821..9c31c0621 100644 --- a/src/lib/config.cc +++ b/src/lib/config.cc @@ -80,6 +80,7 @@ Config::Config () void Config::set_defaults () { + _encoding_backend = EncodingBackend::CPU; _master_encoding_threads = max (2U, boost::thread::hardware_concurrency ()); _server_encoding_threads = max (2U, boost::thread::hardware_concurrency ()); _server_port_base = 6192; @@ -238,6 +239,10 @@ try backup (); } + if (auto encoding_backend = f.optional_string_child("EncodingBackend")) { + _encoding_backend = *encoding_backend == "cpu" ? EncodingBackend::CPU : EncodingBackend::FASTVIDEO; + } + if (f.optional_number_child("NumLocalEncodingThreads")) { _master_encoding_threads = _server_encoding_threads = f.optional_number_child("NumLocalEncodingThreads").get(); } else { @@ -609,6 +614,7 @@ Config::write_config () const /* [XML] Version The version number of the configuration file format. */ root->add_child("Version")->add_child_text (raw_convert(_current_version)); + root->add_child("EncodingBackend")->add_child_text(_encoding_backend == EncodingBackend::CPU ? "cpu" : "fastvideo"); /* [XML] MasterEncodingThreads Number of encoding threads to use when running as master. */ root->add_child("MasterEncodingThreads")->add_child_text (raw_convert (_master_encoding_threads)); /* [XML] ServerEncodingThreads Number of encoding threads to use when running as server. */ diff --git a/src/lib/config.h b/src/lib/config.h index 215e6a4db..26624ff04 100644 --- a/src/lib/config.h +++ b/src/lib/config.h @@ -50,6 +50,15 @@ class DKDMRecipient; class Config : public State { public: + enum class EncodingBackend { + CPU, + FASTVIDEO + }; + + EncodingBackend encoding_backend() const { + return _encoding_backend; + } + /** @return number of threads which a master DoM should use for J2K encoding on the local machine */ int master_encoding_threads () const { return _master_encoding_threads; @@ -545,6 +554,10 @@ public: /* SET (mostly) */ + void set_encoding_backend (EncodingBackend backend) { + maybe_set (_encoding_backend, backend); + } + void set_master_encoding_threads (int n) { maybe_set (_master_encoding_threads, n); } @@ -1122,6 +1135,7 @@ private: changed (prop); } + EncodingBackend _encoding_backend; /** number of threads which a master DoM should use for J2K encoding on the local machine */ int _master_encoding_threads; /** number of threads which a server should use for J2K encoding on the local machine */ diff --git a/src/lib/exceptions.cc b/src/lib/exceptions.cc index 3f87a2ebe..630df7d5c 100644 --- a/src/lib/exceptions.cc +++ b/src/lib/exceptions.cc @@ -163,3 +163,12 @@ VerifyError::VerifyError (string m, int n) } + +#ifdef DCPOMATIC_FASTVIDEO +FastvideoError::FastvideoError (string s, string message) + : runtime_error (String::compose("Fastvideo error in %1 (%2)", s, message)) +{ + +} +#endif + diff --git a/src/lib/exceptions.h b/src/lib/exceptions.h index 9b7837a46..3e841a354 100644 --- a/src/lib/exceptions.h +++ b/src/lib/exceptions.h @@ -32,6 +32,9 @@ extern "C" { #include } +#ifdef DCPOMATIC_FASTVIDEO +#include +#endif #include #include #include @@ -447,4 +450,18 @@ public: }; +#ifdef DCPOMATIC_FASTVIDEO +class FastvideoError : public std::runtime_error +{ +public: + template + FastvideoError (std::string s, T code) + : std::runtime_error (String::compose("Fastvideo error in %1 (%2)", s, EnumToString(code))) + {} + + FastvideoError (std::string s, std::string message); +}; +#endif + + #endif diff --git a/src/lib/j2k_encoder.cc b/src/lib/j2k_encoder.cc index fae7752cf..70f3c32d4 100644 --- a/src/lib/j2k_encoder.cc +++ b/src/lib/j2k_encoder.cc @@ -26,6 +26,9 @@ #include "j2k_encoder_cpu_backend.h" #include "j2k_encoder_remote_backend.h" +#ifdef DCPOMATIC_FASTVIDEO +#include "j2k_encoder_fastvideo_backend.h" +#endif #include "j2k_encoder.h" #include "util.h" #include "film.h" @@ -60,6 +63,9 @@ using namespace dcpomatic; */ J2KEncoder::J2KEncoder (shared_ptr film, shared_ptr writer) : _film (film) +#ifdef DCPOMATIC_FASTVIDEO + , _gpu_barrier (Config::instance()->master_encoding_threads()) +#endif , _history (200) , _writer (writer) { @@ -105,6 +111,10 @@ J2KEncoder::end () LOG_GENERAL (N_("Clearing queue of %1"), _queue.size ()); +#ifdef DCPOMATIC_FASTVIDEO + _gpu_barrier.lower(); +#endif + /* Keep waking workers until the queue is empty */ while (!_queue.empty ()) { rethrow (); @@ -341,20 +351,12 @@ catch (...) } -void -J2KEncoder::servers_list_changed () -try +int +J2KEncoder::create_cpu_threads () { - boost::mutex::scoped_lock lm (_threads_mutex); - - terminate_threads (); - _threads = make_shared(); - - _frames_in_parallel = 0; - - /* XXX: could re-use threads */ + int parallel = 0; - if (!Config::instance()->only_servers_encode ()) { + if (!Config::instance()->only_servers_encode()) { auto backend = std::make_shared(); for (int i = 0; i < Config::instance()->master_encoding_threads (); ++i) { #ifdef DCPOMATIC_LINUX @@ -363,7 +365,7 @@ try #else _threads->create_thread(boost::bind(&J2KEncoder::encoder_thread, this, backend)); #endif - _frames_in_parallel += backend->quantity(); + parallel += backend->quantity(); } } @@ -377,11 +379,54 @@ try LOG_GENERAL (N_("Adding %1 worker threads for remote %2"), i.threads(), i.host_name()); for (int j = 0; j < i.threads(); ++j) { _threads->create_thread(boost::bind(&J2KEncoder::encoder_thread, this, backend)); - _frames_in_parallel += backend->quantity(); + parallel += backend->quantity(); } } - _writer->set_encoder_threads (_threads->size()); + return parallel; +} + + +#ifdef DCPOMATIC_FASTVIDEO +int +J2KEncoder::create_fastvideo_threads () +{ + int parallel = 0; + for (int i = 0; i < Config::instance()->master_encoding_threads(); ++i) { + auto backend = std::make_shared(_gpu_barrier); + _threads->create_thread(boost::bind(&J2KEncoder::encoder_thread, this, backend)); + parallel += backend->quantity(); + } + return parallel; +} +#endif + + +void +J2KEncoder::servers_list_changed () +try +{ + boost::mutex::scoped_lock lm (_threads_mutex); + + terminate_threads (); + _threads = make_shared(); + + /* XXX: could re-use threads */ + +#ifdef DCPOMATIC_FASTVIDEO + switch (Config::instance()->encoding_backend()) { + case Config::EncodingBackend::CPU: + _frames_in_parallel = create_cpu_threads(); + break; + case Config::EncodingBackend::FASTVIDEO: + _frames_in_parallel = create_fastvideo_threads(); + break; + } +#else + _frames_in_parallel = create_cpu_threads(); +#endif + + _writer->set_encoder_threads (_frames_in_parallel); } catch (...) { terminate_threads (); diff --git a/src/lib/j2k_encoder.h b/src/lib/j2k_encoder.h index 2139eee3d..22000424d 100644 --- a/src/lib/j2k_encoder.h +++ b/src/lib/j2k_encoder.h @@ -28,6 +28,7 @@ */ +#include "barrier.h" #include "cross.h" #include "event_history.h" #include "exception_store.h" @@ -86,10 +87,15 @@ private: void frame_done (); void encoder_thread (std::shared_ptr backend); + int create_cpu_threads (); void terminate_threads (); std::shared_ptr _film; +#ifdef DCPOMATIC_FASTVIDEO + int create_fastvideo_threads (); + Barrier _gpu_barrier; +#endif EventHistory _history; diff --git a/src/lib/j2k_encoder_cpu_backend.h b/src/lib/j2k_encoder_cpu_backend.h index 82b8aeb48..c09905454 100644 --- a/src/lib/j2k_encoder_cpu_backend.h +++ b/src/lib/j2k_encoder_cpu_backend.h @@ -29,9 +29,6 @@ class J2KEncoderCPUBackend : public J2KEncoderBackend { public: - J2KEncoderCPUBackend () = default; - J2KEncoderCPUBackend (J2KEncoderCPUBackend&& other) = default; - std::vector encode (std::vector const& video) override; }; diff --git a/src/lib/j2k_encoder_fastvideo_backend.cc b/src/lib/j2k_encoder_fastvideo_backend.cc new file mode 100644 index 000000000..68707ad62 --- /dev/null +++ b/src/lib/j2k_encoder_fastvideo_backend.cc @@ -0,0 +1,211 @@ +/* + Copyright (C) 2021 Carl Hetherington + + This file is part of DCP-o-matic. + + DCP-o-matic is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + DCP-o-matic is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with DCP-o-matic. If not, see . + +*/ + + +#include "barrier.h" +#include "dcp_video.h" +#include "dcpomatic_assert.h" +#include "dcpomatic_log.h" +#include "exceptions.h" +#include "image.h" +#include "j2k_encoder_fastvideo_backend.h" +#include "player_video.h" +#include +#include +#include +#include + + +using std::vector; +using boost::optional; +using dcp::ArrayData; +#if BOOST_VERSION >= 106100 +using namespace boost::placeholders; +#endif + + + +J2KEncoderFastvideoBackend::J2KEncoderFastvideoBackend (Barrier& barrier) + : _barrier (barrier) +{ + fastDeviceProperty* device_property; + int devices; + auto r = fastGetDevices(&device_property, &devices); + if (r != FAST_OK) { + throw FastvideoError ("GetDevices", r); + } + + fastSdkParametersHandle_t sdk_parameters; + r = fastGetSdkParametersHandle(&sdk_parameters); + if (r != FAST_OK) { + throw FastvideoError ("GetSdkParametersHandle", r); + } + r = fastEncoderJ2kLibraryInit(sdk_parameters); + if (r != FAST_OK) { + throw FastvideoError ("DecoderJ2kLibraryInit", r); + } + fastTraceCreate("/home/carl/trace.log"); +} + + +J2KEncoderFastvideoBackend::~J2KEncoderFastvideoBackend () +{ + if (_setup_done) { + fastFree (_xyz_buffer); + fastEncoderJ2kDestroy (_encoder); + fastImportFromHostDestroy (_adapter); + } +} + + +void +J2KEncoderFastvideoBackend::setup (dcp::Size size) +{ + auto r = fastImportFromHostCreate( + &_adapter, FAST_RGB12, size.width, size.height, &_src_buffer + ); + if (r != FAST_OK) { + throw FastvideoError ("ImportFromHostCreate", r); + } + + fastEncoderJ2kStaticParameters_t parameters; + parameters.lossless = false; + parameters.pcrdEnabled = true; + parameters.dwtLevels = 6; + parameters.codeblockSize = 32; + parameters.maxQuality = 1.5; + parameters.compressionRatio = 1; + parameters.info = false; + parameters.tier2Threads = 4; + parameters.tileWidth = size.width; + parameters.tileHeight = size.height; + parameters.noMCT = false; + parameters.ss1_x = 1; + parameters.ss1_y = 1; + parameters.ss2_x = 1; + parameters.ss2_y = 1; + parameters.ss3_x = 1; + parameters.ss3_y = 1; + parameters.yuvSubsampledFormat = false; + + r = fastEncoderJ2kCreate( + &_encoder, ¶meters, FAST_RGB12, size.width, size.height, quantity(), _src_buffer + ); + if (r != FAST_OK) { + throw FastvideoError ("EncoderJ2kCreate", r); + } + + bool success = false; + + r = fastEncoderJ2kIsInitialized(_encoder, &success); + if (r != FAST_OK || !success) { + throw FastvideoError ("EncoderJ2kIsInitialized", r); + } + + _xyz_buffer_stride = size.width * 6; + _xyz_buffer_stride += 4 - (_xyz_buffer_stride % 4); + r = fastMalloc(reinterpret_cast(&_xyz_buffer), _xyz_buffer_stride * size.height); + if (r != FAST_OK) { + throw FastvideoError ("Malloc", r); + } +} + + +vector +J2KEncoderFastvideoBackend::encode (vector const& video) +{ + DCPOMATIC_ASSERT (static_cast(video.size()) <= quantity()); + _barrier.wait(); + + if (!_setup_done) { + setup (video.front().frame()->out_size()); + _setup_done = true; + } + + for (auto const& i: video) { + auto image = i.frame()->image(boost::bind(&PlayerVideo::keep_xyz_or_rgb, _1), VideoRange::FULL, false); + if (i.frame()->colour_conversion()) { + dcp::rgb_to_xyz ( + image->data()[0], + image->size(), + image->stride()[0], + _xyz_buffer, + _xyz_buffer_stride, + i.frame()->colour_conversion().get() + ); + } else { + /* XXX */ + } + + auto r = fastImportFromHostCopy( + _adapter, + _xyz_buffer, + image->size().width, + _xyz_buffer_stride, + image->size().height + ); + if (r != FAST_OK) { + throw FastvideoError ("ImportFromHostCopy", r); + } + + fastEncoderJ2kDynamicParameters_t dynamic_parameters; + dynamic_parameters.targetStreamSize = i.j2k_bandwidth() / (8 * i.frames_per_second()); + dynamic_parameters.quality = 1.0; + dynamic_parameters.writeHeader = false; + + r = fastEncoderJ2kAddImageToBatch( + _encoder, + &dynamic_parameters, + image->size().width, + image->size().height + ); + if (r != FAST_OK) { + throw FastvideoError ("EncoderJ2kAddImageToBatch", r); + } + } + + + fastEncoderJ2kReport_t report; + fastEncoderJ2kOutput_t output; + int constexpr max_j2k_size = 1024 * 1024 * 2; + output.bufferSize = max_j2k_size; + dcp::ArrayData data(output.bufferSize); + output.byteStream = data.data(); + + auto r = fastEncoderJ2kTransformBatch(_encoder, &output, &report); + if (r != FAST_OK) { + fastTraceClose(); + throw FastvideoError ("EncoderJ2KTransformBatch", r); + } + + vector encoded; + for (size_t i = 0; i < video.size(); ++i) { + data.set_size (output.streamSize); + encoded.push_back (data); + data = dcp::ArrayData(output.bufferSize); + output.byteStream = data.data(); + int images_left = 0; + r = fastEncoderJ2kGetNextEncodedImage (_encoder, &output, &report, &images_left); + } + + return encoded; +} + + diff --git a/src/lib/j2k_encoder_fastvideo_backend.h b/src/lib/j2k_encoder_fastvideo_backend.h new file mode 100644 index 000000000..0e5ccb3be --- /dev/null +++ b/src/lib/j2k_encoder_fastvideo_backend.h @@ -0,0 +1,63 @@ +/* + Copyright (C) 2021 Carl Hetherington + + This file is part of DCP-o-matic. + + DCP-o-matic is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + DCP-o-matic is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with DCP-o-matic. If not, see . + +*/ + + +#ifndef DCPOMATIC_J2K_ENCODER_FASTVIDEO_BACKEND_H +#define DCPOMATIC_J2K_ENCODER_FASTVIDEO_BACKEND_H + + +#include "j2k_encoder_backend.h" +#include +#include +#include +#include + + +class Barrier; + + +class J2KEncoderFastvideoBackend : public J2KEncoderBackend +{ +public: + J2KEncoderFastvideoBackend (Barrier& barrier); + ~J2KEncoderFastvideoBackend (); + + std::vector encode (std::vector const& video) override; + + int quantity () const override { + return 1; + } + +private: + void setup (dcp::Size size); + + Barrier& _barrier; + bool _setup_done = false; + fastImportFromHostHandle_t _adapter; + fastDeviceSurfaceBufferHandle_t _src_buffer; + fastEncoderJ2kHandle_t _encoder; + uint16_t* _xyz_buffer; + int _xyz_buffer_stride; +}; + + +#endif + + diff --git a/src/lib/j2k_encoder_remote_backend.cc b/src/lib/j2k_encoder_remote_backend.cc index df5d6e30d..b50ddad7b 100644 --- a/src/lib/j2k_encoder_remote_backend.cc +++ b/src/lib/j2k_encoder_remote_backend.cc @@ -38,22 +38,11 @@ DCPOMATIC_ENABLE_WARNINGS using std::make_shared; -using std::shared_ptr; using std::string; -using std::unique_ptr; using std::vector; -using boost::optional; using dcp::raw_convert; -J2KEncoderRemoteBackend::J2KEncoderRemoteBackend (J2KEncoderRemoteBackend&& other) - : _server (other._server) - , _backoff (other._backoff) -{ - -} - - vector J2KEncoderRemoteBackend::encode (vector const& all_video) { diff --git a/src/lib/j2k_encoder_remote_backend.h b/src/lib/j2k_encoder_remote_backend.h index 962944bbe..b034825d3 100644 --- a/src/lib/j2k_encoder_remote_backend.h +++ b/src/lib/j2k_encoder_remote_backend.h @@ -35,8 +35,6 @@ public: , _timeout (timeout) {} - J2KEncoderRemoteBackend (J2KEncoderRemoteBackend&& other); - std::vector encode (std::vector const& video) override; private: diff --git a/src/lib/wscript b/src/lib/wscript index 9d7e1d11b..80eb7829e 100644 --- a/src/lib/wscript +++ b/src/lib/wscript @@ -44,6 +44,7 @@ sources = """ audio_processor.cc audio_ring_buffers.cc audio_stream.cc + barrier.cc butler.cc text_content.cc text_decoder.cc @@ -226,6 +227,9 @@ def build(bld): obj.source += ' cross_linux.cc' if bld.env.STATIC_DCPOMATIC: obj.uselib += ' XMLPP' + if bld.env.ENABLE_FASTVIDEO: + obj.uselib += ' FASTVIDEO' + obj.source += ' j2k_encoder_fastvideo_backend.cc' obj.target = 'dcpomatic2' diff --git a/src/tools/wscript b/src/tools/wscript index 018689e03..39c8279e7 100644 --- a/src/tools/wscript +++ b/src/tools/wscript @@ -41,6 +41,8 @@ def build(bld): uselib += 'WINSOCK2 DBGHELP SHLWAPI MSWSOCK BOOST_LOCALE WINSOCK2 OLE32 DSOUND WINMM KSUSER SETUPAPI ' if bld.env.TARGET_LINUX: uselib += 'DL ' + if bld.env.ENABLE_FASTVIDEO: + uselib += ' FASTVIDEO' cli_tools = ['dcpomatic_cli', 'dcpomatic_server_cli', 'server_test', 'dcpomatic_kdm_cli', 'dcpomatic_create'] if bld.env.ENABLE_DISK and not bld.env.DISABLE_GUI: diff --git a/src/wx/full_config_dialog.cc b/src/wx/full_config_dialog.cc index db499f575..d10b01d8e 100644 --- a/src/wx/full_config_dialog.cc +++ b/src/wx/full_config_dialog.cc @@ -94,6 +94,13 @@ private: int r = 0; add_language_controls (table, r); +#ifdef DCPOMATIC_FASTVIDEO + add_label_to_sizer (table, _panel, _("Encode using"), true, wxGBPosition(r, 0)); + _encoding_backend = new wxChoice (_panel, wxID_ANY); + table->Add (_encoding_backend, wxGBPosition(r, 1)); + ++r; +#endif + add_label_to_sizer (table, _panel, _("Number of threads DCP-o-matic should use"), true, wxGBPosition (r, 0)); _master_encoding_threads = new wxSpinCtrl (_panel); table->Add (_master_encoding_threads, wxGBPosition (r, 1)); @@ -128,6 +135,11 @@ private: add_update_controls (table, r); +#ifdef DCPOMATIC_FASTVIDEO + _encoding_backend->Append ("CPU"); + _encoding_backend->Append ("GPU"); + _encoding_backend->Bind (wxEVT_CHOICE, boost::bind(&FullGeneralPage::encoding_backend_changed, this)); +#endif _config_file->Bind (wxEVT_FILEPICKER_CHANGED, boost::bind(&FullGeneralPage::config_file_changed, this)); _cinemas_file->Bind (wxEVT_FILEPICKER_CHANGED, boost::bind(&FullGeneralPage::cinemas_file_changed, this)); @@ -147,6 +159,10 @@ private: { auto config = Config::instance (); +#ifdef DCPOMATIC_FASTVIDEO + checked_set (_encoding_backend, Config::instance()->encoding_backend() == Config::EncodingBackend::CPU ? 0 : 1); +#endif + checked_set (_master_encoding_threads, config->master_encoding_threads ()); checked_set (_server_encoding_threads, config->server_encoding_threads ()); #ifdef DCPOMATIC_HAVE_EBUR128_PATCHED_FFMPEG @@ -159,6 +175,11 @@ private: GeneralPage::config_changed (); } + void encoding_backend_changed () + { + Config::instance()->set_encoding_backend(_encoding_backend->GetSelection() == 0 ? Config::EncodingBackend::CPU : Config::EncodingBackend::FASTVIDEO); + } + void export_cinemas_file () { auto d = new wxFileDialog ( @@ -225,6 +246,9 @@ private: Config::instance()->set_cinemas_file (wx_to_std (_cinemas_file->GetPath ())); } +#ifdef DCPOMATIC_FASTVIDEO + wxChoice* _encoding_backend; +#endif wxSpinCtrl* _master_encoding_threads; wxSpinCtrl* _server_encoding_threads; FilePickerCtrl* _config_file; diff --git a/src/wx/wscript b/src/wx/wscript index 50c078f2b..757772183 100644 --- a/src/wx/wscript +++ b/src/wx/wscript @@ -316,6 +316,8 @@ def build(bld): obj.uselib += 'WINSOCK2 OLE32 DSOUND WINMM KSUSER GL GLU GLEW ' if bld.env.TARGET_OSX: obj.framework = ['CoreAudio', 'OpenGL'] + if bld.env.ENABLE_FASTVIDEO: + obj.uselib += ' FASTVIDEO' obj.use = 'libdcpomatic2' obj.source = sources obj.target = 'dcpomatic2-wx' diff --git a/test/barrier_test.cc b/test/barrier_test.cc new file mode 100644 index 000000000..c7bfbb801 --- /dev/null +++ b/test/barrier_test.cc @@ -0,0 +1,98 @@ +/* + Copyright (C) 2021 Carl Hetherington + + This file is part of DCP-o-matic. + + DCP-o-matic is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + DCP-o-matic is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with DCP-o-matic. If not, see . + +*/ + + +#include "lib/barrier.h" +#include "lib/cross.h" +#include +#include + + +BOOST_AUTO_TEST_CASE (barrier_test) +{ + int constexpr num_threads = 128; + + Barrier barrier (num_threads); + + boost::mutex mutex; + int count = 0; + + auto thread_body = [&barrier, &mutex, &count]() { + barrier.wait(); + boost::mutex::scoped_lock lm(mutex); + ++count; + }; + + std::vector threads; + for (int i = 0; i < num_threads - 1; ++i) { + threads.push_back(new boost::thread(thread_body)); + } + + dcpomatic_sleep_seconds(5); + + BOOST_CHECK_EQUAL (count, 0); + + threads.push_back(new boost::thread(thread_body)); + dcpomatic_sleep_seconds(5); + + BOOST_CHECK_EQUAL (count, num_threads); + + for (auto i: threads) { + i->join(); + delete i; + } +} + + +BOOST_AUTO_TEST_CASE (barrier_test_lower) +{ + int constexpr num_threads = 128; + + Barrier barrier (num_threads); + + boost::mutex mutex; + int count = 0; + + auto thread_body = [&barrier, &mutex, &count]() { + barrier.wait(); + boost::mutex::scoped_lock lm(mutex); + ++count; + }; + + std::vector threads; + for (int i = 0; i < num_threads / 2; ++i) { + threads.push_back(new boost::thread(thread_body)); + } + + dcpomatic_sleep_seconds(5); + + BOOST_CHECK_EQUAL (count, 0); + + barrier.lower(); + dcpomatic_sleep_seconds(5); + + BOOST_CHECK_EQUAL (count, num_threads / 2); + + for (auto i: threads) { + i->join(); + delete i; + } +} + diff --git a/test/fastvideo_test.cc b/test/fastvideo_test.cc new file mode 100644 index 000000000..23369de62 --- /dev/null +++ b/test/fastvideo_test.cc @@ -0,0 +1,73 @@ +/* + Copyright (C) 2021 Carl Hetherington + + This file is part of DCP-o-matic. + + DCP-o-matic is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + DCP-o-matic is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with DCP-o-matic. If not, see . + +*/ + + +#include "lib/barrier.h" +#include "lib/colour_conversion.h" +#include "lib/dcp_video.h" +#include "lib/ffmpeg_image_proxy.h" +#include "lib/player_video.h" +#include "lib/j2k_encoder_fastvideo_backend.h" +#include "test.h" +#include + + +BOOST_AUTO_TEST_CASE (fastvideo_frame_size_test) +{ + auto test = [](int j2k_bandwidth) { + Barrier barrier (1); + J2KEncoderFastvideoBackend backend (barrier); + + auto image_proxy = std::make_shared(TestPaths::private_data() / "prophet_frame.tiff"); + auto player_video = std::make_shared ( + image_proxy, + Crop(), + boost::optional(), + dcp::Size(1998, 1080), + dcp::Size(1998, 1080), + Eyes::BOTH, + Part::WHOLE, + PresetColourConversion::from_id("rec709").conversion, + VideoRange::FULL, + std::weak_ptr(), + boost::optional(), + false + ); + + DCPVideo dcp_video ( + player_video, + 0, + 24, + j2k_bandwidth, + Resolution::TWO_K + ); + + auto result = backend.encode ({dcp_video}); + return result[0].size() * 8L * 24 / 1000000.0f; + }; + + auto bandwidth = std::vector{ 50, 100, 150, 200, 250 }; + + for (auto b: bandwidth) { + auto mbps = test(b * 1000000.0f); + BOOST_CHECK_CLOSE (mbps, b, 5); + } +} + diff --git a/test/wscript b/test/wscript index ff6895d9a..d039e083e 100644 --- a/test/wscript +++ b/test/wscript @@ -40,6 +40,8 @@ def build(bld): obj.uselib += 'WINSOCK2 DBGHELP SHLWAPI MSWSOCK BOOST_LOCALE ' if bld.env.TARGET_LINUX: obj.uselib += 'DL ' + if bld.env.ENABLE_FASTVIDEO: + obj.uselib += ' FASTVIDEO' obj.use = 'libdcpomatic2' obj.source = """ 4k_test.cc @@ -53,6 +55,7 @@ def build(bld): audio_processor_test.cc audio_processor_delay_test.cc audio_ring_buffers_test.cc + barrier_test.cc butler_test.cc cinema_sound_processor_test.cc client_server_test.cc @@ -152,6 +155,9 @@ def build(bld): obj.source += " disk_writer_test.cc" obj.uselib += "LWEXT4 NANOMSG " + if bld.env.ENABLE_FASTVIDEO: + obj.source += " fastvideo_test.cc" + # Some difference in font rendering between the test machine and others... # burnt_subtitle_test.cc # This one doesn't check anything diff --git a/wscript b/wscript index 5c729bd43..de116f158 100644 --- a/wscript +++ b/wscript @@ -77,6 +77,7 @@ def options(opt): opt.add_option('--enable-disk', action='store_true', default=False, help='build dcpomatic2_disk tool; requires Boost process, lwext4 and nanomsg libraries') opt.add_option('--warnings-are-errors', action='store_true', default=False, help='build with -Werror') opt.add_option('--wx-config', help='path to wx-config') + opt.add_option('--fastvideo-sdk', help='path to fastvideo SDK (containing fastvideo_sdk directory)') def configure(conf): conf.load('compiler_cxx') @@ -94,6 +95,7 @@ def configure(conf): conf.env.DEBUG = conf.options.enable_debug conf.env.STATIC_DCPOMATIC = conf.options.static_dcpomatic conf.env.ENABLE_DISK = conf.options.enable_disk + conf.env.ENABLE_FASTVIDEO = conf.options.fastvideo_sdk is not None if conf.options.destdir == '': conf.env.INSTALL_PREFIX = conf.options.prefix else: @@ -542,6 +544,17 @@ def configure(conf): lib=deps, uselib_store='BOOST_PROCESS') + # fastvideo + if conf.options.fastvideo_sdk is not None: + conf.env.INCLUDES_FASTVIDEO = [ + os.path.join(conf.options.fastvideo_sdk), + os.path.join(conf.options.fastvideo_sdk, "core_samples"), + os.path.join(conf.options.fastvideo_sdk, "fastvideo_sdk", "inc")] + conf.env.LIBPATH_FASTVIDEO = [ os.path.join(conf.options.fastvideo_sdk, "fastvideo_sdk", "lib") ] + conf.env.LIB_FASTVIDEO = [ 'fastvideo_sdk', 'fastvideo_decoder_j2k', 'fastvideo_encoder_j2k', 'cuda', 'cudart', 'omp5' ] + conf.env.append_value('CXXFLAGS', '-DDCPOMATIC_FASTVIDEO') + + # Other stuff conf.find_program('msgfmt', var='MSGFMT') -- 2.30.2