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

Add support for reading DPX files from memory #2659

Merged
merged 7 commits into from
Aug 8, 2020
Merged

Add support for reading DPX files from memory #2659

merged 7 commits into from
Aug 8, 2020

Conversation

r-arjocu
Copy link
Contributor

@r-arjocu r-arjocu commented Aug 6, 2020

Description

Add support for reading DPX files from memory. I think it's obvious from the code that I've transformed the LibDpx InStream class into an interface, moved its implementation to InStreamFile and added an InStreamMem class that reads from memory; also updated the DPX OIIO code to allow for an IOProxy when reading.

Tests

I have separately tested the LibDpx InStreamMem class in a small project, but it is not straightforward to port and I have little experience with OIIO's testing environment.
But I have seen there are tests in place that test reading from memory if an OIIO ImageInput supports "ioproxy" attribute, so reading DPX from memory is tested automatically.

Checklist:

  • I have read the contribution guidelines.
  • If this is more extensive than a small change to existing code, I
    have previously submitted a Contributor License Agreement
    (individual, and if there is any way my
    employers might think my programming belongs to them, then also
    corporate).
  • I have updated the documentation, if applicable.
  • I have ensured that the change is tested somewhere in the testsuite
    (adding new test cases if necessary).
  • My code follows the prevailing code style of this project.

@r-arjocu
Copy link
Contributor Author

r-arjocu commented Aug 6, 2020

I am unsure about the clang-format, since the LibDpx file modifications were done in a separate project, in order to separately test reading from actual DPX files; I've tried to keep the original LibDpx style. Visual Studio applied the OIIO formatting for "dpxinput.cpp", but I don't think it did for the LibDpx files.

@r-arjocu
Copy link
Contributor Author

r-arjocu commented Aug 6, 2020

Please "forward port" the commit on the 'master' branch!

@lgritz
Copy link
Collaborator

lgritz commented Aug 6, 2020

Please "forward port" the commit on the 'master' branch!

Oh yes, I will always port fixes to master. And also to 2.2 which is in beta now and will soon be the supported release branch.

@Biswa96
Copy link
Contributor

Biswa96 commented Aug 6, 2020

If the memcpy_s logic is correct, it can be used with #ifdef _WIN32 like conditions.

@r-arjocu
Copy link
Contributor Author

r-arjocu commented Aug 6, 2020

If the memcpy_s logic is correct, it can be used with #ifdef _WIN32 like conditions.

I guard against overflowing copy through auto cpySize = (remSize < size) ? remSize : size;. On VS and Windows memcpy_s doesn't give a warning (and is an extra check sometimes). But I have seen gcc and other compilers don't like it, so I think it's safe to use memcpy without complicating the code with #ifdef-s

@lgritz
Copy link
Collaborator

lgritz commented Aug 6, 2020

I really like where this is going, and I appreciate the addition of IOProxy support to DPX.

OK, so if I understand, you now allow a config hint to supply an IOProxy -- but only the memreader kind -- and then you have a subclass of libdpx's InStream that basically "wraps" the IOProxy. When the hint is not configured, you just use another subclass of InStream that basically did what it always did. (Really, you renamed InStream to InStreamFile, made a new InStream as a pure virtual parent class of that and the new InStreamMem.)

But does anything you've done in InStreamMem truly depend on the fact that it's an IOMemReader?

Maybe a more general design would be to ONLY have what is now your InStreamMem (though you can keep the old name, InStream), and let it use any IOProxy type, not just a memreader.

For the case where no IOProxy is supplied by the user, you just create one (of the IOFile variety) internally to the DPXReader.

