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

ClientKit: Remove dependency on OpenCV from Imaging. #210

Merged
merged 5 commits into from
Nov 6, 2015

Conversation

CrossVR
Copy link
Collaborator

@CrossVR CrossVR commented Sep 12, 2015

Wrapping the image data into an OpenCV type should be the responsibility of the application. OpenCV is a very large dependency and it should not be depended on just for a few lines of code.

Fixes issue #77.

newReport.sensor = report->sensor;
newReport.buffer.reset(report->state.data,
ImagingDeleter(self->m_ctx));
newReport.frame = cv::Mat(
Copy link
Member

Choose a reason for hiding this comment

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

This is just in the optional C++ header-only wrapper. While it's recommended, because of typing reasons, it's not strictly required for use of even the imaging interface. There's not much point in having this extra layer wrapping the callbacks without marshalling the data into the OpenCV type - that's what this file exists for. If you just want the bytes, there's the C interface.

Copy link
Collaborator Author

Choose a reason for hiding this comment

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

But I thought we didn't want to encourage people to downgrade to the C interface just to get rid of a dependency? See issue #44.

Copy link
Collaborator Author

Choose a reason for hiding this comment

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

@rpavlik Just to quote the specific argument:

What I really wouldn't want to see is the (slightly unsafe, less usable) C API being used (or a wrapper re-invented) in C++ games/engines because the wrapper uses things that aren't allowed.

Copy link
Member

Choose a reason for hiding this comment

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

Touche - good point :D Probably could split this into two layers - a lifetime-management layer and then a opencv layer on top, so you can use whichever you like. Especially with Imaging at the moment,I really don't want people using the C API because you actually get a lock on an element of a shared-memory ring buffer, so failing to release that is a problem.

Copy link
Collaborator Author

Choose a reason for hiding this comment

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

Exactly, but don't we already have that opencv layer? OpenCVTypeDispatch.h is all a developer would need to wrap the buffer pointer into a matrix using the cv::Mat constructor, it would basically be a one-liner.

@rpavlik
Copy link
Member

rpavlik commented Sep 14, 2015

Thanks for working on this! I'd really rather see a "halfway" approach - being able to build without OpenCV optionally, mangling the library names so it's clear imaging support might be degraded. (It's annoying that the C++ standard doesn't provide all the alignment guarantees needed to support efficient processing with SIMD instructions, GPU processing, etc, but them's the breaks.

None of the changes in the util headers or in the clientkit headers should be needed to build without OpenCV - you should be able to just turn off the building of the "header dependency tests" (which you'd probably want to do in a submodule situation anyway) and then, in proper C++ form, you won't pay for what you don't use.

Thanks for pointing me to this - GitHub's notification system is befuddling and I typically end up getting notified only about things I don't want to be...

@CrossVR
Copy link
Collaborator Author

CrossVR commented Sep 14, 2015

I'm not really convinced this is at all useful, most people will use their own image processing library. They'll load the image into something like a texture and never even use OpenCV, but currently they'll have to pull it in as a huge dependency.

Even if we make it optional for the developers that do use OpenCV, this wrapper only saves them a few lines of code at most. Is it really worth the added complexity of having two different versions of this header in the ClientKit?

@CrossVR CrossVR force-pushed the client-nocv branch 7 times, most recently from a08689f to 31da52d Compare September 20, 2015 00:04
@CrossVR
Copy link
Collaborator Author

CrossVR commented Sep 20, 2015

@rpavlik I'm done implementing those changes. I think benefits of removing the OpenCV dependency outweighs the benefits of having the wrapper and the lifetime management inside the same structure.

If we still want to take care of the lifetime management we should make a child class of cv::Mat that actually supports std::shared_ptr data pointers.

@CrossVR
Copy link
Collaborator Author

CrossVR commented Sep 22, 2015

@rpavlik Any feedback on this PR?

@d235j
Copy link
Contributor

d235j commented Oct 8, 2015

I'm curious as to what (if anything) is holding this up, too; OpenCV is a fairly heavyweight dependency and while it's very useful, I don't think pulling in large dependencies is a great idea.

@rpavlik
Copy link
Member

rpavlik commented Oct 8, 2015

I agree, it's primarily lack of time to review, as well as hesitancy regarding breaking API/ABI.

The ClientKit header patches, as long as they come with matching fixes to the corresponding clients, are fine and easy to approve: those are to semi-optional pieces of the API (the C++ header-only stuff - semi-optional just for imaging because not managing imaging lifetime correctly right now is the only way to freeze the server from the client), don't affect already-compiled client apps, but some extra work to update clients using that wrapper to the new API. In general, pretty good.

However, the change in Common (the change in allocation/deallocation in ImagingComponent) actually breaks our plugin ABI, and would result in all plugins that do imaging crashing the server. (At least on Windows, mixing allocation and deallocation, or doing so over .dll boundaries, is a fast lane to crashtown).

Now, the core deallocating a buffer allocated by a plugin seemed like a good idea at the time because it meant the core didn't do a copy/allocation, and I didn't expect opposition to using OpenCV (considered it the de-facto standard and that delay-loading the DLL in common was good enough to hide the "cost"). However, in retrospect a function with a user-provided deleter would have been better (same advantages: reduced copying, clear ownership semantics, plus no mandatory dependency).

I agree that this (meaning defaulting to C++ standard aligned allocation in imaging component, as well as the change to receive a deleter function from PluginKit) should be done (it's tripped up several folks), we just need to take it to the mailing list probably and hash out how to do it - probably safest and easiest to break the API at the same time removing the old function and creating a new, differently-named function to replace it that takes both the buffer and the deleter. Haven't actually had a non-backward-compatible API/ABI break in pluginkit in months, so you see why I am hesitant :) (There are other things I'd like to break too, see #53 which is already implemented internally, we're just ignoring one parameter - so one break invites the question of doing more breaks all at once or instead giving "version 2" functions in addition...)

CC some folks recently touching imaging stuff: @JeroMiya @zachkinstner

@d235j
Copy link
Contributor

d235j commented Oct 8, 2015

@rpavlik are we at a point yet where OSVR is stable enough that breaking API changes are an issue? Obviously this would have to undergo more serious discussion (mailing list is not a bad idea here), but considering that OSVR is not yet widely used, it's better we do breaking API changes that are needed now rather than putting in place non-deal API. At some point it might be worthwhile doing a review of sorts going over the API and finalizing things.

I have to look at the allocation/deallocation stuff more closely. I've been through that mess on Windows myself and it's not something you want to deal with. It gets even worse when someone decides to replace operator new and delete, and it would not surprise me if some codebases do this.

@CrossVR
Copy link
Collaborator Author

CrossVR commented Oct 9, 2015

@rpavlik The allocator and deallocator work exactly like their OpenCV counterpart. It stores the pointer to the original buffer at index -1 on allocation and uses that pointer when deallocating. It should be no problem for it to deallocate buffers allocated by OpenCV and vice-versa. Have you tested whether it actually breaks the ABI?

The only risk is when OpenCV has been compiled with a different Visual C++ runtime than the server. But that's not a problem, both OpenCV and OSVR use the same VC12 runtime.

Also, as long as OSVR is not at version 1.0 yet I think we should make API (and ABI) changes that make sense, even if they are breaking, instead of already building up a ton of legacy APIs. I see no reason for anyone to expect that version 0.2 and version 0.3 should be backwards compatible. Backwards compatibility is nice, but it should never hold back sensible changes.

You can't hide the cost of including a dependency, OSVR is an open-source project so developers are going to build it from source within their projects as it is much more convenient for debugging and version management.

@rpavlik
Copy link
Member

rpavlik commented Oct 9, 2015

No, I haven't had a chance, but based on the number of crashes I've heard of when people didn't use the exact build of OpenCV we used to allocate their buffer, and the number of times cross DLL deallocation has bitten me, it makes me nervous. (Thinking of an ABI break as in change of behavior that affects library consumers, not directly the binary interface itself, of course. The subtle kind of break that's harder to test for) It would be great if it didn't break ABI, then we could put off the real fix for longer, but I simply haven't had a chance to test it on Windows. Easiest way would be to make a windows build then try to use a plugin (OpenCV camera, leap motion) compiled against a shipped binary snapshot. Testing on Android would be harder for poor @JeroMiya

@d235j
Copy link
Contributor

d235j commented Oct 9, 2015

This is likely to only be an issue on Windows due to the nature of static linking and (de)allocators on that platform. I've spent hours debugging issues caused by (de)allocations across DLL boundaries and this is something we should avoid if possible.
It does look like the changes here use raw malloc and free though, which should be safe.

@CrossVR
Copy link
Collaborator Author

CrossVR commented Oct 12, 2015

@rpavlik I can do the test, but will that really be enough to convince you the changes are safe?

You're getting the crash because people are mixing the Visual C++ versions of the OpenCV DLLs. This change will actually fix the crash because OSVR will always do the allocation/deallocation with its own Visual C++ version.

Except if people are using a different Visual C++ version to compile the plugins. That is probably the issue you're facing. You should cross the DLL boundary again and not make the plugin responsible for the deallocation, instead it should call back to the server for the deallocation. Or vice-versa, which is what you're suggesting.

@rpavlik
Copy link
Member

rpavlik commented Oct 12, 2015

It's not that I need convincing the changes are safe, it's that if they happen to be ABI-safe right now that's purely coincidence, and the correct fix is to fix the API.

If you split this pull request into ClientKit-only and core, I think we can get stuff merged sooner.

@CrossVR
Copy link
Collaborator Author

CrossVR commented Oct 12, 2015

Doesn't the ClientKit rely on ImagingComponent?

@rpavlik
Copy link
Member

rpavlik commented Oct 12, 2015

Its implementation does, but its API doesn't. This accomplishes something (reducing required libraries for the client API) on its own.

@CrossVR
Copy link
Collaborator Author

CrossVR commented Oct 13, 2015

I need to compile the implementation, that's the whole reason I'm making these changes.

So how do you want the API fixed? Will the plugin be responsible for allocation/deallocation or will the server be responsible?

@rpavlik
Copy link
Member

rpavlik commented Oct 13, 2015

I think the plugin should be responsible as mentioned in #210 (comment) but please email the devel list to have a public discussion so that other stakeholders can participate.

@CrossVR
Copy link
Collaborator Author

CrossVR commented Oct 13, 2015

@rpavlik Could you send that email to the devel list? You know more about this than I do.

@CrossVR
Copy link
Collaborator Author

CrossVR commented Oct 20, 2015

@rpavlik No response from the mailing list yet, so I assume everyone is fine with the the current idea?

@CrossVR
Copy link
Collaborator Author

CrossVR commented Oct 20, 2015

Okay, I'll make the allocation/deallocation API changes this week and then we can merge it.

@CrossVR
Copy link
Collaborator Author

CrossVR commented Oct 20, 2015

@rpavlik I've tried looking at the code base today regarding memory management, but it is a lot more complicated than I anticipated. I don't think I'll be able to do that within a week and I honestly don't have any more time for it than that.

Since this PR doesn't make the memory issue any worse, could we please address that issue in a different PR?

I did the test you specified and I had no problems retrieving an image from my webcam using the OpenCV plugin and Imaging example from the 0.6 snapshot with an OSVR server compiled from the code in this PR.

@CrossVR CrossVR force-pushed the client-nocv branch 2 times, most recently from ad675de to d26f4e8 Compare October 20, 2015 20:52
@rpavlik
Copy link
Member

rpavlik commented Oct 21, 2015

I'll see when I can take a look at the pull request. If nothing else I can merge the API-only stuff easily. I've posted a followup on the mailing list (or at least I tried so, though I don't see it yet) laying out the ABI/API break stuff. Quite interesting that you were able to use a plugin from the snapshot (Windows, I'm assuming, since we don't publish other snapshot binaries?) with this PR - given how crash-happy that particular code has been, I'm pleasantly surprised. We'll fix the API and whole thing soon here.

// Allocate a memory pointer aligned to a boundary.
size_t space = bytes + sizeof(void*) + OSVR_ALIGN_SIZE;
void* buffer = malloc(space);
void* aligned = std::align(OSVR_ALIGN_SIZE, bytes, buffer, space);
Copy link
Member

Choose a reason for hiding this comment

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

I'm not terribly familiar with the C++11 alignment functionality yet - are we guaranteed here that aligned > buffer so that below, when we do aligned[-1] we're not writing out of bounds?

Copy link
Collaborator Author

Choose a reason for hiding this comment

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

Yeah that's an error, there should be a buffer+1 there. Thanks for
catching it, will correct it tomorrow.

@CrossVR CrossVR force-pushed the client-nocv branch 2 times, most recently from b7157a8 to 05b4896 Compare October 22, 2015 10:16
@CrossVR
Copy link
Collaborator Author

CrossVR commented Oct 22, 2015

@rpavlik I've made the necessary changes, it's ready for review.

Do note that merging just the API changes without the underlying source changes will not help me since I will still be confronted with the same dependency when compiling the ClientKit.

@CrossVR
Copy link
Collaborator Author

CrossVR commented Oct 30, 2015

@rpavlik You mentioned that you'd like to move the allocator/deallocator to osvrUtil? Are there any other changes you need? I can make those changes this weekend so that the PR can be merged without any more work from your side.

@rpavlik
Copy link
Member

rpavlik commented Oct 30, 2015

oh, thanks! That would be great - yeah, just moved to Util, probably into an implementation file, with a public header exporting the functions, and maybe adjust the C++ wrapper on the pluginkit side so that it uses that, instead of the OpenCV matrix clone.
(The silly build stuff would take me longer to describe than to do.)

@CrossVR CrossVR force-pushed the client-nocv branch 4 times, most recently from cf7a307 to 4692b9d Compare November 3, 2015 17:33
@CrossVR
Copy link
Collaborator Author

CrossVR commented Nov 3, 2015

@rpavlik I'm done moving the allocator function into its own header. I'm not sure what to do with the PluginKit, using these new allocator functions there would result in copying a buffer which we want to avoid.

@rpavlik
Copy link
Member

rpavlik commented Nov 4, 2015

There's already a buffer copy hidden there. I can take care of it - I put it there. I plan to review and merge this today.

@rpavlik
Copy link
Member

rpavlik commented Nov 6, 2015

I've pulled this locally and am just making a few little changes before pushing it to master. Will leave this open until that's in and merged.

@rpavlik rpavlik mentioned this pull request Nov 6, 2015
@rpavlik
Copy link
Member

rpavlik commented Nov 6, 2015

OK, I've opened the new pull request #260 which is just this one, merged into master, with a few changes. Waiting on some help on making sure I didn't break code somebody else understands better than me, and a positive travis-ci result, then should be clear to go.

@rpavlik rpavlik merged commit e31e06f into OSVR:master Nov 6, 2015
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.

4 participants