<p>This is a really hardware-dependent question. But in general the best way to think about this is bottlenecks.</p>

<p>You mentioned:</p>

<blockquote>
  <p>one set of shaders, one <strong>slightly</strong> larger vertex struct and just uniform, and sampler switches</p>
</blockquote>

<p>The slightly larger vertex struct doesn't seem like it will be the more likely bottleneck compared to:</p>

<blockquote>
  <p>With multiple shaders, the required program switches are likely to be in the order of 100's or possibly 1000's </p>
</blockquote>

<p>On modern desktop/console hardware each graphics pipe might have a single-digit # of active contexts. Switching the state too frequently is sometimes to referred to as a context roll stall.</p>

<p>In this case whether you hit that stall case is also dependent on how fast your shader and blend operations complete to make the context available. Given that you're going to do a lot of alpha blending, you might be bound by blending operations.</p>

<p>By the way modern GPUs are much better at dynamic branching, and skipping over fetches with a shader uniform is possible so you don't need to pay the additional memory reads. That said, a 1x1 dummy texture is going to be in the texture cache anyway so again it probably doesn't matter.</p>

<p>I'd say try using a single shader first before you introduce new shader permutations. And in general, do the easiest implementation first, measure on your target hardware, then optimize :) </p>

CC BY-SA 4.0
2018-12-02T02:40:51.407Z
2018-12-03T23:10:26.130Z
2018-12-03T23:10:26.130Z
Calvin
<p>I have some existing OpenGL code that I am porting to Vulkan. The question is somewhat relevant to both OpenGL and Vulkan.</p>

<p>I am rendering some primitives using textures, some single channel, others RGBA, as well as flat colored primitives with per-vertex colors. The GL code that I am porting frequently switches shader programs with <code>glUseProgram</code>.</p>

<p>In Vulkan, using two distinct sets of shaders requires two pipelines created with <code>vkCreateGraphicsPipelines</code> and later bound with <code>vkCmdBindPipeline</code> plus <code>vkCmdBindDescriptorSets</code>, thus changing shader programs equates program switches with graphics pipeline reconfiguration.</p>

<p>I am wondering whether it is efficient to switch to a single shader program and hence a single graphics pipeline with a vertex layout that contains texture coordinates, a color, and texture sampler. When I want to draw flat colored objects, the code would set the color in the vertex and bind a 1x1 white sampler and when I want to render images the code can bind a full image. I currently render the following:</p>

<ul>
<li>flat color using color from vertex</li>
<li>alpha from sampler and color from vertex</li>
<li>RGBA from sampler and alpha from vertex </li>
</ul>

<p>The question is from a hardware, driver/performance perspective, is it more efficient to use a single pipeline and descriptor layout and only performing uniform updates and have a slightly larger Vertex, or is switching pipelines low enough cost to warrant two or three pipelines and two of three sets of shaders and different vertex formats. i.e. where is the best balance between these concerns?</p>

<ul>
<li>three sets of shaders, three small vertex structs; program, uniform, and sampler switches</li>
<li>one set of shaders, one slightly larger vertex struct and just uniform, and sampler switches</li>
</ul>

<p>We could benchmark this but wonder if someone already has data to compare these approaches. I am aware of Ubershaders however this is hardly an Ubershader (see below) rather concerns any data on the ratio of program switch cost overhead versus the overhead of a 1x1 NOP texture lookups and texture color versus switching to a shader with no texture lookups.</p>

<p>With multiple shaders, the required program switches are likely to be in the order of 100's or possibly 1000's because of transparent content that requires a specific rendering order. In my case, it is for a <em>mostly</em> 2D app with a mix of line/block/bezier art, fonts using single alpha channel texture atlas plus vertex color and finally, RGBA images, typically just using the vertex alpha value and all currently using an orthographic projection with no lighting.</p>

<p>Ideally, I would like data on pipeline/program switch cost versus descriptor set switch cost.</p>

<p>For the shared program model, I am thinking about using the following:</p>

<p>This is the vertex struct:</p>

<pre><code>struct Vertex {
    glm::vec3 vertex;
    glm::vec2 texcoord;
    uint32_t color;
};
</code></pre>

<p>and this is the vertex shader:</p>

