2
votes

I'm having trouble with my OpenCL kernel:

 #pragma OPENCL EXTENSION cl_khr_fp64 : enable

struct complex {
    double im;
    double re;
    double r;
    double phi;
};

struct complex createComplexFromPolar(double _r, double _phi){
    struct complex t;
    t.r = _r;
    t.phi = _phi;

    t.re = cos(t.phi);
    t.im = sin(t.phi);

    return t;
}

struct complex createComplexFromKarthes(double real, double imag){
    struct complex t;
    t.re = real;
    t.im = imag;

    t.phi = atan(imag / real);
    t.r = sqrt(pow(real, 2) + pow(imag, 2));

    return t;
}

struct complex recreateComplexFromKarthes(struct complex t){
    return t = createComplexFromKarthes(t.re, t.im);
}

struct complex recreateComplexFromPolar(struct complex t){
    return t = createComplexFromPolar(t.r, t.phi);
}

struct complex addComplex(const struct complex z, const struct complex c){
    struct complex t;
    t.re = c.re + z.re;
    t.im = c.im + z.re;
    return recreateComplexFromKarthes(t);
}

struct complex subComplex(const struct complex z, const struct complex c){
    struct complex t;
    t.re = z.re - c.re;
    t.im = z.im - c.im;
    return recreateComplexFromKarthes(t);
}

struct complex addComplexScalar(const struct complex z, const double n){
    struct complex t;
    t.re = z.re + n;
    return recreateComplexFromKarthes(t);
}

struct complex subComplexScalar(const struct complex z, const double n){
    struct complex t;
    t.re = z.re - n;
    return recreateComplexFromKarthes(t);
}

struct complex multComplexScalar(const struct complex z, const double n) {
    struct complex t;
    t.re = z.re * n;
    t.im = z.im * n;
    return recreateComplexFromKarthes(t);
}

struct complex multComplex(const struct complex z, const struct complex c) {
    struct complex t;
    t.re = z.re*c.re - z.im*c.re;
    t.im = z.re*c.im + z.im*c.re;
    return recreateComplexFromKarthes(t);
}

struct complex divComplex(const struct complex z, const struct complex c) {
    return createComplexFromPolar(z.r / c.r, z.phi - c.phi);
}

__kernel void newtonFraktal(__global const int* res, __global const double* param, __global int* result){
    const int x = get_global_id(0);
    const int y = get_global_id(1);

    const int xRes = res[0];
    const int yRes = res[1];

    struct complex z = createComplexFromKarthes(x - (xRes / 2), y - (yRes / 2));

    struct complex c = createComplexFromKarthes(param[0], param[1]);

    int i = 0;
    while (z.r < 500){
        if (i >= 10000)
            break;
        z = subComplex(z, divComplex(addComplex(addComplex(multComplex(multComplex(z,z),c),multComplex(z,c)),c),addComplex(multComplexScalar(multComplex(z,c),2),c)));
        //z-(c*z*z + c*z + c) / ((c*z) * 2 + c);

        i++;
    }
    result[x + res[0]*y] = i;
}

The aim of this kernel is to to create the image data for a newton fractal. The problem is a crash when param[0] and param[1] differ more than 3 (this causes clFinish(-36)) or when they get too big together (this causes clEnqueueReadBuffer(-36)).

When I run this code as C++ Code it is totally fine with every parameters - I know there's a big difference between the runtime environments..

I'm running the kernel on a nVidia GeForce GTX 770 and the host runs on a AMD FX-8350.

I hope you guys have a tip for me how I can solve this issue. I guess there's no step by step OpenCL Debugger for nVidia?

Thanks for your help in advance,
- fodinabor

EDIT: Well - it seems like my suggestions on when the kernel crashes are not really correct - they were something I thought I was observing yesterday.. but it seems like it's totally independent from the values as they sometimes work and sometimes they don't. For example param[0] = 3;and param[1] = 1; worked wonderful several times - but atm. I'm not able to run it.. so I guess it's something with my host.. I posted it below. Might the issue come from a too big global work size? My graphics card should be able to hold 1024*1024*64 and I'm using 640*480.. so normally it should work? I tried running it with half size -> 2 rounds and it always crashed in the second round - if this helps..

