core/ocl: temporary move device selection from ocl module
authorAlexander Alekhin <alexander.alekhin@itseez.com>
Wed, 25 Dec 2013 10:39:21 +0000 (14:39 +0400)
committerAlexander Alekhin <alexander.alekhin@itseez.com>
Wed, 25 Dec 2013 12:17:40 +0000 (16:17 +0400)
modules/core/include/opencv2/core/ocl.hpp
modules/core/src/ocl.cpp
modules/ocl/perf/main.cpp
modules/ocl/perf/perf_precomp.hpp
modules/ocl/test/main.cpp
modules/ocl/test/test_precomp.hpp
modules/ts/include/opencv2/ts.hpp
modules/ts/src/ocl_test.cpp

index 7caf4c2..3112766 100644 (file)
@@ -210,6 +210,7 @@ public:
     Context2(const Context2& c);
     Context2& operator = (const Context2& c);
 
+    bool create();
     bool create(int dtype);
     size_t ndevices() const;
     const Device& device(size_t idx) const;
index 67e5423..92c9ffb 100644 (file)
@@ -41,6 +41,9 @@
 
 #include "precomp.hpp"
 #include <map>
+#include <string>
+#include <sstream>
+#include <iostream> // std::cerr
 
 #include "opencv2/core/opencl/runtime/opencl_clamdblas.hpp"
 #include "opencv2/core/opencl/runtime/opencl_clamdfft.hpp"
@@ -1905,6 +1908,232 @@ const Device& Device::getDefault()
 
 /////////////////////////////////////////////////////////////////////////////////////////
 
