gpu_compute.c

← Back to explorer
src/ gpu_compute.c
Raw
C 245 lines · UTF-8
123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245
// Created by AG on 17-08-2026

#include "gpu_compute.h"

#ifdef SYSTEM_HAS_OPENCL

#include <stdio.h>
#include <stdlib.h>
#include <string.h>

static const char *kernel_names[OPENCL_KERNEL_COUNT] = {
  "kernel_math_add",
  "kernel_math_subtract",
  "kernel_math_scale",
  "kernel_math_fill",
  "kernel_math_clear",
  "kernel_math_copy",
  "kernel_math_accumulate",
  "kernel_add_bias",
  "kernel_add_bias_gradient",
  "kernel_matrix_multiplication_nn",
  "kernel_matrix_multiplication_nt",
  "kernel_matrix_multiplication_tn",
  "kernel_matrix_multiplication_tt",
  "kernel_relu_forward",
  "kernel_relu_backward",
  "kernel_softmax_forward",
  "kernel_softmax_backward",
  "kernel_cross_entropy_forward",
  "kernel_cross_entropy_predicted",
  "kernel_cross_entropy_expected",
  "kernel_param_update"
};

static char *read_entire_file(const char *filepath) {
  FILE *file = fopen(filepath, "rb");
  if (!file) {
    fprintf(stderr, "Teddy: failed to open kernel file: %s\n", filepath);
    return NULL;
  }

  fseek(file, 0, SEEK_END);
  long file_length = ftell(file);
  fseek(file, 0, SEEK_SET);

  char *source_buffer = (char *)malloc(file_length + 1);
  fread(source_buffer, 1, file_length, file);
  source_buffer[file_length] = '\0';
  fclose(file);

  return source_buffer;
}

static cl_device_id select_gpu_device(cl_platform_id *out_platform) {
  cl_uint platform_count = 0;
  clGetPlatformIDs(0, NULL, &platform_count);

  if (platform_count == 0) {
    fprintf(stderr, "Teddy: no OpenCL platforms found\n");
    return NULL;
  }

  cl_platform_id *platforms = (cl_platform_id *)malloc(sizeof(cl_platform_id) * platform_count);
  clGetPlatformIDs(platform_count, platforms, NULL);

  for (cl_uint p = 0; p < platform_count; p++) {
    cl_uint device_count = 0;
    clGetDeviceIDs(platforms[p], CL_DEVICE_TYPE_GPU, 0, NULL, &device_count);

    if (device_count > 0) {
      cl_device_id device;
      clGetDeviceIDs(platforms[p], CL_DEVICE_TYPE_GPU, 1, &device, NULL);

      char device_name[256];
      clGetDeviceInfo(device, CL_DEVICE_NAME, sizeof(device_name), device_name, NULL);

      char platform_name[256];
      clGetPlatformInfo(platforms[p], CL_PLATFORM_NAME, sizeof(platform_name), platform_name, NULL);

      printf("Teddy: selected GPU: %s (%s)\n", device_name, platform_name);

      *out_platform = platforms[p];
      free(platforms);
      return device;
    }
  }

  free(platforms);
  fprintf(stderr, "Teddy: no GPU devices found on any platform\n");
  return NULL;
}

