Jump to content
  • Advertisement


  • Content Count

  • Joined

  • Last visited

Community Reputation

145 Neutral

About Netzapper

  • Rank
  1. One of my primary goals is reducing repetition. The worst offender, at least in GLSL, is the constant redefinition of `in` and `out` variables. They are *supposed* to form a linking surface, allowing you to interchange different fragment shaders on the same vertex shader. And it works reasonably well for that if you're mostly just putting different materials on realistic meshes. But it breaks down really quickly when you're working on special effects, 2D compositing, or pretty much anything but skinning triangle meshes. You wind up writing custom vertex shaders just to pass through data.   So in Parasol, one goal is permit interface variables to be declared inline to the expressions that use them. Perhaps that's confusing you? The `v[`, `u[`, etc. introduce what I'm calling a "stage scope". The names are short to reduce RSI and clutter, but they could be called `vertex[`, `uniform[` and `fragment[`, etc. I use the `varName: type index` format so that you can introduce a type-inferred variable, a type-defined variable, or an indexed variable (for attribute input and fragment output) optionally. Any other syntax requires that you specify something you may not know yet (specifically, type or index). vertex_pos { v[v_pos] = ; declare `out vec4 v_pos` in the vertex stage, and set its value u[viewMatrix: mat4] * ; declare `uniform mat4 viewMatrix` u[modelMatrix: mat4] * ; declare `uniform mat4 modelMatrix` vec4(a[v_inPosition: vec3], 1) ; declare `in vec3 v_inPosition`, but leave its index undefined } vertex_normal { v[v_normal] = u[normalMatrix: mat3] * a[v_inNormal: vec3] } The key component here is that you can now include `vertex_pos` and `vertex_normal` into another pipeline and your fragment shader in that pipeline could reference `v_pos` and `v_normal` without needing to respec them. Type inference travels across stages, so if you change a type anywhere, the whole expression will update or give an error (for instance, switching from 3d to 4d normals).   Additionally, your vertex position attribute and vertex normal attribute are now handled modularly, in a way that allows you to pull in just one or the other if the material you're working on only requires one or the other. Parasol actually allows you to write all of these "abstract" pipelines without spec'ing the attribute indices or fragment output indices (as above)... you can then provide that final index spec in the final "concrete" pipeline that you synthesize as GLSL or SPIR-V code. normal_debug { include vertex_pos a[v_inPosition: 0] ; spec attribute index v[gl_Position] = u[projMat: mat4] * v_pos include vertex_normal a[v_inNormal: 1] f[outColor: vec3 0] = v_normal.xyz } > Ok so, what's the point? How is this better than imperative?   I don't know that it's better. I'm not a language purist. I do argue that Parasol code is briefer and more modular than GLSL code. And most shaders I write are *already* in closed, branch-free form, which a functional language encourages and supports. I want function templates, and they're super easy to implement in a functional language. Plus a functional language permits aggressive static evaluation with trivially-understood semantics.   But, the main reason that I ditched the imperative model is so that statement order isn't important. This allows you to reason about how a particular attribute or texture is used in your shader program independently of any other usage in your program. That's what lets you pull in `vertex_pos` without worrying about `vertex_normal` stepping on something you're doing. Re-declared variables are merged (or an error occurs if they're incompatibly spec'd), meaning that you can have multiple "overlapping" pipelines reusing the same stage outputs, attributes, and uniforms.   Not that you *can't* achieve that in an imperative language, but you have to be a lot more careful. Just look at the GPU execution model! Look how many C features are missing from GLSL and SPIR-V. No function pointers? No goto? No recursion? The `main()` function executed by any particular shader unit is imperative because that's how we build silicon these days and it's better than the busted-ass VS/FS assembly we had 10 years ago, but the *flow* of the shader as a whole isn't imperative.   I would say there's little reason to use Parasol if all of your shaders are in the fragment stage or if you have very few different shaders in your application. If you've got triangle meshes, and everything has the same attributes, then you're probably just writing fragment shaders, who cares what language they're in. But, I write a lot of non-photorealistic special effects, and right now I'm doing a lot of n-channel 2D compositing and instancing... and I'm tired of re-writing the vertex shader again just because I added (or removed) an attribute or changed the number of channels in a blend operation.   I'm trying to tame the combinatorial explosion at least at the source level, permitting the compiler to generate the necessary combinations for me. And that's most easily done in a functional language.
  2. 1. HLSL - there's no reason Parasol can't include an HLSL codegen, except that I don't know DirectX or HLSL. This would be a place where I definitely need some help.   2. Cross-stage expressions - I believe this is a killer feature in Parasol. The ultimate in don't-repeat-yourself.   2 #include - I guess I missed that on my list. And if you've got that preprocessor, it's not a bad way to go. The problem it doesn't solve for me is generic programming. For instance, switching from a 2D texture to a 2D array texture is only a change of two types: `sampler2D` -> `sampler2DArray`, and `vec2` -> `vec3`. But, with a GLSL function library, now I need two versions of the function, despite the texture expressions being identical.   Frankly, it's *always* felt weird to me to program the GPU in C when so many of the expected constructs are poorly supported. `if`, `switch`, and early return are ways of improving performance in C; but they either decimate performance or have no effect on GPU. Dynamic loops cost no more than static loops in C; but they're a totally different beast on GPU. Basically, while C is an *excellent* model of low-level CPU computation, it does a bad job of mapping to the GPU. I find lots of shader newbies want to write tree code or other recursive structures; they want pointers; they want function pointers and indirection. The C-like language encourages them to program as if they're still on the CPU.
  3. I'm tired of writing shaders in GLSL. It requires a lot of repetition, a lot of boilerplate. And there's no reasonable way to build modular code: uber shaders, conditional compilation, shader graphs, glsw-style string glue, shader templates... all suck for various reasons; I've tried them all. I've still written that same damn vertex multiply a thousand times. So for the past few weeks, I've been spending my free time on Parasol. <https://github.com/aubreyrjones/parasol>   It doesn't generate code yet, but it does parse into a nice AST <http://i.imgur.com/0VAi1xN.png> from a formal grammar (I'm using the lemon parser generator, which is included in the automated build).   I'm looking for feedback on the language design so far, and maybe some assistance in implementing it. I've done some DSLs and language tools (assemblers, toy compilers), but I'd appreciate somebody with more experience in functional languages. Specifically, I'm learning as I go about type theory and type inference (I'm starting to get it, but I'm not sure how to implement it).   Here's a sample: ; standard position pipeline, write it for the last time ever! vert_position { v[gl_Position] = ; define gl_Position in the 'v' (for vertex) stage. u[projMatrix: mat4] * ; here's a uniform matrix u[viewMatrix: mat4] * ; here's another u[modelMatrix: mat4] * ; you'll query their indices from the program like normal vec4(a[v_inPosition: vec3 0], 1) ; this declares a vertex attribute, its type, and its attribute index } ; Here's a complete Parasol pipeline fulfilling the OpenGL vertex and fragment shader contract. vert_color_complete { include vert_position ; Modularity! f[fragColor: 0] = ; the fragment color is equal to the interpolated... v[v_color] = ; ...output of the vertex stage vec4(a[v_inColor: vec3 1], 1) ; which is determined by an attribute, indexed 1 } Parasol is designed to be a simple, statically-typed, type-inferred functional language. It's designed from the ground up for graphical shader development, encapsulating calculations in multiple stages through a scoping mechanism. It eschews loops and ifs, offering map/reduce and psi-case expressions. Functions are implemented as templates, permitting easy generic programming and compile-time functional composition.   The runtime library, which loads compiled GLSL and SPIR-V code, is meant to be easily embedded into any C++11 client application. Additionally, the compiler is implemented as a static library, suitable for inclusion in client applications with access to the full C++11 runtime.   Here's a 16-light Phong-shading example <https://github.com/aubreyrjones/parasol/blob/master/parasol_examples/phong.prsl>.   And here's an incomplete language reference <https://github.com/aubreyrjones/parasol/blob/master/LANGUAGE_README.md>   Parasol builds repeatably anywhere there's a good C++11 compiler and cmake (tested with gcc and mingw-w64 4.8.4), including embedded copies of all required tools and libraries, which are all built automatically. Clone it, cmake it (out of tree), run make, and you too can make pretty `dot` graphs of an imaginary language. Or pitch in, and we'll be that much closer to codegen.
  • Advertisement

Important Information

By using GameDev.net, you agree to our community Guidelines, Terms of Use, and Privacy Policy.

GameDev.net is your game development community. Create an account for your GameDev Portfolio and participate in the largest developer community in the games industry.

Sign me up!