rafx_framework/graph/
graph_image.rs

1use super::*;
2use rafx_api::{RafxExtents3D, RafxFormat, RafxResourceType, RafxSampleCount, RafxTextureBindType};
3
4/// Unique ID for a particular usage (read or write) of a specific image
5#[derive(Clone, Copy, PartialEq, Eq, Hash, Debug)]
6pub struct RenderGraphImageUsageId(pub(super) usize);
7
8/// An ID for an image used within the graph between passes
9#[derive(Copy, Clone, Debug, PartialEq, Eq, Hash)]
10pub struct VirtualImageId(pub(super) usize);
11
12/// An ID for an image allocation (possibly reused)
13#[derive(Copy, Clone, Debug, PartialEq, Eq, Hash)]
14pub struct PhysicalImageId(pub(super) usize);
15
16/// An ID for an image view allocation (possibly reused)
17#[derive(Copy, Clone, Debug, PartialEq, Eq, Hash)]
18pub struct PhysicalImageViewId(pub(super) usize);
19
20/// Unique ID provided for any image registered as an external image
21#[derive(Debug, Copy, Clone)]
22pub struct RenderGraphExternalImageId(pub(super) usize);
23
24/// Unique ID for a particular version of an image. Any time an image is modified, a new version is
25/// produced
26#[derive(Clone, Copy, PartialEq, Eq, Hash, Debug)]
27pub struct RenderGraphImageVersionId {
28    pub(super) index: usize,
29    pub(super) version: usize,
30}
31
32/// A "virtual" image that the render graph knows about. The render graph will allocate images as
33/// needed, but can reuse the same image for multiple resources if the lifetimes of those images
34/// don't overlap
35#[derive(Debug)]
36pub struct RenderGraphImageResource {
37    pub(super) name: Option<RenderGraphResourceName>,
38
39    pub(super) versions: Vec<RenderGraphImageResourceVersionInfo>,
40}
41
42impl RenderGraphImageResource {
43    pub(super) fn new() -> Self {
44        RenderGraphImageResource {
45            name: None,
46            versions: Default::default(),
47        }
48    }
49}
50
51#[derive(Debug, Clone, PartialEq, Eq, Hash)]
52pub struct RenderGraphImageView {
53    pub(super) physical_image: PhysicalImageId,
54    pub(super) format: RafxFormat,
55    pub(super) view_options: RenderGraphImageViewOptions,
56}
57
58/// Defines what created a RenderGraphImageUsage
59#[derive(Debug, Clone, Copy)]
60pub enum RenderGraphImageUser {
61    Node(RenderGraphNodeId),
62    Input(RenderGraphExternalImageId),
63    Output(RenderGraphExternalImageId),
64}
65
66#[derive(Copy, Clone, Debug, PartialEq, Eq, Hash)]
67pub enum RenderGraphImageExtents {
68    MatchSurface,
69    // (width, height, depth)
70    Custom(RafxExtents3D),
71}
72
73impl RenderGraphImageExtents {
74    pub fn into_rafx_extents(
75        self,
76        swapchain_surface_info: &SwapchainSurfaceInfo,
77    ) -> RafxExtents3D {
78        match self {
79            RenderGraphImageExtents::MatchSurface => RafxExtents3D {
80                width: swapchain_surface_info.extents.width,
81                height: swapchain_surface_info.extents.height,
82                depth: 1,
83            },
84            RenderGraphImageExtents::Custom(extents) => extents,
85        }
86    }
87}
88
89impl Default for RenderGraphImageExtents {
90    fn default() -> Self {
91        RenderGraphImageExtents::MatchSurface
92    }
93}
94
95#[derive(Default, Clone, Debug, PartialEq, Eq, Hash)]
96pub struct RenderGraphImageViewOptions {
97    pub texture_bind_type: Option<RafxTextureBindType>,
98    pub array_slice: Option<u16>,
99    pub mip_slice: Option<u8>,
100}
101
102impl RenderGraphImageViewOptions {
103    pub fn array_slice(array_slice: u16) -> Self {
104        RenderGraphImageViewOptions {
105            texture_bind_type: None,
106            array_slice: Some(array_slice),
107            mip_slice: None,
108        }
109    }
110
111    pub fn mip_slice(mip_slice: u8) -> Self {
112        RenderGraphImageViewOptions {
113            texture_bind_type: None,
114            array_slice: None,
115            mip_slice: Some(mip_slice),
116        }
117    }
118}
119
120/// A usage of a particular image
121#[derive(Debug)]
122pub struct RenderGraphImageUsage {
123    pub(super) user: RenderGraphImageUser,
124    pub(super) usage_type: RenderGraphImageUsageType,
125    pub(super) version: RenderGraphImageVersionId,
126
127    pub(super) view_options: RenderGraphImageViewOptions,
128}
129
130/// Immutable, fully-specified attributes of an image. A *constraint* is partially specified and
131/// the graph will use constraints to solve for the specification
132#[derive(Debug, Clone, PartialEq, Eq, Hash)]
133pub struct RenderGraphImageSpecification {
134    // Rename to RenderGraphImageUsageSpecification?
135    pub samples: RafxSampleCount,
136    pub format: RafxFormat,
137    pub resource_type: RafxResourceType,
138    pub extents: RafxExtents3D,
139    pub layer_count: u32,
140    pub mip_count: u32,
141    // image type - always 2D
142}
143
144impl RenderGraphImageSpecification {
145    /// Returns true if no fields in the two constraints are conflicting
146    pub fn can_merge(
147        &self,
148        other: &RenderGraphImageSpecification,
149    ) -> bool {
150        if self.samples != other.samples {
151            return false;
152        }
153        if self.format != other.format {
154            return false;
155        }
156        if self.mip_count != other.mip_count {
157            return false;
158        }
159        if self.layer_count != other.layer_count {
160            return false;
161        }
162        if self.extents != other.extents {
163            return false;
164        }
165
166        true
167    }
168
169    /// Merge other's constraints into self, but only if there are no conflicts. No modification
170    /// occurs if any conflict exists
171    pub fn try_merge(
172        &mut self,
173        other: &RenderGraphImageSpecification,
174    ) -> bool {
175        if !self.can_merge(other) {
176            return false;
177        }
178
179        self.resource_type |= other.resource_type;
180
181        true
182    }
183
184    pub fn specifications_are_compatible(
185        written: &RenderGraphImageSpecification,
186        read: &RenderGraphImageSpecification,
187    ) -> bool {
188        if written.samples != read.samples {
189            return false;
190        }
191        if written.format != read.format {
192            return false;
193        }
194        if written.mip_count != read.mip_count {
195            return false;
196        }
197        if written.layer_count != read.layer_count {
198            return false;
199        }
200        if written.extents != read.extents {
201            return false;
202        }
203        if (written.resource_type | read.resource_type) != written.resource_type {
204            return false;
205        }
206        return true;
207    }
208}
209
210/// Constraints on an image. Constraints are set per-field and start out None (i.e. unconstrained)
211/// The rendergraph will derive specifications from the constraints
212#[derive(Default, Clone, Debug)]
213pub struct RenderGraphImageConstraint {
214    // Rename to RenderGraphImageUsageConstraint?
215    pub samples: Option<RafxSampleCount>,
216    pub format: Option<RafxFormat>,
217    pub resource_type: RafxResourceType,
218    pub extents: Option<RenderGraphImageExtents>,
219    pub layer_count: Option<u32>,
220    pub mip_count: Option<u32>,
221}
222
223impl From<RenderGraphImageSpecification> for RenderGraphImageConstraint {
224    fn from(specification: RenderGraphImageSpecification) -> Self {
225        RenderGraphImageConstraint {
226            samples: Some(specification.samples),
227            format: Some(specification.format),
228            resource_type: specification.resource_type,
229            layer_count: Some(specification.layer_count),
230            mip_count: Some(specification.mip_count),
231            extents: Some(RenderGraphImageExtents::Custom(specification.extents)),
232        }
233    }
234}
235
236impl RenderGraphImageConstraint {
237    pub fn try_convert_to_specification(
238        self,
239        swapchain_surface_info: &SwapchainSurfaceInfo,
240    ) -> Option<RenderGraphImageSpecification> {
241        // Format is the only thing we can't default sensibly
242        if self.format.is_none() {
243            None
244        } else {
245            Some(RenderGraphImageSpecification {
246                samples: self.samples.unwrap_or(RafxSampleCount::SampleCount1),
247                format: self.format.unwrap(),
248                layer_count: self.layer_count.unwrap_or(1),
249                mip_count: self.mip_count.unwrap_or(1),
250                extents: self
251                    .extents
252                    .unwrap_or(RenderGraphImageExtents::MatchSurface)
253                    .into_rafx_extents(swapchain_surface_info),
254                resource_type: self.resource_type,
255            })
256        }
257    }
258}
259
260impl RenderGraphImageConstraint {
261    /// Returns true if no fields in the two constraints are conflicting
262    pub fn can_merge(
263        &self,
264        other: &RenderGraphImageConstraint,
265    ) -> bool {
266        if self.samples.is_some() && other.samples.is_some() && self.samples != other.samples {
267            return false;
268        }
269        if self.format.is_some() && other.format.is_some() && self.format != other.format {
270            return false;
271        }
272        if self.layer_count.is_some()
273            && other.layer_count.is_some()
274            && self.layer_count != other.layer_count
275        {
276            return false;
277        }
278        if self.mip_count.is_some()
279            && other.mip_count.is_some()
280            && self.mip_count != other.mip_count
281        {
282            return false;
283        }
284        if self.extents.is_some() && other.extents.is_some() && self.extents != other.extents {
285            return false;
286        }
287
288        true
289    }
290
291    /// Merge other's constraints into self, but only if there are no conflicts. No modification
292    /// occurs if any conflict exists
293    pub fn try_merge(
294        &mut self,
295        other: &RenderGraphImageConstraint,
296    ) -> bool {
297        if !self.can_merge(other) {
298            return false;
299        }
300
301        if self.samples.is_none() && other.samples.is_some() {
302            self.samples = other.samples;
303        }
304        if self.format.is_none() && other.format.is_some() {
305            self.format = other.format;
306        }
307        if self.layer_count.is_none() && other.layer_count.is_some() {
308            self.layer_count = other.layer_count;
309        }
310        if self.mip_count.is_none() && other.mip_count.is_some() {
311            self.mip_count = other.mip_count;
312        }
313        if self.extents.is_none() && other.extents.is_some() {
314            self.extents = other.extents;
315        }
316
317        self.resource_type |= other.resource_type;
318
319        true
320    }
321
322    /// Merge other's constraints into self. We will merge fields where we can and skip fields with
323    /// conflicts
324    pub fn partial_merge(
325        &mut self,
326        other: &RenderGraphImageConstraint,
327    ) -> bool {
328        let mut complete_merge = true;
329
330        if self.samples.is_some() && other.samples.is_some() && self.samples != other.samples {
331            complete_merge = false;
332        } else if other.samples.is_some() {
333            self.samples = other.samples;
334        }
335
336        if self.format.is_some() && other.format.is_some() && self.format != other.format {
337            complete_merge = false;
338        } else if other.format.is_some() {
339            self.format = other.format;
340        }
341
342        if self.layer_count.is_some()
343            && other.layer_count.is_some()
344            && self.layer_count != other.layer_count
345        {
346            complete_merge = false;
347        } else if other.layer_count.is_some() {
348            self.layer_count = other.layer_count;
349        }
350
351        if self.mip_count.is_some()
352            && other.mip_count.is_some()
353            && self.mip_count != other.mip_count
354        {
355            complete_merge = false;
356        } else if other.mip_count.is_some() {
357            self.mip_count = other.mip_count;
358        }
359
360        if self.extents.is_some() && other.extents.is_some() && self.extents != other.extents {
361            complete_merge = false;
362        } else if other.extents.is_some() {
363            self.extents = other.extents;
364        }
365
366        self.resource_type |= other.resource_type;
367
368        complete_merge
369    }
370
371    /// Sets the constraints based on the given specification
372    pub fn set(
373        &mut self,
374        other: &RenderGraphImageSpecification,
375    ) {
376        *self = other.clone().into();
377    }
378}
379
380/// How an image is being used
381#[derive(Copy, Clone, Debug, PartialEq)]
382pub enum RenderGraphImageUsageType {
383    Create,
384    Input,
385    Read,
386    ModifyRead,
387    ModifyWrite,
388    Output,
389}
390
391impl RenderGraphImageUsageType {
392    //TODO: Add support to see if multiple writes actually overlap
393    pub fn is_read_only(&self) -> bool {
394        match self {
395            RenderGraphImageUsageType::Read => true,
396            RenderGraphImageUsageType::Output => true,
397            RenderGraphImageUsageType::ModifyRead => false,
398            RenderGraphImageUsageType::Create => false,
399            RenderGraphImageUsageType::Input => false,
400            RenderGraphImageUsageType::ModifyWrite => false,
401        }
402    }
403}
404
405/// Information about a specific version of the image.
406#[derive(Debug)]
407pub struct RenderGraphImageResourceVersionInfo {
408    /// What node created the image (keep in mind these are virtual images, not images provided
409    /// from outside the graph. So every image will have a creator node)
410    pub(super) creator_node: RenderGraphNodeId,
411
412    pub(super) create_usage: RenderGraphImageUsageId,
413    pub(super) read_usages: Vec<RenderGraphImageUsageId>,
414}
415
416impl RenderGraphImageResourceVersionInfo {
417    pub(super) fn new(
418        creator: RenderGraphNodeId,
419        create_usage: RenderGraphImageUsageId,
420    ) -> Self {
421        RenderGraphImageResourceVersionInfo {
422            creator_node: creator,
423            create_usage,
424            read_usages: Default::default(),
425        }
426    }
427
428    // for redirect_image_usage
429    pub(super) fn remove_read_usage(
430        &mut self,
431        usage: RenderGraphImageUsageId,
432    ) {
433        if let Some(position) = self.read_usages.iter().position(|x| *x == usage) {
434            self.read_usages.swap_remove(position);
435        }
436    }
437
438    pub(super) fn add_read_usage(
439        &mut self,
440        usage: RenderGraphImageUsageId,
441    ) {
442        self.read_usages.push(usage);
443    }
444}