New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Uninizialised MipMaps for Immutable image after blitting. #989

Open
SiebenCorgie opened this Issue Jul 5, 2018 · 6 comments

Comments

Projects
None yet
2 participants
@SiebenCorgie
Copy link

SiebenCorgie commented Jul 5, 2018

Hi, I am trying to implement mip mapping for my small program. However, I can not find a way to make the mip maps work.
According to this it should be possible to upload the initial image at the mip level 0, and then blit the image down from level to level till we reach 1x1.

Now the problem:
If I do so, all levels except the initial one seam to be uninitialised.
The code I use to create the image including the mip maps from a Vec buffer:

///Takes some image data and creates a image with log2 mip maps from it.
pub fn create_mipmaped_image<P, I, F>(
    iter: I,
    dimensions: Dimensions,
    format: F,
    queue: Arc<Queue>
) -> Result<(
        Arc<ImmutableImage<F>>,
        CommandBufferExecFuture<NowFuture, AutoCommandBuffer>
    ), MipMapGenError>
    where P: Send + Sync + Clone + 'static,
    F: FormatDesc + AcceptsPixels<P> + 'static + Send + Sync,
    I: ExactSizeIterator<Item = P>,
    Format: AcceptsPixels<P>
{
    //First of all, copy the data into a buffer and then copy this buffer to the uninitialised image
    let source = CpuAccessibleBuffer::from_iter(
        queue.device().clone(), BufferUsage::transfer_source(), iter
    ).expect("Failed to generate CPU Buffer for image creation!");

    let usage = ImageUsage {
        transfer_destination: true,
        transfer_source: true,
        sampled: true,
        ..ImageUsage::none()
    };
    let layout = ImageLayout::ShaderReadOnlyOptimal;

    let (img, init) = ImmutableImage::uninitialized(
        source.device().clone(),
        dimensions,
        format,
        MipmapsCount::Log2,
        usage,
        layout,
        source.device().active_queue_families()
    ).expect("failed to create uninitialized image!");

    //Now copy the initial buffer to the image, then blit from one mip to the next lower
    //till we reached the bottom
    let device = source.device().clone();
    let mut cb = AutoCommandBufferBuilder::new(
        device.clone(), queue.family()
    ).expect("Failed to start command buffer for image creation!");

    cb = cb.copy_buffer_to_image_dimensions(
        source,
        init,
        [0, 0, 0],
        dimensions.width_height_depth(),
        0,
        dimensions.array_layers_with_cube(),
        0 //Mip 0 is start
    ).expect("failed to copy initial image to image_buffer!");

    let img_dimensions = img.clone().dimensions();

    //Now reduce and blit, start from one and always take the mip before as input
    for mip_idx in 1..img.clone().mipmap_levels(){
        let source_dim = if let Some(dim) = img_dimensions.mipmap_dimensions(mip_idx - 1){
            if let ImageDimensions::Dim2d{
                width,
                height,
                array_layers,
                cubemap_compatible,
            } = dim {
                [width as i32, height as i32, 1]
            }else{
                println!("MipMapping: Did not get 2D image for blitting", );
                return Err(MipMapGenError::No2dImage);
            }
        }else{
            println!("MipMapping: image has no mip map at level {} ", mip_idx - 1);
            return Err(MipMapGenError::NoMipDimensions);
        };

        let dest_dim = if let Some(dim) = img_dimensions.mipmap_dimensions(mip_idx){
            if let ImageDimensions::Dim2d{
                width,
                height,
                array_layers,
                cubemap_compatible,
            } = dim {
                [width as i32, height as i32, 1]
            }else{
                println!("MipMapping: Did not get 2D image for blitting", );
                return Err(MipMapGenError::No2dImage);
            }
        }else{
            println!("MipMapping: image has no mip map at level {} ", mip_idx);
            return Err(MipMapGenError::NoMipDimensions);
        };

        cb = cb.blit_image(
            img.clone(), //source
            [0; 3], //source_top_left
            source_dim, //source_bottom_right
            0, //source_base_array_layer
            mip_idx - 1, //source_mip_level
            img.clone(), //destination
            [0; 3], //destination_top_left
            dest_dim, //destination_bottom_right
            0, //destination_base_array_layer
            mip_idx, //destination_mip_level
            1, //layer_count
            Filter::Linear //filter
        ).expect("failed to blit a mip map to image!");
    }

    let final_cb = cb.build().expect("failed to build MipMapping command buffer");

    let future = match final_cb.execute(queue){
        Ok(f) => f,
        Err(_) => return Err(MipMapGenError::CouldNotAddExecution),
    };

    Ok((img, future))
}

