-
Notifications
You must be signed in to change notification settings - Fork 608
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
api: use shared_ptr for ImageCache and TextureSystem #4377
Changes from all commits
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -77,7 +77,7 @@ enum class InitializePixels { No = 0, Yes = 1 }; | |
/// translate into a different data format than appears in the file). | ||
/// | ||
/// ImageBuf data coming from disk files may optionally be backed by | ||
/// ImageCache, by explicitly passing an `ImageCache*` to the ImageBuf | ||
/// ImageCache, by explicitly passing an ImageCache to the ImageBuf | ||
/// constructor or `reset()` method (pass `ImageCache::create()` to get a | ||
/// pointer to the default global ImageCache), or by having previously set the | ||
/// global OIIO attribute `"imagebuf:use_imagecache"` to a nonzero value. When | ||
|
@@ -186,9 +186,9 @@ class OIIO_API ImageBuf { | |
/// ensure that it remains valid for the lifetime of the ImageBuf. | ||
/// | ||
explicit ImageBuf(string_view name, int subimage = 0, int miplevel = 0, | ||
ImageCache* imagecache = nullptr, | ||
const ImageSpec* config = nullptr, | ||
Filesystem::IOProxy* ioproxy = nullptr); | ||
std::shared_ptr<ImageCache> imagecache = {}, | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Should this be There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. N/M, I see now: pass by value + use std::move within the ctr will eliminate any extraneous ref count adjustment. |
||
const ImageSpec* config = nullptr, | ||
Filesystem::IOProxy* ioproxy = nullptr); | ||
|
||
/// Construct a writable ImageBuf with the given specification | ||
/// (including resolution, data type, metadata, etc.). The ImageBuf will | ||
|
@@ -270,9 +270,9 @@ class OIIO_API ImageBuf { | |
/// as if newly constructed with the same arguments, as a read-only | ||
/// representation of an existing image file. | ||
void reset(string_view name, int subimage = 0, int miplevel = 0, | ||
ImageCache* imagecache = nullptr, | ||
const ImageSpec* config = nullptr, | ||
Filesystem::IOProxy* ioproxy = nullptr); | ||
std::shared_ptr<ImageCache> imagecache = {}, | ||
const ImageSpec* config = nullptr, | ||
Filesystem::IOProxy* ioproxy = nullptr); | ||
|
||
/// Destroy any previous contents of the ImageBuf and re-initialize it | ||
/// as if newly constructed with the same arguments, as a read/write | ||
|
@@ -1005,9 +1005,9 @@ class OIIO_API ImageBuf { | |
/// image being in RAM somewhere? | ||
bool cachedpixels() const; | ||
|
||
/// A pointer to the underlying ImageCache, or nullptr if this ImageBuf | ||
/// is not backed by an ImageCache. | ||
ImageCache* imagecache() const; | ||
/// A shared pointer to the underlying ImageCache, or empty if this | ||
/// ImageBuf is not backed by an ImageCache. | ||
std::shared_ptr<ImageCache> imagecache() const; | ||
|
||
/// Return the address where pixel `(x,y,z)`, channel `ch`, is stored in | ||
/// the image buffer. Use with extreme caution! Will return `nullptr` | ||
|
@@ -1151,7 +1151,7 @@ class OIIO_API ImageBuf { | |
// Deprecated things -- might be removed at any time | ||
|
||
OIIO_DEPRECATED("Use `ImageBuf(name, 0, 0, imagecache, nullptr)` (2.2)") | ||
ImageBuf(string_view name, ImageCache* imagecache) | ||
ImageBuf(string_view name, std::shared_ptr<ImageCache> imagecache) | ||
: ImageBuf(name, 0, 0, imagecache) | ||
{ | ||
} | ||
|
@@ -1164,7 +1164,7 @@ class OIIO_API ImageBuf { | |
} | ||
|
||
OIIO_DEPRECATED("Use `reset(name, 0, 0, imagecache)` (2.2)") | ||
void reset(string_view name, ImageCache* imagecache) | ||
void reset(string_view name, std::shared_ptr<ImageCache> imagecache) | ||
{ | ||
reset(name, 0, 0, imagecache); | ||
} | ||
|
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
One thing to watch out for, is that passing
std::shared_ptr
by value will twiddle the reference count unnecessarily. In pathological cases it could lead to false sharing type performance bugs. It also signals the function might be trying to keep its own internal reference, which I don't think is the intent here.I think passing the shared_ptr by const reference is the recommended idiom for cases like these where you don't intend to touch the shared pointer itself, just want to use the object it points to.
Some related articles:
https://herbsutter.com/2013/06/05/gotw-91-solution-smart-pointer-parameters/
https://isocpp.github.io/CppCoreGuidelines/CppCoreGuidelines#Rr-uniqueptrparam
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
On reading the reset of the review more carefully, it looks like ImageBuf, does need to keep a reference to the cache, so it looks like the by-value semantics are right here. Still, its worth keeping in mind the hidden cost of the reference count being twiddled at each function call (more than it would have been before).
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Specifically to this place in the code: it won't make a difference, this is in idiff, there is no concurrency here and only a few calls to this function per run.
But I take your point generally. In a case like this, do you think it's better to pass as
const shared_ptr<T>&
or just as aT*
, knowing that the shared_ptr from which it came is guaranteed to outlive the function call?There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
If we passed a
const shared_ptr<T>&
to ImageBuf::reset(), actually, that would be fine because it would still properly make a new shared_ptr internal to the IB, while avoiding the ref count diddling on the way in and out of the call. On the other hand, reset() (much like the constructor) is called, very infrequently if ever (after all, it's basically throwing out the contents of the IB and then reusing or reallocating it for something totally different). So I'm not sure there is a performance issue here in actuality.I definitely agree that if there is any API call that we expect to be calling in a tight loop and it needs access to the cache for informational purposes (not to take ownership), in that case it's very important to pass either a reference to the shared_ptr, or else a regular pointer from ptr.get().
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I will look, too, but please help me double check for instances of API calls that
We should ensure that in such instances, we are passing a reference or a non-owning raw pointer.