From 63e2adaa795f9bcebf83b4ef64feac2e3dfa025d Mon Sep 17 00:00:00 2001 From: Brecht Van Lommel Date: Thu, 23 Jan 2025 21:20:01 +0100 Subject: [PATCH] Fix #127875: Cycles renders image with single UDIM tile wrong Properly support this case instead of assuming it's not a UDIM. Pull Request: https://projects.blender.org/blender/blender/pulls/133504 --- intern/cycles/blender/shader.cpp | 10 ++-- intern/cycles/scene/geometry.cpp | 2 +- intern/cycles/scene/image.cpp | 84 ++++++++++++++++------------ intern/cycles/scene/image.h | 10 ++-- intern/cycles/scene/shader_nodes.cpp | 17 +++--- 5 files changed, 71 insertions(+), 52 deletions(-) diff --git a/intern/cycles/blender/shader.cpp b/intern/cycles/blender/shader.cpp index 87809d0d951..04c363a1a76 100644 --- a/intern/cycles/blender/shader.cpp +++ b/intern/cycles/blender/shader.cpp @@ -842,11 +842,13 @@ static ShaderNode *add_node(Scene *scene, image->set_animated(is_image_animated(b_image_source, b_image_user)); image->set_alpha_type(get_image_alpha_type(b_image)); - array tiles; - for (BL::UDIMTile &b_tile : b_image.tiles) { - tiles.push_back_slow(b_tile.number()); + if (b_image_source == BL::Image::source_TILED) { + array tiles; + for (BL::UDIMTile &b_tile : b_image.tiles) { + tiles.push_back_slow(b_tile.number()); + } + image->set_tiles(tiles); } - image->set_tiles(tiles); /* builtin images will use callback-based reading because * they could only be loaded correct from blender side diff --git a/intern/cycles/scene/geometry.cpp b/intern/cycles/scene/geometry.cpp index f3917d529c9..5b5720f656c 100644 --- a/intern/cycles/scene/geometry.cpp +++ b/intern/cycles/scene/geometry.cpp @@ -636,7 +636,7 @@ void GeometryManager::device_update_displacement_images(Device *device, } ImageSlotTextureNode *image_node = static_cast(node); - for (int i = 0; i < image_node->handle.num_tiles(); i++) { + for (int i = 0; i < image_node->handle.num_svm_slots(); i++) { const int slot = image_node->handle.svm_slot(i); if (slot != -1) { bump_images.insert(slot); diff --git a/intern/cycles/scene/image.cpp b/intern/cycles/scene/image.cpp index e6d721e8535..14a88d446fa 100644 --- a/intern/cycles/scene/image.cpp +++ b/intern/cycles/scene/image.cpp @@ -67,10 +67,10 @@ const char *name_from_type(ImageDataType type) ImageHandle::ImageHandle() : manager(nullptr) {} ImageHandle::ImageHandle(const ImageHandle &other) - : tile_slots(other.tile_slots), manager(other.manager) + : slots(other.slots), is_tiled(other.is_tiled), manager(other.manager) { /* Increase image user count. */ - for (const size_t slot : tile_slots) { + for (const size_t slot : slots) { manager->add_image_user(slot); } } @@ -79,9 +79,10 @@ ImageHandle &ImageHandle::operator=(const ImageHandle &other) { clear(); manager = other.manager; - tile_slots = other.tile_slots; + is_tiled = other.is_tiled; + slots = other.slots; - for (const size_t slot : tile_slots) { + for (const size_t slot : slots) { manager->add_image_user(slot); } @@ -95,66 +96,71 @@ ImageHandle::~ImageHandle() void ImageHandle::clear() { - for (const size_t slot : tile_slots) { + for (const size_t slot : slots) { manager->remove_image_user(slot); } - tile_slots.clear(); + slots.clear(); manager = nullptr; } bool ImageHandle::empty() const { - return tile_slots.empty(); + return slots.empty(); } int ImageHandle::num_tiles() const { - return tile_slots.size(); + return (is_tiled) ? slots.size() : 0; +} + +int ImageHandle::num_svm_slots() const +{ + return slots.size(); } ImageMetaData ImageHandle::metadata() { - if (tile_slots.empty()) { + if (slots.empty()) { return ImageMetaData(); } - ImageManager::Image *img = manager->get_image_slot(tile_slots.front()); + ImageManager::Image *img = manager->get_image_slot(slots.front()); manager->load_image_metadata(img); return img->metadata; } -int ImageHandle::svm_slot(const int tile_index) const +int ImageHandle::svm_slot(const int slot_index) const { - if (tile_index >= tile_slots.size()) { + if (slot_index >= slots.size()) { return -1; } if (manager->osl_texture_system) { - ImageManager::Image *img = manager->get_image_slot(tile_slots[tile_index]); + ImageManager::Image *img = manager->get_image_slot(slots[slot_index]); if (!img->loader->osl_filepath().empty()) { return -1; } } - return tile_slots[tile_index]; + return slots[slot_index]; } vector ImageHandle::get_svm_slots() const { - const size_t num_nodes = divide_up(tile_slots.size(), 2); + const size_t num_nodes = divide_up(slots.size(), 2); vector svm_slots; svm_slots.reserve(num_nodes); for (size_t i = 0; i < num_nodes; i++) { int4 node; - size_t slot = tile_slots[2 * i]; + size_t slot = slots[2 * i]; node.x = manager->get_image_slot(slot)->loader->get_tile_number(); node.y = slot; - if ((2 * i + 1) < tile_slots.size()) { - slot = tile_slots[2 * i + 1]; + if ((2 * i + 1) < slots.size()) { + slot = slots[2 * i + 1]; node.z = manager->get_image_slot(slot)->loader->get_tile_number(); node.w = slot; } @@ -169,23 +175,23 @@ vector ImageHandle::get_svm_slots() const return svm_slots; } -device_texture *ImageHandle::image_memory(const int tile_index) const +device_texture *ImageHandle::image_memory() const { - if (tile_index >= tile_slots.size()) { + if (slots.empty()) { return nullptr; } - ImageManager::Image *img = manager->get_image_slot(tile_slots[tile_index]); + ImageManager::Image *img = manager->get_image_slot(slots[0]); return img ? img->mem.get() : nullptr; } -VDBImageLoader *ImageHandle::vdb_loader(const int tile_index) const +VDBImageLoader *ImageHandle::vdb_loader() const { - if (tile_index >= tile_slots.size()) { + if (slots.empty()) { return nullptr; } - ImageManager::Image *img = manager->get_image_slot(tile_slots[tile_index]); + ImageManager::Image *img = manager->get_image_slot(slots[0]); if (img == nullptr) { return nullptr; @@ -211,7 +217,7 @@ ImageManager *ImageHandle::get_manager() const bool ImageHandle::operator==(const ImageHandle &other) const { - return manager == other.manager && tile_slots == other.tile_slots; + return manager == other.manager && is_tiled == other.is_tiled && slots == other.slots; } /* Image MetaData */ @@ -374,7 +380,7 @@ ImageHandle ImageManager::add_image(const string &filename, const ImageParams &p const size_t slot = add_image_slot(make_unique(filename), params, false); ImageHandle handle; - handle.tile_slots.push_back(slot); + handle.slots.push_back(slot); handle.manager = this; return handle; } @@ -385,21 +391,27 @@ ImageHandle ImageManager::add_image(const string &filename, { ImageHandle handle; handle.manager = this; + handle.is_tiled = !tiles.empty(); + + if (!handle.is_tiled) { + const size_t slot = add_image_slot(make_unique(filename), params, false); + handle.slots.push_back(slot); + return handle; + } for (const int tile : tiles) { string tile_filename = filename; /* Since we don't have information about the exact tile format used in this code location, * just attempt all replacement patterns that Blender supports. */ - if (tile != 0) { - string_replace(tile_filename, "", string_printf("%04d", tile)); + string_replace(tile_filename, "", string_printf("%04d", tile)); + + const int u = ((tile - 1001) % 10); + const int v = ((tile - 1001) / 10); + string_replace(tile_filename, "", string_printf("u%d_v%d", u + 1, v + 1)); - const int u = ((tile - 1001) % 10); - const int v = ((tile - 1001) / 10); - string_replace(tile_filename, "", string_printf("u%d_v%d", u + 1, v + 1)); - } const size_t slot = add_image_slot(make_unique(tile_filename), params, false); - handle.tile_slots.push_back(slot); + handle.slots.push_back(slot); } return handle; @@ -412,7 +424,7 @@ ImageHandle ImageManager::add_image(unique_ptr &&loader, const size_t slot = add_image_slot(std::move(loader), params, builtin); ImageHandle handle; - handle.tile_slots.push_back(slot); + handle.slots.push_back(slot); handle.manager = this; return handle; } @@ -421,11 +433,13 @@ ImageHandle ImageManager::add_image(vector> &&loaders, const ImageParams ¶ms) { ImageHandle handle; + handle.is_tiled = true; + for (unique_ptr &loader : loaders) { unique_ptr local_loader; std::swap(loader, local_loader); const size_t slot = add_image_slot(std::move(local_loader), params, true); - handle.tile_slots.push_back(slot); + handle.slots.push_back(slot); } handle.manager = this; diff --git a/intern/cycles/scene/image.h b/intern/cycles/scene/image.h index cdde1bc8b25..738f58b6041 100644 --- a/intern/cycles/scene/image.h +++ b/intern/cycles/scene/image.h @@ -137,18 +137,20 @@ class ImageHandle { bool empty() const; int num_tiles() const; + int num_svm_slots() const; ImageMetaData metadata(); - int svm_slot(const int tile_index = 0) const; + int svm_slot(const int slot_index = 0) const; vector get_svm_slots() const; - device_texture *image_memory(const int tile_index = 0) const; + device_texture *image_memory() const; - VDBImageLoader *vdb_loader(const int tile_index = 0) const; + VDBImageLoader *vdb_loader() const; ImageManager *get_manager() const; protected: - vector tile_slots; + vector slots; + bool is_tiled = false; ImageManager *manager; friend class ImageManager; diff --git a/intern/cycles/scene/shader_nodes.cpp b/intern/cycles/scene/shader_nodes.cpp index 05564af91a4..9c495e07e53 100644 --- a/intern/cycles/scene/shader_nodes.cpp +++ b/intern/cycles/scene/shader_nodes.cpp @@ -263,7 +263,6 @@ ImageTextureNode::ImageTextureNode() : ImageSlotTextureNode(get_node_type()) { colorspace = u_colorspace_raw; animated = false; - tiles.push_back_slow(1001); } ShaderNode *ImageTextureNode::clone(ShaderGraph *graph) const @@ -289,8 +288,10 @@ void ImageTextureNode::cull_tiles(Scene *scene, ShaderGraph *graph) /* Box projection computes its own UVs that always lie in the * 1001 tile, so there's no point in loading any others. */ if (projection == NODE_IMAGE_PROJ_BOX) { - tiles.clear(); - tiles.push_back_slow(1001); + if (tiles.size()) { + tiles.clear(); + tiles.push_back_slow(1001); + } return; } @@ -301,8 +302,8 @@ void ImageTextureNode::cull_tiles(Scene *scene, ShaderGraph *graph) return; } - /* Only check UVs for tile culling if there are multiple tiles. */ - if (tiles.size() < 2) { + /* Only check UVs for tile culling when using tiles. */ + if (tiles.size() == 0) { return; } @@ -396,7 +397,7 @@ void ImageTextureNode::compile(SVMCompiler &compiler) if (projection != NODE_IMAGE_PROJ_BOX) { /* If there only is one image (a very common case), we encode it as a negative value. */ int num_nodes; - if (handle.num_tiles() == 1) { + if (handle.num_tiles() == 0) { num_nodes = -handle.svm_slot(); } else { @@ -429,7 +430,7 @@ void ImageTextureNode::compile(SVMCompiler &compiler) } } else { - assert(handle.num_tiles() == 1); + assert(handle.num_svm_slots() == 1); compiler.add_node(NODE_TEX_IMAGE_BOX, handle.svm_slot(), compiler.encode_uchar4(vector_offset, @@ -471,7 +472,7 @@ void ImageTextureNode::compile(OSLCompiler &compiler) alpha_type == IMAGE_ALPHA_IGNORE); const bool is_tiled = (filename.find("") != string::npos || filename.find("") != string::npos) || - handle.num_tiles() > 1; + handle.num_tiles() > 0; compiler.parameter(this, "projection"); compiler.parameter(this, "projection_blend");