<pre><code>#version 450

layout (location = 0) in vec4 vcolor;
layout (location = 1) in vec2 vtexcoord;

layout (binding = 1) uniform sampler2D tex;

layout (location = 0) out vec4 outColor;

void main()
{
    outColor = vcolor * texture(tex, vtexcoord);
}
</code></pre>

<p>and this is the fragment shader:</p>

<pre><code>#version 450

layout (location = 0) in vec3 vertex;
layout (location = 1) in vec2 texcoord;
layout (location = 2) in vec4 color;

layout (binding = 0) uniform UBO 
{
    mat4 modelViewProjection;
} ubo;

layout (location = 0) out vec4 vcolor;
layout (location = 1) out vec2 vtexcoord;

out gl_PerVertex 
{
    vec4 gl_Position;
};

void main()
{
    vcolor = color;
    vtexcoord = texcoord;
    gl_Position = ubo.modelViewProjection * vec4(vertex.xyz, 1.0);
}
</code></pre>

<p>Note: as pointed out in the comments, swizzle masks in <code>VkImageViewCreateInfo</code> <code>components</code> field can be used to create <code>VkImageView</code> that translates the color and alpha channels instead of tieing shader code to the number of image channels in the sampler.</p>

<p>To broadcast the first color channel to all elements in OpenGL:</p>

<pre><code>glTexParameteriv(GL_TEXTURE_2D, GL_TEXTURE_SWIZZLE_RGBA,
    (GLint[]) { GL_RED, GL_RED, GL_RED, GL_RED } );
</code></pre>

<p>and for Vulkan, use the <code>VkImageViewCreateInfo</code> <code>components</code> field:</p>

<pre><code>VkImageViewCreateInfo imageViewInfo;
imageViewInfo.components = {
    VK_COMPONENT_SWIZZLE_R,
    VK_COMPONENT_SWIZZLE_R,
    VK_COMPONENT_SWIZZLE_R,
    VK_COMPONENT_SWIZZLE_R
};
</code></pre>

CC BY-SA 4.0
2018-11-28T03:13:28.453Z
"*I also use a matrix to transform textureColor as this allows me to configure the uniform for single channel alpha textures (texture(tex, vtexcoord).r) vs 4 channel RGBA textures (texture(tex, vtexcoord).rgba).*" You really should look into swizzle masks.
Nicol Bolas
CC BY-SA 4.0
2018-11-28T03:15:05.660Z
"*I read that 2x2 is required because 1x1 pixel samplers have different behaviors*" Where did you read this?
Nicol Bolas
CC BY-SA 4.0
2018-11-28T03:40:30.537Z
@NicolBolas I saw the reference to using 2x2 vs 1x1 here "[bug?] sampling of 1x1 texture just returns black" "Rescaling texture to the 2x2 size solves the problem." https://forums.ogre3d.org/viewtopic.php?t=52995 It could be an Ogre loader issue but it influenced my thinking towards using 2x2. Apparently it should be possible to use the so-called unbound sampler. Sampler 0 in OpenGL has defined semantics but I also read there are potentially driver bugs. I am not sure what is defined for Vulkan samplers and whether it has the equivalent of sampler 0 in OpenGL.
Michael Clark
CC BY-SA 4.0
2018-11-28T07:35:19.867Z
@NicolBolas 1x1 pixel works okay with Vulkan here. It is a power of two i.e. 2^0 = 1, so one would expect it to work, however, there is misleading information when searching regarding this topic (folk mentioning driver bugs). Unbound sampler in Vulkan on Nvidia 410.78 driver and GTX 1080 Ti returns a black pixel. Using a sampler with an incorrect descriptor config can lead to machine freezes with the current Nvidia Linux driver.
Michael Clark
CC BY-SA 4.0
2018-11-28T14:55:01.320Z
"*Perhaps there is a way to specify swizzle masks when creating a VkImage or VkSampler*" It's part of `VkImageView`, not of the image itself.
Nicol Bolas
CC BY-SA 4.0
2018-11-28T22:21:24.903Z
@NicolBolas Thanks for pointing out swizzling in VkImageView This is helpful as the linkage between `GL_TEXTURE_SWIZZLE_RGBA` and `components` in `VkImageView` is not immediately obvious. It is subtle. I'll edit the question. Vulkan tutorial leaves out this detail when covering VKImageView. "I've left out the explicit viewInfo.components initialization, because `VK_COMPONENT_SWIZZLE_IDENTITY` is defined as 0" however it is subtly implied if one reads the text closely enough https://vulkan-tutorial.com/Texture_mapping/Image_view_and_sampler
Michael Clark
<opengl><shader><texture><vulkan>
Is a single shader and 1x1 pixel white sampler more efficient than frequent switching between shaders with and without samplers?
Michael Clark
# Is a single shader and 1x1 pixel white sampler more efficient than frequent switching between shaders with and without samplers?

