#vulkan #graphics #gpu #gaming #coding #low-level

nexg

Nexg 是一个纯 Rust 库,使编写 Vulkan 功能更加容易和 Rust 风格。

2 个版本

0.1.1 2024 年 2 月 9 日
0.1.0 2024 年 2 月 8 日

#184 in 图形 API

MIT 许可证

105KB
2.5K SLoC

Nexg(Next GPU)

底层快速 GPU API

GitHub License GitHub top language dependency status Crates.io Total Downloads GitHub code size in bytes GitHub Actions Workflow Status

Nexg 是一个纯 Rust 库,使编写 Vulkan 功能更加容易和 Rust 风格。

Nexg 旨在支持游戏应用以及在 GPU 上的操作

示例

三角形

triangle

use std::ffi::c_void;
use std::mem::offset_of;
use std::{env, fs::File, io::BufWriter};

use nexg::{
    Buffer, BufferDescriptor, CommandPoolDescriptor, CommandRecorderDescriptor, DataFormat,
    Extent3d, FrameBuffer, FrameBufferDescriptor, Image, ImageDescriptor, ImageFormat,
    ImageViewDescriptor, InstanceBuilder, InstanceFeature, LoadOp, Pipeline, PipelineDescriptor,
    PipelineLayout, PipelineLayoutDescriptor, PipelineVertexInputDescriptor, QueueSubmitDescriptor,
    RenderPass, RenderPassBeginDescriptor, RenderPassDescriptor, Shader, ShaderStage,
    ShaderStageDescriptor, Spirv, StoreOp, SubPass, SubPassDescriptor,
    VertexInputAttributeDescriptor, VertexInputBindingDescriptor,
};
use png::text_metadata::ZTXtChunk;

#[repr(C)]
#[derive(Clone, Copy, Debug)]
pub struct Vec4(f32, f32, f32, f32);
#[repr(C)]
#[derive(Clone, Copy, Debug)]
pub struct Vertex {
    pos: Vec4,
    color: Vec4,
}

const VERTEX: [Vertex; 3] = [
    Vertex {
        pos: Vec4(0.0, -0.5, 0.0, 0.0),
        color: Vec4(1.0, 0.0, 0.0, 1.0),
    },
    Vertex {
        pos: Vec4(0.5, 0.5, 0.0, 0.0),
        color: Vec4(0.0, 1.0, 0.0, 1.0),
    },
    Vertex {
        pos: Vec4(-0.5, 0.5, 0.0, 0.0),
        color: Vec4(0.0, 0.0, 1.0, 1.0),
    },
];

const WIDTH: u32 = 640;
const HEIGHT: u32 = 480;

