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
This commit is contained in:
Brecht Van Lommel
2025-01-23 21:20:01 +01:00
committed by Brecht Van Lommel
parent 9b445da803
commit 63e2adaa79
5 changed files with 71 additions and 52 deletions

View File

@@ -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<int> 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<int> 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

View File

@@ -636,7 +636,7 @@ void GeometryManager::device_update_displacement_images(Device *device,
}
ImageSlotTextureNode *image_node = static_cast<ImageSlotTextureNode *>(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);

View File

@@ -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<int4> 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<int4> 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<int4> 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<OIIOImageLoader>(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<OIIOImageLoader>(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, "<UDIM>", string_printf("%04d", tile));
string_replace(tile_filename, "<UDIM>", string_printf("%04d", tile));
const int u = ((tile - 1001) % 10);
const int v = ((tile - 1001) / 10);
string_replace(tile_filename, "<UVTILE>", 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, "<UVTILE>", string_printf("u%d_v%d", u + 1, v + 1));
}
const size_t slot = add_image_slot(make_unique<OIIOImageLoader>(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<ImageLoader> &&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<unique_ptr<ImageLoader>> &&loaders,
const ImageParams &params)
{
ImageHandle handle;
handle.is_tiled = true;
for (unique_ptr<ImageLoader> &loader : loaders) {
unique_ptr<ImageLoader> 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;

View File

@@ -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<int4> 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<size_t> tile_slots;
vector<size_t> slots;
bool is_tiled = false;
ImageManager *manager;
friend class ImageManager;

View File

@@ -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("<UDIM>") != string::npos ||
filename.find("<UVTILE>") != string::npos) ||
handle.num_tiles() > 1;
handle.num_tiles() > 0;
compiler.parameter(this, "projection");
compiler.parameter(this, "projection_blend");