Skip to content

Commit

Permalink
Add new use resources methods (#228)
Browse files Browse the repository at this point in the history
* Add more use methods.

* Add bindless example

* Keep deprecated use mathods in render encoder
  • Loading branch information
mxpv authored Feb 13, 2022
1 parent 9c91373 commit d0454a0
Show file tree
Hide file tree
Showing 6 changed files with 396 additions and 15 deletions.
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

0 comments on commit d0454a0

Please sign in to comment.