Skip to content

Commit

Permalink
Use the swapchain-preferred texture format by default (#182)
Browse files Browse the repository at this point in the history
- 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.
  • Loading branch information
parasyte committed Jul 17, 2021
1 parent ce549a7 commit 288da36
Show file tree
Hide file tree
Showing 7 changed files with 59 additions and 35 deletions.
4 changes: 2 additions & 2 deletions examples/custom-shader/src/main.rs
Original file line number Diff line number Diff line change
Expand Up @@ -45,7 +45,7 @@ fn main() -> Result<(), Error> {
};
let mut world = World::new();
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| {
// Draw the current frame
Expand Down Expand Up @@ -82,7 +82,7 @@ fn main() -> Result<(), Error> {
// Resize the window
if let Some(size) = input.window_resized() {
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
Expand Down
18 changes: 10 additions & 8 deletions examples/custom-shader/src/renderers.rs
Original file line number Diff line number Diff line change
Expand Up @@ -11,13 +11,14 @@ pub(crate) struct 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 module = device.create_shader_module(&shader);

// Create a texture view that will be used as input
// 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
let sampler = device.create_sampler(&wgpu::SamplerDescriptor {
Expand Down Expand Up @@ -137,7 +138,7 @@ impl NoiseRenderer {
module: &module,
entry_point: "fs_main",
targets: &[wgpu::ColorTargetState {
format: wgpu::TextureFormat::Bgra8UnormSrgb,
format: pixels.render_texture_format(),
blend: Some(wgpu::BlendState {
color: wgpu::BlendComponent::REPLACE,
alpha: wgpu::BlendComponent::REPLACE,
Expand All @@ -162,10 +163,10 @@ impl NoiseRenderer {
&self.texture_view
}

pub(crate) fn resize(&mut self, device: &wgpu::Device, width: u32, height: u32) {
self.texture_view = create_texture_view(device, width, height);
pub(crate) fn resize(&mut self, pixels: &pixels::Pixels, width: u32, height: u32) {
self.texture_view = create_texture_view(pixels, width, height);
self.bind_group = create_bind_group(
device,
pixels.device(),
&self.bind_group_layout,
&self.texture_view,
&self.sampler,
Expand Down Expand Up @@ -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 {
label: None,
size: pixels::wgpu::Extent3d {
Expand All @@ -214,7 +216,7 @@ fn create_texture_view(device: &wgpu::Device, width: u32, height: u32) -> wgpu::
mip_level_count: 1,
sample_count: 1,
dimension: wgpu::TextureDimension::D2,
format: wgpu::TextureFormat::Bgra8UnormSrgb,
format: pixels.render_texture_format(),
usage: wgpu::TextureUsage::SAMPLED | wgpu::TextureUsage::RENDER_ATTACHMENT,
};

Expand Down
4 changes: 2 additions & 2 deletions examples/egui-winit/src/gui.rs
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,7 @@ pub(crate) struct Gui {

impl Gui {
/// 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 {
physical_width: width,
physical_height: height,
Expand All @@ -32,7 +32,7 @@ impl Gui {
physical_height: height,
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 {
start_time: Instant::now(),
Expand Down
7 changes: 1 addition & 6 deletions examples/egui-winit/src/main.rs
Original file line number Diff line number Diff line change
Expand Up @@ -43,12 +43,7 @@ fn main() -> Result<(), Error> {
let scale_factor = window.scale_factor();
let surface_texture = SurfaceTexture::new(window_size.width, window_size.height, &window);
let pixels = Pixels::new(WIDTH, HEIGHT, surface_texture)?;
let gui = Gui::new(
window_size.width,
window_size.height,
scale_factor,
pixels.context(),
);
let gui = Gui::new(window_size.width, window_size.height, scale_factor, &pixels);

(pixels, gui)
};
Expand Down
3 changes: 1 addition & 2 deletions examples/imgui-winit/src/gui.rs
Original file line number Diff line number Diff line change
Expand Up @@ -44,9 +44,8 @@ impl Gui {
// Create Dear ImGui WGPU renderer
let device = pixels.device();
let queue = pixels.queue();
let texture_format = wgpu::TextureFormat::Bgra8UnormSrgb;
let config = imgui_wgpu::RendererConfig {
texture_format,
texture_format: pixels.render_texture_format(),
..Default::default()
};
let renderer = imgui_wgpu::Renderer::new(&mut imgui, device, queue, config);
Expand Down
48 changes: 34 additions & 14 deletions src/builder.rs
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,7 @@ pub struct PixelsBuilder<'req, 'dev, 'win, W: HasRawWindowHandle> {
present_mode: wgpu::PresentMode,
surface_texture: SurfaceTexture<'win, W>,
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> {
Expand Down Expand Up @@ -58,7 +58,7 @@ impl<'req, 'dev, 'win, W: HasRawWindowHandle> PixelsBuilder<'req, 'dev, 'win, W>
present_mode: wgpu::PresentMode::Fifo,
surface_texture,
texture_format: wgpu::TextureFormat::Rgba8UnormSrgb,
render_texture_format: wgpu::TextureFormat::Bgra8UnormSrgb,
render_texture_format: None,
}
}

Expand All @@ -82,8 +82,7 @@ impl<'req, 'dev, 'win, W: HasRawWindowHandle> PixelsBuilder<'req, 'dev, 'win, W>

/// Set which backends wgpu will attempt to use.
///
/// The default value of this is [`wgpu::BackendBit::PRIMARY`], which enables
/// the well supported backends for wgpu.
/// The default value is `PRIMARY`, which enables the well supported backends for wgpu.
pub fn wgpu_backend(mut self, backend: wgpu::BackendBit) -> PixelsBuilder<'req, 'dev, 'win, W> {
self.backend = backend;
self
Expand Down Expand Up @@ -144,9 +143,12 @@ impl<'req, 'dev, 'win, W: HasRawWindowHandle> PixelsBuilder<'req, 'dev, 'win, W>

/// Set the texture format.
///
/// The default value is [`wgpu::TextureFormat::Rgba8UnormSrgb`], which is 4 unsigned bytes in
/// `RGBA` order using the SRGB color space. This is typically what you want when you are
/// working with color values from popular image editing tools or web apps.
/// The default value is `Rgba8UnormSrgb`, which is 4 unsigned bytes in `RGBA` order using the
/// sRGB color space. This is typically what you want when you are working with color values
/// 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(
mut self,
texture_format: wgpu::TextureFormat,
Expand All @@ -157,14 +159,27 @@ impl<'req, 'dev, 'win, W: HasRawWindowHandle> PixelsBuilder<'req, 'dev, 'win, W>

/// Set the render texture format.
///
/// The default value is [`wgpu::TextureFormat::Bgra8UnormSrgb`], which is 4 unsigned bytes in
/// `BGRA` order using the SRGB color space. This format depends on the hardware/platform the
/// pixel buffer is rendered to/for.
/// The default value is chosen automatically by the swapchain (if it can) with a fallback to
/// `Bgra8UnormSrgb` (which is 4 unsigned bytes in `BGRA` order using the sRGB color space).
/// 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(
mut self,
texture_format: wgpu::TextureFormat,
) -> PixelsBuilder<'req, 'dev, 'win, W> {
self.render_texture_format = texture_format;
self.render_texture_format = Some(texture_format);
self
}

Expand Down Expand Up @@ -196,13 +211,18 @@ impl<'req, 'dev, 'win, W: HasRawWindowHandle> PixelsBuilder<'req, 'dev, 'win, W>
.map_err(Error::DeviceNotFound)?;

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
let surface_size = self.surface_texture.size;
let swap_chain = create_swap_chain(
&mut device,
&surface,
self.render_texture_format,
render_texture_format,
&surface_size,
present_mode,
);
Expand All @@ -217,7 +237,7 @@ impl<'req, 'dev, 'win, W: HasRawWindowHandle> PixelsBuilder<'req, 'dev, 'win, W>
self.texture_format,
// Render texture values
&surface_size,
self.render_texture_format,
render_texture_format,
);

// Create the pixel buffer
Expand All @@ -241,9 +261,9 @@ impl<'req, 'dev, 'win, W: HasRawWindowHandle> PixelsBuilder<'req, 'dev, 'win, W>
context,
surface_size,
present_mode,
render_texture_format,
pixels,
scaling_matrix_inverse,
render_texture_format: self.render_texture_format,
})
}
}
Expand Down
10 changes: 9 additions & 1 deletion src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -525,8 +525,16 @@ impl Pixels {
&self.context.texture
}

/// Provides access to the internal [`PixelsContext`]
/// Provides access to the internal [`PixelsContext`].
pub fn context(&self) -> &PixelsContext {
&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
}
}

0 comments on commit 288da36

Please sign in to comment.