Skip to content

Commit 6ce4bf5

Browse files
mrchanteycartpaul-hansen
authored
Add RenderTarget::TextureView (#8042)
# Objective We can currently set `camera.target` to either an `Image` or `Window`. For OpenXR & WebXR we need to be able to render to a `TextureView`. This partially addresses #115 as with the addition we can create internal and external xr crates. ## Solution A `TextureView` item is added to the `RenderTarget` enum. It holds an id which is looked up by a `ManualTextureViews` resource, much like how `Assets<Image>` works. I believe this approach was first used by @kcking in their [xr fork](https://github.com/kcking/bevy/blob/eb39afd51bcbab38de6efbeeb0646e01e2ce4766/crates/bevy_render/src/camera/camera.rs#L322). The only change is that a `u32` is used to index the textures as `FromReflect` does not support `uuid` and I don't know how to implement that. --- ## Changelog ### Added Render: Added `RenderTarget::TextureView` as a `camera.target` option, enabling rendering directly to a `TextureView`. ## Migration Guide References to the `RenderTarget` enum will need to handle the additional field, ie in `match` statements. --- ## Comments - The [wgpu work](gfx-rs/wgpu@c039a74) done by @expenses allows us to create framebuffer texture views from `wgpu v0.15, bevy 0.10`. - I got the WebXR techniques from the [xr fork](https://github.com/dekuraan/xr-bevy) by @dekuraan. - I have tested this with a wip [external webxr crate](https://github.com/mrchantey/forky/blob/018e22bb06b7542419db95f5332c7684931c9c95/crates/bevy_webxr/src/bevy_utils/xr_render.rs#L50) on an Oculus Quest 2. ![Screenshot 2023-03-11 230651](https://user-images.githubusercontent.com/25616826/224483696-c176c06f-a806-4abe-a494-b2e096ac96b7.png) --------- Co-authored-by: Carter Anderson <[email protected]> Co-authored-by: Paul Hansen <[email protected]>
1 parent 3959908 commit 6ce4bf5

File tree

4 files changed

+108
-8
lines changed

4 files changed

+108
-8
lines changed

crates/bevy_render/src/camera/camera.rs

Lines changed: 31 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
use crate::{
22
camera::CameraProjection,
3+
camera::{ManualTextureViewHandle, ManualTextureViews},
34
prelude::Image,
45
render_asset::RenderAssets,
56
render_resource::TextureView,
@@ -26,7 +27,6 @@ use bevy_utils::{HashMap, HashSet};
2627
use bevy_window::{
2728
NormalizedWindowRef, PrimaryWindow, Window, WindowCreated, WindowRef, WindowResized,
2829
};
29-
3030
use std::{borrow::Cow, ops::Range};
3131
use wgpu::{BlendState, Extent3d, LoadOp, TextureFormat};
3232

@@ -383,6 +383,9 @@ pub enum RenderTarget {
383383
Window(WindowRef),
384384
/// Image to which the camera's view is rendered.
385385
Image(Handle<Image>),
386+
/// Texture View to which the camera's view is rendered.
387+
/// Useful when the texture view needs to be created outside of Bevy, for example OpenXR.
388+
TextureView(ManualTextureViewHandle),
386389
}
387390

388391
/// Normalized version of the render target.
@@ -394,6 +397,9 @@ pub enum NormalizedRenderTarget {
394397
Window(NormalizedWindowRef),
395398
/// Image to which the camera's view is rendered.
396399
Image(Handle<Image>),
400+
/// Texture View to which the camera's view is rendered.
401+
/// Useful when the texture view needs to be created outside of Bevy, for example OpenXR.
402+
TextureView(ManualTextureViewHandle),
397403
}
398404

399405
impl Default for RenderTarget {
@@ -410,6 +416,7 @@ impl RenderTarget {
410416
.normalize(primary_window)
411417
.map(NormalizedRenderTarget::Window),
412418
RenderTarget::Image(handle) => Some(NormalizedRenderTarget::Image(handle.clone())),
419+
RenderTarget::TextureView(id) => Some(NormalizedRenderTarget::TextureView(*id)),
413420
}
414421
}
415422
}
@@ -419,6 +426,7 @@ impl NormalizedRenderTarget {
419426
&self,
420427
windows: &'a ExtractedWindows,
421428
images: &'a RenderAssets<Image>,
429+
manual_texture_views: &'a ManualTextureViews,
422430
) -> Option<&'a TextureView> {
423431
match self {
424432
NormalizedRenderTarget::Window(window_ref) => windows
@@ -427,6 +435,9 @@ impl NormalizedRenderTarget {
427435
NormalizedRenderTarget::Image(image_handle) => {
428436
images.get(image_handle).map(|image| &image.texture_view)
429437
}
438+
NormalizedRenderTarget::TextureView(id) => {
439+
manual_texture_views.get(id).map(|tex| &tex.texture_view)
440+
}
430441
}
431442
}
432443

@@ -435,6 +446,7 @@ impl NormalizedRenderTarget {
435446
&self,
436447
windows: &'a ExtractedWindows,
437448
images: &'a RenderAssets<Image>,
449+
manual_texture_views: &'a ManualTextureViews,
438450
) -> Option<TextureFormat> {
439451
match self {
440452
NormalizedRenderTarget::Window(window_ref) => windows
@@ -443,13 +455,17 @@ impl NormalizedRenderTarget {
443455
NormalizedRenderTarget::Image(image_handle) => {
444456
images.get(image_handle).map(|image| image.texture_format)
445457
}
458+
NormalizedRenderTarget::TextureView(id) => {
459+
manual_texture_views.get(id).map(|tex| tex.format)
460+
}
446461
}
447462
}
448463

449464
pub fn get_render_target_info<'a>(
450465
&self,
451466
resolutions: impl IntoIterator<Item = (Entity, &'a Window)>,
452467
images: &Assets<Image>,
468+
manual_texture_views: &ManualTextureViews,
453469
) -> Option<RenderTargetInfo> {
454470
match self {
455471
NormalizedRenderTarget::Window(window_ref) => resolutions
@@ -470,6 +486,12 @@ impl NormalizedRenderTarget {
470486
scale_factor: 1.0,
471487
})
472488
}
489+
NormalizedRenderTarget::TextureView(id) => {
490+
manual_texture_views.get(id).map(|tex| RenderTargetInfo {
491+
physical_size: tex.size,
492+
scale_factor: 1.0,
493+
})
494+
}
473495
}
474496
}
475497

