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