Freestyle: View map caching.

New render layer option named "View map cache" is added to reuse a
previously computed view map for subsequent rendering.  The cache is
automatically updated when the mesh geometry of the input 3D scene has
been changed.

This functionality offers a major performance boost for Freestyle
animation rendering when camera-space mesh geometry is static, as well
as for repeated still renders with updates of line stylization options.

Although the "View map cache" toggle is a render layer option, the cache
memory is shared by all render layers and scenes.  This means that if
Freestyle is used for two or more render layers (possibly in different
scenes through the compositor), then the cached view map for one render
layer is replaced by a new view map for another render layer and hence
no performance gain is expected.
This commit is contained in:
Tamito Kajiyama 2014-01-28 23:24:59 +09:00
parent 6bde5381bb
commit dd9c53b312
12 changed files with 203 additions and 38 deletions

@ -122,7 +122,9 @@ class RENDERLAYER_PT_freestyle(RenderLayerFreestyleButtonsPanel, Panel):
layout.active = rl.use_freestyle layout.active = rl.use_freestyle
row = layout.row()
layout.prop(freestyle, "mode", text="Control mode") layout.prop(freestyle, "mode", text="Control mode")
layout.prop(freestyle, "use_view_map_cache", text="View Map Cache")
layout.label(text="Edge Detection Options:") layout.label(text="Edge Detection Options:")
split = layout.split() split = layout.split()

