diff options
author | Lukas Stockner <lukas.stockner@freenet.de> | 2019-12-12 18:06:08 +0300 |
---|---|---|
committer | Lukas Stockner <lukas.stockner@freenet.de> | 2019-12-12 20:40:37 +0300 |
commit | c30d6571bb47734e0bcb2fced5cf11cb6d8b1169 (patch) | |
tree | f92f065147296e6c72e6cd85f26711157d81e09c /intern/cycles | |
parent | d7a8a606889fed58775c88bfdc079bee3c9333e2 (diff) |
Add support for tiled images and the UDIM naming scheme
This patch contains the work that I did during my week at the Code Quest - adding support for tiled images to Blender.
With this patch, images now contain a list of tiles. By default, this just contains one tile, but if the source type is set to Tiled, the user can add additional tiles. When acquiring an ImBuf, the tile to be loaded is specified in the ImageUser.
Therefore, code that is not yet aware of tiles will just access the default tile as usual.
The filenames of the additional tiles are derived from the original filename according to the UDIM naming scheme - the filename contains an index that is calculated as (1001 + 10*<y coordinate of the tile> + <x coordinate of the tile>), where the x coordinate never goes above 9.
Internally, the various tiles are stored in a cache just like sequences. When acquired for the first time, the code will try to load the corresponding file from disk. Alternatively, a new operator can be used to initialize the tile similar to the New Image operator.
The following features are supported so far:
- Automatic detection and loading of all tiles when opening the first tile (1001)
- Saving all tiles
- Adding and removing tiles
- Filling tiles with generated images
- Drawing all tiles in the Image Editor
- Viewing a tiled grid even if no image is selected
- Rendering tiled images in Eevee
- Rendering tiled images in Cycles (in SVM mode)
- Automatically skipping loading of unused tiles in Cycles
- 2D texture painting (also across tiles)
- 3D texture painting (also across tiles, only limitation: individual faces can not cross tile borders)
- Assigning custom labels to individual tiles (drawn in the Image Editor instead of the ID)
- Different resolutions between tiles
There still are some missing features that will be added later (see T72390):
- Workbench engine support
- Packing/Unpacking support
- Baking support
- Cycles OSL support
- many other Blender features that rely on images
Thanks to Brecht for the review and to all who tested the intermediate versions!
Differential Revision: https://developer.blender.org/D3509
Diffstat (limited to 'intern/cycles')
-rw-r--r-- | intern/cycles/blender/blender_session.cpp | 10 | ||||
-rw-r--r-- | intern/cycles/blender/blender_session.h | 2 | ||||
-rw-r--r-- | intern/cycles/blender/blender_shader.cpp | 6 | ||||
-rw-r--r-- | intern/cycles/blender/blender_sync.cpp | 3 | ||||
-rw-r--r-- | intern/cycles/blender/blender_util.h | 19 | ||||
-rw-r--r-- | intern/cycles/kernel/svm/svm.h | 2 | ||||
-rw-r--r-- | intern/cycles/kernel/svm/svm_image.h | 53 | ||||
-rw-r--r-- | intern/cycles/render/image.cpp | 2 | ||||
-rw-r--r-- | intern/cycles/render/image.h | 2 | ||||
-rw-r--r-- | intern/cycles/render/light.cpp | 3 | ||||
-rw-r--r-- | intern/cycles/render/mesh.cpp | 47 | ||||
-rw-r--r-- | intern/cycles/render/mesh.h | 3 | ||||
-rw-r--r-- | intern/cycles/render/nodes.cpp | 288 | ||||
-rw-r--r-- | intern/cycles/render/nodes.h | 18 | ||||
-rw-r--r-- | intern/cycles/render/osl.cpp | 15 | ||||
-rw-r--r-- | intern/cycles/render/osl.h | 8 | ||||
-rw-r--r-- | intern/cycles/render/scene.h | 3 | ||||
-rw-r--r-- | intern/cycles/render/svm.cpp | 18 | ||||
-rw-r--r-- | intern/cycles/render/svm.h | 13 | ||||
-rw-r--r-- | intern/cycles/test/render_graph_finalize_test.cpp | 3 |
20 files changed, 369 insertions, 149 deletions
diff --git a/intern/cycles/blender/blender_session.cpp b/intern/cycles/blender/blender_session.cpp index 53f2fdb91b9..78fb49db6c8 100644 --- a/intern/cycles/blender/blender_session.cpp +++ b/intern/cycles/blender/blender_session.cpp @@ -142,9 +142,9 @@ void BlenderSession::create_session() scene->image_manager->builtin_image_info_cb = function_bind( &BlenderSession::builtin_image_info, this, _1, _2, _3); scene->image_manager->builtin_image_pixels_cb = function_bind( - &BlenderSession::builtin_image_pixels, this, _1, _2, _3, _4, _5, _6); + &BlenderSession::builtin_image_pixels, this, _1, _2, _3, _4, _5, _6, _7); scene->image_manager->builtin_image_float_pixels_cb = function_bind( - &BlenderSession::builtin_image_float_pixels, this, _1, _2, _3, _4, _5, _6); + &BlenderSession::builtin_image_float_pixels, this, _1, _2, _3, _4, _5, _6, _7); session->scene = scene; @@ -1210,6 +1210,7 @@ void BlenderSession::builtin_image_info(const string &builtin_name, bool BlenderSession::builtin_image_pixels(const string &builtin_name, void *builtin_data, + int tile, unsigned char *pixels, const size_t pixels_size, const bool associate_alpha, @@ -1229,7 +1230,7 @@ bool BlenderSession::builtin_image_pixels(const string &builtin_name, const int height = b_image.size()[1]; const int channels = b_image.channels(); - unsigned char *image_pixels = image_get_pixels_for_frame(b_image, frame); + unsigned char *image_pixels = image_get_pixels_for_frame(b_image, frame, tile); const size_t num_pixels = ((size_t)width) * height; if (image_pixels && num_pixels * channels == pixels_size) { @@ -1276,6 +1277,7 @@ bool BlenderSession::builtin_image_pixels(const string &builtin_name, bool BlenderSession::builtin_image_float_pixels(const string &builtin_name, void *builtin_data, + int tile, float *pixels, const size_t pixels_size, const bool, @@ -1299,7 +1301,7 @@ bool BlenderSession::builtin_image_float_pixels(const string &builtin_name, const int channels = b_image.channels(); float *image_pixels; - image_pixels = image_get_float_pixels_for_frame(b_image, frame); + image_pixels = image_get_float_pixels_for_frame(b_image, frame, tile); const size_t num_pixels = ((size_t)width) * height; if (image_pixels && num_pixels * channels == pixels_size) { diff --git a/intern/cycles/blender/blender_session.h b/intern/cycles/blender/blender_session.h index 7445fb53458..2f25ec740f9 100644 --- a/intern/cycles/blender/blender_session.h +++ b/intern/cycles/blender/blender_session.h @@ -157,12 +157,14 @@ class BlenderSession { void builtin_image_info(const string &builtin_name, void *builtin_data, ImageMetaData &metadata); bool builtin_image_pixels(const string &builtin_name, void *builtin_data, + int tile, unsigned char *pixels, const size_t pixels_size, const bool associate_alpha, const bool free_cache); bool builtin_image_float_pixels(const string &builtin_name, void *builtin_data, + int tile, float *pixels, const size_t pixels_size, const bool associate_alpha, diff --git a/intern/cycles/blender/blender_shader.cpp b/intern/cycles/blender/blender_shader.cpp index c3564eac940..6bbc73f72ec 100644 --- a/intern/cycles/blender/blender_shader.cpp +++ b/intern/cycles/blender/blender_shader.cpp @@ -665,6 +665,12 @@ static ShaderNode *add_node(Scene *scene, image->animated = b_image_node.image_user().use_auto_refresh(); image->alpha_type = get_image_alpha_type(b_image); + image->tiles.clear(); + BL::Image::tiles_iterator b_iter; + for (b_image.tiles.begin(b_iter); b_iter != b_image.tiles.end(); ++b_iter) { + image->tiles.push_back(b_iter->number()); + } + /* TODO: restore */ /* TODO(sergey): Does not work properly when we change builtin type. */ #if 0 diff --git a/intern/cycles/blender/blender_sync.cpp b/intern/cycles/blender/blender_sync.cpp index bb52c740bfb..332ee3575c0 100644 --- a/intern/cycles/blender/blender_sync.cpp +++ b/intern/cycles/blender/blender_sync.cpp @@ -729,6 +729,9 @@ SceneParams BlenderSync::get_scene_params(BL::Scene &b_scene, bool background) params.bvh_layout = RNA_boolean_get(&cscene, "use_bvh_embree") ? BVH_LAYOUT_EMBREE : params.bvh_layout; #endif + + params.background = background; + return params; } diff --git a/intern/cycles/blender/blender_util.h b/intern/cycles/blender/blender_util.h index cbe61e367fa..efed96ec9f5 100644 --- a/intern/cycles/blender/blender_util.h +++ b/intern/cycles/blender/blender_util.h @@ -34,8 +34,8 @@ extern "C" { void BKE_image_user_frame_calc(void *ima, void *iuser, int cfra); void BKE_image_user_file_path(void *iuser, void *ima, char *path); -unsigned char *BKE_image_get_pixels_for_frame(void *image, int frame); -float *BKE_image_get_float_pixels_for_frame(void *image, int frame); +unsigned char *BKE_image_get_pixels_for_frame(void *image, int frame, int tile); +float *BKE_image_get_float_pixels_for_frame(void *image, int frame, int tile); } CCL_NAMESPACE_BEGIN @@ -234,8 +234,15 @@ static inline int render_resolution_y(BL::RenderSettings &b_render) static inline string image_user_file_path(BL::ImageUser &iuser, BL::Image &ima, int cfra) { char filepath[1024]; + iuser.tile(0); BKE_image_user_frame_calc(NULL, iuser.ptr.data, cfra); BKE_image_user_file_path(iuser.ptr.data, ima.ptr.data, filepath); + if (ima.source() == BL::Image::source_TILED) { + char *udim_id = strstr(filepath, "1001"); + if (udim_id != NULL) { + memcpy(udim_id, "%04d", 4); + } + } return string(filepath); } @@ -245,14 +252,14 @@ static inline int image_user_frame_number(BL::ImageUser &iuser, int cfra) return iuser.frame_current(); } -static inline unsigned char *image_get_pixels_for_frame(BL::Image &image, int frame) +static inline unsigned char *image_get_pixels_for_frame(BL::Image &image, int frame, int tile) { - return BKE_image_get_pixels_for_frame(image.ptr.data, frame); + return BKE_image_get_pixels_for_frame(image.ptr.data, frame, tile); } -static inline float *image_get_float_pixels_for_frame(BL::Image &image, int frame) +static inline float *image_get_float_pixels_for_frame(BL::Image &image, int frame, int tile) { - return BKE_image_get_float_pixels_for_frame(image.ptr.data, frame); + return BKE_image_get_float_pixels_for_frame(image.ptr.data, frame, tile); } static inline void render_add_metadata(BL::RenderResult &b_rr, string name, string value) diff --git a/intern/cycles/kernel/svm/svm.h b/intern/cycles/kernel/svm/svm.h index c4d7164a4d8..fd2833ee687 100644 --- a/intern/cycles/kernel/svm/svm.h +++ b/intern/cycles/kernel/svm/svm.h @@ -311,7 +311,7 @@ ccl_device_noinline void svm_eval_nodes(KernelGlobals *kg, # endif /* NODES_FEATURE(NODE_FEATURE_BUMP) */ # ifdef __TEXTURES__ case NODE_TEX_IMAGE: - svm_node_tex_image(kg, sd, stack, node); + svm_node_tex_image(kg, sd, stack, node, &offset); break; case NODE_TEX_IMAGE_BOX: svm_node_tex_image_box(kg, sd, stack, node); diff --git a/intern/cycles/kernel/svm/svm_image.h b/intern/cycles/kernel/svm/svm_image.h index 64abdd2d8b3..90f1a7845c7 100644 --- a/intern/cycles/kernel/svm/svm_image.h +++ b/intern/cycles/kernel/svm/svm_image.h @@ -20,6 +20,11 @@ CCL_NAMESPACE_BEGIN ccl_device float4 svm_image_texture(KernelGlobals *kg, int id, float x, float y, uint flags) { + if (id == -1) { + return make_float4( + TEX_IMAGE_MISSING_R, TEX_IMAGE_MISSING_G, TEX_IMAGE_MISSING_B, TEX_IMAGE_MISSING_A); + } + float4 r = kernel_tex_image_interp(kg, id, x, y); const float alpha = r.w; @@ -45,9 +50,9 @@ ccl_device_inline float3 texco_remap_square(float3 co) return (co - make_float3(0.5f, 0.5f, 0.5f)) * 2.0f; } -ccl_device void svm_node_tex_image(KernelGlobals *kg, ShaderData *sd, float *stack, uint4 node) +ccl_device void svm_node_tex_image( + KernelGlobals *kg, ShaderData *sd, float *stack, uint4 node, int *offset) { - uint id = node.y; uint co_offset, out_offset, alpha_offset, flags; svm_unpack_node_uchar4(node.z, &co_offset, &out_offset, &alpha_offset, &flags); @@ -65,6 +70,50 @@ ccl_device void svm_node_tex_image(KernelGlobals *kg, ShaderData *sd, float *sta else { tex_co = make_float2(co.x, co.y); } + + /* TODO(lukas): Consider moving tile information out of the SVM node. + * TextureInfo seems a reasonable candidate. */ + int id = -1; + int num_nodes = (int)node.y; + if (num_nodes > 0) { + /* Remember the offset of the node following the tile nodes. */ + int next_offset = (*offset) + num_nodes; + + /* Find the tile that the UV lies in. */ + int tx = (int)tex_co.x; + int ty = (int)tex_co.y; + + /* Check that we're within a legitimate tile. */ + if (tx >= 0 && ty >= 0 && tx < 10) { + int tile = 1001 + 10 * ty + tx; + + /* Find the index of the tile. */ + for (int i = 0; i < num_nodes; i++) { + uint4 tile_node = read_node(kg, offset); + if (tile_node.x == tile) { + id = tile_node.y; + break; + } + if (tile_node.z == tile) { + id = tile_node.w; + break; + } + } + + /* If we found the tile, offset the UVs to be relative to it. */ + if (id != -1) { + tex_co.x -= tx; + tex_co.y -= ty; + } + } + + /* Skip over the remaining nodes. */ + *offset = next_offset; + } + else { + id = -num_nodes; + } + float4 f = svm_image_texture(kg, id, tex_co.x, tex_co.y, flags); if (stack_valid(out_offset)) diff --git a/intern/cycles/render/image.cpp b/intern/cycles/render/image.cpp index 03ecc9bce52..212a867f9cd 100644 --- a/intern/cycles/render/image.cpp +++ b/intern/cycles/render/image.cpp @@ -630,6 +630,7 @@ bool ImageManager::file_load_image(Image *img, if (FileFormat == TypeDesc::FLOAT) { builtin_image_float_pixels_cb(img->filename, img->builtin_data, + 0, /* TODO(lukas): Support tiles here? */ (float *)&pixels[0], num_pixels * components, image_associate_alpha(img), @@ -638,6 +639,7 @@ bool ImageManager::file_load_image(Image *img, else if (FileFormat == TypeDesc::UINT8) { builtin_image_pixels_cb(img->filename, img->builtin_data, + 0, /* TODO(lukas): Support tiles here? */ (uchar *)&pixels[0], num_pixels * components, image_associate_alpha(img), diff --git a/intern/cycles/render/image.h b/intern/cycles/render/image.h index 459cd8c056c..bc04a667953 100644 --- a/intern/cycles/render/image.h +++ b/intern/cycles/render/image.h @@ -130,6 +130,7 @@ class ImageManager { builtin_image_info_cb; function<bool(const string &filename, void *data, + int tile, unsigned char *pixels, const size_t pixels_size, const bool associate_alpha, @@ -137,6 +138,7 @@ class ImageManager { builtin_image_pixels_cb; function<bool(const string &filename, void *data, + int tile, float *pixels, const size_t pixels_size, const bool associate_alpha, diff --git a/intern/cycles/render/light.cpp b/intern/cycles/render/light.cpp index dc3f7c8f8ac..06304205dc9 100644 --- a/intern/cycles/render/light.cpp +++ b/intern/cycles/render/light.cpp @@ -575,7 +575,8 @@ void LightManager::device_update_background(Device *device, if (node->type == EnvironmentTextureNode::node_type) { EnvironmentTextureNode *env = (EnvironmentTextureNode *)node; ImageMetaData metadata; - if (env->image_manager && env->image_manager->get_image_metadata(env->slot, metadata)) { + if (env->image_manager && !env->slots.empty() && + env->image_manager->get_image_metadata(env->slots[0], metadata)) { res.x = max(res.x, metadata.width); res.y = max(res.y, metadata.height); } diff --git a/intern/cycles/render/mesh.cpp b/intern/cycles/render/mesh.cpp index cffe2bfa70a..2bf1040455f 100644 --- a/intern/cycles/render/mesh.cpp +++ b/intern/cycles/render/mesh.cpp @@ -637,6 +637,50 @@ void Mesh::add_subd_face(int *corners, int num_corners, int shader_, bool smooth subd_faces.push_back_reserved(face); } +static void get_uv_tiles_from_attribute(Attribute *attr, int num, unordered_set<int> &tiles) +{ + if (attr == NULL) { + return; + } + + const float2 *uv = attr->data_float2(); + for (int i = 0; i < num; i++, uv++) { + float u = uv->x, v = uv->y; + int x = (int)u, y = (int)v; + + if (x < 0 || y < 0 || x >= 10) { + continue; + } + + /* Be conservative in corners - precisely touching the right or upper edge of a tile + * should not load its right/upper neighbor as well. */ + if (x > 0 && (u < x + 1e-6f)) { + x--; + } + if (y > 0 && (v < y + 1e-6f)) { + y--; + } + + tiles.insert(1001 + 10 * y + x); + } +} + +void Mesh::get_uv_tiles(ustring map, unordered_set<int> &tiles) +{ + if (map.empty()) { + get_uv_tiles_from_attribute(attributes.find(ATTR_STD_UV), num_triangles() * 3, tiles); + get_uv_tiles_from_attribute( + subd_attributes.find(ATTR_STD_UV), subd_face_corners.size() + num_ngons, tiles); + get_uv_tiles_from_attribute(curve_attributes.find(ATTR_STD_UV), num_curves(), tiles); + } + else { + get_uv_tiles_from_attribute(attributes.find(map), num_triangles() * 3, tiles); + get_uv_tiles_from_attribute( + subd_attributes.find(map), subd_face_corners.size() + num_ngons, tiles); + get_uv_tiles_from_attribute(curve_attributes.find(map), num_curves(), tiles); + } +} + void Mesh::compute_bounds() { BoundBox bnds = BoundBox::empty; @@ -2085,8 +2129,7 @@ void MeshManager::device_update_displacement_images(Device *device, } ImageSlotTextureNode *image_node = static_cast<ImageSlotTextureNode *>(node); - int slot = image_node->slot; - if (slot != -1) { + foreach (int slot, image_node->slots) { bump_images.insert(slot); } } diff --git a/intern/cycles/render/mesh.h b/intern/cycles/render/mesh.h index 4a24a9c2656..c5be0ba60b9 100644 --- a/intern/cycles/render/mesh.h +++ b/intern/cycles/render/mesh.h @@ -28,6 +28,7 @@ #include "util/util_list.h" #include "util/util_map.h" #include "util/util_param.h" +#include "util/util_set.h" #include "util/util_transform.h" #include "util/util_types.h" #include "util/util_vector.h" @@ -314,6 +315,8 @@ class Mesh : public Node { void add_vertex_normals(); void add_undisplaced(); + void get_uv_tiles(ustring map, unordered_set<int> &tiles); + void pack_shaders(Scene *scene, uint *shader); void pack_normals(float4 *vnormal); void pack_verts(const vector<uint> &tri_prim_index, diff --git a/intern/cycles/render/nodes.cpp b/intern/cycles/render/nodes.cpp index 5e12d79bc6b..b8847f92153 100644 --- a/intern/cycles/render/nodes.cpp +++ b/intern/cycles/render/nodes.cpp @@ -19,6 +19,7 @@ #include "render/image.h" #include "render/integrator.h" #include "render/light.h" +#include "render/mesh.h" #include "render/nodes.h" #include "render/scene.h" #include "render/svm.h" @@ -204,6 +205,27 @@ void TextureMapping::compile(OSLCompiler &compiler) /* Image Texture */ +ImageSlotTextureNode::~ImageSlotTextureNode() +{ + if (image_manager) { + foreach (int slot, slots) { + if (slot != -1) { + image_manager->remove_image(slot); + } + } + } +} + +void ImageSlotTextureNode::add_image_user() const +{ + /* Increase image user count for new node. */ + foreach (int slot, slots) { + if (slot != -1) { + image_manager->add_image_user(slot); + } + } +} + NODE_DEFINE(ImageTextureNode) { NodeType *type = NodeType::add("image_texture", create, NodeType::SHADER); @@ -253,30 +275,71 @@ NODE_DEFINE(ImageTextureNode) ImageTextureNode::ImageTextureNode() : ImageSlotTextureNode(node_type) { - image_manager = NULL; - slot = -1; - is_float = -1; + is_float = false; compress_as_srgb = false; colorspace = u_colorspace_raw; builtin_data = NULL; animated = false; + tiles.push_back(1001); } -ImageTextureNode::~ImageTextureNode() +ShaderNode *ImageTextureNode::clone() const { - if (image_manager) { - image_manager->remove_image( - filename.string(), builtin_data, interpolation, extension, alpha_type, colorspace); - } + add_image_user(); + return new ImageTextureNode(*this); } -ShaderNode *ImageTextureNode::clone() const +void ImageTextureNode::cull_tiles(Scene *scene, ShaderGraph *graph) { - /* Increase image user count for new node. */ - if (slot != -1) { - image_manager->add_image_user(slot); + if (!scene->params.background) { + /* During interactive renders, all tiles are loaded. + * While we could support updating this when UVs change, that could lead + * to annoying interruptions when loading images while editing UVs. */ + return; } - return new ImageTextureNode(*this); + + /* Only check UVs for tile culling if there are multiple tiles. */ + if (tiles.size() < 2) { + return; + } + + ShaderInput *vector_in = input("Vector"); + ustring attribute; + if (vector_in->link) { + ShaderNode *node = vector_in->link->parent; + if (node->type == UVMapNode::node_type) { + UVMapNode *uvmap = (UVMapNode *)node; + attribute = uvmap->attribute; + } + else if (node->type == TextureCoordinateNode::node_type) { + if (vector_in->link != node->output("UV")) { + return; + } + } + else { + return; + } + } + + unordered_set<int> used_tiles; + /* TODO(lukas): This is quite inefficient. A fairly simple improvement would + * be to have a cache in each mesh that is indexed by attribute. + * Additionally, building a graph-to-meshes list once could help. */ + foreach (Mesh *mesh, scene->meshes) { + foreach (Shader *shader, mesh->used_shaders) { + if (shader->graph == graph) { + mesh->get_uv_tiles(attribute, used_tiles); + } + } + } + + ccl::vector<int> new_tiles; + foreach (int tile, tiles) { + if (used_tiles.count(tile)) { + new_tiles.push_back(tile); + } + } + tiles.swap(new_tiles); } void ImageTextureNode::attributes(Shader *shader, AttributeRequestSet *attributes) @@ -300,24 +363,61 @@ void ImageTextureNode::compile(SVMCompiler &compiler) ShaderOutput *color_out = output("Color"); ShaderOutput *alpha_out = output("Alpha"); - image_manager = compiler.image_manager; - if (is_float == -1) { - ImageMetaData metadata; - slot = image_manager->add_image(filename.string(), - builtin_data, - animated, - 0, - interpolation, - extension, - alpha_type, - colorspace, - metadata); - is_float = metadata.is_float; - compress_as_srgb = metadata.compress_as_srgb; - known_colorspace = metadata.colorspace; + image_manager = compiler.scene->image_manager; + if (slots.empty()) { + cull_tiles(compiler.scene, compiler.current_graph); } + if (slots.size() < tiles.size()) { + slots.clear(); + slots.reserve(tiles.size()); + + bool have_metadata = false; + foreach (int tile, tiles) { + string tile_name = filename.string(); + if (tiles.size() > 1) { + tile_name = string_printf(tile_name.c_str(), tile); + } + + ImageMetaData metadata; + int slot = image_manager->add_image(tile_name, + builtin_data, + animated, + 0, + interpolation, + extension, + alpha_type, + colorspace, + metadata); + slots.push_back(slot); + + /* We assume that all tiles have the same metadata. */ + if (!have_metadata) { + is_float = metadata.is_float; + compress_as_srgb = metadata.compress_as_srgb; + known_colorspace = metadata.colorspace; + have_metadata = true; + } + } + } + + bool has_image = false; + foreach (int slot, slots) { + if (slot != -1) { + has_image = true; + break; + } + } + + if (has_image) { + /* If there only is one image (a very common case), we encode it as a negative value. */ + int num_nodes; + if (slots.size() == 1) { + num_nodes = -slots[0]; + } + else { + num_nodes = divide_up(slots.size(), 2); + } - if (slot != -1) { int vector_offset = tex_mapping.compile_begin(compiler, vector_in); uint flags = 0; @@ -336,7 +436,7 @@ void ImageTextureNode::compile(SVMCompiler &compiler) if (projection != NODE_IMAGE_PROJ_BOX) { compiler.add_node(NODE_TEX_IMAGE, - slot, + num_nodes, compiler.encode_uchar4(vector_offset, compiler.stack_assign_if_linked(color_out), compiler.stack_assign_if_linked(alpha_out), @@ -345,7 +445,7 @@ void ImageTextureNode::compile(SVMCompiler &compiler) } else { compiler.add_node(NODE_TEX_IMAGE_BOX, - slot, + num_nodes, compiler.encode_uchar4(vector_offset, compiler.stack_assign_if_linked(color_out), compiler.stack_assign_if_linked(alpha_out), @@ -353,6 +453,23 @@ void ImageTextureNode::compile(SVMCompiler &compiler) __float_as_int(projection_blend)); } + if (num_nodes > 0) { + for (int i = 0; i < num_nodes; i++) { + int4 node; + node.x = tiles[2 * i]; + node.y = slots[2 * i]; + if (2 * i + 1 < slots.size()) { + node.z = tiles[2 * i + 1]; + node.w = slots[2 * i + 1]; + } + else { + node.z = -1; + node.w = -1; + } + compiler.add_node(node.x, node.y, node.z, node.w); + } + } + tex_mapping.compile_end(compiler, vector_in, vector_offset); } else { @@ -375,34 +492,37 @@ void ImageTextureNode::compile(OSLCompiler &compiler) tex_mapping.compile(compiler); - image_manager = compiler.image_manager; - if (is_float == -1) { + image_manager = compiler.scene->image_manager; + if (slots.size() == 0) { ImageMetaData metadata; if (builtin_data == NULL) { image_manager->get_image_metadata(filename.string(), NULL, colorspace, metadata); + slots.push_back(-1); } else { - slot = image_manager->add_image(filename.string(), - builtin_data, - animated, - 0, - interpolation, - extension, - alpha_type, - colorspace, - metadata); + /* TODO(lukas): OSL UDIMs */ + int slot = image_manager->add_image(filename.string(), + builtin_data, + animated, + 0, + interpolation, + extension, + alpha_type, + colorspace, + metadata); + slots.push_back(slot); } is_float = metadata.is_float; compress_as_srgb = metadata.compress_as_srgb; known_colorspace = metadata.colorspace; } - if (slot == -1) { + if (slots[0] == -1) { compiler.parameter_texture( "filename", filename, compress_as_srgb ? u_colorspace_raw : known_colorspace); } else { - compiler.parameter_texture("filename", slot); + compiler.parameter_texture("filename", slots[0]); } const bool unassociate_alpha = !(ColorSpaceManager::colorspace_is_data(colorspace) || @@ -462,29 +582,16 @@ NODE_DEFINE(EnvironmentTextureNode) EnvironmentTextureNode::EnvironmentTextureNode() : ImageSlotTextureNode(node_type) { - image_manager = NULL; - slot = -1; - is_float = -1; + is_float = false; compress_as_srgb = false; colorspace = u_colorspace_raw; builtin_data = NULL; animated = false; } -EnvironmentTextureNode::~EnvironmentTextureNode() -{ - if (image_manager) { - image_manager->remove_image( - filename.string(), builtin_data, interpolation, EXTENSION_REPEAT, alpha_type, colorspace); - } -} - ShaderNode *EnvironmentTextureNode::clone() const { - /* Increase image user count for new node. */ - if (slot != -1) { - image_manager->add_image_user(slot); - } + add_image_user(); return new EnvironmentTextureNode(*this); } @@ -507,24 +614,25 @@ void EnvironmentTextureNode::compile(SVMCompiler &compiler) ShaderOutput *color_out = output("Color"); ShaderOutput *alpha_out = output("Alpha"); - image_manager = compiler.image_manager; - if (slot == -1) { + image_manager = compiler.scene->image_manager; + if (slots.empty()) { ImageMetaData metadata; - slot = image_manager->add_image(filename.string(), - builtin_data, - animated, - 0, - interpolation, - EXTENSION_REPEAT, - alpha_type, - colorspace, - metadata); + int slot = image_manager->add_image(filename.string(), + builtin_data, + animated, + 0, + interpolation, + EXTENSION_REPEAT, + alpha_type, + colorspace, + metadata); + slots.push_back(slot); is_float = metadata.is_float; compress_as_srgb = metadata.compress_as_srgb; known_colorspace = metadata.colorspace; } - if (slot != -1) { + if (slots[0] != -1) { int vector_offset = tex_mapping.compile_begin(compiler, vector_in); uint flags = 0; @@ -533,7 +641,7 @@ void EnvironmentTextureNode::compile(SVMCompiler &compiler) } compiler.add_node(NODE_TEX_ENVIRONMENT, - slot, + slots[0], compiler.encode_uchar4(vector_offset, compiler.stack_assign_if_linked(color_out), compiler.stack_assign_if_linked(alpha_out), @@ -563,34 +671,36 @@ void EnvironmentTextureNode::compile(OSLCompiler &compiler) /* See comments in ImageTextureNode::compile about support * of builtin images. */ - image_manager = compiler.image_manager; - if (is_float == -1) { + image_manager = compiler.scene->image_manager; + if (slots.empty()) { ImageMetaData metadata; if (builtin_data == NULL) { image_manager->get_image_metadata(filename.string(), NULL, colorspace, metadata); + slots.push_back(-1); } else { - slot = image_manager->add_image(filename.string(), - builtin_data, - animated, - 0, - interpolation, - EXTENSION_REPEAT, - alpha_type, - colorspace, - metadata); + int slot = image_manager->add_image(filename.string(), + builtin_data, + animated, + 0, + interpolation, + EXTENSION_REPEAT, + alpha_type, + colorspace, + metadata); + slots.push_back(slot); } is_float = metadata.is_float; compress_as_srgb = metadata.compress_as_srgb; known_colorspace = metadata.colorspace; } - if (slot == -1) { + if (slots[0] == -1) { compiler.parameter_texture( "filename", filename, compress_as_srgb ? u_colorspace_raw : known_colorspace); } else { - compiler.parameter_texture("filename", slot); + compiler.parameter_texture("filename", slots[0]); } compiler.parameter(this, "projection"); @@ -1123,7 +1233,7 @@ void IESLightNode::get_slot() void IESLightNode::compile(SVMCompiler &compiler) { - light_manager = compiler.light_manager; + light_manager = compiler.scene->light_manager; get_slot(); ShaderInput *strength_in = input("Strength"); @@ -1145,7 +1255,7 @@ void IESLightNode::compile(SVMCompiler &compiler) void IESLightNode::compile(OSLCompiler &compiler) { - light_manager = compiler.light_manager; + light_manager = compiler.scene->light_manager; get_slot(); tex_mapping.compile(compiler); @@ -1663,7 +1773,7 @@ void PointDensityTextureNode::compile(SVMCompiler &compiler) const bool use_density = !density_out->links.empty(); const bool use_color = !color_out->links.empty(); - image_manager = compiler.image_manager; + image_manager = compiler.scene->image_manager; if (use_density || use_color) { add_image(); @@ -1704,7 +1814,7 @@ void PointDensityTextureNode::compile(OSLCompiler &compiler) const bool use_density = !density_out->links.empty(); const bool use_color = !color_out->links.empty(); - image_manager = compiler.image_manager; + image_manager = compiler.scene->image_manager; if (use_density || use_color) { add_image(); diff --git a/intern/cycles/render/nodes.h b/intern/cycles/render/nodes.h index 54124cd2175..a8fe7644957 100644 --- a/intern/cycles/render/nodes.h +++ b/intern/cycles/render/nodes.h @@ -77,14 +77,17 @@ class ImageSlotTextureNode : public TextureNode { explicit ImageSlotTextureNode(const NodeType *node_type) : TextureNode(node_type) { special_type = SHADER_SPECIAL_TYPE_IMAGE_SLOT; + image_manager = NULL; } - int slot; + ~ImageSlotTextureNode(); + void add_image_user() const; + ImageManager *image_manager; + vector<int> slots; }; class ImageTextureNode : public ImageSlotTextureNode { public: SHADER_NODE_NO_CLONE_CLASS(ImageTextureNode) - ~ImageTextureNode(); ShaderNode *clone() const; void attributes(Shader *shader, AttributeRequestSet *attributes); bool has_attribute_dependency() @@ -110,18 +113,20 @@ class ImageTextureNode : public ImageSlotTextureNode { float projection_blend; bool animated; float3 vector; + ccl::vector<int> tiles; /* Runtime. */ - ImageManager *image_manager; - int is_float; + bool is_float; bool compress_as_srgb; ustring known_colorspace; + + protected: + void cull_tiles(Scene *scene, ShaderGraph *graph); }; class EnvironmentTextureNode : public ImageSlotTextureNode { public: SHADER_NODE_NO_CLONE_CLASS(EnvironmentTextureNode) - ~EnvironmentTextureNode(); ShaderNode *clone() const; void attributes(Shader *shader, AttributeRequestSet *attributes); bool has_attribute_dependency() @@ -151,8 +156,7 @@ class EnvironmentTextureNode : public ImageSlotTextureNode { float3 vector; /* Runtime. */ - ImageManager *image_manager; - int is_float; + bool is_float; bool compress_as_srgb; ustring known_colorspace; }; diff --git a/intern/cycles/render/osl.cpp b/intern/cycles/render/osl.cpp index 889552f49cd..91f02e42071 100644 --- a/intern/cycles/render/osl.cpp +++ b/intern/cycles/render/osl.cpp @@ -118,9 +118,9 @@ void OSLShaderManager::device_update(Device *device, * compile shaders alternating */ thread_scoped_lock lock(ss_mutex); - OSLCompiler compiler(this, services, ss, scene->image_manager, scene->light_manager); + OSLCompiler compiler(this, services, ss, scene); compiler.background = (shader == scene->default_background); - compiler.compile(scene, og, shader); + compiler.compile(og, shader); if (shader->use_mis && shader->has_surface_emission) scene->light_manager->need_update = true; @@ -566,13 +566,8 @@ OSLNode *OSLShaderManager::osl_node(const std::string &filepath, OSLCompiler::OSLCompiler(OSLShaderManager *manager, OSLRenderServices *services, OSL::ShadingSystem *ss, - ImageManager *image_manager, - LightManager *light_manager) - : image_manager(image_manager), - light_manager(light_manager), - manager(manager), - services(services), - ss(ss) + Scene *scene) + : scene(scene), manager(manager), services(services), ss(ss) { current_type = SHADER_TYPE_SURFACE; current_shader = NULL; @@ -1114,7 +1109,7 @@ OSL::ShaderGroupRef OSLCompiler::compile_type(Shader *shader, ShaderGraph *graph return group; } -void OSLCompiler::compile(Scene *scene, OSLGlobals *og, Shader *shader) +void OSLCompiler::compile(OSLGlobals *og, Shader *shader) { if (shader->need_update) { ShaderGraph *graph = shader->graph; diff --git a/intern/cycles/render/osl.h b/intern/cycles/render/osl.h index 17bf98a3433..62cbfebf7eb 100644 --- a/intern/cycles/render/osl.h +++ b/intern/cycles/render/osl.h @@ -131,10 +131,9 @@ class OSLCompiler { OSLCompiler(OSLShaderManager *manager, OSLRenderServices *services, OSL::ShadingSystem *shadingsys, - ImageManager *image_manager, - LightManager *light_manager); + Scene *scene); #endif - void compile(Scene *scene, OSLGlobals *og, Shader *shader); + void compile(OSLGlobals *og, Shader *shader); void add(ShaderNode *node, const char *name, bool isfilepath = false); @@ -165,8 +164,7 @@ class OSLCompiler { } bool background; - ImageManager *image_manager; - LightManager *light_manager; + Scene *scene; private: #ifdef WITH_OSL diff --git a/intern/cycles/render/scene.h b/intern/cycles/render/scene.h index 45997bccf5d..f99510d2d42 100644 --- a/intern/cycles/render/scene.h +++ b/intern/cycles/render/scene.h @@ -170,6 +170,8 @@ class SceneParams { bool persistent_data; int texture_limit; + bool background; + SceneParams() { shadingsystem = SHADINGSYSTEM_SVM; @@ -180,6 +182,7 @@ class SceneParams { num_bvh_time_steps = 0; persistent_data = false; texture_limit = 0; + background = true; } bool modified(const SceneParams ¶ms) diff --git a/intern/cycles/render/svm.cpp b/intern/cycles/render/svm.cpp index 8466742ae38..f42a2ea818d 100644 --- a/intern/cycles/render/svm.cpp +++ b/intern/cycles/render/svm.cpp @@ -57,9 +57,9 @@ void SVMShaderManager::device_update_shader(Scene *scene, svm_nodes->push_back_slow(make_int4(NODE_SHADER_JUMP, 0, 0, 0)); SVMCompiler::Summary summary; - SVMCompiler compiler(scene->shader_manager, scene->image_manager, scene->light_manager); + SVMCompiler compiler(scene); compiler.background = (shader == scene->default_background); - compiler.compile(scene, shader, *svm_nodes, 0, &summary); + compiler.compile(shader, *svm_nodes, 0, &summary); VLOG(2) << "Compilation summary:\n" << "Shader name: " << shader->name << "\n" @@ -169,13 +169,8 @@ void SVMShaderManager::device_free(Device *device, DeviceScene *dscene, Scene *s /* Graph Compiler */ -SVMCompiler::SVMCompiler(ShaderManager *shader_manager_, - ImageManager *image_manager_, - LightManager *light_manager_) +SVMCompiler::SVMCompiler(Scene *scene) : scene(scene) { - shader_manager = shader_manager_; - image_manager = image_manager_; - light_manager = light_manager_; max_stack_use = 0; current_type = SHADER_TYPE_SURFACE; current_shader = NULL; @@ -408,12 +403,12 @@ void SVMCompiler::add_node(const float4 &f) uint SVMCompiler::attribute(ustring name) { - return shader_manager->get_attribute_id(name); + return scene->shader_manager->get_attribute_id(name); } uint SVMCompiler::attribute(AttributeStandard std) { - return shader_manager->get_attribute_id(std); + return scene->shader_manager->get_attribute_id(std); } uint SVMCompiler::attribute_standard(ustring name) @@ -838,8 +833,7 @@ void SVMCompiler::compile_type(Shader *shader, ShaderGraph *graph, ShaderType ty } } -void SVMCompiler::compile( - Scene *scene, Shader *shader, array<int4> &svm_nodes, int index, Summary *summary) +void SVMCompiler::compile(Shader *shader, array<int4> &svm_nodes, int index, Summary *summary) { /* copy graph for shader with bump mapping */ ShaderNode *output = shader->graph->output(); diff --git a/intern/cycles/render/svm.h b/intern/cycles/render/svm.h index d1534567bea..61923fc40ac 100644 --- a/intern/cycles/render/svm.h +++ b/intern/cycles/render/svm.h @@ -93,11 +93,8 @@ class SVMCompiler { string full_report() const; }; - SVMCompiler(ShaderManager *shader_manager, - ImageManager *image_manager, - LightManager *light_manager); - void compile( - Scene *scene, Shader *shader, array<int4> &svm_nodes, int index, Summary *summary = NULL); + SVMCompiler(Scene *scene); + void compile(Shader *shader, array<int4> &svm_nodes, int index, Summary *summary = NULL); int stack_assign(ShaderOutput *output); int stack_assign(ShaderInput *input); @@ -126,9 +123,8 @@ class SVMCompiler { return current_type; } - ImageManager *image_manager; - ShaderManager *shader_manager; - LightManager *light_manager; + Scene *scene; + ShaderGraph *current_graph; bool background; protected: @@ -221,7 +217,6 @@ class SVMCompiler { array<int4> current_svm_nodes; ShaderType current_type; Shader *current_shader; - ShaderGraph *current_graph; Stack active_stack; int max_stack_use; uint mix_weight_offset; diff --git a/intern/cycles/test/render_graph_finalize_test.cpp b/intern/cycles/test/render_graph_finalize_test.cpp index ca93f8b02d0..ace4f29913b 100644 --- a/intern/cycles/test/render_graph_finalize_test.cpp +++ b/intern/cycles/test/render_graph_finalize_test.cpp @@ -158,10 +158,11 @@ class RenderGraph : public testing::Test { Device *device_cpu; SceneParams scene_params; Scene *scene; + Shader shader; ShaderGraph graph; ShaderGraphBuilder builder; - RenderGraph() : testing::Test(), builder(&graph) + RenderGraph() : testing::Test(), graph(&shader), builder(&graph) { } |