OpenCLDevice *opencl_device_create(const char *kernel_source_path) {
  OpenCLDevice *device = (OpenCLDevice *)calloc(1, sizeof(OpenCLDevice));

  device->device = select_gpu_device(&device->platform);
  if (!device->device) {
    free(device);
    return NULL;
  }

  cl_int status;
  device->context = clCreateContext(NULL, 1, &device->device, NULL, NULL, &status);
  if (status != CL_SUCCESS) {
    fprintf(stderr, "Teddy: failed to create OpenCL context: %d\n", status);
    free(device);
    return NULL;
  }

#ifdef CL_VERSION_2_0
  device->queue = clCreateCommandQueueWithProperties(device->context, device->device, NULL, &status);
#else
  device->queue = clCreateCommandQueue(device->context, device->device, 0, &status);
#endif

  if (status != CL_SUCCESS) {
    fprintf(stderr, "Teddy: failed to create command queue: %d\n", status);
    clReleaseContext(device->context);
    free(device);
    return NULL;
  }

  char *kernel_source = read_entire_file(kernel_source_path);
  if (!kernel_source) {
    clReleaseCommandQueue(device->queue);
    clReleaseContext(device->context);
    free(device);
    return NULL;
  }

  const char *source_strings[] = { kernel_source };
  size_t source_lengths[] = { strlen(kernel_source) };
  device->program = clCreateProgramWithSource(device->context, 1, source_strings, source_lengths, &status);
  free(kernel_source);

  if (status != CL_SUCCESS) {
    fprintf(stderr, "Teddy: failed to create program: %d\n", status);
    clReleaseCommandQueue(device->queue);
    clReleaseContext(device->context);
    free(device);
    return NULL;
  }

  status = clBuildProgram(device->program, 1, &device->device, "-cl-fast-relaxed-math", NULL, NULL);
  if (status != CL_SUCCESS) {
    size_t log_size;
    clGetProgramBuildInfo(device->program, device->device, CL_PROGRAM_BUILD_LOG, 0, NULL, &log_size);
    char *build_log = (char *)malloc(log_size + 1);
    clGetProgramBuildInfo(device->program, device->device, CL_PROGRAM_BUILD_LOG, log_size, build_log, NULL);
    build_log[log_size] = '\0';
    fprintf(stderr, "Teddy: kernel build failed:\n%s\n", build_log);
    free(build_log);
    clReleaseProgram(device->program);
    clReleaseCommandQueue(device->queue);
    clReleaseContext(device->context);
    free(device);
    return NULL;
  }

  for (int i = 0; i < OPENCL_KERNEL_COUNT; i++) {
    device->kernels[i] = clCreateKernel(device->program, kernel_names[i], &status);
    if (status != CL_SUCCESS) {
      fprintf(stderr, "Teddy: failed to create kernel '%s': %d\n", kernel_names[i], status);
      for (int j = 0; j < i; j++) {
        clReleaseKernel(device->kernels[j]);
      }
      clReleaseProgram(device->program);
      clReleaseCommandQueue(device->queue);
      clReleaseContext(device->context);
      free(device);
      return NULL;
    }
  }

  device->initialized = 1;
  printf("Teddy: GPU backend initialized with %d kernels\n", OPENCL_KERNEL_COUNT);
  return device;
}

void opencl_device_destroy(OpenCLDevice *device) {
  if (!device) {
    return;
  }

  for (int i = 0; i < OPENCL_KERNEL_COUNT; i++) {
    if (device->kernels[i]) {
      clReleaseKernel(device->kernels[i]);
    }
  }

  if (device->program) {
    clReleaseProgram(device->program);
  }

  if (device->queue) {
    clReleaseCommandQueue(device->queue);
  }

  if (device->context) {
    clReleaseContext(device->context);
  }

  free(device);
}

cl_mem opencl_device_alloc(OpenCLDevice *device, size_t byte_count) {
  cl_int status;
  cl_mem buffer = clCreateBuffer(device->context, CL_MEM_READ_WRITE, byte_count, NULL, &status);
  if (status != CL_SUCCESS) {
    fprintf(stderr, "Teddy: GPU allocation failed for %zu bytes: %d\n", byte_count, status);
    return NULL;
  }
  return buffer;
}

void opencl_device_free(cl_mem buffer) {
  if (buffer) {
    clReleaseMemObject(buffer);
  }
}

void opencl_device_upload(OpenCLDevice *device, cl_mem buffer, const void *host_ptr, size_t byte_count) {
  clEnqueueWriteBuffer(device->queue, buffer, CL_TRUE, 0, byte_count, host_ptr, 0, NULL, NULL);
}

void opencl_device_download(OpenCLDevice *device, cl_mem buffer, void *host_ptr, size_t byte_count) {
  clEnqueueReadBuffer(device->queue, buffer, CL_TRUE, 0, byte_count, host_ptr, 0, NULL, NULL);
}

void opencl_device_finish(OpenCLDevice *device) {
  clFinish(device->queue);
}

void opencl_device_dispatch_1d(OpenCLDevice *device, OpenCLKernelId kernel_id, size_t global_size) {
  size_t local_size = 256;
  size_t aligned_global = ((global_size + local_size - 1) / local_size) * local_size;
  clEnqueueNDRangeKernel(device->queue, device->kernels[kernel_id], 1, NULL,
                         &aligned_global, &local_size, 0, NULL, NULL);
}

cl_kernel opencl_device_get_kernel(OpenCLDevice *device, OpenCLKernelId kernel_id) {
  return device->kernels[kernel_id];
}

#endif