Vector swizzling in C++

Started by
6 comments, last by gwiazdorrr 10 years, 5 months ago

First off, I know there have been many topic at this forum regarding replicating GLSL/HLSL vector "swizzling" in C++, most notably:

http://www.gamedev.net/topic/426538-write-mask-and-swizzle-syntax-for-c/

http://www.gamedev.net/topic/492813-little-program-to-generate-swizzle-functions-for-c-vector-classes/

http://www.gamedev.net/topic/214804-vector-swizzling-in-c/

None of them provides a solution good enough to "run" GLSL/HLSL code. They are all locked now, so I'll start the topic again. Maybe OPs and newcommers will find this infofmation useful. Also, I wanted to set the bar higher - to be able run an actual shader as C++ code, for fun and science.

What we wan't is this code to be valid:


vec4 foo(0);                        // 0,0,0,0
foo.yx = vec2(2, 1);                // 1,2,0,0
foo.zw = foo.xy * 2;                // 1,2,2,4
vec2 bar = max(foo.xw, foo.yz).yx;  // 4,2
bar = clamp(foo.xw, 0, 2);          // 1,2  
mat2 m(foo.xyz, 1);                 // 1,2,2,1
bar *= m;                           // 5,2

I already mentioned previous thread. There also is the GLM library (http://glm.g-truc.net/0.9.4/index.html), which almost gets it right. Swizzling is pretty much replicated from GLSL, along with matrices and math functions. The problem is... you still just can't take any GLSL shader (these days I get ones for tests from http://glsl.heroku.com/ nad https://www.shadertoy.com/) and treat it as C++ code. You'll get ambiguity errors, mostly because in GLSL you don't have to suffix float literal with 'f' for it to be a float (0.5 vs 0.5f), it's more contextual. Also it seems that GLM is simply missing some GLSL features, e.g. it has incomplete vector/matrix constructors list. Apart from that, GLM suffers from what I call "code diarrhea", i.e. there's just to much code repetition and macros all over the place. I appreciate the amount of work that went into GLM, though.

One should also know about Clang's extension (http://clang.llvm.org/docs/LanguageExtensions.html#vectors-and-extended-vectors) that enables vector swizzling *as a compiler feature*. It is very neat and the output assembly looks good, but the syntax of constructing a vector, man...


float4 vf = (float4)((float2)(1.0f, 2.0f), (float2)(3.0f, 4.0f));

A big no-no, no way this can be used to "run" valid GLSL. Also, it binds you to a one compiler, which is never good.

Here steps in the CxxSwizzle (https://github.com/gwiazdorrr/CxxSwizzle), which I myself wrote in my free time. Bottom line, you can take almost any GLSL fragment shader and run it as C++ code, without any changes. Just like that. Conditional breakpoints, assertions, logs - there simply isn't a shader debugging solution (to my knowledge) that enables you to do that.

Feature list is still incomplete, but missing are mostly functions, which are the least difficult to implement (it's just a chore). Also, math implementation is extremely naive and slow, but due to the component structure of the code the "backend" can be easily moved to SSE based one, for instance.

Anyway, the syntactic compability with GLSL has been achieved, with little or no code repetition, using industry-available C++ features (subset of MSVC 2010, also tested on g++4.8.1). If you ever wanted to step through a shader and see "how it works" you now can.

I am extremely interested in (constructive) criticism of the library and, perhaps, discovering a better way of rich shader debugging. This one is slow as hell, but, well.. works. So? :)

Advertisement

Haven't gotten a chance to look at your library, but why would one want to run GLSL code as C/C++ code. The majority of the issue encountered when writing shaders are usually related/closely tied to the driver implementation. Most high level shading languague HLSL/GLSL provide means to identify simple errors, which, when identified are easy to correct. The more esoteric ones such as application crashing / visualy anomalies are usually not easy to track down, and running the code on the CPU is not going to help identify the issue. If the bulk of the shader code is just arithmetic operations, then a library like yours would help in validation at least to the point where stuff like floating point behavior is similiar. Another good use would be for syntax verification like you alluded to, which would be a plus.

Haven't gotten a chance to look at your library, but why would one want to run GLSL code as C/C++ code. The majority of the issue encountered when writing shaders are usually related/closely tied to the driver implementation. Most high level shading languague HLSL/GLSL provide means to identify simple errors, which, when identified are easy to correct. The more esoteric ones such as application crashing / visualy anomalies are usually not easy to track down, and running the code on the CPU is not going to help identify the issue. If the bulk of the shader code is just arithmetic operations, then a library like yours would help in validation at least to the point where stuff like floating point behavior is similiar. Another good use would be for syntax verification like you alluded to, which would be a plus.


I disagree. I think being able to run shader coder in a debugger would be fantastic. Setting breakpoints as being able to inspect values would be a great help in debugging. This also opens up the possibility of unit testing. That way, you can get the shader code working in at least one spot, then try and work out the problems you described later.
My current game project Platform RPG

Here steps in the CxxSwizzle (https://github.com/gwiazdorrr/CxxSwizzle), which I myself wrote in my free time. Bottom line, you can take almost any GLSL fragment shader and run it as C++ code, without any changes. Just like that. Conditional breakpoints, assertions, logs - there simply isn't a shader debugging solution (to my knowledge) that enables you to do that.

Feature list is still incomplete, but missing are mostly functions, which are the least difficult to implement (it's just a chore). Also, math implementation is extremely naive and slow, but due to the component structure of the code the "backend" can be easily moved to SSE based one, for instance.

Anyway, the syntactic compability with GLSL has been achieved, with little or no code repetition, using industry-available C++ features (subset of MSVC 2010, also tested on g++4.8.1). If you ever wanted to step through a shader and see "how it works" you now can.

I am extremely interested in (constructive) criticism of the library and, perhaps, discovering a better way of rich shader debugging. This one is slow as hell, but, well.. works. So? smile.png


When I first read that you achieved vector swizzling in c++ I couldn't think of how to replicate it. So I had to look at your code. I have to say, you have a pretty clever solution. Although I can see how it would be slow but good work.
My current game project Platform RPG

Haven't gotten a chance to look at your library, but why would one want to run GLSL code as C/C++ code. The majority of the issue encountered when writing shaders are usually related/closely tied to the driver implementation. Most high level shading languague HLSL/GLSL provide means to identify simple errors, which, when identified are easy to correct. The more esoteric ones such as application crashing / visualy anomalies are usually not easy to track down, and running the code on the CPU is not going to help identify the issue. If the bulk of the shader code is just arithmetic operations, then a library like yours would help in validation at least to the point where stuff like floating point behavior is similiar. Another good use would be for syntax verification like you alluded to, which would be a plus.

I think the way we think about debugging shaders is... well, dictated by the fact that there are no ways of doing it. We learned to cope with this. Some errors can be picked up by the compiler, some can be easily identified just by observing output. Hell, with CPU code you can debug print messages if for any reason you can't use debugger. With shaders you can't even do that!

While it's true that most shaders are short, they are by no means simple and they are getting increasingly complex. Ability to step through the code gives you an opportunity to get a better understanding of what's going on. You can also easily print results and partial results to, say, a CSV file and then plot them to see how input translates to output in more analytic way. Also, as @HappyCoder mention, you can now unit test your shaders or even single shader functions in isolation. Givin these options was the purpose of the library.

As the saying goes, "you cannot have too much of a good thing".

When I first read that you achieved vector swizzling in c++ I couldn't think of how to replicate it. So I had to look at your code. I have to say, you have a pretty clever solution. Although I can see how it would be slow but good work.

At least with MSVC almost all static_for loops are unrolled and almost all functions are inlined, so the perfomance suffers mostly to naive math implementation (per-component, no vectorisation).

I didn't focus on the performance, since even using SSE it would be suboptimal. You just shouldn't write your CPU code like you write your shaders. For instance, SSE prefers "structure of arrays" (SoA) approach rather than "array of structures" (AoS) - this becomes obvious when you have to process less than 4-component vector and do not use the full width of registers. Still, for some non-critical code it may be an option.

The simple way of using SSE with a shader is to process 4 pixels/verts/etc simultaneously. That way you can treat all the maths as scalar, but do it for four pixels at once (or 8 with AVX).

However what will really kill performance on the CPU is texture sampling.


However what will really kill performance on the CPU is texture sampling.

I worked on SW rasterizer for 3 years, and I have to agree. You have to architect your entire solution around texture-sampling.

For shader debugging, though, performance shouldn't really matter. The main issue there is discrepancy in the results between the CPU and the GPU. It might look like the same shader, but the compiled code will have different instructions flow. Then you have to generate shader inputs, so some more differences. And different texture sampler implementation, so more differences.

All those small differences accumulate. In many cases it's not really a problem, but there are plenty of cases where it's a real issue, especially when a shader suffers from numerical instability.

PIX's shader debugger is a good example - it uses the DX reference rasterizer, and I saw many cases where the results were different than what the GPU was outputing (and our SW rast was different than both of them).

Well, I guess you just have to pick your tools right.

Trying to resolve a visual bug in a complex application? Use PIX or Nsight or any other GPU/pipeline capture tool. They all work "on top" of your code, however complex it is. No changes needed, so if the bug is an effect of complex interactions, you have a chance of finding out the source that way.

Prototyping? Trying to understand how something works? Not sure if the driver is to blame? Well, depending on how much you rely on the debugger, you might choose CxxSwizzle.

This topic is closed to new replies.

Advertisement