Improve filters preview performance (fix #1400)

Now several filter steps are applied in a background thread and a UI
timer refresh several rows in screen at the same time (instead of one
row per time). (This is something in my personal to-do list from long
time ago too.)
This commit is contained in:
David Capello 2017-01-31 09:59:09 -03:00
parent fbb0cdcbe2
commit f381cb972c
6 changed files with 79 additions and 28 deletions

View File

@ -1,5 +1,5 @@
// Aseprite
// Copyright (C) 2001-2016 David Capello
// Copyright (C) 2001-2017 David Capello
//
// This program is distributed under the terms of
// the End-User License Agreement for Aseprite.
@ -110,7 +110,7 @@ void FilterManagerImpl::beginForPreview()
m_previewMask->replace(m_site.sprite()->bounds());
}
m_row = 0;
m_row = m_nextRowToFlush = 0;
m_mask = m_previewMask;
{
@ -257,17 +257,24 @@ void FilterManagerImpl::applyToTarget()
void FilterManagerImpl::flush()
{
if (m_row >= 0) {
int h = m_row - m_nextRowToFlush;
if (m_row >= 0 && h > 0) {
Editor* editor = current_editor;
// We expand the region one pixel at the top and bottom of the
// region [m_row,m_nextRowToFlush) to be updated on the screen to
// avoid screen artifacts when we apply filters like convolution
// matrices.
gfx::Rect rect(
editor->editorToScreen(
gfx::Point(
m_bounds.x,
m_bounds.y+m_row-1)),
m_bounds.y+m_nextRowToFlush-1)),
gfx::Size(
editor->projection().applyX(m_bounds.w),
(editor->projection().scaleY() >= 1 ? editor->projection().applyY(1):
editor->projection().removeY(1))));
(editor->projection().scaleY() >= 1 ? editor->projection().applyY(h+2):
editor->projection().removeY(h+2))));
gfx::Region reg1(rect);
gfx::Region reg2;
@ -275,6 +282,7 @@ void FilterManagerImpl::flush()
reg1.createIntersection(reg1, reg2);
editor->invalidateRegion(reg1);
m_nextRowToFlush = m_row+1;
}
}

View File

@ -1,5 +1,5 @@
// Aseprite
// Copyright (C) 2001-2016 David Capello
// Copyright (C) 2001-2017 David Capello
//
// This program is distributed under the terms of
// the End-User License Agreement for Aseprite.
@ -120,6 +120,7 @@ namespace app {
doc::ImageRef m_src;
doc::ImageRef m_dst;
int m_row;
int m_nextRowToFlush;
gfx::Rect m_bounds;
doc::Mask* m_mask;
base::UniquePtr<doc::Mask> m_previewMask;

View File

@ -1,5 +1,5 @@
// Aseprite
// Copyright (C) 2001-2016 David Capello
// Copyright (C) 2001-2017 David Capello
//
// This program is distributed under the terms of
// the End-User License Agreement for Aseprite.
@ -13,6 +13,8 @@
#include "app/commands/filters/filter_manager_impl.h"
#include "app/modules/editors.h"
#include "app/ui/editor/editor.h"
#include "base/bind.h"
#include "base/scoped_lock.h"
#include "doc/layer.h"
#include "doc/sprite.h"
#include "ui/manager.h"
@ -28,6 +30,7 @@ FilterPreview::FilterPreview(FilterManagerImpl* filterMgr)
: Widget(kGenericWidget)
, m_filterMgr(filterMgr)
, m_timer(1, this)
, m_filterThread(nullptr)
{
setVisible(false);
}
@ -39,25 +42,32 @@ FilterPreview::~FilterPreview()
void FilterPreview::stop()
{
if (m_timer.isRunning()) {
ASSERT(m_filterMgr != NULL);
m_filterMgr->end();
{
base::scoped_lock lock(m_filterMgrMutex);
if (m_timer.isRunning()) {
ASSERT(m_filterMgr);
m_filterMgr->end();
}
}
m_filterMgr = NULL;
m_timer.stop();
if (m_filterThread) {
m_filterThread->join();
m_filterThread.reset();
}
}
void FilterPreview::restartPreview()
{
m_filterMgr->beginForPreview();
m_timer.start();
}
base::scoped_lock lock(m_filterMgrMutex);
FilterManagerImpl* FilterPreview::getFilterManager() const
{
return m_filterMgr;
m_filterMgr->beginForPreview();
m_filterIsDone = false;
m_filterThread.reset(new base::thread(
base::Bind<void>(&FilterPreview::onFilterThread, this)));
m_timer.start();
}
bool FilterPreview::onProcessMessage(Message* msg)
@ -80,17 +90,30 @@ bool FilterPreview::onProcessMessage(Message* msg)
m_timer.stop();
break;
case kTimerMessage:
case kTimerMessage: {
base::scoped_lock lock(m_filterMgrMutex);
if (m_filterMgr) {
if (m_filterMgr->applyStep())
m_filterMgr->flush();
else
m_filterMgr->flush();
if (m_filterIsDone)
m_timer.stop();
}
break;
}
}
return Widget::onProcessMessage(msg);
}
// This is executed in other thread.
void FilterPreview::onFilterThread()
{
while (!m_filterIsDone && m_timer.isRunning()) {
{
base::scoped_lock lock(m_filterMgrMutex);
m_filterIsDone = !m_filterMgr->applyStep();
}
base::this_thread::yield();
}
}
} // namespace app

