mirror of
https://github.com/blender/blender
synced 2026-09-29 04:37:17 +03:00
Cycles: Add Blender UI and operators for texture cache
* Performance > Texture Cache panel with enable and auto-generate options. * Operator to generate texture caches for all images used by shader nodes in a blend file. * Texture cache directory preference with support for both absolute and image relative paths. * Replace Simplify > Texture limit with Texture Resolution factor, that works with both full and tiled images. The texture cache is enable by default but auto generate is not. This means that if you are already image files with tiles and mipmaps it will use them, like the attic, bistro or alab scenes. But it will not automatically generate tx files. Pull Request: https://projects.blender.org/blender/blender/pulls/154913
This commit is contained in:
parent
f791c7164f
commit
e9b53b86f0
24 changed files with 413 additions and 52 deletions
|
|
@ -4,10 +4,6 @@
|
|||
|
||||
#pragma once
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
namespace blender {
|
||||
|
||||
struct Image;
|
||||
|
|
@ -28,7 +24,3 @@ bool CCL_generate_texture_cache(const Image *image,
|
|||
const char *texture_cache_directory = "");
|
||||
|
||||
} // namespace blender
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
|
|
|
|||
|
|
@ -178,16 +178,6 @@ enum_device_type = (
|
|||
('ONEAPI', "oneAPI", "oneAPI", 6)
|
||||
)
|
||||
|
||||
enum_texture_limit = (
|
||||
('OFF', "No Limit", "No texture size limit", 0),
|
||||
('128', "128", "Limit texture size to 128 pixels", 1),
|
||||
('256', "256", "Limit texture size to 256 pixels", 2),
|
||||
('512', "512", "Limit texture size to 512 pixels", 3),
|
||||
('1024', "1024", "Limit texture size to 1024 pixels", 4),
|
||||
('2048', "2048", "Limit texture size to 2048 pixels", 5),
|
||||
('4096', "4096", "Limit texture size to 4096 pixels", 6),
|
||||
('8192', "8192", "Limit texture size to 8192 pixels", 7),
|
||||
)
|
||||
|
||||
enum_fast_gi_method = (
|
||||
('REPLACE', "Replace", "Replace global illumination with ambient occlusion after a specified number of bounces"),
|
||||
|
|
@ -1009,18 +999,20 @@ class CyclesRenderSettings(bpy.types.PropertyGroup):
|
|||
subtype='FACTOR',
|
||||
)
|
||||
|
||||
texture_limit: EnumProperty(
|
||||
name="Viewport Texture Limit",
|
||||
default='OFF',
|
||||
description="Limit texture size used by viewport rendering",
|
||||
items=enum_texture_limit
|
||||
texture_resolution: FloatProperty(
|
||||
name="Viewport Texture Resolution",
|
||||
default=1.0,
|
||||
description="Scale factor for texture resolution used by viewport rendering",
|
||||
min=0.00001, max=1.0,
|
||||
subtype='FACTOR',
|
||||
)
|
||||
|
||||
texture_limit_render: EnumProperty(
|
||||
name="Render Texture Limit",
|
||||
default='OFF',
|
||||
description="Limit texture size used by final rendering",
|
||||
items=enum_texture_limit
|
||||
texture_resolution_render: FloatProperty(
|
||||
name="Render Texture Resolution",
|
||||
default=1.0,
|
||||
description="Scale factor for texture resolution used by final rendering",
|
||||
min=0.0, max=1.0,
|
||||
subtype='FACTOR',
|
||||
)
|
||||
|
||||
use_fast_gi: BoolProperty(
|
||||
|
|
|
|||
|
|
@ -878,6 +878,31 @@ class CYCLES_RENDER_PT_performance_memory(CyclesButtonsPanel, Panel):
|
|||
layout.prop(cscene, "tile_size")
|
||||
|
||||
|
||||
class CYCLES_RENDER_PT_performance_texture_cache(CyclesButtonsPanel, Panel):
|
||||
bl_label = "Texture Cache"
|
||||
bl_parent_id = "CYCLES_RENDER_PT_performance"
|
||||
|
||||
def draw_header(self, context):
|
||||
rd = context.scene.render
|
||||
self.layout.prop(rd, "use_texture_cache", text="")
|
||||
|
||||
def draw(self, context):
|
||||
layout = self.layout
|
||||
layout.use_property_split = True
|
||||
layout.use_property_decorate = False
|
||||
|
||||
rd = context.scene.render
|
||||
|
||||
col = layout.column()
|
||||
col.active = rd.use_texture_cache
|
||||
|
||||
col.prop(rd, "use_auto_generate_texture_cache", text="Auto Generate")
|
||||
|
||||
row = col.split(factor=0.4)
|
||||
row.label()
|
||||
row.operator("render.generate_texture_cache", text="Generate All")
|
||||
|
||||
|
||||
class CYCLES_RENDER_PT_performance_acceleration_structure(CyclesButtonsPanel, Panel):
|
||||
bl_label = "Acceleration Structure"
|
||||
bl_parent_id = "CYCLES_RENDER_PT_performance"
|
||||
|
|
@ -2296,7 +2321,7 @@ class CYCLES_RENDER_PT_simplify_viewport(CyclesButtonsPanel, Panel):
|
|||
col = layout.column()
|
||||
col.prop(rd, "simplify_subdivision", text="Max Subdivision")
|
||||
col.prop(rd, "simplify_child_particles", text="Child Particles")
|
||||
col.prop(cscene, "texture_limit", text="Texture Limit")
|
||||
col.prop(cscene, "texture_resolution", text="Texture Resolution")
|
||||
col.prop(rd, "simplify_volumes", text="Volume Resolution")
|
||||
col.prop(rd, "use_simplify_normals", text="Normals")
|
||||
|
||||
|
|
@ -2322,7 +2347,7 @@ class CYCLES_RENDER_PT_simplify_render(CyclesButtonsPanel, Panel):
|
|||
|
||||
col.prop(rd, "simplify_subdivision_render", text="Max Subdivision")
|
||||
col.prop(rd, "simplify_child_particles_render", text="Child Particles")
|
||||
col.prop(cscene, "texture_limit_render", text="Texture Limit")
|
||||
col.prop(cscene, "texture_resolution_render", text="Texture Resolution")
|
||||
|
||||
|
||||
class CYCLES_RENDER_PT_simplify_culling(CyclesButtonsPanel, Panel):
|
||||
|
|
@ -2552,6 +2577,7 @@ classes = (
|
|||
CYCLES_RENDER_PT_performance_compositor_denoise_settings,
|
||||
CYCLES_RENDER_PT_performance_threads,
|
||||
CYCLES_RENDER_PT_performance_memory,
|
||||
CYCLES_RENDER_PT_performance_texture_cache,
|
||||
CYCLES_RENDER_PT_performance_acceleration_structure,
|
||||
CYCLES_RENDER_PT_performance_final_render,
|
||||
CYCLES_RENDER_PT_performance_viewport,
|
||||
|
|
|
|||
|
|
@ -101,7 +101,7 @@ def do_versions(self):
|
|||
library_versions.setdefault(library.version, []).append(library)
|
||||
|
||||
# Do versioning per library, since they might have different versions.
|
||||
max_need_versioning = (5, 0, 77)
|
||||
max_need_versioning = (5, 2, 8)
|
||||
for version, libraries in library_versions.items():
|
||||
if version > max_need_versioning:
|
||||
continue
|
||||
|
|
@ -111,6 +111,17 @@ def do_versions(self):
|
|||
if scene.library not in libraries:
|
||||
continue
|
||||
|
||||
# Texture limit enum replaced with texture resolution float.
|
||||
# Convert assuming a 1024 image resolution.
|
||||
if version <= (5, 2, 8):
|
||||
cscene = scene.cycles
|
||||
for prop_old, prop_new in (("texture_limit", "texture_resolution"),
|
||||
("texture_limit_render", "texture_resolution_render")):
|
||||
texture_limit_enum = cscene.get(prop_old, 0)
|
||||
if texture_limit_enum > 0:
|
||||
limit_pixels = 1 << (texture_limit_enum + 6)
|
||||
cscene[prop_new] = min(limit_pixels / 1024.0, 1.0)
|
||||
|
||||
# Auto tiling is always enabled now
|
||||
if version <= (5, 0, 77):
|
||||
cscene = scene.cycles
|
||||
|
|
|
|||
|
|
@ -349,7 +349,7 @@ static void blender_camera_from_object(BlenderCamera *bcam,
|
|||
}
|
||||
else {
|
||||
bcam->custom_filepath = blender_absolute_path(
|
||||
b_data, b_camera.id, b_camera.custom_filepath);
|
||||
b_data, &b_camera.id, b_camera.custom_filepath);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -121,7 +121,7 @@ void BlenderSession::create_session()
|
|||
const SessionParams session_params = BlenderSync::get_session_params(
|
||||
b_engine, b_userpref, *b_scene, background, pixelsize);
|
||||
const SceneParams scene_params = BlenderSync::get_scene_params(
|
||||
*b_scene, background, use_developer_ui);
|
||||
b_userpref, *b_data, *b_scene, background, use_developer_ui);
|
||||
const bool session_pause = BlenderSync::get_session_pause(*b_scene, background);
|
||||
|
||||
/* reset status/progress */
|
||||
|
|
@ -208,7 +208,7 @@ void BlenderSession::reset_session(blender::Main &b_data, blender::Depsgraph &b_
|
|||
const SessionParams session_params = BlenderSync::get_session_params(
|
||||
b_engine, b_userpref, *b_scene, background, pixelsize);
|
||||
const SceneParams scene_params = BlenderSync::get_scene_params(
|
||||
*b_scene, background, use_developer_ui);
|
||||
b_userpref, b_data, *b_scene, background, use_developer_ui);
|
||||
|
||||
if (scene->params.modified(scene_params) || session->params.modified(session_params) ||
|
||||
(this->b_render->mode & blender::R_PERSISTENT_DATA) == 0)
|
||||
|
|
@ -782,7 +782,7 @@ void BlenderSession::synchronize(blender::Depsgraph &b_depsgraph_)
|
|||
const SessionParams session_params = BlenderSync::get_session_params(
|
||||
b_engine, b_userpref, *b_scene, background, pixelsize);
|
||||
const SceneParams scene_params = BlenderSync::get_scene_params(
|
||||
*b_scene, background, use_developer_ui);
|
||||
b_userpref, *b_data, *b_scene, background, use_developer_ui);
|
||||
const bool session_pause = BlenderSync::get_session_pause(*b_scene, background);
|
||||
|
||||
if (session->params.modified(session_params) || scene->params.modified(scene_params)) {
|
||||
|
|
|
|||
|
|
@ -255,7 +255,8 @@ static void set_default_value(ShaderInput *input,
|
|||
}
|
||||
case SocketType::STRING: {
|
||||
const auto &default_value = *b_sock.default_value_typed<blender::bNodeSocketValueString>();
|
||||
node->set(socket, (ustring)blender_absolute_path(b_data, b_id, default_value.value).c_str());
|
||||
node->set(socket,
|
||||
(ustring)blender_absolute_path(b_data, &b_id, default_value.value).c_str());
|
||||
break;
|
||||
}
|
||||
default:
|
||||
|
|
@ -812,7 +813,7 @@ static ShaderNode *add_node(Scene *scene,
|
|||
}
|
||||
else {
|
||||
const string absolute_filepath = blender_absolute_path(
|
||||
b_data, b_ntree.id, storage.filepath);
|
||||
b_data, &b_ntree.id, storage.filepath);
|
||||
node = OSLShaderManager::osl_node(graph, scene, absolute_filepath, "");
|
||||
}
|
||||
}
|
||||
|
|
@ -1044,7 +1045,7 @@ static ShaderNode *add_node(Scene *scene,
|
|||
IESLightNode *ies = graph->create_node<IESLightNode>();
|
||||
switch (storage.mode) {
|
||||
case blender::NODE_IES_EXTERNAL:
|
||||
ies->set_filename(ustring(blender_absolute_path(b_data, b_ntree.id, storage.filepath)));
|
||||
ies->set_filename(ustring(blender_absolute_path(b_data, &b_ntree.id, storage.filepath)));
|
||||
break;
|
||||
case blender::NODE_IES_INTERNAL:
|
||||
ustring ies_content = ustring(get_text_datablock_content(b_node.id));
|
||||
|
|
|
|||
|
|
@ -4,6 +4,8 @@
|
|||
|
||||
#include "BKE_appdir.hh"
|
||||
#include "DEG_depsgraph_query.hh"
|
||||
#include "DNA_scene_types.h"
|
||||
#include "DNA_userdef_types.h"
|
||||
#include "DNA_world_types.h"
|
||||
#include "RNA_prototypes.hh"
|
||||
#include "RNA_types.hh"
|
||||
|
|
@ -911,7 +913,9 @@ void BlenderSync::free_data_after_sync(blender::Depsgraph &b_depsgraph)
|
|||
|
||||
/* Scene Parameters */
|
||||
|
||||
SceneParams BlenderSync::get_scene_params(blender::Scene &b_scene,
|
||||
SceneParams BlenderSync::get_scene_params(blender::UserDef &b_preferences,
|
||||
blender::Main &b_data,
|
||||
blender::Scene &b_scene,
|
||||
const bool background,
|
||||
const bool use_developer_ui)
|
||||
{
|
||||
|
|
@ -944,23 +948,27 @@ SceneParams BlenderSync::get_scene_params(blender::Scene &b_scene,
|
|||
params.hair_shape = (CurveShapeType)get_enum(
|
||||
csscene, "shape", CURVE_NUM_SHAPE_TYPES, CURVE_THICK);
|
||||
|
||||
int texture_limit;
|
||||
float texture_resolution;
|
||||
if (background) {
|
||||
texture_limit = RNA_enum_get(&cscene, "texture_limit_render");
|
||||
texture_resolution = RNA_float_get(&cscene, "texture_resolution_render");
|
||||
}
|
||||
else {
|
||||
texture_limit = RNA_enum_get(&cscene, "texture_limit");
|
||||
texture_resolution = RNA_float_get(&cscene, "texture_resolution");
|
||||
}
|
||||
if (texture_limit > 0 && (b_scene.r.mode & blender::R_SIMPLIFY) != 0) {
|
||||
params.texture_resolution = 1 << (texture_limit + 6);
|
||||
if (texture_resolution < 1.0f && (b_scene.r.mode & blender::R_SIMPLIFY) != 0) {
|
||||
params.texture_resolution = texture_resolution;
|
||||
}
|
||||
else {
|
||||
params.texture_resolution = 0;
|
||||
params.texture_resolution = 1.0f;
|
||||
}
|
||||
|
||||
params.bvh_layout = DebugFlags().cpu.bvh_layout;
|
||||
|
||||
params.background = background;
|
||||
params.use_texture_cache = b_scene.r.scemode & blender::R_USE_TEXTURE_CACHE;
|
||||
params.auto_texture_cache = b_scene.r.scemode & blender::R_TEXTURE_CACHE_AUTO_GENERATE;
|
||||
params.texture_cache_path = blender_absolute_path(
|
||||
b_data, nullptr, b_preferences.texture_cachedir);
|
||||
|
||||
return params;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -4,6 +4,7 @@
|
|||
|
||||
#pragma once
|
||||
|
||||
#include "DNA_userdef_types.h"
|
||||
#include "RNA_types.hh"
|
||||
|
||||
#include "blender/id_map.h"
|
||||
|
|
@ -95,7 +96,9 @@ class BlenderSync {
|
|||
void free_data_after_sync(blender::Depsgraph &b_depsgraph);
|
||||
|
||||
/* get parameters */
|
||||
static SceneParams get_scene_params(blender::Scene &b_scene,
|
||||
static SceneParams get_scene_params(blender::UserDef &b_preferences,
|
||||
blender::Main &b_data,
|
||||
blender::Scene &b_scene,
|
||||
const bool background,
|
||||
const bool use_developer_ui);
|
||||
static SessionParams get_session_params(blender::RenderEngine &b_engine,
|
||||
|
|
|
|||
|
|
@ -1,4 +1,4 @@
|
|||
/* SPDX-FileCopyrightText: 2011-2022 Blender Foundation
|
||||
/* SPDX-FileCopyrightText: 2011-2026 Blender Foundation
|
||||
*
|
||||
* SPDX-License-Identifier: Apache-2.0 */
|
||||
|
||||
|
|
|
|||
|
|
@ -477,14 +477,14 @@ static inline void set_string(blender::PointerRNA &ptr, const char *name, const
|
|||
/* Relative Paths */
|
||||
|
||||
static inline string blender_absolute_path(blender::Main &b_data,
|
||||
blender::ID &b_id,
|
||||
blender::ID *b_id,
|
||||
const string &path)
|
||||
{
|
||||
if (path.size() >= 2 && path[0] == '/' && path[1] == '/') {
|
||||
string dirname;
|
||||
|
||||
if (b_id.lib) {
|
||||
dirname = blender_absolute_path(b_data, b_id.lib->id, b_id.lib->filepath);
|
||||
if (b_id && b_id->lib) {
|
||||
dirname = blender_absolute_path(b_data, &b_id->lib->id, b_id->lib->filepath);
|
||||
}
|
||||
else {
|
||||
dirname = b_data.filepath;
|
||||
|
|
|
|||
|
|
@ -1657,6 +1657,7 @@ class USERPREF_PT_file_paths_render(FilePathsPanel, Panel):
|
|||
paths = context.preferences.filepaths
|
||||
|
||||
col = self.layout.column()
|
||||
col.prop(paths, "texture_cache_directory", text="Texture Cache")
|
||||
col.prop(paths, "render_output_directory", text="Render Output")
|
||||
col.prop(paths, "render_cache_directory", text="Render Cache")
|
||||
|
||||
|
|
|
|||
|
|
@ -30,7 +30,7 @@ namespace blender {
|
|||
|
||||
/* Blender file format version. */
|
||||
#define BLENDER_FILE_VERSION BLENDER_VERSION
|
||||
#define BLENDER_FILE_SUBVERSION 14
|
||||
#define BLENDER_FILE_SUBVERSION 15
|
||||
|
||||
/* Minimum Blender version that supports reading file written with the current
|
||||
* version. Older Blender versions will test this and cancel loading the file, showing a warning to
|
||||
|
|
|
|||
|
|
@ -11,6 +11,7 @@
|
|||
#include "DNA_ID.h"
|
||||
#include "DNA_brush_types.h"
|
||||
#include "DNA_curve_types.h"
|
||||
#include "DNA_scene_types.h"
|
||||
#include "DNA_screen_types.h"
|
||||
|
||||
#include "BLI_listbase_iterator.hh"
|
||||
|
|
@ -195,6 +196,11 @@ void blo_do_versions_520(FileData * /*fd*/, Library * /*lib*/, Main *bmain)
|
|||
fix_single_point_curves_custom_knots(bmain);
|
||||
}
|
||||
|
||||
if (!MAIN_VERSION_FILE_ATLEAST(bmain, 502, 15)) {
|
||||
for (Scene &scene : bmain->scenes) {
|
||||
scene.r.scemode |= R_USE_TEXTURE_CACHE;
|
||||
}
|
||||
}
|
||||
/**
|
||||
* Always bump subversion in BKE_blender_version.h when adding versioning
|
||||
* code here, and wrap it inside a MAIN_VERSION_FILE_ATLEAST check.
|
||||
|
|
|
|||
|
|
@ -21,6 +21,7 @@
|
|||
|
||||
#include "ED_screen.hh"
|
||||
|
||||
#include "UI_resources.hh"
|
||||
#include "WM_api.hh"
|
||||
|
||||
#include "UI_interface_c.hh"
|
||||
|
|
@ -212,6 +213,15 @@ void template_running_jobs(Layout *layout, bContext *C)
|
|||
}
|
||||
}
|
||||
|
||||
/* Blend file wide jobs. */
|
||||
if (owner == nullptr) {
|
||||
if (WM_jobs_test(wm, bmain, WM_JOB_TYPE_GENERATE_TEXTURE_CACHE)) {
|
||||
owner = bmain;
|
||||
cancel_fn = set_global_break;
|
||||
icon = ICON_TEXTURE;
|
||||
}
|
||||
}
|
||||
|
||||
if (owner) {
|
||||
const uiFontStyle *fstyle = UI_FSTYLE_WIDGET;
|
||||
const bool active = !(G.is_break || WM_jobs_is_stopped(wm, owner));
|
||||
|
|
|
|||
|
|
@ -18,6 +18,7 @@ set(SRC
|
|||
render_ops.cc
|
||||
render_preview.cc
|
||||
render_shading.cc
|
||||
render_texture_cache.cc
|
||||
render_update.cc
|
||||
render_view.cc
|
||||
|
||||
|
|
@ -61,6 +62,11 @@ if(WITH_FREESTYLE)
|
|||
add_definitions(-DWITH_FREESTYLE)
|
||||
endif()
|
||||
|
||||
if(WITH_CYCLES)
|
||||
add_definitions(-DWITH_CYCLES)
|
||||
list(APPEND INC ../../../../intern/cycles/blender)
|
||||
endif()
|
||||
|
||||
blender_add_lib(bf_editor_render "${SRC}" "${INC}" "${INC_SYS}" "${LIB}")
|
||||
|
||||
# RNA_prototypes.hh
|
||||
|
|
|
|||
|
|
@ -74,6 +74,10 @@ void TEXTURE_OT_slot_copy(wmOperatorType *ot);
|
|||
void TEXTURE_OT_slot_paste(wmOperatorType *ot);
|
||||
void TEXTURE_OT_slot_move(wmOperatorType *ot);
|
||||
|
||||
/* `render_texture_cache.cc` */
|
||||
|
||||
void RENDER_OT_generate_texture_cache(wmOperatorType *ot);
|
||||
|
||||
/* `render_internal.cc` */
|
||||
|
||||
/* Base class for all WM_JOB_TYPE_RENDER jobs. */
|
||||
|
|
|
|||
|
|
@ -76,6 +76,10 @@ void ED_operatortypes_render()
|
|||
WM_operatortype_append(TEXTURE_OT_slot_paste);
|
||||
WM_operatortype_append(TEXTURE_OT_slot_move);
|
||||
|
||||
#ifdef WITH_CYCLES
|
||||
WM_operatortype_append(RENDER_OT_generate_texture_cache);
|
||||
#endif
|
||||
|
||||
/* `render_internal.cc` */
|
||||
WM_operatortype_append(RENDER_OT_view_show);
|
||||
WM_operatortype_append(RENDER_OT_render);
|
||||
|
|
|
|||
262
source/blender/editors/render/render_texture_cache.cc
Normal file
262
source/blender/editors/render/render_texture_cache.cc
Normal file
|
|
@ -0,0 +1,262 @@
|
|||
/* SPDX-FileCopyrightText: 2025 Blender Authors
|
||||
*
|
||||
* SPDX-License-Identifier: GPL-2.0-or-later */
|
||||
|
||||
/** \file
|
||||
* \ingroup edrend
|
||||
*/
|
||||
|
||||
#ifdef WITH_CYCLES
|
||||
|
||||
# include "render_intern.hh" /* own include */
|
||||
|
||||
# include <mutex>
|
||||
|
||||
# include "MEM_guardedalloc.h"
|
||||
|
||||
# include "DNA_image_types.h"
|
||||
# include "DNA_userdef_types.h"
|
||||
# include "DNA_windowmanager_enums.h"
|
||||
|
||||
# include "BLI_fileops.h"
|
||||
# include "BLI_listbase.h"
|
||||
# include "BLI_path_utils.hh"
|
||||
# include "BLI_set.hh"
|
||||
# include "BLI_string.h"
|
||||
# include "BLI_task.hh"
|
||||
# include "BLI_vector.hh"
|
||||
|
||||
# include "BKE_context.hh"
|
||||
# include "BKE_global.hh"
|
||||
# include "BKE_image.hh"
|
||||
# include "BKE_lib_query.hh"
|
||||
# include "BKE_main.hh"
|
||||
# include "BKE_node.hh"
|
||||
# include "BKE_report.hh"
|
||||
|
||||
# include "BLT_translation.hh"
|
||||
|
||||
# include "WM_api.hh"
|
||||
|
||||
# include "CCL_api.h"
|
||||
|
||||
namespace blender {
|
||||
|
||||
/* -------------------------------------------------------------------- */
|
||||
/** \name Generate Texture Cache Operator
|
||||
* \{ */
|
||||
|
||||
struct GenerateTextureCacheJob {
|
||||
Main *bmain;
|
||||
ReportList *reports;
|
||||
};
|
||||
|
||||
static void generate_texture_cache(Main *bmain,
|
||||
ReportList *reports,
|
||||
wmJobWorkerStatus *worker_status = nullptr)
|
||||
{
|
||||
/* Gather images to generate for. */
|
||||
blender::Set<const Image *> images;
|
||||
|
||||
FOREACH_NODETREE_BEGIN (bmain, ntree, id) {
|
||||
if (ntree->type != NTREE_SHADER) {
|
||||
continue;
|
||||
}
|
||||
BKE_library_foreach_ID_link(
|
||||
bmain,
|
||||
&ntree->id,
|
||||
[&](LibraryIDLinkCallbackData *cb_data) {
|
||||
ID *image_id = *cb_data->id_pointer;
|
||||
if (image_id && GS(image_id->name) == ID_IM) {
|
||||
Image *image = blender::id_cast<Image *>(image_id);
|
||||
images.add(image);
|
||||
}
|
||||
return IDWALK_RET_NOP;
|
||||
},
|
||||
nullptr,
|
||||
IDWALK_READONLY);
|
||||
}
|
||||
FOREACH_NODETREE_END;
|
||||
|
||||
/* Gather filepaths to generate for, expanding UDIMs and sequences. */
|
||||
blender::Set<std::pair<const Image *, std::string>> filepaths;
|
||||
int total = 0;
|
||||
|
||||
for (const Image *image : images) {
|
||||
if (ELEM(image->source, IMA_SRC_MOVIE, IMA_SRC_GENERATED, IMA_SRC_VIEWER)) {
|
||||
continue;
|
||||
}
|
||||
if (BKE_image_has_packedfile(image)) {
|
||||
continue;
|
||||
}
|
||||
if (image->filepath[0] == '\0') {
|
||||
continue;
|
||||
}
|
||||
|
||||
/* Get regular absolute path. */
|
||||
char filepath[FILE_MAX];
|
||||
STRNCPY(filepath, image->filepath);
|
||||
BLI_path_abs(filepath, ID_BLEND_PATH_FROM_GLOBAL(&image->id));
|
||||
BLI_path_normalize(filepath);
|
||||
|
||||
/* Handle each UDIM tile. */
|
||||
if (image->source == IMA_SRC_TILED) {
|
||||
eUDIM_TILE_FORMAT tile_format = UDIM_TILE_FORMAT_NONE;
|
||||
char *udim_pattern = BKE_image_get_tile_strformat(filepath, &tile_format);
|
||||
|
||||
if (tile_format != UDIM_TILE_FORMAT_NONE) {
|
||||
for (const ImageTile &tile : image->tiles) {
|
||||
char tile_filepath[FILE_MAX];
|
||||
BKE_image_set_filepath_from_tile_number(
|
||||
tile_filepath, udim_pattern, tile_format, tile.tile_number);
|
||||
if (BLI_is_file(tile_filepath)) {
|
||||
if (!CCL_has_texture_cache(image, tile_filepath, U.texture_cachedir)) {
|
||||
filepaths.add({image, tile_filepath});
|
||||
}
|
||||
total++;
|
||||
}
|
||||
}
|
||||
MEM_delete(udim_pattern);
|
||||
continue;
|
||||
}
|
||||
}
|
||||
|
||||
if (image->source == IMA_SRC_SEQUENCE) {
|
||||
/* TODO: Handle image sequence. */
|
||||
}
|
||||
|
||||
/* Handle regular image. */
|
||||
if (BLI_is_file(filepath)) {
|
||||
if (!CCL_has_texture_cache(image, filepath, U.texture_cachedir)) {
|
||||
filepaths.add({image, filepath});
|
||||
}
|
||||
total++;
|
||||
}
|
||||
}
|
||||
|
||||
/* Generate texture cache. */
|
||||
std::atomic<int> completed = 0;
|
||||
std::atomic<int> failed = 0;
|
||||
std::mutex reports_mutex;
|
||||
|
||||
blender::threading::parallel_for_each(filepaths, [&](const auto &item) {
|
||||
if (worker_status) {
|
||||
if (G.is_break || worker_status->stop) {
|
||||
return;
|
||||
}
|
||||
worker_status->progress = (completed + failed) / float(filepaths.size());
|
||||
worker_status->do_update = true;
|
||||
}
|
||||
|
||||
if (CCL_generate_texture_cache(item.first, item.second.c_str(), U.texture_cachedir)) {
|
||||
completed++;
|
||||
}
|
||||
else {
|
||||
std::scoped_lock lock(reports_mutex);
|
||||
BKE_reportf(
|
||||
reports, RPT_ERROR, "Failed to generate texture cache for: %s", item.second.c_str());
|
||||
failed++;
|
||||
}
|
||||
});
|
||||
|
||||
/* Report stats. */
|
||||
if (total == 0) {
|
||||
BKE_report(reports, RPT_INFO, "No image files found to generate tx files");
|
||||
}
|
||||
else {
|
||||
BKE_reportf(reports,
|
||||
(failed) ? RPT_WARNING : RPT_INFO,
|
||||
"Generated %d tx files, %d failed, %d up to date",
|
||||
completed.load(),
|
||||
failed.load(),
|
||||
total - int(filepaths.size()));
|
||||
}
|
||||
}
|
||||
|
||||
static wmOperatorStatus generate_texture_cache_exec(bContext *C, wmOperator *op)
|
||||
{
|
||||
generate_texture_cache(CTX_data_main(C), op->reports);
|
||||
return OPERATOR_FINISHED;
|
||||
}
|
||||
|
||||
static void generate_texture_cache_startjob(void *customdata, wmJobWorkerStatus *worker_status)
|
||||
{
|
||||
GenerateTextureCacheJob *job = static_cast<GenerateTextureCacheJob *>(customdata);
|
||||
generate_texture_cache(job->bmain, job->reports, worker_status);
|
||||
}
|
||||
|
||||
static wmOperatorStatus generate_texture_cache_invoke(bContext *C,
|
||||
wmOperator *op,
|
||||
const wmEvent * /*event*/)
|
||||
{
|
||||
wmWindowManager *wm = CTX_wm_manager(C);
|
||||
Main *bmain = CTX_data_main(C);
|
||||
|
||||
wmJob *wm_job = WM_jobs_get(wm,
|
||||
CTX_wm_window(C),
|
||||
bmain,
|
||||
RPT_("Generating texture cache..."),
|
||||
WM_JOB_PRIORITY | WM_JOB_PROGRESS,
|
||||
WM_JOB_TYPE_GENERATE_TEXTURE_CACHE);
|
||||
|
||||
GenerateTextureCacheJob *job = MEM_new<GenerateTextureCacheJob>(__func__);
|
||||
job->bmain = bmain;
|
||||
job->reports = op->reports;
|
||||
WM_jobs_customdata_set(wm_job, job, [](void *customdata) {
|
||||
MEM_delete(static_cast<GenerateTextureCacheJob *>(customdata));
|
||||
});
|
||||
|
||||
WM_jobs_timer(wm_job, 0.2, NC_WM | ND_JOB, 0);
|
||||
WM_jobs_callbacks(wm_job, generate_texture_cache_startjob, nullptr, nullptr, nullptr);
|
||||
|
||||
G.is_break = false;
|
||||
WM_jobs_start(wm, wm_job);
|
||||
|
||||
WM_event_add_modal_handler(C, op);
|
||||
|
||||
return OPERATOR_RUNNING_MODAL;
|
||||
}
|
||||
|
||||
static wmOperatorStatus generate_texture_cache_modal(bContext *C,
|
||||
wmOperator * /*op*/,
|
||||
const wmEvent *event)
|
||||
{
|
||||
wmWindowManager *wm = CTX_wm_manager(C);
|
||||
Main *bmain = CTX_data_main(C);
|
||||
|
||||
if (0 == WM_jobs_test(wm, bmain, WM_JOB_TYPE_GENERATE_TEXTURE_CACHE)) {
|
||||
return OPERATOR_FINISHED | OPERATOR_PASS_THROUGH;
|
||||
}
|
||||
|
||||
return (event->type == EVT_ESCKEY) ? OPERATOR_RUNNING_MODAL : OPERATOR_PASS_THROUGH;
|
||||
}
|
||||
|
||||
static void generate_texture_cache_cancel(bContext *C, wmOperator * /*op*/)
|
||||
{
|
||||
wmWindowManager *wm = CTX_wm_manager(C);
|
||||
Main *bmain = CTX_data_main(C);
|
||||
WM_jobs_kill_type(wm, bmain, WM_JOB_TYPE_GENERATE_TEXTURE_CACHE);
|
||||
}
|
||||
|
||||
void RENDER_OT_generate_texture_cache(wmOperatorType *ot)
|
||||
{
|
||||
/* identifiers */
|
||||
ot->name = "Generate Texture Cache";
|
||||
ot->idname = "RENDER_OT_generate_texture_cache";
|
||||
ot->description = "Generate Cycles texture cache files for all images used in shader nodes";
|
||||
|
||||
/* API callbacks. */
|
||||
ot->exec = generate_texture_cache_exec;
|
||||
ot->invoke = generate_texture_cache_invoke;
|
||||
ot->modal = generate_texture_cache_modal;
|
||||
ot->cancel = generate_texture_cache_cancel;
|
||||
|
||||
/* flags */
|
||||
ot->flag = OPTYPE_REGISTER;
|
||||
}
|
||||
|
||||
/** \} */
|
||||
|
||||
} // namespace blender
|
||||
|
||||
#endif
|
||||
|
|
@ -782,6 +782,8 @@ enum {
|
|||
R_SCEMODE_UNUSED_19 = 1 << 19, /* cleared */
|
||||
R_EXR_CACHE_FILE = 1 << 20,
|
||||
R_MULTIVIEW = 1 << 21,
|
||||
R_USE_TEXTURE_CACHE = 1 << 22,
|
||||
R_TEXTURE_CACHE_AUTO_GENERATE = 1 << 23,
|
||||
};
|
||||
|
||||
/** #RenderData::stamp */
|
||||
|
|
@ -886,7 +888,7 @@ struct RenderData {
|
|||
/**
|
||||
* Flags for render settings. Use bit-masking to access the settings.
|
||||
*/
|
||||
int scemode = R_DOCOMP | R_DOSEQ | R_EXTENSION;
|
||||
int scemode = R_DOCOMP | R_DOSEQ | R_EXTENSION | R_USE_TEXTURE_CACHE;
|
||||
|
||||
/**
|
||||
* Flags for render settings. Use bit-masking to access the settings.
|
||||
|
|
|
|||
|
|
@ -897,6 +897,7 @@ struct UserDef {
|
|||
/* EXR cache path */
|
||||
char render_cachedir[/*FILE_MAXDIR*/ 768] = "";
|
||||
char textudir[/*FILE_MAXDIR*/ 768] = "//";
|
||||
char texture_cachedir[/*FILE_MAXDIR*/ 768] = "";
|
||||
/* Deprecated, use #UserDef.script_directories instead. */
|
||||
DNA_DEPRECATED char pythondir_legacy[/*FILE_MAXDIR*/ 768] = "";
|
||||
char sounddir[/*FILE_MAXDIR*/ 768] = "//";
|
||||
|
|
|
|||
|
|
@ -7699,6 +7699,27 @@ static void rna_def_scene_render_data(BlenderRNA *brna)
|
|||
"meshes in the viewport");
|
||||
RNA_def_property_update(prop, 0, "rna_Scene_use_simplify_normals_update");
|
||||
|
||||
/* Texture Cache */
|
||||
prop = RNA_def_property(srna, "use_texture_cache", PROP_BOOLEAN, PROP_NONE);
|
||||
RNA_def_property_boolean_sdna(prop, nullptr, "scemode", R_USE_TEXTURE_CACHE);
|
||||
RNA_def_property_clear_flag(prop, PROP_ANIMATABLE);
|
||||
RNA_def_property_ui_text(
|
||||
prop,
|
||||
"Texture Cache",
|
||||
"Load texture tiles at appropriate resolution on demand to reduce memory usage. This avoids "
|
||||
"loading all textures into memory, at the cost of extra disk space and some perforamnce");
|
||||
RNA_def_property_update(prop, NC_SCENE | ND_RENDER_OPTIONS, nullptr);
|
||||
|
||||
prop = RNA_def_property(srna, "use_auto_generate_texture_cache", PROP_BOOLEAN, PROP_NONE);
|
||||
RNA_def_property_boolean_sdna(prop, nullptr, "scemode", R_TEXTURE_CACHE_AUTO_GENERATE);
|
||||
RNA_def_property_clear_flag(prop, PROP_ANIMATABLE);
|
||||
RNA_def_property_ui_text(prop,
|
||||
"Auto Generate Texture Cache",
|
||||
"Automatically create tx files from image files when rendering, if the "
|
||||
"files do not exist or are outdated. The path to store the texture "
|
||||
"cache files is configured in the preferences");
|
||||
RNA_def_property_update(prop, NC_SCENE | ND_RENDER_OPTIONS, nullptr);
|
||||
|
||||
/* Grease Pencil - Simplify Options */
|
||||
prop = RNA_def_property(srna, "simplify_gpencil", PROP_BOOLEAN, PROP_NONE);
|
||||
RNA_def_property_boolean_sdna(prop, nullptr, "simplify_gpencil", SIMPLIFY_GPENCIL_ENABLE);
|
||||
|
|
|
|||
|
|
@ -7333,6 +7333,16 @@ static void rna_def_userdef_filepaths(BlenderRNA *brna)
|
|||
RNA_def_property_ui_text(prop, "Render Cache Path", "Where to cache raw render results");
|
||||
RNA_def_property_flag(prop, PROP_PATH_SUPPORTS_BLEND_RELATIVE);
|
||||
|
||||
prop = RNA_def_property(srna, "texture_cache_directory", PROP_STRING, PROP_DIRPATH);
|
||||
RNA_def_property_string_sdna(prop, nullptr, "texture_cachedir");
|
||||
RNA_def_property_ui_text(
|
||||
prop,
|
||||
"Texture Cache Path",
|
||||
"The directory for storing tx files generated from image files, for more efficient "
|
||||
"rendering. Paths may be absolute, or relative to the image file. Leave blank to store "
|
||||
"tx files in the same directory as image files");
|
||||
RNA_def_property_flag(prop, PROP_PATH_SUPPORTS_BLEND_RELATIVE);
|
||||
|
||||
prop = RNA_def_property(srna, "image_editor", PROP_STRING, PROP_FILEPATH);
|
||||
RNA_def_property_string_sdna(prop, nullptr, "image_editor");
|
||||
RNA_def_property_ui_text(prop, "Image Editor", "Path to an image editor");
|
||||
|
|
|
|||
|
|
@ -1885,6 +1885,7 @@ enum eWM_JobType {
|
|||
WM_JOB_TYPE_CALCULATE_SIMULATION_NODES,
|
||||
WM_JOB_TYPE_BAKE_GEOMETRY_NODES,
|
||||
WM_JOB_TYPE_UV_PACK,
|
||||
WM_JOB_TYPE_GENERATE_TEXTURE_CACHE,
|
||||
/* Add as needed, bake, seq proxy build
|
||||
* if having hard coded values is a problem. */
|
||||
};
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue