-
-
Notifications
You must be signed in to change notification settings - Fork 56.5k
Description
System information (version)
- OpenCV => 3.3
- Operating System / Platform => Mac OS X 10.12.6 BuildVersion 16G29
- Compiler => Apple LLVM version 8.1.0 (clang-802.0.42)
Detailed description
FYI: Example output used in description is excerpt from the full output linked below.
This issue I stumbled upon whilst trying to write some unit tests, for an image processing pipeline that I'd neatly packaged into a standalone class. This class is responsible for setting up an OpenCL context with OpenGL sharing, e.g.:
cl_context_properties contextProps[] = {
CL_CONTEXT_PROPERTY_USE_CGL_SHAREGROUP_APPLE,
(cl_context_properties) CGLGetShareGroup(CGLGetCurrentContext()),
0
};Subsequently attaching it to OpenCV with cv::ocl::attachContext and then handling the actual processing.
Setting up and tearing down instances of this class in test, was producing errors left and right, so I decided to do some spelunking in cv::ocl to see what was going on. Then I came upon the use of CV_SINGLETON_LAZY_INIT, for a lot of the getDefault methods used in cv::ocl.
As can be seen from the example attached, since OpenCLAllocator is in fact instantiated via this singleton pattern, it makes it impossible to actually use cv::ocl::attachContext, more than once in the lifetime of a program. First time around it allocates an OpenCL buffer for the UMatData:
OpenCL allocate 12288 (0x3000) bytes: 0x7fd92af2b550
Using the initially created OpenCL context:
Created context (cl_context): 0x7fd92af0cc80
But the second time around, it tries to reuse the buffer since it's saved in the buffer pool:
Reuse reserved buffer: 0x7fd92af2b550
The problem is then, that the buffer was orphaned when the initial OpenCL context got destroyed, and is thus no longer valid under the new:
Created context (cl_context): 0x7fd92af2cc10
This inevitably leads to OpenCL errors like this:
[CL_INVALID_CONTEXT] : OpenCL Error : clEnqueueWriteBuffer failed: cl_mem object belongs to a different context 0x7fd92af0cc80 than the queue's context 0x7fd92af2cc10.make: *** [default] Abort trap: 6
I didn't go further with this example, but I'd image that cv::ocl::Queue suffers the same issue, since it as well, is attached as a singleton to the global CoreTLSData as well.
Now then, is the question of how to solve this problem.
On one hand using singletons like this makes it a lot easier to write methods of the getDefault family. On the other hand, it's a leaky abstraction, that forces consumers of the library to use the same pattern in client code as well. Singletons are inherently the nemesis of unit testing and google can quickly provide troves of reading, on the trials and tribulations of people using them. Highest ranking write-up is probably by Brian Button on Why Singletons are Evil
I see a couple of ways this could be approached.
A. An easy way; making single{ton} use an explicit requirement for consumers setting up custom OpenCL contexts, through beefing up the documentation of the cv::ocl module and explicitly cv::ocl::attachContext.
B. A hard way; rewriting the lifetime management part of the various classes of the module, to all live and die with the OpenCL context they got created under. The various getDefault methods would have to somehow tie back to the default context instance, which would have to centrally keep track of things.
C. A ? way; figure out a way to share OpenCL memory objects between different contexts, like with gl_sharing. (I'll admit to not having looked into this enough to know if it's even possible)
I wouldn't mind trying to tackle B, but I would definitely need some supervision/guidance if that's the way to go.
Thoughts?
CC @alalek @ilya-lavrenov @apavlenko @vpisarev
Steps to reproduce
Minimal example program available in this gist (also attached as .zip): https://gist.github.com/rhardih/8cef29ccefe7af9166a2aa03799ca3ec
8cef29ccefe7af9166a2aa03799ca3ec-a0e1354dd4db24d01ccd2cf144fcb070c9926236.zip