Use the swapchain-preferred texture format by default (#182)

- Fixes #140
- Adds a public method to get the current GPU framebuffer texture format (AKA the render texture format).
- This wasn't as difficult as it seemed; the extra API is used by the examples to get the right texture format instead of being hardcoded.
This commit is contained in:
Jay Oster 2021-07-17 08:21:49 -07:00 committed by GitHub
parent ce549a79b6
commit 288da3675f
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
7 changed files with 59 additions and 35 deletions

View file

@ -45,7 +45,7 @@ fn main() -> Result<(), Error> {
}; };
let mut world = World::new(); let mut world = World::new();
let mut time = 0.0; let mut time = 0.0;
let mut noise_renderer = NoiseRenderer::new(pixels.device(), WIDTH, HEIGHT); let mut noise_renderer = NoiseRenderer::new(&pixels, WIDTH, HEIGHT);
event_loop.run(move |event, _, control_flow| { event_loop.run(move |event, _, control_flow| {
// Draw the current frame // Draw the current frame
@ -82,7 +82,7 @@ fn main() -> Result<(), Error> {
// Resize the window // Resize the window
if let Some(size) = input.window_resized() { if let Some(size) = input.window_resized() {
pixels.resize_surface(size.width, size.height); pixels.resize_surface(size.width, size.height);
noise_renderer.resize(pixels.device(), size.width, size.height); noise_renderer.resize(&pixels, size.width, size.height);
} }
// Update internal state and request a redraw // Update internal state and request a redraw

View file

@ -11,13 +11,14 @@ pub(crate) struct NoiseRenderer {
} }
impl NoiseRenderer { impl NoiseRenderer {
pub(crate) fn new(device: &wgpu::Device, width: u32, height: u32) -> Self { pub(crate) fn new(pixels: &pixels::Pixels, width: u32, height: u32) -> Self {
let device = pixels.device();
let shader = wgpu::include_wgsl!("../shaders/noise.wgsl"); let shader = wgpu::include_wgsl!("../shaders/noise.wgsl");
let module = device.create_shader_module(&shader); let module = device.create_shader_module(&shader);
// Create a texture view that will be used as input // Create a texture view that will be used as input
// This will be used as the render target for the default scaling renderer // This will be used as the render target for the default scaling renderer
let texture_view = create_texture_view(device, width, height); let texture_view = create_texture_view(pixels, width, height);
// Create a texture sampler with nearest neighbor // Create a texture sampler with nearest neighbor
let sampler = device.create_sampler(&wgpu::SamplerDescriptor { let sampler = device.create_sampler(&wgpu::SamplerDescriptor {
@ -137,7 +138,7 @@ impl NoiseRenderer {
module: &module, module: &module,
entry_point: "fs_main", entry_point: "fs_main",
targets: &[wgpu::ColorTargetState { targets: &[wgpu::ColorTargetState {
format: wgpu::TextureFormat::Bgra8UnormSrgb, format: pixels.render_texture_format(),
blend: Some(wgpu::BlendState { blend: Some(wgpu::BlendState {
color: wgpu::BlendComponent::REPLACE, color: wgpu::BlendComponent::REPLACE,
alpha: wgpu::BlendComponent::REPLACE, alpha: wgpu::BlendComponent::REPLACE,
@ -162,10 +163,10 @@ impl NoiseRenderer {
&self.texture_view &self.texture_view
} }
pub(crate) fn resize(&mut self, device: &wgpu::Device, width: u32, height: u32) { pub(crate) fn resize(&mut self, pixels: &pixels::Pixels, width: u32, height: u32) {
self.texture_view = create_texture_view(device, width, height); self.texture_view = create_texture_view(pixels, width, height);
self.bind_group = create_bind_group( self.bind_group = create_bind_group(
device, pixels.device(),
&self.bind_group_layout, &self.bind_group_layout,
&self.texture_view, &self.texture_view,
&self.sampler, &self.sampler,
@ -203,7 +204,8 @@ impl NoiseRenderer {
} }
} }
fn create_texture_view(device: &wgpu::Device, width: u32, height: u32) -> wgpu::TextureView { fn create_texture_view(pixels: &pixels::Pixels, width: u32, height: u32) -> wgpu::TextureView {
let device = pixels.device();
let texture_descriptor = wgpu::TextureDescriptor { let texture_descriptor = wgpu::TextureDescriptor {
label: None, label: None,
size: pixels::wgpu::Extent3d { size: pixels::wgpu::Extent3d {
@ -214,7 +216,7 @@ fn create_texture_view(device: &wgpu::Device, width: u32, height: u32) -> wgpu::
mip_level_count: 1, mip_level_count: 1,
sample_count: 1, sample_count: 1,
dimension: wgpu::TextureDimension::D2, dimension: wgpu::TextureDimension::D2,
format: wgpu::TextureFormat::Bgra8UnormSrgb, format: pixels.render_texture_format(),
usage: wgpu::TextureUsage::SAMPLED | wgpu::TextureUsage::RENDER_ATTACHMENT, usage: wgpu::TextureUsage::SAMPLED | wgpu::TextureUsage::RENDER_ATTACHMENT,
}; };

View file

@ -19,7 +19,7 @@ pub(crate) struct Gui {
impl Gui { impl Gui {
/// Create egui. /// Create egui.
pub(crate) fn new(width: u32, height: u32, scale_factor: f64, context: &PixelsContext) -> Self { pub(crate) fn new(width: u32, height: u32, scale_factor: f64, pixels: &pixels::Pixels) -> Self {
let platform = Platform::new(PlatformDescriptor { let platform = Platform::new(PlatformDescriptor {
physical_width: width, physical_width: width,
physical_height: height, physical_height: height,
@ -32,7 +32,7 @@ impl Gui {
physical_height: height, physical_height: height,
scale_factor: scale_factor as f32, scale_factor: scale_factor as f32,
}; };
let rpass = RenderPass::new(&context.device, wgpu::TextureFormat::Bgra8UnormSrgb, 1); let rpass = RenderPass::new(pixels.device(), pixels.render_texture_format(), 1);
Self { Self {
start_time: Instant::now(), start_time: Instant::now(),

View file

@ -43,12 +43,7 @@ fn main() -> Result<(), Error> {
let scale_factor = window.scale_factor(); let scale_factor = window.scale_factor();
let surface_texture = SurfaceTexture::new(window_size.width, window_size.height, &window); let surface_texture = SurfaceTexture::new(window_size.width, window_size.height, &window);
let pixels = Pixels::new(WIDTH, HEIGHT, surface_texture)?; let pixels = Pixels::new(WIDTH, HEIGHT, surface_texture)?;
let gui = Gui::new( let gui = Gui::new(window_size.width, window_size.height, scale_factor, &pixels);
window_size.width,
window_size.height,
scale_factor,
pixels.context(),
);
(pixels, gui) (pixels, gui)
}; };

View file

@ -44,9 +44,8 @@ impl Gui {
// Create Dear ImGui WGPU renderer // Create Dear ImGui WGPU renderer
let device = pixels.device(); let device = pixels.device();
let queue = pixels.queue(); let queue = pixels.queue();
let texture_format = wgpu::TextureFormat::Bgra8UnormSrgb;
let config = imgui_wgpu::RendererConfig { let config = imgui_wgpu::RendererConfig {
texture_format, texture_format: pixels.render_texture_format(),
..Default::default() ..Default::default()
}; };
let renderer = imgui_wgpu::Renderer::new(&mut imgui, device, queue, config); let renderer = imgui_wgpu::Renderer::new(&mut imgui, device, queue, config);

View file

@ -15,7 +15,7 @@ pub struct PixelsBuilder<'req, 'dev, 'win, W: HasRawWindowHandle> {
present_mode: wgpu::PresentMode, present_mode: wgpu::PresentMode,
surface_texture: SurfaceTexture<'win, W>, surface_texture: SurfaceTexture<'win, W>,
texture_format: wgpu::TextureFormat, texture_format: wgpu::TextureFormat,
render_texture_format: wgpu::TextureFormat, render_texture_format: Option<wgpu::TextureFormat>,
} }
impl<'req, 'dev, 'win, W: HasRawWindowHandle> PixelsBuilder<'req, 'dev, 'win, W> { impl<'req, 'dev, 'win, W: HasRawWindowHandle> PixelsBuilder<'req, 'dev, 'win, W> {
@ -58,7 +58,7 @@ impl<'req, 'dev, 'win, W: HasRawWindowHandle> PixelsBuilder<'req, 'dev, 'win, W>
present_mode: wgpu::PresentMode::Fifo, present_mode: wgpu::PresentMode::Fifo,
surface_texture, surface_texture,
texture_format: wgpu::TextureFormat::Rgba8UnormSrgb, texture_format: wgpu::TextureFormat::Rgba8UnormSrgb,
render_texture_format: wgpu::TextureFormat::Bgra8UnormSrgb, render_texture_format: None,
} }
} }
@ -82,8 +82,7 @@ impl<'req, 'dev, 'win, W: HasRawWindowHandle> PixelsBuilder<'req, 'dev, 'win, W>
/// Set which backends wgpu will attempt to use. /// Set which backends wgpu will attempt to use.
/// ///
/// The default value of this is [`wgpu::BackendBit::PRIMARY`], which enables /// The default value is `PRIMARY`, which enables the well supported backends for wgpu.
/// the well supported backends for wgpu.
pub fn wgpu_backend(mut self, backend: wgpu::BackendBit) -> PixelsBuilder<'req, 'dev, 'win, W> { pub fn wgpu_backend(mut self, backend: wgpu::BackendBit) -> PixelsBuilder<'req, 'dev, 'win, W> {
self.backend = backend; self.backend = backend;
self self
@ -144,9 +143,12 @@ impl<'req, 'dev, 'win, W: HasRawWindowHandle> PixelsBuilder<'req, 'dev, 'win, W>
/// Set the texture format. /// Set the texture format.
/// ///
/// The default value is [`wgpu::TextureFormat::Rgba8UnormSrgb`], which is 4 unsigned bytes in /// The default value is `Rgba8UnormSrgb`, which is 4 unsigned bytes in `RGBA` order using the
/// `RGBA` order using the SRGB color space. This is typically what you want when you are /// sRGB color space. This is typically what you want when you are working with color values
/// working with color values from popular image editing tools or web apps. /// from popular image editing tools or web apps.
///
/// This is the pixel format of the texture that most applications will interact with directly.
/// The format influences the structure of byte data that is returned by [`Pixels::get_frame`].
pub fn texture_format( pub fn texture_format(
mut self, mut self,
texture_format: wgpu::TextureFormat, texture_format: wgpu::TextureFormat,
@ -157,14 +159,27 @@ impl<'req, 'dev, 'win, W: HasRawWindowHandle> PixelsBuilder<'req, 'dev, 'win, W>
/// Set the render texture format. /// Set the render texture format.
/// ///
/// The default value is [`wgpu::TextureFormat::Bgra8UnormSrgb`], which is 4 unsigned bytes in /// The default value is chosen automatically by the swapchain (if it can) with a fallback to
/// `BGRA` order using the SRGB color space. This format depends on the hardware/platform the /// `Bgra8UnormSrgb` (which is 4 unsigned bytes in `BGRA` order using the sRGB color space).
/// pixel buffer is rendered to/for. /// Setting this format correctly depends on the hardware/platform the pixel buffer is rendered
/// to. The chosen format can be retrieved later with [`Pixels::render_texture_format`].
///
/// This method controls the format of the swapchain frame buffer, which has strict texture
/// format requirements. Applications will never interact directly with the pixel data of this
/// texture, but a view is provided to the `render_function` closure by [`Pixels::render_with`].
/// The render texture can only be used as the final render target at the end of all
/// post-processing shaders.
///
/// The [`ScalingRenderer`] also uses this format for its own render target. This is because it
/// assumes the render target is always the swapchain current frame. This needs to be kept in
/// mind when writing custom shaders for post-processing effects. There is a full example of a
/// [custom-shader](https://github.com/parasyte/pixels/tree/master/examples/custom-shader)
/// available that demonstrates how to deal with this.
pub fn render_texture_format( pub fn render_texture_format(
mut self, mut self,
texture_format: wgpu::TextureFormat, texture_format: wgpu::TextureFormat,
) -> PixelsBuilder<'req, 'dev, 'win, W> { ) -> PixelsBuilder<'req, 'dev, 'win, W> {
self.render_texture_format = texture_format; self.render_texture_format = Some(texture_format);
self self
} }
@ -196,13 +211,18 @@ impl<'req, 'dev, 'win, W: HasRawWindowHandle> PixelsBuilder<'req, 'dev, 'win, W>
.map_err(Error::DeviceNotFound)?; .map_err(Error::DeviceNotFound)?;
let present_mode = self.present_mode; let present_mode = self.present_mode;
let render_texture_format = self.render_texture_format.unwrap_or_else(|| {
adapter
.get_swap_chain_preferred_format(&surface)
.unwrap_or(wgpu::TextureFormat::Bgra8UnormSrgb)
});
// Create swap chain // Create swap chain
let surface_size = self.surface_texture.size; let surface_size = self.surface_texture.size;
let swap_chain = create_swap_chain( let swap_chain = create_swap_chain(
&mut device, &mut device,
&surface, &surface,
self.render_texture_format, render_texture_format,
&surface_size, &surface_size,
present_mode, present_mode,
); );
@ -217,7 +237,7 @@ impl<'req, 'dev, 'win, W: HasRawWindowHandle> PixelsBuilder<'req, 'dev, 'win, W>
self.texture_format, self.texture_format,
// Render texture values // Render texture values
&surface_size, &surface_size,
self.render_texture_format, render_texture_format,
); );
// Create the pixel buffer // Create the pixel buffer
@ -241,9 +261,9 @@ impl<'req, 'dev, 'win, W: HasRawWindowHandle> PixelsBuilder<'req, 'dev, 'win, W>
context, context,
surface_size, surface_size,
present_mode, present_mode,
render_texture_format,
pixels, pixels,
scaling_matrix_inverse, scaling_matrix_inverse,
render_texture_format: self.render_texture_format,
}) })
} }
} }

View file

@ -525,8 +525,16 @@ impl Pixels {
&self.context.texture &self.context.texture
} }
/// Provides access to the internal [`PixelsContext`] /// Provides access to the internal [`PixelsContext`].
pub fn context(&self) -> &PixelsContext { pub fn context(&self) -> &PixelsContext {
&self.context &self.context
} }
/// Get the render texture format.
///
/// This texture format may be chosen automatically by the swapchain. See
/// [`PixelsBuilder::render_texture_format`] for more information.
pub fn render_texture_format(&self) -> wgpu::TextureFormat {
self.render_texture_format
}
} }