Skip to content

Commit 000ddca

Browse files
authored
Support non-sRGB image formats for RenderTarget::Image (#22031)
# Objective - Fixes #15201 ## Solution - It seems there was some confusion where `OutputColorAttachment::format` could be different from the actual underlying texture format (of `OutputColorAttachment::view`). This would eventually lead to a texture format mismatch on the `BlitPipeline`. I removed the `OutputColorAttachment::format` field to resolve this conflict. - Sim. for `ManualTextureView`. ## Testing - The repro steps provided by #15201 no longer cause a WGPU validation error. - I tested the `pbr` example on native and WebGPU and it still works as before. ## Future Work Although the WGPU validation error is fixed, when using a non-sRGB texture format for the GPU image in the `headless_renderer` example, the output image seems to have incorrect gamma. I think this is because the example does a byte-for-byte copy from the GPU image to the CPU image without any gamma correction; and the `image` crate saves the image assuming a nonlinear color space. I don't think we need to fix this as part of this PR, but it might be nice for `bevy_image::Image::try_into_dynamic` to eventually handle the gamma correction when saving linear color images. <details> <summary>Here you can see a comparison</summary> With sRGB <img width="1920" height="1080" alt="with_srgb" src="https://github.com/user-attachments/assets/b6d96072-2754-4fc3-bb5a-fc2994e65dc0" /> No sRGB <img width="1920" height="1080" alt="no_srgb" src="https://github.com/user-attachments/assets/46f83f85-2202-40f6-9a68-85bdf4631494" /> </details>
1 parent f860ee7 commit 000ddca

File tree

5 files changed

+16
-32
lines changed

5 files changed

+16
-32
lines changed

crates/bevy_render/src/camera.rs

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -213,9 +213,9 @@ impl NormalizedRenderTargetExt for NormalizedRenderTarget {
213213
NormalizedRenderTarget::Image(image_target) => images
214214
.get(&image_target.handle)
215215
.map(|image| image.texture_format),
216-
NormalizedRenderTarget::TextureView(id) => {
217-
manual_texture_views.get(id).map(|tex| tex.format)
218-
}
216+
NormalizedRenderTarget::TextureView(id) => manual_texture_views
217+
.get(id)
218+
.map(|view| view.texture_view.texture().format()),
219219
NormalizedRenderTarget::None { .. } => None,
220220
}
221221
}

crates/bevy_render/src/texture/manual_texture_view.rs

Lines changed: 3 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -1,10 +1,8 @@
11
use bevy_camera::ManualTextureViewHandle;
22
use bevy_ecs::{prelude::Component, resource::Resource};
3-
use bevy_image::BevyDefault;
43
use bevy_math::UVec2;
54
use bevy_platform::collections::HashMap;
65
use bevy_render_macros::ExtractResource;
7-
use wgpu::TextureFormat;
86

97
use crate::render_resource::TextureView;
108

@@ -13,16 +11,11 @@ use crate::render_resource::TextureView;
1311
pub struct ManualTextureView {
1412
pub texture_view: TextureView,
1513
pub size: UVec2,
16-
pub format: TextureFormat,
1714
}
1815