The advantages of this are:

  • Just one code path, not a separate one for files and mem reading.
  • Less total code (you can eliminate the file I/O from the old InStream, all the real work is done by the IOProxy.
  • Can work with other, or future, implementations of IOProxy that are not memreader.

An example of this is what we currently do in the PNG reader, which you can see here: https://github.com/OpenImageIO/oiio/blob/master/src/png.imageio/pnginput.cpp#L144

@@ -43,6 +48,8 @@ class DPXInput final : public ImageInput {
std::vector<unsigned char> m_userBuf;
bool m_rawcolor;
std::vector<unsigned char> m_decodebuf; // temporary decode buffer
int64_t m_io_offset = 0;
Filesystem::IOProxy* m_io = nullptr;
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I believe you also want to add

virtual bool set_ioproxy(Filesystem::IOProxy* ioproxy) override

to this class

Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actually, set_ioproxy may only be a thing for 2.2, not 2.1.

If you're liking ioproxies, you should really try to get to 2.2 as soon as you can. There were several interesting enhancements.

Copy link
Contributor Author

@r-arjocu r-arjocu Aug 7, 2020

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I've seen some in the latest docs that it's easier to create ImageInput-s which read from memory. But I'm somewhat dependent on Vcpkg and especially on time. Maybe we'll switch to 2.2 when a newer version of OpenEXR is available on Vcpgkg -- I don't think I'll get the chance to learn how to modify and modify its "ports".

@r-arjocu
Copy link
Contributor Author

r-arjocu commented Aug 7, 2020

I really like where this is going, and I appreciate the addition of IOProxy support to DPX.

OK, so if I understand, you now allow a config hint to supply an IOProxy -- but only the memreader kind -- and then you have a subclass of libdpx's InStream that basically "wraps" the IOProxy. When the hint is not configured, you just use another subclass of InStream that basically did what it always did. (Really, you renamed InStream to InStreamFile, made a new InStream as a pure virtual parent class of that and the new InStreamMem.)

But does anything you've done in InStreamMem truly depend on the fact that it's an IOMemReader?

Maybe a more general design would be to ONLY have what is now your InStreamMem (though you can keep the old name, InStream), and let it use any IOProxy type, not just a memreader.

For the case where no IOProxy is supplied by the user, you just create one (of the IOFile variety) internally to the DPXReader.

The advantages of this are:

* Just one code path, not a separate one for files and mem reading.

* Less total code (you can eliminate the file I/O from the old InStream, all the real work is done by the IOProxy.

* Can work with other, or future, implementations of IOProxy that are not memreader.

An example of this is what we currently do in the PNG reader, which you can see here: https://github.com/OpenImageIO/oiio/blob/master/src/png.imageio/pnginput.cpp#L144

I understand the benefits, but I'm unsure about the technical implementation you advise. The code in PNG is fairly straightforward regarding m_io. But from the point of view of LibDpx: it needs a "stream reader" for its DPXReader class, set through SetInStream(). Are you telling me to convert back the InStream from a pure virtual to a "normal" class, and to put an IOProxy* member field, and that all calls to the InStream methods should be forwarded to and resolved by the IOProxy* field ?

@lgritz
Copy link
Collaborator

lgritz commented Aug 7, 2020

I'm saying that I think that your implementation of InStreamMem will work with any IOProxy, not just the memreader kind.

So you could call it InStreamIOProxy for clarity. And therefore that's the only kind of InStream you need -- you don't need InStreamFile at all, if you want to read from a file, you just create an IOFile kind of proxy and hand it to InStreamIOProxy. And if you do all I/O through that, you can get rid of InStreamFile, and get rid of the now-useless InStream parent class, and rename the InStreamMem/InStreamIOProxy to be simply the one and only InStream.

If this doesn't make a ton of sense, don't worry about it. I can start with what you have, mock it up, and see if it works.

@r-arjocu
Copy link
Contributor Author

r-arjocu commented Aug 7, 2020

I understand what you're saying. I'll give it a try now. Thank you!

@lgritz
Copy link
Collaborator

lgritz commented Aug 7, 2020

I understand what you're saying. I'll give it a try now. Thank you!

Cool, thanks. I think that if it works, you'll be pleased with the simplicity of the result, and it will make the DPX reader a lot more flexible by being able to handle any kind of proxy nor or in the future.

@r-arjocu
Copy link
Contributor Author

r-arjocu commented Aug 7, 2020

Kind of off topic, but isn't there a bug in the IOProxy class on line 359, RB-2.1?

 bool seek (int64_t offset, int origin) {
        return seek ((origin == SEEK_SET ? offset : 0) +
                     (origin == SEEK_CUR ? offset+tell() : 0) +
                     (origin == SEEK_END ? size() : 0));
    }

Shouldn't it be (origin == SEEK_END ? offset+int64_t(size()) : 0?

@lgritz
Copy link
Collaborator

lgritz commented Aug 7, 2020

Um... I think you are right!

Can you add this fix as well? Thanks.

@r-arjocu
Copy link
Contributor Author

r-arjocu commented Aug 7, 2020

Um... I think you are right!

Can you add this fix as well? Thanks.

I can add it. Should it be a separate pull request? --> I'll add it in the current pull req.

One question regarding tell() function from IOProxy. If the proxy is closed, does it return 0 or -1? --> I deduce from code that it's 0 (zero); I had it -1 in InStreamMem (for testing purposes mainly), but I'll set it to 0 now for uniformity.

@lgritz
Copy link
Collaborator

lgritz commented Aug 7, 2020

Same PR or separate, up to you. I think arguments could be made either way (separate topic than DPX? or both are enhancing our use of IOProxy, so make sense to do together?).

C++ ftell returns -1 if there's an error, so IOProxy::tell should behave the same way.

@r-arjocu
Copy link
Contributor Author

r-arjocu commented Aug 7, 2020

Pushed the advised implementation. I hope it's ok. Since I'll be away for 4 or 5 days, I wanted to finish this.
I'm not particularly proud of the m_io field from LibDpx InStream, but couldn't find a better and quicker solution -- besides, I've wasted more than an hour on a typo.
Done testing with imageinout_test and on our files.

@r-arjocu
Copy link
Contributor Author

r-arjocu commented Aug 7, 2020

A second off topic, I want to mention an inconvenience on Windows at least (maybe it will help somebody else): CMake sets OpenEXP library paths and library names for debug configuration to point to the release ones. I don't know if this has to do with a non-optimal OpenEXR CMake files (I'm new to CMake) or with the Vcpkg ports -- either way, CMake only has the paths and names to the release libraries, and I had to manually do corrections in the project properties of Visual Studio (more than half an hour).
Else, some nasty errors/crashes manifested when running imageinout_test on EXR. Also, saving the project files helps on VS, because it seems it defaults back to the CMake settings when building the whole solution.

Signed-off-by: Larry Gritz <lg@larrygritz.com>
@lgritz
Copy link
Collaborator

lgritz commented Aug 7, 2020

There was some slightly broken CI (mostly because you used a C++14 idiom that broke C++11), but I fixed and pushed an update for you. If that passes, I will merge.

Copy link
Collaborator

@lgritz lgritz left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

LGTM, thanks for the fixes!

@lgritz lgritz merged commit 82ad0b3 into AcademySoftwareFoundation:RB-2.1 Aug 8, 2020
lgritz pushed a commit to lgritz/OpenImageIO that referenced this pull request Aug 8, 2020
…ion#2659)

* Add support for reading DPX files from any IOProxy
* Replace memcpy_s function with memcpy
* Fix IOProxy bug (not just for DPX) seeking from end

Signed-off-by: ra-mt <radu.arjocu@masstech.com>
@lgritz
Copy link
Collaborator

lgritz commented Aug 8, 2020

I will port forward to 2.2 and master

@lgritz
Copy link
Collaborator

lgritz commented Aug 8, 2020

Um... we have a bit of a problem, which I did not discover until after I'd merged this and tried to forward-port it to 2.2 and master (your patch was against RB-2.1). You see, in 2.2+, there's another unit test called imageinout_test, and among other things, it tries reading and writing every format in a variety of ways including with IOProxy, both directly with ImageInput, but also via an ImageBuf (which was extended in 2.2 to support IOProxy, along with ImageCache). And, unfortunately, this test is now crashing in the DPX IOProxy-related code.

I'm not sure how bad it is. It could be a minor bug in DPX's handling of IOProxy, undiscovered in your original port because imageinout_test isn't part of 2.1, but the flaw is still there. Or, it could be that the DPX code is fine per se, but it's exposing a deeper logical problem in how ImageBuf and/or ImageCache deal with IOProxy.

I'll look into it and try to fix over the weekend. If it's the case that it's a simple fix, I'll try to fix and apply the patch to 2.1 as well as merging the fixed version into master and 2.2, and we're all set.

But if the changes necessary to fully fix it are compatibility-breaking, or just too extensive and risky, this will have to be a "master" feature only and I'll need to revert the patch to remove it from 2.1 (which is supposed to be exceptionally stable -- I can't introduce any instability or incompatibility at this point in its life).

I'll let you know what I find. But I just wanted to warn you as early as possible that there's at least some possibility that this will not be able to be a 2.1 feature.

@r-arjocu
Copy link
Contributor Author

r-arjocu commented Aug 8, 2020

There was some slightly broken CI (mostly because you used a C++14 idiom that broke C++11), but I fixed and pushed an update for you. If that passes, I will merge.

I wanted to fix it quickly now, because it was too late last night. So thanks for the fix!

@r-arjocu
Copy link
Contributor Author

r-arjocu commented Aug 8, 2020

Um... we have a bit of a problem, which I did not discover until after I'd merged this and tried to forward-port it to 2.2 and master (your patch was against RB-2.1). You see, in 2.2+, there's another unit test called imageinout_test, and among other things, it tries reading and writing every format in a variety of ways including with IOProxy, both directly with ImageInput, but also via an ImageBuf (which was extended in 2.2 to support IOProxy, along with ImageCache). And, unfortunately, this test is now crashing in the DPX IOProxy-related code.

I'm not sure how bad it is. It could be a minor bug in DPX's handling of IOProxy, undiscovered in your original port because imageinout_test isn't part of 2.1, but the flaw is still there. Or, it could be that the DPX code is fine per se, but it's exposing a deeper logical problem in how ImageBuf and/or ImageCache deal with IOProxy.

I'll look into it and try to fix over the weekend. If it's the case that it's a simple fix, I'll try to fix and apply the patch to 2.1 as well as merging the fixed version into master and 2.2, and we're all set.

But if the changes necessary to fully fix it are compatibility-breaking, or just too extensive and risky, this will have to be a "master" feature only and I'll need to revert the patch to remove it from 2.1 (which is supposed to be exceptionally stable -- I can't introduce any instability or incompatibility at this point in its life).

