#genesis: a haxe rendering library
1 messages Β· Page 2 of 1
Haha my policy is that I'm focusing only on net/http for now, others can try it out on different libraries and see if it might work, but I want to get the most important library for my use case working correctly before I try out any others.
Okay so good news, I finally got conditionals to work after 2 days or so of headscratching
π
[showcase] I've finally gotten conditionals to work in the SPIR-V printer, this means that it is nearing completion! Only for and while loops need to be implemented. Everything else works :D
Other than the changes to support SPIR-V I've also spent some time into making MNSL more robust:
- Made the main function require it has no parameters and returns void.
- Made the input/uniform variables read-only.
- A few other changes like the ones above that makes MNSL smarter regarding user error.
- Added an array type instead of assuming arrayaccess on any type is valid.
- Many bugfixes.
wdym, and i'm using direct exchange apis. I've made an abstraction setup so I can mostly just map the exchange apis and use whatever exchange feed i want
main reason for my own app is there's nothing i've found that can do what i want x)
the debug view looks cool xD
i see, totally valid reason
it's what we all are doing π
Anything out there I want? No. Make it ourself!
that's actually pretty cool, do you usually stick with one or do you move between exchanges for better rates (transaction fees)
more people should think like this
XD
I love how that mindset is engrained in us haxe developers
lol
I had around 4 at one point, but dropped down to just supporting main use case to ease burden
the one i'm maining atm is bybit
will be the priority exchange, and will add an alternative at some point
don't particularly mind fees, care more about liquidity and likelyhood of exchange stability with a decent api
imagine the exchange goes down before you can place a sell order
and when it's back up
the stock has gone down 50%
that would be both sad and hilarious
back in the day for crypto in particular, those kinds of things could happen often on popular exchanges
they would get overloaded with traffic and didn't have the bandwidth or processing power to be able to handle the orders so your order would be placed in a queue
but your order could be invalid, so by the time it reached your order and it checked it. The order would get rejected because of some issue
that's dumb
if the queue is full, it should simply decline anyone's request and wait until the queue is fully empty again before starting to accept orders again
that is my opinion at least
early days kind of thing
but it shouldn't even get overloaded in the first place
most people who started crypto exchanges very early in the scene, are more like us. Relatively skilled people who has general knowledge but not much practical experience
that's quite literally the worst case scenario
yeah defo
i mean the behaviour is correct in most cases
on any website you would want a queue
but on a stock exchange?
the stock mutating while my order is waiting in a queue for an unspecified amount of time is asking for trouble really
i didn't read up on why decisions were made, but i suspect it was more of a situation of the demand outgrew a working solution
and they likely didn't have the technical knowledge or team to sort it out for quite a while
i see
im currently waiting how one of my ideas will pan out
i've bought stocks in a bus company which almost filed for bankruptcy
they will deliver their busses this summer
with stock based things, the responsibility is mostly on you. If you post a market order under these kinds of circumstances then you're subject to these kinds of problems
yeah defo
lol
as long as the "queue" is explicitly stated somewhere it's fine I guess
early crypto scene is a lot like haxe scene
lots of potential
very little detail work
things are maturing, those kinds of issues rarely occur on the bigger exchanges
I'd hope so xD
was fun though
i can believe so
did it ever work btw?
yes it did but the output structure changed a bit, so my search logic is broken
will have to probably add the --diagnostics flag
also, the codebase of scc didn't compile with go2hx due to an error regarding versions
one of the dependencies were too new for the go version that is used
conditionals and loops are now done and tested, SPIRV has reached completion! any sample should now be able to compile without any issues
if you could make issue, I'd love to fix it, diagnostics flag would be nice but it might be possible to catch these types of issues by simply displaying the stderr of the Go process into the Haxe process of the compiler.
no I mean diagnostics to fix this issue
there is no way for me to tell exactly what it is that the user requested to compile
I'd like to show what the files are for each library and some other info regarding times, issues, the cache, etc
I think it's best if there was some kind of API to easily submit these statistics to.
I think i'll be able to look at that flag tomorrow
will do :D
Ah that makes sense, yeah that sounds ideal
I referenced the wrong message, meant to fix this issue.
Lately I have been converting shaders from glsl to unity format. The weird thing regarding that is, I simply asked Claude to do the conversion, using my previously converted shaders as reference. And it works almost perfectly well. (I just had to fix one thing). The thing is: it only took, like, 10 minutes to do the shader conversion, so that makes me wonder how useful it is to actually do shader conversion with some specific script or spirv tools, when it's becoming that easy to ask some LLM to make the conversion
Sure, LLMs will be able to convert let's say 80% of shaders, but what if the shader is too large? What if there are multiple files? What if you have different kinds of shaders? What if the semantics between 2 languages are completely different? The mod operator does behave differently in some shader languages (from what I know)
The question is not "Can LLMs convert shaders", they totally can- But is it practical? You have practically no guarantees, changes need to be replicated across multiple files if they happen, loading shaders through a common API could become problematic. I could have decided to make separate shaders per graphics backend: it isn't that time consuming with LLMs but it is still an inconvenience.
for example: consider there are multiple vector types inside of ceramic, how would you feel if you had to replace Ceramic.Vec2() with Unity.Vec2()?
It's annoying but it could be a simple find & replace
The magic part of tooling (imo) is that things "just work"
If I write some code and suddenly it can run in my browser and on my phone, I will be happy.
Not saying that tooling is always the best solution, especially when you start layering tools over each other.
But, in the case of shaders, I think it is well worth it to include tools like these. Nobody should be required to depend on LLMs to convert their shaders in a reasonable time frame.
isnt this what Slang was kinda designed for-
Oh for sure it's always better to have reliable and automatic conversion by default, what I'm saying is that the existence of LLMs makes this less of a requirement in practice. Like, Claude can work with a lot of different files at the same time, it's totally able to convert the glsl-specific function to the hslsl equivalent, especially if you did give examples as reference. My mirror game lighting shader for Unity has been completely written by Claude from the GLSL one, except one attribute that wasn't converted properly, but something that can easily be addressed with a better prompt
I may still try to look at automated glsl to hlsl conversion anyway, just less useful in practice than I thought. More like a "nice to have" feature
ceramic mnsl plugin when
when MKI says it's production ready
π«‘
you could look it that way, especially considering your "scope" of ceramic is what you need
if you only need to change shaders sometimes then marking shader cross-comp as a "nice to have" makes sense to me
I completed SPIR-V support today, so a stable beta should be here this or next week.
Yeah, I mean, GLSL shaders are working on all Ceramic targets except Unity right now
this is my todo
Right now the minor problem I have with glsl shaders is that they are written in GLES2-style
hmmm
I think I should use a slightly more modern format as default version
I may use GLES 3.0 format as a default format (and rewrite all the default shaders in that format) at some point, and use that as a base for the conversion to HLSL (which is then embedded in some Unity-flavoured shader and so on)
Supporting MNSL with a Ceramic plugin is totally doable
I may only use the MNSL -> GLSL conversion though
Oh, but wait
it NEEDS syntax coloring in vscode (a tmLanguage file)
already have that :P
out of curiousity, what does ceramic need in order to run correctly (in terms of shaders)?
what is the default shader like?
Default shader is pretty basic:
https://github.com/ceramic-engine/ceramic/blob/master/plugins/clay/assets/textured.frag
But it got some annotations to support multi-texture batching
There are more advanced shaders like the pixel art one: https://github.com/ceramic-engine/ceramic/blob/master/plugins/clay/assets/pixelArt.frag
But nothing that a LLM can't convert at this point
And Ceramic only uses vertex and fragment shaders, nothing fancy
You need to be able to pass uniforms, textures and attribute and that's about it
// ceramic: multiTexture
#ifdef GL_ES
precision mediump float;
#else
#define mediump
#endif
// ceramic: multiTexture/texture
uniform sampler2D tex0;
varying vec2 tcoord;
varying vec4 color;
// ceramic: multiTexture/textureId
void main() {
vec4 texColor;
// ceramic: multiTexture/if
texColor = texture2D(tex0, tcoord);
// ceramic: multiTexture/endif
gl_FragColor = color * texColor;
}
yeah mnsl should have everything for that as-is
first question: yes, second question: not yet
uniform sampler2D u_Texture;
in vec2 in_TexCoord;
in vec4 in_Colour;
out vec4 out_Colour;
void main() {
vec4 TextureColour = texture(u_Texture, in_TexCoord);
out_Colour = in_Colour * TextureColour;
}
// uniform
@uniform(Texture: Sampler)
// inputs
@input(TexCoord: Vec2)
@input(Colour: Vec4)
// output
@output(Colour: Vec4)
// functions
func main() {
var TextureColour = texture(uniform.Texture, input.TexCoord);
output.Colour = input.Colour * TextureColour;
}
in case you missed it, you can mess around with it here: https://mki.sh/mnsl.html (note that the uniforms/inputs/outputs you can use on the web demo are limited)
random question for anyone reading, for context I just added this:
func squared(x) -> x * x;
func main() {
var a = squared(5);
var b = squared(a);
}
what is your guys' opinion about arrow syntax in general (any language)?
its peak
i don't really get the benefit of the arrow over the haxe style?
i.e why not just func squared(x) x * x;
var squared = x->x*x; 
i could add that in theory, but it would be way too evil
func main() {
var someValue = 5;
var multiply = x -> x * someValue;
var x = multiply(5);
}
would look in the current scope when the closure is defined, check what vars are available, and then generate something like:
int __mnsl_closure_1(int someValue, int x) {
return x * someValue;
}
void main() {
int someValue = 5;
int x = __mnsl_closure_1(someValue, x);
}
very hacky and very evil
i don't know if this is practical.
you wouldn't be able to pass the created function to another function
If you allow the arrow, it shouldn't need the func keyword
You are this close to rename mnsl to hxsl
isnt that heaps' shading lang
That's the joke (:
:P
:P
was able to get spirv-dis to work in the web browser with emscripten
the way I did it is extremely hacky but it works
the highlighting is nice atleast :D
my favorite thing is still seeing how types get converted in real time
for example:
with optimizer
https://i.mki.sh/35265fc18b29d53b287869b8.gif
without:
just making changes and seeing how a whole bunch of systems work together to form something that is valid glsl is wild to me
and also how just one additional layer (the optimiser) largely changes how something behaves in the final result is also just so cooool
this is quite literally candy for my brain to just mess around with
Relatable
what's this "estimated cost to develop" stat lol
it's based on https://en.wikipedia.org/wiki/COCOMO apparently.
The Constructive Cost Model (COCOMO) is a procedural software cost estimation model developed by Barry W. Boehm. The model parameters are derived from fitting a regression formula using data from historical projects (63 projects for COCOMO 81 and 163 projects for COCOMO II).
nice!
That might mean you are becoming an AI yourself
for sure!
god damn it. and I thought cobalt was real π
NOOO
CobaltAI, the newest release from Silicon Valley
throws pico mix at CobaltAI
Okay uhhh, small issue on the C++ target
so Std.isOfType is a bit weird
Star() is an MNSLToken
yet it passed the isOfType check against node?
wtf
????
does mnsltoken extend anythin
after a bit of messing around, matrices now have propper support
it's also smart enough to know that Mat4 * Vec3 is not valid and will cast Vec3 -> Vec4
and access has been changed quite a bit
here are some tests to get an idea of what is allowed
@uniform(matA: Mat3)
@uniform(matB: Mat3)
@uniform(arrA: Float[10])
@uniform(arrB: Vec3[10])
@uniform(arrC: Mat3[10])
// main function
func main() {
var a0 = uniform.arrA;
var a1 = a0[0];
var a2 = uniform.arrB;
var v0 = vec3(1.0, 2.0, 3.0);
var v1 = v0.y;
var v2 = v0[1];
var m0 = uniform.matA;
var mr0 = m0[0];
var mrc0 = m0[0][0];
var row = 1;
var col = 2;
var mrc1 = m0[row][col];
var idx = 1;
a0[idx] = 5.0;
v0[0] = 5.0;
m0[idx].x = 5.0;
m0[idx].y = 6.0;
m0[idx] = (1.0, 2.0, 3.0);
m0[idx] = 5.0;
a2[idx].x = 5.0;
a2[idx][1] = 6.0;
a2[idx] = 7.0;
m0[idx][idx] = 4;
var p0 = uniform.matA * uniform.matB;
var p1 = uniform.matB * uniform.matA;
var p2 = uniform.matA * 2.0;
var p3 = 2.0 * uniform.matA;
var p5 = uniform.matA * (1.0, 2.0, 3.0);
var p6 = (1.0, 2.0, 3.0) * uniform.matA;
var p7 = uniform.arrC[idx] * uniform.matA;
var p8 = uniform.arrC[idx][idx] * uniform.matA;
var p9 = uniform.arrC[idx][idx][idx] * uniform.matA;
var m1 = mat3(
1.0, 0.0, 0.0,
0.0, 1.0, 0.0,
0.0, 0.0, 1.0
);
var m2 = mat4(
1.0, 0.0, 0.0, 0.0,
0.0, 1.0, 0.0, 0.0,
0.0, 0.0, 1.0, 0.0,
0.0, 0.0, 0.0, 1.0
);
var m3: Mat3 = 6.0;
var m4: Mat4 = 8.0;
}
VecN[idx] is valid too now, as long as idx is a constant
and yeah everything works
even accessing a specific row and column of a Mat3[10] as you can see
func multiply(x, y) -> x * y;
func squared(x) -> x * x;
func halfOf(x) -> x / 2;
func main() {
var x = multiply(1, 2)->halfOf(_)->multiply(_, 3)->squared(_);
}
some people will love this, others will hate this.
note that haxe-like func squared(x) x * x; is also allowed
but the focus here is the chaining
very often i notice nested function calls in shaders, so this could help with readability
so this:
could be written as:
i like pipe operators
meh, not for me
:pipe:
i see
i also discovered that there are some things you can do that are unintentional
so mnsl allows pipe operators to work with any expression
add to that the fact you may initialize vectors with (1, 2)
and access them using array access
you get this
vec2 double_TVec2_TVec2(vec2 x) {
return vec2(x * vec2(2));
}
float double_TFloat_TFloat(float x) {
return float(x * float(2));
}
int double_TInt_TInt(int x) {
return x * 2;
}
void main() {
int x = double_TInt_TInt(5);
float y = double_TFloat_TFloat(5.5);
vec2 z = double_TVec2_TVec2(vec2(5));
}
before generics were only possible for internal functions, now they can be user-defined
still a bit experimental and it does need some more work (for example, in the TFloat_TFloat function is is casting to float twice, so there is something that the typer does not like)
but the basics work now
fixed the bugs :D
also works on spir-v (which isn't anything special considering that mnsl extracts separate functions out of generic functions)
what I also find really cool about this all is how in this example:
func double<T>(x: T): T -> x * 2;
func getX<T>(vec: T) -> vec.x;
func main() {
var v = (5, 5) -> double(_) -> getX(_) -> double(_);
}
it gives a warning because of an implicit conversion from float->int at getX
it says that because it knows x * 2 will multiply TUnknown with TInt, likely resulting in TInt
that information is available when getX(_) is called, and just because it knows it will return a numerical value and that it will be multiplied by an int in the future, it will cast vec.x to an int beforehand
when doing x * 2.0 the warning magically goes away
[showcase] MNSL has gotten new language features :D
- Pipe Operator: Code like
someFunction(doSomething(1.0, 2.0), 3.0)can be rewritten as1.0 -> doSomething(_, 2.0) -> someFunction(_, 3.0)
The pipe operator works with any expression, so1 + 2 -> _ + 4is valid. You can even use vectors to chain using more than one value. - Support for matrices Matrices are now fully supported :D this includes initialization, assigning values and accessing values.
- Generics MNSL now has support for user-defined generics, this helps a lot when you want to do the same operations on any data type or vector. (cool example of something the compiler can do: #1276627002022953000 message)
- Alternative Function Syntax You can now define functions like this
func squared(x) -> x * x;(or haxe-style likefunc squared(x) x * x) - Array access on vectors You can now use array access on vectors as long as the index is constant
- Inline You can now hint the compiler to inline a function (
inline func echo(x) -> x;)
Last but not least there have also been many bugfixes :D
I'd also like to thank Zanzlanz for helping me discover a bug regarding typing.
I think I'm really not a fan of that pipe operator. Seems foreign, redundant and somewhat confusing to me, but I guess some will like it?
Generics are a good addition however!
Out of curiousity, what is confusing to you specifically?
It's almost like static extensions
it tries to solve the same problem
The main difference is that you can pick the argument it uses and that the operator is an arrow compared to a dot
I mean, that's not the _ itself that I don't like. I just think the arrow chaining feels too foreign. My brains definitely doesn't want to read (5, 5) -> ... as a chained thing. At best it would think it's some closure syntax (which isn't the case here). someFunction(doSomething(1.0, 2.0), 3.0) is maybe theoretically less logical to read because of the inverted order of things, but it has been so common in so many programming languages that my brain prefers that. The fact that the language has two different syntaxes to mostly do the same thing is not really a good sign either imho. I'd rather have a language that sticks to a popular and common ground so that it feels natural enough for newcomers, and keep the more innovative syntax bits for things that can't be done with the regular syntax
I see what you mean
But a pipe operator isn't that uncommon
elixr, clojure, f# and many others
many languages have a concept which is the same without any specific operator
fluent interfaces specifically
JS devs do this alot
you have LINQ in c#
new Vec2(5, 5).multiply(2).getX()
compared to
(5, 5) -> multiply(_, 2) -> getX(_)
the main issue is that a dot is reserved to struct access. So this is the second best alternative IMO
I guess the main issue is the _ because I think if I were to just write (5, 5) -> multiply(2) -> getX() that it would already be more "acceptable" for people who are used to fluent interfaces
Also important to note is that there is no concept of classes, this makes fluent interfaces way harder
let's say i do .multiply(2)
that is just an int
Do you have any proposal to make this better?
The LINQ way still feels more natural, there is no need to use underscore
What If i made the underscore optional? I.e it would use the _ as the first argument if the expression isn't found anywhere?
I think a more pragmatic approach could be to still use the dot syntax like LINQ, except that you give priority to vector fields if they exist
so (5, 5) -> multiply(2) -> getX()
(anyway, there shouldn't be any ambiguity: if you read your Vec2().x, it's different than calling .x())
And your parser could recognize the difference
That's better yes
in that case how would you handle var F = fresnelSchlick(max(dot(V, H), 0.0), F0);
I would handle it like this : var F = fresnelSchlick(max(dot(V, H), 0.0), F0); xD
I think removing the underscore and keeping the arrow could work though
Personally I want to avoid nesting
let me try
It's just that the first argument, like a static extension, is always the previous expression
yeah
well it doesn't have to be
var x = (5, 5) -> double(_) -> add(5, _) -> multiply(_, _);
is also valid
in add it's the second argument, or in multiply it's both
but I guess if no _ is found I could default it to the first argument
Yeah, I mean it's cool if you like that syntax. I guess that's really not my stuff
I see
I'd rather either nest everything, or if that not readable enough, splitting into multiple lines and named variables is just fine to my book
var F = V -> dot(H) -> max(0.0) -> fresnelSchlick(F0);
so this makes more sense?
Hmm I see
It's your language anyway, you don't have to convince me :D, you are the first and most important user of it
yeah defo, but trying to make something that is liked by more people is also an interesting challenge
(that's the least one can have when designing their own language)
I am the primary user for sure, but, my goal is learning primarily
making something is one thing, but making something that is generally liked by people with different preferences is an even more valuable learning experience (for me personally)
Anyway, thank you for your input :D
[showcase] I've started a blog, feel free to read the first post here:
warning: the writing style is probably pretty horrible π I hope to improve it, so any and all suggestions are welcome.
lol weird, if you go to the mnsl page from your blog it keeps the fancy cursor
oop
wtf
that is buggy
yeah i wrote this blogging system in like 1,5 hours
and the codebase is from 2023 i think
so i prob did not handle URL's correctly
will fix
fixed
... also it seems that the cursor doesn't render on firefox
I think your blog design is cool, but you should remove the custom cursor entirely and stick to standard HTML page output. Sometimes the cursor doesn't even show up. It doesn't add anything except annoyances imho, and things like middle click to open a link on another tab are not working either