Skip to content
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

Add new use resources methods #228

Merged
merged 3 commits into from
Feb 13, 2022
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 3 additions & 0 deletions Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -57,6 +57,9 @@ name = "caps"
[[example]]
name = "argument-buffer"

[[example]]
name = "bindless"

[[example]]
name = "circle"
path = "examples/circle/main.rs"
Expand Down
65 changes: 59 additions & 6 deletions examples/argument-buffer/main.rs
Original file line number Diff line number Diff line change
Expand Up @@ -12,24 +12,77 @@ fn main() {
autoreleasepool(|| {
let device = Device::system_default().expect("no device found");

/*

// Build encoder for the following MSL argument buffer:
struct ArgumentBuffer {
texture2d<float> texture [[id(0)]];
sampler sampler [[id(1)]];
array<device float *, 2> buffers [[id(2)]];
}

*/

let desc1 = ArgumentDescriptor::new();
desc1.set_index(0);
desc1.set_data_type(MTLDataType::Texture);
desc1.set_texture_type(MTLTextureType::D2);

let desc2 = ArgumentDescriptor::new();
desc2.set_data_type(MTLDataType::Sampler);
desc2.set_index(1);
desc2.set_data_type(MTLDataType::Sampler);

let desc3 = ArgumentDescriptor::new();
desc3.set_index(2);
desc3.set_data_type(MTLDataType::Pointer);
desc3.set_array_length(2);

let encoder = device.new_argument_encoder(&Array::from_slice(&[desc1, desc2]));
println!("{:?}", encoder);
let encoder = device.new_argument_encoder(Array::from_slice(&[desc1, desc2, desc3]));
println!("Encoder: {:?}", encoder);

let buffer = device.new_buffer(encoder.encoded_length(), MTLResourceOptions::empty());
encoder.set_argument_buffer(&buffer, 0);
let argument_buffer =
device.new_buffer(encoder.encoded_length(), MTLResourceOptions::empty());
encoder.set_argument_buffer(&argument_buffer, 0);

let sampler = {
let descriptor = SamplerDescriptor::new();
descriptor.set_support_argument_buffers(true);
device.new_sampler(&descriptor)
};
encoder.set_sampler_state(1, &sampler);
println!("{:?}", sampler);

let buffer1 = device.new_buffer(1024, MTLResourceOptions::empty());
println!("Buffer1: {:?}", buffer1);
let buffer2 = device.new_buffer(1024, MTLResourceOptions::empty());
println!("Buffer2: {:?}", buffer2);

encoder.set_sampler_state(1, &sampler);
encoder.set_buffer(2, &buffer1, 0);
encoder.set_buffer(3, &buffer2, 0);

// How to use argument buffer with render encoder.

let queue = device.new_command_queue();
let command_buffer = queue.new_command_buffer();

let render_pass_descriptor = RenderPassDescriptor::new();
let encoder = command_buffer.new_render_command_encoder(render_pass_descriptor);

// This method makes the array of resources resident for the selected stages of the render pass.
// Call this method before issuing any draw calls that may access the array of resources.
encoder.use_resources(
&[&buffer1, &buffer2],
MTLResourceUsage::Read,
MTLRenderStages::Vertex,
);
// Bind argument buffer to vertex stage.
encoder.set_vertex_buffer(0, Some(&argument_buffer), 0);

// Render pass here...

encoder.end_encoding();
println!("Encoder: {:?}", encoder);

command_buffer.commit();
});
}
149 changes: 149 additions & 0 deletions examples/bindless/main.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,149 @@
// Copyright 2017 GFX developers
//
// Licensed under the Apache License, Version 2.0, <LICENSE-APACHE or
// http://apache.org/licenses/LICENSE-2.0> or the MIT license <LICENSE-MIT or
// http://opensource.org/licenses/MIT>, at your option. This file may not be
// copied, modified, or distributed except according to those terms.

use metal::*;
use objc::rc::autoreleasepool;

const BINDLESS_TEXTURE_COUNT: NSUInteger = 100_000; // ~25Mb