I'll let you know what I find. But I just wanted to warn you as early as possible that there's at least some possibility that this will not be able to be a 2.1 feature.

If it's not going to make it in 2.1, it's not a big problem. I've locally added my original solution to 2.1.17.0 and built that, and that code is running ok, tested by our QA. It will be shortly in production usage, and we'll get some feedback from there. If we'll need other fixes and enhancements, I think we'll go with OIIO 2.2 -- with the help of Vcpkg hopefully.

I'll have an eye over what you discover and the fixes when I'll come back. Thank you!

@lgritz
Copy link
Collaborator

lgritz commented Aug 10, 2020

I think #2665 solves the problem for 2.1 on the DPX side, then there's an additional small change for the ImageCache side I will submit separately.

lgritz pushed a commit to lgritz/OpenImageIO that referenced this pull request Aug 11, 2020
…ion#2659)

* Add support for reading DPX files from any IOProxy
* Replace memcpy_s function with memcpy
* Fix IOProxy bug (not just for DPX) seeking from end

Signed-off-by: ra-mt <radu.arjocu@masstech.com>
lgritz pushed a commit to lgritz/OpenImageIO that referenced this pull request Aug 11, 2020
…ion#2659)

* Add support for reading DPX files from any IOProxy
* Replace memcpy_s function with memcpy
* Fix IOProxy bug (not just for DPX) seeking from end

Signed-off-by: ra-mt <radu.arjocu@masstech.com>
@r-arjocu
Copy link
Contributor Author

That's great news. Thank you!

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

Successfully merging this pull request may close these issues.

None yet

3 participants