fixed various caching issues when rapidly changing values
This commit is contained in:
@@ -337,6 +337,7 @@ void Core::DeclareTypesForQt()
|
||||
qRegisterMetaType<Task::Status>("Task::Status");
|
||||
qRegisterMetaType<NodeDependency>();
|
||||
qRegisterMetaType<rational>();
|
||||
qRegisterMetaType<RenderTexturePtr>();
|
||||
}
|
||||
|
||||
void Core::StartGUI(bool full_screen)
|
||||
|
||||
@@ -251,6 +251,7 @@ FramePtr FFmpegDecoder::Retrieve(const rational &timecode, const rational &lengt
|
||||
frame_container->set_height(frame_->height);
|
||||
frame_container->set_format(output_fmt_);
|
||||
frame_container->set_timestamp(rational(frame_->pts * avstream_->time_base.num, avstream_->time_base.den));
|
||||
frame_container->set_native_timestamp(frame_->pts);
|
||||
frame_container->allocate();
|
||||
|
||||
// Convert pixel format/linesize if necessary
|
||||
|
||||
+14
-1
@@ -27,7 +27,10 @@
|
||||
|
||||
Frame::Frame() :
|
||||
width_(0),
|
||||
height_(0)
|
||||
height_(0),
|
||||
format_(-1),
|
||||
timestamp_(0),
|
||||
native_timestamp_(0)
|
||||
{
|
||||
}
|
||||
|
||||
@@ -66,6 +69,16 @@ void Frame::set_timestamp(const rational ×tamp)
|
||||
timestamp_ = timestamp;
|
||||
}
|
||||
|
||||
const int64_t &Frame::native_timestamp()
|
||||
{
|
||||
return native_timestamp_;
|
||||
}
|
||||
|
||||
void Frame::set_native_timestamp(const int64_t ×tamp)
|
||||
{
|
||||
native_timestamp_ = timestamp;
|
||||
}
|
||||
|
||||
const int &Frame::format()
|
||||
{
|
||||
return format_;
|
||||
|
||||
@@ -65,6 +65,9 @@ public:
|
||||
const rational& timestamp();
|
||||
void set_timestamp(const rational& timestamp);
|
||||
|
||||
const int64_t& native_timestamp();
|
||||
void set_native_timestamp(const int64_t& timestamp);
|
||||
|
||||
/**
|
||||
* @brief Get frame's format
|
||||
*
|
||||
@@ -110,6 +113,8 @@ private:
|
||||
|
||||
rational timestamp_;
|
||||
|
||||
int64_t native_timestamp_;
|
||||
|
||||
};
|
||||
|
||||
#endif // FRAME_H
|
||||
|
||||
@@ -109,7 +109,7 @@ void Block::EdgeAddedSlot(NodeEdgePtr edge)
|
||||
RefreshFollowing();
|
||||
|
||||
// Entire track will have shifted, so the whole cache needs to be re-validated
|
||||
InvalidateCache(0, RATIONAL_MAX);
|
||||
SendInvalidateCache(0, RATIONAL_MAX);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -182,7 +182,7 @@ void Block::set_media_in(const rational &media_in)
|
||||
media_in_ = media_in;
|
||||
|
||||
// Signal that this clips contents have changed
|
||||
InvalidateCache(in(), out());
|
||||
SendInvalidateCache(in(), out());
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -69,11 +69,9 @@ NodeInput *ClipBlock::texture_input()
|
||||
|
||||
QVariant ClipBlock::Value(NodeOutput* param, const rational& time)
|
||||
{
|
||||
QVariant value = Block::Value(param, time);
|
||||
|
||||
if (param == texture_output()) {
|
||||
// If the time retrieved is within this block, get texture information
|
||||
if (time >= in() && time < out()) {
|
||||
if (texture_input()->IsConnected() && time >= in() && time < out()) {
|
||||
// We convert the time given (timeline time) to media time
|
||||
rational media_time = SequenceToMediaTime(time);
|
||||
|
||||
|
||||
@@ -29,6 +29,8 @@ OpacityNode::OpacityNode()
|
||||
opacity_input_ = new NodeInput("opacity_in");
|
||||
opacity_input_->add_data_input(NodeParam::kFloat);
|
||||
opacity_input_->set_value(100);
|
||||
opacity_input_->set_minimum(0);
|
||||
opacity_input_->set_maximum(100);
|
||||
AddParameter(opacity_input_);
|
||||
|
||||
texture_input_ = new NodeInput("tex_in");
|
||||
|
||||
+36
-2
@@ -26,7 +26,9 @@
|
||||
NodeInput::NodeInput(const QString& id) :
|
||||
NodeParam(id),
|
||||
keyframing_(false),
|
||||
dependent_(true)
|
||||
dependent_(true),
|
||||
has_minimum_(false),
|
||||
has_maximum_(false)
|
||||
{
|
||||
// Have at least one keyframe/value active at any time
|
||||
keyframes_.append(NodeKeyframe());
|
||||
@@ -71,7 +73,7 @@ QVariant NodeInput::get_value(const rational& time)
|
||||
{
|
||||
QVariant v;
|
||||
|
||||
if (time_ != time) {
|
||||
if (time_ != time || !value_caching_) {
|
||||
// Retrieve the value
|
||||
if (!edges_.isEmpty()) {
|
||||
// A connection - use the output of the connected Node
|
||||
@@ -128,6 +130,38 @@ void NodeInput::set_dependent(bool d)
|
||||
dependent_ = d;
|
||||
}
|
||||
|
||||
const QVariant &NodeInput::minimum()
|
||||
{
|
||||
return minimum_;
|
||||
}
|
||||
|
||||
bool NodeInput::has_minimum()
|
||||
{
|
||||
return has_minimum_;
|
||||
}
|
||||
|
||||
void NodeInput::set_minimum(const QVariant &min)
|
||||
{
|
||||
minimum_ = min;
|
||||
has_minimum_ = true;
|
||||
}
|
||||
|
||||
const QVariant &NodeInput::maximum()
|
||||
{
|
||||
return maximum_;
|
||||
}
|
||||
|
||||
bool NodeInput::has_maximum()
|
||||
{
|
||||
return has_maximum_;
|
||||
}
|
||||
|
||||
void NodeInput::set_maximum(const QVariant &max)
|
||||
{
|
||||
maximum_ = max;
|
||||
has_maximum_ = true;
|
||||
}
|
||||
|
||||
NodeParam::DataType NodeInput::data_type()
|
||||
{
|
||||
if (inputs_.isEmpty()) {
|
||||
|
||||
@@ -124,6 +124,14 @@ public:
|
||||
*/
|
||||
void set_dependent(bool d);
|
||||
|
||||
const QVariant& minimum();
|
||||
bool has_minimum();
|
||||
void set_minimum(const QVariant& min);
|
||||
|
||||
const QVariant& maximum();
|
||||
bool has_maximum();
|
||||
void set_maximum(const QVariant& max);
|
||||
|
||||
virtual DataType data_type() override;
|
||||
|
||||
/**
|
||||
@@ -165,6 +173,26 @@ private:
|
||||
*/
|
||||
bool dependent_;
|
||||
|
||||
/**
|
||||
* @brief Sets whether this param has a minimum value or not
|
||||
*/
|
||||
bool has_minimum_;
|
||||
|
||||
/**
|
||||
* @brief Internal minimum value
|
||||
*/
|
||||
QVariant minimum_;
|
||||
|
||||
/**
|
||||
* @brief Sets whether this param has a maximum value or not
|
||||
*/
|
||||
bool has_maximum_;
|
||||
|
||||
/**
|
||||
* @brief Internal maximum value
|
||||
*/
|
||||
QVariant maximum_;
|
||||
|
||||
};
|
||||
|
||||
#endif // NODEINPUT_H
|
||||
|
||||
@@ -34,7 +34,8 @@ MediaInput::MediaInput() :
|
||||
decoder_(nullptr),
|
||||
color_service_(nullptr),
|
||||
pipeline_(nullptr),
|
||||
ocio_texture_(0)
|
||||
ocio_texture_(0),
|
||||
frame_(nullptr)
|
||||
{
|
||||
footage_input_ = new NodeInput("footage_in");
|
||||
footage_input_->add_data_input(NodeInput::kFootage);
|
||||
@@ -46,6 +47,7 @@ MediaInput::MediaInput() :
|
||||
|
||||
texture_output_ = new NodeOutput("tex_out");
|
||||
texture_output_->set_data_type(NodeOutput::kTexture);
|
||||
texture_output_->SetValueCachingEnabled(false);
|
||||
AddParameter(texture_output_);
|
||||
}
|
||||
|
||||
@@ -73,6 +75,7 @@ void MediaInput::Release()
|
||||
{
|
||||
internal_tex_.Destroy();
|
||||
|
||||
frame_ = nullptr;
|
||||
decoder_ = nullptr;
|
||||
color_service_ = nullptr;
|
||||
pipeline_ = nullptr;
|
||||
@@ -128,62 +131,67 @@ QVariant MediaInput::Value(NodeOutput *output, const rational &time)
|
||||
return 0;
|
||||
}
|
||||
|
||||
// Get frame from Decoder
|
||||
FramePtr frame = decoder_->Retrieve(time);
|
||||
// Check if we need to get a frame or not
|
||||
if (frame_ == nullptr || frame_native_ts_ != decoder_->GetTimestampFromTime(time)) {
|
||||
// Get frame from Decoder
|
||||
frame_ = decoder_->Retrieve(time);
|
||||
|
||||
if (frame == nullptr) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
if (color_service_ == nullptr) {
|
||||
// FIXME: Hardcoded values for testing
|
||||
color_service_ = std::make_shared<ColorService>("srgb", OCIO::ROLE_SCENE_LINEAR);
|
||||
}
|
||||
|
||||
// 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) {
|
||||
// Unassociate alpha here if associated
|
||||
ColorService::DisassociateAlpha(frame);
|
||||
if (frame_ == nullptr) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
// Transform color to reference space
|
||||
color_service_->ConvertFrame(frame);
|
||||
frame_native_ts_ = frame_->native_timestamp();
|
||||
|
||||
if (alpha_is_associated) {
|
||||
// If alpha was associated, reassociate here
|
||||
ColorService::ReassociateAlpha(frame);
|
||||
if (color_service_ == nullptr) {
|
||||
// FIXME: Hardcoded values for testing
|
||||
color_service_ = std::make_shared<ColorService>("srgb", OCIO::ROLE_SCENE_LINEAR);
|
||||
}
|
||||
|
||||
// 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) {
|
||||
// Unassociate alpha here if associated
|
||||
ColorService::DisassociateAlpha(frame_);
|
||||
}
|
||||
|
||||
// Transform color to reference space
|
||||
color_service_->ConvertFrame(frame_);
|
||||
|
||||
if (alpha_is_associated) {
|
||||
// If alpha was associated, reassociate here
|
||||
ColorService::ReassociateAlpha(frame_);
|
||||
} else {
|
||||
// If alpha was not associated, associate here
|
||||
ColorService::AssociateAlpha(frame_);
|
||||
}
|
||||
}
|
||||
|
||||
// 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 {
|
||||
// If alpha was not associated, associate here
|
||||
ColorService::AssociateAlpha(frame);
|
||||
internal_tex_.Upload(frame_->data());
|
||||
}
|
||||
}
|
||||
|
||||
// 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>();
|
||||
|
||||
@@ -53,8 +53,6 @@ public:
|
||||
|
||||
virtual void Hash(QCryptographicHash *hash, NodeOutput* from, const rational &time) override;
|
||||
|
||||
|
||||
|
||||
protected:
|
||||
virtual QVariant Value(NodeOutput* output, const rational& time) override;
|
||||
|
||||
@@ -78,6 +76,9 @@ private:
|
||||
QOpenGLContext* ocio_ctx_;
|
||||
GLuint ocio_texture_;
|
||||
|
||||
FramePtr frame_;
|
||||
int64_t frame_native_ts_;
|
||||
|
||||
};
|
||||
|
||||
#endif // IMAGE_H
|
||||
|
||||
+24
-16
@@ -71,30 +71,28 @@ void Node::InvalidateCache(const rational &start_range, const rational &end_rang
|
||||
{
|
||||
Q_UNUSED(from)
|
||||
|
||||
ClearCachedValuesInParameters(start_range, end_range);
|
||||
|
||||
SendInvalidateCache(start_range, end_range);
|
||||
}
|
||||
|
||||
void Node::SendInvalidateCache(const rational &start_range, const rational &end_range)
|
||||
{
|
||||
QList<NodeParam *> params = parameters();
|
||||
|
||||
// Loop through all parameters (there should be no children that are not NodeParams)
|
||||
foreach (NodeParam* param, params) {
|
||||
|
||||
// If the Node is an output, relay the signal to any Nodes that are connected to it
|
||||
if (param->type() == NodeParam::kOutput) {
|
||||
|
||||
QVector<NodeEdgePtr> edges = param->edges();
|
||||
|
||||
foreach (NodeEdgePtr edge, edges) {
|
||||
|
||||
NodeInput* connected_input = edge->input();
|
||||
Node* connected_node = connected_input->parent();
|
||||
|
||||
// Only send this signal if the Node isn't ignoring invalidate cache signals from this input
|
||||
if (!connected_node->ignore_invalid_cache_inputs_.contains(connected_input)) {
|
||||
// Clear values cached in the parameters
|
||||
connected_input->ClearCachedValue();
|
||||
edge->output()->ClearCachedValue();
|
||||
|
||||
// Send clear cache signal to the Node
|
||||
connected_node->InvalidateCache(start_range, end_range, connected_input);
|
||||
}
|
||||
// Send clear cache signal to the Node
|
||||
connected_node->InvalidateCache(start_range, end_range, connected_input);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -130,11 +128,6 @@ void Node::CopyInputs(Node *source, Node *destination)
|
||||
}
|
||||
}
|
||||
|
||||
void Node::IgnoreCacheInvalidationFrom(NodeInput *input)
|
||||
{
|
||||
ignore_invalid_cache_inputs_.append(input);
|
||||
}
|
||||
|
||||
rational Node::LastProcessedTime()
|
||||
{
|
||||
rational t;
|
||||
@@ -161,6 +154,21 @@ NodeOutput *Node::LastProcessedOutput()
|
||||
return o;
|
||||
}
|
||||
|
||||
void Node::ClearCachedValuesInParameters(const rational &start_range, const rational &end_range)
|
||||
{
|
||||
Q_UNUSED(start_range)
|
||||
Q_UNUSED(end_range)
|
||||
|
||||
QList<NodeParam *> params = parameters();
|
||||
|
||||
// Loop through all parameters and clear cached values
|
||||
foreach (NodeParam* param, params) {
|
||||
//if (param->LastRequestedTime() >= start_range && param->LastRequestedTime() <= end_range) {
|
||||
param->ClearCachedValue();
|
||||
//}
|
||||
}
|
||||
}
|
||||
|
||||
QVariant Node::Run(NodeOutput* output, const rational& time)
|
||||
{
|
||||
return Value(output, time);
|
||||
|
||||
+4
-10
@@ -209,11 +209,6 @@ protected:
|
||||
*/
|
||||
void RemoveParameter(NodeParam* param);
|
||||
|
||||
/**
|
||||
* @brief If we receive a signal from NodeInput `input`, don't propagate it.
|
||||
*/
|
||||
void IgnoreCacheInvalidationFrom(NodeInput* input);
|
||||
|
||||
/**
|
||||
* @brief The main processing function
|
||||
*
|
||||
@@ -239,6 +234,10 @@ protected:
|
||||
*/
|
||||
NodeOutput* LastProcessedOutput();
|
||||
|
||||
void ClearCachedValuesInParameters(const rational& start_range, const rational& end_range);
|
||||
|
||||
void SendInvalidateCache(const rational& start_range, const rational& end_range);
|
||||
|
||||
public slots:
|
||||
|
||||
signals:
|
||||
@@ -266,11 +265,6 @@ private:
|
||||
*/
|
||||
bool HasParamWithID(const QString& id);
|
||||
|
||||
/**
|
||||
* @brief Internal list of inputs to ignore InvalidateCache() signals from
|
||||
*/
|
||||
QList<NodeInput*> ignore_invalid_cache_inputs_;
|
||||
|
||||
/**
|
||||
* @brief The last timecode Process() was called with
|
||||
*/
|
||||
|
||||
+7
-1
@@ -46,7 +46,7 @@ QVariant NodeOutput::get_value(const rational& time)
|
||||
{
|
||||
QVariant v;
|
||||
|
||||
if (time_ != time) {
|
||||
if (time_ != time || !value_caching_) {
|
||||
// Update the value
|
||||
value_ = parent()->Run(this, time);
|
||||
|
||||
@@ -58,3 +58,9 @@ QVariant NodeOutput::get_value(const rational& time)
|
||||
return v;
|
||||
}
|
||||
|
||||
void NodeOutput::push_value(const QVariant &v, const rational &time)
|
||||
{
|
||||
value_ = v;
|
||||
time_ = time;
|
||||
}
|
||||
|
||||
|
||||
@@ -63,6 +63,8 @@ public:
|
||||
*/
|
||||
virtual QVariant get_value(const rational &time);
|
||||
|
||||
void push_value(const QVariant& v, const rational& time);
|
||||
|
||||
private:
|
||||
DataType data_type_;
|
||||
|
||||
|
||||
@@ -21,8 +21,7 @@
|
||||
#include "viewer.h"
|
||||
|
||||
ViewerOutput::ViewerOutput() :
|
||||
attached_viewer_(nullptr),
|
||||
current_time_(0)
|
||||
attached_viewer_(nullptr)
|
||||
{
|
||||
texture_input_ = new NodeInput("tex_out");
|
||||
texture_input_->add_data_input(NodeInput::kTexture);
|
||||
@@ -68,6 +67,9 @@ void ViewerOutput::AttachViewer(ViewerPanel *viewer)
|
||||
// Disconnect old viewer if there's one attached
|
||||
if (attached_viewer_ != nullptr) {
|
||||
disconnect(attached_viewer_, SIGNAL(TimeChanged(const rational&)), this, SLOT(ViewerTimeChanged(const rational&)));
|
||||
|
||||
// Clear any existing texture
|
||||
attached_viewer_->SetTexture(0);
|
||||
}
|
||||
|
||||
// FIXME: Currently this attaches to ViewerPanels, but should it attached to Viewers instead?
|
||||
@@ -76,24 +78,28 @@ void ViewerOutput::AttachViewer(ViewerPanel *viewer)
|
||||
if (attached_viewer_ != nullptr) {
|
||||
connect(attached_viewer_, SIGNAL(TimeChanged(const rational&)), this, SLOT(ViewerTimeChanged(const rational&)));
|
||||
SetTimebase(timebase_);
|
||||
|
||||
// Update the texture
|
||||
ViewerTimeChanged(attached_viewer_->GetTime());
|
||||
}
|
||||
}
|
||||
|
||||
void ViewerOutput::InvalidateCache(const rational &start_range, const rational &end_range, NodeInput *from)
|
||||
{
|
||||
if (start_range == current_time_ || end_range == current_time_) {
|
||||
Node::InvalidateCache(start_range, end_range, from);
|
||||
|
||||
if (attached_viewer_ != nullptr
|
||||
&& (start_range == attached_viewer_->GetTime() || end_range == attached_viewer_->GetTime())) {
|
||||
// Update any attached viewer
|
||||
UpdateViewer();
|
||||
ForceUpdateViewer();
|
||||
}
|
||||
|
||||
Node::InvalidateCache(start_range, end_range, from);
|
||||
SendInvalidateCache(start_range, end_range);
|
||||
}
|
||||
|
||||
void ViewerOutput::UpdateViewer()
|
||||
void ViewerOutput::ForceUpdateViewer()
|
||||
{
|
||||
texture_input_->ClearCachedValue();
|
||||
|
||||
ViewerTimeChanged(current_time_);
|
||||
ViewerTimeChanged(attached_viewer_->GetTime());
|
||||
}
|
||||
|
||||
QVariant ViewerOutput::Value(NodeOutput *output, const rational &time)
|
||||
@@ -115,6 +121,4 @@ void ViewerOutput::ViewerTimeChanged(const rational &t)
|
||||
} else {
|
||||
attached_viewer_->SetTexture(0);
|
||||
}
|
||||
|
||||
current_time_ = t;
|
||||
}
|
||||
|
||||
@@ -53,7 +53,7 @@ protected:
|
||||
virtual QVariant Value(NodeOutput* output, const rational& time) override;
|
||||
|
||||
private:
|
||||
void UpdateViewer();
|
||||
void ForceUpdateViewer();
|
||||
|
||||
NodeInput* texture_input_;
|
||||
|
||||
@@ -61,8 +61,6 @@ private:
|
||||
|
||||
rational timebase_;
|
||||
|
||||
rational current_time_;
|
||||
|
||||
private slots:
|
||||
void ViewerTimeChanged(const rational& t);
|
||||
|
||||
|
||||
@@ -28,6 +28,7 @@
|
||||
|
||||
NodeParam::NodeParam(const QString &id) :
|
||||
time_(-1),
|
||||
value_caching_(true),
|
||||
id_(id)
|
||||
{
|
||||
Q_ASSERT(!id_.isEmpty());
|
||||
@@ -251,3 +252,8 @@ const rational &NodeParam::LastRequestedTime()
|
||||
{
|
||||
return time_;
|
||||
}
|
||||
|
||||
void NodeParam::SetValueCachingEnabled(bool enabled)
|
||||
{
|
||||
value_caching_ = enabled;
|
||||
}
|
||||
|
||||
@@ -250,6 +250,8 @@ public:
|
||||
*/
|
||||
const rational& LastRequestedTime();
|
||||
|
||||
void SetValueCachingEnabled(bool enabled);
|
||||
|
||||
virtual DataType data_type() = 0;
|
||||
|
||||
signals:
|
||||
@@ -285,6 +287,11 @@ protected:
|
||||
*/
|
||||
rational time_;
|
||||
|
||||
/**
|
||||
* @brief Internal value for whether value caching is enabled
|
||||
*/
|
||||
bool value_caching_;
|
||||
|
||||
private:
|
||||
/**
|
||||
* @brief Internal name string
|
||||
|
||||
@@ -99,30 +99,23 @@ QVariant RendererProcessor::Value(NodeOutput* output, const rational& time)
|
||||
|
||||
// Find frame in map
|
||||
if (time_hash_map_.contains(time)) {
|
||||
|
||||
QString fn = CachePathName(time_hash_map_[time]);
|
||||
|
||||
if (QFileInfo::exists(fn)) {
|
||||
qDebug() << "Reading" << fn;
|
||||
auto in = OIIO::ImageInput::open(fn.toStdString());
|
||||
|
||||
if (in) {
|
||||
in->read_image(PixelService::GetPixelFormatInfo(format_).oiio_desc, cache_frame_load_buffer_.data());
|
||||
|
||||
in->close();
|
||||
qDebug() << "Stopped reading" << fn;
|
||||
|
||||
master_texture_->Upload(cache_frame_load_buffer_.data());
|
||||
|
||||
return QVariant::fromValue(master_texture_);
|
||||
} else {
|
||||
qDebug() << "OIIO failed to read EXR:" << OIIO::geterror().c_str();
|
||||
qWarning() << "OIIO Error:" << OIIO::geterror().c_str();
|
||||
}
|
||||
} else {
|
||||
qDebug() << "Texture did NOT exist";
|
||||
}
|
||||
} else {
|
||||
qDebug() << "Hash map did NOT have this time";
|
||||
}
|
||||
}
|
||||
|
||||
@@ -138,14 +131,18 @@ void RendererProcessor::InvalidateCache(const rational &start_range, const ratio
|
||||
{
|
||||
Q_UNUSED(from)
|
||||
|
||||
//ClearCachedValuesInParameters(start_range, end_range);
|
||||
texture_input_->ClearCachedValue();
|
||||
length_input_->ClearCachedValue();
|
||||
|
||||
// Adjust range to min/max values
|
||||
rational start_range_adj = qMax(rational(0), start_range);
|
||||
rational end_range_adj = qMin(length_input()->get_value(0).value<rational>(), end_range);
|
||||
|
||||
qDebug() << "[RendererProcessor] Cache invalidated between"
|
||||
/*qDebug() << "[RendererProcessor] Cache invalidated between"
|
||||
<< start_range_adj.toDouble()
|
||||
<< "and"
|
||||
<< end_range_adj.toDouble();
|
||||
<< end_range_adj.toDouble();*/
|
||||
|
||||
// Snap start_range to timebase
|
||||
double start_range_dbl = start_range_adj.toDouble();
|
||||
@@ -265,10 +262,30 @@ void RendererProcessor::Start()
|
||||
|
||||
// Ensure this connection is "Queued" so that it always runs in this object's threaded rather than any of the
|
||||
// other threads
|
||||
connect(threads_[i].get(), SIGNAL(FinishedPath()), this, SLOT(ThreadCallback()), Qt::QueuedConnection);
|
||||
connect(threads_[i].get(), SIGNAL(RequestSibling(NodeDependency)), this, SLOT(ThreadRequestSibling(NodeDependency)), Qt::QueuedConnection);
|
||||
connect(threads_.at(i).get(),
|
||||
SIGNAL(RequestSibling(NodeDependency)),
|
||||
this,
|
||||
SLOT(ThreadRequestSibling(NodeDependency)),
|
||||
Qt::QueuedConnection);
|
||||
}
|
||||
|
||||
// Connect first thread (master thread) to the callback
|
||||
connect(threads_.first().get(),
|
||||
SIGNAL(CachedFrame(RenderTexturePtr, const rational&, const QByteArray&)),
|
||||
this,
|
||||
SLOT(ThreadCallback(RenderTexturePtr, const rational&, const QByteArray&)),
|
||||
Qt::QueuedConnection);
|
||||
connect(threads_.first().get(),
|
||||
SIGNAL(FrameExists(const rational&, const QByteArray&)),
|
||||
this,
|
||||
SLOT(ThreadFrameAlreadyExists(const rational&, const QByteArray&)),
|
||||
Qt::QueuedConnection);
|
||||
connect(threads_.first().get(),
|
||||
SIGNAL(FrameIgnored()),
|
||||
this,
|
||||
SLOT(ThreadIgnoredFrame()),
|
||||
Qt::QueuedConnection);
|
||||
|
||||
download_threads_.resize(background_thread_count);
|
||||
|
||||
for (int i=0;i<download_threads_.size();i++) {
|
||||
@@ -279,7 +296,8 @@ void RendererProcessor::Start()
|
||||
connect(download_threads_[i].get(),
|
||||
SIGNAL(Downloaded(const rational&, const QByteArray&)),
|
||||
this,
|
||||
SLOT(DownloadThreadFinished(const rational&, const QByteArray&)));
|
||||
SLOT(MapHashToTimecode(const rational&, const QByteArray&)),
|
||||
Qt::QueuedConnection);
|
||||
}
|
||||
|
||||
last_download_thread_ = 0;
|
||||
@@ -344,12 +362,11 @@ void RendererProcessor::CacheNext()
|
||||
// Make sure cache has started
|
||||
Start();
|
||||
|
||||
cache_frame_ = cache_queue_.takeFirst();
|
||||
rational cache_frame = cache_queue_.takeFirst();
|
||||
|
||||
qDebug() << "[RendererProcessor] Caching" << cache_frame_.toDouble();
|
||||
//qDebug() << "[RendererProcessor] Caching" << cache_frame.toDouble();
|
||||
|
||||
master_thread_ = threads_.at(0).get();
|
||||
master_thread_->Queue(NodeDependency(texture_input_->get_connected_output(), cache_frame_), true);
|
||||
threads_.first()->Queue(NodeDependency(texture_input_->get_connected_output(), cache_frame), true);
|
||||
|
||||
caching_ = true;
|
||||
}
|
||||
@@ -366,13 +383,33 @@ QString RendererProcessor::CachePathName(const QByteArray &hash)
|
||||
|
||||
bool RendererProcessor::HasHash(const QByteArray &hash)
|
||||
{
|
||||
download_list_mutex_.lock();
|
||||
return QFileInfo::exists(CachePathName(hash));
|
||||
}
|
||||
|
||||
bool dl_list_contains = download_list_.contains(hash);
|
||||
bool RendererProcessor::IsCaching(const QByteArray &hash)
|
||||
{
|
||||
cache_hash_list_mutex_.lock();
|
||||
|
||||
download_list_mutex_.unlock();
|
||||
bool is_caching = cache_hash_list_.contains(hash);
|
||||
|
||||
return QFileInfo::exists(CachePathName(hash)) || dl_list_contains;
|
||||
cache_hash_list_mutex_.unlock();
|
||||
|
||||
return is_caching;
|
||||
}
|
||||
|
||||
bool RendererProcessor::TryCache(const QByteArray &hash)
|
||||
{
|
||||
cache_hash_list_mutex_.lock();
|
||||
|
||||
bool is_caching = cache_hash_list_.contains(hash);
|
||||
|
||||
if (!is_caching) {
|
||||
cache_hash_list_.append(hash);
|
||||
}
|
||||
|
||||
cache_hash_list_mutex_.unlock();
|
||||
|
||||
return !is_caching;
|
||||
}
|
||||
|
||||
void RendererProcessor::CalculateEffectiveDimensions()
|
||||
@@ -381,68 +418,79 @@ void RendererProcessor::CalculateEffectiveDimensions()
|
||||
effective_height_ = height_ / divider_;
|
||||
}
|
||||
|
||||
void RendererProcessor::ThreadCallback()
|
||||
void RendererProcessor::ThreadCallback(RenderTexturePtr texture, const rational& time, const QByteArray& hash)
|
||||
{
|
||||
if (sender() == master_thread_) {
|
||||
// Threads are all done now, time to proceed
|
||||
caching_ = false;
|
||||
// Threads are all done now, time to proceed
|
||||
caching_ = false;
|
||||
|
||||
RenderTexturePtr texture = master_thread_->texture();
|
||||
if (texture != nullptr) {
|
||||
// We received a texture, time to start downloading it
|
||||
QString fn = CachePathName(hash);
|
||||
|
||||
bool hash_exists = HasHash(master_thread_->hash());
|
||||
download_threads_[last_download_thread_%download_threads_.size()]->Queue(texture,
|
||||
fn,
|
||||
time,
|
||||
hash);
|
||||
|
||||
if (!hash_exists) {
|
||||
if (texture == nullptr) {
|
||||
// We didn't receive a texture to download, but the viewer may still need updating
|
||||
DownloadThreadFinished(cache_frame_, master_thread_->hash());
|
||||
} else {
|
||||
// We received a texture, time to start downloading it
|
||||
download_list_mutex_.lock();
|
||||
download_list_.append(master_thread_->hash());
|
||||
download_list_mutex_.unlock();
|
||||
|
||||
QString fn = CachePathName(master_thread_->hash());
|
||||
|
||||
download_threads_[last_download_thread_%download_threads_.size()]->Queue(texture,
|
||||
fn,
|
||||
cache_frame_,
|
||||
master_thread_->hash());
|
||||
|
||||
last_download_thread_++;
|
||||
}
|
||||
}
|
||||
|
||||
CacheNext();
|
||||
last_download_thread_++;
|
||||
} else {
|
||||
// There was no texture here, we must update the viewer
|
||||
MapHashToTimecode(time, hash);
|
||||
}
|
||||
|
||||
// If the connected output is using this time, signal it to update
|
||||
if (texture_output_->IsConnected()
|
||||
&& texture_output_->LastRequestedTime() == time) {
|
||||
texture_output_->push_value(QVariant::fromValue(texture), time);
|
||||
SendInvalidateCache(time, time);
|
||||
}
|
||||
|
||||
CacheNext();
|
||||
}
|
||||
|
||||
void RendererProcessor::ThreadRequestSibling(NodeDependency dep)
|
||||
{
|
||||
// Try to queue another thread to run this dep in advance
|
||||
for (int i=0;i<threads_.size();i++) {
|
||||
if (threads_.at(i).get() != master_thread_
|
||||
&& threads_.at(i)->Queue(dep, false)) {
|
||||
for (int i=1;i<threads_.size();i++) {
|
||||
if (threads_.at(i)->Queue(dep, false)) {
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void RendererProcessor::DownloadThreadFinished(const rational& time, const QByteArray& hash)
|
||||
void RendererProcessor::ThreadFrameAlreadyExists(const rational &time, const QByteArray &hash)
|
||||
{
|
||||
caching_ = false;
|
||||
|
||||
// Update hash map with new hash
|
||||
MapHashToTimecode(time, hash);
|
||||
|
||||
// Signal output to update value
|
||||
if (texture_output_->IsConnected()
|
||||
&& texture_output_->LastRequestedTime() == time) {
|
||||
texture_output_->ClearCachedValue();
|
||||
SendInvalidateCache(time, time);
|
||||
}
|
||||
|
||||
// Start caching the next frame
|
||||
CacheNext();
|
||||
}
|
||||
|
||||
void RendererProcessor::MapHashToTimecode(const rational& time, const QByteArray& hash)
|
||||
{
|
||||
// Insert into hash map
|
||||
time_hash_map_.insert(time, hash);
|
||||
|
||||
download_list_mutex_.lock();
|
||||
download_list_.removeAll(hash);
|
||||
download_list_mutex_.unlock();
|
||||
cache_hash_list_mutex_.lock();
|
||||
cache_hash_list_.removeAll(hash);
|
||||
cache_hash_list_mutex_.unlock();
|
||||
}
|
||||
|
||||
// Check if we just downloaded (aka finished caching) the frame we're currently on
|
||||
if (texture_output_->IsConnected()
|
||||
&& texture_output_->LastRequestedTime() == time) {
|
||||
texture_output_->ClearCachedValue();
|
||||
void RendererProcessor::ThreadIgnoredFrame()
|
||||
{
|
||||
caching_ = false;
|
||||
|
||||
Node::InvalidateCache(time, time, nullptr);
|
||||
}
|
||||
CacheNext();
|
||||
}
|
||||
|
||||
RendererThreadBase* RendererProcessor::CurrentThread()
|
||||
|
||||
@@ -89,6 +89,16 @@ public:
|
||||
*/
|
||||
bool HasHash(const QByteArray& hash);
|
||||
|
||||
/**
|
||||
* @brief Return whether a frame is currently being cached
|
||||
*/
|
||||
bool IsCaching(const QByteArray& hash);
|
||||
|
||||
/**
|
||||
* @brief Check if a frame is currently being cached, and if not reserve it
|
||||
*/
|
||||
bool TryCache(const QByteArray& hash);
|
||||
|
||||
/**
|
||||
* @brief Return current instance of a RenderThread (or nullptr if there is none)
|
||||
*
|
||||
@@ -131,6 +141,8 @@ private:
|
||||
*/
|
||||
void CacheNext();
|
||||
|
||||
bool ShouldPushTexture(const rational &time);
|
||||
|
||||
/**
|
||||
* @brief Return the path of the cached image at this time
|
||||
*/
|
||||
@@ -174,8 +186,6 @@ private:
|
||||
QString cache_id_;
|
||||
|
||||
bool caching_;
|
||||
RendererProcessThread* master_thread_;
|
||||
rational cache_frame_;
|
||||
QVector<uchar*> cache_frame_load_buffer_;
|
||||
|
||||
QVector<RendererDownloadThreadPtr> download_threads_;
|
||||
@@ -185,15 +195,19 @@ private:
|
||||
|
||||
QMap<rational, QByteArray> time_hash_map_;
|
||||
|
||||
QMutex download_list_mutex_;
|
||||
QVector<QByteArray> download_list_;
|
||||
QMutex cache_hash_list_mutex_;
|
||||
QVector<QByteArray> cache_hash_list_;
|
||||
|
||||
private slots:
|
||||
void ThreadCallback();
|
||||
void ThreadCallback(RenderTexturePtr texture, const rational& time, const QByteArray& hash);
|
||||
|
||||
void ThreadRequestSibling(NodeDependency dep);
|
||||
|
||||
void DownloadThreadFinished(const rational &time, const QByteArray &hash);
|
||||
void ThreadFrameAlreadyExists(const rational &time, const QByteArray &hash);
|
||||
|
||||
void MapHashToTimecode(const rational &time, const QByteArray &hash);
|
||||
|
||||
void ThreadIgnoredFrame();
|
||||
|
||||
};
|
||||
|
||||
|
||||
@@ -113,11 +113,9 @@ void RendererDownloadThread::ProcessLoop()
|
||||
std::unique_ptr<OIIO::ImageOutput> out = OIIO::ImageOutput::create(working_fn_std);
|
||||
|
||||
if (out) {
|
||||
qDebug() << "Writing" << entry.filename;
|
||||
out->open(working_fn_std, spec);
|
||||
out->write_image(format_info.oiio_desc, data_buffer.data());
|
||||
out->close();
|
||||
qDebug() << "Stopped writing" << entry.filename;
|
||||
|
||||
emit Downloaded(entry.time, entry.hash);
|
||||
} else {
|
||||
|
||||
@@ -61,16 +61,6 @@ bool RendererProcessThread::Queue(const NodeDependency& dep, bool wait)
|
||||
return true;
|
||||
}
|
||||
|
||||
const QByteArray &RendererProcessThread::hash()
|
||||
{
|
||||
return hash_;
|
||||
}
|
||||
|
||||
RenderTexturePtr RendererProcessThread::texture()
|
||||
{
|
||||
return texture_;
|
||||
}
|
||||
|
||||
void RendererProcessThread::Cancel()
|
||||
{
|
||||
cancelled_ = true;
|
||||
@@ -115,20 +105,27 @@ void RendererProcessThread::ProcessLoop()
|
||||
|
||||
texture_ = nullptr;
|
||||
|
||||
if (!parent_->HasHash(hash_)) {
|
||||
QList<NodeDependency> deps = node_to_process->RunDependencies(output_to_process, path_.time());
|
||||
bool has_hash = parent_->HasHash(hash_);
|
||||
bool can_cache = false;
|
||||
|
||||
// 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));
|
||||
if (!has_hash){
|
||||
|
||||
if ((can_cache = parent_->TryCache(hash_))) {
|
||||
|
||||
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
|
||||
texture_ = output_to_process->get_value(path_.time()).value<RenderTexturePtr>();
|
||||
|
||||
render_instance()->context()->functions()->glFinish();
|
||||
}
|
||||
|
||||
// Get the requested value
|
||||
texture_ = output_to_process->get_value(path_.time()).value<RenderTexturePtr>();
|
||||
|
||||
render_instance()->context()->functions()->glFinish();
|
||||
}
|
||||
|
||||
foreach (Node* dep, all_deps) {
|
||||
@@ -137,6 +134,15 @@ void RendererProcessThread::ProcessLoop()
|
||||
|
||||
node_to_process->Unlock();
|
||||
|
||||
emit FinishedPath();
|
||||
if (has_hash && !parent_->IsCaching(hash_)) {
|
||||
// This hash already exists, no need to cache, just record it
|
||||
emit FrameExists(path_.time(), hash_);
|
||||
} else if (can_cache) {
|
||||
// We cached this frame, signal that it will need to be downloaded to disk
|
||||
emit CachedFrame(texture_, path_.time(), hash_);
|
||||
} else {
|
||||
// Some other dork is caching this frame, skip it
|
||||
emit FrameIgnored();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -38,10 +38,6 @@ public:
|
||||
|
||||
bool Queue(const NodeDependency &dep, bool wait);
|
||||
|
||||
const QByteArray& hash();
|
||||
|
||||
RenderTexturePtr texture();
|
||||
|
||||
public slots:
|
||||
virtual void Cancel() override;
|
||||
|
||||
@@ -51,15 +47,17 @@ protected:
|
||||
signals:
|
||||
void RequestSibling(NodeDependency dep);
|
||||
|
||||
void FinishedPath();
|
||||
void CachedFrame(RenderTexturePtr texture, const rational& time, const QByteArray& hash);
|
||||
|
||||
void FrameExists(const rational& time, const QByteArray& hash);
|
||||
|
||||
void FrameIgnored();
|
||||
|
||||
private:
|
||||
RendererProcessor* parent_;
|
||||
|
||||
NodeDependency path_;
|
||||
|
||||
rational time_;
|
||||
|
||||
QByteArray hash_;
|
||||
|
||||
RenderTexturePtr texture_;
|
||||
|
||||
@@ -74,6 +74,11 @@ void ViewerPanel::SetTimebase(const rational &timebase)
|
||||
viewer_->SetTimebase(timebase);
|
||||
}
|
||||
|
||||
rational ViewerPanel::GetTime()
|
||||
{
|
||||
return viewer_->GetTime();
|
||||
}
|
||||
|
||||
void ViewerPanel::SetTexture(GLuint tex)
|
||||
{
|
||||
viewer_->SetTexture(tex);
|
||||
|
||||
@@ -50,6 +50,8 @@ public:
|
||||
|
||||
void SetTimebase(const rational& timebase);
|
||||
|
||||
rational GetTime();
|
||||
|
||||
public slots:
|
||||
/**
|
||||
* @brief Set the texture to draw and draw it
|
||||
|
||||
@@ -64,9 +64,20 @@ void NodeParamViewWidgetBridge::CreateWidgets()
|
||||
case NodeParam::kFloat:
|
||||
{
|
||||
FloatSlider* slider = new FloatSlider();
|
||||
|
||||
slider->SetValue(base_input->get_value(0).toDouble());
|
||||
widgets_.append(slider);
|
||||
|
||||
if (base_input->has_minimum()) {
|
||||
slider->SetMinimum(base_input->minimum().toDouble());
|
||||
}
|
||||
|
||||
if (base_input->has_maximum()) {
|
||||
slider->SetMaximum(base_input->maximum().toDouble());
|
||||
}
|
||||
|
||||
connect(slider, SIGNAL(ValueChanged(double)), this, SLOT(WidgetCallback()));
|
||||
|
||||
widgets_.append(slider);
|
||||
break;
|
||||
}
|
||||
case NodeParam::kVec2:
|
||||
|
||||
@@ -36,6 +36,16 @@ void FloatSlider::SetValue(const double &d)
|
||||
SliderBase::SetValue(d);
|
||||
}
|
||||
|
||||
void FloatSlider::SetMinimum(const double &d)
|
||||
{
|
||||
SetMinimumInternal(d);
|
||||
}
|
||||
|
||||
void FloatSlider::SetMaximum(const double &d)
|
||||
{
|
||||
SetMaximumInternal(d);
|
||||
}
|
||||
|
||||
void FloatSlider::SetDecimalPlaces(int i)
|
||||
{
|
||||
decimal_places_ = i;
|
||||
|
||||
@@ -33,6 +33,10 @@ public:
|
||||
|
||||
void SetValue(const double& d);
|
||||
|
||||
void SetMinimum(const double& d);
|
||||
|
||||
void SetMaximum(const double& d);
|
||||
|
||||
void SetDecimalPlaces(int i);
|
||||
|
||||
signals:
|
||||
|
||||
@@ -36,6 +36,16 @@ void IntegerSlider::SetValue(const int &v)
|
||||
SliderBase::SetValue(v);
|
||||
}
|
||||
|
||||
void IntegerSlider::SetMinimum(const int &d)
|
||||
{
|
||||
SetMinimumInternal(d);
|
||||
}
|
||||
|
||||
void IntegerSlider::SetMaximum(const int &d)
|
||||
{
|
||||
SetMaximumInternal(d);
|
||||
}
|
||||
|
||||
void IntegerSlider::ConvertValue(QVariant v)
|
||||
{
|
||||
emit ValueChanged(v.toInt());
|
||||
|
||||
@@ -33,6 +33,10 @@ public:
|
||||
|
||||
void SetValue(const int& v);
|
||||
|
||||
void SetMinimum(const int& d);
|
||||
|
||||
void SetMaximum(const int& d);
|
||||
|
||||
signals:
|
||||
void ValueChanged(int);
|
||||
|
||||
|
||||
@@ -28,6 +28,8 @@ SliderBase::SliderBase(Mode mode, QWidget *parent) :
|
||||
QStackedWidget(parent),
|
||||
decimal_places_(1),
|
||||
drag_multiplier_(1.0),
|
||||
has_min_(false),
|
||||
has_max_(false),
|
||||
mode_(mode),
|
||||
dragged_(false)
|
||||
{
|
||||
@@ -49,16 +51,14 @@ SliderBase::SliderBase(Mode mode, QWidget *parent) :
|
||||
switch (mode_) {
|
||||
case kString:
|
||||
setCursor(Qt::PointingHandCursor);
|
||||
value_ = "";
|
||||
SetValue("");
|
||||
break;
|
||||
case kInteger:
|
||||
case kFloat:
|
||||
setCursor(Qt::SizeHorCursor);
|
||||
value_ = 0;
|
||||
SetValue(0);
|
||||
break;
|
||||
}
|
||||
|
||||
UpdateLabel(value_);
|
||||
}
|
||||
|
||||
void SliderBase::SetDragMultiplier(const double &d)
|
||||
@@ -77,11 +77,33 @@ const QVariant &SliderBase::Value()
|
||||
|
||||
void SliderBase::SetValue(const QVariant &v)
|
||||
{
|
||||
value_ = v;
|
||||
value_ = ClampValue(v);
|
||||
|
||||
UpdateLabel(value_);
|
||||
}
|
||||
|
||||
void SliderBase::SetMinimumInternal(const QVariant &v)
|
||||
{
|
||||
min_value_ = v;
|
||||
has_min_ = true;
|
||||
|
||||
// Limit value by this new minimum value
|
||||
if (value_ < min_value_) {
|
||||
SetValue(min_value_);
|
||||
}
|
||||
}
|
||||
|
||||
void SliderBase::SetMaximumInternal(const QVariant &v)
|
||||
{
|
||||
max_value_ = v;
|
||||
has_max_ = true;
|
||||
|
||||
// Limit value by this new maximum value
|
||||
if (value_ > max_value_) {
|
||||
SetValue(max_value_);
|
||||
}
|
||||
}
|
||||
|
||||
void SliderBase::changeEvent(QEvent *e)
|
||||
{
|
||||
if (e->type() == QEvent::LanguageChange) {
|
||||
@@ -90,6 +112,19 @@ void SliderBase::changeEvent(QEvent *e)
|
||||
QStackedWidget::changeEvent(e);
|
||||
}
|
||||
|
||||
const QVariant &SliderBase::ClampValue(const QVariant &v)
|
||||
{
|
||||
if (has_min_ && v < min_value_) {
|
||||
return min_value_;
|
||||
}
|
||||
|
||||
if (has_max_ && v > max_value_) {
|
||||
return max_value_;
|
||||
}
|
||||
|
||||
return v;
|
||||
}
|
||||
|
||||
void SliderBase::UpdateLabel(const QVariant &v)
|
||||
{
|
||||
switch (mode_) {
|
||||
@@ -132,14 +167,12 @@ void SliderBase::LabelClicked()
|
||||
// No-op
|
||||
break;
|
||||
case kInteger:
|
||||
value_ = qRound(dragged_diff_);
|
||||
SetValue(qRound(dragged_diff_));
|
||||
break;
|
||||
case kFloat:
|
||||
value_ = dragged_diff_;
|
||||
SetValue(dragged_diff_);
|
||||
break;
|
||||
}
|
||||
|
||||
UpdateLabel(value_);
|
||||
} else {
|
||||
// This was a simple click
|
||||
|
||||
@@ -177,6 +210,8 @@ void SliderBase::LabelDragged(int i)
|
||||
temp_dragged_value_ = dragged_diff_;
|
||||
}
|
||||
|
||||
temp_dragged_value_ = ClampValue(temp_dragged_value_);
|
||||
|
||||
UpdateLabel(temp_dragged_value_);
|
||||
emit ValueChanged(temp_dragged_value_);
|
||||
break;
|
||||
@@ -210,9 +245,7 @@ void SliderBase::LineEditConfirmed()
|
||||
}
|
||||
|
||||
if (is_valid) {
|
||||
value_ = test_val;
|
||||
|
||||
UpdateLabel(value_);
|
||||
SetValue(test_val);
|
||||
|
||||
emit ValueChanged(value_);
|
||||
|
||||
|
||||
@@ -48,6 +48,10 @@ protected:
|
||||
|
||||
void SetValue(const QVariant& v);
|
||||
|
||||
void SetMinimumInternal(const QVariant& v);
|
||||
|
||||
void SetMaximumInternal(const QVariant& v);
|
||||
|
||||
void UpdateLabel(const QVariant& v);
|
||||
|
||||
virtual void changeEvent(QEvent* e) override;
|
||||
@@ -57,12 +61,20 @@ protected:
|
||||
double drag_multiplier_;
|
||||
|
||||
private:
|
||||
const QVariant& ClampValue(const QVariant& v);
|
||||
|
||||
SliderLabel* label_;
|
||||
|
||||
SliderLineEdit* editor_;
|
||||
|
||||
QVariant value_;
|
||||
|
||||
bool has_min_;
|
||||
QVariant min_value_;
|
||||
|
||||
bool has_max_;
|
||||
QVariant max_value_;
|
||||
|
||||
Mode mode_;
|
||||
|
||||
bool dragged_;
|
||||
|
||||
@@ -92,7 +92,7 @@ void TimeRuler::SetTimebase(const rational &r)
|
||||
update();
|
||||
}
|
||||
|
||||
const int64_t &TimeRuler::Time()
|
||||
const int64_t &TimeRuler::GetTime()
|
||||
{
|
||||
return time_;
|
||||
}
|
||||
|
||||
@@ -42,7 +42,7 @@ public:
|
||||
|
||||
void SetCenteredText(bool c);
|
||||
|
||||
const int64_t& Time();
|
||||
const int64_t& GetTime();
|
||||
|
||||
public slots:
|
||||
void SetTime(const int64_t &r);
|
||||
|
||||
@@ -91,6 +91,11 @@ const double &ViewerWidget::scale()
|
||||
return ruler_->scale();
|
||||
}
|
||||
|
||||
rational ViewerWidget::GetTime()
|
||||
{
|
||||
return rational(ruler_->GetTime()) * time_base_;
|
||||
}
|
||||
|
||||
void ViewerWidget::SetScale(const double &scale_)
|
||||
{
|
||||
ruler_->SetScale(scale_);
|
||||
@@ -145,7 +150,7 @@ void ViewerWidget::Play()
|
||||
}
|
||||
|
||||
start_msec_ = QDateTime::currentMSecsSinceEpoch();
|
||||
start_timestamp_ = ruler_->Time();
|
||||
start_timestamp_ = ruler_->GetTime();
|
||||
|
||||
playback_timer_.start();
|
||||
|
||||
@@ -170,14 +175,14 @@ void ViewerWidget::PrevFrame()
|
||||
{
|
||||
Pause();
|
||||
|
||||
SetTime(ruler_->Time() - 1);
|
||||
SetTime(qMax(static_cast<int64_t>(0), ruler_->GetTime() - 1));
|
||||
}
|
||||
|
||||
void ViewerWidget::NextFrame()
|
||||
{
|
||||
Pause();
|
||||
|
||||
SetTime(ruler_->Time() + 1);
|
||||
SetTime(ruler_->GetTime() + 1);
|
||||
}
|
||||
|
||||
void ViewerWidget::GoToEnd()
|
||||
|
||||
@@ -49,6 +49,8 @@ public:
|
||||
|
||||
const double& scale();
|
||||
|
||||
rational GetTime();
|
||||
|
||||
void SetScale(const double& scale_);
|
||||
|
||||
void SetTime(const int64_t& time);
|
||||
|
||||
Reference in New Issue
Block a user