@ -412,6 +412,8 @@ set(SRC
intern/scene_graph/OrientedLineRep.h intern/scene_graph/OrientedLineRep.h
intern/scene_graph/Rep.cpp intern/scene_graph/Rep.cpp
intern/scene_graph/Rep.h intern/scene_graph/Rep.h
intern/scene_graph/SceneHash.cpp
intern/scene_graph/SceneHash.h
intern/scene_graph/ScenePrettyPrinter.cpp intern/scene_graph/ScenePrettyPrinter.cpp
intern/scene_graph/ScenePrettyPrinter.h intern/scene_graph/ScenePrettyPrinter.h
intern/scene_graph/SceneVisitor.cpp intern/scene_graph/SceneVisitor.cpp

@ -48,6 +48,7 @@ int FRS_is_freestyle_enabled(struct SceneRenderLayer *srl);
void FRS_init_stroke_rendering(struct Render *re); void FRS_init_stroke_rendering(struct Render *re);
struct Render *FRS_do_stroke_rendering(struct Render *re, struct SceneRenderLayer *srl, int render); struct Render *FRS_do_stroke_rendering(struct Render *re, struct SceneRenderLayer *srl, int render);
void FRS_finish_stroke_rendering(struct Render *re); void FRS_finish_stroke_rendering(struct Render *re);
void FRS_free_view_map_cache();
void FRS_composite_result(struct Render *re, struct SceneRenderLayer *srl, struct Render *freestyle_render); void FRS_composite_result(struct Render *re, struct SceneRenderLayer *srl, struct Render *freestyle_render);
void FRS_exit(void); void FRS_exit(void);

@ -118,6 +118,7 @@ Controller::Controller()
_Canvas = new AppCanvas; _Canvas = new AppCanvas;
_inter = new PythonInterpreter(); _inter = new PythonInterpreter();
_EnableViewMapCache = false;
_EnableQI = true; _EnableQI = true;
_EnableFaceSmoothness = false; _EnableFaceSmoothness = false;
_ComputeRidges = true; _ComputeRidges = true;
@ -212,6 +213,19 @@ void Controller::setContext(bContext *C)
py_inter->setContext(C); py_inter->setContext(C);
} }
bool Controller::hitViewMapCache()
{
if (!_EnableViewMapCache) {
return false;
}
real hashCode = sceneHashFunc.getValue();
if (prevSceneHash == hashCode) {
return (NULL != _ViewMap);
}
prevSceneHash = hashCode;
return false;
}
int Controller::LoadMesh(Render *re, SceneRenderLayer *srl) int Controller::LoadMesh(Render *re, SceneRenderLayer *srl)
{ {
BlenderFileLoader loader(re, srl); BlenderFileLoader loader(re, srl);
@ -242,6 +256,7 @@ int Controller::LoadMesh(Render *re, SceneRenderLayer *srl)
if (G.debug & G_DEBUG_FREESTYLE) { if (G.debug & G_DEBUG_FREESTYLE) {
cout << "Scene loaded" << endl; cout << "Scene loaded" << endl;
printf("Mesh cleaning : %lf\n", duration); printf("Mesh cleaning : %lf\n", duration);
printf("View map cache : %s\n", _EnableViewMapCache ? "enabled" : "disabled");
} }
_SceneNumFaces += loader.numFacesRead(); _SceneNumFaces += loader.numFacesRead();
@ -263,6 +278,22 @@ int Controller::LoadMesh(Render *re, SceneRenderLayer *srl)
if (_pRenderMonitor->testBreak()) if (_pRenderMonitor->testBreak())
return 0; return 0;
if (_EnableViewMapCache) {
sceneHashFunc.reset();
blenderScene->accept(sceneHashFunc);
if (G.debug & G_DEBUG_FREESTYLE) {
printf("Scene hash : %.16e\n", sceneHashFunc.getValue());
}
if (hitViewMapCache()) {
ClearRootNode();
return 0;
}
else {
delete _ViewMap;
_ViewMap = NULL;
}
}
_Chrono.start(); _Chrono.start();
WXEdgeBuilder wx_builder; WXEdgeBuilder wx_builder;
@ -357,7 +388,7 @@ void Controller::DeleteWingedEdge()
_minEdgeSize = DBL_MAX; _minEdgeSize = DBL_MAX;
} }
void Controller::DeleteViewMap() void Controller::DeleteViewMap(bool freeCache)
{ {
_pView->DetachSilhouette(); _pView->DetachSilhouette();
if (NULL != _SilhouetteNode) { if (NULL != _SilhouetteNode) {
@ -387,14 +418,15 @@ void Controller::DeleteViewMap()
_pView->DetachDebug(); _pView->DetachDebug();
if (NULL != _DebugNode) { if (NULL != _DebugNode) {
int ref = _DebugNode->destroy(); int ref = _DebugNode->destroy();
if (0 == ref) if (0 == ref)
_DebugNode->addRef(); _DebugNode->addRef();
} }
if (NULL != _ViewMap) { if ((freeCache || !_EnableViewMapCache) && NULL != _ViewMap) {
delete _ViewMap; delete _ViewMap;
_ViewMap = NULL; _ViewMap = NULL;
prevSceneHash = -1.0;
} }
} }
@ -403,40 +435,7 @@ void Controller::ComputeViewMap()
if (!_ListOfModels.size()) if (!_ListOfModels.size())
return; return;
if (NULL != _ViewMap) { DeleteViewMap(true);
delete _ViewMap;
_ViewMap = NULL;
}
_pView->DetachDebug();
if (NULL != _DebugNode) {
int ref = _DebugNode->destroy();
if (0 == ref)
_DebugNode->addRef();
}
_pView->DetachSilhouette();
if (NULL != _SilhouetteNode) {
int ref = _SilhouetteNode->destroy();
if (0 == ref)
delete _SilhouetteNode;
}
#if 0
if (NULL != _ProjectedSilhouette) {
int ref = _ProjectedSilhouette->destroy();
if (0 == ref)
delete _ProjectedSilhouette;
}
if (NULL != _VisibleProjectedSilhouette) {
int ref = _VisibleProjectedSilhouette->destroy();
if (0 == ref) {
delete _VisibleProjectedSilhouette;
_VisibleProjectedSilhouette = NULL;
}
}
#endif
// retrieve the 3D viewpoint and transformations information // retrieve the 3D viewpoint and transformations information
//---------------------------------------------------------- //----------------------------------------------------------
@ -763,6 +762,16 @@ int Controller::getVisibilityAlgo()
return FREESTYLE_ALGO_ADAPTIVE_TRADITIONAL; return FREESTYLE_ALGO_ADAPTIVE_TRADITIONAL;
} }
void Controller::setViewMapCache(bool iBool)
{
_EnableViewMapCache = iBool;
}
bool Controller::getViewMapCache() const
{
return _EnableViewMapCache;
}
void Controller::setQuantitativeInvisibility(bool iBool) void Controller::setQuantitativeInvisibility(bool iBool)
{ {
_EnableQI = iBool; _EnableQI = iBool;

@ -32,6 +32,7 @@
//#include "ConfigIO.h" //#include "ConfigIO.h"
#include "../geometry/FastGrid.h" #include "../geometry/FastGrid.h"
#include "../scene_graph/SceneHash.h"
#include "../system/Interpreter.h" #include "../system/Interpreter.h"
#include "../system/ProgressBar.h" #include "../system/ProgressBar.h"
#include "../system/Precision.h" #include "../system/Precision.h"
@ -96,7 +97,7 @@ public:
void Clear(); void Clear();
void ClearRootNode(); void ClearRootNode();
void DeleteWingedEdge(); void DeleteWingedEdge();
void DeleteViewMap(); void DeleteViewMap(bool freeCache = false);
void toggleLayer(unsigned index, bool iDisplay); void toggleLayer(unsigned index, bool iDisplay);
void setModified(unsigned index, bool iMod); void setModified(unsigned index, bool iMod);
void resetModified(bool iMod=false); void resetModified(bool iMod=false);
@ -118,6 +119,8 @@ public:
void setVisibilityAlgo(int algo); void setVisibilityAlgo(int algo);
int getVisibilityAlgo(); int getVisibilityAlgo();
void setViewMapCache(bool iBool);
bool getViewMapCache() const;
void setQuantitativeInvisibility(bool iBool); // if true, we compute quantitativeInvisibility void setQuantitativeInvisibility(bool iBool); // if true, we compute quantitativeInvisibility
bool getQuantitativeInvisibility() const; bool getQuantitativeInvisibility() const;
void setFaceSmoothness(bool iBool); void setFaceSmoothness(bool iBool);
@ -144,6 +147,8 @@ public:
void setModulesDir(const string& dir); void setModulesDir(const string& dir);
string getModulesDir() const; string getModulesDir() const;
bool hitViewMapCache();
void resetInterpreter(); void resetInterpreter();
public: public:
@ -231,6 +236,7 @@ private:
string _help_index; string _help_index;
string _browser_cmd; string _browser_cmd;
bool _EnableViewMapCache;
bool _EnableQI; bool _EnableQI;
bool _EnableFaceSmoothness; bool _EnableFaceSmoothness;
bool _ComputeRidges; bool _ComputeRidges;
@ -244,6 +250,9 @@ private:
FEdgeXDetector edgeDetector; FEdgeXDetector edgeDetector;
SceneHash sceneHashFunc;
real prevSceneHash = -1.0;
#ifdef WITH_CXX_GUARDEDALLOC #ifdef WITH_CXX_GUARDEDALLOC
MEM_CXX_CLASS_ALLOC_FUNCS("Freestyle:Controller") MEM_CXX_CLASS_ALLOC_FUNCS("Freestyle:Controller")
#endif #endif

@ -474,6 +474,9 @@ static void prepare(Main *bmain, Render *re, SceneRenderLayer *srl)
cout << " Z = " << (z ? "enabled" : "disabled") << endl; cout << " Z = " << (z ? "enabled" : "disabled") << endl;
} }
if (controller->hitViewMapCache())
return;
// compute view map // compute view map
re->i.infostr = "Freestyle: View map creation"; re->i.infostr = "Freestyle: View map creation";
re->stats_draw(re->sdh, &re->i); re->stats_draw(re->sdh, &re->i);
@ -589,6 +592,7 @@ Render *FRS_do_stroke_rendering(Render *re, SceneRenderLayer *srl, int render)
RenderMonitor monitor(re); RenderMonitor monitor(re);
controller->setRenderMonitor(&monitor); controller->setRenderMonitor(&monitor);
controller->setViewMapCache((srl->freestyleConfig.flags & FREESTYLE_VIEW_MAP_CACHE) ? true : false);
if (G.debug & G_DEBUG_FREESTYLE) { if (G.debug & G_DEBUG_FREESTYLE) {
cout << endl; cout << endl;
@ -647,6 +651,17 @@ void FRS_finish_stroke_rendering(Render *re)
controller->Clear(); controller->Clear();
} }
void FRS_free_view_map_cache()
{
// free cache
controller->DeleteViewMap(true);
#if 0
if (G.debug & G_DEBUG_FREESTYLE) {
printf("View map cache freed\n");
}
#endif
}
//======================================================= //=======================================================
// Freestyle Panel Configuration // Freestyle Panel Configuration
//======================================================= //=======================================================

