summaryrefslogtreecommitdiff
path: root/src/opencl.lisp
blob: ca959285ab1f43c79495ead0e678fb9f453b2cd1 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
(in-package #:ocl)

(defmacro define-get-info ())

(define-condition opencl-error ()
  ((code :initarg :code
          :reader opencl-error-code))
  (:report (lambda (condition stream)
             (format stream "OPENCL-ERROR: ~A~%" (opencl-error-code condition)))))

(defmacro check-error (error-code)
  (alexandria:once-only (error-code)
    `(unless (eq ,error-code :success)
       (error 'opencl-error :code ,error-code))))

;; TODO: Finish me
(defun array-foreign-type (array)
  "Returns the most appropiate foreign type to store ARRAY"
  (case (array-element-type array)
    (single-float :float)
    (double-float :double)
    (t nil)))

(defun array-foreign-size (array)
  "Returns the size in bytes required to store a foreign version of ARRAY"
  (reduce #'* (array-dimensions array)
          :initial-value (cffi:foreign-type-size (array-foreign-type array))))

(defmacro check-error-arg (form)
  (let ((value (gensym "VALUE"))
        (error-code (gensym "ERROR-CODE"))
        (c-error-code (gensym "C-ERROR-CODE")))
    `(cffi:with-foreign-object (,c-error-code 'cl-error-code)
       (let* ((,value (,@form ,c-error-code))
              (,error-code (cffi:mem-ref ,c-error-code 'cl-error-code)))
         (if (eq ,error-code :success)
             ,value
             (error 'opencl-error :code ,error-code))))))

(defmacro with-foreign-arrays (bindings &body body)
  (if (null bindings)
      `(progn ,@body)
      `(cffi:with-foreign-array ,(first bindings)
         (with-foreign-arrays ,(rest bindings)
           ,@body))))

(defmacro with-foreign-sequence ((var lisp-sequence type &optional length) &body body)
  "Like CFFI:WITH-FOREIGN-ARRAY but works also for lists"
  (unless length (setf length (gensym "LENGTH")))
  (alexandria:once-only (lisp-sequence type)
    `(let ((,length (length ,lisp-sequence)))
       (cffi:with-foreign-object (,var ,type ,length)
         (dotimes (i ,length)
           (setf (cffi:mem-aref ,var ,type i) (elt ,lisp-sequence i)))
         ,@body))))

(defmacro with-foreign-sequences (bindings &body body)
  (if bindings
      `(with-foreign-sequence ,(first bindings)
         (with-foreign-sequences ,(rest bindings)
           ,@body))
      `(progn ,@body)))

(defun vector-type-p (type)
  (and (consp type) (eq (car type) :pointer)))

(defun string-type-p (type)
  (eq type :string))

(defmacro wrap-get-info-function (name low-level-name type-alist)
  ""
  ;; FIXME? This is a top level form, no call to GENSYM is needed, right?
  `(defun ,name (object param-name)
     (let ((type (assoc-value ,type-alist param-name))
           (return-size nil)
           (error-code nil)
           (return-value nil))
       (cffi:with-foreign-object (c-return-size :size)
         (setf error-code (,low-level-name object param-name 0 (cffi:null-pointer) c-return-size))
         (check-error error-code)
         (setf return-size (cffi:mem-ref c-return-size :size)))
       (cond ((vector-type-p type)
              (let ((count (/ return-size (cffi:foreign-type-size (second type)))))
                (cffi:with-foreign-object (c-value type count)
                  (setf error-code (,low-level-name object param-name return-size c-value (cffi:null-pointer)))
                  (setf return-value (loop :for i :below count :collect (cffi:mem-aref c-value type i))))))
             ((string-type-p type)
              (setf return-value
                    (cffi:with-foreign-pointer-as-string (c-string return-size)
                      (setf error-code (,low-level-name object param-name return-size c-string (cffi:null-pointer))))))
             (t
              (cffi:with-foreign-object (c-value type)
                (setf error-code (,low-level-name object param-name (cffi:foreign-type-size type)
                                                  c-value (cffi:null-pointer)))
                (setf return-value (cffi:mem-ref c-value type)))))
       (check-error error-code)
       return-value)))

(defmacro get-cl-objects (name (&rest args) low-level-name type)
  (assert (every #'symbolp args) nil "ARGS must be a list of only symbols")
  `(defun ,name (,@args)
     (let (n)
       (cffi:with-foreign-object (cn 'cl-uint)
         (check-error (,low-level-name ,@args 0 (cffi:null-pointer) cn))
         (setf n (cffi:mem-ref cn 'cl-uint)))
       (cffi:with-foreign-object (c-objects ,type n)
         (check-error (,low-level-name ,@args n c-objects (cffi:null-pointer)))
         (loop :for i :below n
               :collect (cffi:mem-aref c-objects ,type i))))))