1 // Copyright (c) 2017 The vulkano developers
2 // Licensed under the Apache License, Version 2.0
3 // <LICENSE-APACHE or
4 // https://www.apache.org/licenses/LICENSE-2.0> or the MIT
5 // license <LICENSE-MIT or https://opensource.org/licenses/MIT>,
6 // at your option. All files in the project carrying such
7 // notice may not be copied, modified, or distributed except
8 // according to those terms.
9
10 //! This module contains the `ensure_image_view_compatible` function, which verifies whether
11 //! an image view can be used as a render pass attachment.
12
13 use crate::image::view::ImageViewAbstract;
14 use crate::render_pass::RenderPassDesc;
15 use crate::{format::Format, image::SampleCount};
16 use std::error;
17 use std::fmt;
18
19 /// Checks whether the given image view is allowed to be the nth attachment of the given render
20 /// pass.
21 ///
22 /// # Panic
23 ///
24 /// Panics if the attachment number is out of range.
25 // TODO: add a specializable trait instead, that uses this function
26 // TODO: ImageView instead of ImageViewAbstract?
ensure_image_view_compatible<I>( render_pass_desc: &RenderPassDesc, attachment_num: usize, image_view: &I, ) -> Result<(), IncompatibleRenderPassAttachmentError> where I: ?Sized + ImageViewAbstract,27 pub fn ensure_image_view_compatible<I>(
28 render_pass_desc: &RenderPassDesc,
29 attachment_num: usize,
30 image_view: &I,
31 ) -> Result<(), IncompatibleRenderPassAttachmentError>
32 where
33 I: ?Sized + ImageViewAbstract,
34 {
35 let attachment_desc = render_pass_desc
36 .attachments()
37 .get(attachment_num)
38 .expect("Attachment num out of range");
39
40 if image_view.format() != attachment_desc.format {
41 return Err(IncompatibleRenderPassAttachmentError::FormatMismatch {
42 expected: attachment_desc.format,
43 obtained: image_view.format(),
44 });
45 }
46
47 if image_view.image().samples() != attachment_desc.samples {
48 return Err(IncompatibleRenderPassAttachmentError::SamplesMismatch {
49 expected: attachment_desc.samples,
50 obtained: image_view.image().samples(),
51 });
52 }
53
54 if !image_view.component_mapping().is_identity() {
55 return Err(IncompatibleRenderPassAttachmentError::NotIdentitySwizzled);
56 }
57
58 for subpass in render_pass_desc.subpasses() {
59 if subpass
60 .color_attachments
61 .iter()
62 .any(|&(n, _)| n == attachment_num)
63 {
64 debug_assert!(image_view.image().has_color()); // Was normally checked by the render pass.
65 if !image_view.image().inner().image.usage().color_attachment {
66 return Err(IncompatibleRenderPassAttachmentError::MissingColorAttachmentUsage);
67 }
68 }
69
70 if let Some((ds, _)) = subpass.depth_stencil {
71 if ds == attachment_num {
72 // Was normally checked by the render pass.
73 debug_assert!(image_view.image().has_depth() || image_view.image().has_stencil());
74 if !image_view
75 .image()
76 .inner()
77 .image
78 .usage()
79 .depth_stencil_attachment
80 {
81 return Err(
82 IncompatibleRenderPassAttachmentError::MissingDepthStencilAttachmentUsage,
83 );
84 }
85 }
86 }
87
88 if subpass
89 .input_attachments
90 .iter()
91 .any(|&(n, _)| n == attachment_num)
92 {
93 if !image_view.image().inner().image.usage().input_attachment {
94 return Err(IncompatibleRenderPassAttachmentError::MissingInputAttachmentUsage);
95 }
96 }
97 }
98
99 // TODO: consider forbidding LoadOp::Load if image is transient
100
101 // TODO: are all image layouts allowed? check this
102
103 Ok(())
104 }
105
106 /// Error that can happen when an image is not compatible with a render pass attachment slot.
107 #[derive(Copy, Clone, Debug)]
108 pub enum IncompatibleRenderPassAttachmentError {
109 /// The image format expected by the render pass doesn't match the actual format of
110 /// the image.
111 FormatMismatch {
112 /// Format expected by the render pass.
113 expected: Format,
114 /// Format of the image.
115 obtained: Format,
116 },
117
118 /// The number of samples expected by the render pass doesn't match the number of samples of
119 /// the image.
120 SamplesMismatch {
121 /// Number of samples expected by the render pass.
122 expected: SampleCount,
123 /// Number of samples of the image.
124 obtained: SampleCount,
125 },
126
127 /// The image view has a component swizzle that is different from identity.
128 NotIdentitySwizzled,
129
130 /// The image is used as a color attachment but is missing the color attachment usage.
131 MissingColorAttachmentUsage,
132
133 /// The image is used as a depth/stencil attachment but is missing the depth-stencil attachment
134 /// usage.
135 MissingDepthStencilAttachmentUsage,
136
137 /// The image is used as an input attachment but is missing the input attachment usage.
138 MissingInputAttachmentUsage,
139 }
140
141 impl error::Error for IncompatibleRenderPassAttachmentError {}
142
143 impl fmt::Display for IncompatibleRenderPassAttachmentError {
144 #[inline]
fmt(&self, fmt: &mut fmt::Formatter) -> Result<(), fmt::Error>145 fn fmt(&self, fmt: &mut fmt::Formatter) -> Result<(), fmt::Error> {
146 write!(
147 fmt,
148 "{}",
149 match *self {
150 IncompatibleRenderPassAttachmentError::FormatMismatch { .. } => {
151 "mismatch between the format expected by the render pass and the actual format"
152 }
153 IncompatibleRenderPassAttachmentError::SamplesMismatch { .. } => {
154 "mismatch between the number of samples expected by the render pass and the actual \
155 number of samples"
156 }
157 IncompatibleRenderPassAttachmentError::NotIdentitySwizzled => {
158 "the image view's component mapping is not identity swizzled"
159 }
160 IncompatibleRenderPassAttachmentError::MissingColorAttachmentUsage => {
161 "the image is used as a color attachment but is missing the color attachment usage"
162 }
163 IncompatibleRenderPassAttachmentError::MissingDepthStencilAttachmentUsage => {
164 "the image is used as a depth/stencil attachment but is missing the depth-stencil \
165 attachment usage"
166 }
167 IncompatibleRenderPassAttachmentError::MissingInputAttachmentUsage => {
168 "the image is used as an input attachment but is missing the input \
169 attachment usage"
170 }
171 }
172 )
173 }
174 }
175
176 #[cfg(test)]
177 mod tests {
178 use super::ensure_image_view_compatible;
179 use super::IncompatibleRenderPassAttachmentError;
180 use crate::format::Format;
181 use crate::image::view::ImageView;
182 use crate::image::AttachmentImage;
183 use crate::render_pass::RenderPassDesc;
184
185 #[test]
basic_ok()186 fn basic_ok() {
187 let (device, _) = gfx_dev_and_queue!();
188
189 let rp = single_pass_renderpass!(device.clone(),
190 attachments: {
191 color: {
192 load: Clear,
193 store: Store,
194 format: Format::R8G8B8A8Unorm,
195 samples: 1,
196 }
197 },
198 pass: {
199 color: [color],
200 depth_stencil: {}
201 }
202 )
203 .unwrap();
204
205 let view = ImageView::new(
206 AttachmentImage::new(device, [128, 128], Format::R8G8B8A8Unorm).unwrap(),
207 )
208 .unwrap();
209
210 ensure_image_view_compatible(rp.desc(), 0, &view).unwrap();
211 }
212
213 #[test]
format_mismatch()214 fn format_mismatch() {
215 let (device, _) = gfx_dev_and_queue!();
216
217 let rp = single_pass_renderpass!(device.clone(),
218 attachments: {
219 color: {
220 load: Clear,
221 store: Store,
222 format: Format::R16G16Sfloat,
223 samples: 1,
224 }
225 },
226 pass: {
227 color: [color],
228 depth_stencil: {}
229 }
230 )
231 .unwrap();
232
233 let view = ImageView::new(
234 AttachmentImage::new(device, [128, 128], Format::R8G8B8A8Unorm).unwrap(),
235 )
236 .unwrap();
237
238 match ensure_image_view_compatible(rp.desc(), 0, &view) {
239 Err(IncompatibleRenderPassAttachmentError::FormatMismatch {
240 expected: Format::R16G16Sfloat,
241 obtained: Format::R8G8B8A8Unorm,
242 }) => (),
243 e => panic!("{:?}", e),
244 }
245 }
246
247 #[test]
attachment_out_of_range()248 fn attachment_out_of_range() {
249 let (device, _) = gfx_dev_and_queue!();
250
251 let rp = RenderPassDesc::empty();
252 let view = ImageView::new(
253 AttachmentImage::new(device, [128, 128], Format::R8G8B8A8Unorm).unwrap(),
254 )
255 .unwrap();
256
257 assert_should_panic!("Attachment num out of range", {
258 let _ = ensure_image_view_compatible(&rp, 0, &view);
259 });
260 }
261
262 // TODO: more tests
263 }
264