Archive for September, 2013
Before delving into Wayland‘s shaders, we need to define what a shader is and the difference between vertex shader and fragment shaders.
A shader is a computer program that runs on the GPU and is used to produce appropriate levels of light and colour within an image. Shaders are written in a specific shading language and they can be used to achieve customised effects.
There are different types of shaders, applied in different stages of the graphic pipeline and having different purposes. Here, we are mainly concerned with two types: the Vertex Shader and the Fragment Shader.
A vertex shader is run once for each vertex given to the graphics processor in the early stages of the graphical pipeline. It can manipulate properties such as position, colour, and texture coordinate. It’s in the vertex shader stage that each vertex’s 3D position is transformed to the 2D coordinate on the screen.
The fragment shader, on the other hand, manipulates pixels instead of vertices. It takes part in the rasterisation step, when colour and other attributes are computed for each fragment (pixel). With limited information about the whole scene — the fragment shader can only operate in a single pixel and sample nearby pixels — fragment shaders cannot produce very complex effects apart from blur, edge detection/enhancement, post-processing and filtering.
Because our objective is to manipulate colour spaces, it’s clear from the previous discussion about shaders that we need a fragment shader. In Wayland, adding a fragment shader is just a matter of creating an array of chars with the fragment code:
static const char texture_fragment_shader_color =
” vec3 color = texture2D(cms_lut, v_texcoord).rgb;\n”
” gl_FragColor.rgb = color;\n”
and concatenate it to the others fragments sources in the shader_init function, using the sources variable:
sources[i++] = texture_fragment_shader_color;
Now, the most difficult part is the creation of the colour transformation texture (cms_lut) and its integration into the fragment shader. To create the texture, we need the LCMS library and a code like this (implementation details omitted):
static unsigned char
weston_cms_transform_data(struct weston_color_profile *p,
unsigned short data[GRID_SIZE][GRID_SIZE][GRID_SIZE])
input = cmsCreate_sRGBProfile();
transform = cmsCreateTransform(input, TYPE_RGB_16, p->lcms_handle,
TYPE_RGB_16, INTENT_PERCEPTUAL, 0);
cmsDoTransform(transform, data, data, GRID_SIZE * GRID_SIZE * GRID_SIZE);
As it can be seen in this code, the texture data is being written by the LCMS library in the cmsDoTransform function. Now, we need to link this data with the fragment shader. That is accomplished in two steps. First, we need to tell OpenGL that this data exists in the fragment shader:
static const char fragment_shader_color_uniforms =
“uniform sampler2D cms_lut;\n”
Because of the way the fragments shaders are constructed in Wayland, the uniform declaration must the separated from the shader code and inserted at the right point in the shader_init function. For the fragment to have a pointer to this memory area, we use the getUniformLocation function to save it into the shader program:
shader->cms_uniform = glGetUniformLocation(shader->program, “cms_lut”);
Finally, we bind the uniform as a texture type and generate the final texture:
glTexImage3D(GL_TEXTURE_3D, 0, GL_RGB, GRID_SIZE, GRID_SIZE, GRID_SIZE, 0, GL_RGB, GL_UNSIGNED_SHORT, data);
The interesting thing about the colour conversion is that all the conversion data is stored per output. So if you have two different monitors with different colour profiles, the appropriate conversion is done to each of one, separately.
The complete set of changes can be seen in the branch ‘colour’ of my clone of the Weston repo.