diff --git a/CHANGELOG.md b/CHANGELOG.md index 937aac29..ff646ef4 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,6 +1,8 @@ ## Master -- TBD +- Don't make things const by default as we don't get `const` info from the jextracts anyway +- Add `vc/comptime` +- Support nested components in VybeC ## v0.7.469 diff --git a/resources/shaders/edge_2d.fs b/resources/shaders/edge_2d.fs index 965f9f40..44b9c6ce 100644 --- a/resources/shaders/edge_2d.fs +++ b/resources/shaders/edge_2d.fs @@ -17,7 +17,7 @@ out vec4 finalColor; // #define BLUENOISE_TEXTURE_RESOLUTION u_noiseResolution #include "lygia/sample/clamp2edge.glsl" -#define EDGE_SAMPLER_FNC(TEX, UV) sampleClamp2edge(TEX, UV).r +#define EDGE_SAMPLER_FNC(TEX, UV) sampleClamp2edge(TEX, UV).g #include "lygia/filter/edge.glsl" void main (void) { diff --git a/src/data_readers.cljc b/src/data_readers.cljc new file mode 100644 index 00000000..4edd253a --- /dev/null +++ b/src/data_readers.cljc @@ -0,0 +1 @@ +{vp/& vybe.panama/&-reader} diff --git a/src/vybe/c.clj b/src/vybe/c.clj index 733552f4..85f7a83f 100644 --- a/src/vybe/c.clj +++ b/src/vybe/c.clj @@ -78,19 +78,22 @@ (defn ->name [component-or-var] - (let [var-str (cond - (vp/component? component-or-var) - (vp/comp-name component-or-var) - - (:no-ns (meta component-or-var)) - (name (symbol component-or-var)) - - :else - (str (symbol component-or-var)))] - (-> var-str - (str/replace #"\." "_") - (str/replace #"/" "___") - (str/replace #"-" "_")))) + (try + (let [var-str (cond + (vp/component? component-or-var) + (name component-or-var) + + (:no-ns (meta component-or-var)) + (name (symbol component-or-var)) + + :else + (str (symbol component-or-var)))] + (-> var-str + (str/replace #"\." "_DOT_") + (str/replace #"/" "_SLASH_") + (str/replace #"-" "_DASH_"))) + (catch Exception e + (throw (ex-info "Error in ->name" {:component-or-var component-or-var} e))))) (defn- schema-adapter [v] @@ -105,7 +108,7 @@ :pointer (vp/component? v) - (symbol (vp/comp-name v)) + (symbol (->name v)) :else v)) @@ -127,6 +130,12 @@ (->> type* (walk/prewalk (fn prewalk [v] (cond + (and (vector? v) + (contains? #{:vec} + (first v))) + (str (prewalk (last v)) + "[]") + (and (vector? v) (contains? #{:pointer :*} (first v))) @@ -148,7 +157,8 @@ (if-let [resolved (and (symbol? v) (resolve v))] (->name @resolved) - (name v))))))))) + (->name v))))))))) +#_(-adapt-type [:vec Rate]) #_(-adapt-type [:* {:const true} Rate]) #_(-adapt-type [:* Rate]) #_(-adapt-type :pointer) @@ -204,40 +214,81 @@ [:bb [:* [:* Unit]]]]) (defn- comp->c - [component] - (let [c-name (->name component)] - (format "typedef struct %s {\n%s\n} %s;" - c-name - (->> (vp/comp-fields component) - (sort-by (comp :idx last)) - (mapv (fn [[k type]] - (if (vp/fn-descriptor? type) - (let [{:keys [ret args]} (vp/fn-descriptor->map type)] - (format " %s (*%s)(%s);" - (-adapt-type (:schema ret)) - (name k) - (->> (mapv (fn [{:keys [symbol schema]}] - (str (-adapt-type schema) " " (->name symbol))) - args) - (str/join ", ")))) - (str " " (cond - (and (vector? type) - (= (first type) :pointer)) - (-adapt-type type) - - (= type :pointer) - "void*" - - (= type :string) - "char*" - - :else - (name type)) - " " (name k) ";")))) - (str/join "\n")) - c-name))) -#_ (comp->c VybeAllocator) -#_ (comp->c Unit) + ([component] + (comp->c component {})) + ([component {:keys [level embedded] + :or {level 0 + embedded false} + :as opts}] + (let [c-name (->name component) + opts (assoc opts :level level) + nesting (str/join "" (repeat (* (inc level) 2) \space))] + (format "%sstruct %s {\n%s\n%s}%s" + (if embedded + "" + "typedef ") + c-name + (->> (vp/comp-fields component) + (sort-by (comp :idx last)) + (mapv (fn [[k type]] + (if (vp/fn-descriptor? type) + (let [{:keys [ret args]} (vp/fn-descriptor->map type)] + (format "%s (*%s)(%s);" + (-adapt-type (:schema ret)) + (name k) + (->> (mapv (fn [{:keys [symbol schema]}] + (str (-adapt-type schema) " " (->name symbol))) + args) + (str/join ", ")))) + (cond + (and (vector? type) + (= (first type) :vec)) + (let [[_ {:keys [size]} vec-type] type] + (format "%s %s[%s];" + (if (vp/component? vec-type) + (comp->c vec-type (merge (update opts :level inc) + {:embedded true})) + (-adapt-type vec-type)) + (name k) + size)) + + :else + (str (cond + (and (vector? type) + (= (first type) :pointer)) + (-adapt-type type) + + (= type :pointer) + "void*" + + (= type :string) + "char*" + + :else + (try + (if (vp/component? type) + (comp->c type (merge (update opts :level inc) + {:embedded true})) + (name type)) + #_(name type) + (catch Exception e + (throw (ex-info "Error on `comp->c` when getting name" + {:k k + :type type + :component component} + e))))) + " " (name k) ";"))))) + (mapv #(str nesting %)) + (str/join "\n")) + (if embedded + (str/join "" (repeat (* level 2) \space)) + "") + (if embedded + "" + (str " " c-name ";")))))) +#_ (println (comp->c vybe.flecs/system_desc_t)) +#_ (println (comp->c VybeAllocator)) +#_ (println (comp->c Unit)) (def -clz-h "From clz.h in SC." @@ -249,6 +300,9 @@ # include #endif // __cplusplus +typedef bool boolean; +typedef char byte; + typedef int SCErr; typedef int64_t int64; @@ -610,11 +664,13 @@ static inline int32 NEXTPOWEROFTWO(int32 x) { return (int32)1L << LOG2CEIL(x); } ([form] (analyze form (ana/empty-env) {})) ([form env] - (analyze form env {})) + (analyze form (or env (ana/empty-env)) {})) ([form env opts] (try - (ana/analyze form env (merge {:bindings {#'ana-/macroexpand-1 -macroexpand-1}} - opts)) + (ana/analyze form + (or env (ana/empty-env)) + (merge {:bindings {#'ana-/macroexpand-1 -macroexpand-1}} + opts)) (catch Exception ex (let [data {:form form :env env @@ -670,9 +726,10 @@ static inline int32 NEXTPOWEROFTWO(int32 x) { return (int32)1L << LOG2CEIL(x); } (->name (resolve name)) (->> params (mapv (fn [{:keys [tag form]}] - (str (if (:mut (meta form)) - "" - "const ") + (str #_(if (:mut (meta form)) + "" + "const ") + "" (case (.getName ^Class tag) "[F" "float*" @@ -754,7 +811,13 @@ signal(SIGSEGV, sighandler); (format "{%s}" (->> (:val v) (mapv (fn [[k v]] - (str "." (name k) " = " v))) + (str "." (name k) " = " + (emit (analyze v))))) + (str/join ", "))) + + (= (:type v) :vector) + (format "{%s}" + (->> (:val v) (str/join ", "))) (string? (:val v)) @@ -773,6 +836,11 @@ signal(SIGSEGV, sighandler); nil "NULL" (:val v))) + :vector + (format "{%s}" + (->> (mapv emit (:items v)) + (str/join ", "))) + :static-call (let [{:keys [method args] klass :class} v] (case (->sym klass) @@ -939,7 +1007,7 @@ signal(SIGSEGV, sighandler); (->name (:var v)) (vp/component? var-value) - (->name (vp/comp-name var-value)) + (->name var-value) (get-method c-replace my-var) (c-replace v) @@ -978,9 +1046,10 @@ signal(SIGSEGV, sighandler); "" ;; Variables are defined as ;; `const` by default. - (if (:mut (meta form)) - "__auto_type " - "const __auto_type ")) + #_(if (:mut (meta form)) + "__auto_type " + "const __auto_type ") + "__auto_type ") form (emit init))] (-> acc @@ -1060,8 +1129,7 @@ long long int: \"long long int\", unsigned long long int: \"unsigned long long i (mapv (fn [c] (let [n (-adapt-type c)] (format " struct %s: \"%s\"" n (vp/comp-name c))))) - (str/join ", \\\n")) - ) + (str/join ", \\\n"))) ""))) #_ (-typename-schemas [VybeHooks]) @@ -1260,7 +1328,7 @@ long long int: \"long long int\", unsigned long long int: \"unsigned long long i {:keys [c-code ::c-data form-hash final-form init-struct-val]} (-> code-form - (transpile (assoc opts ::version 30))) + (transpile (assoc opts ::version 33))) obj-name (str "vybe_" sym-name "_" (when (or (:no-cache sym-meta) @@ -1498,7 +1566,13 @@ long long int: \"long long int\", unsigned long long int: \"unsigned long long i (defonce -*vybec-fn-watchers (atom {})) (defmacro defn* - "Transpiles Clojure code into a C function." + "Transpiles Clojure code into a C function. + + E.g. + + (vc/defn* simple-10 :- :int + [v :- Translation] + (simple v))" {:clj-kondo/lint-as 'schema.core/defn} [n _ ret-schema args & fn-tail] `(let [args-desc-1# (quote ~(->> args @@ -1674,6 +1748,16 @@ long long int: \"long long int\", unsigned long long int: \"unsigned long long i :data (vybe.type/Vector2 [10 5]) :metadata "MY META"})) +(defn comptime + "In the JVM, it returns `v`. In VybeC, it runs the + code in compile time (in the JVM) and returns the value." + [v] + v) + +(defmethod c-macroexpand #'comptime + [{:keys [form]}] + (eval form)) + (declare ^:no-ns typeof ^:no-ns typename #_^:no-ns comptime) @@ -1687,7 +1771,7 @@ long long int: \"long long int\", unsigned long long int: \"unsigned long long i :metadata ~(let [{:keys [line column]} (meta form)] (str *ns* ":" line ":" column)) :form ~(str form) - :data (vp/address arg#)})) + :data (vp/& arg#)})) arg#)) ;; ================= c-invoke methods =================== @@ -1728,7 +1812,7 @@ long long int: \"long long int\", unsigned long long int: \"unsigned long long i (emit (analyze arg-form (:env arg))))) ;; Map. (some-> arg emit))] - (str "(" (->name v) ")" + (str "(" (->name @v) ")" (or arg-str "{}")))) ;; -- Clojure core. @@ -1777,6 +1861,17 @@ long long int: \"long long int\", unsigned long long int: \"unsigned long long i [{:keys [args]}] (format "&%s" (emit (first args)))) +(defmethod c-invoke #'vp/& + [{:keys [args]}] + (format "&%s" (emit (first args)))) + +(defmethod c-invoke #'vp/as + [{:keys [args]}] + (def args args) + (format "(%s)%s" + (-adapt-type (:val (second args))) + (emit (first args)))) + (defmethod c-invoke #'vp/sizeof [{:keys [args]}] (if (= (count args) 2) diff --git a/src/vybe/flecs.clj b/src/vybe/flecs.clj index f8edf86f..34064aec 100644 --- a/src/vybe/flecs.clj +++ b/src/vybe/flecs.clj @@ -20,7 +20,8 @@ ecs_os_api_log_t ecs_os_api_log_t$Function ecs_os_api_abort_t ecs_os_api_abort_t$Function ecs_os_api_t ecs_ref_t ecs_system_t ecs_query_t - ecs_system_stats_t ecs_query_stats_t) + ecs_system_stats_t ecs_query_stats_t ecs_system_desc_t + ecs_observer_t) (java.lang.foreign AddressLayout MemoryLayout$PathElement MemoryLayout ValueLayout ValueLayout$OfDouble ValueLayout$OfLong ValueLayout$OfInt ValueLayout$OfBoolean ValueLayout$OfFloat @@ -31,9 +32,9 @@ ;; -- Flecs types (vp/defcomp ecs_type_t (org.vybe.flecs.ecs_type_t/layout)) -(vp/defcomp ecs_system_desc_t (org.vybe.flecs.ecs_system_desc_t/layout)) (vp/defcomp ecs_observer_desc_t (org.vybe.flecs.ecs_observer_desc_t/layout)) -(vp/defcomp observer_t (org.vybe.flecs.ecs_observer_t/layout)) + +(vp/defcomp observer_t (ecs_observer_t/layout)) (vp/defcomp iter_t (ecs_iter_t/layout)) (vp/defcomp query_desc_t (ecs_query_desc_t/layout)) (vp/defcomp app_desc_t (ecs_app_desc_t/layout)) @@ -43,7 +44,8 @@ (vp/defcomp query_t (ecs_query_t/layout)) (vp/defcomp ref_t (ecs_ref_t/layout)) (vp/defcomp os_api_t (ecs_os_api_t/layout)) -(vp/defcomp os_api_t (ecs_os_api_t/layout)) +(vp/defcomp system_desc_t (ecs_system_desc_t/layout)) +(vp/defcomp entity_desc_t (ecs_entity_desc_t/layout)) (vp/defcomp DocDescription (EcsDocDescription/layout)) (vp/defcomp Identifier (org.vybe.flecs.EcsIdentifier/layout)) @@ -107,14 +109,17 @@ (list `path (->> (str/split s #"\.") (mapv -flecs->vybe))) - (let [parsed (str/replace s #"!!" ".")] + (let [parsed (-> s + (str/replace #"_DOT_" ".") + (str/replace #"_SLASH_" "/") + (str/replace #"_DASH_" "-"))] (if (str/starts-with? s "C_") (-> (subs parsed 2) symbol) (-> parsed keyword -adapt-name)))))))) -#_ (-flecs->vybe "s!!f/f.al") +#_ (-flecs->vybe "s_DOT_f_DASH_f.al") #_ (-flecs->vybe (vybe-name Position)) (declare eid) @@ -685,21 +690,22 @@ (vybe-name [v] (str "#" (unchecked-int v))) + clojure.lang.Keyword + (vybe-name [k] + (-> (symbol k) + str + (str/replace #"\." "_DOT_") + (str/replace #"/" "_SLASH_") + (str/replace #"-" "_DASH_"))) + VybeComponent (vybe-name [v] - (str "C_" (-> (.get (.name ^MemoryLayout (.layout v))) - (str/replace #"\." "!!")))) + (name v)) IVybeWithComponent (vybe-name [v] (vybe-name (.component v))) - clojure.lang.Keyword - (vybe-name [k] - (-> (symbol k) - str - (str/replace #"\." "!!"))) - String (vybe-name [s] s) @@ -1841,7 +1847,7 @@ opts _system-id (vf.c/ecs-system-init - w (ecs_system_desc_t + w (system_desc_t (merge {:entity e :immediate immediate :query (parse-query-expr w query-expr)} diff --git a/src/vybe/game.clj b/src/vybe/game.clj index b98364a1..dc8396b3 100644 --- a/src/vybe/game.clj +++ b/src/vybe/game.clj @@ -566,6 +566,9 @@ (defn- -safe-eval-model-meta [node v] (cond + (nil? v) + v + (vector? v) (mapv #(-safe-eval-model-meta node %) v) diff --git a/src/vybe/panama.clj b/src/vybe/panama.clj index 343c2c96..c18f16ac 100644 --- a/src/vybe/panama.clj +++ b/src/vybe/panama.clj @@ -206,6 +206,14 @@ (to_with_pmap [_] -to-with-pmap) (opts [_] -opts) + clojure.lang.Named + (getName [this] + (str "C_" + (-> (-> this .layout .name .get) + (str/replace #"\." "_DOT_") + (str/replace #"/" "_SLASH_") + (str/replace #"-" "_DASH_")))) + #_ #_clojure.lang.IPersistentCollection (equiv [this x] (and (instance? VybeComponent x) @@ -608,6 +616,9 @@ (string? v) (.allocateFrom (default-arena) v) + (int? v) + (MemorySegment/ofAddress v) + :else v)) (^MemorySegment [identifier v] @@ -654,6 +665,15 @@ [v] (.address (mem v))) +(defn & + "Get address from a value (same as `address`)." + [v] + (.address (mem v))) + +(defn- &-reader + [form] + `(vybe.panama/& ~form)) + (declare -vybe-popaque-rep) (deftype+ VybePOpaque [-mem-segment identifier -component] diff --git a/test/vybe/c_test.clj b/test/vybe/c_test.clj index 5c2b0704..a3645d94 100644 --- a/test/vybe/c_test.clj +++ b/test/vybe/c_test.clj @@ -78,7 +78,7 @@ ;; From https://github.com/supercollider/example-plugins/blob/main/03-AnalogEcho/AnalogEcho.cpp (vc/defn* mydsp :- :void [unit :- [:* vc/Unit] - ^:mut echo :- [:* AnalogEcho] + echo :- [:* AnalogEcho] n-samples :- :int] (let [[input] (:in_buf @unit) [output] (:out_buf @unit) @@ -87,8 +87,8 @@ coeff 0.95 buf (:buf @echo) mask (:mask @echo) - ^:mut write_phase (:write_phase @echo) - ^:mut s1 (:s1 @echo) + write_phase (:write_phase @echo) + s1 (:s1 @echo) max-delay (:max_delay @echo) delay (if (> 0.01 max-delay) @@ -259,7 +259,7 @@ (is (> (myflecs 4) 500))) (vc/defn* myraylib :- vt/Vector2 - [^:mut myint :- :int] + [myint :- :int] ;; Check that myint is really mutable. (swap! myint + 3) diff --git a/test/vybe/flecs_test.clj b/test/vybe/flecs_test.clj index 0913162c..84d08d14 100644 --- a/test/vybe/flecs_test.clj +++ b/test/vybe/flecs_test.clj @@ -7,6 +7,7 @@ [clojure.edn :as edn] [vybe.panama :as vp] [vybe.type :as vt] + [vybe.c :as vc] #_[matcher-combinators.test]) (:import (java.lang.foreign Arena ValueLayout MemorySegment) @@ -89,6 +90,25 @@ ["alice" {:x 10.0, :y 20.0}]} (set @*acc)))))) +(vc/defn* ^:debug default-systems :- :void + [^:mut w :- :*] + (let [e (vf.c/ecs-entity-init w #vp/& (vf/entity_desc_t + {:name "vybe_transform" + :add (-> [(vc/comptime + (vf.c/vybe-pair (flecs/EcsDependsOn) + (flecs/EcsOnUpdate)))] + (vp/as [:vec :long]))}))] + (vf.c/ecs-system-init + w #vp/& (vf/system_desc_t + {:entity e + :query {} + #_ #_:callback (-system-callback + (fn [it-p] + (let [it (vp/jx-p->map it-p ecs_iter_t) + f-idx (mapv (fn [f] (f it)) f-arr)] + (doseq [idx (range (ecs_iter_t/count it-p))] + (each-handler (mapv (fn [f] (f idx)) f-idx))))))})))) + ;; Based on https://github.com/SanderMertens/flecs/blob/master/examples/c/entities/basics/src/main.c (deftest ex-1-w-map ;; Create the world. @@ -96,12 +116,13 @@ #_(def w (vf/make-world)) #_(def w w) - (vf/eid w vt/Translation) - (vf/eid w vt/Rotation) - (vf/eid w vt/Scale) - (vf/eid w vt/Transform) - (vf/eid w :global) - (vf.c/vybe-default-systems-c w) + #_(do + (vf/eid w vt/Translation) + (vf/eid w vt/Rotation) + (vf/eid w vt/Scale) + (vf/eid w vt/Transform) + (vf/eid w :global) + (vf.c/vybe-default-systems-c w)) ;; Create a observer. (vf/with-observer w [:vf/name :ex-1-observer diff --git a/todo.md b/todo.md index 724fa159..cae4040e 100644 --- a/todo.md +++ b/todo.md @@ -249,6 +249,24 @@ - [x] square - [x] circle - [x] animate blob +- [ ] VybeC + - [ ] move existing system into VybeC from C + - [ ] direct translation + - [ ] with-system working in VybeC + - [ ] `defsystem`? + - [ ] read from source if some function has a inline (or not) metadata telling + us the fn spec + - [ ] eval + - [ ] form to quickly evaluate stuff + - [ ] REPL plugin like portal does for cljs? + - [ ] better way to jump to definition + - [ ] allocator setting + - [ ] comptime + - [ ] exe mode + - [ ] check that we don't have unitialized resources in the init function if + in this mode +- [ ] interior light + - [ ] point light - [ ] multiple worlds, render twice? - [ ] 2-player where you pass one object to the other? - [ ] 3d? @@ -361,16 +379,6 @@ - [ ] charts in TVs - [ ] press button - [ ] GUI sound -- [ ] VybeC - - [ ] better way to jump to definition - - [ ] allocator setting - - [ ] comptime - - [ ] exe mode - - [ ] check that we don't have unitialized resources in the init function if - in this mode - - [ ] eval - - [ ] form to quickly evaluate stuff - - [ ] REPL plugin like portal does for cljs? - [ ] use portal - [ ] memory segment - [ ] component