fn main() {
    let feature = InstanceFeature::empty();
    let instance = InstanceBuilder::new().feature(feature).build().unwrap();
    
    // Request suitable connecter.
    let desc = RequestConnecterDescriptor::new()
        .graphic_support(true)
        .compute_support(true)
        .transfer_support(true);
    let connecters = instance.request_connecters(&[desc]).unwrap();
    let connecter = connecters[0];
    let index = connecter.get_queue_family_index();

    let device = connecter.create_device(&instance, index).unwrap();

    let queue = device.get_queue(index);
    let desc = CommandPoolDescriptor::empty().queue_family_index(index);
    let pool = device.create_command_pool(&desc).unwrap();
    let desc = CommandRecorderDescriptor::empty();
    let recorders = device.allocate_command_recorder(pool, &desc).unwrap();
    let desc = ImageDescriptor::new().extent(Extent3d::new(WIDTH, HEIGHT, 1));
    let image = Image::create(&instance, &device, connecter, &desc).unwrap();
    let desc = ImageViewDescriptor::empty().format(ImageFormat::R8G8B8A8Unorm);
    let image_view = image.create_image_view(&device, &desc);

    let vertex = Shader::new(
        &device,
        &Spirv::new(concat!(
            env!("CARGO_MANIFEST_DIR"),
            "/examples/shader/shader.vert.spv"
        ))
        .unwrap(),
    );

    let fragment = Shader::new(
        &device,
        &Spirv::new(concat!(
            env!("CARGO_MANIFEST_DIR"),
            "/examples/shader/shader.frag.spv"
        ))
        .unwrap(),
    );

    let desc = BufferDescriptor::empty().size(std::mem::size_of::<Vertex>() * VERTEX.len());
    let vertex_buffer = Buffer::new(&instance, connecter, &device, &desc).unwrap();
    vertex_buffer.write(&device, VERTEX.as_ptr() as *const c_void);
    vertex_buffer.lock(&device);

    let desc = SubPassDescriptor::empty();
    let subpass = SubPass::new(connecter, &desc);
    let subpasses = &[subpass];
    let desc = RenderPassDescriptor::empty()
        .subpasses(subpasses)
        .load_op(LoadOp::Clear)
        .store_op(StoreOp::Store);
    let render_pass = RenderPass::new(&device, &desc).unwrap();
    let desc = PipelineLayoutDescriptor::empty().render_pass(&render_pass);
    let pipeline_layout = PipelineLayout::new(&device, &desc).unwrap();
    let shader_stages = vec![
        ShaderStageDescriptor::empty()
            .entry_point("main")
            .stage(ShaderStage::Vertex)
            .shaders(&vertex),
        ShaderStageDescriptor::empty()
            .entry_point("main")
            .stage(ShaderStage::Fragment)
            .shaders(&fragment),
    ];
    let binding_desc = vec![VertexInputBindingDescriptor::empty()
        .binding(0)
        .stride(std::mem::size_of::<Vertex>())];
    let attribute_desc = vec![
        VertexInputAttributeDescriptor::empty()
            .binding(0)
            .location(0)
            .format(DataFormat::R32G32SFloat)
            .offset(offset_of!(Vertex, pos)),
        VertexInputAttributeDescriptor::empty()
            .binding(0)
            .location(1)
            .format(DataFormat::R32G32B32SFloat)
            .offset(offset_of!(Vertex, color)),
    ];
    let vertex_input_desc = PipelineVertexInputDescriptor::empty()
        .attribute_desc(&attribute_desc)
        .binding_desc(&binding_desc);
    let desc = PipelineDescriptor::empty()
        .shader_stages(&shader_stages)
        .input_descriptor(&vertex_input_desc)
        .width(WIDTH)
        .height(HEIGHT);
    let pipeline = Pipeline::new(&device, pipeline_layout, &render_pass, &desc).unwrap();

    let desc = FrameBufferDescriptor::empty()
        .render_pass(&render_pass)
        .image_view(&image_view)
        .width(WIDTH)
        .height(HEIGHT);
    let framebuffer = FrameBuffer::new(&device, &desc).unwrap();

    let begin_desc = RenderPassBeginDescriptor::empty()
        .width(WIDTH)
        .height(HEIGHT)
        .clear(1.0, 1.0, 1.0, 1.0)
        .render_pass(&render_pass)
        .frame_buffer(&framebuffer);
    recorders[0].begin(&device, begin_desc);
    recorders[0].bind_pipeline(&device, &pipeline[0]);
    recorders[0].bind_vertex_buffer(&device, &vertex_buffer);
    recorders[0].draw(&device, 3, 1, 0, 0);
    recorders[0].end(&device);

    let desc = QueueSubmitDescriptor::empty();
    queue.submit(&device, &desc, &recorders);

    let file = File::create("triangle.png").unwrap();
    let w = &mut BufWriter::new(file);

    let mut encoder = png::Encoder::new(w, WIDTH, HEIGHT);
    encoder.set_color(png::ColorType::Rgba);
    encoder.set_depth(png::BitDepth::Eight);

    let mut writer = encoder.write_header().unwrap();
    let slice = image.as_raw_data(&device, WIDTH, HEIGHT).unwrap();
    writer.write_image_data(&slice).unwrap(); // Save

    let tail_ztxt_chunk = ZTXtChunk::new(
        "Comment".to_string(),
        "A zTXt chunk after the image data.".to_string(),
    );
    writer.write_text_chunk(&tail_ztxt_chunk).unwrap();
}

注意

此 API 不是其他图形 API (DirectX, Metal) 的 抽象
它是一个使使用和 优化 Vulkan 功能 更容易的 API。

目标

  • 快速 API,开销低

许可证

Nexg 根据 MIT 许可证授权

依赖项

~6MB
~147K SLoC