+template <typename Functor, typename ObjectType>
+inline cl_int getStringInfo(Functor f, ObjectType obj, cl_uint name, std::string& param)
+{
+    ::size_t required;
+    cl_int err = f(obj, name, 0, NULL, &required);
+    if (err != CL_SUCCESS)
+        return err;
+
+    param.clear();
+    if (required > 0)
+    {
+        std::vector<char> buf(required + 1, char(0));
+        err = f(obj, name, required, &buf[0], NULL);
+        if (err != CL_SUCCESS)
+            return err;
+        param = &buf[0];
+    }
+
+    return CL_SUCCESS;
+};
+
+static void split(const std::string &s, char delim, std::vector<std::string> &elems) {
+    std::stringstream ss(s);
+    std::string item;
+    while (std::getline(ss, item, delim)) {
+        elems.push_back(item);
+    }
+}
+
+static std::vector<std::string> split(const std::string &s, char delim) {
+    std::vector<std::string> elems;
+    split(s, delim, elems);
+    return elems;
+}
+
+// Layout: <Platform>:<CPU|GPU|ACCELERATOR|nothing=GPU/CPU>:<deviceName>
+// Sample: AMD:GPU:
+// Sample: AMD:GPU:Tahiti
+// Sample: :GPU|CPU: = '' = ':' = '::'
+static bool parseOpenCLDeviceConfiguration(const std::string& configurationStr,
+        std::string& platform, std::vector<std::string>& deviceTypes, std::string& deviceNameOrID)
+{
+    std::string deviceTypesStr;
+    size_t p0 = configurationStr.find(':');
+    if (p0 != std::string::npos)
+    {
+        size_t p1 = configurationStr.find(':', p0 + 1);
+        if (p1 != std::string::npos)
+        {
+            size_t p2 = configurationStr.find(':', p1 + 1);
+            if (p2 != std::string::npos)
+            {
+                std::cerr << "ERROR: Invalid configuration string for OpenCL device" << std::endl;
+                return false;
+            }
+            else
+            {
+                // assume platform + device types + device name/id
+                platform = configurationStr.substr(0, p0);
+                deviceTypesStr = configurationStr.substr(p0 + 1, p1 - (p0 + 1));
+                deviceNameOrID = configurationStr.substr(p1 + 1, configurationStr.length() - (p1 + 1));
+            }
+        }
+        else
+        {
+            // assume platform + device types
+            platform = configurationStr.substr(0, p0);
+            deviceTypesStr = configurationStr.substr(p0 + 1, configurationStr.length() - (p0 + 1));
+        }
+    }
+    else
+    {
+        // assume only platform
+        platform = configurationStr;
+    }
+    deviceTypes = split(deviceTypesStr, '|');
+    return true;
+}
+
+static cl_device_id selectOpenCLDevice()
+{
+    std::string platform;
+    std::vector<std::string> deviceTypes;
+    std::string deviceName;
+    const char* configuration = getenv("OPENCV_OPENCL_DEVICE");
+    if (configuration)
+    {
+        if (!parseOpenCLDeviceConfiguration(std::string(configuration), platform, deviceTypes, deviceName))
+            return NULL;
+    }
+
+    bool isID = false;
+    int deviceID = -1;
+    if (deviceName.length() == 1)
+    // We limit ID range to 0..9, because we want to write:
+    // - '2500' to mean i5-2500
+    // - '8350' to mean AMD FX-8350
+    // - '650' to mean GeForce 650
+    // To extend ID range change condition to '> 0'
+    {
+        isID = true;
+        for (size_t i = 0; i < deviceName.length(); i++)
+        {
+            if (!isdigit(deviceName[i]))
+            {
+                isID = false;
+                break;
+            }
+        }
+        if (isID)
+        {
+            deviceID = atoi(deviceName.c_str());
+            CV_Assert(deviceID >= 0);
+        }
+    }
+
+    std::vector<cl_platform_id> platforms;
+    cl_uint numPlatforms = 0;
+    cl_int status = clGetPlatformIDs(0, NULL, &numPlatforms);
+    CV_Assert(status == CL_SUCCESS);
+    if (numPlatforms == 0)
+        return NULL;
+    platforms.resize((size_t)numPlatforms);
+    status = clGetPlatformIDs(numPlatforms, &platforms[0], &numPlatforms);
+    CV_Assert(status == CL_SUCCESS);
+
+    int selectedPlatform = -1;
+    if (platform.length() > 0)
+    {
+        for (size_t i = 0; i < platforms.size(); i++)
+        {
+            std::string name;
+            status = getStringInfo(clGetPlatformInfo, platforms[i], CL_PLATFORM_NAME, name);
+            CV_Assert(status == CL_SUCCESS);
+            if (name.find(platform) != std::string::npos)
+            {
+                selectedPlatform = (int)i;
+                break;
+            }
+        }
+        if (selectedPlatform == -1)
+        {
+            std::cerr << "ERROR: Can't find OpenCL platform by name: " << platform << std::endl;
+            goto not_found;
+        }
+    }
+
+    if (deviceTypes.size() == 0)
+    {
+        if (!isID)
+        {
+            deviceTypes.push_back("GPU");
+            deviceTypes.push_back("CPU");
+        }
+        else
+        {
+            deviceTypes.push_back("ALL");
+        }
+    }
+    for (size_t t = 0; t < deviceTypes.size(); t++)
+    {
+        int deviceType = 0;
+        if (deviceTypes[t] == "GPU")
+        {
+            deviceType = Device::TYPE_GPU;
+        }
+        else if (deviceTypes[t] == "CPU")
+        {
+            deviceType = Device::TYPE_CPU;
+        }
+        else if (deviceTypes[t] == "ACCELERATOR")
+        {
+            deviceType = Device::TYPE_ACCELERATOR;
+        }
+        else if (deviceTypes[t] == "ALL")
+        {
+            deviceType = Device::TYPE_ALL;
+        }
+        else
+        {
+            std::cerr << "ERROR: Unsupported device type for OpenCL device (GPU, CPU, ACCELERATOR): " << deviceTypes[t] << std::endl;
+            goto not_found;
+        }
+
+        std::vector<cl_device_id> devices; // TODO Use clReleaseDevice to cleanup
+        for (int i = selectedPlatform >= 0 ? selectedPlatform : 0;
+                (selectedPlatform >= 0 ? i == selectedPlatform : true) && (i < (int)platforms.size());
+                i++)
+        {
+            cl_uint count = 0;
+            status = clGetDeviceIDs(platforms[i], deviceType, 0, NULL, &count);
+            CV_Assert(status == CL_SUCCESS || status == CL_DEVICE_NOT_FOUND);
+            if (count == 0)
+                continue;
+            size_t base = devices.size();
+            devices.resize(base + count);
+            status = clGetDeviceIDs(platforms[i], deviceType, count, &devices[base], &count);
+            CV_Assert(status == CL_SUCCESS || status == CL_DEVICE_NOT_FOUND);
+        }
+
+        for (size_t i = (isID ? deviceID : 0);
+             (isID ? (i == (size_t)deviceID) : true) && (i < devices.size());
+             i++)
+        {
+            std::string name;
+            status = getStringInfo(clGetDeviceInfo, devices[i], CL_DEVICE_NAME, name);
+            CV_Assert(status == CL_SUCCESS);
+            if (isID || name.find(deviceName) != std::string::npos)
+            {
+                // TODO check for OpenCL 1.1
+                return devices[i];
+            }
+        }
+    }
+not_found:
+    std::cerr << "ERROR: Required OpenCL device not found, check configuration: " << (configuration == NULL ? "" : configuration) << std::endl
+            << "    Platform: " << (platform.length() == 0 ? "any" : platform) << std::endl
+            << "    Device types: ";
+    for (size_t t = 0; t < deviceTypes.size(); t++)
+    {
+        std::cerr << deviceTypes[t] << " ";
+    }
+    std::cerr << std::endl << "    Device name: " << (deviceName.length() == 0 ? "any" : deviceName) << std::endl;
+    return NULL;
+}
+
 struct Context2::Impl
 {
     Impl()
@@ -1913,6 +2142,42 @@ struct Context2::Impl
         handle = 0;
     }
 
