Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[BUG] Unable to sample UDIMs from pre-populated in-memory ImageCache #4378

Open
jonahfriedman opened this issue Aug 14, 2024 · 1 comment
Open

Comments

@jonahfriedman
Copy link

Description

Note - this is the result of a discussion on the Academy Software Foundation slack.

I wanted to sample a number of in-memory buffers representing tiles using the texturesystem. I thought (based on advice) that I could populate an ImageCache with "fake" images named in the fashion of UDIM tiles such as "test.1001.null", "test.1002.null", and sample them using the name containing the UDIM pattern (such as test.<UDIM>.null). This does work for single images but not for UDIM tiles.

OpenImageIO version and dependencies

OpenImageIO 2.5.10.0

To Reproduce

Steps to reproduce the behavior:

  1. Use the test C++ code snippet below in a setup that has OIIO available (sorry I don't have exact steps here).
  2. Call OIIOTest::testBufferUDIMSampling();
  3. The output will be:
Checking test.1001.null at st: {0.5,0.5}
    No inventory test.1001.null u0 v0
Checking test.1002.null at st: {0.5,0.5}
    No inventory test.1002.null u0 v0
Checking test.<UDIM>.null at st: {0.5,0.5}
    No inventory test.<UDIM>.null u0 v0
    test.<UDIM>.null did not sample correctly.
    Incorrect result: {0,0,0,0}
  1. This indicates that after populating the cache we were able to sample the image test.1001.null and test.1002.null, and get the expected result, but were unable to sample test.<UDIM>.null. The inventory of UDIM tiles always gives 0 tiles, in all three cases, which may be a clue.

The code using the nullImageInputCreator follows the example found here:

static ImageInput*
NullInputCreator()
{
// Note: we can't create it directly, but we can ask for a managed
// pointer and then release the raw pointer from it.
return ImageInput::create("0.null").release();
}
// Test the ability to add an application buffer to make it appear as if
// it's an image in the cache.
void
test_app_buffer()
{
ImageCache* imagecache = ImageCache::create(false /*not shared*/);
// Add a file entry with a "null" ImageInput proxy configured to look
// like a 2x2 RGB float image.
ustring fooname("foo");
const int xres = 2, yres = 2, chans = 3;
TypeDesc imgtype = TypeDesc::FLOAT;
ImageSpec config(xres, yres, chans, imgtype);
config.tile_width = xres;
config.tile_height = yres;
config.attribute("null:force", 1); // necessary because no .null extension
bool fadded = imagecache->add_file(fooname, NullInputCreator, &config);

Test code snippet:

#include <OpenImageIO/imagecache.h>
#include <OpenImageIO/imageio.h>
#include <OpenImageIO/texture.h>

#include <iostream>
#include <vector>

namespace OIIOTest {

using namespace OIIO;

// Simple wrapper to return a raw "null" ImageInput*.
// Taken from OIIO tests
ImageInput* nullImageInputCreator() {
    // Original comment:
    //      Note: we can't create it directly, but we can ask for a managed
    //      pointer and then release the raw pointer from it.
    return ImageInput::create("0.null").release();
}

bool checkSample(ImageCache* imageCache, ustring filename, float s, float t, float* expected) {
    std::cerr << "Checking " << filename << " at st: {" << s << "," << t << "}\n";
    float      r[4] = {-1.f, -1.f, -1.f, -1.f};
    TextureOpt opt;
    opt.interpmode = TextureOpt::InterpMode::InterpClosest;
    bool ok;
    {
        auto textureSys = TextureSystem::create(false /*Not shared*/, imageCache);

        {
            std::vector<ustring> filenames;
            int                  ut, vt;
            textureSys->inventory_udim(filename, filenames, ut, vt);
            if (filenames.empty())
                std::cerr << "    No inventory " << filename << " u" << ut << " v" << vt << "\n";
        }

        ok = textureSys->texture(filename, opt,      //
                                 s, t,               // float s, t
                                 0.f, 0.f, 0.f, 0.f, // float dsdx, dtdx, dsdy, dtdy,
                                 4,                  // int nchannels,
                                 r);                 // float *result
        TextureSystem::destroy(textureSys);
    }

    if (!imageCache->geterror(false).empty() || !ok) {
        std::cerr << "     " << filename << " gave OIIO error. \n";
        std::cerr << "     " << imageCache->geterror(true);
        return false;
    }
    if (r[0] != expected[0] || r[1] != expected[1] || r[2] != expected[2] || r[3] != expected[3]) {
        std::cerr << "    " << filename << " did not sample correctly. \n";
        std::cerr << "    Incorrect result: {" << r[0] << "," << r[1] << "," << r[2] << "," << r[3]
                  << "}\n";
        return false;
    }
    return true;
}

bool testBufferUDIMSampling() {
    /*
    We create two UDIM tiles, 1001 and 1002.
    */
    std::vector<float> pixels1001 = {
        1.f, 0.f, 0.f, 1.f, // red
        1.f, 0.f, 0.f, 1.f, // red
        1.f, 0.f, 0.f, 1.f, // red
        1.f, 0.f, 0.f, 1.f, // red
    };
    std::vector<float> pixels1002 = {
        0.f, 1.f, 0.f, 1.f, // green
        0.f, 1.f, 0.f, 1.f, // green
        0.f, 1.f, 0.f, 1.f, // green
        0.f, 1.f, 0.f, 1.f, // green
    };

    const ustring nameUDIM("test.<UDIM>.null");
    const ustring name1001("test.1001.null");
    const ustring name1002("test.1002.null");

    auto imageCache = ImageCache::create(false); // unshared

    ImageSpec spec = ImageSpec(2, 2, 4, TypeDesc::FLOAT);
    spec.attribute("null:force", 1); // necessary because no .null extension

    imageCache->add_file(name1001, nullImageInputCreator, &spec);
    imageCache->add_tile(name1001,
                         0,                                  // subimage
                         0,                                  // miplevel
                         0, 0, 0,                            // origin x, y, z
                         0, 4,                               // channel start, end
                         TypeDesc::FLOAT,                    // image type
                         pixels1001.data(),                  // the buffer
                         AutoStride, AutoStride, AutoStride, // strides
                         true                                // copy
    );
    imageCache->add_file(name1002, nullImageInputCreator, &spec);
    imageCache->add_tile(name1002,
                         0,                                  // subimage
                         0,                                  // miplevel
                         0, 0, 0,                            // origin x, y, z
                         0, 4,                               // channel start, end
                         TypeDesc::FLOAT,                    // image type
                         pixels1002.data(),                  // the buffer
                         AutoStride, AutoStride, AutoStride, // strides
                         true                                // copy
    );

    if (!imageCache->geterror(false).empty()) {
        std::cerr << imageCache->geterror(true);
        return false;
    }

    float s = 0.5f, t = 0.5f;
    float expected1001[4] = {1.f, 0.f, 0.f, 1.f};
    float expected1002[4] = {0.f, 1.f, 0.f, 1.f};

    // Sampling "test.1001.null" works
    if (!checkSample(imageCache, name1001, s, t, expected1001)) return false;

    // Sampling "test.1002.null" works as well (no udim tile behavior, just shows this loaded.)
    if (!checkSample(imageCache, name1002, s, t, expected1002)) return false;

    // Sampling it as "test.<UDIM>.null" fails
    if (!checkSample(imageCache, nameUDIM, s, t, expected1001)) return false;

    // (never reached)
    if (!checkSample(imageCache, nameUDIM, s + 1.f, t, expected1002)) return false;

    std::cerr << "Success \n";
    return true;
}
} // namespace OIIOTest
@jessey-git
Copy link
Contributor

Debugged through this today and, yeah, the current UDIM code here only traverses the file-system, not the items loaded into the ImageCache, when building its inventory. This lack of inventory for test.<UDIM>.null means sampling won't work as you expect it to.

Let me see if I can get others to chime in here about possible ways forward.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

2 participants