cbaggers / cepl

Code Evaluate Play Loop
BSD 2-Clause "Simplified" License
861 stars 52 forks source link

GPU array bugs #339

Open stacksmith opened 5 years ago

stacksmith commented 5 years ago

1: immediate commands containing MAKE-GPU-ARRAY crash on compile inside an Emacs file (C-C), but work fine in REPL.

(defparameter garray (make-gpu-array '(0.0 1.0 2.0)
                    :element-type :float))

Heap exhausted (no more space for allocation).
917504000 bytes available, 15089770976 requested.

It seems there is no way to compile immediate-mode GPU allocations.

2: GPU arrays lose sync with underlying C arrays

(defun test1 ()
  (setf garray (make-gpu-array '(0.0 1.0 2.0)
                   :element-type :float)))
(test1)  ;;yea, this will crash too.  Use REPL

>garray

#<GPU-ARRAY :element-type :FLOAT :dimensions (3) :backed-by :BUFFER>

> (pull-g garray)
(0.0 1.0 2.0)

>(with-gpu-array-as-c-array (qqq garray) (setf (aref-c qqq 0) 999))
999

>(pull-g garray)
(0.0 1.0 2.0)

I though pull-g only goes one level and pulls GPU arrays into C arrays? Here we wind up with a Lisp list...

cbaggers commented 5 years ago

Hey @stacksmith, sorry for the slow replies I've been putting all time into work recently. The days need extra hours.

Luckily these two are easy to explain. In reverse order

  1. Gpu-arrays are not designed to stay in sync. they are just meant to be an array on the gpu. c-arrays are the local analog and are meant to have a layout that makes it easy to transfer data (relatively) efficiently between them.

  2. Is a symptom of how slime/slync etc work. When you compile top level forms in slime it runs them on a different thread than the thread the repl is running on. This then causes an error usually we start cepl from the repl and that means that is the thread the GL context is bound to. I've looked into using multiple contexts on the threads in question but whilst it would help for some GL objects, not all of them are available on all contexts (VAOs are a good example).

The way I usually handle it on small sessions is to (defvar something nil) and then have a 'reset' method that is called on start and then I can call on demand whilst I'm experiementing. For larger projects I'm likely to have some more interesting lifetime requirements anyway and so I'm less likely to just have misc gpu-arrays in top level vars.

It's a bit of a pain in the ass though. You can start swank using communication-mode nil which means no multi-threading at all. With everything on the same thread i think what you are trying will work but that has it's own disadvantages as you could imagine.

stacksmith commented 5 years ago

Thanks. I am still confused about 2. So I cannot use 'with-gpu-array-as-c-array' to mutate the GPU array? I thought that's exactly what it's for...

cbaggers commented 5 years ago

@stacksmith You are totally right. Sorry for misreading. Here is my test I think is equivilent

(defun test1 ()
  (let ((garray (make-gpu-array '(0.0 1.0 2.0) :element-type :float)))
    (print (pull-g garray))
    (with-gpu-array-as-c-array (qqq garray)
      (setf (aref-c qqq 0) 999))
    (print (pull-g garray))))

I get an error saying

The value
  999
is not of type
  SINGLE-FLOAT

when I change it to 999f0 I get

TESTS> (test1)

(0.0 1.0 2.0) 
(999.0 1.0 2.0) 
(999.0 1.0 2.0)

I then wondered if my test was too different and compiled these:

(defvar garray nil)

(defun test1 ()
  (setf garray (make-gpu-array '(0.0 1.0 2.0)
                               :element-type :float)))

and then ran this

TESTS> (test1)
#<GPU-ARRAY :element-type :FLOAT :dimensions (3) :backed-by :BUFFER>
TESTS> garray
#<GPU-ARRAY :element-type :FLOAT :dimensions (3) :backed-by :BUFFER>
TESTS> (pull-g garray)
(0.0 1.0 2.0)
TESTS> (with-gpu-array-as-c-array (qqq garray) (setf (aref-c qqq 0) 999))
; Evaluation aborted on #<TYPE-ERROR expected-type: SINGLE-FLOAT datum: 999>.
TESTS> (with-gpu-array-as-c-array (qqq garray) (setf (aref-c qqq 0) 999f0))
999.0
TESTS> (pull-g garray)
(999.0 1.0 2.0)

I'm not sure what to try next to replicate the issue