AcademySoftwareFoundation / AcademySoftwareFoundation/OpenImageIO
[BUG] Unable to sample UDIMs from pre-populated in-memory ImageCache
- Dominant language
- C++
- Stars
- 2.4k
- Forks
- 698
- Avg merge
- 3d 4h
- Merged PRs (30d)
- 47
Description
**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..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..null at st: {0.5,0.5}
No inventory test..null u0 v0
test..null did not sample correctly.
Incorrect result: {0,0,0,0}
```
4. 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..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: https://github.com/AcademySoftwareFoundation/OpenImageIO/blob/64f829febd352686538beaba10e4ca716a9403a1/src/libOpenImageIO/imagecache_test.cpp#L161-L186
Test code snippet:
```cpp
#include
#include
#include
#include
#include
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 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 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 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..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..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
```
Contributor guide
Research direction
Start by reproducing OIIOTest::testBufferUDIMSampling() with ImageCache::add_file/add_tile and the TextureSystem inventory_udim and texture entry points shown in the report. Compare direct tile sampling with sampling through test..null; done means the in-memory tiles are inventoried and the UDIM pattern returns the expected red and green samples.
Written by the indexing model from the issue text.
Assessment
- Tech stack
- cpp
- Domain
- computer-graphics
- Issue type
- Bug
- Difficulty
- 4/5
- Estimated time
- 3-5 days
- Activity status
- Stale
- Clarity
- Mostly clear
- Newbie friendliness
- 35/100