Merge branch 'master' into pr/1987

This commit is contained in:
itsmattkc
2022-07-27 10:05:00 -07:00
45 changed files with 546 additions and 340 deletions
+7 -3
View File
@@ -24,6 +24,7 @@ on:
env:
DOWNLOAD_TOOL: curl -fLOSs --retry 2 --retry-delay 60
UPLOAD_TOOL: curl -X POST --retry 2 --retry-delay 60
CMAKE_ARGS: -DUSE_WERROR=ON -DBUILD_TESTS=ON
jobs:
linux:
@@ -73,7 +74,8 @@ jobs:
cmake .. -G "${{ matrix.cmake-gen }}" \
-DCMAKE_BUILD_TYPE="${{ matrix.build-type }}" \
-DCMAKE_C_COMPILER="${{ matrix.cc-compiler }}" \
-DCMAKE_CXX_COMPILER="${{ matrix.cxx-compiler }}"
-DCMAKE_CXX_COMPILER="${{ matrix.cxx-compiler }}" \
$CMAKE_ARGS
- name: Build
working-directory: build
@@ -204,7 +206,8 @@ jobs:
working-directory: ${{ runner.workspace }}/build
run: |
cmake $GITHUB_WORKSPACE -DCMAKE_BUILD_TYPE=${{ matrix.build-type }} -G "${{ matrix.cmake-gen }}" \
-DCMAKE_TOOLCHAIN_FILE="$VCPKG_ROOT/scripts/buildsystems/vcpkg.cmake"
-DCMAKE_TOOLCHAIN_FILE="$VCPKG_ROOT/scripts/buildsystems/vcpkg.cmake" \
$CMAKE_ARGS
- name: Build
working-directory: ${{ runner.workspace }}/build
@@ -356,7 +359,8 @@ jobs:
PATH=$DEP_LOCATION:$DEP_LOCATION/bin:$DEP_LOCATION/include:$DEP_LOCATION/lib:$DEP_LOCATION/crashpad:$PATH \
cmake $GITHUB_WORKSPACE -DCMAKE_BUILD_TYPE=${{ matrix.build-type }} \
-DCMAKE_OSX_DEPLOYMENT_TARGET=${{ matrix.min-deploy }} -G "${{ matrix.cmake-gen }}" \
-DCMAKE_OSX_ARCHITECTURES="${{ matrix.os-arch }}"
-DCMAKE_OSX_ARCHITECTURES="${{ matrix.os-arch }}" \
$CMAKE_ARGS
- name: Build
working-directory: ${{ runner.workspace }}/build
+2 -2
View File
@@ -19,8 +19,8 @@ cmake_minimum_required(VERSION 3.13 FATAL_ERROR)
project(olive-editor VERSION 0.2.0 LANGUAGES CXX)
option(BUILD_DOXYGEN "Build Doxygen documentation" OFF)
option(BUILD_TESTS "Build unit tests" ON)
option(USE_WERROR "Error on compile warning" ON)
option(BUILD_TESTS "Build unit tests" OFF)
option(USE_WERROR "Error on compile warning" OFF)
set(CMAKE_CXX_STANDARD 17)
set(CMAKE_CXX_STANDARD_REQUIRED ON)
+6
View File
@@ -43,6 +43,12 @@ Decoder::Decoder() :
UpdateLastAccessed();
}
void Decoder::IncrementAccessTime(qint64 t)
{
QMutexLocker locker(&mutex_);
last_accessed_ += t;
}
bool Decoder::Open(const CodecStream &stream)
{
QMutexLocker locker(&mutex_);
+2
View File
@@ -87,6 +87,8 @@ public:
virtual bool SupportsVideo(){return false;}
virtual bool SupportsAudio(){return false;}
void IncrementAccessTime(qint64 t);
class CodecStream
{
public:
+2
View File
@@ -513,6 +513,8 @@ FootageDescription FFmpegDecoder::Probe(const QString &filename, CancelAtom *can
}
desc.SetStreamCount(fmt_ctx->nb_streams);
}
// Free all memory
-10
View File
@@ -1,10 +0,0 @@
#ifndef FOOTAGEMETA_H
#define FOOTAGEMETA_H
struct FootageData {
struct StreamData {
};
};
#endif // FOOTAGEMETA_H
+4 -1
View File
@@ -73,7 +73,8 @@ FootageDescription OIIODecoder::Probe(const QString &filename, CancelAtom *cance
bool stream_enabled = true;
for (int i=0; in->seek_subimage(i, 0); i++) {
int i;
for (i=0; in->seek_subimage(i, 0); i++) {
OIIO::ImageSpec spec = in->spec();
VideoParams video_params = GetVideoParamsFromImageSpec(spec);
@@ -104,6 +105,8 @@ FootageDescription OIIODecoder::Probe(const QString &filename, CancelAtom *cance
desc.AddVideoStream(video_params);
}
desc.SetStreamCount(i);
// If we're here, we have a successful image open
in->close();
+2
View File
@@ -586,6 +586,8 @@ void Core::ImportTaskComplete(Task* task)
}
undo_stack_.pushIfHasChildren(command);
main_window_->SelectFootage(import_task->GetImportedFootage());
}
bool Core::ConfirmImageSequence(const QString& filename)
@@ -35,7 +35,7 @@ ExportAdvancedVideoDialog::ExportAdvancedVideoDialog(const QList<QString> &pix_f
pixel_layout->addWidget(new QLabel(tr("YUV Color Range:")), row, 0);
yuv_color_range_combobox_ = new QComboBox();
yuv_color_range_combobox_->addItems({tr("MPEG (16-235)"), tr("JPEG (0-255)")});
yuv_color_range_combobox_->addItems({tr("Limited (16-235)"), tr("Full (0-255)")});
pixel_layout->addWidget(yuv_color_range_combobox_, row, 1);
}
@@ -22,12 +22,9 @@
#include <QGuiApplication>
#include "common/range.h"
#include "core.h"
#include "node/traverser.h"
namespace olive {
const QString TransformDistortNode::kParentInput = QStringLiteral("parent_in");
const QString TransformDistortNode::kTextureInput = QStringLiteral("tex_in");
const QString TransformDistortNode::kAutoscaleInput = QStringLiteral("autoscale_in");
const QString TransformDistortNode::kInterpolationInput = QStringLiteral("interpolation_in");
@@ -36,6 +33,8 @@ const QString TransformDistortNode::kInterpolationInput = QStringLiteral("interp
TransformDistortNode::TransformDistortNode()
{
AddInput(kParentInput, NodeValue::kMatrix);
AddInput(kAutoscaleInput, NodeValue::kCombo, 0);
AddInput(kInterpolationInput, NodeValue::kCombo, 2);
@@ -73,6 +72,7 @@ void TransformDistortNode::Retranslate()
{
super::Retranslate();
SetInputName(kParentInput, tr("Parent"));
SetInputName(kAutoscaleInput, tr("Auto-Scale"));
SetInputName(kTextureInput, tr("Texture"));
SetInputName(kInterpolationInput, tr("Interpolation"));
@@ -84,12 +84,12 @@ void TransformDistortNode::Retranslate()
void TransformDistortNode::Value(const NodeValueRow &value, const NodeGlobals &globals, NodeValueTable *table) const
{
// Generate matrix
QMatrix4x4 generated_matrix = GenerateMatrix(value, false, false, false);
QMatrix4x4 generated_matrix = GenerateMatrix(value, false, false, false, value[kParentInput].toMatrix());
// Pop texture
NodeValue texture_meta = value[kTextureInput];
bool pushed_job = false;
QVariant job_to_push;
// If we have a texture, generate a matrix and make it happen
if (TexturePtr texture = texture_meta.toTexture()) {
@@ -103,15 +103,17 @@ void TransformDistortNode::Value(const NodeValueRow &value, const NodeGlobals &g
job.Insert(QStringLiteral("ove_mvpmat"), NodeValue(NodeValue::kMatrix, real_matrix, this));
job.SetInterpolation(QStringLiteral("ove_maintex"), static_cast<Texture::Interpolation>(value[kInterpolationInput].toInt()));
table->Push(NodeValue::kTexture, QVariant::fromValue(job), this);
pushed_job = true;
job_to_push = QVariant::fromValue(job);
}
}
if (!pushed_job) {
table->Push(NodeValue::kMatrix, QVariant::fromValue(generated_matrix), this);
if (job_to_push.isNull()) {
// Re-push whatever value we received
table->Push(texture_meta);
} else {
table->Push(NodeValue::kTexture, job_to_push, this);
}
}
@@ -129,7 +131,7 @@ void TransformDistortNode::GizmoDragStart(const NodeValueRow &row, double x, dou
if (gizmo == anchor_gizmo_) {
gizmo_inverted_transform_ = GenerateMatrix(row, true, true, false).toTransform().inverted();
gizmo_inverted_transform_ = GenerateMatrix(row, true, true, false, row[kParentInput].toMatrix()).toTransform().inverted();
} else if (IsAScaleGizmo(gizmo)) {
@@ -171,7 +173,7 @@ void TransformDistortNode::GizmoDragStart(const NodeValueRow &row, double x, dou
}
// Store current matrix
gizmo_inverted_transform_ = GenerateMatrix(row, true, true, true).toTransform().inverted();
gizmo_inverted_transform_ = GenerateMatrix(row, true, true, true, row[kParentInput].toMatrix()).toTransform().inverted();
} else if (gizmo == rotation_gizmo_) {
@@ -356,7 +358,7 @@ void TransformDistortNode::UpdateGizmoPositions(const NodeValueRow &row, const N
// Fold values into a matrix for the rectangle
QMatrix4x4 rectangle_matrix;
rectangle_matrix.scale(sequence_half_res);
rectangle_matrix *= AdjustMatrixByResolutions(GenerateMatrix(row, false, false, false),
rectangle_matrix *= AdjustMatrixByResolutions(GenerateMatrix(row, false, false, false, row[kParentInput].toMatrix()),
sequence_res,
tex_sz,
tex_offset,
@@ -376,7 +378,7 @@ void TransformDistortNode::UpdateGizmoPositions(const NodeValueRow &row, const N
// Draw anchor point
QMatrix4x4 anchor_matrix;
anchor_matrix.scale(sequence_half_res);
anchor_matrix *= AdjustMatrixByResolutions(GenerateMatrix(row, true, false, false),
anchor_matrix *= AdjustMatrixByResolutions(GenerateMatrix(row, true, false, false, row[kParentInput].toMatrix()),
sequence_res,
tex_sz,
tex_offset,
@@ -402,7 +404,7 @@ void TransformDistortNode::UpdateGizmoPositions(const NodeValueRow &row, const N
QTransform TransformDistortNode::GizmoTransformation(const NodeValueRow &row, const NodeGlobals &globals) const
{
if (TexturePtr texture = row[kTextureInput].toTexture()) {
auto m = GenerateMatrix(row, false, false, false);
auto m = GenerateMatrix(row, false, false, false, row[kParentInput].toMatrix());
return GenerateAutoScaledMatrix(m, row, globals, texture->params()).toTransform();
}
return super::GizmoTransformation(row, globals);
@@ -84,6 +84,7 @@ public:
virtual void UpdateGizmoPositions(const NodeValueRow &row, const NodeGlobals &globals) override;
virtual QTransform GizmoTransformation(const NodeValueRow &row, const NodeGlobals &globals) const override;
static const QString kParentInput;
static const QString kTextureInput;
static const QString kAutoscaleInput;
static const QString kInterpolationInput;
+6 -6
View File
@@ -90,11 +90,11 @@ void MatrixGenerator::Retranslate()
void MatrixGenerator::Value(const NodeValueRow &value, const NodeGlobals &globals, NodeValueTable *table) const
{
// Push matrix output
QMatrix4x4 mat = GenerateMatrix(value, false, false, false);
QMatrix4x4 mat = GenerateMatrix(value, false, false, false, QMatrix4x4());
table->Push(NodeValue::kMatrix, mat, this);
}
QMatrix4x4 MatrixGenerator::GenerateMatrix(const NodeValueRow &value, bool ignore_anchor, bool ignore_position, bool ignore_scale) const
QMatrix4x4 MatrixGenerator::GenerateMatrix(const NodeValueRow &value, bool ignore_anchor, bool ignore_position, bool ignore_scale, const QMatrix4x4 &mat) const
{
QVector2D anchor;
QVector2D position;
@@ -116,17 +116,17 @@ QMatrix4x4 MatrixGenerator::GenerateMatrix(const NodeValueRow &value, bool ignor
value[kRotationInput].toDouble(),
scale,
value[kUniformScaleInput].toBool(),
anchor);
anchor,
mat);
}
QMatrix4x4 MatrixGenerator::GenerateMatrix(const QVector2D& pos,
const float& rot,
const QVector2D& scale,
bool uniform_scale,
const QVector2D& anchor)
const QVector2D& anchor,
QMatrix4x4 mat)
{
QMatrix4x4 mat;
// Position
mat.translate(pos);
+3 -2
View File
@@ -53,12 +53,13 @@ public:
static const QString kAnchorInput;
protected:
QMatrix4x4 GenerateMatrix(const NodeValueRow &value, bool ignore_anchor, bool ignore_position, bool ignore_scale) const;
QMatrix4x4 GenerateMatrix(const NodeValueRow &value, bool ignore_anchor, bool ignore_position, bool ignore_scale, const QMatrix4x4 &mat) const;
static QMatrix4x4 GenerateMatrix(const QVector2D &pos,
const float &rot,
const QVector2D &scale,
bool uniform_scale,
const QVector2D &anchor);
const QVector2D &anchor,
QMatrix4x4 mat);
virtual void InputValueChangedEvent(const QString& input, int element) override;
+1 -1
View File
@@ -126,7 +126,7 @@ public:
return InputArraySize(kSubtitleParamsInput);
}
int GetTotalStreamCount() const
virtual int GetTotalStreamCount() const
{
return GetVideoStreamCount() + GetAudioStreamCount() + GetSubtitleStreamCount();
}
+15
View File
@@ -73,6 +73,21 @@ bool Folder::ChildExistsWithName(const QString &s) const
return ChildExistsWithNameInternal(this, s);
}
bool Folder::HasChildRecursive(Node *child) const
{
for (Node *i : item_children_) {
if (i == child) {
return true;
} else if (Folder *f = dynamic_cast<Folder*>(i)) {
if (f->HasChildRecursive(child)) {
return true;
}
}
}
return false;
}
int Folder::index_of_child_in_array(Node *item) const
{
int index_of_item = item_children_.indexOf(item);
+2
View File
@@ -65,6 +65,8 @@ public:
bool ChildExistsWithName(const QString& s) const;
bool HasChildRecursive(Node *child) const;
int item_child_count() const
{
return item_children_.size();
+7 -1
View File
@@ -44,7 +44,8 @@ Footage::Footage(const QString &filename) :
ViewerOutput(false, false),
timestamp_(0),
valid_(false),
cancelled_(nullptr)
cancelled_(nullptr),
total_stream_count_(0)
{
SetCacheTextures(true);
@@ -125,6 +126,9 @@ void Footage::Clear()
// Clear decoder link
decoder_.clear();
// Clear total stream count
total_stream_count_ = 0;
// Reset ready state
valid_ = false;
}
@@ -497,6 +501,8 @@ void Footage::Reprobe()
SetStream(Track::kSubtitle, QVariant::fromValue(footage_info.GetSubtitleStreams().at(i)), i);
}
total_stream_count_ = footage_info.GetStreamCount();
SetValid();
}
+4
View File
@@ -180,6 +180,8 @@ public:
virtual qint64 creation_time() const override;
virtual qint64 mod_time() const override;
virtual int GetTotalStreamCount() const override { return total_stream_count_; }
static const QString kFilenameInput;
protected:
@@ -224,6 +226,8 @@ private:
CancelAtom *cancelled_;
int total_stream_count_;
private slots:
void CheckFootage();
@@ -43,9 +43,11 @@ bool FootageDescription::Load(const QString &filename)
// Default to first version of metadata (which wasn't versioned at all)
unsigned version = 1;
XMLAttributeLoop((&reader), attr) {
if (attr.name() == QStringLiteral("version")) {
version = attr.value().toUInt();
{
XMLAttributeLoop((&reader), attr) {
if (attr.name() == QStringLiteral("version")) {
version = attr.value().toUInt();
}
}
}
@@ -58,6 +60,14 @@ bool FootageDescription::Load(const QString &filename)
if (reader.name() == QStringLiteral("decoder")) {
decoder_ = reader.readElementText();
} else if (reader.name() == QStringLiteral("streams")) {
{
XMLAttributeLoop((&reader), attr) {
if (attr.name() == QStringLiteral("count")) {
total_stream_count_ = attr.value().toInt();
}
}
}
while (XMLReadNextStartElement(&reader)) {
if (reader.name() == QStringLiteral("video")) {
VideoParams vp;
@@ -116,6 +126,8 @@ bool FootageDescription::Save(const QString &filename) const
writer.writeStartElement(QStringLiteral("streams"));
writer.writeAttribute(QStringLiteral("count"), QString::number(total_stream_count_));
foreach (const VideoParams& vp, video_streams_) {
writer.writeStartElement(QStringLiteral("video"));
vp.Save(&writer);
@@ -32,7 +32,8 @@ class FootageDescription
{
public:
FootageDescription(const QString& decoder = QString()) :
decoder_(decoder)
decoder_(decoder),
total_stream_count_(0)
{
}
@@ -118,6 +119,9 @@ public:
return StreamIsVideo(index) || StreamIsAudio(index) || StreamIsSubtitle(index);
}
int GetStreamCount() const { return total_stream_count_; }
void SetStreamCount(int s) { total_stream_count_ = s; }
bool Load(const QString& filename);
bool Save(const QString& filename) const;
@@ -138,7 +142,7 @@ public:
}
private:
static constexpr unsigned kFootageMetaVersion = 2;
static constexpr unsigned kFootageMetaVersion = 4;
QString decoder_;
@@ -148,6 +152,8 @@ private:
QVector<SubtitleParams> subtitle_streams_;
int total_stream_count_;
};
}
+2 -2
View File
@@ -52,9 +52,9 @@ public:
ProjectViewModel* model() const;
bool SelectItem(Node *n)
bool SelectItem(Node *n, bool deselect_all_first = true)
{
return explorer_->SelectItem(n);
return explorer_->SelectItem(n, deselect_all_first);
}
virtual void SelectAll() override;
+39 -68
View File
@@ -78,9 +78,9 @@ private:
#define PRINT_GL_ERRORS ErrorPrinter __e(__FUNCTION__, functions_)
#define GL_PREAMBLE \
QMutexLocker __l(&global_opengl_mutex);
//QMutexLocker __l(&global_opengl_mutex);
QMutex global_opengl_mutex;
//QMutex global_opengl_mutex;
OpenGLRenderer::OpenGLRenderer(QObject* parent) :
Renderer(parent),
@@ -107,7 +107,7 @@ void OpenGLRenderer::Init(QOpenGLContext *existing_ctx)
bool OpenGLRenderer::Init()
{
QMutexLocker locker(&global_opengl_mutex);
GL_PREAMBLE;
if (context_) {
qCritical() << "Can't initialize already initialized OpenGLRenderer";
@@ -177,7 +177,7 @@ void OpenGLRenderer::ClearDestination(Texture *texture, double r, double g, doub
GL_PREAMBLE;
if (texture) {
AttachTextureAsDestination(texture);
AttachTextureAsDestination(texture->id());
}
ClearDestinationInternal(r, g, b, a);
@@ -187,16 +187,11 @@ void OpenGLRenderer::ClearDestination(Texture *texture, double r, double g, doub
}
}
QVariant OpenGLRenderer::CreateNativeTexture2D(int width, int height, VideoParams::Format format, int channel_count, const void *data, int linesize)
QVariant OpenGLRenderer::CreateNativeTexture(int width, int height, int depth, VideoParams::Format format, int channel_count, const void *data, int linesize)
{
GL_PREAMBLE;
return CreateNativeTexture2DInternal(width, height, format, channel_count, data, linesize);
}
QVariant OpenGLRenderer::CreateNativeTexture3D(int width, int height, int depth, VideoParams::Format format, int channel_count, const void *data, int linesize)
{
GL_PREAMBLE;
bool is_3d = depth > 1;
// Generate new texture
GLuint texture;
@@ -205,23 +200,32 @@ QVariant OpenGLRenderer::CreateNativeTexture3D(int width, int height, int depth,
functions_->glPixelStorei(GL_UNPACK_ROW_LENGTH, linesize);
GLenum target_current = is_3d ? GL_TEXTURE_BINDING_3D : GL_TEXTURE_BINDING_2D;
GLenum target = is_3d ? GL_TEXTURE_3D : GL_TEXTURE_2D;
GLint current_tex;
functions_->glGetIntegerv(GL_TEXTURE_BINDING_3D, &current_tex);
functions_->glGetIntegerv(target_current, &current_tex);
functions_->glBindTexture(GL_TEXTURE_3D, texture);
functions_->glBindTexture(target, texture);
context_->extraFunctions()->glTexImage3D(GL_TEXTURE_3D, 0, GetInternalFormat(format, channel_count),
width, height, depth, 0, GetPixelFormat(channel_count),
GetPixelType(format), data);
if (is_3d) {
context_->extraFunctions()->glTexImage3D(target, 0, GetInternalFormat(format, channel_count),
width, height, depth, 0, GetPixelFormat(channel_count),
GetPixelType(format), data);
} else {
functions_->glTexImage2D(target, 0, GetInternalFormat(format, channel_count),
width, height, 0, GetPixelFormat(channel_count),
GetPixelType(format), data);
}
functions_->glPixelStorei(GL_UNPACK_ROW_LENGTH, 0);
functions_->glBindTexture(GL_TEXTURE_3D, current_tex);
functions_->glBindTexture(target, current_tex);
return texture;
}
void OpenGLRenderer::AttachTextureAsDestination(Texture* texture)
void OpenGLRenderer::AttachTextureAsDestination(const QVariant &texture)
{
PRINT_GL_ERRORS;
@@ -229,7 +233,7 @@ void OpenGLRenderer::AttachTextureAsDestination(Texture* texture)
functions_->glFramebufferTexture2D(GL_FRAMEBUFFER,
GL_COLOR_ATTACHMENT0,
GL_TEXTURE_2D,
texture->id().value<GLuint>(),
texture.value<GLuint>(),
0);
}
@@ -287,15 +291,16 @@ void OpenGLRenderer::DestroyNativeShader(QVariant shader)
functions_->glDeleteProgram(program);
}
void OpenGLRenderer::UploadToTexture(Texture *texture, const void *data, int linesize)
void OpenGLRenderer::UploadToTexture(const QVariant &handle, const VideoParams &p, const void *data, int linesize)
{
GL_PREAMBLE;
GLuint t = texture->id().value<GLuint>();
const VideoParams& p = texture->params();
GLuint t = handle.value<GLuint>();
GLenum tex_type = texture->type() == Texture::k2D ? GL_TEXTURE_2D : GL_TEXTURE_3D;
GLenum tex_binding = texture->type() == Texture::k2D ? GL_TEXTURE_BINDING_2D : GL_TEXTURE_BINDING_3D;
bool is_3d = p.is_3d();
GLenum tex_type = !is_3d ? GL_TEXTURE_2D : GL_TEXTURE_3D;
GLenum tex_binding = !is_3d ? GL_TEXTURE_BINDING_2D : GL_TEXTURE_BINDING_3D;
// Store currently bound texture so it can be restored later
GLint current_tex;
@@ -308,7 +313,7 @@ void OpenGLRenderer::UploadToTexture(Texture *texture, const void *data, int lin
{
PRINT_GL_ERRORS;
if (texture->type() == Texture::k2D) {
if (!is_3d) {
functions_->glTexSubImage2D(tex_type, 0, 0, 0,
p.effective_width(), p.effective_height(),
GetPixelFormat(p.channel_count()), GetPixelType(p.format()),
@@ -326,16 +331,14 @@ void OpenGLRenderer::UploadToTexture(Texture *texture, const void *data, int lin
functions_->glBindTexture(tex_type, current_tex);
}
void OpenGLRenderer::DownloadFromTexture(Texture* texture, void *data, int linesize)
void OpenGLRenderer::DownloadFromTexture(const QVariant &id, const VideoParams &p, void *data, int linesize)
{
GL_PREAMBLE;
const VideoParams& p = texture->params();
GLint current_tex;
functions_->glGetIntegerv(GL_TEXTURE_BINDING_2D, &current_tex);
AttachTextureAsDestination(texture);
AttachTextureAsDestination(id);
functions_->glPixelStorei(GL_PACK_ROW_LENGTH, linesize);
@@ -366,7 +369,7 @@ void OpenGLRenderer::Flush()
Color OpenGLRenderer::GetPixelFromTexture(Texture *texture, const QPointF &pt)
{
AttachTextureAsDestination(texture);
AttachTextureAsDestination(texture->id());
QByteArray data(VideoParams::GetBytesPerPixel(texture->format(), texture->channel_count()), Qt::Uninitialized);
@@ -502,7 +505,7 @@ void OpenGLRenderer::Blit(QVariant s, ShaderJob job, Texture *destination, Video
functions_->glActiveTexture(GL_TEXTURE0 + i);
GLenum target = (texture && texture->type() == Texture::k3D) ? GL_TEXTURE_3D : GL_TEXTURE_2D;
GLenum target = (texture && texture->params().is_3d()) ? GL_TEXTURE_3D : GL_TEXTURE_2D;
functions_->glBindTexture(target, tex_id);
if (tex_id) {
@@ -584,11 +587,11 @@ void OpenGLRenderer::Blit(QVariant s, ShaderJob job, Texture *destination, Video
TexturePtr output_tex, input_tex;
if (real_iteration_count > 1) {
// Create one texture to bounce off
output_tex = CreateTextureFromNativeHandle(CreateNativeTexture2DInternal(destination_params), destination_params);
output_tex = CreateTexture(destination_params);
if (real_iteration_count > 2) {
// Create a second texture bounce off
input_tex = CreateTextureFromNativeHandle(CreateNativeTexture2DInternal(destination_params), destination_params);
input_tex = CreateTexture(destination_params);
}
}
@@ -604,7 +607,7 @@ void OpenGLRenderer::Blit(QVariant s, ShaderJob job, Texture *destination, Video
// This is the last iteration, draw to the destination
if (destination) {
// If we have a destination texture, draw to it
AttachTextureAsDestination(destination);
AttachTextureAsDestination(destination->id());
} else if (iteration > 0) {
// Otherwise, if we were iterating before, detach texture now
DetachTextureAsDestination();
@@ -616,7 +619,7 @@ void OpenGLRenderer::Blit(QVariant s, ShaderJob job, Texture *destination, Video
}
} else {
// Always draw to output_tex, which gets swapped with input_tex every iteration
AttachTextureAsDestination(output_tex.get());
AttachTextureAsDestination(output_tex->id());
}
if (iteration > 0) {
@@ -648,7 +651,7 @@ void OpenGLRenderer::Blit(QVariant s, ShaderJob job, Texture *destination, Video
// Release any textures we bound before
for (int i=textures_to_bind.size()-1; i>=0; i--) {
TexturePtr texture = textures_to_bind.at(i).texture;
GLenum target = (texture && texture->type() == Texture::k3D) ? GL_TEXTURE_3D : GL_TEXTURE_2D;
GLenum target = (texture && texture->params().is_3d()) ? GL_TEXTURE_3D : GL_TEXTURE_2D;
functions_->glActiveTexture(GL_TEXTURE0 + i);
functions_->glBindTexture(target, 0);
}
@@ -788,38 +791,6 @@ void OpenGLRenderer::ClearDestinationInternal(double r, double g, double b, doub
functions_->glClear(GL_COLOR_BUFFER_BIT);
}
QVariant OpenGLRenderer::CreateNativeTexture2DInternal(int width, int height, VideoParams::Format format, int channel_count, const void *data, int linesize)
{
GLuint texture;
functions_->glGenTextures(1, &texture);
texture_params_.insert(texture, {width, height, 1, format, channel_count});
functions_->glPixelStorei(GL_UNPACK_ROW_LENGTH, linesize);
GLint current_tex;
functions_->glGetIntegerv(GL_TEXTURE_BINDING_2D, &current_tex);
functions_->glBindTexture(GL_TEXTURE_2D, texture);
{
PRINT_GL_ERRORS;
functions_->glTexImage2D(GL_TEXTURE_2D, 0, GetInternalFormat(format, channel_count),
width, height, 0, GetPixelFormat(channel_count),
GetPixelType(format), data);
}
functions_->glPixelStorei(GL_UNPACK_ROW_LENGTH, 0);
functions_->glBindTexture(GL_TEXTURE_2D, current_tex);
return texture;
}
QVariant OpenGLRenderer::CreateNativeTexture2DInternal(const VideoParams &params, const void *data, int linesize)
{
return CreateNativeTexture2DInternal(params.effective_width(), params.effective_height(), params.format(), params.channel_count(), data, linesize);
}
GLuint OpenGLRenderer::CompileShader(GLenum type, const QString &code)
{
static const QString shader_preamble =
+10 -15
View File
@@ -47,37 +47,35 @@ public:
virtual void PostDestroy() override;
public slots:
virtual void PostInit() override;
virtual void DestroyInternal() override;
virtual void ClearDestination(olive::Texture *texture = nullptr, double r = 0.0, double g = 0.0, double b = 0.0, double a = 0.0) override;
virtual QVariant CreateNativeTexture2D(int width, int height, olive::VideoParams::Format format, int channel_count, const void* data = nullptr, int linesize = 0) override;
virtual QVariant CreateNativeTexture3D(int width, int height, int depth, olive::VideoParams::Format format, int channel_count, const void* data = nullptr, int linesize = 0) override;
virtual void DestroyNativeTexture(QVariant texture) override;
virtual QVariant CreateNativeShader(olive::ShaderCode code) override;
virtual void DestroyNativeShader(QVariant shader) override;
virtual void UploadToTexture(olive::Texture* texture, const void* data, int linesize) override;
virtual void UploadToTexture(const QVariant &handle, const VideoParams &params, const void* data, int linesize) override;
virtual void DownloadFromTexture(olive::Texture* texture, void* data, int linesize) override;
virtual void DownloadFromTexture(const QVariant &handle, const VideoParams &params, void* data, int linesize) override;
virtual void Flush() override;
virtual Color GetPixelFromTexture(olive::Texture *texture, const QPointF &pt) override;
protected slots:
protected:
virtual void Blit(QVariant shader,
olive::ShaderJob job,
olive::Texture* destination,
olive::VideoParams destination_params,
bool clear_destination) override;
virtual QVariant CreateNativeTexture(int width, int height, int depth, olive::VideoParams::Format format, int channel_count, const void* data = nullptr, int linesize = 0) override;
virtual void DestroyNativeTexture(QVariant texture) override;
virtual void DestroyInternal() override;
private:
static GLint GetInternalFormat(VideoParams::Format format, int channel_layout);
@@ -85,7 +83,7 @@ private:
static GLenum GetPixelFormat(int channel_count);
void AttachTextureAsDestination(olive::Texture* texture);
void AttachTextureAsDestination(const QVariant &texture);
void DetachTextureAsDestination();
@@ -93,9 +91,6 @@ private:
void ClearDestinationInternal(double r = 0.0, double g = 0.0, double b = 0.0, double a = 0.0);
QVariant CreateNativeTexture2DInternal(int width, int height, olive::VideoParams::Format format, int channel_count, const void* data = nullptr, int linesize = 0);
QVariant CreateNativeTexture2DInternal(const VideoParams &params, const void* data = nullptr, int linesize = 0);
GLuint CompileShader(GLenum type, const QString &code);
QOpenGLContext* context_;
+8 -5
View File
@@ -61,7 +61,7 @@ PreviewAutoCacher::~PreviewAutoCacher()
SetViewerNode(nullptr);
}
RenderTicketPtr PreviewAutoCacher::GetSingleFrame(const rational &t)
RenderTicketPtr PreviewAutoCacher::GetSingleFrame(const rational &t, bool dry)
{
// If we have a single frame render queued (but not yet sent to the RenderManager), cancel it now
CancelQueuedSingleFrameRender();
@@ -70,6 +70,7 @@ RenderTicketPtr PreviewAutoCacher::GetSingleFrame(const rational &t)
auto sfr = std::make_shared<RenderTicket>();
sfr->Start();
sfr->setProperty("time", QVariant::fromValue(t));
sfr->setProperty("dry", dry);
// Queue it and try to render
single_frame_render_ = sfr;
@@ -566,7 +567,8 @@ void PreviewAutoCacher::TryRender()
if (single_frame_render_) {
// Check if already caching this
RenderTicketWatcher *watcher = RenderFrame(single_frame_render_->property("time").value<rational>(),
nullptr);
nullptr,
single_frame_render_->property("dry").toBool());
video_immediate_passthroughs_[watcher].append(single_frame_render_);
single_frame_render_ = nullptr;
@@ -583,7 +585,7 @@ void PreviewAutoCacher::TryRender()
// We want this hash, if we're not already rendering, start render now
if (!render_task) {
// Don't render any hash more than once
RenderFrame(t, viewer_node_->video_frame_cache());
RenderFrame(t, viewer_node_->video_frame_cache(), false);
}
emit SignalCacheProxyTaskProgress(double(queued_frame_iterator_.frame_index()) / double(queued_frame_iterator_.size()));
@@ -609,7 +611,7 @@ void PreviewAutoCacher::TryRender()
}
}
RenderTicketWatcher* PreviewAutoCacher::RenderFrame(Node *node, const rational& time, FrameHashCache *cache)
RenderTicketWatcher* PreviewAutoCacher::RenderFrame(Node *node, const rational& time, FrameHashCache *cache, bool dry)
{
RenderTicketWatcher* watcher = new RenderTicketWatcher();
watcher->setProperty("job", QVariant::fromValue(last_update_time_));
@@ -623,7 +625,8 @@ RenderTicketWatcher* PreviewAutoCacher::RenderFrame(Node *node, const rational&
time,
RenderMode::kOffline,
cache,
RenderManager::kTexture));
dry ? RenderManager::kNull : RenderManager::kTexture));
return watcher;
}
+4 -4
View File
@@ -49,7 +49,7 @@ public:
virtual ~PreviewAutoCacher() override;
RenderTicketPtr GetSingleFrame(const rational& t);
RenderTicketPtr GetSingleFrame(const rational& t, bool dry = false);
RenderTicketPtr GetRangeOfAudio(TimeRange range);
@@ -98,10 +98,10 @@ signals:
private:
void TryRender();
RenderTicketWatcher *RenderFrame(Node *node, const rational &time, FrameHashCache *cache);
RenderTicketWatcher *RenderFrame(const rational &time, FrameHashCache *cache)
RenderTicketWatcher *RenderFrame(Node *node, const rational &time, FrameHashCache *cache, bool dry);
RenderTicketWatcher *RenderFrame(const rational &time, FrameHashCache *cache, bool dry)
{
return RenderFrame(copied_viewer_node_->GetConnectedTextureOutput(), time, cache);
return RenderFrame(copied_viewer_node_->GetConnectedTextureOutput(), time, cache, dry);
}
RenderTicketPtr RenderAudio(Node *node, const TimeRange &range, bool generate_waveforms);
+66 -26
View File
@@ -20,36 +20,62 @@
#include "renderer.h"
#include <QDateTime>
#include <QTimer>
#include <QVector2D>
#include "common/ocioutils.h"
namespace olive {
Renderer::Renderer(QObject *parent) :
QObject(parent)
{
}
TexturePtr Renderer::CreateTexture(const VideoParams &params, Texture::Type type, const void *data, int linesize)
{
QVariant v;
if (type == Texture::k3D) {
v = CreateNativeTexture3D(params.effective_width(), params.effective_height(),
params.effective_depth(), params.format(), params.channel_count(), data, linesize);
} else {
v = CreateNativeTexture2D(params.effective_width(), params.effective_height(), params.format(),
params.channel_count(), data, linesize);
}
return CreateTextureFromNativeHandle(v, params, type);
}
TexturePtr Renderer::CreateTexture(const VideoParams &params, const void *data, int linesize)
{
return CreateTexture(params, Texture::k2D, data, linesize);
QVariant v;
if (USE_TEXTURE_CACHE) {
for (auto it=texture_cache_.begin(); it!=texture_cache_.end(); it++) {
if (it->width == params.effective_width()
&& it->height == params.effective_height()
&& it->depth == params.effective_depth()
&& it->format == params.format()
&& it->channel_count == params.channel_count()) {
v = it->handle;
texture_cache_.erase(it);
break;
}
}
}
if (v.isNull()) {
v = CreateNativeTexture(params.effective_width(), params.effective_height(), params.effective_depth(),
params.format(), params.channel_count(), data, linesize);
} else if (data) {
UploadToTexture(v, params, data, linesize);
} else {
this->Flush();
}
return CreateTextureFromNativeHandle(v, params);
}
void Renderer::DestroyTexture(Texture *texture)
{
if (USE_TEXTURE_CACHE) {
texture_cache_.push_back({texture->params().effective_width(),
texture->params().effective_height(),
texture->params().effective_depth(),
texture->params().format(),
texture->params().channel_count(),
texture->id(),
QDateTime::currentMSecsSinceEpoch()});
ClearOldTextures();
} else {
DestroyNativeTexture(texture->id());
}
}
TexturePtr Renderer::InterlaceTexture(TexturePtr top, TexturePtr bottom, const VideoParams &params)
@@ -95,16 +121,21 @@ void Renderer::Destroy()
interlace_texture_.clear();
}
for (auto it=texture_cache_.begin(); it!=texture_cache_.end(); it++) {
DestroyNativeTexture(it->handle);
}
texture_cache_.clear();
DestroyInternal();
}
TexturePtr Renderer::CreateTextureFromNativeHandle(const QVariant &v, const VideoParams &params, Texture::Type type)
TexturePtr Renderer::CreateTextureFromNativeHandle(const QVariant &v, const VideoParams &params)
{
if (v.isNull()) {
return nullptr;
}
return std::make_shared<Texture>(this, v, params, type);
return std::make_shared<Texture>(this, v, params);
}
bool Renderer::GetColorContext(const ColorTransformJob &color_job, Renderer::ColorContext *ctx)
@@ -175,8 +206,7 @@ bool Renderer::GetColorContext(const ColorTransformJob &color_job, Renderer::Col
}
// Allocate 3D LUT
color_ctx.lut3d_textures[i].texture = CreateTexture(VideoParams(edge_len, edge_len, edge_len, VideoParams::kFormatFloat32, VideoParams::kRGBChannelCount),
Texture::k3D, values);
color_ctx.lut3d_textures[i].texture = CreateTexture(VideoParams(edge_len, edge_len, edge_len, VideoParams::kFormatFloat32, VideoParams::kRGBChannelCount), values);
color_ctx.lut3d_textures[i].name = sampler_name;
color_ctx.lut3d_textures[i].interpolation = (interpolation == OCIO::INTERP_NEAREST) ? Texture::kNearest : Texture::kLinear;
}
@@ -206,9 +236,7 @@ bool Renderer::GetColorContext(const ColorTransformJob &color_job, Renderer::Col
}
// Allocate 1D LUT
color_ctx.lut1d_textures[i].texture = CreateTexture(VideoParams(width, height, VideoParams::kFormatFloat32, (channel == OCIO::GpuShaderDesc::TEXTURE_RED_CHANNEL) ? 1 : VideoParams::kRGBChannelCount),
Texture::k2D,
values);
color_ctx.lut1d_textures[i].texture = CreateTexture(VideoParams(width, height, VideoParams::kFormatFloat32, (channel == OCIO::GpuShaderDesc::TEXTURE_RED_CHANNEL) ? 1 : VideoParams::kRGBChannelCount), values);
color_ctx.lut1d_textures[i].name = sampler_name;
color_ctx.lut1d_textures[i].interpolation = (interpolation == OCIO::INTERP_NEAREST) ? Texture::kNearest : Texture::kLinear;
}
@@ -219,6 +247,18 @@ bool Renderer::GetColorContext(const ColorTransformJob &color_job, Renderer::Col
}
}
void Renderer::ClearOldTextures()
{
for (auto it=texture_cache_.begin(); it!=texture_cache_.end(); ) {
if (it->accessed < QDateTime::currentMSecsSinceEpoch() - MAX_TEXTURE_LIFE) {
DestroyNativeTexture(it->handle);
it = texture_cache_.erase(it);
} else {
it++;
}
}
}
void Renderer::BlitColorManaged(const ColorTransformJob &color_job, Texture *destination, const VideoParams &params)
{
ColorContext color_ctx;
+29 -14
View File
@@ -45,9 +45,10 @@ public:
virtual bool Init() = 0;
TexturePtr CreateTexture(const VideoParams& params, Texture::Type type, const void* data = nullptr, int linesize = 0);
TexturePtr CreateTexture(const VideoParams& params, const void *data = nullptr, int linesize = 0);
void DestroyTexture(Texture *texture);
void BlitToTexture(QVariant shader,
olive::ShaderJob job,
olive::Texture* destination,
@@ -82,39 +83,34 @@ public:
virtual void PostDestroy() = 0;
public slots:
virtual void PostInit() = 0;
virtual void DestroyInternal() = 0;
virtual void ClearDestination(olive::Texture *texture = nullptr, double r = 0.0, double g = 0.0, double b = 0.0, double a = 0.0) = 0;
virtual QVariant CreateNativeTexture2D(int width, int height, olive::VideoParams::Format format, int channel_count, const void* data = nullptr, int linesize = 0) = 0;
virtual QVariant CreateNativeTexture3D(int width, int height, int depth, olive::VideoParams::Format format, int channel_count, const void* data = nullptr, int linesize = 0) = 0;
virtual void DestroyNativeTexture(QVariant texture) = 0;
virtual QVariant CreateNativeShader(olive::ShaderCode code) = 0;
virtual void DestroyNativeShader(QVariant shader) = 0;
virtual void UploadToTexture(olive::Texture* texture, const void* data, int linesize) = 0;
virtual void UploadToTexture(const QVariant &handle, const VideoParams &params, const void* data, int linesize) = 0;
virtual void DownloadFromTexture(olive::Texture* texture, void* data, int linesize) = 0;
virtual void DownloadFromTexture(const QVariant &handle, const VideoParams &params, void* data, int linesize) = 0;
virtual void Flush() = 0;
virtual Color GetPixelFromTexture(olive::Texture *texture, const QPointF &pt) = 0;
protected slots:
protected:
virtual void Blit(QVariant shader,
olive::ShaderJob job,
olive::Texture* destination,
olive::VideoParams destination_params,
bool clear_destination) = 0;
protected:
TexturePtr CreateTextureFromNativeHandle(const QVariant &v, const VideoParams &params, Texture::Type type = Texture::k2D);
virtual QVariant CreateNativeTexture(int width, int height, int depth, olive::VideoParams::Format format, int channel_count, const void* data = nullptr, int linesize = 0) = 0;
virtual void DestroyNativeTexture(QVariant texture) = 0;
virtual void DestroyInternal() = 0;
private:
struct ColorContext {
@@ -130,10 +126,29 @@ private:
};
TexturePtr CreateTextureFromNativeHandle(const QVariant &v, const VideoParams &params);
bool GetColorContext(const ColorTransformJob &color_job, ColorContext* ctx);
void ClearOldTextures();
QHash<QString, ColorContext> color_cache_;
struct CachedTexture
{
int width;
int height;
int depth;
VideoParams::Format format;
int channel_count;
QVariant handle;
qint64 accessed;
};
static const int MAX_TEXTURE_LIFE = 5000;
static const bool USE_TEXTURE_CACHE = true;
std::list<CachedTexture> texture_cache_;
QMutex color_cache_mutex_;
QVariant default_shader_;
+13 -1
View File
@@ -35,6 +35,7 @@
namespace olive {
RenderManager* RenderManager::instance_ = nullptr;
const rational RenderManager::kDryRunInterval = rational(10);
RenderManager::RenderManager(QObject *parent) :
backend_(kOpenGL),
@@ -52,9 +53,11 @@ RenderManager::RenderManager(QObject *parent) :
if (context_) {
video_thread_ = new RenderThread(context_, decoder_cache_, shader_cache_, this);
dry_run_thread_ = new RenderThread(nullptr, decoder_cache_, shader_cache_, this);
audio_thread_ = new RenderThread(nullptr, decoder_cache_, shader_cache_, this);
video_thread_->start(QThread::IdlePriority);
dry_run_thread_->start(QThread::IdlePriority);
audio_thread_->start(QThread::IdlePriority);
}
@@ -73,6 +76,9 @@ RenderManager::~RenderManager()
video_thread_->quit();
video_thread_->wait();
dry_run_thread_->quit();
dry_run_thread_->wait();
context_->PostDestroy();
delete context_;
@@ -129,7 +135,11 @@ RenderTicketPtr RenderManager::RenderFrame(Node *node, ColorManager* color_manag
ticket->setProperty("cacheuuid", QVariant::fromValue(cache->GetUuid()));
}
video_thread_->AddTicket(ticket);
if (return_type == ReturnType::kNull) {
dry_run_thread_->AddTicket(ticket);
} else {
video_thread_->AddTicket(ticket);
}
return ticket;
}
@@ -157,6 +167,8 @@ bool RenderManager::RemoveTicket(RenderTicketPtr ticket)
return true;
} else if (audio_thread_->RemoveTicket(ticket)) {
return true;
} else if (dry_run_thread_->RemoveTicket(ticket)) {
return true;
} else {
return false;
}
+5 -1
View File
@@ -97,9 +97,12 @@ public:
enum ReturnType {
kTexture,
kFrame
kFrame,
kNull
};
static const rational kDryRunInterval;
/**
* @brief Asynchronously generate a frame at a given time
*
@@ -168,6 +171,7 @@ private:
QTimer *decoder_clear_timer_;
RenderThread *video_thread_;
RenderThread *dry_run_thread_;
RenderThread *audio_thread_;
private slots:
+58 -51
View File
@@ -123,7 +123,7 @@ FramePtr RenderProcessor::GenerateFrame(TexturePtr texture, const rational& time
render_ctx_->Flush();
render_ctx_->DownloadFromTexture(texture.get(), frame->data(), frame->linesize_pixels());
render_ctx_->DownloadFromTexture(texture->id(), texture->params(), frame->data(), frame->linesize_pixels());
}
return frame;
@@ -151,53 +151,57 @@ void RenderProcessor::Run()
TexturePtr texture = GenerateTexture(time, frame_length);
if (GetCacheVideoParams().interlacing() != VideoParams::kInterlaceNone) {
// Get next between frame and interlace it
TexturePtr top = texture;
TexturePtr bottom = GenerateTexture(time + frame_length, frame_length);
if (GetCacheVideoParams().interlacing() == VideoParams::kInterlacedBottomFirst) {
std::swap(top, bottom);
}
texture = render_ctx_->InterlaceTexture(top, bottom, GetCacheVideoParams());
}
if (HeardCancel()) {
// Finish cancelled ticket with nothing since we can't guarantee the frame we generated
// is actually "complete
if (!render_ctx_) {
ticket_->Finish();
} else {
RenderManager::ReturnType return_type = RenderManager::ReturnType(ticket_->property("return").toInt());
if (GetCacheVideoParams().interlacing() != VideoParams::kInterlaceNone) {
// Get next between frame and interlace it
TexturePtr top = texture;
TexturePtr bottom = GenerateTexture(time + frame_length, frame_length);
FramePtr frame;
QString cache = ticket_->property("cache").toString();
if (return_type == RenderManager::kFrame || !cache.isEmpty()) {
// Convert to CPU frame
frame = GenerateFrame(texture, time);
// Save to cache if requested
if (!cache.isEmpty()) {
rational timebase = ticket_->property("cachetimebase").value<rational>();
QUuid uuid = ticket_->property("cacheuuid").value<QUuid>();
bool cache_result = FrameHashCache::SaveCacheFrame(cache, uuid, time, timebase, frame);
ticket_->setProperty("cached", cache_result);
if (GetCacheVideoParams().interlacing() == VideoParams::kInterlacedBottomFirst) {
std::swap(top, bottom);
}
texture = render_ctx_->InterlaceTexture(top, bottom, GetCacheVideoParams());
}
if (return_type == RenderManager::kTexture) {
// Return GPU texture
if (!texture) {
texture = render_ctx_->CreateTexture(GetCacheVideoParams());
render_ctx_->ClearDestination(texture.get());
if (HeardCancel()) {
// Finish cancelled ticket with nothing since we can't guarantee the frame we generated
// is actually "complete
ticket_->Finish();
} else {
RenderManager::ReturnType return_type = RenderManager::ReturnType(ticket_->property("return").toInt());
FramePtr frame;
QString cache = ticket_->property("cache").toString();
if (return_type == RenderManager::kFrame || !cache.isEmpty()) {
// Convert to CPU frame
frame = GenerateFrame(texture, time);
// Save to cache if requested
if (!cache.isEmpty()) {
rational timebase = ticket_->property("cachetimebase").value<rational>();
QUuid uuid = ticket_->property("cacheuuid").value<QUuid>();
bool cache_result = FrameHashCache::SaveCacheFrame(cache, uuid, time, timebase, frame);
ticket_->setProperty("cached", cache_result);
}
}
render_ctx_->Flush();
if (return_type == RenderManager::kTexture) {
// Return GPU texture
if (!texture) {
texture = render_ctx_->CreateTexture(GetCacheVideoParams());
render_ctx_->ClearDestination(texture.get());
}
ticket_->Finish(QVariant::fromValue(texture));
} else {
ticket_->Finish(QVariant::fromValue(frame));
render_ctx_->Flush();
ticket_->Finish(QVariant::fromValue(texture));
} else {
ticket_->Finish(QVariant::fromValue(frame));
}
}
}
break;
@@ -265,6 +269,11 @@ DecoderPtr RenderProcessor::ResolveDecoderFromInput(const QString& decoder_id, c
<< "::" << stream.stream();
return nullptr;
}
if (!render_ctx_) {
// Assume dry run and increment access time
decoder.decoder->IncrementAccessTime(RenderManager::kDryRunInterval.toDouble() * 1000);
}
}
return decoder.decoder;
@@ -404,10 +413,6 @@ NodeValueTable RenderProcessor::GenerateBlockTable(const Track *track, const Tim
void RenderProcessor::ProcessVideoFootage(TexturePtr destination, const FootageJob &stream, const rational &input_time)
{
if (!render_ctx_) {
return;
}
if (ticket_->property("type").value<RenderManager::TicketType>() != RenderManager::kTypeVideo) {
// Video cannot contribute to audio, so we do nothing here
return;
@@ -440,21 +445,23 @@ void RenderProcessor::ProcessVideoFootage(TexturePtr destination, const FootageJ
break;
case VideoParams::kVideoTypeImageSequence:
{
// Since image sequences involve multiple files, we don't engage the decoder cache
decoder = Decoder::CreateFromID(decoder_id);
if (render_ctx_) {
// Since image sequences involve multiple files, we don't engage the decoder cache
decoder = Decoder::CreateFromID(decoder_id);
QString frame_filename;
QString frame_filename;
int64_t frame_number = stream_data.get_time_in_timebase_units(input_time);
frame_filename = Decoder::TransformImageSequenceFileName(stream.filename(), frame_number);
int64_t frame_number = stream_data.get_time_in_timebase_units(input_time);
frame_filename = Decoder::TransformImageSequenceFileName(stream.filename(), frame_number);
// Decoder will close automatically since it's a stream_ptr
decoder->Open(Decoder::CodecStream(frame_filename, stream_data.stream_index(), GetCurrentBlock()));
// Decoder will close automatically since it's a stream_ptr
decoder->Open(Decoder::CodecStream(frame_filename, stream_data.stream_index(), GetCurrentBlock()));
}
break;
}
}
if (decoder) {
if (decoder && render_ctx_) {
Decoder::RetrieveVideoParams p;
p.divider = stream.video_params().divider();
p.maximum_format = destination->format();
+3 -3
View File
@@ -29,21 +29,21 @@ const Texture::Interpolation Texture::kDefaultInterpolation = Texture::kMipmappe
Texture::~Texture()
{
if (renderer_) {
renderer_->DestroyNativeTexture(id_);
renderer_->DestroyTexture(this);
}
}
void Texture::Upload(void *data, int linesize)
{
if (renderer_) {
renderer_->UploadToTexture(this, data, linesize);
renderer_->UploadToTexture(this->id(), this->params(), data, linesize);
}
}
void Texture::Download(void *data, int linesize)
{
if (renderer_) {
renderer_->DownloadFromTexture(this, data, linesize);
renderer_->DownloadFromTexture(this->id(), this->params(), data, linesize);
}
}
+3 -17
View File
@@ -32,11 +32,6 @@ class Renderer;
class Texture
{
public:
enum Type {
k2D,
k3D
};
enum Interpolation {
kNearest,
kLinear,
@@ -50,19 +45,17 @@ public:
*/
Texture(const VideoParams& param) :
renderer_(nullptr),
params_(param),
type_(k2D)
params_(param)
{
}
/**
* @brief Construct a real texture linked to a renderer backend
*/
Texture(Renderer* renderer, const QVariant& native, const VideoParams& param, Type type) :
Texture(Renderer* renderer, const QVariant& native, const VideoParams& param) :
renderer_(renderer),
params_(param),
id_(native),
type_(type)
id_(native)
{
}
@@ -117,11 +110,6 @@ public:
return params_.pixel_aspect_ratio();
}
Type type() const
{
return type_;
}
Renderer* renderer() const
{
return renderer_;
@@ -134,8 +122,6 @@ private:
QVariant id_;
Type type_;
};
using TexturePtr = std::shared_ptr<Texture>;
+2
View File
@@ -121,6 +121,8 @@ public:
calculate_effective_size();
}
bool is_3d() const { return depth_ > 1; }
const rational& time_base() const
{
return time_base_;
@@ -62,7 +62,11 @@ ManagedDisplayWidget::ManagedDisplayWidget(QWidget *parent) :
attached_renderer_ = new OpenGLRenderer(this);
// Create widget wrapper for OpenGL window
#ifdef USE_QOPENGLWINDOW
wrapper_ = QWidget::createWindowContainer(static_cast<ManagedDisplayWidgetOpenGL*>(inner_widget_));
#else
wrapper_ = inner_widget_;
#endif
layout->addWidget(wrapper_);
} else {
inner_widget_ = nullptr;
+22 -2
View File
@@ -21,9 +21,15 @@
#ifndef MANAGEDDISPLAYOBJECT_H
#define MANAGEDDISPLAYOBJECT_H
#define USE_QOPENGLWINDOW
#include <QMouseEvent>
#include <QOpenGLContext>
#ifdef USE_QOPENGLWINDOW
#include <QOpenGLWindow>
#else
#include <QOpenGLWidget>
#endif
#include "node/color/colormanager/colormanager.h"
#include "render/renderer.h"
@@ -31,7 +37,12 @@
namespace olive {
class ManagedDisplayWidgetOpenGL : public QOpenGLWindow
class ManagedDisplayWidgetOpenGL
#ifdef USE_QOPENGLWINDOW
: public QOpenGLWindow
#else
: public QOpenGLWidget
#endif
{
Q_OBJECT
public:
@@ -175,7 +186,12 @@ protected:
void doneCurrent();
QWindow* inner_widget() const
#ifdef USE_QOPENGLWINDOW
QWindow*
#else
QWidget*
#endif
inner_widget() const
{
return inner_widget_;
}
@@ -225,7 +241,11 @@ private:
/**
* @brief Main drawing surface abstraction
*/
#ifdef USE_QOPENGLWINDOW
QWindow* inner_widget_;
#else
QWidget* inner_widget_;
#endif
QWidget *wrapper_;
/**
@@ -653,9 +653,11 @@ void ProjectExplorer::DeleteSelected()
}
}
bool ProjectExplorer::SelectItem(Node *n)
bool ProjectExplorer::SelectItem(Node *n, bool deselect_all_first)
{
DeselectAll();
if (deselect_all_first) {
DeselectAll();
}
QModelIndex index = model_.CreateIndexFromItem(n);
+1 -1
View File
@@ -85,7 +85,7 @@ public:
void DeleteSelected();
bool SelectItem(Node *n);
bool SelectItem(Node *n, bool deselect_all_first = true);
public slots:
void set_view_type(ProjectToolbar::ViewType type);
@@ -86,6 +86,9 @@ TimelineWidget::TimelineWidget(QWidget *parent) :
ruler_and_time_layout->addWidget(ruler());
ruler()->setFocusPolicy(Qt::TabFocus);
QWidget::setTabOrder(ruler(), timecode_label_);
// Create list of TimelineViews - these MUST correspond to the ViewType enum
view_splitter_ = new QSplitter(Qt::Vertical);
@@ -63,8 +63,15 @@ void TrackRippleRemoveAreaCommand::prepare()
// If it's getting trimmed, determine if it's actually getting spliced
if (first_block_is_out_trimmed && first_block_is_in_trimmed) {
// This block is getting spliced, so we'll handle that later
splice_split_command_ = new BlockSplitCommand(first_block, range_.in());
if (dynamic_cast<GapBlock*>(first_block)) {
// As a rule, we don't split gaps, so we just treat it as a trim of the range requested
trim_out_ = {first_block,
first_block->length(),
first_block->length() - range_.length()};
} else {
// This block is getting spliced, so we'll handle that later
splice_split_command_ = new BlockSplitCommand(first_block, range_.in());
}
} else {
// It's just getting trimmed or removed, so we'll append that operation
if (first_block_is_out_trimmed) {
+35 -1
View File
@@ -560,6 +560,35 @@ void ViewerWidget::ShowSubtitleProperties()
}
}
void ViewerWidget::DryRunFinished()
{
RenderTicketWatcher *w = static_cast<RenderTicketWatcher*>(sender());
if (dry_run_watchers_.contains(w)) {
RequestNextDryRun();
}
delete w;
}
void ViewerWidget::RequestNextDryRun()
{
if (IsPlaying()) {
rational next_time = Timecode::timestamp_to_time(dry_run_next_frame_, timebase());
if (FrameExistsAtTime(next_time)) {
if (next_time > GetTime() + RenderManager::kDryRunInterval) {
QTimer::singleShot(timebase().toDouble() / playback_speed_, this, &ViewerWidget::RequestNextDryRun);
} else {
RenderTicketWatcher *watcher = new RenderTicketWatcher(this);
connect(watcher, &RenderTicketWatcher::Finished, this, &ViewerWidget::DryRunFinished);
watcher->SetTicket(auto_cacher_.GetSingleFrame(next_time, true));
dry_run_next_frame_ += playback_speed_;
dry_run_watchers_.append(watcher);
}
}
}
}
void ViewerWidget::CloseAudioProcessor()
{
audio_processor_.Close();
@@ -828,6 +857,9 @@ void ViewerWidget::PlayInternal(int speed, bool in_to_out_only)
for (int i=0; i<prequeue_length_; i++) {
RequestNextFrameForQueue();
}
dry_run_next_frame_ = playback_queue_next_frame_;
RequestNextDryRun();
}
}
@@ -845,6 +877,7 @@ void ViewerWidget::PlayInternal(int speed, bool in_to_out_only)
QueueNextAudioBuffer();
}
}
// Force screen to stay awake
PreventSleep(true);
}
@@ -893,6 +926,7 @@ void ViewerWidget::PauseInternal()
prequeuing_video_ = false;
prequeuing_audio_ = 0;
dry_run_watchers_.clear();
// Reset screen timeout timer
PreventSleep(false);
@@ -1048,7 +1082,7 @@ int ViewerWidget::DeterminePlaybackQueueSize()
end_ts = 0;
}
int remaining_frames = (end_ts - GetTimestamp()) / playback_speed_;
int remaining_frames = (end_ts - GetTimestamp() - 1) / playback_speed_;
// Generate maximum queue
int max_frames = qCeil(kVideoPlaybackInterval.toDouble() / timebase().toDouble());
+7
View File
@@ -256,6 +256,7 @@ private:
QTimer playback_backup_timer_;
int64_t playback_queue_next_frame_;
int64_t dry_run_next_frame_;
QVector<ViewerDisplayWidget*> playback_devices_;
bool prequeuing_video_;
@@ -294,6 +295,8 @@ private:
WaveformMode waveform_mode_;
QVector<RenderTicketWatcher*> dry_run_watchers_;
private slots:
void PlaybackTimerUpdate();
@@ -354,6 +357,10 @@ private slots:
void ShowSubtitleProperties();
void DryRunFinished();
void RequestNextDryRun();
};
}
+88 -78
View File
@@ -456,84 +456,7 @@ void ViewerDisplayWidget::OnPaint()
}
// Extraordinarily basic subtitle renderer. Hoping to swap this out with libass at some point.
if (show_subtitles_ && subtitle_tracks_) {
const QVector<Track*> &subtitle_tracklist = subtitle_tracks_->track_list(Track::kSubtitle)->GetTracks();
if (!subtitle_tracklist.empty()) {
QPainterPath path;
QTransform transform = GenerateWorldTransform();
QRect bounding_box = transform.mapRect(rect());
QFont f;
qreal font_sz = OLIVE_CONFIG("DefaultSubtitleSize").toInt();
{
// Scale font size by transform
QTransform display_transform = GenerateDisplayTransform();
font_sz *= display_transform.m11();
}
f.setPointSizeF(font_sz);
QString family = OLIVE_CONFIG("DefaultSubtitleFamily").toString();
if (!family.isEmpty()) {
f.setFamily(family);
}
f.setWeight(OLIVE_CONFIG("DefaultSubtitleWeight").toInt());
bounding_box.adjust(bounding_box.width()/10, bounding_box.height()/10, -bounding_box.width()/10, -bounding_box.height()/10);
QFontMetrics fm(f);
for (int j=subtitle_tracklist.size()-1; j>=0; j--) {
Track *sub_track = subtitle_tracklist.at(j);
if (!sub_track->IsMuted()) {
if (SubtitleBlock *sub = dynamic_cast<SubtitleBlock*>(sub_track->BlockAtTime(time_))) {
// Split into lines
QStringList list = QtUtils::WordWrapString(sub->GetText(), fm, bounding_box.width());
for (int i=list.size()-1; i>=0; i--) {
int w = QtUtils::QFontMetricsWidth(fm, list.at(i));
path.addText(bounding_box.width()/2 - w/2, bounding_box.height() - fm.height() * (list.size() - i) + fm.ascent(), f, list.at(i));
}
}
}
}
bool antialias = OLIVE_CONFIG("AntialiasSubtitles").toBool();
QPixmap *aa_pixmap;
QPainter *text_painter;
if (antialias) {
// QPainter only supports anti-aliasing in software, so to achieve it, we draw to a
// software buffer first and then draw that onto the hardware
aa_pixmap = new QPixmap(bounding_box.width(), bounding_box.height());
aa_pixmap->fill(Qt::transparent);
text_painter = new QPainter(aa_pixmap);
} else {
// Just draw straight to the hardware
text_painter = new QPainter(paint_device());
// Offset path by however much is necessary
path.translate(bounding_box.x(), bounding_box.y());
}
text_painter->setPen(QPen(Qt::black, f.pointSizeF() / 16));
text_painter->setBrush(Qt::white);
text_painter->setRenderHint(QPainter::Antialiasing);
text_painter->drawPath(path);
delete text_painter;
if (antialias) {
// We just drew to a software buffer, now draw this image onto the hardware device
QPainter p(paint_device());
p.drawPixmap(bounding_box.x(), bounding_box.y(), *aa_pixmap);
delete aa_pixmap;
}
}
}
DrawSubtitleTracks();
if (add_band_) {
QPainter p(paint_device());
@@ -974,6 +897,93 @@ void ViewerDisplayWidget::EmitColorAtCursor(QMouseEvent *e)
}
}
void ViewerDisplayWidget::DrawSubtitleTracks()
{
if (!show_subtitles_ || !subtitle_tracks_) {
return;
}
const QVector<Track*> &subtitle_tracklist = subtitle_tracks_->track_list(Track::kSubtitle)->GetTracks();
if (subtitle_tracklist.empty()) {
return;
}
// Scale font size by transform
QTransform display_transform = GenerateDisplayTransform();
qreal font_sz = OLIVE_CONFIG("DefaultSubtitleSize").toInt();
font_sz *= display_transform.m11();
if (qIsNaN(font_sz)) {
return;
}
QPainterPath path;
QTransform transform = GenerateWorldTransform();
QRect bounding_box = transform.mapRect(rect());
QFont f;
f.setPointSizeF(font_sz);
QString family = OLIVE_CONFIG("DefaultSubtitleFamily").toString();
if (!family.isEmpty()) {
f.setFamily(family);
}
f.setWeight(OLIVE_CONFIG("DefaultSubtitleWeight").toInt());
bounding_box.adjust(bounding_box.width()/10, bounding_box.height()/10, -bounding_box.width()/10, -bounding_box.height()/10);
QFontMetrics fm(f);
for (int j=subtitle_tracklist.size()-1; j>=0; j--) {
Track *sub_track = subtitle_tracklist.at(j);
if (!sub_track->IsMuted()) {
if (SubtitleBlock *sub = dynamic_cast<SubtitleBlock*>(sub_track->BlockAtTime(time_))) {
// Split into lines
QStringList list = QtUtils::WordWrapString(sub->GetText(), fm, bounding_box.width());
for (int i=list.size()-1; i>=0; i--) {
int w = QtUtils::QFontMetricsWidth(fm, list.at(i));
path.addText(bounding_box.width()/2 - w/2, bounding_box.height() - fm.height() * (list.size() - i) + fm.ascent(), f, list.at(i));
}
}
}
}
bool antialias = OLIVE_CONFIG("AntialiasSubtitles").toBool();
QPixmap *aa_pixmap;
QPainter *text_painter;
if (antialias) {
// QPainter only supports anti-aliasing in software, so to achieve it, we draw to a
// software buffer first and then draw that onto the hardware
aa_pixmap = new QPixmap(bounding_box.width(), bounding_box.height());
aa_pixmap->fill(Qt::transparent);
text_painter = new QPainter(aa_pixmap);
} else {
// Just draw straight to the hardware
text_painter = new QPainter(paint_device());
// Offset path by however much is necessary
path.translate(bounding_box.x(), bounding_box.y());
}
text_painter->setPen(QPen(Qt::black, f.pointSizeF() / 16));
text_painter->setBrush(Qt::white);
text_painter->setRenderHint(QPainter::Antialiasing);
text_painter->drawPath(path);
delete text_painter;
if (antialias) {
// We just drew to a software buffer, now draw this image onto the hardware device
QPainter p(paint_device());
p.drawPixmap(bounding_box.x(), bounding_box.y(), *aa_pixmap);
delete aa_pixmap;
}
}
void ViewerDisplayWidget::SetShowFPS(bool e)
{
show_fps_ = e;
+2
View File
@@ -263,6 +263,8 @@ private:
void EmitColorAtCursor(QMouseEvent* e);
void DrawSubtitleTracks();
/**
* @brief Internal reference to the OpenGL texture to draw. Set in SetTexture() and used in paintGL().
*/
+20
View File
@@ -410,6 +410,16 @@ void MainWindow::SetApplicationProgressValue(int value)
#endif
}
void MainWindow::SelectFootage(const QVector<Footage *> &e)
{
for (ProjectPanel *p : project_panels_) {
SelectFootageForProjectPanel(e, p);
}
for (ProjectPanel *p : folder_panels_) {
SelectFootageForProjectPanel(e, p);
}
}
void MainWindow::closeEvent(QCloseEvent *e)
{
// Try to close all projects (this will return false if the user chooses not to close)
@@ -759,6 +769,16 @@ void MainWindow::UpdateNodePanelContextFromTimelinePanel(TimelinePanel *panel)
param_panel_->SetContexts(context);
}
void MainWindow::SelectFootageForProjectPanel(const QVector<Footage *> &e, ProjectPanel *p)
{
p->DeselectAll();
for (Footage *f : e) {
if (p->get_root()->HasChildRecursive(f)) {
p->SelectItem(f, false);
}
}
}
void MainWindow::FocusedPanelChanged(PanelWidget *panel)
{
// Update audio monitor panel
+4
View File
@@ -92,6 +92,8 @@ public:
*/
void SetApplicationProgressValue(int value);
void SelectFootage(const QVector<Footage*> &e);
public slots:
void ProjectOpen(Project *p);
@@ -142,6 +144,8 @@ private:
void UpdateNodePanelContextFromTimelinePanel(TimelinePanel *panel);
void SelectFootageForProjectPanel(const QVector<Footage*> &e, ProjectPanel *p);
QByteArray premaximized_state_;
// Standard panels