+    void setDefault()
+    {
+        CV_Assert(handle == NULL);
+
+        cl_device_id d = selectOpenCLDevice();
+
+        if (d == NULL)
+            return;
+
+        cl_platform_id pl = NULL;
+        cl_int status = clGetDeviceInfo(d, CL_DEVICE_PLATFORM, sizeof(cl_platform_id), &pl, NULL);
+        CV_Assert(status == CL_SUCCESS);
+
+        cl_context_properties prop[] =
+        {
+            CL_CONTEXT_PLATFORM, (cl_context_properties)pl,
+            0
+        };
+
+        // !!! in the current implementation force the number of devices to 1 !!!
+        int nd = 1;
+
+        handle = clCreateContext(prop, nd, &d, 0, 0, &status);
+        CV_Assert(status == CL_SUCCESS);
+        bool ok = handle != 0 && status >= 0;
+        if( ok )
+        {
+            devices.resize(nd);
+            devices[0].set(d);
+        }
+        else
+        {
+            handle = NULL;
+        }
+    }
+
     Impl(int dtype0)
     {
         refcount = 1;
@@ -2022,6 +2287,21 @@ Context2::Context2(int dtype)
     create(dtype);
 }
 
+bool Context2::create()
+{
+    if( !haveOpenCL() )
+        return false;
+    if(p)
+        p->release();
+    p = new Impl();
+    if(!p->handle)
+    {
+        delete p;
+        p = 0;
+    }
+    return p != 0;
+}
+
 bool Context2::create(int dtype0)
 {
     if( !haveOpenCL() )
@@ -2081,23 +2361,16 @@ Context2& Context2::getDefault(bool initialize)
     static Context2 ctx;
     if(!ctx.p && haveOpenCL())
     {
+        if (!ctx.p)
+            ctx.p = new Impl();
         if (initialize)
         {
             // do not create new Context2 right away.
             // First, try to retrieve existing context of the same type.
             // In its turn, Platform::getContext() may call Context2::create()
             // if there is no such context.
-            ctx.create(Device::TYPE_ACCELERATOR);
-            if(!ctx.p)
-                ctx.create(Device::TYPE_DGPU);
-            if(!ctx.p)
-                ctx.create(Device::TYPE_IGPU);
-            if(!ctx.p)
-                ctx.create(Device::TYPE_CPU);
-        }
-        else
-        {
-            ctx.p = new Impl();
+            if (ctx.p->handle == NULL)
+                ctx.p->setDefault();
         }
     }
 
index c3b2f36..b537ec1 100644 (file)
@@ -72,5 +72,5 @@ int main(int argc, char ** argv)
 {
     ::perf::TestBase::setModulePerformanceStrategy(::perf::PERF_STRATEGY_SIMPLE);
 
-    CV_PERF_TEST_MAIN_INTERNALS(ocl, impls, dumpOpenCLDevice())
+    CV_PERF_TEST_MAIN_INTERNALS(ocl, impls, ::dumpOpenCLDevice())
 }
index 01626d5..366329c 100644 (file)
@@ -59,6 +59,8 @@
 #  endif
 #endif
 
+#define CV_BUILD_OCL_MODULE
+
 #include <iomanip>
 #include <stdexcept>
 #include <string>
index 0d51461..d284fcf 100644 (file)
@@ -76,5 +76,5 @@ void readLoopTimes(int argc, char ** argv)
     CV_Assert(LOOP_TIMES > 0);
 }
 
