以OpenGL/ES視角介紹gfx-hal(Vulkan) Texture介面使用

熊皮皮發表於2018-11-28

文件列表見:Rust 移動端跨平臺複雜圖形渲染專案開發系列總結(目錄)

就OpenGL、OpenGL ES、gfx-hal(1:1仿Vulkan介面定義)等KHR定義的圖形庫介面而言,Texture都有建立、上傳資料、下載繪製結果資料、作為Framebuffer的掛載點等操作,下面分別介紹。

OpenGL(ES)的Texture對應到Vulkan分別是Image、ImageView、Memory、Sampler,上傳/下載Image的資料還需要配合Buffer,涉及到非常多細節。如果是剛開始學習圖形開發,不建議使用Vulkan。

建立紋理的整體流程:

  1. 建立Image
  2. 建立Image關聯的Memory
  3. 關聯Image到Memory

上傳資料到紋理的整體流程:

  1. 建立Staging Buffer
  2. 建立Fence
  3. 建立用於資料拷貝的Submmit
    • 建立Command Buffer
    • 建立Barrier
    • 向Command Buffer提交Barrier
    • 向Command Buffer提交Copy Buffer to Image命令
    • 結束Command Buffer編碼
  4. 提交Submmit到GPU命令佇列

在Shader使用紋理的整體流程:

  1. 建立ImageView
  2. 建立Sampler
  3. 建立、配置DescriptorSet、DescriptorSetLayout

下載繪製結果資料的整體流程:

待續

作為Framebuffer掛載點的整體流程:

待續

建立紋理的整體流程

建立Image

let kind = image::Kind::D2(dims.width as image::Size, dims.height as image::Size, 1 /* Layer */, 1 /* NumSamples */);
let unbound = device.create_image(
        kind,
        1 /* mip_levels */,
        ColorFormat::SELF,
        image::Tiling::Optimal,
        image::Usage::TRANSFER_DST | image::Usage::SAMPLED,
        image::StorageFlags::empty(),
    )
    .unwrap();
複製程式碼

建立Image關聯的Memory

let req = device.get_image_requirements(&unbound);

let device_type = adapter
    .memory_types
    .iter()
    .enumerate()
    .position(|(id, memory_type)| {
        req.type_mask & (1 << id) != 0 && memory_type.properties.contains(memory::Properties::DEVICE_LOCAL)
    })
    .unwrap()
    .into();

let memory = device.allocate_memory(device_type, req.size).unwrap();
複製程式碼

關聯Image到Memory

let image = device.bind_image_memory(&memory, 0, unbound).unwrap();
複製程式碼

上傳資料到紋理的整體流程

建立傳輸Fence

let mut transfered_image_fence = device.create_fence(false);
複製程式碼

上傳CPU資料到紋理

// copy buffer to texture
{
    let submit = {
        let mut cmd_buffer = staging_pool.acquire_command_buffer(false);

        let image_barrier = memory::Barrier::Image {
            states: (image::Access::empty(), image::Layout::Undefined)
                ..(image::Access::TRANSFER_WRITE, image::Layout::TransferDstOptimal),
            target: &image,
            range: COLOR_RANGE.clone(),
        };

        cmd_buffer.pipeline_barrier(
            PipelineStage::TOP_OF_PIPE..PipelineStage::TRANSFER,
            memory::Dependencies::empty(),
            &[image_barrier],
        );

        cmd_buffer.copy_buffer_to_image(
            buffer.as_ref().unwrap().get_buffer(),
            &image,
            image::Layout::TransferDstOptimal,
            &[command::BufferImageCopy {
                buffer_offset: 0,
                buffer_width: row_pitch / (stride as u32),
                buffer_height: dims.height as u32,
                image_layers: image::SubresourceLayers {
                    aspects: f::Aspects::COLOR,
                    level: 0,
                    layers: 0..1,
                },
                image_offset: image::Offset { x: 0, y: 0, z: 0 },
                image_extent: image::Extent {
                    width: dims.width,
                    height: dims.height,
                    depth: 1,
                },
            }],
        );

        let image_barrier = memory::Barrier::Image {
            states: (image::Access::TRANSFER_WRITE, image::Layout::TransferDstOptimal)
                ..(image::Access::SHADER_READ, image::Layout::ShaderReadOnlyOptimal),
            target: &image,
            range: COLOR_RANGE.clone(),
        };
        cmd_buffer.pipeline_barrier(
            PipelineStage::TRANSFER..PipelineStage::FRAGMENT_SHADER,
            memory::Dependencies::empty(),
            &[image_barrier],
        );

        cmd_buffer.finish()
    };

    let submission = Submission::new().submit(Some(submit));
    device_state.queues.queues[0].submit(submission, Some(&mut transfered_image_fence));
}
複製程式碼

在Shader使用紋理的整體流程

建立ImageView

let image_view = device.create_image_view(
        &image,
        image::ViewKind::D2,
        ColorFormat::SELF,
        Swizzle::NO,
        COLOR_RANGE.clone(),
    )
    .unwrap();
複製程式碼

建立Sampler

let sampler = device.create_sampler(image::SamplerInfo::new(image::Filter::Linear, image::WrapMode::Clamp));
複製程式碼

配置DescriptorSet

device.write_descriptor_sets(vec![
    pso::DescriptorSetWrite {
        set: &desc_set,
        binding: 0,
        array_offset: 0,
        descriptors: Some(pso::Descriptor::Image(&image_srv, image::Layout::Undefined)),
    },
    pso::DescriptorSetWrite {
        set: &desc_set,
        binding: 1,
        array_offset: 0,
        descriptors: Some(pso::Descriptor::Sampler(&sampler)),
    },
]);
複製程式碼

下載繪製結果資料的整體流程

待續

作為Framebuffer掛載點的整體流程

待續

相關文章