@ -0,0 +1,39 @@
/*
* ***** BEGIN GPL LICENSE BLOCK *****
*
* 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 2
* 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, write to the Free Software Foundation,
* Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
*
* ***** END GPL LICENSE BLOCK *****
*/
/** \file blender/freestyle/intern/scene_graph/SceneHash.cpp
* \ingroup freestyle
*/
#include "SceneHash.h"
namespace Freestyle {
void SceneHash::visitIndexedFaceSet(IndexedFaceSet& ifs)
{
const real *v = ifs.vertices();
const unsigned n = ifs.vsize();
for (unsigned i = 0; i < n; i++) {
_hashcode += v[i];
}
}
} /* namespace Freestyle */

@ -0,0 +1,67 @@
/*
* ***** BEGIN GPL LICENSE BLOCK *****
*
* 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 2
* 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, write to the Free Software Foundation,
* Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
*
* ***** END GPL LICENSE BLOCK *****
*/
#ifndef __FREESTYLE_SCENE_HASH_H__
#define __FREESTYLE_SCENE_HASH_H__
/** \file blender/freestyle/intern/scene_graph/SceneHash.h
* \ingroup freestyle
*/
#include "IndexedFaceSet.h"
#include "SceneVisitor.h"
#ifdef WITH_CXX_GUARDEDALLOC
#include "MEM_guardedalloc.h"
#endif
namespace Freestyle {
class SceneHash : public SceneVisitor
{
public:
inline SceneHash() : SceneVisitor()
{
_hashcode = 0.0;
}
virtual ~SceneHash() {}
VISIT_DECL(IndexedFaceSet)
inline real getValue() {
return _hashcode;
}
inline void reset() {
_hashcode = 0.0;
}
private:
real _hashcode;
#ifdef WITH_CXX_GUARDEDALLOC
MEM_CXX_CLASS_ALLOC_FUNCS("Freestyle:SceneHash")
#endif
};
} /* namespace Freestyle */
#endif // __FREESTYLE_SCENE_HASH_H__

