- Move Behavior-General options (hover focus, slider ladder, scroll zooms) into the General preferences tab. - Move Behavior-Audio option (audio scrubbing) into the Audio preferences tab. - Promote remaining Behavior categories (Timeline, Playback, Project, Nodes, Rendering) to top-level sidebar entries without the 'Behavior - ' prefix. - Update Chinese translations for the new sidebar titles and fill unfinished Behavior tab strings. NodeView: - Remove double-click jump-to-parameter-editor behavior; keep expand/collapse. - Add right-click context menu item 'Show in Parameter Editor'. - Add Shift+P shortcut bound to the same action. Render: - Fix crash in PreviewAutoCacher::ClearSingleFrameRenders when proxy playback causes a render ticket to finish synchronously before the watcher pointer is returned. Defer the watcher Finished signal via queued connection so the caller can safely register the watcher before it is deleted.
215 lines
3.9 KiB
C++
215 lines
3.9 KiB
C++
/***
|
|
|
|
Olive - Non-Linear Video Editor
|
|
Copyright (C) 2022 Olive Team
|
|
Modifications Copyright (C) 2025 mikesolar
|
|
|
|
This program is free software: you can redistribute it and/or modify
|
|
it under the terms of the GNU General Public License as published by
|
|
the Free Software Foundation, either version 3 of the License, or
|
|
(at your option) any later version.
|
|
|
|
This program is distributed in the hope that it will be useful,
|
|
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
GNU General Public License for more details.
|
|
|
|
You should have received a copy of the GNU General Public License
|
|
along with this program. If not, see <http://www.gnu.org/licenses/>.
|
|
|
|
***/
|
|
|
|
#include "renderticket.h"
|
|
|
|
namespace olive
|
|
{
|
|
|
|
RenderTicket::RenderTicket()
|
|
: is_running_(false)
|
|
, has_result_(false)
|
|
, finish_count_(0)
|
|
{
|
|
}
|
|
|
|
void RenderTicket::WaitForFinished(QMutex *mutex)
|
|
{
|
|
if (is_running_) {
|
|
wait_.wait(mutex);
|
|
}
|
|
}
|
|
|
|
void RenderTicket::Start()
|
|
{
|
|
QMutexLocker locker(&lock_);
|
|
|
|
is_running_ = true;
|
|
has_result_ = false;
|
|
result_.clear();
|
|
}
|
|
|
|
void RenderTicket::Finish()
|
|
{
|
|
FinishInternal(false, QVariant());
|
|
}
|
|
|
|
void RenderTicket::Finish(QVariant result)
|
|
{
|
|
FinishInternal(true, result);
|
|
}
|
|
|
|
QVariant RenderTicket::Get()
|
|
{
|
|
WaitForFinished();
|
|
|
|
// We don't have to mutex around this because there is no way to write to `result_` after
|
|
// the ticket has finished and the above function blocks the calling thread until it is finished
|
|
return result_;
|
|
}
|
|
|
|
void RenderTicket::WaitForFinished()
|
|
{
|
|
QMutexLocker locker(&lock_);
|
|
|
|
WaitForFinished(&lock_);
|
|
}
|
|
|
|
bool RenderTicket::IsRunning(bool lock)
|
|
{
|
|
if (lock) {
|
|
lock_.lock();
|
|
}
|
|
|
|
bool running = is_running_;
|
|
|
|
if (lock) {
|
|
lock_.unlock();
|
|
}
|
|
|
|
return running;
|
|
}
|
|
|
|
int RenderTicket::GetFinishCount(bool lock)
|
|
{
|
|
if (lock) {
|
|
lock_.lock();
|
|
}
|
|
|
|
int count = finish_count_;
|
|
|
|
if (lock) {
|
|
lock_.unlock();
|
|
}
|
|
|
|
return count;
|
|
}
|
|
|
|
bool RenderTicket::HasResult()
|
|
{
|
|
QMutexLocker locker(&lock_);
|
|
|
|
return has_result_;
|
|
}
|
|
|
|
void RenderTicket::FinishInternal(bool has_result, QVariant result)
|
|
{
|
|
QMutexLocker locker(&lock_);
|
|
|
|
if (!is_running_) {
|
|
qWarning() << "Tried to finish ticket that wasn't running";
|
|
} else {
|
|
is_running_ = false;
|
|
has_result_ = has_result;
|
|
result_ = result;
|
|
finish_count_++;
|
|
|
|
wait_.wakeAll();
|
|
|
|
locker.unlock();
|
|
|
|
emit Finished();
|
|
}
|
|
}
|
|
|
|
RenderTicketWatcher::RenderTicketWatcher(QObject *parent)
|
|
: QObject(parent)
|
|
, ticket_(nullptr)
|
|
{
|
|
}
|
|
|
|
void RenderTicketWatcher::SetTicket(RenderTicketPtr ticket)
|
|
{
|
|
if (ticket_) {
|
|
qCritical() << "Tried to set a ticket on a RenderTicketWatcher twice";
|
|
return;
|
|
}
|
|
|
|
if (!ticket) {
|
|
qCritical() << "Tried to set a null ticket on a RenderTicketWatcher";
|
|
return;
|
|
}
|
|
|
|
ticket_ = ticket;
|
|
|
|
// Lock ticket so we can query if it's already finished by the time this code runs
|
|
QMutexLocker locker(ticket->lock());
|
|
|
|
connect(ticket_.get(), &RenderTicket::Finished, this,
|
|
&RenderTicketWatcher::TicketFinished);
|
|
|
|
if (!ticket_->IsRunning(false) && ticket_->GetFinishCount(false) > 0) {
|
|
// Ticket has already finished before, so we emit a signal asynchronously
|
|
// to avoid deleting this watcher before the caller has a chance to use
|
|
// the returned pointer.
|
|
QMetaObject::invokeMethod(this, &RenderTicketWatcher::TicketFinished,
|
|
Qt::QueuedConnection);
|
|
}
|
|
}
|
|
|
|
bool RenderTicketWatcher::IsRunning()
|
|
{
|
|
if (ticket_) {
|
|
return ticket_->IsRunning();
|
|
} else {
|
|
return false;
|
|
}
|
|
}
|
|
|
|
void RenderTicketWatcher::WaitForFinished()
|
|
{
|
|
if (ticket_) {
|
|
ticket_->WaitForFinished();
|
|
}
|
|
}
|
|
|
|
QVariant RenderTicketWatcher::Get()
|
|
{
|
|
if (ticket_) {
|
|
return ticket_->Get();
|
|
} else {
|
|
return QVariant();
|
|
}
|
|
}
|
|
|
|
bool RenderTicketWatcher::HasResult()
|
|
{
|
|
if (ticket_) {
|
|
return ticket_->HasResult();
|
|
} else {
|
|
return false;
|
|
}
|
|
}
|
|
|
|
void RenderTicketWatcher::Cancel()
|
|
{
|
|
if (ticket_) {
|
|
ticket_->Cancel();
|
|
}
|
|
}
|
|
|
|
void RenderTicketWatcher::TicketFinished()
|
|
{
|
|
emit Finished(this);
|
|
}
|
|
|
|
}
|