(Asked by: username_0 on 2018-11-28)

I have some existing OpenGL code that I am porting to Vulkan. The question is somewhat relevant to both OpenGL and Vulkan.

I am rendering some primitives using textures, some single channel, others RGBA, as well as flat colored primitives with per-vertex colors. The GL code that I am porting frequently switches shader programs with `glUseProgram`.

In Vulkan, using two distinct sets of shaders requires two pipelines created with `vkCreateGraphicsPipelines` and later bound with `vkCmdBindPipeline` plus `vkCmdBindDescriptorSets`, thus changing shader programs equates program switches with graphics pipeline reconfiguration.

I am wondering whether it is efficient to switch to a single shader program and hence a single graphics pipeline with a vertex layout that contains texture coordinates, a color, and texture sampler. When I want to draw flat colored objects, the code would set the color in the vertex and bind a 1x1 white sampler and when I want to render images the code can bind a full image. I currently render the following:

* flat color using color from vertex
* alpha from sampler and color from vertex
* RGBA from sampler and alpha from vertex

The question is from a hardware, driver/performance perspective, is it more efficient to use a single pipeline and descriptor layout and only performing uniform updates and have a slightly larger Vertex, or is switching pipelines low enough cost to warrant two or three pipelines and two of three sets of shaders and different vertex formats. i.e. where is the best balance between these concerns?

* three sets of shaders, three small vertex structs; program, uniform, and sampler switches
* one set of shaders, one slightly larger vertex struct and just uniform, and sampler switches

We could benchmark this but wonder if someone already has data to compare these approaches. I am aware of Ubershaders however this is hardly an Ubershader (see below) rather concerns any data on the ratio of program switch cost overhead versus the overhead of a 1x1 NOP texture lookups and texture color versus switching to a shader with no texture lookups.

With multiple shaders, the required program switches are likely to be in the order of 100's or possibly 1000's because of transparent content that requires a specific rendering order. In my case, it is for a *mostly* 2D app with a mix of line/block/bezier art, fonts using single alpha channel texture atlas plus vertex color and finally, RGBA images, typically just using the vertex alpha value and all currently using an orthographic projection with no lighting.

Ideally, I would like data on pipeline/program switch cost versus descriptor set switch cost.

For the shared program model, I am thinking about using the following:

This is the vertex struct:

```
struct Vertex {
    glm::vec3 vertex;
    glm::vec2 texcoord;
    uint32_t color;
};

```

and this is the vertex shader:

```
#version 450

layout (location = 0) in vec4 vcolor;
layout (location = 1) in vec2 vtexcoord;

layout (binding = 1) uniform sampler2D tex;

layout (location = 0) out vec4 outColor;

void main()
{
    outColor = vcolor * texture(tex, vtexcoord);
}

```

and this is the fragment shader:

```
#version 450

layout (location = 0) in vec3 vertex;
layout (location = 1) in vec2 texcoord;
layout (location = 2) in vec4 color;

layout (binding = 0) uniform UBO 
{
    mat4 modelViewProjection;
} ubo;

layout (location = 0) out vec4 vcolor;
layout (location = 1) out vec2 vtexcoord;

out gl_PerVertex 
{
    vec4 gl_Position;
};

void main()
{
    vcolor = color;
    vtexcoord = texcoord;
    gl_Position = ubo.modelViewProjection * vec4(vertex.xyz, 1.0);
}

```