/// This example demonstrates:
/// - How to create a heap
/// - How to allocate textures from heap.
/// - How to create bindless resources via Metal's argument buffers.
/// - How to bind argument buffer to render encoder
fn main() {
autoreleasepool(|| {
let device = Device::system_default().expect("no device found");

/*

MSL

struct Textures {
texture2d<float> texture;
};
struct BindlessTextures {
device Textures *textures;
};

*/

// Tier 2 argument buffers are supported by macOS devices with a discrete GPU and by the A13 GPU.
// The maximum per-app resources available at any given time are:
// - 500,000 buffers or textures
// - 2048 unique samplers
let tier = device.argument_buffers_support();
println!("Argument buffer support: {:?}", tier);
assert_eq!(MTLArgumentBuffersTier::Tier2, tier);

let texture_descriptor = TextureDescriptor::new();
texture_descriptor.set_width(1);
texture_descriptor.set_height(1);
texture_descriptor.set_depth(1);
texture_descriptor.set_texture_type(MTLTextureType::D2);
texture_descriptor.set_pixel_format(MTLPixelFormat::R8Uint);
texture_descriptor.set_storage_mode(MTLStorageMode::Private); // GPU only.
println!("Texture descriptor: {:?}", texture_descriptor);

// Determine the size required for the heap for the given descriptor
let size_and_align = device.heap_texture_size_and_align(&texture_descriptor);

// Align the size so that more resources will fit in the heap after this texture
// See https://developer.apple.com/documentation/metal/buffers/using_argument_buffers_with_resource_heaps
let texture_size =
(size_and_align.size & (size_and_align.align - 1)) + size_and_align.align;
let heap_size = texture_size * BINDLESS_TEXTURE_COUNT;

let heap_descriptor = HeapDescriptor::new();
heap_descriptor.set_storage_mode(texture_descriptor.storage_mode()); // Must be compatible
heap_descriptor.set_size(heap_size);
println!("Heap descriptor: {:?}", heap_descriptor);

let heap = device.new_heap(&heap_descriptor);
println!("Heap: {:?}", heap);

// Allocate textures from heap
let textures = (0..BINDLESS_TEXTURE_COUNT)
.map(|i| {
heap.new_texture(&texture_descriptor)
.expect(&format!("Failed to allocate texture {}", i))
})
.collect::<Vec<_>>();

// Crate argument encoder that knows how to encode single texture
let descriptor = ArgumentDescriptor::new();
descriptor.set_index(0);
descriptor.set_data_type(MTLDataType::Texture);
descriptor.set_texture_type(MTLTextureType::D2);
descriptor.set_access(MTLArgumentAccess::ReadOnly);
println!("Argument descriptor: {:?}", descriptor);

let encoder = device.new_argument_encoder(Array::from_slice(&[descriptor]));
println!("Encoder: {:?}", encoder);

// Determinate argument buffer size to allocate.
// Size needed to encode one texture * total number of bindless textures.
let argument_buffer_size = encoder.encoded_length() * BINDLESS_TEXTURE_COUNT;
let argument_buffer = device.new_buffer(argument_buffer_size, MTLResourceOptions::empty());

// Encode textures to the argument buffer.
textures.iter().enumerate().for_each(|(index, texture)| {
// Offset encoder to a proper texture slot
let offset = index as NSUInteger * encoder.encoded_length();
encoder.set_argument_buffer(&argument_buffer, offset);
encoder.set_texture(0, texture);
});

// How to use bindless argument buffer when drawing

let queue = device.new_command_queue();
let command_buffer = queue.new_command_buffer();

let render_pass_descriptor = RenderPassDescriptor::new();
let encoder = command_buffer.new_render_command_encoder(render_pass_descriptor);

// Bind argument buffer.
encoder.set_fragment_buffer(0, Some(&argument_buffer), 0);
// Make sure all textures are available to the pass.
encoder.use_heap_at(&heap, MTLRenderStages::Fragment);

// Bind material buffer at index 1
// Draw

/*

// Now instead of binding individual textures each draw call,
// you can just bind material information instead:

MSL

struct Material {
int diffuse_texture_index;
int normal_texture_index;
// ...
}

fragment float4 pixel(
VertexOut v [[stage_in]],
constant const BindlessTextures * textures [[buffer(0)]],
constant Material * material [[buffer(1)]]
) {
if (material->base_color_texture_index != -1) {
textures[material->diffuse_texture_index].texture.sampler(...)
}
if (material->normal_texture_index != -1) {
...
}
...
}

*/

encoder.end_encoding();
command_buffer.commit();
});
}
Loading