-
Discover Metal for immersive apps
Find out how you can use Metal to render fully immersive experiences for visionOS. We'll show you how to set up a rendering session on the platform and create a basic render loop, and share how you can make your experience interactive by incorporating spatial input.
Capítulos
- 0:06 - Intro
- 1:50 - App Architecture
- 5:58 - Render configuration
- 11:29 - Render Loop
- 13:00 - Render a frame
- 17:28 - User input
- 20:22 - Wrap-Up
Recursos
Videos relacionados
WWDC23
- Build great games for spatial computing
- Go beyond the window with SwiftUI
- Meet ARKit for spatial computing
WWDC21
-
Buscar este video…
-
-
4:45 - App architecture
@main struct MyApp: App { var body: some Scene { ImmersiveSpace { CompositorLayer { layerRenderer in let engine = my_engine_create(layerRenderer) let renderThread = Thread { my_engine_render_loop(engine) } renderThread.name = "Render Thread" renderThread.start() } } } } -
10:32 - CompositorLayer Configuration
// CompositorLayer configuration struct MyConfiguration: CompositorLayerConfiguration { func makeConfiguration(capabilities: LayerRenderer.Capabilities, configuration: inout LayerRenderer.Configuration) { let supportsFoveation = capabilities.supportsFoveation let supportedLayouts = capabilities.supportedLayouts(options: supportsFoveation ? [.foveationEnabled] : []) configuration.layout = supportedLayouts.contains(.layered) ? .layered : .dedicated configuration.isFoveationEnabled = supportsFoveation // HDR support configuration.colorFormat = .rgba16Float } } -
12:20 - Render loop
void my_engine_render_loop(my_engine *engine) { my_engine_setup_render_pipeline(engine); bool is_rendering = true; while (is_rendering) @autoreleasepool { switch (cp_layer_renderer_get_state(engine->layer_renderer)) { case cp_layer_renderer_state_paused: cp_layer_renderer_wait_until_running(engine->layer_renderer); break; case cp_layer_renderer_state_running: my_engine_render_new_frame(engine); break; case cp_layer_renderer_state_invalidated: is_rendering = false; break; } } my_engine_invalidate(engine); } -
15:56 - Render new frame
void my_engine_render_new_frame(my_engine *engine) { cp_frame_t frame = cp_layer_renderer_query_next_frame(engine->layer_renderer); if (frame == nullptr) { return; } cp_frame_timing_t timing = cp_frame_predict_timing(frame); if (timing == nullptr) { return; } cp_frame_start_update(frame); my_input_state input_state = my_engine_gather_inputs(engine, timing); my_engine_update_frame(engine, timing, input_state); cp_frame_end_update(frame); // Wait until the optimal time for querying the input cp_time_wait_until(cp_frame_timing_get_optimal_input_time(timing)); cp_frame_start_submission(frame); cp_drawable_t drawable = cp_frame_query_drawable(frame); if (drawable == nullptr) { return; } cp_frame_timing_t final_timing = cp_drawable_get_frame_timing(drawable); ar_pose_t pose = my_engine_get_ar_pose(engine, final_timing); cp_drawable_set_ar_pose(drawable, pose); my_engine_draw_and_submit_frame(engine, frame, drawable); cp_frame_end_submission(frame); } -
18:57 - App architecture + input support
@main struct MyApp: App { var body: some Scene { ImmersiveSpace { CompositorLayer(configuration: MyConfiguration()) { layerRenderer in let engine = my_engine_create(layerRenderer) let renderThread = Thread { my_engine_render_loop(engine) } renderThread.name = "Render Thread" renderThread.start() layerRenderer.onSpatialEvent = { eventCollection in var events = eventCollection.map { my_spatial_event($0) } my_engine_push_spatial_events(engine, &events, events.count) } } } .upperLimbVisibility(.hidden) } } -
18:57 - Push spatial events
void my_engine_push_spatial_events(my_engine *engine, my_spatial_event *spatial_event_collection, size_t event_count) { os_unfair_lock_lock(&engine->input_event_lock); // Copy events into an internal queue os_unfair_lock_unlock(&engine->input_event_lock); } -
19:57 - Gather inputs
my_input_state my_engine_gather_inputs(my_engine *engine, cp_frame_timing_t timing) { my_input_state input_state = my_input_state_create(); os_unfair_lock_lock(&engine->input_event_lock); input_state.current_pinch_collection = my_engine_pop_spatial_events(engine); os_unfair_lock_unlock(&engine->input_event_lock); ar_hand_tracking_provider_get_latest_anchors(engine->hand_tracking_provider, input_state.left_hand, input_state.right_hand); return input_state; }
-