Skip to content
Snippets Groups Projects
Select Git revision
  • 31cb1fc56f98178d976f21f6f6d0f08f196e553a
  • master default protected
  • CLFFT_NO_MAD_ENABLE
  • BRP_build_fixes
  • override_cl_compile_options
  • improve_Makefile
  • HSA
  • clmathfft
  • longer_dft_support
  • current_brp_apps
  • current_fgrp_apps
11 results

main.cpp

Blame
  • gpu_amd.cpp 16.34 KiB
    // This file is part of BOINC.
    // http://boinc.berkeley.edu
    // Copyright (C) 2012 University of California
    //
    // BOINC is free software; you can redistribute it and/or modify it
    // under the terms of the GNU Lesser General Public License
    // as published by the Free Software Foundation,
    // either version 3 of the License, or (at your option) any later version.
    //
    // BOINC is distributed in the hope that it will be useful,
    // but WITHOUT ANY WARRANTY; without even the implied warranty of
    // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
    // See the GNU Lesser General Public License for more details.
    //
    // You should have received a copy of the GNU Lesser General Public License
    // along with BOINC.  If not, see <http://www.gnu.org/licenses/>.
    
    // Detection of AMD/ATI GPUs
    //
    // Docs:
    // http://developer.amd.com/gpu/ATIStreamSDK/assets/ATI_Stream_SDK_CAL_Programming_Guide_v2.0%5B1%5D.pdf
    // ?? why don't they have HTML docs??
    
    #ifdef _WIN32
    #include "boinc_win.h"
    #ifdef _MSC_VER
    #define snprintf _snprintf
    #endif
    #else
    #ifdef __APPLE__
    // Suppress obsolete warning when building for OS 10.3.9
    #define DLOPEN_NO_WARN
    #include <mach-o/dyld.h>
    #endif
    #include "config.h"
    #include <dlfcn.h>
    #endif
    
    #include <vector>
    #include <string>
    
    using std::vector;
    using std::string;
    
    #include "coproc.h"
    #include "str_replace.h"
    #include "util.h"
    
    #include "client_msgs.h"
    #include "gpu_detect.h"
    
    static void get_available_ati_ram(COPROC_ATI &cc, vector<string>& warnings);
    
    // criteria:
    //
    // - double precision support
    // - local RAM
    // - speed
    //
    int ati_compare(COPROC_ATI& c1, COPROC_ATI& c2, bool loose) {
        if (c1.attribs.doublePrecision && !c2.attribs.doublePrecision) return 1;
        if (!c1.attribs.doublePrecision && c2.attribs.doublePrecision) return -1;
        if (loose) {
            if (c1.attribs.localRAM> 1.4*c2.attribs.localRAM) return 1;
            if (c1.attribs.localRAM< .7* c2.attribs.localRAM) return -1;
            return 0;
        }
        if (c1.attribs.localRAM > c2.attribs.localRAM) return 1;
        if (c1.attribs.localRAM < c2.attribs.localRAM) return -1;
        double s1 = c1.peak_flops;
        double s2 = c2.peak_flops;
        if (s1 > s2) return 1;
        if (s1 < s2) return -1;
        return 0;
    }
    
    #ifdef _WIN32
    typedef int (__stdcall *ATI_ATTRIBS) (CALdeviceattribs *attribs, CALuint ordinal);
    typedef int (__stdcall *ATI_CLOSE)(void);
    typedef int (__stdcall *ATI_GDC)(CALuint *numDevices);
    typedef int (__stdcall *ATI_GDI)(void);
    typedef int (__stdcall *ATI_INFO) (CALdeviceinfo *info, CALuint ordinal);
    typedef int (__stdcall *ATI_VER) (CALuint *cal_major, CALuint *cal_minor, CALuint *cal_imp);
    typedef int (__stdcall *ATI_STATUS) (CALdevicestatus*, CALdevice);
    typedef int (__stdcall *ATI_DEVICEOPEN) (CALdevice*, CALuint);
    typedef int (__stdcall *ATI_DEVICECLOSE) (CALdevice);
    
    ATI_ATTRIBS __calDeviceGetAttribs = NULL;
    ATI_CLOSE   __calShutdown = NULL;
    ATI_GDC     __calDeviceGetCount = NULL;
    ATI_GDI     __calInit = NULL;
    ATI_INFO    __calDeviceGetInfo = NULL;
    ATI_VER     __calGetVersion = NULL;
    ATI_STATUS  __calDeviceGetStatus = NULL;
    ATI_DEVICEOPEN  __calDeviceOpen = NULL;
    ATI_DEVICECLOSE  __calDeviceClose = NULL;
    
    #else
    
    int (*__calInit)();
    int (*__calGetVersion)(CALuint*, CALuint*, CALuint*);
    int (*__calDeviceGetCount)(CALuint*);
    int (*__calDeviceGetAttribs)(CALdeviceattribs*, CALuint);
    int (*__calShutdown)();
    int (*__calDeviceGetInfo)(CALdeviceinfo*, CALuint);
    int (*__calDeviceGetStatus)(CALdevicestatus*, CALdevice);
    int (*__calDeviceOpen)(CALdevice*, CALuint);
    int (*__calDeviceClose)(CALdevice);
    
    #endif
    
    void COPROC_ATI::get(
        vector<string>& warnings
    ) {
        CALuint numDevices, cal_major, cal_minor, cal_imp;
        char buf[256];
        int retval;
        COPROC_ATI cc, cc2;
        string s, gpu_name;
    
        attribs.struct_size = sizeof(CALdeviceattribs);
        numDevices =0;
    
    #ifdef _WIN32
    
    #if defined _M_X64
        const char* atilib_name = "aticalrt64.dll";
        const char* amdlib_name = "amdcalrt64.dll";
    #else
        const char* atilib_name = "aticalrt.dll";
        const char* amdlib_name = "amdcalrt.dll";
    #endif
    
        HINSTANCE callib = LoadLibrary(atilib_name);
        if (callib) {
            atirt_detected = true;
        } else {
            callib = LoadLibrary(amdlib_name);
            if (callib) {
                amdrt_detected = true;
            }
        }
    
        if (!callib) {
            warnings.push_back("No ATI library found.");
            return;
        }
    
        __calInit = (ATI_GDI)GetProcAddress(callib, "calInit" );
        __calGetVersion = (ATI_VER)GetProcAddress(callib, "calGetVersion" );
        __calDeviceGetCount = (ATI_GDC)GetProcAddress(callib, "calDeviceGetCount" );
        __calDeviceGetAttribs =(ATI_ATTRIBS)GetProcAddress(callib, "calDeviceGetAttribs" );
        __calShutdown = (ATI_CLOSE)GetProcAddress(callib, "calShutdown" );
        __calDeviceGetInfo = (ATI_INFO)GetProcAddress(callib, "calDeviceGetInfo" );
        __calDeviceGetStatus = (ATI_STATUS)GetProcAddress(callib, "calDeviceGetStatus" );
        __calDeviceOpen = (ATI_DEVICEOPEN)GetProcAddress(callib, "calDeviceOpen" );
        __calDeviceClose = (ATI_DEVICECLOSE)GetProcAddress(callib, "calDeviceClose" );
    
    #else
    
        void* callib = dlopen("libaticalrt.so", RTLD_NOW);
        if (!callib) {
            snprintf(buf, sizeof(buf), "ATI: %s", dlerror());
            warnings.push_back(buf);
            return;
        }
    
        atirt_detected = true;
    
        __calInit = (int(*)()) dlsym(callib, "calInit");
        __calGetVersion = (int(*)(CALuint*, CALuint*, CALuint*)) dlsym(callib, "calGetVersion");
        __calDeviceGetCount = (int(*)(CALuint*)) dlsym(callib, "calDeviceGetCount");
        __calDeviceGetAttribs = (int(*)(CALdeviceattribs*, CALuint)) dlsym(callib, "calDeviceGetAttribs");
        __calShutdown = (int(*)()) dlsym(callib, "calShutdown");
        __calDeviceGetInfo = (int(*)(CALdeviceinfo*, CALuint)) dlsym(callib, "calDeviceGetInfo");
        __calDeviceGetStatus = (int(*)(CALdevicestatus*, CALdevice)) dlsym(callib, "calDeviceGetStatus");
        __calDeviceOpen = (int(*)(CALdevice*, CALuint)) dlsym(callib, "calDeviceOpen");
        __calDeviceClose = (int(*)(CALdevice)) dlsym(callib, "calDeviceClose");
    
    #endif
    
        if (!__calInit) {
            warnings.push_back("calInit() missing from CAL library");
            goto leave;
        }
        if (!__calGetVersion) {
            warnings.push_back("calGetVersion() missing from CAL library");
            goto leave;
        }
        if (!__calDeviceGetCount) {
            warnings.push_back("calDeviceGetCount() missing from CAL library");
            goto leave;
        }
        if (!__calDeviceGetAttribs) {
            warnings.push_back("calDeviceGetAttribs() missing from CAL library");
            goto leave;
        }
        if (!__calDeviceGetInfo) {
            warnings.push_back("calDeviceGetInfo() missing from CAL library");
            goto leave;
        }
    
        retval = (*__calInit)();
        if (retval != CAL_RESULT_OK) {
            snprintf(buf, sizeof(buf), "calInit() returned %d", retval);
            warnings.push_back(buf);
            goto leave;
        }
        retval = (*__calDeviceGetCount)(&numDevices);
        if (retval != CAL_RESULT_OK) {
            snprintf(buf, sizeof(buf), "calDeviceGetCount() returned %d", retval);
            warnings.push_back(buf);
            goto leave;
        }
        retval = (*__calGetVersion)(&cal_major, &cal_minor, &cal_imp);
        if (retval != CAL_RESULT_OK) {
            snprintf(buf, sizeof(buf), "calGetVersion() returned %d", retval);
            warnings.push_back(buf);
            goto leave;
        }
    
        if (!numDevices) {
            warnings.push_back("No usable CAL devices found");
            goto leave;
        }
    
        for (CALuint i=0; i<numDevices; i++) {
            retval = (*__calDeviceGetInfo)(&info, i);
            if (retval != CAL_RESULT_OK) {
                snprintf(buf, sizeof(buf), "calDeviceGetInfo() returned %d", retval);
                warnings.push_back(buf);
                goto leave;
            }
            retval = (*__calDeviceGetAttribs)(&attribs, i);
            if (retval != CAL_RESULT_OK) {
                snprintf(buf, sizeof(buf), "calDeviceGetAttribs() returned %d", retval);
                warnings.push_back(buf);
                goto leave;
            }
            switch ((int)attribs.target) {
            case CAL_TARGET_600:
                gpu_name="ATI Radeon HD 2900 (RV600)";
                break;
            case CAL_TARGET_610:
                gpu_name="ATI Radeon HD 2300/2400/3200/4200 (RV610)";
                attribs.numberOfSIMD=1;        // set correct values (reported wrong by driver)
                attribs.wavefrontSize=32;
                break;
            case CAL_TARGET_630:
                gpu_name="ATI Radeon HD 2600/3650 (RV630/RV635)";
                // set correct values (reported wrong by driver)
                attribs.numberOfSIMD=3;
                attribs.wavefrontSize=32;
                break;
            case CAL_TARGET_670:
                gpu_name="ATI Radeon HD 3800 (RV670)";
                break;
            case CAL_TARGET_710:
                gpu_name="ATI Radeon HD 4350/4550 (R710)";
                break;
            case CAL_TARGET_730:
                gpu_name="ATI Radeon HD 4600 series (R730)";
                break;
            case CAL_TARGET_7XX:
                gpu_name="ATI Radeon (RV700 class)";
                break;
            case CAL_TARGET_770:
                gpu_name="ATI Radeon HD 4700/4800 (RV740/RV770)";
                break;
            case 8:
                gpu_name="ATI Radeon HD 5800/5900 series (Cypress/Hemlock)";
                break;
            case 9:
                gpu_name="ATI Radeon HD 5700/6750/6770 series (Juniper)";
                break;
            case 10:
                gpu_name="ATI Radeon HD 5500/5600 series (Redwood)";
                break;
            case 11:
                gpu_name="ATI Radeon HD 5400/R5 210 series (Cedar)";
                break;
            case 12:
                gpu_name="AMD Radeon HD 6370D/6380G/6410D/6480G (Sumo)";
                break;
            case 13:
                gpu_name="AMD Radeon HD 6520G/6530D/6550D/6620G (SuperSumo)";
                break;
            case 14:
                gpu_name="AMD Radeon HD 6200/6300/7200/7300 series (Wrestler)";
                break;
            case 15:
                gpu_name="AMD Radeon HD 6900 series (Cayman)";
                break;
            case 16:
                gpu_name="AMD Radeon HD (Kauai)";
                break;
            case 17:
                gpu_name="AMD Radeon HD 6790/6850/6870 series (Barts)";
                break;
            case 18:
                gpu_name="AMD Radeon HD 6570/6670/7570/7670 series (Turks)";
                break;
            case 19:
                gpu_name="AMD Radeon HD 6350/6450/7450/7470/R5 230 series (Caicos)";
                break;
            case 20:
                gpu_name="AMD Radeon HD 7870/7950/7970/R9 280/R9 280X series (Tahiti)";
                break;
            case 21:
                gpu_name="AMD Radeon HD 7850/7870 series (Pitcairn)";
                break;
            case 22:
                gpu_name="AMD Radeon HD 7700/R7 250X/R9 255 series (Capeverde)";
                break;
            case 23:
                gpu_name="AMD Radeon HD 7500/7600/8500/8600 series (Devastator)";
                break;
            case 24:
                gpu_name="AMD Radeon HD 7400/7500/8300/8400 series (Scrapper)";
                break;
            case 25:
                gpu_name="AMD Radeon HD 8600/8790M/R5 330/R5 340/R7 240/R7 250/R7 340/R7 350 (Oland)";
                break;
            case 26:
                gpu_name="AMD Radeon HD 7790/R7 260/R7 260X/R9 360 (Bonaire)";
                break;
            case 27:
                gpu_name="AMD Radeon HD (Spectre)"; // Kaveri
                break;
            case 28:
                gpu_name="AMD Radeon HD (Spooky)";  // Kaveri
                break;
            case 29:
                gpu_name="AMD Radeon HD 8200/8300/8400 series (Kalindi)"; // Kabini
                break;
            case 30:
                gpu_name="AMD Radeon HD 8600M (Hainan)";
                break;
            case 31:
                gpu_name="AMD Radeon R7 265/R9 270/R9 270X/R9 370 (Curacao)";
                break;
            case 32:
                gpu_name="AMD Radeon R9 290 (Hawaii)";
                break;
            case 33:
                gpu_name="AMD Radeon R2/R3 (Skunk)"; // Mullins/new FT3 APU
                break;
            case 34:
                gpu_name="AMD Radeon R9 285/R9 380 (Tonga)";
                break;
            case 35:
                gpu_name="AMD Radeon R9 295X2 (Vesuvius)";
                break;
            case 36:
                gpu_name="AMD Radeon R7 360 (Tobago)";
                break;
            case 37:
                gpu_name="AMD Radeon R7 370/R9 370X (Trinidad)";
                break;
            case 38:
                gpu_name="AMD Radeon R9 390/R9 390X (Grenada)";
                break;
            case 39:
                gpu_name="AMD Radeon R9 Fury/R9 Nano/R9 Fury X/R9 Fury X2 (Fiji)";
                break;
            default:
                gpu_name="AMD Radeon HD (unknown)";
                break;
            }
            have_cal = true;
            cc.have_cal = true;
            cc.attribs = attribs;
            cc.info = info;
            safe_strcpy(cc.name, gpu_name.c_str());
            snprintf(cc.version, sizeof(cc.version), "%d.%d.%d", cal_major, cal_minor, cal_imp);
            cc.amdrt_detected = amdrt_detected;
            cc.atirt_detected = atirt_detected;
            cc.device_num = i;
            cc.set_peak_flops();
            get_available_ati_ram(cc, warnings);
            ati_gpus.push_back(cc);
        }
    
        // shut down CAL, otherwise Lenovo won't be able to switch to low-power GPU
        //
        retval = (*__calShutdown)();
    
        if (!ati_gpus.size()) {
            warnings.push_back("No ATI GPUs found");
        }
    leave:
    #ifdef _WIN32
        if (callib) FreeLibrary(callib);
    #else
        if (callib) dlclose(callib);
    #endif
    }
    
    void COPROC_ATI::correlate(
        bool use_all,
        vector<int>& ignore_devs
    ) {
        char buf[256];
    
        if (!ati_gpus.size()) return;
    
        // find the most capable non-ignored instance
        //
        bool first = true;
        unsigned int i;
        for (i=0; i<ati_gpus.size(); i++) {
            if (in_vector(ati_gpus[i].device_num, ignore_devs)) continue;
            if (first) {
                *this = ati_gpus[i];
                first = false;
            } else if (ati_compare(ati_gpus[i], *this, false) > 0) {
                *this = ati_gpus[i];
            }
        }
    
        // see which other instances are equivalent,
        // and set the "count" and "device_nums" fields
        //
        count = 0;
        for (i=0; i<ati_gpus.size(); i++) {
            ati_gpus[i].description(buf, sizeof(buf));
            if (in_vector(ati_gpus[i].device_num, ignore_devs)) {
                ati_gpus[i].is_used = COPROC_IGNORED;
            } else if (this->have_opencl && !ati_gpus[i].have_opencl) {
                ati_gpus[i].is_used = COPROC_UNUSED;
            } else if (this->have_cal && !ati_gpus[i].have_cal) {
                ati_gpus[i].is_used = COPROC_UNUSED;
            } else if (use_all || !ati_compare(ati_gpus[i], *this, true)) {
                device_nums[count] = ati_gpus[i].device_num;
                count++;
                ati_gpus[i].is_used = COPROC_USED;
            } else {
                ati_gpus[i].is_used = COPROC_UNUSED;
            }
        }
    }
    
    // get available RAM of ATI GPU
    //
    // CAUTION: as currently written, this method should be
    // called only from COPROC_ATI::get().  If in the future
    // you wish to call it from additional places:
    // * It must be called from a separate child process on
    //   dual-GPU laptops (e.g., Macbook Pros) with the results
    //   communicated to the main client process via IPC or a
    //   temp file.  See the comments about dual-GPU laptops 
    //   in gpu_detect.cpp and main.cpp for more details.
    // * The CAL library must be loaded and calInit() called 
    //   first.
    // * See client/coproc_detect.cpp and cpu_sched.cpp in
    //   BOINC 6.12.36 for an earlier attempt to call this
    //   from the scheduler.  Note that it was abandoned
    //   due to repeated calls crashing the driver.
    //
    static void get_available_ati_ram(COPROC_ATI &cc, vector<string>& warnings) {
        CALdevicestatus st;
        CALdevice dev;
        char buf[256];
        int retval;
    
        cc.available_ram = cc.attribs.localRAM*MEGA;
    
        st.struct_size = sizeof(CALdevicestatus);
    
        if (!__calDeviceOpen) {
            warnings.push_back("calDeviceOpen() missing from CAL library");
            return;
        }
        if (!__calDeviceGetStatus) {
            warnings.push_back("calDeviceGetStatus() missing from CAL library");
            return;
        }
        if (!__calDeviceClose) {
            warnings.push_back("calDeviceClose() missing from CAL library");
            return;
        }
    
        retval = (*__calDeviceOpen)(&dev, cc.device_num);
        if (retval) {
            snprintf(buf, sizeof(buf),
                "[coproc] calDeviceOpen(%d) returned %d", cc.device_num, retval
            );
            warnings.push_back(buf);
            return;
        }
        retval = (*__calDeviceGetStatus)(&st, dev);
        if (retval) {
            snprintf(buf, sizeof(buf),
                "[coproc] calDeviceGetStatus(%d) returned %d",
                cc.device_num, retval
            );
            warnings.push_back(buf);
            (*__calDeviceClose)(dev);
            return;
        }
        cc.available_ram = st.availLocalRAM*MEGA;
        (*__calDeviceClose)(dev);
    }