@@ -106,6 +106,37 @@ index buffer for each bind group. This buffer is accessed in the shader to get t
106
106
sampler index within the heap. See the wgpu_hal dx12 backend documentation for more
107
107
information.
108
108
109
+ # External textures
110
+
111
+ Support for [`crate::ImageClass::External`] textures is implemented by lowering
112
+ each external texture global variable to 3 `Texture2D<float4>`s, and a `cbuffer`
113
+ of type `NagaExternalTextureParams`. This provides up to 3 planes of texture
114
+ data (for example single planar RGBA, or separate Y, Cb, and Cr planes), and the
115
+ parameters buffer containing information describing how to handle these
116
+ correctly. The bind target to use for each of these globals is specified via
117
+ [`Options::external_texture_binding_map`].
118
+
119
+ External textures are supported by WGSL's `textureDimensions()`,
120
+ `textureLoad()`, and `textureSampleBaseClampToEdge()` built-in functions. These
121
+ are implemented using helper functions. See the following functions for how
122
+ these are generated:
123
+ * `Writer::write_wrapped_image_query_function`
124
+ * `Writer::write_wrapped_image_load_function`
125
+ * `Writer::write_wrapped_image_sample_function`
126
+
127
+ Ideally the set of global variables could be wrapped in a single struct that
128
+ could conveniently be passed around. But, alas, HLSL does not allow structs to
129
+ have `Texture2D` members. Fortunately, however, external textures can only be
130
+ used as arguments to either built-in or user-defined functions. We therefore
131
+ expand any external texture function argument to four consecutive arguments (3
132
+ textures and the params struct) when declaring user-defined functions, and
133
+ ensure our built-in function implementations take the same arguments. Then,
134
+ whenever we need to emit an external texture in `Writer::write_expr`, which
135
+ fortunately can only ever be for a global variable or function argument, we
136
+ simply emit the variable name of each of the three textures and the parameters
137
+ struct in a comma-separated list. This won't win any awards for elegance, but
138
+ it works for our purposes.
139
+
109
140
[hlsl]: https://docs.microsoft.com/en-us/windows/win32/direct3dhlsl/dx-graphics-hlsl
110
141
[ilov]: https://gpuweb.github.io/gpuweb/wgsl/#internal-value-layout
111
142
[16bb]: https://github.com/microsoft/DirectXShaderCompiler/wiki/Buffer-Packing#constant-buffer-packing
@@ -126,6 +157,35 @@ use thiserror::Error;
126
157
127
158
use crate :: { back, ir, proc} ;
128
159
160
+ /// Direct3D 12 binding information for a global variable.
161
+ ///
162
+ /// This type provides the HLSL-specific information Naga needs to declare and
163
+ /// access an HLSL global variable that cannot be derived from the `Module`
164
+ /// itself.
165
+ ///
166
+ /// An HLSL global variable declaration includes details that the Direct3D API
167
+ /// will use to refer to it. For example:
168
+ ///
169
+ /// RWByteAddressBuffer s_sasm : register(u0, space2);
170
+ ///
171
+ /// This defines a global `s_sasm` that a Direct3D root signature would refer to
172
+ /// as register `0` in register space `2` in a `UAV` descriptor range. Naga can
173
+ /// infer the register's descriptor range type from the variable's address class
174
+ /// (writable [`Storage`] variables are implemented by Direct3D Unordered Access
175
+ /// Views, the `u` register type), but the register number and register space
176
+ /// must be supplied by the user.
177
+ ///
178
+ /// The [`back::hlsl::Options`] structure provides `BindTarget`s for various
179
+ /// situations in which Naga may need to generate an HLSL global variable, like
180
+ /// [`binding_map`] for Naga global variables, or [`push_constants_target`] for
181
+ /// a module's sole [`PushConstant`] variable. See those fields' documentation
182
+ /// for details.
183
+ ///
184
+ /// [`Storage`]: crate::ir::AddressSpace::Storage
185
+ /// [`back::hlsl::Options`]: Options
186
+ /// [`binding_map`]: Options::binding_map
187
+ /// [`push_constants_target`]: Options::push_constants_target
188
+ /// [`PushConstant`]: crate::ir::AddressSpace::PushConstant
129
189
#[ derive( Copy , Clone , Debug , Default , PartialEq , Eq , Hash ) ]
130
190
#[ cfg_attr( feature = "serialize" , derive( serde:: Serialize ) ) ]
131
191
#[ cfg_attr( feature = "deserialize" , derive( serde:: Deserialize ) ) ]
@@ -335,6 +395,62 @@ where
335
395
336
396
pub type DynamicStorageBufferOffsetsTargets = alloc:: collections:: BTreeMap < u32 , OffsetsBindTarget > ;
337
397
398
+ /// HLSL binding information for a Naga [`External`] image global variable.
399
+ ///
400
+ /// See the module documentation's section on [External textures][mod] for details.
401
+ ///
402
+ /// [`External`]: crate::ir::ImageClass::External
403
+ /// [mod]: #external-textures
404
+ #[ derive( Copy , Clone , Debug , Default , PartialEq , Eq , Hash ) ]
405
+ #[ cfg_attr( feature = "serialize" , derive( serde:: Serialize ) ) ]
406
+ #[ cfg_attr( feature = "deserialize" , derive( serde:: Deserialize ) ) ]
407
+ pub struct ExternalTextureBindTarget {
408
+ /// HLSL binding information for the individual plane textures.
409
+ ///
410
+ /// Each of these should refer to an HLSL `Texture2D<float4>` holding one
411
+ /// plane of data for the external texture. The exact meaning of each plane
412
+ /// varies at runtime depending on where the external texture's data
413
+ /// originated.
414
+ pub planes : [ BindTarget ; 3 ] ,
415
+
416
+ /// HLSL binding information for a buffer holding the sampling parameters.
417
+ ///
418
+ /// This should refer to a cbuffer of type `NagaExternalTextureParams`, that
419
+ /// the code Naga generates for `textureSampleBaseClampToEdge` consults to
420
+ /// decide how to combine the data in [`planes`] to get the result required
421
+ /// by the spec.
422
+ ///
423
+ /// [`planes`]: Self::planes
424
+ pub params : BindTarget ,
425
+ }
426
+
427
+ #[ cfg( any( feature = "serialize" , feature = "deserialize" ) ) ]
428
+ #[ cfg_attr( feature = "serialize" , derive( serde:: Serialize ) ) ]
429
+ #[ cfg_attr( feature = "deserialize" , derive( serde:: Deserialize ) ) ]
430
+ struct ExternalTextureBindingMapSerialization {
431
+ resource_binding : crate :: ResourceBinding ,
432
+ bind_target : ExternalTextureBindTarget ,
433
+ }
434
+
435
+ #[ cfg( feature = "deserialize" ) ]
436
+ fn deserialize_external_texture_binding_map < ' de , D > (
437
+ deserializer : D ,
438
+ ) -> Result < ExternalTextureBindingMap , D :: Error >
439
+ where
440
+ D : serde:: Deserializer < ' de > ,
441
+ {
442
+ use serde:: Deserialize ;
443
+
444
+ let vec = Vec :: < ExternalTextureBindingMapSerialization > :: deserialize ( deserializer) ?;
445
+ let mut map = ExternalTextureBindingMap :: default ( ) ;
446
+ for item in vec {
447
+ map. insert ( item. resource_binding , item. bind_target ) ;
448
+ }
449
+ Ok ( map)
450
+ }
451
+ pub type ExternalTextureBindingMap =
452
+ alloc:: collections:: BTreeMap < crate :: ResourceBinding , ExternalTextureBindTarget > ;
453
+
338
454
/// Shorthand result used internally by the backend
339
455
type BackendResult = Result < ( ) , Error > ;
340
456
@@ -354,21 +470,47 @@ pub enum EntryPointError {
354
470
pub struct Options {
355
471
/// The hlsl shader model to be used
356
472
pub shader_model : ShaderModel ,
357
- /// Map of resources association to binding locations.
473
+
474
+ /// HLSL binding information for each Naga global variable.
475
+ ///
476
+ /// This maps Naga [`GlobalVariable`]'s [`ResourceBinding`]s to a
477
+ /// [`BindTarget`] specifying its register number and space, along with
478
+ /// other details necessary to generate a full HLSL declaration for it,
479
+ /// or to access its value.
480
+ ///
481
+ /// This must provide a [`BindTarget`] for every [`GlobalVariable`] in the
482
+ /// [`Module`] that has a [`binding`].
483
+ ///
484
+ /// [`GlobalVariable`]: crate::ir::GlobalVariable
485
+ /// [`ResourceBinding`]: crate::ir::ResourceBinding
486
+ /// [`Module`]: crate::ir::Module
487
+ /// [`binding`]: crate::ir::GlobalVariable::binding
358
488
#[ cfg_attr(
359
489
feature = "deserialize" ,
360
490
serde( deserialize_with = "deserialize_binding_map" )
361
491
) ]
362
492
pub binding_map : BindingMap ,
493
+
363
494
/// Don't panic on missing bindings, instead generate any HLSL.
364
495
pub fake_missing_bindings : bool ,
365
496
/// Add special constants to `SV_VertexIndex` and `SV_InstanceIndex`,
366
497
/// to make them work like in Vulkan/Metal, with help of the host.
367
498
pub special_constants_binding : Option < BindTarget > ,
368
- /// Bind target of the push constant buffer
499
+
500
+ /// HLSL binding information for the [`PushConstant`] global, if present.
501
+ ///
502
+ /// If a module contains a global in the [`PushConstant`] address space, the
503
+ /// `dx12` backend stores its value directly in the root signature as a
504
+ /// series of [`D3D12_ROOT_PARAMETER_TYPE_32BIT_CONSTANTS`], whose binding
505
+ /// information is given here.
506
+ ///
507
+ /// [`PushConstant`]: crate::ir::AddressSpace::PushConstant
508
+ /// [`D3D12_ROOT_PARAMETER_TYPE_32BIT_CONSTANTS`]: https://learn.microsoft.com/en-us/windows/win32/api/d3d12/ne-d3d12-d3d12_root_parameter_type
369
509
pub push_constants_target : Option < BindTarget > ,
370
- /// Bind target of the sampler heap and comparison sampler heap.
510
+
511
+ /// HLSL binding information for the sampler heap and comparison sampler heap.
371
512
pub sampler_heap_target : SamplerHeapBindTargets ,
513
+
372
514
/// Mapping of each bind group's sampler index buffer to a bind target.
373
515
#[ cfg_attr(
374
516
feature = "deserialize" ,
@@ -381,6 +523,18 @@ pub struct Options {
381
523
serde( deserialize_with = "deserialize_storage_buffer_offsets" )
382
524
) ]
383
525
pub dynamic_storage_buffer_offsets_targets : DynamicStorageBufferOffsetsTargets ,
526
+ #[ cfg_attr(
527
+ feature = "deserialize" ,
528
+ serde( deserialize_with = "deserialize_external_texture_binding_map" )
529
+ ) ]
530
+
531
+ /// HLSL binding information for [`External`] image global variables.
532
+ ///
533
+ /// See [`ExternalTextureBindTarget`] for details.
534
+ ///
535
+ /// [`External`]: crate::ir::ImageClass::External
536
+ pub external_texture_binding_map : ExternalTextureBindingMap ,
537
+
384
538
/// Should workgroup variables be zero initialized (by polyfilling)?
385
539
pub zero_initialize_workgroup_memory : bool ,
386
540
/// Should we restrict indexing of vectors, matrices and arrays?
@@ -401,6 +555,7 @@ impl Default for Options {
401
555
sampler_buffer_binding_map : alloc:: collections:: BTreeMap :: default ( ) ,
402
556
push_constants_target : None ,
403
557
dynamic_storage_buffer_offsets_targets : alloc:: collections:: BTreeMap :: new ( ) ,
558
+ external_texture_binding_map : ExternalTextureBindingMap :: default ( ) ,
404
559
zero_initialize_workgroup_memory : true ,
405
560
restrict_indexing : true ,
406
561
force_loop_bounding : true ,
@@ -425,6 +580,29 @@ impl Options {
425
580
None => Err ( EntryPointError :: MissingBinding ( * res_binding) ) ,
426
581
}
427
582
}
583
+
584
+ fn resolve_external_texture_resource_binding (
585
+ & self ,
586
+ res_binding : & crate :: ResourceBinding ,
587
+ ) -> Result < ExternalTextureBindTarget , EntryPointError > {
588
+ match self . external_texture_binding_map . get ( res_binding) {
589
+ Some ( target) => Ok ( * target) ,
590
+ None if self . fake_missing_bindings => {
591
+ let fake = BindTarget {
592
+ space : res_binding. group as u8 ,
593
+ register : res_binding. binding ,
594
+ binding_array_size : None ,
595
+ dynamic_storage_buffer_offsets_index : None ,
596
+ restrict_indexing : false ,
597
+ } ;
598
+ Ok ( ExternalTextureBindTarget {
599
+ planes : [ fake, fake, fake] ,
600
+ params : fake,
601
+ } )
602
+ }
603
+ None => Err ( EntryPointError :: MissingBinding ( * res_binding) ) ,
604
+ }
605
+ }
428
606
}
429
607
430
608
/// Reflection info for entry point names.
@@ -479,6 +657,7 @@ enum WrappedType {
479
657
ArrayLength ( help:: WrappedArrayLength ) ,
480
658
ImageSample ( help:: WrappedImageSample ) ,
481
659
ImageQuery ( help:: WrappedImageQuery ) ,
660
+ ImageLoad ( help:: WrappedImageLoad ) ,
482
661
ImageLoadScalar ( crate :: Scalar ) ,
483
662
Constructor ( help:: WrappedConstructor ) ,
484
663
StructMatrixAccess ( help:: WrappedStructMatrixAccess ) ,
0 commit comments