From 707ad3c26495d270b6ac9316aecf9b5ff97f3b29 Mon Sep 17 00:00:00 2001 From: itsmattkc Date: Thu, 30 May 2019 21:50:18 +1000 Subject: [PATCH] finished prototype for float pix fmt converter --- CMakeLists.txt | 12 +- decoders/decoder.h | 4 - decoders/ffmpegdecoder.cpp | 4 + decoders/ffmpegvideodecoder.cpp | 7 + decoders/frame.cpp | 10 + decoders/frame.h | 10 + decoders/pixelformatconverter.cpp | 74 +++++--- decoders/pixelformatconverter.h | 4 + dialogs/newsequencedialog.cpp | 6 + dialogs/preferencesdialog.cpp | 4 +- global/config.cpp | 4 +- global/config.h | 5 +- global/global.cpp | 7 + global/global.h | 16 +- main.cpp | 4 + nodes/nodegraph.cpp | 12 +- nodes/nodegraph.h | 6 +- nodes/nodes/nodemedia.cpp | 58 +++++- nodes/nodes/nodemedia.h | 9 +- rendering/framebufferobject.cpp | 4 +- rendering/imagecache.cpp | 296 ++++++++++++++++++++++++++++++ rendering/imagecache.h | 91 +++++++++ rendering/memorybuffer.cpp | 43 +++++ rendering/memorybuffer.h | 28 +++ rendering/memorycache.cpp | 182 ------------------ rendering/memorycache.h | 58 ------ timeline/sequence.cpp | 5 +- ui/viewerwidget.cpp | 8 +- 28 files changed, 671 insertions(+), 300 deletions(-) create mode 100644 rendering/imagecache.cpp create mode 100644 rendering/imagecache.h create mode 100644 rendering/memorybuffer.cpp create mode 100644 rendering/memorybuffer.h delete mode 100644 rendering/memorycache.cpp delete mode 100644 rendering/memorycache.h diff --git a/CMakeLists.txt b/CMakeLists.txt index b7629de44..8b821505e 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -294,8 +294,10 @@ set(OLIVE_SOURCES rendering/exportthread.h rendering/framebufferobject.cpp rendering/framebufferobject.h - rendering/memorycache.h - rendering/memorycache.cpp + rendering/imagecache.h + rendering/imagecache.cpp + rendering/memorybuffer.h + rendering/memorybuffer.cpp rendering/pixelformats.cpp rendering/pixelformats.h rendering/qopenglshaderprogramptr.h @@ -544,9 +546,9 @@ if(MINGW) target_link_libraries(${OLIVE_TARGET} PRIVATE DbgHelp) # Crash report uses MSVC-style PDBs, we convert symbols to this format here - add_custom_command(TARGET ${OLIVE_TARGET} - POST_BUILD - COMMAND ${CMAKE_SOURCE_DIR}/packaging/windows/cv2pdb -C -n "${CMAKE_BINARY_DIR}/${OLIVE_TARGET}.exe") + #add_custom_command(TARGET ${OLIVE_TARGET} + # POST_BUILD + # COMMAND ${CMAKE_SOURCE_DIR}/packaging/windows/cv2pdb -C -n "${CMAKE_BINARY_DIR}/${OLIVE_TARGET}.exe") endif() if(DOXYGEN_FOUND AND BUILD_DOXYGEN) diff --git a/decoders/decoder.h b/decoders/decoder.h index 2ba937bcb..91d9452bc 100644 --- a/decoders/decoder.h +++ b/decoders/decoder.h @@ -33,10 +33,6 @@ public: virtual FramePtr Retrieve(const rational& timecode, const rational& length = 0) = 0; virtual void Close() = 0; - // For video decoding - virtual int width() = 0; - virtual int height() = 0; - protected: bool open_; diff --git a/decoders/ffmpegdecoder.cpp b/decoders/ffmpegdecoder.cpp index c620c30b4..0885fc709 100644 --- a/decoders/ffmpegdecoder.cpp +++ b/decoders/ffmpegdecoder.cpp @@ -27,6 +27,10 @@ bool FFmpegDecoder::Open() int error_code; + qDebug() << "shid"; + qDebug() << stream()->footage; + qDebug() << stream()->footage->url; + QByteArray ba = stream()->footage->url.toUtf8(); const char* filename = ba.constData(); diff --git a/decoders/ffmpegvideodecoder.cpp b/decoders/ffmpegvideodecoder.cpp index 3b5e9fec7..09d3ea3e9 100644 --- a/decoders/ffmpegvideodecoder.cpp +++ b/decoders/ffmpegvideodecoder.cpp @@ -9,6 +9,13 @@ FFmpegVideoDecoder::FFmpegVideoDecoder() FramePtr FFmpegVideoDecoder::Retrieve(const rational &timecode, const rational &length) { + if (!open_ && !Open()) { + qWarning() << "Failed to open FFmpeg stream"; + return nullptr; + } + + + // TODO index frames int receive_ret; diff --git a/decoders/frame.cpp b/decoders/frame.cpp index 2bcc001d4..dec60f5b9 100644 --- a/decoders/frame.cpp +++ b/decoders/frame.cpp @@ -70,6 +70,16 @@ const int &Frame::format() return frame_->format; } +uint8_t **Frame::data() +{ + return frame_->data; +} + +int *Frame::linesize() +{ + return frame_->linesize; +} + void Frame::FreeChild() { if (frame_ != nullptr) { diff --git a/decoders/frame.h b/decoders/frame.h index ed2d16491..f2bb98160 100644 --- a/decoders/frame.h +++ b/decoders/frame.h @@ -73,6 +73,16 @@ public: */ const int& format(); + /** + * @brief Get the data buffer of this frame + */ + uint8_t** data(); + + /** + * @brief Get the linesize information for this frame + */ + int* linesize(); + private: void FreeChild(); diff --git a/decoders/pixelformatconverter.cpp b/decoders/pixelformatconverter.cpp index ede2d20c1..5b2a28dc0 100644 --- a/decoders/pixelformatconverter.cpp +++ b/decoders/pixelformatconverter.cpp @@ -43,9 +43,10 @@ void PixelFormatConverter::AVFrameToPipeline(uint8_t **input_buffer, // Currently we don't natively support anything other than RGBA and RGBA64 so if it isn't this, we'll need to // swscale it to one of those for the timebeing uint8_t *converted_buffer = nullptr; + int converted_linesize[AV_NUM_DATA_POINTERS]; if (input_fmt != AV_PIX_FMT_RGBA && input_fmt != AV_PIX_FMT_RGBA64) { - converted_buffer = new uint8_t[input_linesize[0] * height]; + converted_buffer = new uint8_t[input_linesize[0] * height * 4]; // Determine whether this is an 8-bit image or higher AVPixelFormat possible_pix_fmts[] = { @@ -70,9 +71,26 @@ void PixelFormatConverter::AVFrameToPipeline(uint8_t **input_buffer, nullptr, nullptr); - sws_scale(sws_ctx, input_buffer, input_linesize, 0, height, &converted_buffer, input_linesize); + + switch (pix_fmt) { + case AV_PIX_FMT_RGBA: + converted_linesize[0] = width * 4; + break; + case AV_PIX_FMT_RGBA64: + converted_linesize[0] = width * 8; + break; + default: + // We shouldn't really ever get here, but we may as well handle it + qWarning() << "Invalid destination pixel format"; + } + + + sws_scale(sws_ctx, input_buffer, input_linesize, 0, height, &converted_buffer, converted_linesize); + + sws_freeContext(sws_ctx); input_fmt = pix_fmt; + input_buffer = &converted_buffer; } @@ -101,27 +119,27 @@ void PixelFormatConverter::AVFrameToPipeline(uint8_t **input_buffer, // Wait for each thread to complete for (int i=0;iwait(); + threads_.at(i)->WaitUntilComplete(); } mutex_.unlock(); - if (converted_buffer != nullptr) { - delete [] converted_buffer; - } + delete [] converted_buffer; } int PixelFormatConverter::GetBufferSize(olive::PixelFormat format, const int &width, const int &height) { + int rgba_channels = 4; + switch (format) { case olive::PIX_FMT_RGBA8: - return width * height; + return width * height * rgba_channels; case olive::PIX_FMT_RGBA16: case olive::PIX_FMT_RGBA16F: - return 2 * width * height; + return 2 * width * height * rgba_channels; case olive::PIX_FMT_RGBA32F: - return 4 * width * height; + return 4 * width * height * rgba_channels; default: return 0; } @@ -134,12 +152,16 @@ PixFmtConvertThread::PixFmtConvertThread() : void PixFmtConvertThread::run() { + mutex_.lock(); + while (!cancelled_) { wait_cond_.wait(&mutex_); if (cancelled_) break; Process(); } + + mutex_.unlock(); } void PixFmtConvertThread::Convert(uint8_t **input_buffer, @@ -151,6 +173,7 @@ void PixFmtConvertThread::Convert(uint8_t **input_buffer, void *output_buffer, olive::PixelFormat output_fmt) { + mutex2_.lock(); mutex_.lock(); input_buffer_ = input_buffer; @@ -167,6 +190,14 @@ void PixFmtConvertThread::Convert(uint8_t **input_buffer, mutex_.unlock(); } +void PixFmtConvertThread::WaitUntilComplete() +{ + mutex2_.lock(); + mutex_.lock(); + mutex2_.unlock(); + mutex_.unlock(); +} + void PixFmtConvertThread::Cancel() { cancelled_ = true; @@ -176,28 +207,29 @@ void PixFmtConvertThread::Cancel() void PixFmtConvertThread::Process() { + mutex2_.unlock(); + switch (input_fmt_) { case AV_PIX_FMT_RGBA: { - int in_start = input_linesize_[0]*line_start_; - int out_start = width_*line_start_; - float f; int channels = 4; // FIXME RGBA magic number + int byte_linesize = input_linesize_[0]*channels; + + int input_start = byte_linesize*line_start_; + int output_start = width_*channels*line_start_; + for (int i=0;i(output_buffer_)[out_line_start+j] = f; + // Convert 8-bit integer to float and store in output buffer + static_cast(output_buffer_)[output_line_start+j] = input_buffer_[0][input_line_start+j] / 255.0f; } } diff --git a/decoders/pixelformatconverter.h b/decoders/pixelformatconverter.h index 55d040763..5957ef942 100644 --- a/decoders/pixelformatconverter.h +++ b/decoders/pixelformatconverter.h @@ -25,6 +25,9 @@ public: AVPixelFormat input_fmt, void* output_buffer, olive::PixelFormat output_fmt); + + void WaitUntilComplete(); + void Cancel(); private: @@ -34,6 +37,7 @@ private: // Threading variables QWaitCondition wait_cond_; QMutex mutex_; + QMutex mutex2_; bool cancelled_; // Input variables for conversion diff --git a/dialogs/newsequencedialog.cpp b/dialogs/newsequencedialog.cpp index ce4d12e37..8724c2482 100644 --- a/dialogs/newsequencedialog.cpp +++ b/dialogs/newsequencedialog.cpp @@ -114,6 +114,12 @@ void NewSequenceDialog::accept() { // FIXME: TEST CODE NodeMedia* m = new NodeMedia(s.get()); s->texture_io = m->texture_output(); + + QStringList strings = {"E:/samples/P1270472.MP4"}; + olive::project_model.process_file_list(strings); + Footage* fff = olive::project_model.GetLastImportedMedia().first()->to_footage(); + fff->ready_lock.lock(); + m->SetMedia(&fff->video_tracks.first()); // END TEST CODE ComboAction* ca = new ComboAction(); diff --git a/dialogs/preferencesdialog.cpp b/dialogs/preferencesdialog.cpp index 81eacefb3..ed9ac7dea 100644 --- a/dialogs/preferencesdialog.cpp +++ b/dialogs/preferencesdialog.cpp @@ -449,8 +449,8 @@ void PreferencesDialog::accept() { olive::config.ocio_config_path = ocio_config_file->text(); } olive::config.enable_color_management = enable_color_management->isChecked(); - olive::config.playback_bit_depth = playback_bit_depth->currentIndex(); - olive::config.export_bit_depth = export_bit_depth->currentIndex(); + olive::config.playback_bit_depth = static_cast(playback_bit_depth->currentIndex()); + olive::config.export_bit_depth = static_cast(export_bit_depth->currentIndex()); olive::config.ocio_display = ocio_display->currentText(); olive::config.ocio_default_input_colorspace = ocio_default_input->currentText(); olive::config.ocio_view = ocio_view->currentText(); diff --git a/global/config.cpp b/global/config.cpp index 917bc66cc..14a37c832 100644 --- a/global/config.cpp +++ b/global/config.cpp @@ -261,10 +261,10 @@ void Config::load(QString path) { default_sequence_audio_channel_layout = stream.text().toInt(); } else if (stream.name() == "PlaybackBitDepth") { stream.readNext(); - playback_bit_depth = stream.text().toInt(); + playback_bit_depth = static_cast(stream.text().toInt()); } else if (stream.name() == "ExportBitDepth") { stream.readNext(); - export_bit_depth = stream.text().toInt(); + export_bit_depth = static_cast(stream.text().toInt()); } else if (stream.name() == "DontUseProxiesOnExport") { stream.readNext(); dont_use_proxies_on_export = (stream.text() == "1"); diff --git a/global/config.h b/global/config.h index 293bcdcca..ddb6b12cd 100644 --- a/global/config.h +++ b/global/config.h @@ -25,6 +25,7 @@ #include "ui/styling.h" #include "timeline/timelinetools.h" +#include "rendering/pixelformats.h" namespace olive { /** @@ -597,12 +598,12 @@ struct Config { /** * @brief Playback bit depth (an index of olive::rendering::bit_depths) */ - int playback_bit_depth; + olive::PixelFormat playback_bit_depth; /** * @brief Export bit depth (an index of olive::rendering::bit_depths) */ - int export_bit_depth; + olive::PixelFormat export_bit_depth; /** * @brief Don't use proxies on export (use originals instead) diff --git a/global/global.cpp b/global/global.cpp index e1bf29bc9..479c4d960 100644 --- a/global/global.cpp +++ b/global/global.cpp @@ -120,6 +120,13 @@ bool OliveGlobal::is_exporting() return rendering_; } +const olive::PixelFormat &OliveGlobal::effective_bit_depth() +{ + // FIXME uncomment this +// return olive::Global->is_exporting() ? olive::config.export_bit_depth : olive::config.playback_bit_depth; + return olive::config.export_bit_depth; +} + void OliveGlobal::set_export_state(bool rendering) { rendering_ = rendering; if (rendering) { diff --git a/global/global.h b/global/global.h index 458c41c6c..5722088f7 100644 --- a/global/global.h +++ b/global/global.h @@ -22,12 +22,14 @@ #define OLIVEGLOBAL_H #include -#include "undo/undo.h" #include #include #include +#include "undo/undo.h" +#include "rendering/pixelformats.h" + /** * @brief The Olive Global class * @@ -84,6 +86,14 @@ public: */ bool is_exporting(); + /** + * @brief Returns the "effective" bit depth for the composition pipeline + * + * Convenience function for using Config::playback_bit_depth or Config::export_bit_depth depending on the state of + * OliveGlobal::is_exporting() + */ + const olive::PixelFormat& effective_bit_depth(); + /** * @brief Set the application state depending on if the user is exporting a video * @@ -554,6 +564,10 @@ extern QString ActiveProjectFilename; * @brief Current application name */ extern QString AppName; + +/** + * Rational type for + */ } #endif // OLIVEGLOBAL_H diff --git a/main.cpp b/main.cpp index 510cbbf77..6397b8413 100644 --- a/main.cpp +++ b/main.cpp @@ -21,6 +21,7 @@ #include #include +#include "decoders/pixelformatconverter.h" #include "dialogs/crashdialog.h" #include "global/crashhandler.h" #include "global/debug.h" @@ -147,6 +148,9 @@ int main(int argc, char *argv[]) { // set up rendering bit depths olive::InitializePixelFormats(); + // initialize pixel format converter + olive::pix_fmt_conv = new PixelFormatConverter(); + // connect main window's first paint to global's init finished function QObject::connect(&w, SIGNAL(finished_first_paint()), olive::Global.get(), SLOT(finished_initialize()), Qt::QueuedConnection); diff --git a/nodes/nodegraph.cpp b/nodes/nodegraph.cpp index d2bfb22fe..e65be0639 100644 --- a/nodes/nodegraph.cpp +++ b/nodes/nodegraph.cpp @@ -45,11 +45,16 @@ void NodeGraph::SetTime(const rational &d) emit TimeChanged(); } -MemoryCache *NodeGraph::memory_cache() +ImageCache *NodeGraph::memory_cache() { return &memory_cache_; } +QOpenGLContext *NodeGraph::GLContext() +{ + return ctx_; +} + const int &NodeGraph::width() { return width_; @@ -74,5 +79,8 @@ void NodeGraph::set_height(const int& h) void NodeGraph::SetGLContext(QOpenGLContext *ctx) { - memory_cache_.SetParameters(ctx, width_, height_); + if (ctx_ != ctx) { + ctx_ = ctx; + memory_cache_.SetParameters(ctx, width_, height_); + } } diff --git a/nodes/nodegraph.h b/nodes/nodegraph.h index 1b61fc699..d0506400a 100644 --- a/nodes/nodegraph.h +++ b/nodes/nodegraph.h @@ -5,7 +5,7 @@ #include #include "nodes/node.h" -#include "rendering/memorycache.h" +#include "rendering/imagecache.h" class NodeGraph : public QObject { @@ -51,7 +51,7 @@ public: const rational &Time(); void SetTime(const rational& d); - MemoryCache* memory_cache(); + ImageCache* memory_cache(); QOpenGLContext* GLContext(); void SetGLContext(QOpenGLContext* ctx); @@ -67,7 +67,7 @@ protected: private: Node* output_node_; - MemoryCache memory_cache_; + ImageCache memory_cache_; QOpenGLContext* ctx_; int width_; diff --git a/nodes/nodes/nodemedia.cpp b/nodes/nodes/nodemedia.cpp index 613fd3d8f..8b1458d99 100644 --- a/nodes/nodes/nodemedia.cpp +++ b/nodes/nodes/nodemedia.cpp @@ -3,11 +3,16 @@ #include #include "nodes/nodegraph.h" +#include "decoders/ffmpegvideodecoder.h" +#include "decoders/pixelformatconverter.h" +#include "global/config.h" +#include "global/global.h" NodeMedia::NodeMedia(NodeGraph* c) : Node(c), media_(nullptr), - buffer_(c->memory_cache()), + img_buffer_(c->memory_cache()), + tex_buffer_(c->memory_cache()), decoder_(nullptr) { matrix_input_ = new NodeIO(this, "matrix", tr("Matrix"), true, false); @@ -29,21 +34,58 @@ QString NodeMedia::id() void NodeMedia::Process(const rational &time) { - Q_UNUSED(time) + QOpenGLFunctions* f = ParentGraph()->GLContext()->functions(); - buffer_.buffer()->BindBuffer(); + tex_buffer_.buffer()->BindBuffer(); - glClearColor(0.5, 0.0, 1.0, 1.0); - glClear(GL_COLOR_BUFFER_BIT); + f->glClearColor(0.0, 0.5, 0.0, 0.0); + f->glClear(GL_COLOR_BUFFER_BIT); - buffer_.buffer()->ReleaseBuffer(); + tex_buffer_.buffer()->ReleaseBuffer(); - texture_output_->SetValue(buffer_.buffer()->texture()); + FramePtr frame = decoder_->Retrieve(time); + + olive::pix_fmt_conv->AVFrameToPipeline(frame->data(), + frame->linesize(), + frame->width(), + frame->height(), + static_cast(frame->format()), + img_buffer_.buffer()->data(), + olive::Global->effective_bit_depth()); + + const olive::PixelFormatInfo& pix_fmt_info = olive::pixel_formats.at(olive::Global->effective_bit_depth()); + + tex_buffer_.buffer()->BindTexture(); + + f->glTexSubImage2D(GL_TEXTURE_2D, + 0, + 0, + 0, + frame->width(), + frame->height(), + pix_fmt_info.pixel_format, + pix_fmt_info.pixel_type, + img_buffer_.buffer()->data() + ); + + tex_buffer_.buffer()->ReleaseTexture(); + + qDebug() << "Uploaded to texture" << tex_buffer_.buffer()->texture(); + + texture_output_->SetValue(tex_buffer_.buffer()->texture()); } -void NodeMedia::SetMedia(Media *f) +void NodeMedia::SetMedia(FootageStream *f) { + // TODO method of finding the decoder we need + decoder_ = std::make_shared(); + + media_ = f; + + decoder_->set_stream(media_); + + img_buffer_.SetSize(f->video_width, f->video_height); } NodeIO *NodeMedia::matrix_input() diff --git a/nodes/nodes/nodemedia.h b/nodes/nodes/nodemedia.h index 25e20eb92..36f218333 100644 --- a/nodes/nodes/nodemedia.h +++ b/nodes/nodes/nodemedia.h @@ -2,7 +2,7 @@ #define MEDIANODE_H #include "nodes/node.h" -#include "rendering/memorycache.h" +#include "rendering/imagecache.h" #include "project/media.h" #include "decoders/decoder.h" @@ -29,7 +29,7 @@ public: virtual void Process(const rational& time) override; - void SetMedia(Media* f); + void SetMedia(FootageStream* f); NodeIO* matrix_input(); NodeIO* texture_output(); @@ -42,11 +42,12 @@ private: NodeIO* texture_output_; // Media object to display - Media* media_; + FootageStream* media_; // Texture buffer // TODO Probable cache point - MemoryCache::Reference buffer_; + ImageCache::ImgRef img_buffer_; + ImageCache::TexRef tex_buffer_; // Decoder DecoderPtr decoder_; diff --git a/rendering/framebufferobject.cpp b/rendering/framebufferobject.cpp index 153c6f7eb..bfc9ed70a 100644 --- a/rendering/framebufferobject.cpp +++ b/rendering/framebufferobject.cpp @@ -67,9 +67,7 @@ void FramebufferObject::Create(QOpenGLContext *ctx, int width, int height) f->glBindTexture(GL_TEXTURE_2D, texture_); // allocate storage for texture - const olive::PixelFormatInfo& bit_depth = olive::pixel_formats.at(olive::Global->is_exporting() ? - olive::config.export_bit_depth : - olive::config.playback_bit_depth); + const olive::PixelFormatInfo& bit_depth = olive::pixel_formats.at(olive::Global->effective_bit_depth()); ctx->functions()->glTexImage2D( GL_TEXTURE_2D, diff --git a/rendering/imagecache.cpp b/rendering/imagecache.cpp new file mode 100644 index 000000000..6f2c473ce --- /dev/null +++ b/rendering/imagecache.cpp @@ -0,0 +1,296 @@ +#include "imagecache.h" + +#include + +#include "decoders/pixelformatconverter.h" +#include "global/config.h" +#include "global/global.h" + +ImageCache::ImageCache() : + ctx_(nullptr), + width_(0), + height_(0) +{ + +} + +ImageCache::~ImageCache() +{ + buffer_array_mutex_.lock(); + + Clear(); + + buffer_array_mutex_.unlock(); +} + +void ImageCache::SetParameters(QOpenGLContext *ctx, int width, int height) +{ + buffer_array_mutex_.lock(); + + ctx_ = ctx; + + if (ctx_ != nullptr) { + Q_ASSERT(width > 0 && height > 0); + + width_ = width; + height_ = height; + } + + Clear(); + + buffer_array_mutex_.unlock(); +} + +int ImageCache::RequestBuffer(Ref* r, const BufferType& type, int width, int height) +{ + if (type == kTexBuf && (ctx_ == nullptr || width_ == 0 || height_ == 0)) { + qWarning() << "A texture cache request was made without valid parameters [" << ctx_ << width_ << "," << height_ << "]"; + return -1; + } + + buffer_array_mutex_.lock(); + + int buffer = RequestBufferInternal(r, type, width, height); + + buffer_array_mutex_.unlock(); + + return buffer; +} + +int ImageCache::RequestBufferInternal(Ref *r, const BufferType& type, int width, int height) +{ + Q_ASSERT(type != kInvalidBuf); + + QList& relinquished = (type == kMemBuf) ? img_relinquished_ : tex_relinquished_; + QList& refs = (type == kMemBuf) ? img_refs_ : tex_refs_; + QList& access_times = (type == kMemBuf) ? img_access_times_ : tex_access_times_; + + // Try to return a relinquished buffer + if (!relinquished.isEmpty()) { + + if (type == kTexBuf) { + + int buf_index = relinquished.takeFirst(); + + refs.replace(buf_index, r); + + return buf_index; + + } else if (type == kMemBuf) { + + // Check for a relinquished buffer with the desired size + + // + // TODO consider a database for sorting through these buffers + // + for (int i=0;iRelinquish(); + refs.replace(least_recent_access, r); + return least_recent_access; + } + + // Otherwise, just generate a new buffer + int index = -1; + switch (type) { + case kMemBuf: + index = img_buffers_.size(); + img_buffers_.append(MemoryBuffer()); + img_buffers_.last().Create(width_, height_, olive::Global->effective_bit_depth()); + break; + case kTexBuf: + index = tex_buffers_.size(); + tex_buffers_.append(FramebufferObject()); + tex_buffers_.last().Create(ctx_, width_, height_); + break; + default: + Q_ASSERT(false); + } + + refs.append(r); + access_times.append(time(nullptr)); + + return index; +} + +void ImageCache::RelinquishBuffer(int index, const BufferType &type) +{ + buffer_array_mutex_.lock(); + + switch (type) { + case kMemBuf: + img_refs_.replace(index, nullptr); + img_relinquished_.append(index); + break; + case kTexBuf: + tex_refs_.replace(index, nullptr); + tex_relinquished_.append(index); + break; + default: + qFatal("Invalid buffer type on ImageCache::RelinquishBuffer"); + } + + buffer_array_mutex_.unlock(); +} + +void *ImageCache::Buffer(int index, const BufferType &type) +{ + switch (type) { + case kMemBuf: + img_access_times_.replace(index, time(nullptr)); + return &img_buffers_[index]; + case kTexBuf: + tex_access_times_.replace(index, time(nullptr)); + return &tex_buffers_[index]; + default: + qFatal("Invalid buffer type on ImageCache::Buffer"); + } +} + +bool ImageCache::OutOfMemory() +{ + // TODO actually check if we have any memory or not + return false; +} + +void ImageCache::Clear() +{ + for (int i=0;iRelinquish(); + } + + for (int i=0;iRelinquish(); + } + + img_refs_.clear(); + tex_refs_.clear(); + + img_buffers_.clear(); + tex_buffers_.clear(); + + img_relinquished_.clear(); + tex_relinquished_.clear(); +} + +ImageCache::Ref::Ref(ImageCache *cache) : + buffer_type_(kInvalidBuf), + cache_(cache), + buffer_(-1), + width_(-1), + height_(-1) +{ +} + +ImageCache::Ref::~Ref() +{ + Relinquish(); +} + +void *ImageCache::Ref::BufferInternal() +{ + // If we don't have a buffer yet, request one + if (buffer_ == -1) { + Request(); + } + + // If we didn't receive one, return null + if (buffer_ == -1) { + return nullptr; + } + + // Otherwise, return the buffer we received + return cache_->Buffer(buffer_, buffer_type_); +} + +void ImageCache::Ref::Relinquish() +{ + if (buffer_ == -1) { + return; + } + + cache_->RelinquishBuffer(buffer_, buffer_type_); + buffer_ = -1; +} + +void ImageCache::Ref::Request() +{ + // Check if we already have a buffer, in which case we don't need to request a new one + if (buffer_ != -1) { + return; + } + + // Check if we have a valid buffer type to request + if (buffer_type_ == kInvalidBuf) { + qWarning() << "Tried to request an invalid buffer type"; + return; + } + + buffer_ = cache_->RequestBuffer(this, buffer_type_, width_, height_); +} + +ImageCache::ImgRef::ImgRef(ImageCache *cache) : + Ref(cache) +{ + buffer_type_ = kMemBuf; +} + +void ImageCache::ImgRef::SetSize(int w, int h) +{ + width_ = w; + height_ = h; + + Relinquish(); +} + +MemoryBuffer *ImageCache::ImgRef::buffer() +{ + return static_cast(BufferInternal()); +} + +ImageCache::TexRef::TexRef(ImageCache *cache) : + Ref(cache) +{ + buffer_type_ = kTexBuf; +} + +FramebufferObject *ImageCache::TexRef::buffer() +{ + return static_cast(BufferInternal()); +} diff --git a/rendering/imagecache.h b/rendering/imagecache.h new file mode 100644 index 000000000..07a4050e0 --- /dev/null +++ b/rendering/imagecache.h @@ -0,0 +1,91 @@ +#ifndef MEMORYCACHE_H +#define MEMORYCACHE_H + +#include +#include + +#include "framebufferobject.h" +#include "memorybuffer.h" + +class ImageCache +{ +public: + + enum BufferType { + kInvalidBuf, + kMemBuf, // RAM (CPU) buffer + kTexBuf // VRAM (GPU) buffer + }; + + class Ref { + public: + Ref(ImageCache* cache); + ~Ref(); + + void Relinquish(); + protected: + void* BufferInternal(); + + BufferType buffer_type_; + + int width_; + int height_; + private: + void Request(); + + ImageCache* cache_; + int buffer_; + }; + + class ImgRef : public Ref { + public: + ImgRef(ImageCache* cache); + + void SetSize(int w, int h); + + MemoryBuffer* buffer(); + + }; + + class TexRef : public Ref { + public: + TexRef(ImageCache* cache); + FramebufferObject* buffer(); + }; + + ImageCache(); + ~ImageCache(); + + void SetParameters(QOpenGLContext* ctx, int width, int height); + +private: + int RequestBuffer(Ref *r, const BufferType& type, int width, int height); + int RequestBufferInternal(Ref *r, const BufferType& type, int width, int height); + + void RelinquishBuffer(int index, const BufferType& type); + + void *Buffer(int index, const BufferType &type); + + bool OutOfMemory(); + + void Clear(); + + QList img_buffers_; + QList img_refs_; + QList img_access_times_; + QList img_relinquished_; + + QList tex_buffers_; + QList tex_refs_; + QList tex_access_times_; + QList tex_relinquished_; + + QOpenGLContext* ctx_; + + int width_; + int height_; + + QMutex buffer_array_mutex_; +}; + +#endif // MEMORYCACHE_H diff --git a/rendering/memorybuffer.cpp b/rendering/memorybuffer.cpp new file mode 100644 index 000000000..6e496372a --- /dev/null +++ b/rendering/memorybuffer.cpp @@ -0,0 +1,43 @@ +#include "memorybuffer.h" + +#include + +#include "decoders/pixelformatconverter.h" + +MemoryBuffer::MemoryBuffer() +{ +} + +void MemoryBuffer::Create(int width, int height, const olive::PixelFormat &format) +{ + width_ = width; + height_ = height; + format_ = format; + + data_.resize(olive::pix_fmt_conv->GetBufferSize(format, width, height)); +} + +const int &MemoryBuffer::width() const +{ + return width_; +} + +const int &MemoryBuffer::height() const +{ + return height_; +} + +const olive::PixelFormat &MemoryBuffer::format() const +{ + return format_; +} + +uint8_t *MemoryBuffer::data() +{ + return data_.data(); +} + +const uint8_t *MemoryBuffer::const_data() const +{ + return data_.constData(); +} diff --git a/rendering/memorybuffer.h b/rendering/memorybuffer.h new file mode 100644 index 000000000..423f60b98 --- /dev/null +++ b/rendering/memorybuffer.h @@ -0,0 +1,28 @@ +#ifndef MEMORYBUFFER_H +#define MEMORYBUFFER_H + +#include + +#include "pixelformats.h" + +class MemoryBuffer +{ +public: + MemoryBuffer(); + + void Create(int width, int height, const olive::PixelFormat& format); + + const int& width() const; + const int& height() const; + const olive::PixelFormat& format() const; + uint8_t* data(); + const uint8_t* const_data() const; + +private: + QVector data_; + int width_; + int height_; + olive::PixelFormat format_; +}; + +#endif // MEMORYBUFFER_H diff --git a/rendering/memorycache.cpp b/rendering/memorycache.cpp deleted file mode 100644 index 1f3c62c8c..000000000 --- a/rendering/memorycache.cpp +++ /dev/null @@ -1,182 +0,0 @@ -#include "memorycache.h" - -#include - -MemoryCache::MemoryCache() : - ctx_(nullptr), - width_(0), - height_(0) -{ - -} - -MemoryCache::~MemoryCache() -{ - buffer_array_mutex_.lock(); - - Clear(); - - buffer_array_mutex_.unlock(); -} - -void MemoryCache::SetParameters(QOpenGLContext *ctx, int width, int height) -{ - buffer_array_mutex_.lock(); - - ctx_ = ctx; - - if (ctx_ != nullptr) { - Q_ASSERT(width > 0 && height > 0); - - width_ = width; - height_ = height; - } - - Clear(); - - buffer_array_mutex_.unlock(); -} - -int MemoryCache::RequestBuffer(Reference* r) -{ - if (ctx_ == nullptr) { - qWarning() << "A memcache request was made with an invalid context"; - return -1; - } - - if (width_ == 0 || height_ == 0) { - qWarning() << "A memcache request was made with an invalid size [" << width_ << "," << height_ << "]"; - return -1; - } - - buffer_array_mutex_.lock(); - - int buffer = RequestBufferInternal(r); - - buffer_array_mutex_.unlock(); - - return buffer; -} - -int MemoryCache::RequestBufferInternal(MemoryCache::Reference *r) -{ - // Try to return a relinquished buffer - if (!relinquished_buffers_.isEmpty()) { - - int buf_index = relinquished_buffers_.takeFirst(); - - refs_.replace(buf_index, r); - - return buf_index; - } - - // If no buffer was available, we may have to create a new one - - // Check if we have enough memory (according to user-defined limits in the Config), if we don't, try to relinquish an - // old buffer and return that. - if (OutOfMemory()) { - - int least_recent_access = 0; - - // Loop through buffers for the oldest accessed buffer - for (int i=1;iRelinquish(); - refs_.replace(least_recent_access, r); - return least_recent_access; - } - - // Otherwise, just generate a new buffer - buffers_.append(FramebufferObject()); - refs_.append(r); - access_times_.append(time(nullptr)); - buffers_.last().Create(ctx_, width_, height_); - - return buffers_.size() - 1; -} - -void MemoryCache::RelinquishBuffer(int index) -{ - buffer_array_mutex_.lock(); - - refs_.replace(index, nullptr); - relinquished_buffers_.append(index); - - buffer_array_mutex_.unlock(); -} - -FramebufferObject* MemoryCache::Buffer(int index) -{ - access_times_.replace(index, time(nullptr)); - return &buffers_[index]; -} - -bool MemoryCache::OutOfMemory() -{ - // TODO actually check if we have any memory or not - return false; -} - -void MemoryCache::Clear() -{ - for (int i=0;iRelinquish(); - } - - buffers_.clear(); - refs_.clear(); - relinquished_buffers_.clear(); -} - -MemoryCache::Reference::Reference(MemoryCache *cache) : - cache_(cache), - buffer_(-1) -{ -} - -MemoryCache::Reference::~Reference() -{ - Relinquish(); -} - -FramebufferObject *MemoryCache::Reference::buffer() -{ - // If we don't have a buffer yet, request one - if (buffer_ == -1) { - Request(); - } - - // If we didn't receive one, return null - if (buffer_ == -1) { - return nullptr; - } - - // Otherwise, return the buffer we received - return &cache_->buffers_[buffer_]; -} - -void MemoryCache::Reference::Relinquish() -{ - if (buffer_ == -1) { - return; - } - - cache_->RelinquishBuffer(buffer_); - buffer_ = -1; -} - -void MemoryCache::Reference::Request() -{ - // Check if we already have a buffer, in which case we don't need to request a new one - if (buffer_ != -1) { - return; - } - - buffer_ = cache_->RequestBuffer(this); -} diff --git a/rendering/memorycache.h b/rendering/memorycache.h deleted file mode 100644 index b5f024582..000000000 --- a/rendering/memorycache.h +++ /dev/null @@ -1,58 +0,0 @@ -#ifndef MEMORYCACHE_H -#define MEMORYCACHE_H - -#include -#include - -#include "framebufferobject.h" - -class MemoryCache -{ -public: - class Reference { - public: - Reference(MemoryCache* cache); - ~Reference(); - - FramebufferObject* buffer(); - - void Relinquish(); - private: - void Request(); - - MemoryCache* cache_; - int buffer_; - }; - - MemoryCache(); - ~MemoryCache(); - - void SetParameters(QOpenGLContext* ctx, int width, int height); - -private: - int RequestBuffer(Reference *r); - int RequestBufferInternal(Reference *r); - - void RelinquishBuffer(int index); - - FramebufferObject *Buffer(int index); - - bool OutOfMemory(); - - void Clear(); - - QList buffers_; - QList refs_; - QList access_times_; - - QOpenGLContext* ctx_; - - QList relinquished_buffers_; - - int width_; - int height_; - - QMutex buffer_array_mutex_; -}; - -#endif // MEMORYCACHE_H diff --git a/timeline/sequence.cpp b/timeline/sequence.cpp index bb520372b..2c5a5049f 100644 --- a/timeline/sequence.cpp +++ b/timeline/sequence.cpp @@ -33,7 +33,8 @@ Sequence::Sequence() : using_workarea(false), workarea_in(0), workarea_out(0), - wrapper_sequence(false) + wrapper_sequence(false), + texture_io(nullptr) { AddTrack(olive::kTypeVideo); AddTrack(olive::kTypeAudio); @@ -197,6 +198,8 @@ QVector Sequence::GetTrackList(olive::TrackType type) GLuint Sequence::texture() { + if (texture_io == nullptr) return 0; + texture_io->ParentNode()->Process(0); return texture_io->GetValue().value(); } diff --git a/ui/viewerwidget.cpp b/ui/viewerwidget.cpp index 7c247385b..ecef98126 100644 --- a/ui/viewerwidget.cpp +++ b/ui/viewerwidget.cpp @@ -728,6 +728,8 @@ void ViewerWidget::paintGL() { QOpenGLFunctions* f = context()->functions(); + viewer->seq->SetGLContext(context()); + makeCurrent(); // clear to solid black @@ -739,9 +741,11 @@ void ViewerWidget::paintGL() { f->glViewport(0, 0, width(), height()); - f->glBindTexture(GL_TEXTURE_2D, viewer->seq->texture()); + GLuint tex = viewer->seq->texture(); - qDebug() << "drawing texture" << viewer->seq->texture(); + f->glBindTexture(GL_TEXTURE_2D, tex); + + qDebug() << "drawing texture" << tex; olive::rendering::Blit(pipeline_.get(), true, get_matrix());