@@ -486,6 +508,7 @@ impl NormalizedRenderTarget {
486508
NormalizedRenderTarget::Image(image_handle) => {
487509
changed_image_handles.contains(&image_handle)
488510
}
511+
NormalizedRenderTarget::TextureView(_) => true,
489512
}
490513
}
491514
}
@@ -509,13 +532,15 @@ impl NormalizedRenderTarget {
509532
/// [`OrthographicProjection`]: crate::camera::OrthographicProjection
510533
/// [`PerspectiveProjection`]: crate::camera::PerspectiveProjection
511534
/// [`Projection`]: crate::camera::Projection
535+
#[allow(clippy::too_many_arguments)]
512536
pub fn camera_system<T: CameraProjection + Component>(
513537
mut window_resized_events: EventReader<WindowResized>,
514538
mut window_created_events: EventReader<WindowCreated>,
515539
mut image_asset_events: EventReader<AssetEvent<Image>>,
516540
primary_window: Query<Entity, With<PrimaryWindow>>,
517541
windows: Query<(Entity, &Window)>,
518542
images: Res<Assets<Image>>,
543+
manual_texture_views: Res<ManualTextureViews>,
519544
mut cameras: Query<(&mut Camera, &mut T)>,
520545
) {
521546
let primary_window = primary_window.iter().next();
@@ -547,8 +572,11 @@ pub fn camera_system<T: CameraProjection + Component>(
547572
|| camera_projection.is_changed()
548573
|| camera.computed.old_viewport_size != viewport_size
549574
{
550-
camera.computed.target_info =
551-
normalized_target.get_render_target_info(&windows, &images);
575+
camera.computed.target_info = normalized_target.get_render_target_info(
576+
&windows,
577+
&images,
578+
&manual_texture_views,
579+
);
552580
if let Some(size) = camera.logical_viewport_size() {
553581
camera_projection.update(size.x, size.y);
554582
camera.computed.projection_matrix = camera_projection.get_projection_matrix();
Lines changed: 64 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,64 @@
1+
use crate::extract_resource::ExtractResource;
2+
use crate::render_resource::TextureView;
3+
use crate::texture::BevyDefault;
4+
use bevy_ecs::system::Resource;
5+
use bevy_ecs::{prelude::Component, reflect::ReflectComponent};
6+
use bevy_math::UVec2;
7+
use bevy_reflect::prelude::*;
8+
use bevy_reflect::FromReflect;
9+
use bevy_utils::HashMap;
10+
use wgpu::TextureFormat;
11+
12+
/// A unique id that corresponds to a specific [`ManualTextureView`] in the [`ManualTextureViews`] collection.
13+
#[derive(
14+
Default,
15+
Debug,
16+
Clone,
17+
Copy,
18+
PartialEq,
19+
Eq,
20+
Hash,
21+
PartialOrd,
22+
Ord,
23+
Component,
24+
Reflect,
25+
FromReflect,
26+
)]
27+
#[reflect(Component, Default)]
28+
pub struct ManualTextureViewHandle(pub u32);
29+
30+
/// A manually managed [`TextureView`] for use as a [`crate::camera::RenderTarget`].
31+
#[derive(Debug, Clone, Component)]
32+
pub struct ManualTextureView {
33+
pub texture_view: TextureView,
34+
pub size: UVec2,
35+
pub format: TextureFormat,
36+
}
37+
38+
impl ManualTextureView {
39+
pub fn with_default_format(texture_view: TextureView, size: UVec2) -> Self {
40+
Self {
41+
texture_view,
42+
size,
43+
format: TextureFormat::bevy_default(),
44+
}
45+
}
46+
}
47+
48+
/// Stores manually managed [`ManualTextureView`]s for use as a [`crate::camera::RenderTarget`].
49+
#[derive(Default, Clone, Resource, ExtractResource)]
50+
pub struct ManualTextureViews(HashMap<ManualTextureViewHandle, ManualTextureView>);
51+
52+
impl std::ops::Deref for ManualTextureViews {
53+
type Target = HashMap<ManualTextureViewHandle, ManualTextureView>;
54+
55+
fn deref(&self) -> &Self::Target {
56+
&self.0
57+
}
58+
}
59+
60+
impl std::ops::DerefMut for ManualTextureViews {
61+
fn deref_mut(&mut self) -> &mut Self::Target {
62+
&mut self.0
63+
}
64+
}

crates/bevy_render/src/camera/mod.rs

Lines changed: 9 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,13 +1,18 @@
11
#[allow(clippy::module_inception)]
22
mod camera;
33
mod camera_driver_node;
4+
mod manual_texture_view;
45
mod projection;
56

67
pub use camera::*;
78
pub use camera_driver_node::*;
9+
pub use manual_texture_view::*;
810
pub use projection::*;
911

10-
use crate::{render_graph::RenderGraph, ExtractSchedule, Render, RenderApp, RenderSet};
12+
use crate::{
13+
extract_resource::ExtractResourcePlugin, render_graph::RenderGraph, ExtractSchedule, Render,
14+
RenderApp, RenderSet,
15+
};
1116
use bevy_app::{App, Plugin};
1217
use bevy_ecs::schedule::IntoSystemConfigs;
1318

@@ -22,9 +27,11 @@ impl Plugin for CameraPlugin {
2227
.register_type::<ScalingMode>()
2328
.register_type::<CameraRenderGraph>()
2429
.register_type::<RenderTarget>()
30+
.init_resource::<ManualTextureViews>()
2531
.add_plugin(CameraProjectionPlugin::<Projection>::default())
2632
.add_plugin(CameraProjectionPlugin::<OrthographicProjection>::default())
27-
.add_plugin(CameraProjectionPlugin::<PerspectiveProjection>::default());
33+
.add_plugin(CameraProjectionPlugin::<PerspectiveProjection>::default())
34+
.add_plugin(ExtractResourcePlugin::<ManualTextureViews>::default());
2835

2936
if let Ok(render_app) = app.get_sub_app_mut(RenderApp) {
3037
render_app

crates/bevy_render/src/view/mod.rs

Lines changed: 4 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,7 @@ pub use visibility::*;
66
pub use window::*;
77

88
use crate::{
9-
camera::{ExtractedCamera, TemporalJitter},
9+
camera::{ExtractedCamera, ManualTextureViews, TemporalJitter},
1010
extract_resource::{ExtractResource, ExtractResourcePlugin},
1111
prelude::{Image, Shader},
1212
render_asset::RenderAssets,
@@ -414,13 +414,14 @@ fn prepare_view_targets(
414414
render_device: Res<RenderDevice>,
415415
mut texture_cache: ResMut<TextureCache>,
416416
cameras: Query<(Entity, &ExtractedCamera, &ExtractedView)>,
417+
manual_texture_views: Res<ManualTextureViews>,
417418
) {
418419
let mut textures = HashMap::default();
419420
for (entity, camera, view) in cameras.iter() {
420421
if let (Some(target_size), Some(target)) = (camera.physical_target_size, &camera.target) {
421422
if let (Some(out_texture_view), Some(out_texture_format)) = (
422-
target.get_texture_view(&windows, &images),
423-
target.get_texture_format(&windows, &images),
423+
target.get_texture_view(&windows, &images, &manual_texture_views),
424+
target.get_texture_format(&windows, &images, &manual_texture_views),
424425
) {
425426
let size = Extent3d {
426427
width: target_size.x,

0 commit comments

Comments
 (0)