Entries for tag "opengl", ordered from most recent. Entry count: 3.
# System Value Semantics in Compute Shaders - Cheat Sheet
After compute shaders appeared, programmers no longer need to pretend they do graphics and render pixels when they want to do some general-purpose computations on a GPU (GPGPU). They can just dispatch a shader that reads and writes memory in a custom way. Such shader is a short (or not so short) program to be invoked thousands or millions of times to process a piece of data. To work correctly, it needs to know which is the current thread. Threads (invocations) of a compute shader are not just indexed linearly as 0, 1, 2, ... It's more complex than that. Their indexing can use up to 3 dimensions, which simplifies operation on some data like images or matrices. They also come in groups, with the number of threads in one group declared statically as part of the shader code and the number of groups to execute passed dynamically in CPU code when dispatching the shader.
This raises a question of how to identify the current thread. HLSL offers a number of system-value semantics for this purpose and so does GLSL by defining equivalent built-in variables. For long time I couldn't remember their names, as the ones in HLSL are quite misleading. If
GroupID is an ID of the entire group, and
GroupThreadID is an ID of the thread within a group,
GroupIndex should be a flattened index of the entire group, right? Wrong! It's actually an index of a single thread within a group. GLSL is more consistent in this regard, clearly stating "WorkGroup" versus "Invocation" and "Local" versus "Global". So, although Microsoft provides a great explanation of their SVs with a picture on pages like SV_DispatchThreadID, I thought it would be nice to gather all this in form of a table, a small cheat sheet:
|Global in dispatch
|Local in group
|Global in dispatch
|Local in group
Update 2023-08-30: There is another article about this topic that I recommend: "Dispatch IDs and you".
# Watch out for reduced precision normalize/length in OpenGL ES
GLSL language for OpenGL ES introduces concept of precision. You can annotate variable declaration (both float and int/uint) with a precision qualifier:
mediump float a = 3.0;
You can also specify default precision qualifier by using precision statement. Language specification defines minimum required range and precision for each precision qualifier.
highp basically means normal, single-precision, 32-bit float (IEEE 754), as we know it from CPU programming.
mediump is said to have have range of at least -2^14 ... 2^14 and relative precision 2^-10, so it can be, for example, implemented using a 16-bit, half-precision float.
lowp is said to have range at least -2 ... 2 and absolute precision 2^-8, so basically it can be stored as a 10-bit, fixed-point number.
GPU vendors are free to use more precise data types, or even full 32-bit float for all of them. What exact precision is used depends on specific GPU and maybe even operating system or graphics driver version. Using smaller data types can occupy less memory, calculate faster and consume less battery power. But it comes at the price of reduced precision and range of these numbers. Tom Olson wrote interesting articles about this: "Benchmarking floating-point precision in mobile GPUs": Part I, Part II, Part III.
In this post I'd like to warn you against a specific problem related to it - usage of
distance() functions. Using smaller data types not only limits precision in terms of number of significant digits, but also available range (over which the value will saturate to -INF/+INF). For mediump, this range is defined as +/-2^14, which is only 16384.
This may still look like a lot, but let's remember that calculating vector length involves intermediate value that it sum of squares of this components. This can grow very big before a square root is applied. For example, for 3D vector:
length(a) = sqrt(a.x*a.x + a.y*a.y + a.z*a.z)
If you do this operation on a mediump vector, the term
a.x*a.x + a.y*a.y + a.z*a.z can exceed maximum value for vector as small as (74.0, 74.0, 74.0). It can be very dangerous if you do something like this in your fragment shader:
precision mediump float;
uniform vec3 light_pos;
vec3 dir_to_light = normalize(pos - light_pos);
// Calculate your lighting and so on...
You might ask: Why isn't this intermediate value stored in high precision before taking its square root to avoid this overflow problem? Obviously it could be, as precision in any place of the shader is free to be higher than the minimum allowed in that place, so some GPU vendors can do it this way, but you shouldn't rely on this. GLSL specification clearly says that the shader is free to use same, reduced precision for intermediate values.
The precision used to internally evaluate an operation, and the precision qualification subsequently associated with any resulting intermediate values, must be at least as high as the highest precision qualification of the operands consumed by the operation.
Conclusion is: When you write shaders for OpenGL ES, watch out for operations that involve calculating vector length (or dot product) like
highp precision for vectors involved and remember that what works on one GPU due to using precision higher than minimum required, may not work on another GPU and it’s still an application issue.
# Beginning with OpenGL ES on Android
I've started learning programming for Android. It's (probably) nothing commercial, I just love to learn new technologies and APIs while I have not much experience in either Java, OpenGL ES or mobile technologies at all. The first thing I did was... buying a phone - LG GT540 in my case. It has Android 1.6 (upgrade to 2.1 is scheduled for this year).
Android is a nice platform. Anyone can code for his phone in either Windows or Linux, using Java language (native code development is also possible). To start my coding, I needed to install JDK (SDK for Java), Android SDK, Java version of Eclipse 3.5 (they say Android SDK is not fully compatible with new Eclipse 3.6) and Eclipse JDT plugin to connect them all. Android Developers website does a great job explaining all the steps required to setup the whole development environment so I've came across not so many issues to get annoyed as I often get when installing some C/C++ stuff :)
I've read a bit about the fundamentals of developing applications for Android and I like the API. All these ideas like Activity, Service, Intent, View, Widget, Task etc. seem very well designed. But just like PC game developers learn WinAPI to only initialize an empty window and launch DirectX or OpenGL in it, I went straight to OpenGL on my Android. Android 1.6 has OpenGL ES 1.1, which in turn is the embedded equivalent for OpenGL 1.5. This 3D API has no shaders, so all the graphics has to be done using fixed function pipeline, including MODELVIEW and PROJECTION matrices, 2 textures with register combiners, 8 dynamic per-vertex lights, 1 user clip plane, fog etc. - something like on the old good GeForce 2 MX :)
It's easy to start using OpenGL for someone who has experience in DirectX because all the concepts that are so diffcult to understand at the beginning of the game programming adventure stay the same - like 3D coordinate system, vertex, triange, matrix, texture and so on. Some things are only reversed - textures are addressed from left-bottom corner in OGL, matrices are stored in column-major order, matrix multiplication is right-to-left like Point2 = Xform2 * Xform1 * Point1, post-projective space is -1..1, -1..1, -1..1, coordinate system is right-handed so Z points to the viewer, angles are given in degrees, counterclockwise oriented triangles are considered front-facing by default and so one. Some objects have also different names, so in OGL there is "model" not "world" matrix, "fragment" not "pixel" operation etc.
Here is the list of Internet sources I already know about and I've been learning from:
Now you can expect more entries about Android development on my blog :)