Search code examples
graphicsvulkan

How to use multiple textures in Vulkan LLVMpipe (in Docker on CPU)


I develop offscreen Vulkan based render server to perform 2D scene drawing per request.

  • Target platform: Ubuntu 18.04 into Docker container
  • Physical device: llvmpipe (LLVM 11.0.1, 256 bits)

The scene consists of the same type of meshes and textures of different sizes. Each mesh is bound to its own texture. The maximum number of scene elements is 200. I have just 1 material (vertex + fragment shaders) so I use just 1 pipeline.

High level description of my workfllow:

1) Setup framebuffer and readback image
2) Load all meshes (VBOs and IBOs)
3) Load all textures (images, views, samplers)
4) Create descriptor set for material exposes (mesh transform and texture sampler)
5) Put per-mesh parameters to storage buffer (transform matrices)
6) Update fixed array of texture samplers. 
7) Draw each mesh
8) Send readback image to response.

Thats works great on dedicated GPU, llvmpipe does not support VK_EXT_descriptor_indexing and shaderSampledImageArrayDynamicIndexing feature. Its mean I cant indexing (in shaders) texture samler array by value from PushConstants.

#version 450

layout(set = 0, binding = 2) uniform sampler2D textures[200];
layout(push_constant) uniform Constants
{
    uint id;
} meta;

void main()
{
    // ... 
    vec4 t = texture(textures, uv);    // failed on llvmpipe 
    // ... 
}

To use only one sampler I need:

clear(framebuffer)
for mesh in meshes
{
    bind(mesh.vbo)
    bind(mesh.ibo)
    bind(descriptorset)
    update(sampler)       // write current mesh texture
    submit()
} 
read(readback) 
...

I dont understand how to setup renderpass to perform this steps. submit() in middle of this approach is confuse me.

Could you help me ?


Solution

  • I tried another approach that is based on StorageTexelBuffers.

     1. Get max size of texel storage from device limits
        (maxTexelBufferElements)
     2. Split scene data ito chunks limited by maxTexelBufferElements.
     3. Setup framebuffer and clear it 
     4. Draw a chunk[i]
     5. Read back result
    

    In this case samplers usage are not required. I put N images in 1D array and pass it to fragment shader. In the shader I calculate index of the specific texel and gather it by imageLoad(...)

    layout(location = 0) in vec2 uv;
    layout(set = 0, binding = 2, rgba32f) uniform imageBuffer texels;
    layout(push_constant) uniform Constants
    {
        uint id;
        uint textureStart;
        uint textureWidth;
        uint textureHeight;
    } meta;
    
    void main()
    {
        // calculate specific texel real coordinates
        uint s = uint(uv.x * float(meta.textureWidth));
        uint t = uint(uv.y * float(meta.textureHeight));
        
        // calculate texel index in global array 
        int index = int(meta.textureStart + s + t * meta.textureWidth);
        outColor = imageLoad(noise, tx);
    }
    

    Start of the texture is passed in PushConstants.