@ -50,6 +50,7 @@ struct Text;
#define FREESTYLE_FACE_SMOOTHNESS_FLAG (1 << 3) #define FREESTYLE_FACE_SMOOTHNESS_FLAG (1 << 3)
#define FREESTYLE_ADVANCED_OPTIONS_FLAG (1 << 4) #define FREESTYLE_ADVANCED_OPTIONS_FLAG (1 << 4)
#define FREESTYLE_CULLING (1 << 5) #define FREESTYLE_CULLING (1 << 5)
#define FREESTYLE_VIEW_MAP_CACHE (1 << 6)
/* FreestyleConfig::mode */ /* FreestyleConfig::mode */
#define FREESTYLE_CONTROL_SCRIPT_MODE 1 #define FREESTYLE_CONTROL_SCRIPT_MODE 1

@ -265,6 +265,9 @@ if(WITH_BULLET)
endif() endif()
if(WITH_FREESTYLE) if(WITH_FREESTYLE)
list(APPEND INC
../../freestyle
)
add_definitions(-DWITH_FREESTYLE) add_definitions(-DWITH_FREESTYLE)
endif() endif()

@ -143,6 +143,7 @@ if env['WITH_BF_CYCLES']:
if env['WITH_BF_FREESTYLE']: if env['WITH_BF_FREESTYLE']:
defs.append('WITH_FREESTYLE') defs.append('WITH_FREESTYLE')
incs += ' ../../freestyle'
if env['OURPLATFORM'] == 'linux': if env['OURPLATFORM'] == 'linux':
cflags='-pthread' cflags='-pthread'

