finished prototype for float pix fmt converter
This commit is contained in:
@@ -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,
|
||||
|
||||
@@ -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());
|
||||
}
|
||||
@@ -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
|
||||
@@ -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();
|
||||
}
|
||||
@@ -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
|
||||
@@ -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);
|
||||
}
|
||||
@@ -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
|
||||
Reference in New Issue
Block a user