media node upgrades to reference pixel format
This commit is contained in:
@@ -61,3 +61,15 @@ QString GetMediaIndexFilename(const QString &filename)
|
||||
{
|
||||
return QDir(GetMediaIndexLocation()).filePath(filename);
|
||||
}
|
||||
|
||||
QString GetMediaCacheLocation()
|
||||
{
|
||||
QDir local_appdata_dir(QStandardPaths::writableLocation(QStandardPaths::AppLocalDataLocation));
|
||||
|
||||
QDir media_cache_dir = local_appdata_dir.filePath("mediacache");
|
||||
|
||||
// Attempt to ensure this folder exists
|
||||
media_cache_dir.mkpath(".");
|
||||
|
||||
return media_cache_dir.absolutePath();
|
||||
}
|
||||
|
||||
@@ -29,4 +29,6 @@ QString GetMediaIndexLocation();
|
||||
|
||||
QString GetMediaIndexFilename(const QString& filename);
|
||||
|
||||
QString GetMediaCacheLocation();
|
||||
|
||||
#endif // FILEFUNCTIONS_H
|
||||
|
||||
+107
-28
@@ -34,8 +34,10 @@
|
||||
// End test code
|
||||
|
||||
MediaInput::MediaInput() :
|
||||
//ocio_shader_(nullptr),
|
||||
decoder_(nullptr)
|
||||
decoder_(nullptr),
|
||||
color_service_(nullptr),
|
||||
pipeline_(nullptr),
|
||||
ocio_texture_(0)
|
||||
{
|
||||
footage_input_ = new NodeInput("footage_in");
|
||||
footage_input_->add_data_input(NodeInput::kFootage);
|
||||
@@ -72,9 +74,20 @@ QString MediaInput::Description()
|
||||
|
||||
void MediaInput::Release()
|
||||
{
|
||||
texture_.Destroy();
|
||||
internal_tex_.Destroy();
|
||||
|
||||
decoder_ = nullptr;
|
||||
color_service_ = nullptr;
|
||||
pipeline_ = nullptr;
|
||||
|
||||
if (ocio_texture_ != 0) {
|
||||
ocio_ctx_->functions()->glDeleteTextures(1, &ocio_texture_);
|
||||
}
|
||||
}
|
||||
|
||||
NodeInput *MediaInput::matrix_input()
|
||||
{
|
||||
return matrix_input_;
|
||||
}
|
||||
|
||||
NodeOutput *MediaInput::texture_output()
|
||||
@@ -89,6 +102,8 @@ void MediaInput::SetFootage(Footage *f)
|
||||
|
||||
QVariant MediaInput::Value(NodeOutput *output, const rational &time)
|
||||
{
|
||||
bool alpha_is_associated = false;
|
||||
|
||||
if (output == texture_output_) {
|
||||
// Find the current Renderer instance
|
||||
RenderInstance* renderer = RendererProcessor::CurrentInstance();
|
||||
@@ -126,42 +141,106 @@ QVariant MediaInput::Value(NodeOutput *output, const rational &time)
|
||||
return 0;
|
||||
}
|
||||
|
||||
RenderTexturePtr texture = std::make_shared<RenderTexture>();
|
||||
if (color_service_ == nullptr) {
|
||||
// FIXME: Hardcoded values for texting
|
||||
color_service_ = std::make_shared<ColorService>("srgb", OCIO::ROLE_SCENE_LINEAR);
|
||||
}
|
||||
|
||||
texture->Create(renderer->context(),
|
||||
// OpenColorIO v1's color transforms can be done on GPU, which improves performance but reduces accuracy. When
|
||||
// online, we prefer accuracy over performance so we use the CPU path instead:
|
||||
// NOTE: OCIO v2 boasts 1:1 results with the CPU and GPU path so this won't be necessary forever
|
||||
if (renderer->mode() == olive::RenderMode::kOnline) {
|
||||
// Convert to 32F, which is required for OpenColorIO's color transformation
|
||||
frame = PixelService::ConvertPixelFormat(frame, olive::PIX_FMT_RGBA32F);
|
||||
|
||||
if (alpha_is_associated) {
|
||||
// FIXME: Unassociate alpha here if associated
|
||||
}
|
||||
|
||||
// Transform color to reference space
|
||||
color_service_->ConvertFrame(frame);
|
||||
|
||||
if (alpha_is_associated) {
|
||||
// FIXME: Reassociate alpha here
|
||||
} else {
|
||||
// FIXME: Associate alpha here
|
||||
}
|
||||
}
|
||||
|
||||
// We use an internal texture to bring the texture into GPU space before performing transformations
|
||||
|
||||
// Ensure the texture is the accurate to the frame
|
||||
if (internal_tex_.width() != frame->width()
|
||||
|| internal_tex_.height() != frame->height()
|
||||
|| internal_tex_.format() != frame->format()) {
|
||||
internal_tex_.Destroy();
|
||||
}
|
||||
|
||||
// Create or upload the new data to the texture
|
||||
if (!internal_tex_.IsCreated()) {
|
||||
internal_tex_.Create(renderer->context(),
|
||||
frame->width(),
|
||||
frame->height(),
|
||||
static_cast<olive::PixelFormat>(frame->format()),
|
||||
frame->data());
|
||||
} else {
|
||||
internal_tex_.Upload(frame->data());
|
||||
}
|
||||
|
||||
// Create new texture in reference space to send throughout the rest of the graph
|
||||
|
||||
RenderTexturePtr output_texture = std::make_shared<RenderTexture>();
|
||||
|
||||
output_texture->Create(renderer->context(),
|
||||
renderer->width(),
|
||||
renderer->height(),
|
||||
static_cast<olive::PixelFormat>(frame->format()),
|
||||
frame->data());
|
||||
renderer->format(),
|
||||
RenderTexture::kDoubleBuffer);
|
||||
|
||||
return QVariant::fromValue(texture);
|
||||
// Using the transformation matrix, blit our internal texture (in frame format) to our output texture (in
|
||||
// reference format)
|
||||
|
||||
/*renderer->buffer()->Upload(frame->data());
|
||||
if (renderer->mode() == olive::RenderMode::kOffline) {
|
||||
// For offline rendering, OCIO's GPU path is acceptable:
|
||||
// NOTE: OCIO v2 boasts 1:1 results with the CPU and GPU path so this won't be necessary forever
|
||||
|
||||
texture_output_->set_value(renderer->buffer()->texture());*/
|
||||
// Use an OCIO pipeline shader (which wraps in a default pipeline and will also handle alpha association)
|
||||
if (pipeline_ == nullptr) {
|
||||
/*pipeline_ = olive::ShaderGenerator::OCIOPipeline(renderer->context(),
|
||||
ocio_texture_, // FIXME: A raw GLuint texture, should wrap this up
|
||||
color_service_->GetProcessor(),
|
||||
alpha_is_associated);
|
||||
|
||||
// Convert the frame to the Renderer format
|
||||
// frame = PixelService::ConvertPixelFormat(frame, olive::PIX_FMT_RGBA16F);
|
||||
// Used for cleanup later
|
||||
ocio_ctx_ = renderer->context();*/
|
||||
|
||||
// Convert the frame to the Renderer color space
|
||||
//color_service_.ConvertFrame(frame);
|
||||
pipeline_ = olive::ShaderGenerator::DefaultPipeline();
|
||||
}
|
||||
} else if (pipeline_ == nullptr) {
|
||||
// In online, the color transformation was performed on the CPU (see above), so we only need to blit
|
||||
pipeline_ = olive::ShaderGenerator::DefaultPipeline();
|
||||
}
|
||||
|
||||
// Upload this frame to the GPU
|
||||
/*if (buffer_.IsCreated()) {
|
||||
buffer_.Upload(frame->data());
|
||||
} else {
|
||||
buffer_.Create(QOpenGLContext::currentContext(),
|
||||
static_cast<olive::PixelFormat>(frame->format()),
|
||||
frame->width(),
|
||||
frame->height(),
|
||||
frame->data());
|
||||
}*/
|
||||
// Draw onto the output texture using the renderer's framebuffer
|
||||
renderer->buffer()->Attach(output_texture);
|
||||
renderer->buffer()->Bind();
|
||||
|
||||
// Draw according to matrix
|
||||
// BLIT
|
||||
glClearColor(1.0, 0.0, 0.0, 1.0);
|
||||
glClear(GL_COLOR_BUFFER_BIT);
|
||||
|
||||
//texture_output_->set_value(tex_buf_.texture());
|
||||
// End test code
|
||||
// Draw with the internal texture
|
||||
internal_tex_.Bind();
|
||||
|
||||
// Use pipeline to blit using transformation matrix from input
|
||||
QMatrix4x4 m;
|
||||
olive::gl::Blit(pipeline_, false);
|
||||
|
||||
// Release everything
|
||||
internal_tex_.Release();
|
||||
renderer->buffer()->Detach();
|
||||
renderer->buffer()->Release();
|
||||
|
||||
return QVariant::fromValue(output_texture);
|
||||
}
|
||||
|
||||
return 0;
|
||||
|
||||
@@ -26,11 +26,8 @@
|
||||
#include "decoder/decoder.h"
|
||||
#include "node/node.h"
|
||||
#include "render/colorservice.h"
|
||||
|
||||
// FIXME: Test code only
|
||||
#include "render/rendertexture.h"
|
||||
#include "render/gl/shaderptr.h"
|
||||
// End test code
|
||||
#include "render/gl/shadergenerators.h"
|
||||
|
||||
/**
|
||||
* @brief A node that imports an image
|
||||
@@ -48,6 +45,8 @@ public:
|
||||
|
||||
virtual void Release() override;
|
||||
|
||||
NodeInput* matrix_input();
|
||||
|
||||
NodeOutput* texture_output();
|
||||
|
||||
void SetFootage(Footage* f);
|
||||
@@ -62,11 +61,16 @@ private:
|
||||
|
||||
NodeOutput* texture_output_;
|
||||
|
||||
RenderTexture texture_;
|
||||
RenderTexture internal_tex_;
|
||||
|
||||
DecoderPtr decoder_;
|
||||
|
||||
ColorService color_service_;
|
||||
ColorServicePtr color_service_;
|
||||
|
||||
ShaderPtr pipeline_;
|
||||
|
||||
QOpenGLContext* ocio_ctx_;
|
||||
GLuint ocio_texture_;
|
||||
|
||||
};
|
||||
|
||||
|
||||
@@ -108,6 +108,8 @@ void ViewerOutput::ViewerTimeChanged(const rational &t)
|
||||
// Send the texture to the Viewer
|
||||
if (current_texture != nullptr) {
|
||||
attached_viewer_->SetTexture(current_texture->texture());
|
||||
} else {
|
||||
attached_viewer_->SetTexture(0);
|
||||
}
|
||||
|
||||
current_time_ = t;
|
||||
|
||||
@@ -18,7 +18,11 @@ set(OLIVE_SOURCES
|
||||
${OLIVE_SOURCES}
|
||||
node/processor/renderer/renderer.h
|
||||
node/processor/renderer/renderer.cpp
|
||||
node/processor/renderer/rendererthread.h
|
||||
node/processor/renderer/rendererthread.cpp
|
||||
node/processor/renderer/rendererthreadbase.h
|
||||
node/processor/renderer/rendererthreadbase.cpp
|
||||
node/processor/renderer/rendererdownloadthread.h
|
||||
node/processor/renderer/rendererdownloadthread.cpp
|
||||
node/processor/renderer/rendererprocessthread.h
|
||||
node/processor/renderer/rendererprocessthread.cpp
|
||||
PARENT_SCOPE
|
||||
)
|
||||
|
||||
@@ -24,9 +24,12 @@
|
||||
#include <QCryptographicHash>
|
||||
#include <QDateTime>
|
||||
#include <QDebug>
|
||||
#include <QDir>
|
||||
#include <QImage>
|
||||
#include <QtMath>
|
||||
|
||||
#include "common/filefunctions.h"
|
||||
|
||||
RendererProcessor::RendererProcessor() :
|
||||
started_(false),
|
||||
width_(0),
|
||||
@@ -70,6 +73,7 @@ void RendererProcessor::SetCacheName(const QString &s)
|
||||
GenerateCacheIDInternal();
|
||||
}
|
||||
|
||||
#include <QFileInfo>
|
||||
QVariant RendererProcessor::Value(NodeOutput* output, const rational& time)
|
||||
{
|
||||
if (output == texture_output_) {
|
||||
@@ -89,55 +93,25 @@ QVariant RendererProcessor::Value(NodeOutput* output, const rational& time)
|
||||
}
|
||||
|
||||
// FIXME: Test code only
|
||||
return texture_input_->get_value(time);
|
||||
QString fn = CachePathName(time);
|
||||
if (QFileInfo::exists(fn)) {
|
||||
QFile cache_img(fn);
|
||||
|
||||
if (cache_img.open(QFile::ReadOnly)) {
|
||||
|
||||
QByteArray bytes = cache_img.readAll();
|
||||
|
||||
master_texture_->Upload(bytes.constData());
|
||||
|
||||
cache_img.close();
|
||||
|
||||
return QVariant::fromValue(master_texture_);
|
||||
}
|
||||
}
|
||||
// End test code
|
||||
}
|
||||
|
||||
return 0;
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
// This Renderer node relies on a disk cache so this Process() function should be quite fast. Either it returns the
|
||||
// cached frame or it returns nothing.
|
||||
|
||||
// Perhaps it should lookahead to load textures into VRAM in advance?
|
||||
|
||||
// Should it cache the final result or the result in an 8-bit image or a 16-bit intermediate image?
|
||||
|
||||
/*qDebug() << QString("Requesting %1/%2").arg(QString::number(time.numerator()), QString::number(time.denominator()));
|
||||
|
||||
if (cache_map_.contains(time)) {
|
||||
qDebug() << " We have this frame!";
|
||||
} else {
|
||||
qDebug() << " No frame at this address";
|
||||
cache_map_.insert(time, true);
|
||||
}*/
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
// FIXME: Test code only
|
||||
//GLuint tex = texture_input_->get_value(time).value<GLuint>();
|
||||
|
||||
//glReadPixels()
|
||||
|
||||
//texture_output_->set_value(tex);
|
||||
// End test code
|
||||
|
||||
/*
|
||||
// Ensure we have started
|
||||
if (!started_) {
|
||||
Start();
|
||||
|
||||
if (!started_) {
|
||||
qWarning() << tr("An error occurred starting the Renderer node");
|
||||
return;
|
||||
}
|
||||
}
|
||||
*/
|
||||
}
|
||||
|
||||
void RendererProcessor::Release()
|
||||
@@ -152,9 +126,13 @@ void RendererProcessor::InvalidateCache(const rational &start_range, const ratio
|
||||
<< "and"
|
||||
<< end_range.toDouble();
|
||||
|
||||
// FIXME: Snap start_range to timebase
|
||||
// Snap start_range to timebase
|
||||
double start_range_dbl = start_range.toDouble();
|
||||
double start_range_numf = start_range_dbl * static_cast<double>(timebase_.denominator());
|
||||
int64_t start_range_numround = qFloor(start_range_numf/static_cast<double>(timebase_.numerator())) * timebase_.numerator();
|
||||
rational true_start_range(start_range_numround, timebase_.denominator());
|
||||
|
||||
for (rational r=start_range;r<=end_range;r+=timebase_) {
|
||||
for (rational r=true_start_range;r<=end_range;r+=timebase_) {
|
||||
if (!cache_queue_.contains(r)) {
|
||||
cache_queue_.append(r);
|
||||
}
|
||||
@@ -196,10 +174,12 @@ void RendererProcessor::Start()
|
||||
return;
|
||||
}
|
||||
|
||||
QOpenGLContext* ctx = QOpenGLContext::currentContext();
|
||||
|
||||
threads_.resize(QThread::idealThreadCount());
|
||||
|
||||
for (int i=0;i<threads_.size();i++) {
|
||||
threads_[i] = std::make_shared<RendererThread>(QOpenGLContext::currentContext(), width_, height_, format_, mode_);
|
||||
threads_[i] = std::make_shared<RendererProcessThread>(ctx, width_, height_, format_, mode_);
|
||||
threads_[i]->StartThread(QThread::HighPriority);
|
||||
|
||||
// Ensure this connection is "Queued" so that it always runs in this object's threaded rather than any of the
|
||||
@@ -208,6 +188,14 @@ void RendererProcessor::Start()
|
||||
connect(threads_[i].get(), SIGNAL(RequestSibling(NodeDependency)), this, SLOT(ThreadRequestSibling(NodeDependency)), Qt::QueuedConnection);
|
||||
}
|
||||
|
||||
// Create download thread
|
||||
download_thread_ = std::make_shared<RendererDownloadThread>(ctx, width_, height_, format_, mode_);
|
||||
download_thread_->StartThread(QThread::HighPriority);
|
||||
|
||||
// Create master texture (the one sent to the viewer)
|
||||
master_texture_ = std::make_shared<RenderTexture>();
|
||||
master_texture_->Create(ctx, width_, height_, format_);
|
||||
|
||||
started_ = true;
|
||||
}
|
||||
|
||||
@@ -219,11 +207,15 @@ void RendererProcessor::Stop()
|
||||
|
||||
started_ = false;
|
||||
|
||||
for (int i=0;i<threads_.size();i++) {
|
||||
threads_[i]->Cancel();
|
||||
foreach (RendererProcessThreadPtr process_thread, threads_) {
|
||||
process_thread->Cancel();
|
||||
}
|
||||
|
||||
threads_.clear();
|
||||
|
||||
download_thread_->Cancel();
|
||||
download_thread_ = nullptr;
|
||||
|
||||
master_texture_ = nullptr;
|
||||
}
|
||||
|
||||
void RendererProcessor::GenerateCacheIDInternal()
|
||||
@@ -253,20 +245,26 @@ void RendererProcessor::CacheNext()
|
||||
// Make sure cache has started
|
||||
Start();
|
||||
|
||||
rational time_to_cache = cache_queue_.takeFirst();
|
||||
cache_frame_ = cache_queue_.takeFirst();
|
||||
|
||||
qDebug() << "Caching" << time_to_cache.toDouble();
|
||||
qDebug() << "[RendererProcessor] Caching" << cache_frame_.toDouble();
|
||||
|
||||
// Run this probe in another thread
|
||||
master_thread_ = threads_.at(0).get();
|
||||
master_thread_->Queue(NodeDependency(texture_input_->get_connected_output(), time_to_cache), true);
|
||||
master_thread_->Queue(NodeDependency(texture_input_->get_connected_output(), cache_frame_), true);
|
||||
|
||||
caching_ = true;
|
||||
}
|
||||
|
||||
// FIXME: Test code only
|
||||
#include "node/output/viewer/viewer.h"
|
||||
// End test code
|
||||
QString RendererProcessor::CachePathName(const rational &time)
|
||||
{
|
||||
QDir this_cache_dir = QDir(GetMediaCacheLocation()).filePath(cache_id_);
|
||||
this_cache_dir.mkpath(".");
|
||||
|
||||
QString filename = QString("%1.%2.jpg").arg(QString::number(time.numerator()), QString::number(time.denominator()));
|
||||
|
||||
return this_cache_dir.filePath(filename);
|
||||
}
|
||||
|
||||
void RendererProcessor::ThreadCallback()
|
||||
{
|
||||
@@ -275,6 +273,15 @@ void RendererProcessor::ThreadCallback()
|
||||
caching_ = false;
|
||||
|
||||
// FIXME: Save the texture results here
|
||||
RenderTexturePtr texture = texture_input_->get_value(cache_frame_).value<RenderTexturePtr>();
|
||||
|
||||
|
||||
QString fn = CachePathName(cache_frame_);
|
||||
if (texture == nullptr && QFileInfo::exists(fn)) {
|
||||
QFile(fn).remove();
|
||||
} else {
|
||||
download_thread_->Queue(texture, fn);
|
||||
}
|
||||
|
||||
CacheNext();
|
||||
}
|
||||
@@ -290,14 +297,14 @@ void RendererProcessor::ThreadRequestSibling(NodeDependency dep)
|
||||
}
|
||||
}
|
||||
|
||||
RendererThread* RendererProcessor::CurrentThread()
|
||||
RendererThreadBase* RendererProcessor::CurrentThread()
|
||||
{
|
||||
return dynamic_cast<RendererThread*>(QThread::currentThread());
|
||||
return dynamic_cast<RendererThreadBase*>(QThread::currentThread());
|
||||
}
|
||||
|
||||
RenderInstance *RendererProcessor::CurrentInstance()
|
||||
{
|
||||
RendererThread* thread = CurrentThread();
|
||||
RendererThreadBase* thread = CurrentThread();
|
||||
|
||||
if (thread != nullptr) {
|
||||
return thread->render_instance();
|
||||
|
||||
@@ -26,7 +26,8 @@
|
||||
#include "node/node.h"
|
||||
#include "render/pixelformat.h"
|
||||
#include "render/rendermodes.h"
|
||||
#include "rendererthread.h"
|
||||
#include "rendererdownloadthread.h"
|
||||
#include "rendererprocessthread.h"
|
||||
|
||||
/**
|
||||
* @brief A multithreaded OpenGL based renderer for node systems
|
||||
@@ -85,7 +86,7 @@ public:
|
||||
* This function attempts a dynamic_cast on QThread::currentThread() to RendererThread, which will return nullptr if
|
||||
* the cast fails (e.g. if this function is called from the main thread rather than a RendererThread).
|
||||
*/
|
||||
static RendererThread* CurrentThread();
|
||||
static RendererThreadBase* CurrentThread();
|
||||
|
||||
static RenderInstance* CurrentInstance();
|
||||
|
||||
@@ -120,9 +121,14 @@ private:
|
||||
void CacheNext();
|
||||
|
||||
/**
|
||||
* @brief Internal list of RenderThreads
|
||||
* @brief Return the path of the cached image at this time
|
||||
*/
|
||||
QVector<RendererThreadPtr> threads_;
|
||||
QString CachePathName(const rational& time);
|
||||
|
||||
/**
|
||||
* @brief Internal list of RenderProcessThreads
|
||||
*/
|
||||
QVector<RendererProcessThreadPtr> threads_;
|
||||
|
||||
/**
|
||||
* @brief Internal variable that contains whether the Renderer has started or not
|
||||
@@ -148,8 +154,14 @@ private:
|
||||
QString cache_name_;
|
||||
qint64 cache_time_;
|
||||
QString cache_id_;
|
||||
|
||||
bool caching_;
|
||||
RendererThread* master_thread_;
|
||||
RendererProcessThread* master_thread_;
|
||||
rational cache_frame_;
|
||||
|
||||
RendererDownloadThreadPtr download_thread_;
|
||||
|
||||
RenderTexturePtr master_texture_;
|
||||
|
||||
private slots:
|
||||
void ThreadCallback();
|
||||
|
||||
@@ -0,0 +1,105 @@
|
||||
#include "rendererdownloadthread.h"
|
||||
|
||||
#include <QFile>
|
||||
|
||||
#include "render/pixelservice.h"
|
||||
|
||||
RendererDownloadThread::RendererDownloadThread(QOpenGLContext *share_ctx,
|
||||
const int &width,
|
||||
const int &height,
|
||||
const olive::PixelFormat &format,
|
||||
const olive::RenderMode &mode) :
|
||||
RendererThreadBase(share_ctx, width, height, format, mode)
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
void RendererDownloadThread::Queue(RenderTexturePtr texture, const QString& fn)
|
||||
{
|
||||
texture_queue_lock_.lock();
|
||||
|
||||
texture_queue_.append(texture);
|
||||
download_filenames_.append(fn);
|
||||
|
||||
wait_cond_.wakeAll();
|
||||
|
||||
texture_queue_lock_.unlock();
|
||||
}
|
||||
|
||||
void RendererDownloadThread::ProcessLoop()
|
||||
{
|
||||
QOpenGLFunctions* f = render_instance()->context()->functions();
|
||||
QOpenGLExtraFunctions* xf = render_instance()->context()->extraFunctions();
|
||||
|
||||
f->glGenFramebuffers(1, &read_buffer_);
|
||||
|
||||
RenderTexturePtr working_texture;
|
||||
QString working_filename;
|
||||
|
||||
int buffer_size = PixelService::GetBufferSize(render_instance()->format(),
|
||||
render_instance()->width(),
|
||||
render_instance()->height());
|
||||
|
||||
uchar* data_buffer = new uchar[buffer_size];
|
||||
|
||||
while (!Cancelled()) {
|
||||
// Check queue for textures to download (use mutex to prevent collisions)
|
||||
texture_queue_lock_.lock();
|
||||
|
||||
do {
|
||||
if (texture_queue_.isEmpty()) {
|
||||
working_texture = nullptr;
|
||||
} else {
|
||||
working_texture = texture_queue_.takeFirst();
|
||||
working_filename = download_filenames_.takeFirst();
|
||||
}
|
||||
|
||||
if (working_texture == nullptr) {
|
||||
// Main waiting condition
|
||||
wait_cond_.wait(&texture_queue_lock_);
|
||||
}
|
||||
} while (working_texture == nullptr);
|
||||
|
||||
texture_queue_lock_.unlock();
|
||||
|
||||
// Download the texture
|
||||
f->glBindFramebuffer(GL_READ_FRAMEBUFFER, read_buffer_);
|
||||
|
||||
xf->glFramebufferTexture2D(GL_READ_FRAMEBUFFER,
|
||||
GL_COLOR_ATTACHMENT0,
|
||||
GL_TEXTURE_2D,
|
||||
working_texture->texture(),
|
||||
0);
|
||||
|
||||
PixelFormatInfo format_info = PixelService::GetPixelFormatInfo(working_texture->format());
|
||||
|
||||
f->glReadPixels(0,
|
||||
0,
|
||||
working_texture->width(),
|
||||
working_texture->height(),
|
||||
format_info.pixel_format,
|
||||
format_info.pixel_type,
|
||||
data_buffer);
|
||||
|
||||
render_instance()->context()->extraFunctions()->glFramebufferTexture2D(
|
||||
GL_READ_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D, 0, 0
|
||||
);
|
||||
|
||||
f->glBindFramebuffer(GL_READ_FRAMEBUFFER, 0);
|
||||
|
||||
QFile data_dump(working_filename);
|
||||
|
||||
if (data_dump.open(QFile::WriteOnly)) {
|
||||
data_dump.write(reinterpret_cast<char*>(data_buffer), buffer_size);
|
||||
|
||||
data_dump.close();
|
||||
}
|
||||
|
||||
qDebug() << "Saved" << working_filename;
|
||||
|
||||
}
|
||||
|
||||
delete [] data_buffer;
|
||||
|
||||
f->glDeleteFramebuffers(1, &read_buffer_);
|
||||
}
|
||||
@@ -0,0 +1,34 @@
|
||||
#ifndef RENDERERDOWNLOADTHREAD_H
|
||||
#define RENDERERDOWNLOADTHREAD_H
|
||||
|
||||
#include "rendererthreadbase.h"
|
||||
|
||||
class RendererDownloadThread : public RendererThreadBase
|
||||
{
|
||||
Q_OBJECT
|
||||
public:
|
||||
RendererDownloadThread(QOpenGLContext* share_ctx,
|
||||
const int& width,
|
||||
const int& height,
|
||||
const olive::PixelFormat& format,
|
||||
const olive::RenderMode& mode);
|
||||
|
||||
void Queue(RenderTexturePtr texture, const QString &fn);
|
||||
|
||||
protected:
|
||||
virtual void ProcessLoop() override;
|
||||
|
||||
private:
|
||||
GLuint read_buffer_;
|
||||
|
||||
QVector<RenderTexturePtr> texture_queue_;
|
||||
|
||||
QVector<QString> download_filenames_;
|
||||
|
||||
QMutex texture_queue_lock_;
|
||||
|
||||
};
|
||||
|
||||
using RendererDownloadThreadPtr = std::shared_ptr<RendererDownloadThread>;
|
||||
|
||||
#endif // RENDERERDOWNLOADTHREAD_H
|
||||
@@ -0,0 +1,88 @@
|
||||
/***
|
||||
|
||||
Olive - Non-Linear Video Editor
|
||||
Copyright (C) 2019 Olive Team
|
||||
|
||||
This program is free software: you can redistribute it and/or modify
|
||||
it under the terms of the GNU General Public License as published by
|
||||
the Free Software Foundation, either version 3 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
This program is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU General Public License
|
||||
along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
***/
|
||||
|
||||
#include "rendererprocessthread.h"
|
||||
|
||||
RendererProcessThread::RendererProcessThread(QOpenGLContext *share_ctx,
|
||||
const int &width,
|
||||
const int &height,
|
||||
const olive::PixelFormat &format,
|
||||
const olive::RenderMode &mode) :
|
||||
RendererThreadBase(share_ctx, width, height, format, mode)
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
bool RendererProcessThread::Queue(const NodeDependency& dep, bool wait)
|
||||
{
|
||||
if (wait) {
|
||||
// Wait for thread to be available
|
||||
mutex_.lock();
|
||||
} else if (!mutex_.tryLock()) {
|
||||
return false;
|
||||
}
|
||||
|
||||
// We can now change params without the other thread using them
|
||||
path_ = dep;
|
||||
|
||||
// Prepare to wait for thread to respond
|
||||
caller_mutex_.lock();
|
||||
|
||||
// Wake up our main thread
|
||||
wait_cond_.wakeAll();
|
||||
mutex_.unlock();
|
||||
|
||||
// Wait for thread to start before returning
|
||||
wait_cond_.wait(&caller_mutex_);
|
||||
caller_mutex_.unlock();
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
void RendererProcessThread::ProcessLoop()
|
||||
{
|
||||
while (!Cancelled()) {
|
||||
// Main waiting condition
|
||||
wait_cond_.wait(&mutex_);
|
||||
|
||||
// Wake up main thread
|
||||
caller_mutex_.lock();
|
||||
wait_cond_.wakeAll();
|
||||
caller_mutex_.unlock();
|
||||
|
||||
// Process the Node
|
||||
NodeOutput* output_to_process = path_.node();
|
||||
Node* node_to_process = output_to_process->parent();
|
||||
|
||||
QList<NodeDependency> deps = node_to_process->RunDependencies(output_to_process, path_.time());
|
||||
|
||||
// Ask for other threads to run these deps while we're here
|
||||
if (!deps.isEmpty()) {
|
||||
for (int i=1;i<deps.size();i++) {
|
||||
emit RequestSibling(deps.at(i));
|
||||
}
|
||||
}
|
||||
|
||||
// Get the requested value
|
||||
output_to_process->get_value(path_.time());
|
||||
|
||||
emit FinishedPath();
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,55 @@
|
||||
/***
|
||||
|
||||
Olive - Non-Linear Video Editor
|
||||
Copyright (C) 2019 Olive Team
|
||||
|
||||
This program is free software: you can redistribute it and/or modify
|
||||
it under the terms of the GNU General Public License as published by
|
||||
the Free Software Foundation, either version 3 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
This program is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU General Public License
|
||||
along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
***/
|
||||
|
||||
#ifndef RENDERERPROCESSTHREAD_H
|
||||
#define RENDERERPROCESSTHREAD_H
|
||||
|
||||
#include "rendererthreadbase.h"
|
||||
|
||||
class RendererProcessThread : public RendererThreadBase
|
||||
{
|
||||
Q_OBJECT
|
||||
public:
|
||||
RendererProcessThread(QOpenGLContext* share_ctx,
|
||||
const int& width,
|
||||
const int& height,
|
||||
const olive::PixelFormat& format,
|
||||
const olive::RenderMode& mode);
|
||||
|
||||
bool Queue(const NodeDependency &dep, bool wait);
|
||||
|
||||
protected:
|
||||
virtual void ProcessLoop() override;
|
||||
|
||||
signals:
|
||||
void RequestSibling(NodeDependency dep);
|
||||
|
||||
void FinishedPath();
|
||||
|
||||
private:
|
||||
NodeDependency path_;
|
||||
|
||||
rational time_;
|
||||
|
||||
};
|
||||
|
||||
using RendererProcessThreadPtr = std::shared_ptr<RendererProcessThread>;
|
||||
|
||||
#endif // RENDERERPROCESSTHREAD_H
|
||||
+12
-58
@@ -18,11 +18,11 @@
|
||||
|
||||
***/
|
||||
|
||||
#include "rendererthread.h"
|
||||
#include "rendererthreadbase.h"
|
||||
|
||||
#include <QDebug>
|
||||
|
||||
RendererThread::RendererThread(QOpenGLContext *share_ctx, const int &width, const int &height, const olive::PixelFormat &format, const olive::RenderMode &mode) :
|
||||
RendererThreadBase::RendererThreadBase(QOpenGLContext *share_ctx, const int &width, const int &height, const olive::PixelFormat &format, const olive::RenderMode &mode) :
|
||||
share_ctx_(share_ctx),
|
||||
cancelled_(false),
|
||||
width_(width),
|
||||
@@ -33,33 +33,7 @@ RendererThread::RendererThread(QOpenGLContext *share_ctx, const int &width, cons
|
||||
{
|
||||
}
|
||||
|
||||
bool RendererThread::Queue(const NodeDependency& dep, bool wait)
|
||||
{
|
||||
if (wait) {
|
||||
// Wait for thread to be available
|
||||
mutex_.lock();
|
||||
} else if (!mutex_.tryLock()) {
|
||||
return false;
|
||||
}
|
||||
|
||||
// We can now change params without the other thread using them
|
||||
path_ = dep;
|
||||
|
||||
// Prepare to wait for thread to respond
|
||||
caller_mutex_.lock();
|
||||
|
||||
// Wake up our main thread
|
||||
wait_cond_.wakeAll();
|
||||
mutex_.unlock();
|
||||
|
||||
// Wait for thread to start before returning
|
||||
wait_cond_.wait(&caller_mutex_);
|
||||
caller_mutex_.unlock();
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
void RendererThread::Cancel()
|
||||
void RendererThreadBase::Cancel()
|
||||
{
|
||||
// Escape main loop
|
||||
cancelled_ = true;
|
||||
@@ -68,12 +42,12 @@ void RendererThread::Cancel()
|
||||
wait();
|
||||
}
|
||||
|
||||
RenderInstance *RendererThread::render_instance()
|
||||
RenderInstance *RendererThreadBase::render_instance()
|
||||
{
|
||||
return render_instance_;
|
||||
}
|
||||
|
||||
void RendererThread::run()
|
||||
void RendererThreadBase::run()
|
||||
{
|
||||
// Lock mutex for main loop
|
||||
mutex_.lock();
|
||||
@@ -92,33 +66,8 @@ void RendererThread::run()
|
||||
if (instance.Start()) {
|
||||
|
||||
// Main loop (use Cancel() to exit it)
|
||||
while (!cancelled_) {
|
||||
// Main waiting condition
|
||||
wait_cond_.wait(&mutex_);
|
||||
ProcessLoop();
|
||||
|
||||
// Wake up main thread
|
||||
caller_mutex_.lock();
|
||||
wait_cond_.wakeAll();
|
||||
caller_mutex_.unlock();
|
||||
|
||||
// Process the Node
|
||||
NodeOutput* output_to_process = path_.node();
|
||||
Node* node_to_process = output_to_process->parent();
|
||||
|
||||
QList<NodeDependency> deps = node_to_process->RunDependencies(output_to_process, path_.time());
|
||||
|
||||
// Ask for other threads to run these deps while we're here
|
||||
if (!deps.isEmpty()) {
|
||||
for (int i=1;i<deps.size();i++) {
|
||||
emit RequestSibling(deps.at(i));
|
||||
}
|
||||
}
|
||||
|
||||
// Get the requested value
|
||||
output_to_process->get_value(path_.time());
|
||||
|
||||
emit FinishedPath();
|
||||
}
|
||||
}
|
||||
|
||||
// Free all resources
|
||||
@@ -129,7 +78,12 @@ void RendererThread::run()
|
||||
mutex_.unlock();
|
||||
}
|
||||
|
||||
void RendererThread::StartThread(QThread::Priority priority)
|
||||
bool RendererThreadBase::Cancelled()
|
||||
{
|
||||
return cancelled_;
|
||||
}
|
||||
|
||||
void RendererThreadBase::StartThread(QThread::Priority priority)
|
||||
{
|
||||
caller_mutex_.lock();
|
||||
|
||||
+13
-19
@@ -29,33 +29,28 @@
|
||||
#include "node/node.h"
|
||||
#include "render/renderinstance.h"
|
||||
|
||||
class RendererThread : public QThread
|
||||
class RendererThreadBase : public QThread
|
||||
{
|
||||
Q_OBJECT
|
||||
public:
|
||||
RendererThread(QOpenGLContext* share_ctx,
|
||||
const int& width,
|
||||
const int& height,
|
||||
const olive::PixelFormat& format,
|
||||
const olive::RenderMode& mode);
|
||||
|
||||
bool Queue(const NodeDependency &dep, bool wait);
|
||||
RendererThreadBase(QOpenGLContext* share_ctx,
|
||||
const int& width,
|
||||
const int& height,
|
||||
const olive::PixelFormat& format,
|
||||
const olive::RenderMode& mode);
|
||||
|
||||
void Cancel();
|
||||
|
||||
RenderInstance* render_instance();
|
||||
|
||||
virtual void run() override;
|
||||
|
||||
void StartThread(Priority priority = InheritPriority);
|
||||
|
||||
signals:
|
||||
void RequestSibling(NodeDependency dep);
|
||||
virtual void run() override;
|
||||
|
||||
void FinishedPath();
|
||||
protected:
|
||||
virtual void ProcessLoop() = 0;
|
||||
|
||||
private:
|
||||
QOpenGLContext* share_ctx_;
|
||||
bool Cancelled();
|
||||
|
||||
QWaitCondition wait_cond_;
|
||||
|
||||
@@ -63,9 +58,8 @@ private:
|
||||
|
||||
QMutex caller_mutex_;
|
||||
|
||||
NodeDependency path_;
|
||||
|
||||
rational time_;
|
||||
private:
|
||||
QOpenGLContext* share_ctx_;
|
||||
|
||||
bool cancelled_;
|
||||
|
||||
@@ -81,6 +75,6 @@ private:
|
||||
|
||||
};
|
||||
|
||||
using RendererThreadPtr = std::shared_ptr<RendererThread>;
|
||||
using RendererThreadPtr = std::shared_ptr<RendererThreadBase>;
|
||||
|
||||
#endif // RENDERTHREAD_H
|
||||
@@ -2,13 +2,13 @@
|
||||
|
||||
const int kRGBAChannels = 4;
|
||||
|
||||
ColorService::ColorService()
|
||||
ColorService::ColorService(const char* source_space, const char* dest_space)
|
||||
{
|
||||
// FIXME: Hardcoded values for testing purposes
|
||||
OCIO::ConstConfigRcPtr config = OCIO::Config::CreateFromFile("/run/media/matt/Home/OpenColorIO/ocio.configs.0.7v4/nuke-default/config.ocio");
|
||||
|
||||
processor = config->getProcessor("srgb",
|
||||
OCIO::ROLE_SCENE_LINEAR);
|
||||
processor = config->getProcessor(source_space,
|
||||
dest_space);
|
||||
}
|
||||
|
||||
void ColorService::ConvertFrame(FramePtr f)
|
||||
@@ -17,3 +17,8 @@ void ColorService::ConvertFrame(FramePtr f)
|
||||
|
||||
processor->apply(img);
|
||||
}
|
||||
|
||||
OpenColorIO::v1::ConstProcessorRcPtr ColorService::GetProcessor()
|
||||
{
|
||||
return processor;
|
||||
}
|
||||
|
||||
@@ -6,14 +6,17 @@
|
||||
namespace OCIO = OCIO_NAMESPACE::v1;
|
||||
|
||||
#include "decoder/frame.h"
|
||||
#include "render/gl/shadergenerators.h"
|
||||
|
||||
class ColorService
|
||||
{
|
||||
public:
|
||||
ColorService();
|
||||
ColorService(const char *source_space, const char *dest_space);
|
||||
|
||||
void ConvertFrame(FramePtr f);
|
||||
|
||||
OCIO::ConstProcessorRcPtr GetProcessor();
|
||||
|
||||
private:
|
||||
OCIO::ConstProcessorRcPtr processor;
|
||||
};
|
||||
|
||||
@@ -41,9 +41,9 @@ public:
|
||||
static ShaderPtr DefaultPipeline(const QString &function_name = QString(), const QString &shader_code = QString());
|
||||
|
||||
static ShaderPtr OCIOPipeline(QOpenGLContext *ctx,
|
||||
GLuint &lut_texture,
|
||||
OCIO::ConstProcessorRcPtr processor,
|
||||
bool alpha_is_associated);
|
||||
GLuint &lut_texture,
|
||||
OCIO::ConstProcessorRcPtr processor,
|
||||
bool alpha_is_associated);
|
||||
|
||||
static QString AlphaDisassociateFunction(const QString& function_name);
|
||||
static QString AlphaReassociateFunction(const QString& function_name);
|
||||
|
||||
@@ -127,16 +127,10 @@ void RenderFramebuffer::AttachInternal(GLuint tex)
|
||||
// bind framebuffer for attaching
|
||||
f->glBindFramebuffer(GL_DRAW_FRAMEBUFFER, buffer_);
|
||||
|
||||
// bind texture
|
||||
f->glBindTexture(GL_TEXTURE_2D, tex);
|
||||
|
||||
context_->extraFunctions()->glFramebufferTexture2D(
|
||||
GL_DRAW_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D, tex, 0
|
||||
);
|
||||
|
||||
// release texture
|
||||
f->glBindTexture(GL_TEXTURE_2D, 0);
|
||||
|
||||
// release framebuffer
|
||||
f->glBindFramebuffer(GL_DRAW_FRAMEBUFFER, 0);
|
||||
}
|
||||
|
||||
@@ -66,6 +66,8 @@ bool RenderInstance::Start()
|
||||
|
||||
buffer_.Create(&ctx_);
|
||||
|
||||
ctx_.functions()->glViewport(0, 0, width_, height_);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
@@ -20,6 +20,7 @@
|
||||
|
||||
#include "rendertexture.h"
|
||||
|
||||
#include <QDateTime>
|
||||
#include <QDebug>
|
||||
|
||||
#include "render/pixelservice.h"
|
||||
@@ -139,7 +140,7 @@ void RenderTexture::SwapFrontAndBack()
|
||||
back_texture_ = temp;
|
||||
}
|
||||
|
||||
void RenderTexture::Upload(void *data)
|
||||
void RenderTexture::Upload(const void *data)
|
||||
{
|
||||
if (!IsCreated()) {
|
||||
qWarning() << tr("RenderTexture::Upload() called while it wasn't created");
|
||||
@@ -163,16 +164,36 @@ void RenderTexture::Upload(void *data)
|
||||
Release();
|
||||
}
|
||||
|
||||
void *RenderTexture::Download() const
|
||||
uchar *RenderTexture::Download() const
|
||||
{
|
||||
if (!IsCreated()) {
|
||||
qWarning() << tr("RenderTexture::Download() called while it wasn't created");
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
// FIXME: Implement this
|
||||
QOpenGLFunctions* f = context_->functions();
|
||||
|
||||
return nullptr;
|
||||
GLuint read_fbo;
|
||||
|
||||
f->glGenFramebuffers(1, &read_fbo);
|
||||
|
||||
f->glBindFramebuffer(GL_READ_FRAMEBUFFER, read_fbo);
|
||||
|
||||
context_->extraFunctions()->glFramebufferTexture2D(
|
||||
GL_READ_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D, texture_, 0
|
||||
);
|
||||
|
||||
PixelFormatInfo format_info = PixelService::GetPixelFormatInfo(format_);
|
||||
|
||||
uchar* data = new uchar[PixelService::GetBufferSize(format_, width_, height_)];
|
||||
|
||||
f->glReadPixels(0, 0, width_, height_, format_info.pixel_format, format_info.pixel_type, data);
|
||||
|
||||
f->glBindFramebuffer(GL_READ_FRAMEBUFFER, 0);
|
||||
|
||||
f->glDeleteFramebuffers(1, &read_fbo);
|
||||
|
||||
return data;
|
||||
}
|
||||
|
||||
void RenderTexture::CreateInternal(void *data)
|
||||
|
||||
@@ -64,9 +64,9 @@ public:
|
||||
const GLuint& back_texture() const;
|
||||
void SwapFrontAndBack();
|
||||
|
||||
void Upload(void* data);
|
||||
void Upload(const void *data);
|
||||
|
||||
void* Download() const;
|
||||
uchar *Download() const;
|
||||
|
||||
private:
|
||||
void CreateInternal(void *data = nullptr);
|
||||
|
||||
Reference in New Issue
Block a user