View File

@ -1,5 +1,5 @@
// Aseprite
// Copyright (C) 2001-2015 David Capello
// Copyright (C) 2001-2017 David Capello
//
// This program is distributed under the terms of
// the End-User License Agreement for Aseprite.
@ -8,6 +8,9 @@
#define APP_COMMANDS_FILTERS_FILTER_PREVIEW_H_INCLUDED
#pragma once
#include "base/mutex.h"
#include "base/thread.h"
#include "base/unique_ptr.h"
#include "ui/timer.h"
#include "ui/widget.h"
@ -23,14 +26,18 @@ namespace app {
void stop();
void restartPreview();
FilterManagerImpl* getFilterManager() const;
protected:
bool onProcessMessage(ui::Message* msg) override;
private:
void onFilterThread();
FilterManagerImpl* m_filterMgr;
ui::Timer m_timer;
base::mutex m_filterMgrMutex;
base::UniquePtr<base::thread> m_filterThread;
bool m_filterIsDone;
};
} // namespace app

View File

@ -1,5 +1,5 @@
// Aseprite
// Copyright (C) 2001-2015 David Capello
// Copyright (C) 2001-2017 David Capello
//
// This program is distributed under the terms of
// the End-User License Agreement for Aseprite.
@ -121,6 +121,8 @@ void FilterWindow::restartPreview()
void FilterWindow::setNewTarget(Target target)
{
stopPreview();
m_filterMgr->setTarget(target);
m_targetButton.setTarget(target);
}
@ -143,6 +145,8 @@ void FilterWindow::onShowPreview(Event& ev)
// Called when the user changes the target-buttons.
void FilterWindow::onTargetButtonChange()
{
stopPreview();
// Change the targets in the filter manager and restart the filter preview.
m_filterMgr->setTarget(m_targetButton.getTarget());
restartPreview();
@ -150,7 +154,8 @@ void FilterWindow::onTargetButtonChange()
void FilterWindow::onTiledChange()
{
ASSERT(m_tiledCheck != NULL);
ASSERT(m_tiledCheck);
stopPreview();
// Call derived class implementation of setupTiledMode() so the
// filter is modified.
@ -162,4 +167,9 @@ void FilterWindow::onTiledChange()
restartPreview();
}
void FilterWindow::stopPreview()
{
m_preview.stop();
}
} // namespace app

View File

@ -1,5 +1,5 @@
// Aseprite
// Copyright (C) 2001-2015 David Capello
// Copyright (C) 2001-2017 David Capello
//
// This program is distributed under the terms of
// the End-User License Agreement for Aseprite.
@ -62,6 +62,8 @@ namespace app {
virtual void setupTiledMode(TiledMode tiledMode) { }
private:
void stopPreview();
const char* m_cfgSection;
FilterManagerImpl* m_filterMgr;
ui::Box m_hbox;