1916
impl ManualTextureView {
20-
pub fn with_default_format(texture_view: TextureView, size: UVec2) -> Self {
21-
Self {
22-
texture_view,
23-
size,
24-
format: TextureFormat::bevy_default(),
25-
}
17+
pub fn new(texture_view: TextureView, size: UVec2) -> Self {
18+
Self { texture_view, size }
2619
}
2720
}
2821

@@ -36,7 +29,7 @@ impl ManualTextureView {
3629
/// # world.insert_resource(ManualTextureViews::default());
3730
/// # let texture_view = todo!();
3831
/// let manual_views = world.resource_mut::<ManualTextureViews>();
39-
/// let manual_view = ManualTextureView::with_default_format(texture_view, UVec2::new(1024, 1024));
32+
/// let manual_view = ManualTextureView::new(texture_view, UVec2::new(1024, 1024));
4033
///
4134
/// // Choose an unused handle value; it's likely only you are inserting manual views.
4235
/// const MANUAL_VIEW_HANDLE: ManualTextureViewHandle = ManualTextureViewHandle::new(42);

crates/bevy_render/src/texture/texture_attachment.rs

Lines changed: 2 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
use super::CachedTexture;
2-
use crate::render_resource::{TextureFormat, TextureView};
2+
use crate::render_resource::TextureView;
33
use alloc::sync::Arc;
44
use bevy_color::LinearRgba;
55
use core::sync::atomic::{AtomicBool, Ordering};
@@ -127,15 +127,13 @@ impl DepthAttachment {
127127
#[derive(Clone)]
128128
pub struct OutputColorAttachment {
129129
pub view: TextureView,
130-
pub format: TextureFormat,
131130
is_first_call: Arc<AtomicBool>,
132131
}
133132

134133
impl OutputColorAttachment {
135-
pub fn new(view: TextureView, format: TextureFormat) -> Self {
134+
pub fn new(view: TextureView) -> Self {
136135
Self {
137136
view,
138-
format,
139137
is_first_call: Arc::new(AtomicBool::new(true)),
140138
}
141139
}

crates/bevy_render/src/view/mod.rs

Lines changed: 3 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -838,7 +838,7 @@ impl ViewTarget {
838838
/// The format of the final texture this view will render to
839839
#[inline]
840840
pub fn out_texture_format(&self) -> TextureFormat {
841-
self.out_texture.format
841+
self.out_texture.view.texture().format()
842842
}
843843

844844
/// This will start a new "post process write", which assumes that the caller
@@ -1014,10 +1014,7 @@ pub fn prepare_view_attachments(
10141014
let Some(attachment) = target
10151015
.get_texture_view(&windows, &images, &manual_texture_views)
10161016
.cloned()
1017-
.zip(target.get_texture_format(&windows, &images, &manual_texture_views))
1018-
.map(|(view, format)| {
1019-
OutputColorAttachment::new(view.clone(), format.add_srgb_suffix())
1020-
})
1017+
.map(OutputColorAttachment::new)
10211018
else {
10221019
continue;
10231020
};
@@ -1086,11 +1083,7 @@ pub fn prepare_view_targets(
10861083
dimension: TextureDimension::D2,
10871084
format: main_texture_format,
10881085
usage: texture_usage.0,
1089-
view_formats: match main_texture_format {
1090-
TextureFormat::Bgra8Unorm => &[TextureFormat::Bgra8UnormSrgb],
1091-
TextureFormat::Rgba8Unorm => &[TextureFormat::Rgba8UnormSrgb],
1092-
_ => &[],
1093-
},
1086+
view_formats: &[],
10941087
};
10951088
let a = texture_cache.get(
10961089
&render_device,

crates/bevy_render/src/view/window/screenshot.rs

Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -295,7 +295,7 @@ fn prepare_screenshots(
295295
prepared.insert(*entity, state);
296296
view_target_attachments.insert(
297297
target.clone(),
298-
OutputColorAttachment::new(texture_view.clone(), format.add_srgb_suffix()),
298+
OutputColorAttachment::new(texture_view.clone()),
299299
);
300300
}
301301
NormalizedRenderTarget::Image(image) => {
@@ -315,7 +315,7 @@ fn prepare_screenshots(
315315
prepared.insert(*entity, state);
316316
view_target_attachments.insert(
317317
target.clone(),
318-
OutputColorAttachment::new(texture_view.clone(), format.add_srgb_suffix()),
318+
OutputColorAttachment::new(texture_view.clone()),
319319
);
320320
}
321321
NormalizedRenderTarget::TextureView(texture_view) => {
@@ -326,7 +326,7 @@ fn prepare_screenshots(
326326
);
327327
continue;
328328
};
329-
let format = manual_texture_view.format;
329+
let format = manual_texture_view.texture_view.texture().format();
330330
let size = manual_texture_view.size.to_extents();
331331
let (texture_view, state) = prepare_screenshot_state(
332332
size,
@@ -339,7 +339,7 @@ fn prepare_screenshots(
339339
prepared.insert(*entity, state);
340340
view_target_attachments.insert(
341341
target.clone(),
342-
OutputColorAttachment::new(texture_view.clone(), format.add_srgb_suffix()),
342+
OutputColorAttachment::new(texture_view.clone()),
343343
);
344344
}
345345
NormalizedRenderTarget::None { .. } => {
@@ -550,7 +550,7 @@ pub(crate) fn submit_screenshot_commands(world: &World, encoder: &mut CommandEnc
550550
};
551551
let width = texture_view.size.x;
552552
let height = texture_view.size.y;
553-
let texture_format = texture_view.format;
553+
let texture_format = texture_view.texture_view.texture().format();
554554
let texture_view = texture_view.texture_view.deref();
555555
render_screenshot(
556556
encoder,

0 commit comments

Comments
 (0)