-CV_TEST_MAIN(".", dumpOpenCLDevice(),
+CV_TEST_MAIN(".", ::dumpOpenCLDevice(),
                   readLoopTimes(argc, argv))
index af467f5..f1887db 100644 (file)
@@ -50,6 +50,8 @@
 #ifndef __OPENCV_TEST_PRECOMP_HPP__
 #define __OPENCV_TEST_PRECOMP_HPP__
 
+#define CV_BUILD_OCL_MODULE
+
 #include <cmath>
 #include <cstdio>
 #include <iostream>
index 8e898af..72a7ae6 100644 (file)
@@ -4,6 +4,8 @@
 #include "opencv2/core/cvdef.h"
 #include <stdarg.h> // for va_list
 
+#include "cvconfig.h"
+
 #ifdef HAVE_WINRT
     #pragma warning(disable:4447) // Disable warning 'main' signature found without threading model
 #endif
@@ -548,6 +550,15 @@ CV_EXPORTS void printVersionInfo(bool useStdOut = true);
 #endif
 #endif
 
+#if defined(HAVE_OPENCL) && !defined(CV_BUILD_OCL_MODULE)
+namespace cvtest { namespace ocl {
+void dumpOpenCLDevice();
+}}
+#define TEST_DUMP_OCL_INFO cvtest::ocl::dumpOpenCLDevice();
+#else
+#define TEST_DUMP_OCL_INFO
+#endif
+
 #define CV_TEST_MAIN(resourcesubdir, ...) \
 int main(int argc, char **argv) \
 { \
@@ -555,6 +566,7 @@ int main(int argc, char **argv) \
     ::testing::InitGoogleTest(&argc, argv); \
     cvtest::printVersionInfo(); \
     __CV_TEST_EXEC_ARGS(__VA_ARGS__) \
+    TEST_DUMP_OCL_INFO \
     return RUN_ALL_TESTS(); \
 }
 
index d2ee771..201c5f4 100644 (file)
@@ -52,6 +52,146 @@ using namespace cv;
 
 int test_loop_times = 1; // TODO Read from command line / environment
 
+
+#define DUMP_PROPERTY_XML(propertyName, propertyValue) \
+    do { \
+        std::stringstream ssName, ssValue;\
+        ssName << propertyName;\
+        ssValue << (propertyValue); \
+        ::testing::Test::RecordProperty(ssName.str(), ssValue.str()); \
+    } while (false)
+
+#define DUMP_MESSAGE_STDOUT(msg) \
+    do { \
+        std::cout << msg << std::endl; \
+    } while (false)
+
+static std::string bytesToStringRepr(size_t value)
+{
+    size_t b = value % 1024;
+    value /= 1024;
+
+    size_t kb = value % 1024;
+    value /= 1024;
+
+    size_t mb = value % 1024;
+    value /= 1024;
+
+    size_t gb = value;
+
+    std::ostringstream stream;
+
+    if (gb > 0)
+        stream << gb << " GB ";
+    if (mb > 0)
+        stream << mb << " MB ";
+    if (kb > 0)
+        stream << kb << " kB ";
+    if (b > 0)
+        stream << b << " B";
+
+    return stream.str();
+}
+
+void dumpOpenCLDevice()
+{
+    using namespace cv::ocl;
+    try
+    {
+#if 0
+        Platforms platforms;
+        getOpenCLPlatforms(platforms);
+        if (platforms.size() > 0)
+        {
+            DUMP_MESSAGE_STDOUT("OpenCL Platforms: ");
+            for (size_t i = 0; i < platforms.size(); i++)
+            {
+                const Platform* platform = platforms.at(i);
+                DUMP_MESSAGE_STDOUT("    " << platform->name().c_str());
+                const Devices& devices = platform->devices();
+                for (size_t j = 0; j < devices.size(); j++)
+                {
+                    const Device& current_device = *devices.at(j);
+                    const char* deviceTypeStr = current_device.type() == Device::TYPE_CPU
+                                ? ("CPU") : (current_device.type() == Device::TYPE_GPU ? "GPU" : "unknown");
+                    DUMP_MESSAGE_STDOUT( "        " << deviceTypeStr << ": " << current_device.name().c_str() << " (" << current_device.version().c_str() << ")");
+                    DUMP_PROPERTY_XML(cv::format("cv_ocl_platform_%d_device_%d", (int)i, (int)j),
+                            "(Platform=" << current_device.getPlatform().name().c_str()
+                            << ")(Type=" << deviceTypeStr
+                            << ")(Name=" << current_device.name().c_str()
+                            << ")(Version=" << current_device.version().c_str() << ")");
+                }
+            }
+        }
+        else
+        {
+            DUMP_MESSAGE_STDOUT("OpenCL is not available");
+            DUMP_PROPERTY_XML("cv_ocl", "not available");
+            return;
+        }
+#endif
+        DUMP_MESSAGE_STDOUT("Current OpenCL device: ");
+
+        const Device& device = Device::getDefault();
+
+#if 0
+        DUMP_MESSAGE_STDOUT("    Platform = "<< device.getPlatform().name());
+        DUMP_PROPERTY_XML("cv_ocl_current_platformName", device.getPlatform().name());
+#endif
+
+        const char* deviceTypeStr = device.type() == Device::TYPE_CPU
+                        ? "CPU" : (device.type() == Device::TYPE_GPU ? "GPU" : "unknown");
+        DUMP_MESSAGE_STDOUT("    Type = "<< deviceTypeStr);
+        DUMP_PROPERTY_XML("cv_ocl_current_deviceType", deviceTypeStr);
+
+        DUMP_MESSAGE_STDOUT("    Name = "<< device.name());
+        DUMP_PROPERTY_XML("cv_ocl_current_deviceName", device.name());
+
+#if 0
+        DUMP_MESSAGE_STDOUT("    Version = " << device.version());
+        DUMP_PROPERTY_XML("cv_ocl_current_deviceVersion", device.version());
+#endif
+
+        DUMP_MESSAGE_STDOUT("    Compute units = "<< device.maxComputeUnits());
+        DUMP_PROPERTY_XML("cv_ocl_current_maxComputeUnits", device.maxComputeUnits());
+
+        DUMP_MESSAGE_STDOUT("    Max work group size = "<< device.maxWorkGroupSize());
+        DUMP_PROPERTY_XML("cv_ocl_current_maxWorkGroupSize", device.maxWorkGroupSize());
+
+        std::string localMemorySizeStr = bytesToStringRepr(device.localMemSize());
+        DUMP_MESSAGE_STDOUT("    Local memory size = " << localMemorySizeStr);
+        DUMP_PROPERTY_XML("cv_ocl_current_localMemSize", device.localMemSize());
+
+        std::string maxMemAllocSizeStr = bytesToStringRepr(device.maxMemAllocSize());
+        DUMP_MESSAGE_STDOUT("    Max memory allocation size = "<< maxMemAllocSizeStr);
+        DUMP_PROPERTY_XML("cv_ocl_current_maxMemAllocSize", device.maxMemAllocSize());
+
+#if 0
+        const char* doubleSupportStr = device.haveDoubleSupport() ? "Yes" : "No";
+        DUMP_MESSAGE_STDOUT("    Double support = "<< doubleSupportStr);
+        DUMP_PROPERTY_XML("cv_ocl_current_haveDoubleSupport", device.haveDoubleSupport());
+#else
+        const char* doubleSupportStr = device.doubleFPConfig() > 0 ? "Yes" : "No";
+        DUMP_MESSAGE_STDOUT("    Double support = "<< doubleSupportStr);
+        DUMP_PROPERTY_XML("cv_ocl_current_haveDoubleSupport", device.doubleFPConfig() > 0);
+
+#endif
+
+        const char* isUnifiedMemoryStr = device.hostUnifiedMemory() ? "Yes" : "No";
+        DUMP_MESSAGE_STDOUT("    Host unified memory = "<< isUnifiedMemoryStr);
+        DUMP_PROPERTY_XML("cv_ocl_current_hostUnifiedMemory", device.hostUnifiedMemory());
+    }
+    catch (...)
+    {
+        DUMP_MESSAGE_STDOUT("Exception. Can't dump OpenCL info");
+        DUMP_MESSAGE_STDOUT("OpenCL device not available");
+        DUMP_PROPERTY_XML("cv_ocl", "not available");
+    }
+}
+#undef DUMP_MESSAGE_STDOUT
+#undef DUMP_PROPERTY_XML
+
+
 Mat TestUtils::readImage(const String &fileName, int flags)
 {
     return cv::imread(cvtest::TS::ptr()->get_data_path() + fileName, flags);