Note: as pointed out in the comments, swizzle masks in `VkImageViewCreateInfo` `components` field can be used to create `VkImageView` that translates the color and alpha channels instead of tieing shader code to the number of image channels in the sampler.

To broadcast the first color channel to all elements in OpenGL:

```
glTexParameteriv(GL_TEXTURE_2D, GL_TEXTURE_SWIZZLE_RGBA,
    (GLint[]) { GL_RED, GL_RED, GL_RED, GL_RED } );

```

and for Vulkan, use the `VkImageViewCreateInfo` `components` field:

```
VkImageViewCreateInfo imageViewInfo;
imageViewInfo.components = {
    VK_COMPONENT_SWIZZLE_R,
    VK_COMPONENT_SWIZZLE_R,
    VK_COMPONENT_SWIZZLE_R,
    VK_COMPONENT_SWIZZLE_R
};

```

**Tags:** opengl, shader, texture, vulkan

> **Comments:**
> **username_1**: "*I also use a matrix to transform textureColor as this allows me to configure the uniform for single channel alpha textures (texture(tex, vtexcoord).r) vs 4 channel RGBA textures (texture(tex, vtexcoord).rgba).*" You really should look into swizzle masks.  
> **username_1**: "*I read that 2x2 is required because 1x1 pixel samplers have different behaviors*" Where did you read this?  
> **username_0**: @username_1 I saw the reference to using 2x2 vs 1x1 here "[bug?] sampling of 1x1 texture just returns black" "Rescaling texture to the 2x2 size solves the problem." https://forums.ogre3d.org/viewtopic.php?t=52995 It could be an Ogre loader issue but it influenced my thinking towards using 2x2. Apparently it should be possible to use the so-called unbound sampler. Sampler 0 in OpenGL has defined semantics but I also read there are potentially driver bugs. I am not sure what is defined for Vulkan samplers and whether it has the equivalent of sampler 0 in OpenGL.  
> **username_0**: @username_1 1x1 pixel works okay with Vulkan here. It is a power of two i.e. 2^0 = 1, so one would expect it to work, however, there is misleading information when searching regarding this topic (folk mentioning driver bugs). Unbound sampler in Vulkan on Nvidia 410.78 driver and GTX 1080 Ti returns a black pixel. Using a sampler with an incorrect descriptor config can lead to machine freezes with the current Nvidia Linux driver.  
> **username_1**: "*Perhaps there is a way to specify swizzle masks when creating a VkImage or VkSampler*" It's part of `VkImageView`, not of the image itself.  
> **username_0**: @username_1 Thanks for pointing out swizzling in VkImageView This is helpful as the linkage between `GL_TEXTURE_SWIZZLE_RGBA` and `components` in `VkImageView` is not immediately obvious. It is subtle. I'll edit the question. Vulkan tutorial leaves out this detail when covering VKImageView. "I've left out the explicit viewInfo.components initialization, because `VK_COMPONENT_SWIZZLE_IDENTITY` is defined as 0" however it is subtly implied if one reads the text closely enough https://vulkan-tutorial.com/Texture_mapping/Image_view_and_sampler  

---

## 1 answers

### Answer by username_2 on 2018-12-02. Score: 6 | Accepted answer

This is a really hardware-dependent question. But in general the best way to think about this is bottlenecks.

You mentioned:

> one set of shaders, one **slightly** larger vertex struct and just uniform, and sampler switches

The slightly larger vertex struct doesn't seem like it will be the more likely bottleneck compared to:

> With multiple shaders, the required program switches are likely to be in the order of 100's or possibly 1000's

On modern desktop/console hardware each graphics pipe might have a single-digit # of active contexts. Switching the state too frequently is sometimes to referred to as a context roll stall.

In this case whether you hit that stall case is also dependent on how fast your shader and blend operations complete to make the context available. Given that you're going to do a lot of alpha blending, you might be bound by blending operations.

By the way modern GPUs are much better at dynamic branching, and skipping over fetches with a shader uniform is possible so you don't need to pay the additional memory reads. That said, a 1x1 dummy texture is going to be in the texture cache anyway so again it probably doesn't matter.

I'd say try using a single shader first before you introduce new shader permutations. And in general, do the easiest implementation first, measure on your target hardware, then optimize :)


---