@ -370,6 +370,10 @@ EnumPropertyItem bake_save_mode_items[] = {
#include "RE_engine.h" #include "RE_engine.h"
#ifdef WITH_FREESTYLE
#include "FRS_freestyle.h"
#endif
static void rna_SpaceImageEditor_uv_sculpt_update(Main *bmain, Scene *scene, PointerRNA *UNUSED(ptr)) static void rna_SpaceImageEditor_uv_sculpt_update(Main *bmain, Scene *scene, PointerRNA *UNUSED(ptr))
{ {
ED_space_image_uv_sculpt_update(bmain->wm.first, scene->toolsettings); ED_space_image_uv_sculpt_update(bmain->wm.first, scene->toolsettings);
@ -1179,6 +1183,13 @@ static void rna_Scene_freestyle_update(Main *UNUSED(bmain), Scene *UNUSED(scene)
DAG_id_tag_update(&scene->id, 0); DAG_id_tag_update(&scene->id, 0);
} }
static void rna_Scene_use_view_map_cache_update(Main *UNUSED(bmain), Scene *UNUSED(scene), PointerRNA *ptr)
{
#ifdef WITH_FREESTYLE
FRS_free_view_map_cache();
#endif
}
static void rna_SceneRenderLayer_name_set(PointerRNA *ptr, const char *value) static void rna_SceneRenderLayer_name_set(PointerRNA *ptr, const char *value)
{ {
Scene *scene = (Scene *)ptr->id.data; Scene *scene = (Scene *)ptr->id.data;
@ -3109,6 +3120,11 @@ static void rna_def_freestyle_settings(BlenderRNA *brna)
"Enable advanced edge detection options (sphere radius and Kr derivative epsilon)"); "Enable advanced edge detection options (sphere radius and Kr derivative epsilon)");
RNA_def_property_update(prop, NC_SCENE | ND_RENDER_OPTIONS, "rna_Scene_freestyle_update"); RNA_def_property_update(prop, NC_SCENE | ND_RENDER_OPTIONS, "rna_Scene_freestyle_update");
prop = RNA_def_property(srna, "use_view_map_cache", PROP_BOOLEAN, PROP_NONE);
RNA_def_property_boolean_sdna(prop, NULL, "flags", FREESTYLE_VIEW_MAP_CACHE);
RNA_def_property_ui_text(prop, "View Map Cache", "Keep the computed view map and avoid re-calculating it if mesh geometry is unchanged");
RNA_def_property_update(prop, NC_SCENE | ND_RENDER_OPTIONS, "rna_Scene_use_view_map_cache_update");
prop = RNA_def_property(srna, "sphere_radius", PROP_FLOAT, PROP_NONE); prop = RNA_def_property(srna, "sphere_radius", PROP_FLOAT, PROP_NONE);
RNA_def_property_float_sdna(prop, NULL, "sphere_radius"); RNA_def_property_float_sdna(prop, NULL, "sphere_radius");
RNA_def_property_range(prop, 0.0, 1000.0); RNA_def_property_range(prop, 0.0, 1000.0);