NewtonFraktalCLGeneration::NewtonFraktalCLGeneration(cl_double* param){
    FILE* f;
    if (fopen_s(&f, "newton.cl", "r") != 0){
        return;
    }
    char* buf = (char*)malloc(100 * sizeof(char));
    char* temp = buf;
    int recv_size = 0, total_recv = 0;
    int i = 1;
    while ((recv_size = fread_s(temp, sizeof(char) * 100, sizeof(char), 100, f)) > 0){
        total_recv += recv_size;
        buf = (char*)realloc(buf, total_recv + 100 * sizeof(char));
        temp = buf + total_recv;
    }
    buf[total_recv] = '\0';

    err = CL_SUCCESS;
    try {
        cl::vector<cl::Platform> platforms;
        cl::Platform::get(&platforms);

        cl_context_properties properties[] =
            { CL_CONTEXT_PLATFORM, (cl_context_properties)(platforms[0])(), 0 };
        cl::Context context(CL_DEVICE_TYPE_GPU, properties);

        cl::vector<cl::Device> devices = context.getInfo<CL_CONTEXT_DEVICES>();

        cl::Program::Sources source(1, std::make_pair(buf,strlen(buf)));
        cl::Program program_ = cl::Program(context, source);
        program_.build(devices);

        cl::Kernel kernel(program_, "newtonFraktal", &err);

        int *res = new int[2];
        res[0] = Services()->getCore()->getXRes(), res[1] = Services()->getCore()->getYRes();
        cl::Buffer resBuf(context, CL_MEM_READ_ONLY, 2 * sizeof(int));
        cl::Buffer paramBuf(context, CL_MEM_READ_ONLY, 2 * sizeof(cl_double));

        result = (cl_int*)calloc(res[0] * res[1], sizeof(cl_int) + 1);
        cl::Buffer outBuf(context, CL_MEM_WRITE_ONLY | CL_MEM_USE_HOST_PTR, res[0] * res[1] * sizeof(cl_int) + 1, result);

        cl::CommandQueue queue(context, devices[0], 0, &err);
        cl::Event event;

        queue.enqueueWriteBuffer(resBuf, CL_TRUE, 0, 2 * sizeof(int), res);
        queue.enqueueWriteBuffer(paramBuf, CL_TRUE, 0, 2 * sizeof(double), param);

        kernel.setArg(0, resBuf);
        kernel.setArg(1, paramBuf);
        kernel.setArg(2, outBuf);

        queue.enqueueNDRangeKernel(
            kernel,
            cl::NullRange,
            cl::NDRange(res[0], res[1]),
            cl::NullRange,
            NULL,
            &event);

        queue.finish();

        queue.enqueueReadBuffer(outBuf, CL_TRUE, 0, res[0] * res[1] * sizeof(cl_int) + 1, result);
    }
    catch (cl::Error& err) {
        std::cerr
             << "ERROR: "
             << err.what()
             << "("
             << err.err()
             << ")"
             << std::endl;
        this->err = err.err();
    }
}
1

1 Answers

0
votes

Okay - so now I really got the issue (I hope :D):
my kernel was executed on the Graphics Card with a monitor connected to it .. and took a bit more execution time than 5 sec. -> the TDR (something with Windows Driver security) reset the Driver -> the Kernel execution was killed.. so for now I set the TDRDelay in the registry to a higher value and maybe will use another Graphics Card in the future :D

It's a bit annoying to me .. but finally I solved it.. so thanks to all these people who helped me through their answers on other questions.

If someone should have this issue again here the description how you can increase the TDRDelay (shouldn't be used for production systems ;-)):
Open the registry editor (e.g. by Win Key + R and writing regedit in the window and execute) surf to HKEY_LOCAL_MACHINE\SYSTEM\CurrentControlSet\Control\GraphicsDrivers and create a new entry called TdrDelay with a value you may choose (it's indirectly the max execution time of your kernel) in seconds (I've set it to 10sec). Now restart your system and enjoy your unblocked OpenCL / CUDA Kernels :D