Things I also tried:

  • Copy the buffer to the uninitialised image handle (ini) in one command buffer, execute this, then blit the mip maps. Problem: throws runtime error about conflicting CommandBufferBuilder sync.

  • Put the init in and arc and use it for the bliting operation as well: Problem: Doesn't change anything.

  • I tried to call then_signal_fence_and_flush() and wait() to actually wait for the upload/ execution, but it didn't change anything.

Now I am not sure if that is a bug or if I am misunderstanding something. I would appreciate any help/ info. If you need any more info I'd be happy to help.

@SiebenCorgie

This comment has been minimized.

Copy link

SiebenCorgie commented Jul 5, 2018

Here is a small image showing how it looks

screenshot from 2018-07-03 22-55-12

@SiebenCorgie SiebenCorgie reopened this Jul 5, 2018

@SiebenCorgie

This comment has been minimized.

Copy link

SiebenCorgie commented Jul 5, 2018

I had a closer look at the different levels now. It seams that level 0,1,2 are actually okay, starting from the third the artefacts are increasing.

This is for instance the third level:
level

@tomaka tomaka added the type: bug label Jul 8, 2018

@tomaka

This comment has been minimized.

Copy link
Member

tomaka commented Jul 8, 2018

Could you try looking at the content with a debugger, in order to be sure that it's a problem with the upload and not with the texture sampling method?

@SiebenCorgie

This comment has been minimized.

Copy link

SiebenCorgie commented Jul 8, 2018

Hi,
I had a look at the textures via RenderDoc on Windows. Starting from the third mip map there are artefacts as well. They are a bit different though, more like black dots on the surface. The older screenshots where taken on Linux.
However, the 5th and lower mip map seam to be not there at all, they are just black.
Unfortunately I couldn't get RenderDoc working on my linux distro yet.

Would it help you If I'd upload the Renderdoc capture? I could upload it to g-drive or something.

An example texture from the scene in render doc:
mipcap

@SiebenCorgie

This comment has been minimized.

Copy link

SiebenCorgie commented Jul 8, 2018

Never mind, I have a capture for Linux now as well.
The artefacts start at the same level here, but keep on being colourful till the last level. However, they have some patterns, I'll attach an example as well.

So If you want I could upload a linux capture as well :)

screenshot from 2018-07-08 20-54-02

@SiebenCorgie

This comment has been minimized.

Copy link

SiebenCorgie commented Jul 9, 2018

Hey,
today I started my app with the validation layers turned on (totally forgot about those, sorry :/ ). Judging by the warnings it seams like the layout transition of the different mipmaps of one image happen inappropriate.

There are two different validation errors:

Validation(ERROR): msg_code: 6: Object: 0x7fa294216d50 (Type = 6) | vkCmdBlitImage(): Cannot use image 0xe3b with specific layout VK_IMAGE_LAYOUT_TRANSFER_SRC_OPTIMAL that doesn't match the actual current layout VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL.
(null)(ERROR / SPEC): msgNum: 6 - vkCmdBlitImage(): Cannot use image 0xe3b with specific layout VK_IMAGE_LAYOUT_TRANSFER_SRC_OPTIMAL that doesn't match the actual current layout VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL.

and

Validation(ERROR): msg_code: 167774554: Object: 0x7fa294216d50 (Type = 6) | For image 0xe3b you cannot transition the layout of aspect=1 level=10 layer=0 from VK_IMAGE_LAYOUT_TRANSFER_SRC_OPTIMAL when current layout is VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL. The spec valid usage text states 'oldLayout must be VK_IMAGE_LAYOUT_UNDEFINED or the current layout of the image subresources affected by the barrier' (https://www.khronos.org/registry/vulkan/specs/1.0/html/vkspec.html#VUID-VkImageMemoryBarrier-oldLayout-01197)
(null)(ERROR / SPEC): msgNum: 167774554 - For image 0xe3b you cannot transition the layout of aspect=1 level=11 layer=0 from VK_IMAGE_LAYOUT_TRANSFER_SRC_OPTIMAL when current layout is VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL. The spec valid usage text states 'oldLayout must be VK_IMAGE_LAYOUT_UNDEFINED or the current layout of the image subresources affected by the barrier' (https://www.khronos.org/registry/vulkan/specs/1.0/html/vkspec.html#VUID-VkImageMemoryBarrier-oldLayout-01197)

In the latter the the number after level= goes from 0 up to the last mipmap level.

Both errors happen for every blit operation.

According to this it is possible to transition the layout per mip_map level.
So, would it be possible in vulkano to check if src_img and dest_img in the blitting operation are the same and then just transition the mip levels manually withing the blit function?

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment