mirror of
https://github.com/gfx-rs/wgpu.git
synced 2025-12-08 21:26:17 +00:00
[hal/dx12] Mesh Shaders (#8110)
* Features and draw commands added * Tried to implement the pipeline creation (completely untested) * Fixed clippy issues * Fixed something I think * A little bit of work on the mesh shader example (currently doesn't work on dx12) * Reached a new kind of error state * Fixed an alignment issue * DirectX 12 mesh shaders working :party: * Removed stupid change and updated changelog * Fixed typo * Added backends option to example framework * Removed silly no write fragment shader from tests to see if anything breaks * Tried to make mesh shader tests run elsewhere too * Removed printlns and checked that dx12 mesh shader tests run * Documented very strange issue * I'm so lost * Fixed stupid typos * Fixed all issues * Removed unnecessary example stuff, updated tests * Updated typos.toml * Updated limits * Apply suggestion from @cwfitzgerald Co-authored-by: Connor Fitzgerald <connorwadefitzgerald@gmail.com> * Apply suggestion from @cwfitzgerald Co-authored-by: Connor Fitzgerald <connorwadefitzgerald@gmail.com> * Removed supported backends, made example & tests always pass the filename to shader compilers * Removed excessive bools in test params * Added new tests to the list * I'm a sinner for this one (unused import) * Replaced random stuff with test params hashing * Updated typos.toml * Updated -Fo typo thing * Actually fixed typo issue this time * Update CHANGELOG.md Co-authored-by: Connor Fitzgerald <connorwadefitzgerald@gmail.com> * Update tests/tests/wgpu-gpu/mesh_shader/mod.rs Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com> * Update wgpu-hal/src/dx12/mod.rs Co-authored-by: Connor Fitzgerald <connorwadefitzgerald@gmail.com> * Addressed comments * Lmao --------- Co-authored-by: Connor Fitzgerald <connorwadefitzgerald@gmail.com> Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com>
This commit is contained in:
parent
1f10d0ce8a
commit
05cc6dca82
@ -202,6 +202,7 @@ By @cwfitzgerald in [#8162](https://github.com/gfx-rs/wgpu/pull/8162).
|
|||||||
#### DX12
|
#### DX12
|
||||||
|
|
||||||
- Allow disabling waiting for latency waitable object. By @marcpabst in [#7400](https://github.com/gfx-rs/wgpu/pull/7400)
|
- Allow disabling waiting for latency waitable object. By @marcpabst in [#7400](https://github.com/gfx-rs/wgpu/pull/7400)
|
||||||
|
- Add mesh shader support, including to the example. By @SupaMaggie70Incorporated in [#8110](https://github.com/gfx-rs/wgpu/issues/8110)
|
||||||
|
|
||||||
### Bug Fixes
|
### Bug Fixes
|
||||||
|
|
||||||
|
|||||||
@ -268,9 +268,9 @@ impl ExampleContext {
|
|||||||
async fn init_async<E: Example>(surface: &mut SurfaceWrapper, window: Arc<Window>) -> Self {
|
async fn init_async<E: Example>(surface: &mut SurfaceWrapper, window: Arc<Window>) -> Self {
|
||||||
log::info!("Initializing wgpu...");
|
log::info!("Initializing wgpu...");
|
||||||
|
|
||||||
let instance = wgpu::Instance::new(&wgpu::InstanceDescriptor::from_env_or_default());
|
let instance_descriptor = wgpu::InstanceDescriptor::from_env_or_default();
|
||||||
|
let instance = wgpu::Instance::new(&instance_descriptor);
|
||||||
surface.pre_adapter(&instance, window);
|
surface.pre_adapter(&instance, window);
|
||||||
|
|
||||||
let adapter = get_adapter_with_capabilities_or_from_env(
|
let adapter = get_adapter_with_capabilities_or_from_env(
|
||||||
&instance,
|
&instance,
|
||||||
&E::required_features(),
|
&E::required_features(),
|
||||||
|
|||||||
@ -1,15 +1,13 @@
|
|||||||
use std::{io::Write, process::Stdio};
|
use std::process::Stdio;
|
||||||
|
|
||||||
// Same as in mesh shader tests
|
// Same as in mesh shader tests
|
||||||
fn compile_glsl(
|
fn compile_glsl(device: &wgpu::Device, shader_stage: &'static str) -> wgpu::ShaderModule {
|
||||||
device: &wgpu::Device,
|
|
||||||
data: &[u8],
|
|
||||||
shader_stage: &'static str,
|
|
||||||
) -> wgpu::ShaderModule {
|
|
||||||
let cmd = std::process::Command::new("glslc")
|
let cmd = std::process::Command::new("glslc")
|
||||||
.args([
|
.args([
|
||||||
&format!("-fshader-stage={shader_stage}"),
|
&format!(
|
||||||
"-",
|
"{}/src/mesh_shader/shader.{shader_stage}",
|
||||||
|
env!("CARGO_MANIFEST_DIR")
|
||||||
|
),
|
||||||
"-o",
|
"-o",
|
||||||
"-",
|
"-",
|
||||||
"--target-env=vulkan1.2",
|
"--target-env=vulkan1.2",
|
||||||
@ -19,8 +17,6 @@ fn compile_glsl(
|
|||||||
.stdout(Stdio::piped())
|
.stdout(Stdio::piped())
|
||||||
.spawn()
|
.spawn()
|
||||||
.expect("Failed to call glslc");
|
.expect("Failed to call glslc");
|
||||||
cmd.stdin.as_ref().unwrap().write_all(data).unwrap();
|
|
||||||
println!("{shader_stage}");
|
|
||||||
let output = cmd.wait_with_output().expect("Error waiting for glslc");
|
let output = cmd.wait_with_output().expect("Error waiting for glslc");
|
||||||
assert!(output.status.success());
|
assert!(output.status.success());
|
||||||
unsafe {
|
unsafe {
|
||||||
@ -32,6 +28,38 @@ fn compile_glsl(
|
|||||||
})
|
})
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
fn compile_hlsl(device: &wgpu::Device, entry: &str, stage_str: &str) -> wgpu::ShaderModule {
|
||||||
|
let out_path = format!(
|
||||||
|
"{}/src/mesh_shader/shader.{stage_str}.cso",
|
||||||
|
env!("CARGO_MANIFEST_DIR")
|
||||||
|
);
|
||||||
|
let cmd = std::process::Command::new("dxc")
|
||||||
|
.args([
|
||||||
|
"-T",
|
||||||
|
&format!("{stage_str}_6_5"),
|
||||||
|
"-E",
|
||||||
|
entry,
|
||||||
|
&format!("{}/src/mesh_shader/shader.hlsl", env!("CARGO_MANIFEST_DIR")),
|
||||||
|
"-Fo",
|
||||||
|
&out_path,
|
||||||
|
])
|
||||||
|
.output()
|
||||||
|
.unwrap();
|
||||||
|
if !cmd.status.success() {
|
||||||
|
panic!("DXC failed:\n{}", String::from_utf8(cmd.stderr).unwrap());
|
||||||
|
}
|
||||||
|
let file = std::fs::read(&out_path).unwrap();
|
||||||
|
std::fs::remove_file(out_path).unwrap();
|
||||||
|
unsafe {
|
||||||
|
device.create_shader_module_passthrough(wgpu::ShaderModuleDescriptorPassthrough {
|
||||||
|
entry_point: entry.to_owned(),
|
||||||
|
label: None,
|
||||||
|
num_workgroups: (1, 1, 1),
|
||||||
|
dxil: Some(std::borrow::Cow::Owned(file)),
|
||||||
|
..Default::default()
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
pub struct Example {
|
pub struct Example {
|
||||||
pipeline: wgpu::RenderPipeline,
|
pipeline: wgpu::RenderPipeline,
|
||||||
@ -39,20 +67,30 @@ pub struct Example {
|
|||||||
impl crate::framework::Example for Example {
|
impl crate::framework::Example for Example {
|
||||||
fn init(
|
fn init(
|
||||||
config: &wgpu::SurfaceConfiguration,
|
config: &wgpu::SurfaceConfiguration,
|
||||||
_adapter: &wgpu::Adapter,
|
adapter: &wgpu::Adapter,
|
||||||
device: &wgpu::Device,
|
device: &wgpu::Device,
|
||||||
_queue: &wgpu::Queue,
|
_queue: &wgpu::Queue,
|
||||||
) -> Self {
|
) -> Self {
|
||||||
|
let (ts, ms, fs) = if adapter.get_info().backend == wgpu::Backend::Vulkan {
|
||||||
|
(
|
||||||
|
compile_glsl(device, "task"),
|
||||||
|
compile_glsl(device, "mesh"),
|
||||||
|
compile_glsl(device, "frag"),
|
||||||
|
)
|
||||||
|
} else if adapter.get_info().backend == wgpu::Backend::Dx12 {
|
||||||
|
(
|
||||||
|
compile_hlsl(device, "Task", "as"),
|
||||||
|
compile_hlsl(device, "Mesh", "ms"),
|
||||||
|
compile_hlsl(device, "Frag", "ps"),
|
||||||
|
)
|
||||||
|
} else {
|
||||||
|
panic!("Example can only run on vulkan or dx12");
|
||||||
|
};
|
||||||
let pipeline_layout = device.create_pipeline_layout(&wgpu::PipelineLayoutDescriptor {
|
let pipeline_layout = device.create_pipeline_layout(&wgpu::PipelineLayoutDescriptor {
|
||||||
label: None,
|
label: None,
|
||||||
bind_group_layouts: &[],
|
bind_group_layouts: &[],
|
||||||
push_constant_ranges: &[],
|
push_constant_ranges: &[],
|
||||||
});
|
});
|
||||||
let (ts, ms, fs) = (
|
|
||||||
compile_glsl(device, include_bytes!("shader.task"), "task"),
|
|
||||||
compile_glsl(device, include_bytes!("shader.mesh"), "mesh"),
|
|
||||||
compile_glsl(device, include_bytes!("shader.frag"), "frag"),
|
|
||||||
);
|
|
||||||
let pipeline = device.create_mesh_pipeline(&wgpu::MeshPipelineDescriptor {
|
let pipeline = device.create_mesh_pipeline(&wgpu::MeshPipelineDescriptor {
|
||||||
label: None,
|
label: None,
|
||||||
layout: Some(&pipeline_layout),
|
layout: Some(&pipeline_layout),
|
||||||
|
|||||||
53
examples/features/src/mesh_shader/shader.hlsl
Normal file
53
examples/features/src/mesh_shader/shader.hlsl
Normal file
@ -0,0 +1,53 @@
|
|||||||
|
struct OutVertex {
|
||||||
|
float4 Position : SV_POSITION;
|
||||||
|
float4 Color: COLOR;
|
||||||
|
};
|
||||||
|
struct OutPrimitive {
|
||||||
|
float4 ColorMask : COLOR_MASK : PRIMITIVE;
|
||||||
|
bool CullPrimitive: SV_CullPrimitive;
|
||||||
|
};
|
||||||
|
struct InVertex {
|
||||||
|
float4 Color: COLOR;
|
||||||
|
};
|
||||||
|
struct InPrimitive {
|
||||||
|
float4 ColorMask : COLOR_MASK : PRIMITIVE;
|
||||||
|
};
|
||||||
|
struct PayloadData {
|
||||||
|
float4 ColorMask;
|
||||||
|
bool Visible;
|
||||||
|
};
|
||||||
|
|
||||||
|
|
||||||
|
static const float4 positions[3] = {float4(0., 1.0, 0., 1.0), float4(-1.0, -1.0, 0., 1.0), float4(1.0, -1.0, 0., 1.0)};
|
||||||
|
static const float4 colors[3] = {float4(0., 1., 0., 1.), float4(0., 0., 1., 1.), float4(1., 0., 0., 1.)};
|
||||||
|
|
||||||
|
groupshared PayloadData outPayload;
|
||||||
|
|
||||||
|
[numthreads(1, 1, 1)]
|
||||||
|
void Task() {
|
||||||
|
outPayload.ColorMask = float4(1.0, 1.0, 0.0, 1.0);
|
||||||
|
outPayload.Visible = true;
|
||||||
|
DispatchMesh(3, 1, 1, outPayload);
|
||||||
|
}
|
||||||
|
|
||||||
|
[outputtopology("triangle")]
|
||||||
|
[numthreads(1, 1, 1)]
|
||||||
|
void Mesh(out indices uint3 triangles[1], out vertices OutVertex vertices[3], out primitives OutPrimitive primitives[1], in payload PayloadData payload) {
|
||||||
|
SetMeshOutputCounts(3, 1);
|
||||||
|
|
||||||
|
vertices[0].Position = positions[0];
|
||||||
|
vertices[1].Position = positions[1];
|
||||||
|
vertices[2].Position = positions[2];
|
||||||
|
|
||||||
|
vertices[0].Color = colors[0] * payload.ColorMask;
|
||||||
|
vertices[1].Color = colors[1] * payload.ColorMask;
|
||||||
|
vertices[2].Color = colors[2] * payload.ColorMask;
|
||||||
|
|
||||||
|
triangles[0] = uint3(0, 1, 2);
|
||||||
|
primitives[0].ColorMask = float4(1.0, 0.0, 0.0, 1.0);
|
||||||
|
primitives[0].CullPrimitive = !payload.Visible;
|
||||||
|
}
|
||||||
|
|
||||||
|
float4 Frag(InVertex vertex, InPrimitive primitive) : SV_Target {
|
||||||
|
return vertex.Color * primitive.ColorMask;
|
||||||
|
}
|
||||||
@ -20,18 +20,18 @@ vertexOutput[];
|
|||||||
layout(location = 1) perprimitiveEXT out PrimitiveOutput { vec4 colorMask; }
|
layout(location = 1) perprimitiveEXT out PrimitiveOutput { vec4 colorMask; }
|
||||||
primitiveOutput[];
|
primitiveOutput[];
|
||||||
|
|
||||||
shared uint sharedData;
|
|
||||||
|
|
||||||
layout(triangles, max_vertices = 3, max_primitives = 1) out;
|
layout(triangles, max_vertices = 3, max_primitives = 1) out;
|
||||||
void main() {
|
void main() {
|
||||||
sharedData = 5;
|
|
||||||
SetMeshOutputsEXT(3, 1);
|
SetMeshOutputsEXT(3, 1);
|
||||||
|
|
||||||
gl_MeshVerticesEXT[0].gl_Position = positions[0];
|
gl_MeshVerticesEXT[0].gl_Position = positions[0];
|
||||||
gl_MeshVerticesEXT[1].gl_Position = positions[1];
|
gl_MeshVerticesEXT[1].gl_Position = positions[1];
|
||||||
gl_MeshVerticesEXT[2].gl_Position = positions[2];
|
gl_MeshVerticesEXT[2].gl_Position = positions[2];
|
||||||
|
|
||||||
vertexOutput[0].color = colors[0] * payloadData.colorMask;
|
vertexOutput[0].color = colors[0] * payloadData.colorMask;
|
||||||
vertexOutput[1].color = colors[1] * payloadData.colorMask;
|
vertexOutput[1].color = colors[1] * payloadData.colorMask;
|
||||||
vertexOutput[2].color = colors[2] * payloadData.colorMask;
|
vertexOutput[2].color = colors[2] * payloadData.colorMask;
|
||||||
|
|
||||||
gl_PrimitiveTriangleIndicesEXT[gl_LocalInvocationIndex] = uvec3(0, 1, 2);
|
gl_PrimitiveTriangleIndicesEXT[gl_LocalInvocationIndex] = uvec3(0, 1, 2);
|
||||||
primitiveOutput[0].colorMask = vec4(1.0, 0.0, 1.0, 1.0);
|
primitiveOutput[0].colorMask = vec4(1.0, 0.0, 1.0, 1.0);
|
||||||
gl_MeshPrimitivesEXT[0].gl_CullPrimitiveEXT = !payloadData.visible;
|
gl_MeshPrimitivesEXT[0].gl_CullPrimitiveEXT = !payloadData.visible;
|
||||||
|
|||||||
@ -1,7 +1,7 @@
|
|||||||
#version 450
|
#version 450
|
||||||
#extension GL_EXT_mesh_shader : require
|
#extension GL_EXT_mesh_shader : require
|
||||||
|
|
||||||
layout(local_size_x = 4, local_size_y = 1, local_size_z = 1) in;
|
layout(local_size_x = 1, local_size_y = 1, local_size_z = 1) in;
|
||||||
|
|
||||||
struct TaskPayload {
|
struct TaskPayload {
|
||||||
vec4 colorMask;
|
vec4 colorMask;
|
||||||
|
|||||||
@ -283,7 +283,8 @@ impl crate::ShaderStage {
|
|||||||
Self::Vertex => "vs",
|
Self::Vertex => "vs",
|
||||||
Self::Fragment => "ps",
|
Self::Fragment => "ps",
|
||||||
Self::Compute => "cs",
|
Self::Compute => "cs",
|
||||||
Self::Task | Self::Mesh => unreachable!(),
|
Self::Task => "as",
|
||||||
|
Self::Mesh => "ms",
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@ -12,6 +12,7 @@ use crate::{
|
|||||||
GpuTestConfiguration,
|
GpuTestConfiguration,
|
||||||
};
|
};
|
||||||
|
|
||||||
|
#[derive(Hash)]
|
||||||
/// Parameters and resources handed to the test function.
|
/// Parameters and resources handed to the test function.
|
||||||
pub struct TestingContext {
|
pub struct TestingContext {
|
||||||
pub instance: Instance,
|
pub instance: Instance,
|
||||||
|
|||||||
41
tests/tests/wgpu-gpu/mesh_shader/basic.hlsl
Normal file
41
tests/tests/wgpu-gpu/mesh_shader/basic.hlsl
Normal file
@ -0,0 +1,41 @@
|
|||||||
|
struct OutVertex {
|
||||||
|
float4 Position : SV_POSITION;
|
||||||
|
float4 Color: COLOR;
|
||||||
|
};
|
||||||
|
struct InVertex {
|
||||||
|
float4 Color: COLOR;
|
||||||
|
};
|
||||||
|
|
||||||
|
|
||||||
|
static const float4 positions[3] = {float4(0., 1.0, 0., 1.0), float4(-1.0, -1.0, 0., 1.0), float4(1.0, -1.0, 0., 1.0)};
|
||||||
|
static const float4 colors[3] = {float4(0., 1., 0., 1.), float4(0., 0., 1., 1.), float4(1., 0., 0., 1.)};
|
||||||
|
|
||||||
|
struct EmptyPayload {
|
||||||
|
uint _nullField;
|
||||||
|
};
|
||||||
|
groupshared EmptyPayload _emptyPayload;
|
||||||
|
|
||||||
|
[numthreads(4, 1, 1)]
|
||||||
|
void Task() {
|
||||||
|
DispatchMesh(1, 1, 1, _emptyPayload);
|
||||||
|
}
|
||||||
|
|
||||||
|
[outputtopology("triangle")]
|
||||||
|
[numthreads(1, 1, 1)]
|
||||||
|
void Mesh(out indices uint3 triangles[1], out vertices OutVertex vertices[3], in payload EmptyPayload _emptyPayload) {
|
||||||
|
SetMeshOutputCounts(3, 1);
|
||||||
|
|
||||||
|
vertices[0].Position = positions[0];
|
||||||
|
vertices[1].Position = positions[1];
|
||||||
|
vertices[2].Position = positions[2];
|
||||||
|
|
||||||
|
vertices[0].Color = colors[0];
|
||||||
|
vertices[1].Color = colors[1];
|
||||||
|
vertices[2].Color = colors[2];
|
||||||
|
|
||||||
|
triangles[0] = uint3(0, 1, 2);
|
||||||
|
}
|
||||||
|
|
||||||
|
float4 Frag(InVertex vertex) : SV_Target {
|
||||||
|
return vertex.Color;
|
||||||
|
}
|
||||||
@ -1,4 +1,7 @@
|
|||||||
use std::{io::Write, process::Stdio};
|
use std::{
|
||||||
|
hash::{DefaultHasher, Hash, Hasher},
|
||||||
|
process::Stdio,
|
||||||
|
};
|
||||||
|
|
||||||
use wgpu::util::DeviceExt;
|
use wgpu::util::DeviceExt;
|
||||||
use wgpu_test::{
|
use wgpu_test::{
|
||||||
@ -14,19 +17,19 @@ pub fn all_tests(tests: &mut Vec<GpuTestInitializer>) {
|
|||||||
MESH_DRAW_INDIRECT,
|
MESH_DRAW_INDIRECT,
|
||||||
MESH_MULTI_DRAW_INDIRECT,
|
MESH_MULTI_DRAW_INDIRECT,
|
||||||
MESH_MULTI_DRAW_INDIRECT_COUNT,
|
MESH_MULTI_DRAW_INDIRECT_COUNT,
|
||||||
|
MESH_PIPELINE_BASIC_MESH_NO_DRAW,
|
||||||
|
MESH_PIPELINE_BASIC_TASK_MESH_FRAG_NO_DRAW,
|
||||||
]);
|
]);
|
||||||
}
|
}
|
||||||
|
|
||||||
// Same as in mesh shader example
|
// Same as in mesh shader example
|
||||||
fn compile_glsl(
|
fn compile_glsl(device: &wgpu::Device, shader_stage: &'static str) -> wgpu::ShaderModule {
|
||||||
device: &wgpu::Device,
|
|
||||||
data: &[u8],
|
|
||||||
shader_stage: &'static str,
|
|
||||||
) -> wgpu::ShaderModule {
|
|
||||||
let cmd = std::process::Command::new("glslc")
|
let cmd = std::process::Command::new("glslc")
|
||||||
.args([
|
.args([
|
||||||
&format!("-fshader-stage={shader_stage}"),
|
&format!(
|
||||||
"-",
|
"{}/tests/wgpu-gpu/mesh_shader/basic.{shader_stage}",
|
||||||
|
env!("CARGO_MANIFEST_DIR")
|
||||||
|
),
|
||||||
"-o",
|
"-o",
|
||||||
"-",
|
"-",
|
||||||
"--target-env=vulkan1.2",
|
"--target-env=vulkan1.2",
|
||||||
@ -36,8 +39,6 @@ fn compile_glsl(
|
|||||||
.stdout(Stdio::piped())
|
.stdout(Stdio::piped())
|
||||||
.spawn()
|
.spawn()
|
||||||
.expect("Failed to call glslc");
|
.expect("Failed to call glslc");
|
||||||
cmd.stdin.as_ref().unwrap().write_all(data).unwrap();
|
|
||||||
println!("{shader_stage}");
|
|
||||||
let output = cmd.wait_with_output().expect("Error waiting for glslc");
|
let output = cmd.wait_with_output().expect("Error waiting for glslc");
|
||||||
assert!(output.status.success());
|
assert!(output.status.success());
|
||||||
unsafe {
|
unsafe {
|
||||||
@ -50,6 +51,70 @@ fn compile_glsl(
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn compile_hlsl(
|
||||||
|
device: &wgpu::Device,
|
||||||
|
entry: &str,
|
||||||
|
stage_str: &str,
|
||||||
|
test_name: &str,
|
||||||
|
) -> wgpu::ShaderModule {
|
||||||
|
// Each test needs its own files
|
||||||
|
let out_path = format!(
|
||||||
|
"{}/tests/wgpu-gpu/mesh_shader/{test_name}.{stage_str}.cso",
|
||||||
|
env!("CARGO_MANIFEST_DIR")
|
||||||
|
);
|
||||||
|
let cmd = std::process::Command::new("dxc")
|
||||||
|
.args([
|
||||||
|
"-T",
|
||||||
|
&format!("{stage_str}_6_5"),
|
||||||
|
"-E",
|
||||||
|
entry,
|
||||||
|
&format!(
|
||||||
|
"{}/tests/wgpu-gpu/mesh_shader/basic.hlsl",
|
||||||
|
env!("CARGO_MANIFEST_DIR")
|
||||||
|
),
|
||||||
|
"-Fo",
|
||||||
|
&out_path,
|
||||||
|
])
|
||||||
|
.output()
|
||||||
|
.unwrap();
|
||||||
|
if !cmd.status.success() {
|
||||||
|
panic!("DXC failed:\n{}", String::from_utf8(cmd.stderr).unwrap());
|
||||||
|
}
|
||||||
|
let file = std::fs::read(&out_path).unwrap();
|
||||||
|
std::fs::remove_file(out_path).unwrap();
|
||||||
|
unsafe {
|
||||||
|
device.create_shader_module_passthrough(wgpu::ShaderModuleDescriptorPassthrough {
|
||||||
|
entry_point: entry.to_owned(),
|
||||||
|
label: None,
|
||||||
|
num_workgroups: (1, 1, 1),
|
||||||
|
dxil: Some(std::borrow::Cow::Owned(file)),
|
||||||
|
..Default::default()
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn get_shaders(
|
||||||
|
device: &wgpu::Device,
|
||||||
|
backend: wgpu::Backend,
|
||||||
|
test_name: &str,
|
||||||
|
) -> (wgpu::ShaderModule, wgpu::ShaderModule, wgpu::ShaderModule) {
|
||||||
|
if backend == wgpu::Backend::Vulkan {
|
||||||
|
(
|
||||||
|
compile_glsl(device, "task"),
|
||||||
|
compile_glsl(device, "mesh"),
|
||||||
|
compile_glsl(device, "frag"),
|
||||||
|
)
|
||||||
|
} else if backend == wgpu::Backend::Dx12 {
|
||||||
|
(
|
||||||
|
compile_hlsl(device, "Task", "as", test_name),
|
||||||
|
compile_hlsl(device, "Mesh", "ms", test_name),
|
||||||
|
compile_hlsl(device, "Frag", "ps", test_name),
|
||||||
|
)
|
||||||
|
} else {
|
||||||
|
unreachable!()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
fn create_depth(
|
fn create_depth(
|
||||||
device: &wgpu::Device,
|
device: &wgpu::Device,
|
||||||
) -> (wgpu::Texture, wgpu::TextureView, wgpu::DepthStencilState) {
|
) -> (wgpu::Texture, wgpu::TextureView, wgpu::DepthStencilState) {
|
||||||
@ -79,18 +144,30 @@ fn create_depth(
|
|||||||
(depth_texture, depth_view, state)
|
(depth_texture, depth_view, state)
|
||||||
}
|
}
|
||||||
|
|
||||||
fn mesh_pipeline_build(
|
struct MeshPipelineTestInfo {
|
||||||
ctx: &TestingContext,
|
use_task: bool,
|
||||||
task: Option<&[u8]>,
|
use_frag: bool,
|
||||||
mesh: &[u8],
|
|
||||||
frag: Option<&[u8]>,
|
|
||||||
draw: bool,
|
draw: bool,
|
||||||
) {
|
}
|
||||||
|
|
||||||
|
fn hash_testing_context(ctx: &TestingContext) -> u64 {
|
||||||
|
let mut hasher = DefaultHasher::new();
|
||||||
|
ctx.hash(&mut hasher);
|
||||||
|
hasher.finish()
|
||||||
|
}
|
||||||
|
|
||||||
|
fn mesh_pipeline_build(ctx: &TestingContext, info: MeshPipelineTestInfo) {
|
||||||
|
let backend = ctx.adapter.get_info().backend;
|
||||||
|
if backend != wgpu::Backend::Vulkan && backend != wgpu::Backend::Dx12 {
|
||||||
|
return;
|
||||||
|
}
|
||||||
let device = &ctx.device;
|
let device = &ctx.device;
|
||||||
let (_depth_image, depth_view, depth_state) = create_depth(device);
|
let (_depth_image, depth_view, depth_state) = create_depth(device);
|
||||||
let task = task.map(|t| compile_glsl(device, t, "task"));
|
|
||||||
let mesh = compile_glsl(device, mesh, "mesh");
|
let test_hash = hash_testing_context(ctx).to_string();
|
||||||
let frag = frag.map(|f| compile_glsl(device, f, "frag"));
|
let (task, mesh, frag) = get_shaders(device, backend, &test_hash);
|
||||||
|
let task = if info.use_task { Some(task) } else { None };
|
||||||
|
let frag = if info.use_frag { Some(frag) } else { None };
|
||||||
let layout = device.create_pipeline_layout(&wgpu::PipelineLayoutDescriptor {
|
let layout = device.create_pipeline_layout(&wgpu::PipelineLayoutDescriptor {
|
||||||
label: None,
|
label: None,
|
||||||
bind_group_layouts: &[],
|
bind_group_layouts: &[],
|
||||||
@ -124,7 +201,7 @@ fn mesh_pipeline_build(
|
|||||||
multiview: None,
|
multiview: None,
|
||||||
cache: None,
|
cache: None,
|
||||||
});
|
});
|
||||||
if draw {
|
if info.draw {
|
||||||
let mut encoder =
|
let mut encoder =
|
||||||
device.create_command_encoder(&wgpu::CommandEncoderDescriptor { label: None });
|
device.create_command_encoder(&wgpu::CommandEncoderDescriptor { label: None });
|
||||||
{
|
{
|
||||||
@ -160,11 +237,14 @@ pub enum DrawType {
|
|||||||
}
|
}
|
||||||
|
|
||||||
fn mesh_draw(ctx: &TestingContext, draw_type: DrawType) {
|
fn mesh_draw(ctx: &TestingContext, draw_type: DrawType) {
|
||||||
|
let backend = ctx.adapter.get_info().backend;
|
||||||
|
if backend != wgpu::Backend::Vulkan && backend != wgpu::Backend::Dx12 {
|
||||||
|
return;
|
||||||
|
}
|
||||||
let device = &ctx.device;
|
let device = &ctx.device;
|
||||||
let (_depth_image, depth_view, depth_state) = create_depth(device);
|
let (_depth_image, depth_view, depth_state) = create_depth(device);
|
||||||
let task = compile_glsl(device, BASIC_TASK, "task");
|
let test_hash = hash_testing_context(ctx).to_string();
|
||||||
let mesh = compile_glsl(device, BASIC_MESH, "mesh");
|
let (task, mesh, frag) = get_shaders(device, backend, &test_hash);
|
||||||
let frag = compile_glsl(device, NO_WRITE_FRAG, "frag");
|
|
||||||
let layout = device.create_pipeline_layout(&wgpu::PipelineLayoutDescriptor {
|
let layout = device.create_pipeline_layout(&wgpu::PipelineLayoutDescriptor {
|
||||||
label: None,
|
label: None,
|
||||||
bind_group_layouts: &[],
|
bind_group_layouts: &[],
|
||||||
@ -256,11 +336,6 @@ fn mesh_draw(ctx: &TestingContext, draw_type: DrawType) {
|
|||||||
ctx.device.poll(wgpu::PollType::Wait).unwrap();
|
ctx.device.poll(wgpu::PollType::Wait).unwrap();
|
||||||
}
|
}
|
||||||
|
|
||||||
const BASIC_TASK: &[u8] = include_bytes!("basic.task");
|
|
||||||
const BASIC_MESH: &[u8] = include_bytes!("basic.mesh");
|
|
||||||
//const BASIC_FRAG: &[u8] = include_bytes!("basic.frag.spv");
|
|
||||||
const NO_WRITE_FRAG: &[u8] = include_bytes!("no-write.frag");
|
|
||||||
|
|
||||||
fn default_gpu_test_config(draw_type: DrawType) -> GpuTestConfiguration {
|
fn default_gpu_test_config(draw_type: DrawType) -> GpuTestConfiguration {
|
||||||
GpuTestConfiguration::new().parameters(
|
GpuTestConfiguration::new().parameters(
|
||||||
TestParameters::default()
|
TestParameters::default()
|
||||||
@ -279,47 +354,92 @@ fn default_gpu_test_config(draw_type: DrawType) -> GpuTestConfiguration {
|
|||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
// Mesh pipeline configs
|
|
||||||
#[gpu_test]
|
#[gpu_test]
|
||||||
static MESH_PIPELINE_BASIC_MESH: GpuTestConfiguration = default_gpu_test_config(DrawType::Standard)
|
pub static MESH_PIPELINE_BASIC_MESH: GpuTestConfiguration =
|
||||||
.run_sync(|ctx| {
|
|
||||||
mesh_pipeline_build(&ctx, None, BASIC_MESH, None, true);
|
|
||||||
});
|
|
||||||
#[gpu_test]
|
|
||||||
static MESH_PIPELINE_BASIC_TASK_MESH: GpuTestConfiguration =
|
|
||||||
default_gpu_test_config(DrawType::Standard).run_sync(|ctx| {
|
|
||||||
mesh_pipeline_build(&ctx, Some(BASIC_TASK), BASIC_MESH, None, true);
|
|
||||||
});
|
|
||||||
#[gpu_test]
|
|
||||||
static MESH_PIPELINE_BASIC_MESH_FRAG: GpuTestConfiguration =
|
|
||||||
default_gpu_test_config(DrawType::Standard).run_sync(|ctx| {
|
|
||||||
mesh_pipeline_build(&ctx, None, BASIC_MESH, Some(NO_WRITE_FRAG), true);
|
|
||||||
});
|
|
||||||
#[gpu_test]
|
|
||||||
static MESH_PIPELINE_BASIC_TASK_MESH_FRAG: GpuTestConfiguration =
|
|
||||||
default_gpu_test_config(DrawType::Standard).run_sync(|ctx| {
|
default_gpu_test_config(DrawType::Standard).run_sync(|ctx| {
|
||||||
mesh_pipeline_build(
|
mesh_pipeline_build(
|
||||||
&ctx,
|
&ctx,
|
||||||
Some(BASIC_TASK),
|
MeshPipelineTestInfo {
|
||||||
BASIC_MESH,
|
use_task: false,
|
||||||
Some(NO_WRITE_FRAG),
|
use_frag: false,
|
||||||
true,
|
draw: true,
|
||||||
|
},
|
||||||
|
);
|
||||||
|
});
|
||||||
|
#[gpu_test]
|
||||||
|
pub static MESH_PIPELINE_BASIC_TASK_MESH: GpuTestConfiguration =
|
||||||
|
default_gpu_test_config(DrawType::Standard).run_sync(|ctx| {
|
||||||
|
mesh_pipeline_build(
|
||||||
|
&ctx,
|
||||||
|
MeshPipelineTestInfo {
|
||||||
|
use_task: true,
|
||||||
|
use_frag: false,
|
||||||
|
draw: true,
|
||||||
|
},
|
||||||
|
);
|
||||||
|
});
|
||||||
|
#[gpu_test]
|
||||||
|
pub static MESH_PIPELINE_BASIC_MESH_FRAG: GpuTestConfiguration =
|
||||||
|
default_gpu_test_config(DrawType::Standard).run_sync(|ctx| {
|
||||||
|
mesh_pipeline_build(
|
||||||
|
&ctx,
|
||||||
|
MeshPipelineTestInfo {
|
||||||
|
use_task: false,
|
||||||
|
use_frag: true,
|
||||||
|
draw: true,
|
||||||
|
},
|
||||||
|
);
|
||||||
|
});
|
||||||
|
#[gpu_test]
|
||||||
|
pub static MESH_PIPELINE_BASIC_TASK_MESH_FRAG: GpuTestConfiguration =
|
||||||
|
default_gpu_test_config(DrawType::Standard).run_sync(|ctx| {
|
||||||
|
mesh_pipeline_build(
|
||||||
|
&ctx,
|
||||||
|
MeshPipelineTestInfo {
|
||||||
|
use_task: true,
|
||||||
|
use_frag: true,
|
||||||
|
draw: true,
|
||||||
|
},
|
||||||
|
);
|
||||||
|
});
|
||||||
|
#[gpu_test]
|
||||||
|
pub static MESH_PIPELINE_BASIC_MESH_NO_DRAW: GpuTestConfiguration =
|
||||||
|
default_gpu_test_config(DrawType::Standard).run_sync(|ctx| {
|
||||||
|
mesh_pipeline_build(
|
||||||
|
&ctx,
|
||||||
|
MeshPipelineTestInfo {
|
||||||
|
use_task: false,
|
||||||
|
use_frag: false,
|
||||||
|
draw: false,
|
||||||
|
},
|
||||||
|
);
|
||||||
|
});
|
||||||
|
#[gpu_test]
|
||||||
|
pub static MESH_PIPELINE_BASIC_TASK_MESH_FRAG_NO_DRAW: GpuTestConfiguration =
|
||||||
|
default_gpu_test_config(DrawType::Standard).run_sync(|ctx| {
|
||||||
|
mesh_pipeline_build(
|
||||||
|
&ctx,
|
||||||
|
MeshPipelineTestInfo {
|
||||||
|
use_task: true,
|
||||||
|
use_frag: true,
|
||||||
|
draw: false,
|
||||||
|
},
|
||||||
);
|
);
|
||||||
});
|
});
|
||||||
|
|
||||||
// Mesh draw
|
// Mesh draw
|
||||||
#[gpu_test]
|
#[gpu_test]
|
||||||
static MESH_DRAW_INDIRECT: GpuTestConfiguration = default_gpu_test_config(DrawType::Indirect)
|
pub static MESH_DRAW_INDIRECT: GpuTestConfiguration = default_gpu_test_config(DrawType::Indirect)
|
||||||
.run_sync(|ctx| {
|
.run_sync(|ctx| {
|
||||||
mesh_draw(&ctx, DrawType::Indirect);
|
mesh_draw(&ctx, DrawType::Indirect);
|
||||||
});
|
});
|
||||||
#[gpu_test]
|
#[gpu_test]
|
||||||
static MESH_MULTI_DRAW_INDIRECT: GpuTestConfiguration =
|
pub static MESH_MULTI_DRAW_INDIRECT: GpuTestConfiguration =
|
||||||
default_gpu_test_config(DrawType::MultiIndirect).run_sync(|ctx| {
|
default_gpu_test_config(DrawType::MultiIndirect).run_sync(|ctx| {
|
||||||
mesh_draw(&ctx, DrawType::MultiIndirect);
|
mesh_draw(&ctx, DrawType::MultiIndirect);
|
||||||
});
|
});
|
||||||
#[gpu_test]
|
#[gpu_test]
|
||||||
static MESH_MULTI_DRAW_INDIRECT_COUNT: GpuTestConfiguration =
|
pub static MESH_MULTI_DRAW_INDIRECT_COUNT: GpuTestConfiguration =
|
||||||
default_gpu_test_config(DrawType::MultiIndirectCount).run_sync(|ctx| {
|
default_gpu_test_config(DrawType::MultiIndirectCount).run_sync(|ctx| {
|
||||||
mesh_draw(&ctx, DrawType::MultiIndirectCount);
|
mesh_draw(&ctx, DrawType::MultiIndirectCount);
|
||||||
});
|
});
|
||||||
|
|||||||
@ -1,7 +0,0 @@
|
|||||||
#version 450
|
|
||||||
#extension GL_EXT_mesh_shader : require
|
|
||||||
|
|
||||||
in VertexInput { layout(location = 0) vec4 color; }
|
|
||||||
vertexInput;
|
|
||||||
|
|
||||||
void main() {}
|
|
||||||
@ -20,6 +20,9 @@ extend-exclude = [
|
|||||||
lod = "lod"
|
lod = "lod"
|
||||||
metalness = "metalness"
|
metalness = "metalness"
|
||||||
|
|
||||||
|
# A DXC command line argument
|
||||||
|
Fo = "Fo"
|
||||||
|
|
||||||
# Usernames
|
# Usernames
|
||||||
Healthire = "Healthire"
|
Healthire = "Healthire"
|
||||||
REASY = "REASY"
|
REASY = "REASY"
|
||||||
|
|||||||
@ -528,6 +528,22 @@ impl super::Adapter {
|
|||||||
wgt::Features::SHADER_INT64_ATOMIC_ALL_OPS | wgt::Features::SHADER_INT64_ATOMIC_MIN_MAX,
|
wgt::Features::SHADER_INT64_ATOMIC_ALL_OPS | wgt::Features::SHADER_INT64_ATOMIC_MIN_MAX,
|
||||||
atomic_int64_on_typed_resource_supported,
|
atomic_int64_on_typed_resource_supported,
|
||||||
);
|
);
|
||||||
|
let mesh_shader_supported = {
|
||||||
|
let mut features7 = Direct3D12::D3D12_FEATURE_DATA_D3D12_OPTIONS7::default();
|
||||||
|
unsafe {
|
||||||
|
device.CheckFeatureSupport(
|
||||||
|
Direct3D12::D3D12_FEATURE_D3D12_OPTIONS7,
|
||||||
|
<*mut _>::cast(&mut features7),
|
||||||
|
size_of_val(&features7) as u32,
|
||||||
|
)
|
||||||
|
}
|
||||||
|
.is_ok()
|
||||||
|
&& features7.MeshShaderTier != Direct3D12::D3D12_MESH_SHADER_TIER_NOT_SUPPORTED
|
||||||
|
};
|
||||||
|
features.set(
|
||||||
|
wgt::Features::EXPERIMENTAL_MESH_SHADER,
|
||||||
|
mesh_shader_supported,
|
||||||
|
);
|
||||||
|
|
||||||
// TODO: Determine if IPresentationManager is supported
|
// TODO: Determine if IPresentationManager is supported
|
||||||
let presentation_timer = auxil::dxgi::time::PresentationTimer::new_dxgi();
|
let presentation_timer = auxil::dxgi::time::PresentationTimer::new_dxgi();
|
||||||
@ -648,10 +664,15 @@ impl super::Adapter {
|
|||||||
max_buffer_size: i32::MAX as u64,
|
max_buffer_size: i32::MAX as u64,
|
||||||
max_non_sampler_bindings: 1_000_000,
|
max_non_sampler_bindings: 1_000_000,
|
||||||
|
|
||||||
max_task_workgroup_total_count: 0,
|
// Source: https://microsoft.github.io/DirectX-Specs/d3d/MeshShader.html#dispatchmesh-api
|
||||||
max_task_workgroups_per_dimension: 0,
|
max_task_workgroup_total_count: 2u32.pow(22),
|
||||||
|
// Technically it says "64k" but I highly doubt they want 65536 for compute and exactly 64,000 for task workgroups
|
||||||
|
max_task_workgroups_per_dimension:
|
||||||
|
Direct3D12::D3D12_CS_DISPATCH_MAX_THREAD_GROUPS_PER_DIMENSION,
|
||||||
|
// Multiview not supported by WGPU yet
|
||||||
max_mesh_multiview_count: 0,
|
max_mesh_multiview_count: 0,
|
||||||
max_mesh_output_layers: 0,
|
// This seems to be right, and I can't find anything to suggest it would be less than the 2048 provided here
|
||||||
|
max_mesh_output_layers: Direct3D12::D3D12_REQ_TEXTURE2D_ARRAY_AXIS_DIMENSION,
|
||||||
|
|
||||||
max_blas_primitive_count: if supports_ray_tracing {
|
max_blas_primitive_count: if supports_ray_tracing {
|
||||||
1 << 29 // 2^29
|
1 << 29 // 2^29
|
||||||
|
|||||||
@ -1228,11 +1228,16 @@ impl crate::CommandEncoder for super::CommandEncoder {
|
|||||||
}
|
}
|
||||||
unsafe fn draw_mesh_tasks(
|
unsafe fn draw_mesh_tasks(
|
||||||
&mut self,
|
&mut self,
|
||||||
_group_count_x: u32,
|
group_count_x: u32,
|
||||||
_group_count_y: u32,
|
group_count_y: u32,
|
||||||
_group_count_z: u32,
|
group_count_z: u32,
|
||||||
) {
|
) {
|
||||||
unreachable!()
|
self.prepare_dispatch([group_count_x, group_count_y, group_count_z]);
|
||||||
|
let cmd_list6: Direct3D12::ID3D12GraphicsCommandList6 =
|
||||||
|
self.list.as_ref().unwrap().cast().unwrap();
|
||||||
|
unsafe {
|
||||||
|
cmd_list6.DispatchMesh(group_count_x, group_count_y, group_count_z);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
unsafe fn draw_indirect(
|
unsafe fn draw_indirect(
|
||||||
&mut self,
|
&mut self,
|
||||||
@ -1314,11 +1319,36 @@ impl crate::CommandEncoder for super::CommandEncoder {
|
|||||||
}
|
}
|
||||||
unsafe fn draw_mesh_tasks_indirect(
|
unsafe fn draw_mesh_tasks_indirect(
|
||||||
&mut self,
|
&mut self,
|
||||||
_buffer: &<Self::A as crate::Api>::Buffer,
|
buffer: &<Self::A as crate::Api>::Buffer,
|
||||||
_offset: wgt::BufferAddress,
|
offset: wgt::BufferAddress,
|
||||||
_draw_count: u32,
|
draw_count: u32,
|
||||||
) {
|
) {
|
||||||
unreachable!()
|
if self
|
||||||
|
.pass
|
||||||
|
.layout
|
||||||
|
.special_constants
|
||||||
|
.as_ref()
|
||||||
|
.and_then(|sc| sc.indirect_cmd_signatures.as_ref())
|
||||||
|
.is_some()
|
||||||
|
{
|
||||||
|
self.update_root_elements();
|
||||||
|
} else {
|
||||||
|
self.prepare_dispatch([0; 3]);
|
||||||
|
}
|
||||||
|
|
||||||
|
let cmd_list6: Direct3D12::ID3D12GraphicsCommandList6 =
|
||||||
|
self.list.as_ref().unwrap().cast().unwrap();
|
||||||
|
let cmd_signature = &self
|
||||||
|
.pass
|
||||||
|
.layout
|
||||||
|
.special_constants
|
||||||
|
.as_ref()
|
||||||
|
.and_then(|sc| sc.indirect_cmd_signatures.as_ref())
|
||||||
|
.unwrap_or_else(|| &self.shared.cmd_signatures)
|
||||||
|
.draw_mesh;
|
||||||
|
unsafe {
|
||||||
|
cmd_list6.ExecuteIndirect(cmd_signature, draw_count, &buffer.resource, offset, None, 0);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
unsafe fn draw_indirect_count(
|
unsafe fn draw_indirect_count(
|
||||||
&mut self,
|
&mut self,
|
||||||
@ -1362,13 +1392,25 @@ impl crate::CommandEncoder for super::CommandEncoder {
|
|||||||
}
|
}
|
||||||
unsafe fn draw_mesh_tasks_indirect_count(
|
unsafe fn draw_mesh_tasks_indirect_count(
|
||||||
&mut self,
|
&mut self,
|
||||||
_buffer: &<Self::A as crate::Api>::Buffer,
|
buffer: &<Self::A as crate::Api>::Buffer,
|
||||||
_offset: wgt::BufferAddress,
|
offset: wgt::BufferAddress,
|
||||||
_count_buffer: &<Self::A as crate::Api>::Buffer,
|
count_buffer: &<Self::A as crate::Api>::Buffer,
|
||||||
_count_offset: wgt::BufferAddress,
|
count_offset: wgt::BufferAddress,
|
||||||
_max_count: u32,
|
max_count: u32,
|
||||||
) {
|
) {
|
||||||
unreachable!()
|
self.prepare_dispatch([0; 3]);
|
||||||
|
let cmd_list6: Direct3D12::ID3D12GraphicsCommandList6 =
|
||||||
|
self.list.as_ref().unwrap().cast().unwrap();
|
||||||
|
unsafe {
|
||||||
|
cmd_list6.ExecuteIndirect(
|
||||||
|
&self.shared.cmd_signatures.draw_mesh,
|
||||||
|
max_count,
|
||||||
|
&buffer.resource,
|
||||||
|
offset,
|
||||||
|
&count_buffer.resource,
|
||||||
|
count_offset,
|
||||||
|
);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// compute
|
// compute
|
||||||
|
|||||||
@ -140,6 +140,16 @@ impl super::Device {
|
|||||||
}],
|
}],
|
||||||
0,
|
0,
|
||||||
)?,
|
)?,
|
||||||
|
draw_mesh: Self::create_command_signature(
|
||||||
|
&raw,
|
||||||
|
None,
|
||||||
|
size_of::<wgt::DispatchIndirectArgs>(),
|
||||||
|
&[Direct3D12::D3D12_INDIRECT_ARGUMENT_DESC {
|
||||||
|
Type: Direct3D12::D3D12_INDIRECT_ARGUMENT_TYPE_DISPATCH_MESH,
|
||||||
|
..Default::default()
|
||||||
|
}],
|
||||||
|
0,
|
||||||
|
)?,
|
||||||
dispatch: Self::create_command_signature(
|
dispatch: Self::create_command_signature(
|
||||||
&raw,
|
&raw,
|
||||||
None,
|
None,
|
||||||
@ -1394,6 +1404,19 @@ impl crate::Device for super::Device {
|
|||||||
],
|
],
|
||||||
0,
|
0,
|
||||||
)?,
|
)?,
|
||||||
|
draw_mesh: Self::create_command_signature(
|
||||||
|
&self.raw,
|
||||||
|
Some(&raw),
|
||||||
|
special_constant_buffer_args_len + size_of::<wgt::DispatchIndirectArgs>(),
|
||||||
|
&[
|
||||||
|
constant_indirect_argument_desc,
|
||||||
|
Direct3D12::D3D12_INDIRECT_ARGUMENT_DESC {
|
||||||
|
Type: Direct3D12::D3D12_INDIRECT_ARGUMENT_TYPE_DISPATCH_MESH,
|
||||||
|
..Default::default()
|
||||||
|
},
|
||||||
|
],
|
||||||
|
0,
|
||||||
|
)?,
|
||||||
dispatch: Self::create_command_signature(
|
dispatch: Self::create_command_signature(
|
||||||
&self.raw,
|
&self.raw,
|
||||||
Some(&raw),
|
Some(&raw),
|
||||||
@ -1825,60 +1848,10 @@ impl crate::Device for super::Device {
|
|||||||
super::PipelineCache,
|
super::PipelineCache,
|
||||||
>,
|
>,
|
||||||
) -> Result<super::RenderPipeline, crate::PipelineError> {
|
) -> Result<super::RenderPipeline, crate::PipelineError> {
|
||||||
|
let mut shader_stages = wgt::ShaderStages::empty();
|
||||||
|
let root_signature =
|
||||||
|
unsafe { borrow_optional_interface_temporarily(&desc.layout.shared.signature) };
|
||||||
let (topology_class, topology) = conv::map_topology(desc.primitive.topology);
|
let (topology_class, topology) = conv::map_topology(desc.primitive.topology);
|
||||||
let mut shader_stages = wgt::ShaderStages::VERTEX;
|
|
||||||
|
|
||||||
let (vertex_stage_desc, vertex_buffers_desc) = match &desc.vertex_processor {
|
|
||||||
crate::VertexProcessor::Standard {
|
|
||||||
vertex_buffers,
|
|
||||||
vertex_stage,
|
|
||||||
} => (vertex_stage, *vertex_buffers),
|
|
||||||
crate::VertexProcessor::Mesh { .. } => unreachable!(),
|
|
||||||
};
|
|
||||||
|
|
||||||
let blob_vs = self.load_shader(
|
|
||||||
vertex_stage_desc,
|
|
||||||
desc.layout,
|
|
||||||
naga::ShaderStage::Vertex,
|
|
||||||
desc.fragment_stage.as_ref(),
|
|
||||||
)?;
|
|
||||||
let blob_fs = match desc.fragment_stage {
|
|
||||||
Some(ref stage) => {
|
|
||||||
shader_stages |= wgt::ShaderStages::FRAGMENT;
|
|
||||||
Some(self.load_shader(stage, desc.layout, naga::ShaderStage::Fragment, None)?)
|
|
||||||
}
|
|
||||||
None => None,
|
|
||||||
};
|
|
||||||
|
|
||||||
let mut vertex_strides = [None; crate::MAX_VERTEX_BUFFERS];
|
|
||||||
let mut input_element_descs = Vec::new();
|
|
||||||
for (i, (stride, vbuf)) in vertex_strides
|
|
||||||
.iter_mut()
|
|
||||||
.zip(vertex_buffers_desc)
|
|
||||||
.enumerate()
|
|
||||||
{
|
|
||||||
*stride = NonZeroU32::new(vbuf.array_stride as u32);
|
|
||||||
let (slot_class, step_rate) = match vbuf.step_mode {
|
|
||||||
wgt::VertexStepMode::Vertex => {
|
|
||||||
(Direct3D12::D3D12_INPUT_CLASSIFICATION_PER_VERTEX_DATA, 0)
|
|
||||||
}
|
|
||||||
wgt::VertexStepMode::Instance => {
|
|
||||||
(Direct3D12::D3D12_INPUT_CLASSIFICATION_PER_INSTANCE_DATA, 1)
|
|
||||||
}
|
|
||||||
};
|
|
||||||
for attribute in vbuf.attributes {
|
|
||||||
input_element_descs.push(Direct3D12::D3D12_INPUT_ELEMENT_DESC {
|
|
||||||
SemanticName: windows::core::PCSTR(NAGA_LOCATION_SEMANTIC.as_ptr()),
|
|
||||||
SemanticIndex: attribute.shader_location,
|
|
||||||
Format: auxil::dxgi::conv::map_vertex_format(attribute.format),
|
|
||||||
InputSlot: i as u32,
|
|
||||||
AlignedByteOffset: attribute.offset as u32,
|
|
||||||
InputSlotClass: slot_class,
|
|
||||||
InstanceDataStepRate: step_rate,
|
|
||||||
});
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
let mut rtv_formats = [Dxgi::Common::DXGI_FORMAT_UNKNOWN;
|
let mut rtv_formats = [Dxgi::Common::DXGI_FORMAT_UNKNOWN;
|
||||||
Direct3D12::D3D12_SIMULTANEOUS_RENDER_TARGET_COUNT as usize];
|
Direct3D12::D3D12_SIMULTANEOUS_RENDER_TARGET_COUNT as usize];
|
||||||
for (rtv_format, ct) in rtv_formats.iter_mut().zip(desc.color_targets) {
|
for (rtv_format, ct) in rtv_formats.iter_mut().zip(desc.color_targets) {
|
||||||
@ -1893,7 +1866,7 @@ impl crate::Device for super::Device {
|
|||||||
.map(|ds| ds.bias)
|
.map(|ds| ds.bias)
|
||||||
.unwrap_or_default();
|
.unwrap_or_default();
|
||||||
|
|
||||||
let raw_rasterizer = Direct3D12::D3D12_RASTERIZER_DESC {
|
let rasterizer_state = Direct3D12::D3D12_RASTERIZER_DESC {
|
||||||
FillMode: conv::map_polygon_mode(desc.primitive.polygon_mode),
|
FillMode: conv::map_polygon_mode(desc.primitive.polygon_mode),
|
||||||
CullMode: match desc.primitive.cull_mode {
|
CullMode: match desc.primitive.cull_mode {
|
||||||
None => Direct3D12::D3D12_CULL_MODE_NONE,
|
None => Direct3D12::D3D12_CULL_MODE_NONE,
|
||||||
@ -1917,39 +1890,101 @@ impl crate::Device for super::Device {
|
|||||||
Direct3D12::D3D12_CONSERVATIVE_RASTERIZATION_MODE_OFF
|
Direct3D12::D3D12_CONSERVATIVE_RASTERIZATION_MODE_OFF
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
|
let blob_fs = match desc.fragment_stage {
|
||||||
let raw_desc = Direct3D12::D3D12_GRAPHICS_PIPELINE_STATE_DESC {
|
Some(ref stage) => {
|
||||||
pRootSignature: unsafe {
|
shader_stages |= wgt::ShaderStages::FRAGMENT;
|
||||||
borrow_optional_interface_temporarily(&desc.layout.shared.signature)
|
Some(self.load_shader(stage, desc.layout, naga::ShaderStage::Fragment, None)?)
|
||||||
},
|
}
|
||||||
VS: blob_vs.create_native_shader(),
|
None => None,
|
||||||
PS: match &blob_fs {
|
};
|
||||||
|
let pixel_shader = match &blob_fs {
|
||||||
Some(shader) => shader.create_native_shader(),
|
Some(shader) => shader.create_native_shader(),
|
||||||
None => Direct3D12::D3D12_SHADER_BYTECODE::default(),
|
None => Direct3D12::D3D12_SHADER_BYTECODE::default(),
|
||||||
},
|
};
|
||||||
GS: Direct3D12::D3D12_SHADER_BYTECODE::default(),
|
let mut vertex_strides = [None; crate::MAX_VERTEX_BUFFERS];
|
||||||
DS: Direct3D12::D3D12_SHADER_BYTECODE::default(),
|
let stream_output = Direct3D12::D3D12_STREAM_OUTPUT_DESC {
|
||||||
HS: Direct3D12::D3D12_SHADER_BYTECODE::default(),
|
|
||||||
StreamOutput: Direct3D12::D3D12_STREAM_OUTPUT_DESC {
|
|
||||||
pSODeclaration: ptr::null(),
|
pSODeclaration: ptr::null(),
|
||||||
NumEntries: 0,
|
NumEntries: 0,
|
||||||
pBufferStrides: ptr::null(),
|
pBufferStrides: ptr::null(),
|
||||||
NumStrides: 0,
|
NumStrides: 0,
|
||||||
RasterizedStream: 0,
|
RasterizedStream: 0,
|
||||||
},
|
};
|
||||||
BlendState: Direct3D12::D3D12_BLEND_DESC {
|
let blend_state = Direct3D12::D3D12_BLEND_DESC {
|
||||||
AlphaToCoverageEnable: Foundation::BOOL::from(
|
AlphaToCoverageEnable: Foundation::BOOL::from(
|
||||||
desc.multisample.alpha_to_coverage_enabled,
|
desc.multisample.alpha_to_coverage_enabled,
|
||||||
),
|
),
|
||||||
IndependentBlendEnable: true.into(),
|
IndependentBlendEnable: true.into(),
|
||||||
RenderTarget: conv::map_render_targets(desc.color_targets),
|
RenderTarget: conv::map_render_targets(desc.color_targets),
|
||||||
},
|
};
|
||||||
SampleMask: desc.multisample.mask as u32,
|
let depth_stencil_state = match desc.depth_stencil {
|
||||||
RasterizerState: raw_rasterizer,
|
|
||||||
DepthStencilState: match desc.depth_stencil {
|
|
||||||
Some(ref ds) => conv::map_depth_stencil(ds),
|
Some(ref ds) => conv::map_depth_stencil(ds),
|
||||||
None => Default::default(),
|
None => Default::default(),
|
||||||
},
|
};
|
||||||
|
let dsv_format = desc
|
||||||
|
.depth_stencil
|
||||||
|
.as_ref()
|
||||||
|
.map_or(Dxgi::Common::DXGI_FORMAT_UNKNOWN, |ds| {
|
||||||
|
auxil::dxgi::conv::map_texture_format(ds.format)
|
||||||
|
});
|
||||||
|
let sample_desc = Dxgi::Common::DXGI_SAMPLE_DESC {
|
||||||
|
Count: desc.multisample.count,
|
||||||
|
Quality: 0,
|
||||||
|
};
|
||||||
|
let cached_pso = Direct3D12::D3D12_CACHED_PIPELINE_STATE {
|
||||||
|
pCachedBlob: ptr::null(),
|
||||||
|
CachedBlobSizeInBytes: 0,
|
||||||
|
};
|
||||||
|
let flags = Direct3D12::D3D12_PIPELINE_STATE_FLAG_NONE;
|
||||||
|
|
||||||
|
let raw: Direct3D12::ID3D12PipelineState = match &desc.vertex_processor {
|
||||||
|
&crate::VertexProcessor::Standard {
|
||||||
|
vertex_buffers,
|
||||||
|
ref vertex_stage,
|
||||||
|
} => {
|
||||||
|
shader_stages |= wgt::ShaderStages::VERTEX;
|
||||||
|
let blob_vs = self.load_shader(
|
||||||
|
vertex_stage,
|
||||||
|
desc.layout,
|
||||||
|
naga::ShaderStage::Vertex,
|
||||||
|
desc.fragment_stage.as_ref(),
|
||||||
|
)?;
|
||||||
|
|
||||||
|
let mut input_element_descs = Vec::new();
|
||||||
|
for (i, (stride, vbuf)) in vertex_strides.iter_mut().zip(vertex_buffers).enumerate()
|
||||||
|
{
|
||||||
|
*stride = NonZeroU32::new(vbuf.array_stride as u32);
|
||||||
|
let (slot_class, step_rate) = match vbuf.step_mode {
|
||||||
|
wgt::VertexStepMode::Vertex => {
|
||||||
|
(Direct3D12::D3D12_INPUT_CLASSIFICATION_PER_VERTEX_DATA, 0)
|
||||||
|
}
|
||||||
|
wgt::VertexStepMode::Instance => {
|
||||||
|
(Direct3D12::D3D12_INPUT_CLASSIFICATION_PER_INSTANCE_DATA, 1)
|
||||||
|
}
|
||||||
|
};
|
||||||
|
for attribute in vbuf.attributes {
|
||||||
|
input_element_descs.push(Direct3D12::D3D12_INPUT_ELEMENT_DESC {
|
||||||
|
SemanticName: windows::core::PCSTR(NAGA_LOCATION_SEMANTIC.as_ptr()),
|
||||||
|
SemanticIndex: attribute.shader_location,
|
||||||
|
Format: auxil::dxgi::conv::map_vertex_format(attribute.format),
|
||||||
|
InputSlot: i as u32,
|
||||||
|
AlignedByteOffset: attribute.offset as u32,
|
||||||
|
InputSlotClass: slot_class,
|
||||||
|
InstanceDataStepRate: step_rate,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
let raw_desc = Direct3D12::D3D12_GRAPHICS_PIPELINE_STATE_DESC {
|
||||||
|
pRootSignature: root_signature,
|
||||||
|
VS: blob_vs.create_native_shader(),
|
||||||
|
PS: pixel_shader,
|
||||||
|
GS: Direct3D12::D3D12_SHADER_BYTECODE::default(),
|
||||||
|
DS: Direct3D12::D3D12_SHADER_BYTECODE::default(),
|
||||||
|
HS: Direct3D12::D3D12_SHADER_BYTECODE::default(),
|
||||||
|
StreamOutput: stream_output,
|
||||||
|
BlendState: blend_state,
|
||||||
|
SampleMask: desc.multisample.mask as u32,
|
||||||
|
RasterizerState: rasterizer_state,
|
||||||
|
DepthStencilState: depth_stencil_state,
|
||||||
InputLayout: Direct3D12::D3D12_INPUT_LAYOUT_DESC {
|
InputLayout: Direct3D12::D3D12_INPUT_LAYOUT_DESC {
|
||||||
pInputElementDescs: if input_element_descs.is_empty() {
|
pInputElementDescs: if input_element_descs.is_empty() {
|
||||||
ptr::null()
|
ptr::null()
|
||||||
@ -1970,27 +2005,78 @@ impl crate::Device for super::Device {
|
|||||||
PrimitiveTopologyType: topology_class,
|
PrimitiveTopologyType: topology_class,
|
||||||
NumRenderTargets: desc.color_targets.len() as u32,
|
NumRenderTargets: desc.color_targets.len() as u32,
|
||||||
RTVFormats: rtv_formats,
|
RTVFormats: rtv_formats,
|
||||||
DSVFormat: desc
|
DSVFormat: dsv_format,
|
||||||
.depth_stencil
|
SampleDesc: sample_desc,
|
||||||
.as_ref()
|
|
||||||
.map_or(Dxgi::Common::DXGI_FORMAT_UNKNOWN, |ds| {
|
|
||||||
auxil::dxgi::conv::map_texture_format(ds.format)
|
|
||||||
}),
|
|
||||||
SampleDesc: Dxgi::Common::DXGI_SAMPLE_DESC {
|
|
||||||
Count: desc.multisample.count,
|
|
||||||
Quality: 0,
|
|
||||||
},
|
|
||||||
NodeMask: 0,
|
NodeMask: 0,
|
||||||
CachedPSO: Direct3D12::D3D12_CACHED_PIPELINE_STATE {
|
CachedPSO: cached_pso,
|
||||||
pCachedBlob: ptr::null(),
|
Flags: flags,
|
||||||
CachedBlobSizeInBytes: 0,
|
|
||||||
},
|
|
||||||
Flags: Direct3D12::D3D12_PIPELINE_STATE_FLAG_NONE,
|
|
||||||
};
|
};
|
||||||
|
unsafe {
|
||||||
let raw: Direct3D12::ID3D12PipelineState = {
|
|
||||||
profiling::scope!("ID3D12Device::CreateGraphicsPipelineState");
|
profiling::scope!("ID3D12Device::CreateGraphicsPipelineState");
|
||||||
unsafe { self.raw.CreateGraphicsPipelineState(&raw_desc) }
|
self.raw.CreateGraphicsPipelineState(&raw_desc)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
crate::VertexProcessor::Mesh {
|
||||||
|
task_stage,
|
||||||
|
mesh_stage,
|
||||||
|
} => {
|
||||||
|
let blob_ts = if let Some(ts) = task_stage {
|
||||||
|
shader_stages |= wgt::ShaderStages::TASK;
|
||||||
|
Some(self.load_shader(
|
||||||
|
ts,
|
||||||
|
desc.layout,
|
||||||
|
naga::ShaderStage::Task,
|
||||||
|
desc.fragment_stage.as_ref(),
|
||||||
|
)?)
|
||||||
|
} else {
|
||||||
|
None
|
||||||
|
};
|
||||||
|
let task_shader = if let Some(ts) = &blob_ts {
|
||||||
|
ts.create_native_shader()
|
||||||
|
} else {
|
||||||
|
Default::default()
|
||||||
|
};
|
||||||
|
shader_stages |= wgt::ShaderStages::MESH;
|
||||||
|
let blob_ms = self.load_shader(
|
||||||
|
mesh_stage,
|
||||||
|
desc.layout,
|
||||||
|
naga::ShaderStage::Mesh,
|
||||||
|
desc.fragment_stage.as_ref(),
|
||||||
|
)?;
|
||||||
|
let desc = super::MeshShaderPipelineStateStream {
|
||||||
|
root_signature: root_signature
|
||||||
|
.as_ref()
|
||||||
|
.map(|a| a.as_raw().cast())
|
||||||
|
.unwrap_or(ptr::null_mut()),
|
||||||
|
task_shader,
|
||||||
|
pixel_shader,
|
||||||
|
mesh_shader: blob_ms.create_native_shader(),
|
||||||
|
blend_state,
|
||||||
|
sample_mask: desc.multisample.mask as u32,
|
||||||
|
rasterizer_state,
|
||||||
|
depth_stencil_state,
|
||||||
|
primitive_topology_type: topology_class,
|
||||||
|
rtv_formats: Direct3D12::D3D12_RT_FORMAT_ARRAY {
|
||||||
|
RTFormats: rtv_formats,
|
||||||
|
NumRenderTargets: desc.color_targets.len() as u32,
|
||||||
|
},
|
||||||
|
dsv_format,
|
||||||
|
sample_desc,
|
||||||
|
node_mask: 0,
|
||||||
|
cached_pso,
|
||||||
|
flags,
|
||||||
|
};
|
||||||
|
let mut raw_desc = unsafe { desc.to_bytes() };
|
||||||
|
let stream_desc = Direct3D12::D3D12_PIPELINE_STATE_STREAM_DESC {
|
||||||
|
SizeInBytes: raw_desc.len(),
|
||||||
|
pPipelineStateSubobjectStream: raw_desc.as_mut_ptr().cast(),
|
||||||
|
};
|
||||||
|
let device: Direct3D12::ID3D12Device2 = self.raw.cast().unwrap();
|
||||||
|
unsafe {
|
||||||
|
profiling::scope!("ID3D12Device2::CreatePipelineState");
|
||||||
|
device.CreatePipelineState(&stream_desc)
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
.map_err(|err| crate::PipelineError::Linkage(shader_stages, err.to_string()))?;
|
.map_err(|err| crate::PipelineError::Linkage(shader_stages, err.to_string()))?;
|
||||||
|
|
||||||
|
|||||||
@ -660,6 +660,7 @@ struct Idler {
|
|||||||
struct CommandSignatures {
|
struct CommandSignatures {
|
||||||
draw: Direct3D12::ID3D12CommandSignature,
|
draw: Direct3D12::ID3D12CommandSignature,
|
||||||
draw_indexed: Direct3D12::ID3D12CommandSignature,
|
draw_indexed: Direct3D12::ID3D12CommandSignature,
|
||||||
|
draw_mesh: Direct3D12::ID3D12CommandSignature,
|
||||||
dispatch: Direct3D12::ID3D12CommandSignature,
|
dispatch: Direct3D12::ID3D12CommandSignature,
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -1600,3 +1601,116 @@ pub enum ShaderModuleSource {
|
|||||||
DxilPassthrough(DxilPassthroughShader),
|
DxilPassthrough(DxilPassthroughShader),
|
||||||
HlslPassthrough(HlslPassthroughShader),
|
HlslPassthrough(HlslPassthroughShader),
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[repr(C)]
|
||||||
|
#[derive(Debug)]
|
||||||
|
struct MeshShaderPipelineStateStream {
|
||||||
|
root_signature: *mut Direct3D12::ID3D12RootSignature,
|
||||||
|
task_shader: Direct3D12::D3D12_SHADER_BYTECODE,
|
||||||
|
mesh_shader: Direct3D12::D3D12_SHADER_BYTECODE,
|
||||||
|
pixel_shader: Direct3D12::D3D12_SHADER_BYTECODE,
|
||||||
|
blend_state: Direct3D12::D3D12_BLEND_DESC,
|
||||||
|
sample_mask: u32,
|
||||||
|
rasterizer_state: Direct3D12::D3D12_RASTERIZER_DESC,
|
||||||
|
depth_stencil_state: Direct3D12::D3D12_DEPTH_STENCIL_DESC,
|
||||||
|
primitive_topology_type: Direct3D12::D3D12_PRIMITIVE_TOPOLOGY_TYPE,
|
||||||
|
rtv_formats: Direct3D12::D3D12_RT_FORMAT_ARRAY,
|
||||||
|
dsv_format: Dxgi::Common::DXGI_FORMAT,
|
||||||
|
sample_desc: Dxgi::Common::DXGI_SAMPLE_DESC,
|
||||||
|
node_mask: u32,
|
||||||
|
cached_pso: Direct3D12::D3D12_CACHED_PIPELINE_STATE,
|
||||||
|
flags: Direct3D12::D3D12_PIPELINE_STATE_FLAGS,
|
||||||
|
}
|
||||||
|
impl MeshShaderPipelineStateStream {
|
||||||
|
/// # Safety
|
||||||
|
///
|
||||||
|
/// Returned bytes contain pointers into this struct, for them to be valid,
|
||||||
|
/// this struct may be at the same location. As if `as_bytes<'a>(&'a self) -> Vec<u8> + 'a`
|
||||||
|
pub unsafe fn to_bytes(&self) -> Vec<u8> {
|
||||||
|
use Direct3D12::*;
|
||||||
|
let mut bytes = Vec::new();
|
||||||
|
|
||||||
|
macro_rules! push_subobject {
|
||||||
|
($subobject_type:expr, $data:expr) => {{
|
||||||
|
// Ensure 8-byte alignment for the subobject start
|
||||||
|
let alignment = 8;
|
||||||
|
let aligned_length = bytes.len().next_multiple_of(alignment);
|
||||||
|
bytes.resize(aligned_length, 0);
|
||||||
|
|
||||||
|
// Append the type tag (u32)
|
||||||
|
let tag: u32 = $subobject_type.0 as u32;
|
||||||
|
bytes.extend_from_slice(&tag.to_ne_bytes());
|
||||||
|
|
||||||
|
// Align the data
|
||||||
|
let obj_align = align_of_val(&$data);
|
||||||
|
let data_start = bytes.len().next_multiple_of(obj_align);
|
||||||
|
bytes.resize(data_start, 0);
|
||||||
|
|
||||||
|
// Append the data itself
|
||||||
|
#[allow(clippy::ptr_as_ptr, trivial_casts)]
|
||||||
|
let data_ptr = &$data as *const _ as *const u8;
|
||||||
|
let data_size = size_of_val(&$data);
|
||||||
|
let slice = unsafe { core::slice::from_raw_parts(data_ptr, data_size) };
|
||||||
|
bytes.extend_from_slice(slice);
|
||||||
|
}};
|
||||||
|
}
|
||||||
|
push_subobject!(
|
||||||
|
D3D12_PIPELINE_STATE_SUBOBJECT_TYPE_ROOT_SIGNATURE,
|
||||||
|
self.root_signature
|
||||||
|
);
|
||||||
|
if !self.task_shader.pShaderBytecode.is_null() {
|
||||||
|
push_subobject!(D3D12_PIPELINE_STATE_SUBOBJECT_TYPE_AS, self.task_shader);
|
||||||
|
}
|
||||||
|
push_subobject!(D3D12_PIPELINE_STATE_SUBOBJECT_TYPE_MS, self.mesh_shader);
|
||||||
|
if !self.pixel_shader.pShaderBytecode.is_null() {
|
||||||
|
push_subobject!(D3D12_PIPELINE_STATE_SUBOBJECT_TYPE_PS, self.pixel_shader);
|
||||||
|
}
|
||||||
|
push_subobject!(D3D12_PIPELINE_STATE_SUBOBJECT_TYPE_BLEND, self.blend_state);
|
||||||
|
push_subobject!(
|
||||||
|
D3D12_PIPELINE_STATE_SUBOBJECT_TYPE_SAMPLE_MASK,
|
||||||
|
self.sample_mask
|
||||||
|
);
|
||||||
|
push_subobject!(
|
||||||
|
D3D12_PIPELINE_STATE_SUBOBJECT_TYPE_RASTERIZER,
|
||||||
|
self.rasterizer_state
|
||||||
|
);
|
||||||
|
push_subobject!(
|
||||||
|
D3D12_PIPELINE_STATE_SUBOBJECT_TYPE_DEPTH_STENCIL,
|
||||||
|
self.depth_stencil_state
|
||||||
|
);
|
||||||
|
push_subobject!(
|
||||||
|
D3D12_PIPELINE_STATE_SUBOBJECT_TYPE_PRIMITIVE_TOPOLOGY,
|
||||||
|
self.primitive_topology_type
|
||||||
|
);
|
||||||
|
if self.rtv_formats.NumRenderTargets != 0 {
|
||||||
|
push_subobject!(
|
||||||
|
D3D12_PIPELINE_STATE_SUBOBJECT_TYPE_RENDER_TARGET_FORMATS,
|
||||||
|
self.rtv_formats
|
||||||
|
);
|
||||||
|
}
|
||||||
|
if self.dsv_format != Dxgi::Common::DXGI_FORMAT_UNKNOWN {
|
||||||
|
push_subobject!(
|
||||||
|
D3D12_PIPELINE_STATE_SUBOBJECT_TYPE_DEPTH_STENCIL_FORMAT,
|
||||||
|
self.dsv_format
|
||||||
|
);
|
||||||
|
}
|
||||||
|
push_subobject!(
|
||||||
|
D3D12_PIPELINE_STATE_SUBOBJECT_TYPE_SAMPLE_DESC,
|
||||||
|
self.sample_desc
|
||||||
|
);
|
||||||
|
if self.node_mask != 0 {
|
||||||
|
push_subobject!(
|
||||||
|
D3D12_PIPELINE_STATE_SUBOBJECT_TYPE_NODE_MASK,
|
||||||
|
self.node_mask
|
||||||
|
);
|
||||||
|
}
|
||||||
|
if !self.cached_pso.pCachedBlob.is_null() {
|
||||||
|
push_subobject!(
|
||||||
|
D3D12_PIPELINE_STATE_SUBOBJECT_TYPE_CACHED_PSO,
|
||||||
|
self.cached_pso
|
||||||
|
);
|
||||||
|
}
|
||||||
|
push_subobject!(D3D12_PIPELINE_STATE_SUBOBJECT_TYPE_FLAGS, self.flags);
|
||||||
|
bytes
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user