finished prototype for float pix fmt converter

This commit is contained in:
itsmattkc
2019-05-30 21:50:18 +10:00
parent c8fe9c9fc7
commit 707ad3c264
28 changed files with 671 additions and 300 deletions
+7 -5
View File
@@ -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)
-4
View File
@@ -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_;
+4
View File
@@ -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();
+7
View File
@@ -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;
+10
View File
@@ -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) {
+10
View File
@@ -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();
+52 -20
View File
@@ -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;i<threads_.size();i++) {
threads_.at(i)->wait();
threads_.at(i)->WaitUntilComplete();
}
mutex_.unlock();
if (converted_buffer != nullptr) {
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<line_count_;i++) {
int in_line_start = in_start + input_linesize_[0]*i;
int in_line_end = in_line_start + width_*channels;
int out_line_start = out_start + width_*i;
int input_line_start = input_start + byte_linesize*i;
int input_line_end = width_*channels;
for (int j=in_line_start;j<in_line_end;j++) {
int output_line_start = output_start + width_*channels*i;
// Convert 8-bit integer to float
f = input_buffer_[0][in_line_start+j] / 255.0f;
for (int j=0;j<input_line_end;j++) {
// Place float into output buffer
// TODO only float support, no half float support yet
static_cast<float*>(output_buffer_)[out_line_start+j] = f;
// Convert 8-bit integer to float and store in output buffer
static_cast<float*>(output_buffer_)[output_line_start+j] = input_buffer_[0][input_line_start+j] / 255.0f;
}
}
+4
View File
@@ -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
+6
View File
@@ -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();
+2 -2
View File
@@ -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<olive::PixelFormat>(playback_bit_depth->currentIndex());
olive::config.export_bit_depth = static_cast<olive::PixelFormat>(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();
+2 -2
View File
@@ -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<olive::PixelFormat>(stream.text().toInt());
} else if (stream.name() == "ExportBitDepth") {
stream.readNext();
export_bit_depth = stream.text().toInt();
export_bit_depth = static_cast<olive::PixelFormat>(stream.text().toInt());
} else if (stream.name() == "DontUseProxiesOnExport") {
stream.readNext();
dont_use_proxies_on_export = (stream.text() == "1");
+3 -2
View File
@@ -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)
+7
View File
@@ -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) {
+15 -1
View File
@@ -22,12 +22,14 @@
#define OLIVEGLOBAL_H
#include <memory>
#include "undo/undo.h"
#include <QTimer>
#include <QFile>
#include <QTranslator>
#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
+4
View File
@@ -21,6 +21,7 @@
#include <QApplication>
#include <QMessageBox>
#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);
+9 -1
View File
@@ -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)
{
if (ctx_ != ctx) {
ctx_ = ctx;
memory_cache_.SetParameters(ctx, width_, height_);
}
}
+3 -3
View File
@@ -5,7 +5,7 @@
#include <QObject>
#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_;
+50 -8
View File
@@ -3,11 +3,16 @@
#include <QDebug>
#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<AVPixelFormat>(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<FFmpegVideoDecoder>();
media_ = f;
decoder_->set_stream(media_);
img_buffer_.SetSize(f->video_width, f->video_height);
}
NodeIO *NodeMedia::matrix_input()
+5 -4
View File
@@ -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_;
+1 -3
View File
@@ -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,
+296
View File
@@ -0,0 +1,296 @@
#include "imagecache.h"
#include <QDebug>
#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<int>& relinquished = (type == kMemBuf) ? img_relinquished_ : tex_relinquished_;
QList<Ref*>& refs = (type == kMemBuf) ? img_refs_ : tex_refs_;
QList<time_t>& 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;i<relinquished.size();i++) {
const MemoryBuffer& b = img_buffers_.at(relinquished.at(i));
if (b.width() == width && b.height() == height) {
// This buffer fits, we'll use it
int buffer_index = relinquished.takeAt(i);
refs.replace(buffer_index, r);
return buffer_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()) {
//
// TODO no support for a MemBuf's variable size
//
int least_recent_access = 0;
// Loop through buffers for the oldest accessed buffer
for (int i=1;i<access_times.size();i++) {
if (access_times.at(i) < access_times.at(least_recent_access)
&& refs.at(i) != nullptr) { // ensure this buffer has not been relinquished
least_recent_access = i;
}
}
// Relinquish this buffer
refs.at(least_recent_access)->Relinquish();
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;i<img_refs_.size();i++) {
img_refs_.at(i)->Relinquish();
}
for (int i=0;i<tex_refs_.size();i++) {
tex_refs_.at(i)->Relinquish();
}
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<MemoryBuffer*>(BufferInternal());
}
ImageCache::TexRef::TexRef(ImageCache *cache) :
Ref(cache)
{
buffer_type_ = kTexBuf;
}
FramebufferObject *ImageCache::TexRef::buffer()
{
return static_cast<FramebufferObject*>(BufferInternal());
}
+91
View File
@@ -0,0 +1,91 @@
#ifndef MEMORYCACHE_H
#define MEMORYCACHE_H
#include <QList>
#include <QMutex>
#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<MemoryBuffer> img_buffers_;
QList<Ref*> img_refs_;
QList<time_t> img_access_times_;
QList<int> img_relinquished_;
QList<FramebufferObject> tex_buffers_;
QList<Ref*> tex_refs_;
QList<time_t> tex_access_times_;
QList<int> tex_relinquished_;
QOpenGLContext* ctx_;
int width_;
int height_;
QMutex buffer_array_mutex_;
};
#endif // MEMORYCACHE_H
+43
View File
@@ -0,0 +1,43 @@
#include "memorybuffer.h"
#include <QDebug>
#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();
}
+28
View File
@@ -0,0 +1,28 @@
#ifndef MEMORYBUFFER_H
#define MEMORYBUFFER_H
#include <QVector>
#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<uint8_t> data_;
int width_;
int height_;
olive::PixelFormat format_;
};
#endif // MEMORYBUFFER_H
-182
View File
@@ -1,182 +0,0 @@
#include "memorycache.h"
#include <QDebug>
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;i<access_times_.size();i++) {
if (access_times_.at(i) < access_times_.at(least_recent_access)
&& refs_.at(i) != nullptr) { // ensure this buffer has not been relinquished
least_recent_access = i;
}
}
// Relinquish this buffer
refs_.at(least_recent_access)->Relinquish();
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;i<refs_.size();i++) {
refs_.at(i)->Relinquish();
}
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);
}
-58
View File
@@ -1,58 +0,0 @@
#ifndef MEMORYCACHE_H
#define MEMORYCACHE_H
#include <QList>
#include <QMutex>
#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<FramebufferObject> buffers_;
QList<Reference*> refs_;
QList<time_t> access_times_;
QOpenGLContext* ctx_;
QList<int> relinquished_buffers_;
int width_;
int height_;
QMutex buffer_array_mutex_;
};
#endif // MEMORYCACHE_H
+4 -1
View File
@@ -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<Track *> Sequence::GetTrackList(olive::TrackType type)
GLuint Sequence::texture()
{
if (texture_io == nullptr) return 0;
texture_io->ParentNode()->Process(0);
return texture_io->GetValue().value<GLuint>();
}
+6 -2
View File
@@ -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());