Shaders and Appearance

Shaders give clients their appearance. They are responsible for rendering client contents and decorations such as borders and shadows. With the default shaders, the clients are rendered with black, slightly rounded (chamfered!) corners. The current focus is indicated by an orange dashed line around the borders. Alternatively, task switching indicator (Alt+Tab, Super+Tab) is shown in a much lighter variant.

This section describes how to deploy and edit shaders for Chamferwm to customize the appearance of the desktop. To make only basic changes to the stock appearance, such as color or border attributes, see Basic Appearance.

Shader Paths

Chamferwm will look for compiled shader objects from lookup directories specified by user. User will specify the shader lookup paths with command line parameter --shader-path=[path]. More than one path may be given. At least one path must be specificed as there are no preset locations. The following configuration is suggested:

--shader-path=~/.local/share/chamfer/shaders --shader-path=~/.config/chamfer/shaders/ --shader-path=/usr/share/chamfer/shaders/

Shaders in this case are SPIR-V objects. From the given directories, Chamferwm will look for files with .spv extension. The path given first in the command line will have a higher priority to the next one, and between conflicting filenames the shader from the directory that was given first will be picked.

Basic Appearance

The appearance of clients and their effects such as decorations, borders and shadows is fully controlled by either adjusting the parameters of the given default shaders, or by designing custom solutions by modifying existing or creating new shaders. Since the appearance of the clients can be completely arbitrary and is dictated by the shaders only, there are no style specific configuration options in the configuration script such as color, border width or anything that might not be relevant to a custom design. Instead, these are intended to be defined directly within the shader source.

The look of the stock appearance of Chamferwm is given by the fragment shader portion of frame.hlsl (compiled frame_fragment.spv). By adjusting a set of constants defined in the HLSL source, user may control some of the basic features of the stock appearance:

borderScaling

Scaling constant for the width of the client border

borderColor

Color of the client border

focusColor

Color of the focus indicator

titleBackground

Background color of a title bar for an unfocused client

taskSelectColor

Color of the task selection indicator

STOCK_FRAME_STYLE

Choose between different demo styles (= 1: chamfered edges, other: simple borders)

In order to apply the changes, recompile the fragment shader using

glslc --target-env=vulkan -fhlsl_functionality1 -fshader-stage=fragment -x hlsl -DSHADER_STAGE_PS -o - frame.hlsl | spirv-opt -O -o frame_fragment.spv -

Once successfully compiled, the output object frame_fragment.spv can then be placed in one of the shader lookup directories.

Rendering Pipeline

Chamferwm requires three different shaders for its client rendering pipeline:

  1. Vertex shader

  2. Geometry shader

  3. Fragment shader

For each client, a single vertex is drawn. This vertex has no attributes, and the default vertex shader of Chamferwm simply passes it through to the geometry shader. From this single vertex the geometry shader then expands the necessary surfaces for the client according to the dimensions provided through the push constants. The following push constants are available, and are accessible in all shader stages:

Available push constants for client rendering pipeline
float2 xy0; //normalized top-left corner location
float2 xy1; //normalized bottom-right corner location
float2 screen; //screen pixel dimensions
float2 margin; //normalized gap margin in x and y directions
float2 titlePad; //A vector indicating the location and size of the titlebar if any
uint flags; //flags such as whether client is focused
float time; //time in seconds since client creation

The shader may declare any number of variables from the above list. The order of declaration does not matter, but should be shared among all shader stages.

The actual appearance is given by the fragment shader. The fragment shader identifies the fragments rasterized from surfaces given by the geometry shader, and paints the shadow, the window border and the client contents on them. The results are alpha-blended on previously rendered surfaces.

Text on the title bars and such is rendered using another pipeline, which defines a vertex shader and a fragment shader. A minimal vertex shader expects a position and a texture coordinate to sample the font atlas:

Available vertex attributes for text rendering pipeline
float2 pos : POSITION; //normalized point location
uint2 texc : TEXCOORD; //texture pixel coordinates

In HLSL the attributes are identified using the semantics POSITION, TEXCOORD etc. As with push constants, the vertex attributes may be declared as needed, and in any order. For text pipeline, following push constants are available:

Available push constants for text rendering pipeline
float2 xy0; //normalized baseline location at the beginning of the text
float2 screen; //screen pixel dimensions
float2x2 transform; //text transform matrix, generally for the rotation

Advanced Customization

The shader sources provided with Chamferwm can be found in the source tree. The provided shaders are written in HLSL, although any other language that compiles to SPIR-V, such as GLSL may be used. Following shader sources are provided:

chamfer.hlsl

Common definitions, and the push constants defined according to Available push constants for client rendering pipeline.

default.hlsl

Basic shaders to draw clients without any decorations or effects. Good starting point for new designs.

frame.hlsl

Shaders to draw clients with border and shadow. Stock look of Chamferwm.

solid.hlsl

Simple fragment shader to fill with solid color. Mostly used to draw solid color backgrounds when wallpaper is not set.

text.hlsl

Vertex and fragment shader for text rendering

Some of the sources contain portions for more than one shader stage, guarded by preprocessing directives. The build system for Chamferwm preprocesses and compiles the sources into one or more SPIR-V objects. User may also manually invoke a shader compiler (such as glslc also used by default) in order to deploy any modifications and additions:

glslc --target-env=vulkan -fhlsl_functionality1 -fshader-stage=fragment -x hlsl - fragment_shader.hlsl | spirv-opt -O -o fragment_shader.spv -

to compile a fragment shader fragment_shader.hlsl written in HLSL into a SPIR-V object fragment_shader.spv as an example. Here spirv-opt is used to performed the optimization as a post-processing step, as glslc does not preserve the reflection information during optimization. The output object should be placed into one of the command line provided shader lookup directories for it to become available.

The details on how to write shaders are beyond the scope of this manual. However, user may use the provided default.hlsl, which contains all the relevant shader stages in their simplest form to be used with Chamferwm, as a starting point.

In order to use shaders found in the lookup directories, they must be assigned to a client in the configuration script. See chamfer.Container.vertexShader and the respective attributes for geometry and fragment shaders on how to do this. All attributes must point to a valid shader object.