From 6c5f7da18731bde0b341bc89f8eabca8d9534d9c Mon Sep 17 00:00:00 2001 From: Brindusan Cristian Date: Wed, 18 Aug 2021 07:58:38 +0300 Subject: [PATCH] Backed out changeset e34f15d5e74d (bug 1622846) for causing linux toolchain build bustages. CLOSED TREE --- .cargo/config.in | 10 + Cargo.lock | 109 +- dom/bindings/Bindings.conf | 7 +- dom/webgpu/Adapter.cpp | 8 +- dom/webgpu/Adapter.h | 8 +- ...{SupportedLimits.cpp => AdapterLimits.cpp} | 46 +- .../{SupportedLimits.h => AdapterLimits.h} | 16 +- dom/webgpu/CanvasContext.cpp | 96 +- dom/webgpu/CanvasContext.h | 20 +- dom/webgpu/CompilationMessage.h | 4 - dom/webgpu/Device.cpp | 34 +- dom/webgpu/Device.h | 8 +- dom/webgpu/RenderPassEncoder.cpp | 2 +- dom/webgpu/SwapChain.cpp | 52 + dom/webgpu/SwapChain.h | 54 + dom/webgpu/ipc/WebGPUChild.cpp | 4 +- dom/webgpu/moz.build | 3 +- dom/webidl/WebGPU.webidl | 55 +- gfx/wgpu/.gitattributes | 2 - gfx/wgpu/.github/ISSUE_TEMPLATE/config.yml | 8 - gfx/wgpu/.github/ISSUE_TEMPLATE/other.md | 3 + gfx/wgpu/.github/pull_request_template.md | 9 + gfx/wgpu/.github/workflows/ci.yml | 31 +- gfx/wgpu/.github/workflows/docs.yml | 45 - gfx/wgpu/.github/workflows/publish.yml | 47 - gfx/wgpu/.gitignore | 15 +- gfx/wgpu/CHANGELOG.md | 90 +- gfx/wgpu/Cargo.lock | 434 +- gfx/wgpu/Cargo.toml | 31 +- gfx/wgpu/LICENSE | 373 + gfx/wgpu/LICENSE.APACHE | 176 - gfx/wgpu/LICENSE.MIT | 21 - gfx/wgpu/README.md | 13 +- gfx/wgpu/bors.toml | 2 - gfx/wgpu/dummy/Cargo.toml | 1 - gfx/wgpu/player/Cargo.toml | 2 +- gfx/wgpu/player/src/lib.rs | 2 +- gfx/wgpu/player/tests/data/all.ron | 1 - .../tests/data/pipeline-statistics-query.ron | 81 - gfx/wgpu/wgpu-core/Cargo.toml | 27 +- gfx/wgpu/wgpu-core/src/command/clear.rs | 2 +- gfx/wgpu/wgpu-core/src/command/query.rs | 18 +- gfx/wgpu/wgpu-core/src/device/life.rs | 2 + gfx/wgpu/wgpu-core/src/device/mod.rs | 102 +- gfx/wgpu/wgpu-core/src/device/queue.rs | 11 +- gfx/wgpu/wgpu-core/src/pipeline.rs | 18 - gfx/wgpu/wgpu-core/src/track/mod.rs | 2 +- gfx/wgpu/wgpu-core/src/validation.rs | 2 +- gfx/wgpu/wgpu-types/Cargo.toml | 4 +- gfx/wgpu_bindings/src/lib.rs | 2 +- gfx/wgpu_bindings/src/server.rs | 5 +- layout/generic/nsHTMLCanvasFrame.cpp | 41 +- taskcluster/ci/toolchain/misc.yml | 3 +- .../rust/drm-fourcc/.cargo-checksum.json | 1 - third_party/rust/drm-fourcc/Cargo.toml | 37 - third_party/rust/drm-fourcc/README.md | 43 - third_party/rust/drm-fourcc/build.rs | 244 - third_party/rust/drm-fourcc/src/as_enum.rs | 369 - third_party/rust/drm-fourcc/src/consts.rs | 149 - third_party/rust/drm-fourcc/src/lib.rs | 370 - .../rust/external-memory/.cargo-checksum.json | 1 - third_party/rust/external-memory/Cargo.toml | 32 - third_party/rust/external-memory/src/fd.rs | 20 - .../rust/external-memory/src/handle.rs | 20 - third_party/rust/external-memory/src/lib.rs | 686 -- third_party/rust/external-memory/src/ptr.rs | 20 - .../rust/gfx-auxil/.cargo-checksum.json | 2 +- third_party/rust/gfx-auxil/Cargo.toml | 38 +- .../gfx-backend-dx11/.cargo-checksum.json | 2 +- third_party/rust/gfx-backend-dx11/Cargo.toml | 103 +- third_party/rust/gfx-backend-dx11/README.md | 46 +- .../rust/gfx-backend-dx11/shaders/blit.hlsl | 126 +- .../rust/gfx-backend-dx11/shaders/clear.hlsl | 44 +- .../rust/gfx-backend-dx11/shaders/copy.hlsl | 1230 +-- third_party/rust/gfx-backend-dx11/src/conv.rs | 1748 ++-- .../rust/gfx-backend-dx11/src/debug.rs | 228 +- .../rust/gfx-backend-dx11/src/device.rs | 5036 +++++---- third_party/rust/gfx-backend-dx11/src/dxgi.rs | 488 +- .../rust/gfx-backend-dx11/src/internal.rs | 3126 +++--- third_party/rust/gfx-backend-dx11/src/lib.rs | 8985 ++++++++--------- .../rust/gfx-backend-dx11/src/shader.rs | 946 +- .../gfx-backend-dx12/.cargo-checksum.json | 2 +- third_party/rust/gfx-backend-dx12/Cargo.toml | 105 +- third_party/rust/gfx-backend-dx12/README.md | 38 +- .../rust/gfx-backend-dx12/shaders/blit.hlsl | 58 +- .../rust/gfx-backend-dx12/src/command.rs | 5644 +++++------ third_party/rust/gfx-backend-dx12/src/conv.rs | 1354 +-- .../gfx-backend-dx12/src/descriptors_cpu.rs | 610 +- .../rust/gfx-backend-dx12/src/device.rs | 7689 +++++++------- .../rust/gfx-backend-dx12/src/internal.rs | 488 +- third_party/rust/gfx-backend-dx12/src/lib.rs | 3488 ++++--- third_party/rust/gfx-backend-dx12/src/pool.rs | 372 +- .../rust/gfx-backend-dx12/src/resource.rs | 1743 ++-- .../gfx-backend-dx12/src/root_constants.rs | 592 +- .../rust/gfx-backend-dx12/src/window.rs | 698 +- .../gfx-backend-empty/.cargo-checksum.json | 2 +- third_party/rust/gfx-backend-empty/Cargo.toml | 32 +- third_party/rust/gfx-backend-empty/src/lib.rs | 185 +- .../gfx-backend-metal/.cargo-checksum.json | 2 +- third_party/rust/gfx-backend-metal/Cargo.toml | 145 +- .../rust/gfx-backend-metal/src/command.rs | 4 +- .../rust/gfx-backend-metal/src/conversions.rs | 2 - .../rust/gfx-backend-metal/src/device.rs | 153 +- third_party/rust/gfx-backend-metal/src/lib.rs | 18 +- .../rust/gfx-backend-metal/src/window.rs | 14 +- .../gfx-backend-vulkan/.cargo-checksum.json | 2 +- .../rust/gfx-backend-vulkan/Cargo.toml | 103 +- .../rust/gfx-backend-vulkan/src/device.rs | 1258 +-- .../rust/gfx-backend-vulkan/src/lib.rs | 554 +- .../rust/gfx-backend-vulkan/src/native.rs | 6 - .../gfx-backend-vulkan/src/physical_device.rs | 1150 +-- third_party/rust/gfx-hal/.cargo-checksum.json | 2 +- third_party/rust/gfx-hal/Cargo.toml | 57 +- third_party/rust/gfx-hal/src/adapter.rs | 85 +- third_party/rust/gfx-hal/src/device.rs | 161 +- .../rust/gfx-hal/src/display/control.rs | 39 - third_party/rust/gfx-hal/src/display/mod.rs | 245 - .../gfx-hal/src/external_memory/errors.rs | 51 - .../rust/gfx-hal/src/external_memory/mod.rs | 19 - third_party/rust/gfx-hal/src/format.rs | 27 +- third_party/rust/gfx-hal/src/lib.rs | 42 - third_party/rust/gfx-hal/src/window.rs | 35 - .../rust/gfx-renderdoc/.cargo-checksum.json | 1 - third_party/rust/gfx-renderdoc/Cargo.toml | 35 - third_party/rust/gfx-renderdoc/src/lib.rs | 127 - third_party/rust/metal/.cargo-checksum.json | 2 +- third_party/rust/metal/Cargo.lock | 4 +- third_party/rust/metal/Cargo.toml | 4 +- third_party/rust/metal/examples/caps/main.rs | 2 +- .../rust/metal/examples/circle/main.rs | 3 +- .../rust/metal/examples/shader-dylib/main.rs | 6 +- .../rust/metal/examples/window/main.rs | 1 - third_party/rust/metal/src/argument.rs | 4 +- third_party/rust/metal/src/device.rs | 44 +- third_party/rust/metal/src/drawable.rs | 2 +- third_party/rust/metal/src/encoder.rs | 9 +- third_party/rust/metal/src/heap.rs | 2 + .../rust/metal/src/indirect_encoder.rs | 2 + third_party/rust/metal/src/lib.rs | 35 +- third_party/rust/metal/src/library.rs | 1 + .../rust/metal/src/pipeline/compute.rs | 1 + third_party/rust/metal/src/pipeline/render.rs | 1 + third_party/rust/metal/src/renderpass.rs | 8 +- third_party/rust/metal/src/resource.rs | 3 +- third_party/rust/metal/src/sampler.rs | 5 +- third_party/rust/metal/src/texture.rs | 1 + third_party/rust/metal/src/types.rs | 2 +- .../rust/metal/src/vertexdescriptor.rs | 2 +- third_party/rust/naga/.cargo-checksum.json | 2 +- .../rust/naga/.github/workflows/lazy.yml | 28 + .../rust/naga/.github/workflows/pipeline.yml | 44 + .../.github/workflows/validation-linux.yml | 21 + .../.github/workflows/validation-macos.yml | 13 + .../.github/workflows/validation-windows.yml | 16 + third_party/rust/naga/.monocodus | 7 + third_party/rust/naga/CHANGELOG.md | 51 - third_party/rust/naga/Cargo.toml | 114 +- third_party/rust/naga/Makefile | 22 +- third_party/rust/naga/README.md | 17 +- third_party/rust/naga/bin/naga.rs | 298 + third_party/rust/naga/src/back/dot/mod.rs | 54 +- .../rust/naga/src/back/glsl/features.rs | 11 - third_party/rust/naga/src/back/glsl/mod.rs | 199 +- .../rust/naga/src/back/hlsl/keywords.rs | 164 +- third_party/rust/naga/src/back/hlsl/mod.rs | 58 +- third_party/rust/naga/src/back/hlsl/writer.rs | 1076 +- third_party/rust/naga/src/back/mod.rs | 8 +- third_party/rust/naga/src/back/msl/mod.rs | 2 +- third_party/rust/naga/src/back/msl/writer.rs | 58 +- third_party/rust/naga/src/back/spv/writer.rs | 531 +- third_party/rust/naga/src/back/wgsl/mod.rs | 2 - third_party/rust/naga/src/back/wgsl/writer.rs | 775 +- third_party/rust/naga/src/front/glsl/ast.rs | 1070 +- .../rust/naga/src/front/glsl/constants.rs | 6 +- third_party/rust/naga/src/front/glsl/error.rs | 170 +- .../rust/naga/src/front/glsl/functions.rs | 892 +- third_party/rust/naga/src/front/glsl/lex.rs | 282 +- third_party/rust/naga/src/front/glsl/mod.rs | 10 +- .../rust/naga/src/front/glsl/parser.rs | 2940 +++--- .../rust/naga/src/front/glsl/parser_tests.rs | 454 +- third_party/rust/naga/src/front/glsl/token.rs | 138 +- .../rust/naga/src/front/glsl/variables.rs | 680 +- third_party/rust/naga/src/front/mod.rs | 29 +- third_party/rust/naga/src/front/spv/error.rs | 2 + third_party/rust/naga/src/front/spv/flow.rs | 108 +- .../rust/naga/src/front/spv/function.rs | 8 +- third_party/rust/naga/src/front/spv/mod.rs | 67 +- third_party/rust/naga/src/front/wgsl/conv.rs | 36 +- .../layouter.rs => front/wgsl/layout.rs} | 79 +- third_party/rust/naga/src/front/wgsl/lexer.rs | 106 +- third_party/rust/naga/src/front/wgsl/mod.rs | 1108 +- third_party/rust/naga/src/lib.rs | 200 +- .../rust/naga/src/proc/interpolator.rs | 71 +- third_party/rust/naga/src/proc/mod.rs | 45 - third_party/rust/naga/src/proc/namer.rs | 131 +- third_party/rust/naga/src/proc/typifier.rs | 75 +- third_party/rust/naga/src/valid/analyzer.rs | 31 +- third_party/rust/naga/src/valid/expression.rs | 30 +- third_party/rust/naga/src/valid/function.rs | 2 +- third_party/rust/naga/src/valid/interface.rs | 48 +- third_party/rust/naga/src/valid/mod.rs | 7 +- third_party/rust/naga/src/valid/type.rs | 117 +- .../tests/cases/glsl_constant_expression.vert | 9 + .../tests/cases/glsl_if_preprocessor.vert | 14 + .../naga/tests/cases/glsl_phong_lighting.frag | 56 + .../tests/cases/glsl_preprocessor_abuse.vert | 11 + .../tests/cases/glsl_vertex_test_shader.vert | 29 + .../rust/naga/tests/in/access.param.ron | 21 + third_party/rust/naga/tests/in/access.wgsl | 23 + .../rust/naga/tests/in/boids.param.ron | 23 + third_party/rust/naga/tests/in/boids.wgsl | 109 + .../rust/naga/tests/in/collatz.param.ron | 5 + third_party/rust/naga/tests/in/collatz.wgsl | 36 + .../rust/naga/tests/in/control-flow.param.ron | 4 + .../rust/naga/tests/in/control-flow.wgsl | 6 + .../rust/naga/tests/in/empty.param.ron | 4 + third_party/rust/naga/tests/in/empty.wgsl | 2 + .../rust/naga/tests/in/extra.param.ron | 5 + third_party/rust/naga/tests/in/extra.wgsl | 11 + .../rust/naga/tests/in/image.param.ron | 5 + third_party/rust/naga/tests/in/image.wgsl | 60 + .../rust/naga/tests/in/interpolate.param.ron | 7 + .../rust/naga/tests/in/interpolate.wgsl | 29 + .../rust/naga/tests/in/operators.param.ron | 4 + third_party/rust/naga/tests/in/operators.wgsl | 10 + third_party/rust/naga/tests/in/quad-glsl.glsl | 28 + .../rust/naga/tests/in/quad-glsl.param.ron | 6 + third_party/rust/naga/tests/in/quad-vert.spv | Bin 0 -> 968 bytes third_party/rust/naga/tests/in/quad.param.ron | 6 + third_party/rust/naga/tests/in/quad.wgsl | 31 + .../rust/naga/tests/in/shadow.param.ron | 6 + third_party/rust/naga/tests/in/shadow.spv | Bin 0 -> 4764 bytes third_party/rust/naga/tests/in/shadow.wgsl | 63 + .../rust/naga/tests/in/skybox.param.ron | 33 + third_party/rust/naga/tests/in/skybox.wgsl | 39 + third_party/rust/naga/tests/out/access.msl | 28 + third_party/rust/naga/tests/out/access.spvasm | 81 + .../rust/naga/tests/out/boids.Compute.glsl | 98 + third_party/rust/naga/tests/out/boids.msl | 105 + third_party/rust/naga/tests/out/boids.spvasm | 330 + .../rust/naga/tests/out/collatz.info.ron | 497 + third_party/rust/naga/tests/out/collatz.msl | 42 + third_party/rust/naga/tests/out/collatz.ron | 346 + .../rust/naga/tests/out/collatz.spvasm | 105 + .../naga/tests/out/control-flow.Compute.glsl | 14 + .../rust/naga/tests/out/control-flow.msl | 13 + .../rust/naga/tests/out/control-flow.spvasm | 29 + .../rust/naga/tests/out/empty.Compute.glsl | 11 + .../rust/naga/tests/out/empty.Compute.hlsl | 6 + third_party/rust/naga/tests/out/empty.hlsl | 5 + third_party/rust/naga/tests/out/empty.msl | 8 + third_party/rust/naga/tests/out/empty.spvasm | 17 + third_party/rust/naga/tests/out/empty.wgsl | 4 + third_party/rust/naga/tests/out/extra.msl | 21 + third_party/rust/naga/tests/out/extra.spvasm | 37 + third_party/rust/naga/tests/out/image.msl | 32 + third_party/rust/naga/tests/out/image.spvasm | 181 + .../naga/tests/out/interpolate.Fragment.glsl | 25 + .../naga/tests/out/interpolate.Vertex.glsl | 41 + .../rust/naga/tests/out/interpolate.msl | 56 + .../rust/naga/tests/out/interpolate.spvasm | 209 + .../rust/naga/tests/out/operators.Vertex.glsl | 10 + third_party/rust/naga/tests/out/operators.msl | 17 + .../rust/naga/tests/out/operators.spvasm | 60 + .../rust/naga/tests/out/quad-vert.Vertex.glsl | 48 + third_party/rust/naga/tests/out/quad-vert.msl | 60 + .../rust/naga/tests/out/quad-vert.wgsl | 29 + .../rust/naga/tests/out/quad.Fragment.glsl | 24 + .../rust/naga/tests/out/quad.Vertex.glsl | 22 + third_party/rust/naga/tests/out/quad.dot | 85 + third_party/rust/naga/tests/out/quad.msl | 45 + third_party/rust/naga/tests/out/quad.spvasm | 105 + third_party/rust/naga/tests/out/quad.wgsl | 25 + .../rust/naga/tests/out/shadow.Fragment.glsl | 50 + .../rust/naga/tests/out/shadow.info.ron | 2876 ++++++ third_party/rust/naga/tests/out/shadow.msl | 68 + third_party/rust/naga/tests/out/shadow.ron | 1580 +++ third_party/rust/naga/tests/out/shadow.spvasm | 195 + .../rust/naga/tests/out/skybox.Fragment.glsl | 21 + .../rust/naga/tests/out/skybox.Vertex.glsl | 29 + third_party/rust/naga/tests/out/skybox.msl | 55 + third_party/rust/naga/tests/out/skybox.spvasm | 133 + third_party/rust/naga/tests/snapshots.rs | 346 + third_party/rust/naga/tests/wgsl-errors.rs | 177 + .../rust/range-alloc/.cargo-checksum.json | 2 +- third_party/rust/range-alloc/Cargo.toml | 25 +- 286 files changed, 36906 insertions(+), 38613 deletions(-) rename dom/webgpu/{SupportedLimits.cpp => AdapterLimits.cpp} (51%) rename dom/webgpu/{SupportedLimits.h => AdapterLimits.h} (79%) create mode 100644 dom/webgpu/SwapChain.cpp create mode 100644 dom/webgpu/SwapChain.h delete mode 100644 gfx/wgpu/.gitattributes delete mode 100644 gfx/wgpu/.github/ISSUE_TEMPLATE/config.yml delete mode 100644 gfx/wgpu/.github/workflows/docs.yml delete mode 100644 gfx/wgpu/.github/workflows/publish.yml create mode 100644 gfx/wgpu/LICENSE delete mode 100644 gfx/wgpu/LICENSE.APACHE delete mode 100644 gfx/wgpu/LICENSE.MIT delete mode 100644 gfx/wgpu/player/tests/data/pipeline-statistics-query.ron delete mode 100644 third_party/rust/drm-fourcc/.cargo-checksum.json delete mode 100644 third_party/rust/drm-fourcc/Cargo.toml delete mode 100644 third_party/rust/drm-fourcc/README.md delete mode 100644 third_party/rust/drm-fourcc/build.rs delete mode 100644 third_party/rust/drm-fourcc/src/as_enum.rs delete mode 100644 third_party/rust/drm-fourcc/src/consts.rs delete mode 100644 third_party/rust/drm-fourcc/src/lib.rs delete mode 100644 third_party/rust/external-memory/.cargo-checksum.json delete mode 100644 third_party/rust/external-memory/Cargo.toml delete mode 100644 third_party/rust/external-memory/src/fd.rs delete mode 100644 third_party/rust/external-memory/src/handle.rs delete mode 100644 third_party/rust/external-memory/src/lib.rs delete mode 100644 third_party/rust/external-memory/src/ptr.rs delete mode 100644 third_party/rust/gfx-hal/src/display/control.rs delete mode 100644 third_party/rust/gfx-hal/src/display/mod.rs delete mode 100644 third_party/rust/gfx-hal/src/external_memory/errors.rs delete mode 100644 third_party/rust/gfx-hal/src/external_memory/mod.rs delete mode 100644 third_party/rust/gfx-renderdoc/.cargo-checksum.json delete mode 100644 third_party/rust/gfx-renderdoc/Cargo.toml delete mode 100644 third_party/rust/gfx-renderdoc/src/lib.rs create mode 100644 third_party/rust/naga/.github/workflows/lazy.yml create mode 100644 third_party/rust/naga/.github/workflows/pipeline.yml create mode 100644 third_party/rust/naga/.github/workflows/validation-linux.yml create mode 100644 third_party/rust/naga/.github/workflows/validation-macos.yml create mode 100644 third_party/rust/naga/.github/workflows/validation-windows.yml create mode 100644 third_party/rust/naga/.monocodus create mode 100644 third_party/rust/naga/bin/naga.rs rename third_party/rust/naga/src/{proc/layouter.rs => front/wgsl/layout.rs} (58%) create mode 100644 third_party/rust/naga/tests/cases/glsl_constant_expression.vert create mode 100644 third_party/rust/naga/tests/cases/glsl_if_preprocessor.vert create mode 100644 third_party/rust/naga/tests/cases/glsl_phong_lighting.frag create mode 100644 third_party/rust/naga/tests/cases/glsl_preprocessor_abuse.vert create mode 100644 third_party/rust/naga/tests/cases/glsl_vertex_test_shader.vert create mode 100644 third_party/rust/naga/tests/in/access.param.ron create mode 100644 third_party/rust/naga/tests/in/access.wgsl create mode 100644 third_party/rust/naga/tests/in/boids.param.ron create mode 100644 third_party/rust/naga/tests/in/boids.wgsl create mode 100644 third_party/rust/naga/tests/in/collatz.param.ron create mode 100644 third_party/rust/naga/tests/in/collatz.wgsl create mode 100644 third_party/rust/naga/tests/in/control-flow.param.ron create mode 100644 third_party/rust/naga/tests/in/control-flow.wgsl create mode 100644 third_party/rust/naga/tests/in/empty.param.ron create mode 100644 third_party/rust/naga/tests/in/empty.wgsl create mode 100644 third_party/rust/naga/tests/in/extra.param.ron create mode 100644 third_party/rust/naga/tests/in/extra.wgsl create mode 100644 third_party/rust/naga/tests/in/image.param.ron create mode 100644 third_party/rust/naga/tests/in/image.wgsl create mode 100644 third_party/rust/naga/tests/in/interpolate.param.ron create mode 100644 third_party/rust/naga/tests/in/interpolate.wgsl create mode 100644 third_party/rust/naga/tests/in/operators.param.ron create mode 100644 third_party/rust/naga/tests/in/operators.wgsl create mode 100644 third_party/rust/naga/tests/in/quad-glsl.glsl create mode 100644 third_party/rust/naga/tests/in/quad-glsl.param.ron create mode 100644 third_party/rust/naga/tests/in/quad-vert.spv create mode 100644 third_party/rust/naga/tests/in/quad.param.ron create mode 100644 third_party/rust/naga/tests/in/quad.wgsl create mode 100644 third_party/rust/naga/tests/in/shadow.param.ron create mode 100644 third_party/rust/naga/tests/in/shadow.spv create mode 100644 third_party/rust/naga/tests/in/shadow.wgsl create mode 100644 third_party/rust/naga/tests/in/skybox.param.ron create mode 100644 third_party/rust/naga/tests/in/skybox.wgsl create mode 100644 third_party/rust/naga/tests/out/access.msl create mode 100644 third_party/rust/naga/tests/out/access.spvasm create mode 100644 third_party/rust/naga/tests/out/boids.Compute.glsl create mode 100644 third_party/rust/naga/tests/out/boids.msl create mode 100644 third_party/rust/naga/tests/out/boids.spvasm create mode 100644 third_party/rust/naga/tests/out/collatz.info.ron create mode 100644 third_party/rust/naga/tests/out/collatz.msl create mode 100644 third_party/rust/naga/tests/out/collatz.ron create mode 100644 third_party/rust/naga/tests/out/collatz.spvasm create mode 100644 third_party/rust/naga/tests/out/control-flow.Compute.glsl create mode 100644 third_party/rust/naga/tests/out/control-flow.msl create mode 100644 third_party/rust/naga/tests/out/control-flow.spvasm create mode 100644 third_party/rust/naga/tests/out/empty.Compute.glsl create mode 100644 third_party/rust/naga/tests/out/empty.Compute.hlsl create mode 100644 third_party/rust/naga/tests/out/empty.hlsl create mode 100644 third_party/rust/naga/tests/out/empty.msl create mode 100644 third_party/rust/naga/tests/out/empty.spvasm create mode 100644 third_party/rust/naga/tests/out/empty.wgsl create mode 100644 third_party/rust/naga/tests/out/extra.msl create mode 100644 third_party/rust/naga/tests/out/extra.spvasm create mode 100644 third_party/rust/naga/tests/out/image.msl create mode 100644 third_party/rust/naga/tests/out/image.spvasm create mode 100644 third_party/rust/naga/tests/out/interpolate.Fragment.glsl create mode 100644 third_party/rust/naga/tests/out/interpolate.Vertex.glsl create mode 100644 third_party/rust/naga/tests/out/interpolate.msl create mode 100644 third_party/rust/naga/tests/out/interpolate.spvasm create mode 100644 third_party/rust/naga/tests/out/operators.Vertex.glsl create mode 100644 third_party/rust/naga/tests/out/operators.msl create mode 100644 third_party/rust/naga/tests/out/operators.spvasm create mode 100644 third_party/rust/naga/tests/out/quad-vert.Vertex.glsl create mode 100644 third_party/rust/naga/tests/out/quad-vert.msl create mode 100644 third_party/rust/naga/tests/out/quad-vert.wgsl create mode 100644 third_party/rust/naga/tests/out/quad.Fragment.glsl create mode 100644 third_party/rust/naga/tests/out/quad.Vertex.glsl create mode 100644 third_party/rust/naga/tests/out/quad.dot create mode 100644 third_party/rust/naga/tests/out/quad.msl create mode 100644 third_party/rust/naga/tests/out/quad.spvasm create mode 100644 third_party/rust/naga/tests/out/quad.wgsl create mode 100644 third_party/rust/naga/tests/out/shadow.Fragment.glsl create mode 100644 third_party/rust/naga/tests/out/shadow.info.ron create mode 100644 third_party/rust/naga/tests/out/shadow.msl create mode 100644 third_party/rust/naga/tests/out/shadow.ron create mode 100644 third_party/rust/naga/tests/out/shadow.spvasm create mode 100644 third_party/rust/naga/tests/out/skybox.Fragment.glsl create mode 100644 third_party/rust/naga/tests/out/skybox.Vertex.glsl create mode 100644 third_party/rust/naga/tests/out/skybox.msl create mode 100644 third_party/rust/naga/tests/out/skybox.spvasm create mode 100644 third_party/rust/naga/tests/snapshots.rs create mode 100644 third_party/rust/naga/tests/wgsl-errors.rs diff --git a/.cargo/config.in b/.cargo/config.in index 0d6fb8369005..ba4ce900c963 100644 --- a/.cargo/config.in +++ b/.cargo/config.in @@ -87,6 +87,16 @@ git = "https://github.com/hsivonen/chardetng" replace-with = "vendored-sources" rev = "302c995f91f44cf26e77dc4758ad56c3ff0153ad" +[source."https://github.com/gfx-rs/naga"] +git = "https://github.com/gfx-rs/naga" +replace-with = "vendored-sources" +tag = "gfx-25" + +[source."https://github.com/gfx-rs/gfx"] +git = "https://github.com/gfx-rs/gfx" +replace-with = "vendored-sources" +rev = "27a1dae3796d33d23812f2bb8c7e3b5aea18b521" + [source."https://github.com/bytecodealliance/wasmtime"] git = "https://github.com/bytecodealliance/wasmtime" replace-with = "vendored-sources" diff --git a/Cargo.lock b/Cargo.lock index 7fe6591a36ab..e269c84272c3 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -1219,15 +1219,6 @@ dependencies = [ "smallbitvec", ] -[[package]] -name = "drm-fourcc" -version = "2.1.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ebbf3a5ed4671aabffefce172ff43d69c1f27dd2c6aea28e5212a70f32ada0cf" -dependencies = [ - "serde", -] - [[package]] name = "dtoa" version = "0.4.8" @@ -1349,35 +1340,6 @@ dependencies = [ "serde", ] -[[package]] -name = "external-memory" -version = "0.0.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e4dfe8d292b014422776a8c516862d2bff8a81b223a4461dfdc45f3862dc9d39" -dependencies = [ - "bitflags", - "drm-fourcc", -] - -[[package]] -name = "failure" -version = "0.1.8" -dependencies = [ - "failure_derive", -] - -[[package]] -name = "failure_derive" -version = "0.1.8" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "aa4da3c766cd7a0db8242e326e9e4e081edd567072893ed320008189715366a4" -dependencies = [ - "proc-macro2", - "quote", - "syn", - "synstructure", -] - [[package]] name = "fake-simd" version = "0.1.2" @@ -1865,9 +1827,8 @@ dependencies = [ [[package]] name = "gfx-auxil" -version = "0.10.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1694991b11d642680e82075a75c7c2bd75556b805efa7660b705689f05b1ab1c" +version = "0.9.0" +source = "git+https://github.com/gfx-rs/gfx?rev=27a1dae3796d33d23812f2bb8c7e3b5aea18b521#27a1dae3796d33d23812f2bb8c7e3b5aea18b521" dependencies = [ "fxhash", "gfx-hal", @@ -1876,15 +1837,13 @@ dependencies = [ [[package]] name = "gfx-backend-dx11" -version = "0.9.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8f9e453baf3aaef2b0c354ce0b3d63d76402e406a59b64b7182d123cfa6635ae" +version = "0.8.0" +source = "git+https://github.com/gfx-rs/gfx?rev=27a1dae3796d33d23812f2bb8c7e3b5aea18b521#27a1dae3796d33d23812f2bb8c7e3b5aea18b521" dependencies = [ "arrayvec", "bitflags", "gfx-auxil", "gfx-hal", - "gfx-renderdoc", "libloading 0.7.0", "log", "parking_lot", @@ -1899,9 +1858,8 @@ dependencies = [ [[package]] name = "gfx-backend-dx12" -version = "0.9.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "21506399f64a3c4d389182a89a30073856ae33eb712315456b4fd8f39ee7682a" +version = "0.8.0" +source = "git+https://github.com/gfx-rs/gfx?rev=27a1dae3796d33d23812f2bb8c7e3b5aea18b521#27a1dae3796d33d23812f2bb8c7e3b5aea18b521" dependencies = [ "arrayvec", "bit-set", @@ -1909,7 +1867,6 @@ dependencies = [ "d3d12", "gfx-auxil", "gfx-hal", - "gfx-renderdoc", "log", "parking_lot", "range-alloc", @@ -1922,9 +1879,8 @@ dependencies = [ [[package]] name = "gfx-backend-empty" -version = "0.9.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "29c8f813c47791918aa00dc9c9ddf961d23fa8c2a5d869e6cb8ea84f944820f4" +version = "0.8.0" +source = "git+https://github.com/gfx-rs/gfx?rev=27a1dae3796d33d23812f2bb8c7e3b5aea18b521#27a1dae3796d33d23812f2bb8c7e3b5aea18b521" dependencies = [ "gfx-hal", "log", @@ -1933,16 +1889,14 @@ dependencies = [ [[package]] name = "gfx-backend-metal" -version = "0.9.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0de85808e2a98994c6af925253f8a9593bc57180ef1ea137deab6d35cc949517" +version = "0.8.1" +source = "git+https://github.com/gfx-rs/gfx?rev=27a1dae3796d33d23812f2bb8c7e3b5aea18b521#27a1dae3796d33d23812f2bb8c7e3b5aea18b521" dependencies = [ "arrayvec", "bitflags", "block", "cocoa-foundation", "copyless", - "core-graphics-types", "foreign-types", "fxhash", "gfx-auxil", @@ -1961,50 +1915,37 @@ dependencies = [ [[package]] name = "gfx-backend-vulkan" -version = "0.9.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a9861ec855acbbc65c0e4f966d761224886e811dc2c6d413a4776e9293d0e5c0" +version = "0.8.0" +source = "git+https://github.com/gfx-rs/gfx?rev=27a1dae3796d33d23812f2bb8c7e3b5aea18b521#27a1dae3796d33d23812f2bb8c7e3b5aea18b521" dependencies = [ "arrayvec", "ash", "byteorder", "core-graphics-types", "gfx-hal", - "gfx-renderdoc", "inplace_it", + "libloading 0.7.0", "log", "naga", "objc", "parking_lot", "raw-window-handle", + "renderdoc-sys", "smallvec", "winapi", ] [[package]] name = "gfx-hal" -version = "0.9.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7fbb575ea793dd0507b3082f4f2cde62dc9f3cebd98f5cd49ba2a4da97a976fd" +version = "0.8.0" +source = "git+https://github.com/gfx-rs/gfx?rev=27a1dae3796d33d23812f2bb8c7e3b5aea18b521#27a1dae3796d33d23812f2bb8c7e3b5aea18b521" dependencies = [ "bitflags", - "external-memory", "naga", "raw-window-handle", "thiserror", ] -[[package]] -name = "gfx-renderdoc" -version = "0.1.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c8027995e247e2426d3a00d13f5191dd56c314bff02dc4b54cbf727f1ba9c40a" -dependencies = [ - "libloading 0.7.0", - "log", - "renderdoc-sys", -] - [[package]] name = "gkrust" version = "0.1.0" @@ -3054,13 +2995,13 @@ dependencies = [ [[package]] name = "metal" -version = "0.23.0" +version = "0.22.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "79d7d769f1c104b8388294d6594d491d2e21240636f5f94d37f8a0f3d7904450" +checksum = "1c12e48c737ee9a55e8bb2352bcde588f79ae308d3529ee888f7cc0f469b5777" dependencies = [ "bitflags", "block", - "core-graphics-types", + "cocoa-foundation", "foreign-types", "log", "objc", @@ -3328,9 +3269,8 @@ checksum = "a2983372caf4480544083767bf2d27defafe32af49ab4df3a0b7fc90793a3664" [[package]] name = "naga" -version = "0.5.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ef670817eef03d356d5a509ea275e7dd3a78ea9e24261ea3cb2dfed1abb08f64" +version = "0.4.0" +source = "git+https://github.com/gfx-rs/naga?tag=gfx-25#057d03ad86f18e3bb3866b20901d8d4e892dd3d6" dependencies = [ "bit-set", "bitflags", @@ -4132,8 +4072,7 @@ dependencies = [ [[package]] name = "range-alloc" version = "0.1.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "63e935c45e09cc6dcf00d2f0b2d630a58f4095320223d47fc68918722f0538b6" +source = "git+https://github.com/gfx-rs/gfx?rev=27a1dae3796d33d23812f2bb8c7e3b5aea18b521#27a1dae3796d33d23812f2bb8c7e3b5aea18b521" [[package]] name = "raw-window-handle" @@ -5756,7 +5695,7 @@ dependencies = [ [[package]] name = "wgpu-core" -version = "0.9.2" +version = "0.8.0" dependencies = [ "arrayvec", "bitflags", @@ -5784,7 +5723,7 @@ dependencies = [ [[package]] name = "wgpu-types" -version = "0.9.0" +version = "0.8.0" dependencies = [ "bitflags", "serde", diff --git a/dom/bindings/Bindings.conf b/dom/bindings/Bindings.conf index 60cccf5a057c..9f40cc4d5fbc 100644 --- a/dom/bindings/Bindings.conf +++ b/dom/bindings/Bindings.conf @@ -1347,6 +1347,9 @@ DOMInterfaces = { 'GPUAdapterFeatures': { 'nativeType': 'mozilla::webgpu::AdapterFeatures', }, +'GPUAdapterLimits': { + 'nativeType': 'mozilla::webgpu::AdapterLimits', +}, 'GPUBindGroup': { 'nativeType': 'mozilla::webgpu::BindGroup', }, @@ -1414,8 +1417,8 @@ DOMInterfaces = { 'GPUShaderModule': { 'nativeType': 'mozilla::webgpu::ShaderModule', }, -'GPUSupportedLimits': { - 'nativeType': 'mozilla::webgpu::SupportedLimits', +'GPUSwapChain': { + 'nativeType': 'mozilla::webgpu::SwapChain', }, 'GPUTexture': { 'nativeType': 'mozilla::webgpu::Texture', diff --git a/dom/webgpu/Adapter.cpp b/dom/webgpu/Adapter.cpp index 29f7e5fa1f44..54571d04ba91 100644 --- a/dom/webgpu/Adapter.cpp +++ b/dom/webgpu/Adapter.cpp @@ -7,9 +7,9 @@ #include "Adapter.h" #include "AdapterFeatures.h" +#include "AdapterLimits.h" #include "Device.h" #include "Instance.h" -#include "SupportedLimits.h" #include "ipc/WebGPUChild.h" #include "mozilla/dom/Promise.h" @@ -25,8 +25,7 @@ Adapter::Adapter(Instance* const aParent, mBridge(aParent->mBridge), mId(aInfo.id), mFeatures(new AdapterFeatures(this)), - mLimits(new SupportedLimits(this, aInfo.limits)), - mIsSoftware(aInfo.ty == ffi::WGPUDeviceType_Cpu) {} + mLimits(new AdapterLimits(this, aInfo.limits)) {} Adapter::~Adapter() { Cleanup(); } @@ -38,8 +37,7 @@ void Adapter::Cleanup() { } const RefPtr& Adapter::Features() const { return mFeatures; } -const RefPtr& Adapter::Limits() const { return mLimits; } -bool Adapter::IsSoftware() const { return mIsSoftware; } +const RefPtr& Adapter::Limits() const { return mLimits; } already_AddRefed Adapter::RequestDevice( const dom::GPUDeviceDescriptor& aDesc, ErrorResult& aRv) { diff --git a/dom/webgpu/Adapter.h b/dom/webgpu/Adapter.h index 8cee1d451e64..f9a5fe37175a 100644 --- a/dom/webgpu/Adapter.h +++ b/dom/webgpu/Adapter.h @@ -22,9 +22,9 @@ struct GPUFeatures; namespace webgpu { class AdapterFeatures; +class AdapterLimits; class Device; class Instance; -class SupportedLimits; class WebGPUChild; namespace ffi { struct WGPUAdapterInformation; @@ -46,15 +46,13 @@ class Adapter final : public ObjectBase, public ChildOf { // Cant have them as `const` right now, since we wouldn't be able // to unlink them in CC unlink. RefPtr mFeatures; - RefPtr mLimits; - const bool mIsSoftware = false; + RefPtr mLimits; public: Adapter(Instance* const aParent, const ffi::WGPUAdapterInformation& aInfo); void GetName(nsString& out) const { out = mName; } const RefPtr& Features() const; - const RefPtr& Limits() const; - bool IsSoftware() const; + const RefPtr& Limits() const; already_AddRefed RequestDevice( const dom::GPUDeviceDescriptor& aDesc, ErrorResult& aRv); diff --git a/dom/webgpu/SupportedLimits.cpp b/dom/webgpu/AdapterLimits.cpp similarity index 51% rename from dom/webgpu/SupportedLimits.cpp rename to dom/webgpu/AdapterLimits.cpp index 5416d7e5aa7b..30595ed62184 100644 --- a/dom/webgpu/SupportedLimits.cpp +++ b/dom/webgpu/AdapterLimits.cpp @@ -3,7 +3,7 @@ * License, v. 2.0. If a copy of the MPL was not distributed with this * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ -#include "SupportedLimits.h" +#include "AdapterLimits.h" #include "Adapter.h" #include "mozilla/dom/WebGPUBinding.h" #include "mozilla/webgpu/ffi/wgpu.h" @@ -11,64 +11,64 @@ namespace mozilla { namespace webgpu { -GPU_IMPL_CYCLE_COLLECTION(SupportedLimits, mParent) -GPU_IMPL_JS_WRAP(SupportedLimits) +GPU_IMPL_CYCLE_COLLECTION(AdapterLimits, mParent) +GPU_IMPL_JS_WRAP(AdapterLimits) -SupportedLimits::SupportedLimits(Adapter* const aParent, - const ffi::WGPULimits& aLimits) +AdapterLimits::AdapterLimits(Adapter* const aParent, + const ffi::WGPULimits& aLimits) : ChildOf(aParent), mLimits(new ffi::WGPULimits(aLimits)) {} -SupportedLimits::~SupportedLimits() = default; +AdapterLimits::~AdapterLimits() = default; -uint32_t SupportedLimits::MaxTextureDimension1D() const { +uint32_t AdapterLimits::MaxTextureDimension1D() const { return mLimits->max_texture_dimension_1d; } -uint32_t SupportedLimits::MaxTextureDimension2D() const { +uint32_t AdapterLimits::MaxTextureDimension2D() const { return mLimits->max_texture_dimension_2d; } -uint32_t SupportedLimits::MaxTextureDimension3D() const { +uint32_t AdapterLimits::MaxTextureDimension3D() const { return mLimits->max_texture_dimension_3d; } -uint32_t SupportedLimits::MaxTextureArrayLayers() const { +uint32_t AdapterLimits::MaxTextureArrayLayers() const { return mLimits->max_texture_array_layers; } -uint32_t SupportedLimits::MaxBindGroups() const { +uint32_t AdapterLimits::MaxBindGroups() const { return mLimits->max_bind_groups; } -uint32_t SupportedLimits::MaxDynamicUniformBuffersPerPipelineLayout() const { +uint32_t AdapterLimits::MaxDynamicUniformBuffersPerPipelineLayout() const { return mLimits->max_dynamic_uniform_buffers_per_pipeline_layout; } -uint32_t SupportedLimits::MaxDynamicStorageBuffersPerPipelineLayout() const { +uint32_t AdapterLimits::MaxDynamicStorageBuffersPerPipelineLayout() const { return mLimits->max_dynamic_storage_buffers_per_pipeline_layout; } -uint32_t SupportedLimits::MaxSampledTexturesPerShaderStage() const { +uint32_t AdapterLimits::MaxSampledTexturesPerShaderStage() const { return mLimits->max_sampled_textures_per_shader_stage; } -uint32_t SupportedLimits::MaxSamplersPerShaderStage() const { +uint32_t AdapterLimits::MaxSamplersPerShaderStage() const { return mLimits->max_samplers_per_shader_stage; } -uint32_t SupportedLimits::MaxStorageBuffersPerShaderStage() const { +uint32_t AdapterLimits::MaxStorageBuffersPerShaderStage() const { return mLimits->max_storage_buffers_per_shader_stage; } -uint32_t SupportedLimits::MaxStorageTexturesPerShaderStage() const { +uint32_t AdapterLimits::MaxStorageTexturesPerShaderStage() const { return mLimits->max_storage_textures_per_shader_stage; } -uint32_t SupportedLimits::MaxUniformBuffersPerShaderStage() const { +uint32_t AdapterLimits::MaxUniformBuffersPerShaderStage() const { return mLimits->max_uniform_buffers_per_shader_stage; } -uint32_t SupportedLimits::MaxUniformBufferBindingSize() const { +uint32_t AdapterLimits::MaxUniformBufferBindingSize() const { return mLimits->max_uniform_buffer_binding_size; } -uint32_t SupportedLimits::MaxStorageBufferBindingSize() const { +uint32_t AdapterLimits::MaxStorageBufferBindingSize() const { return mLimits->max_storage_buffer_binding_size; } -uint32_t SupportedLimits::MaxVertexBuffers() const { +uint32_t AdapterLimits::MaxVertexBuffers() const { return mLimits->max_vertex_buffers; } -uint32_t SupportedLimits::MaxVertexAttributes() const { +uint32_t AdapterLimits::MaxVertexAttributes() const { return mLimits->max_vertex_attributes; } -uint32_t SupportedLimits::MaxVertexBufferArrayStride() const { +uint32_t AdapterLimits::MaxVertexBufferArrayStride() const { return mLimits->max_vertex_buffer_array_stride; } diff --git a/dom/webgpu/SupportedLimits.h b/dom/webgpu/AdapterLimits.h similarity index 79% rename from dom/webgpu/SupportedLimits.h rename to dom/webgpu/AdapterLimits.h index 1b124414076f..f1f725722a09 100644 --- a/dom/webgpu/SupportedLimits.h +++ b/dom/webgpu/AdapterLimits.h @@ -3,8 +3,8 @@ * License, v. 2.0. If a copy of the MPL was not distributed with this * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ -#ifndef GPU_SupportedLimits_H_ -#define GPU_SupportedLimits_H_ +#ifndef GPU_AdapterLimitss_H_ +#define GPU_AdapterLimitss_H_ #include "nsWrapperCache.h" #include "ObjectModel.h" @@ -16,12 +16,12 @@ struct WGPULimits; } class Adapter; -class SupportedLimits final : public nsWrapperCache, public ChildOf { +class AdapterLimits final : public nsWrapperCache, public ChildOf { const UniquePtr mLimits; public: - GPU_DECL_CYCLE_COLLECTION(SupportedLimits) - GPU_DECL_JS_WRAP(SupportedLimits) + GPU_DECL_CYCLE_COLLECTION(AdapterLimits) + GPU_DECL_JS_WRAP(AdapterLimits) uint32_t MaxTextureDimension1D() const; uint32_t MaxTextureDimension2D() const; @@ -41,14 +41,14 @@ class SupportedLimits final : public nsWrapperCache, public ChildOf { uint32_t MaxVertexAttributes() const; uint32_t MaxVertexBufferArrayStride() const; - SupportedLimits(Adapter* const aParent, const ffi::WGPULimits& aLimits); + AdapterLimits(Adapter* const aParent, const ffi::WGPULimits& aLimits); private: - ~SupportedLimits(); + ~AdapterLimits(); void Cleanup() {} }; } // namespace webgpu } // namespace mozilla -#endif // GPU_SupportedLimits_H_ +#endif // GPU_AdapterLimitss_H_ diff --git a/dom/webgpu/CanvasContext.cpp b/dom/webgpu/CanvasContext.cpp index 733baa9a04a9..65711a4ba2e2 100644 --- a/dom/webgpu/CanvasContext.cpp +++ b/dom/webgpu/CanvasContext.cpp @@ -5,12 +5,11 @@ #include "mozilla/dom/WebGPUBinding.h" #include "CanvasContext.h" +#include "SwapChain.h" #include "nsDisplayList.h" #include "LayerUserData.h" #include "mozilla/dom/HTMLCanvasElement.h" #include "mozilla/layers/CompositorManagerChild.h" -#include "mozilla/layers/ImageDataSerializer.h" -#include "mozilla/layers/LayersSurfaces.h" #include "mozilla/layers/RenderRootStateManager.h" #include "mozilla/layers/WebRenderBridgeChild.h" #include "ipc/WebGPUChild.h" @@ -21,7 +20,7 @@ namespace webgpu { NS_IMPL_CYCLE_COLLECTING_ADDREF(CanvasContext) NS_IMPL_CYCLE_COLLECTING_RELEASE(CanvasContext) -GPU_IMPL_CYCLE_COLLECTION_WRAPPERCACHE(CanvasContext, mTexture, mBridge, +GPU_IMPL_CYCLE_COLLECTION_WRAPPERCACHE(CanvasContext, mSwapChain, mCanvasElement, mOffscreenCanvas) NS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION(CanvasContext) @@ -40,7 +39,10 @@ CanvasContext::~CanvasContext() { } void CanvasContext::Cleanup() { - Unconfigure(); + if (mSwapChain) { + mSwapChain->Destroy(mExternalImageId); + mSwapChain = nullptr; + } if (mRenderRootStateManager && mImageKey) { mRenderRootStateManager->AddImageKeyForDiscard(mImageKey.value()); mRenderRootStateManager = nullptr; @@ -64,32 +66,41 @@ bool CanvasContext::UpdateWebRenderCanvasData( return true; } -void CanvasContext::Configure(const dom::GPUCanvasConfiguration& aDesc) { - Unconfigure(); +dom::GPUTextureFormat CanvasContext::GetSwapChainPreferredFormat( + Adapter&) const { + return dom::GPUTextureFormat::Bgra8unorm; +} +RefPtr CanvasContext::ConfigureSwapChain( + const dom::GPUSwapChainDescriptor& aDesc, ErrorResult& aRv) { + Cleanup(); + + gfx::SurfaceFormat format; switch (aDesc.mFormat) { case dom::GPUTextureFormat::Rgba8unorm: - mGfxFormat = gfx::SurfaceFormat::R8G8B8A8; + format = gfx::SurfaceFormat::R8G8B8A8; break; case dom::GPUTextureFormat::Bgra8unorm: - mGfxFormat = gfx::SurfaceFormat::B8G8R8A8; + format = gfx::SurfaceFormat::B8G8R8A8; break; default: - NS_WARNING("Specified swap chain format is not supported"); - return; + aRv.Throw(NS_ERROR_DOM_NOT_SUPPORTED_ERR); + return nullptr; } - gfx::IntSize actualSize(mWidth, mHeight); - mTexture = aDesc.mDevice->InitSwapChain(aDesc, mExternalImageId, mGfxFormat, - &actualSize); - mTexture->mTargetCanvasElement = mCanvasElement; - mBridge = aDesc.mDevice->GetBridge(); - mGfxSize = actualSize; + dom::GPUExtent3DDict extent; + extent.mWidth = mWidth; + extent.mHeight = mHeight; + extent.mDepthOrArrayLayers = 1; + mSwapChain = new SwapChain(aDesc, extent, mExternalImageId, format); // Force a new frame to be built, which will execute the // `CanvasContextType::WebGPU` switch case in `CreateWebRenderCommands` and // populate the WR user data. mCanvasElement->InvalidateCanvas(); + + mSwapChain->GetCurrentTexture()->mTargetCanvasElement = mCanvasElement; + return mSwapChain; } Maybe CanvasContext::GetImageKey() const { return mImageKey; } @@ -102,40 +113,37 @@ wr::ImageKey CanvasContext::CreateImageKey( return key; } -void CanvasContext::Unconfigure() { - if (mBridge && mBridge->IsOpen()) { - mBridge->SendSwapChainDestroy(mExternalImageId); - } - mBridge = nullptr; - mTexture = nullptr; -} - -dom::GPUTextureFormat CanvasContext::GetPreferredFormat(Adapter&) const { - return dom::GPUTextureFormat::Bgra8unorm; -} - -RefPtr CanvasContext::GetCurrentTexture() { return mTexture; } - bool CanvasContext::UpdateWebRenderLocalCanvasData( layers::WebRenderLocalCanvasData* aCanvasData) { - if (!mTexture) { + if (!mSwapChain || !mSwapChain->GetParent()) { return false; } - aCanvasData->mGpuBridge = mBridge.get(); - aCanvasData->mGpuTextureId = mTexture->mId; - aCanvasData->mExternalImageId = mExternalImageId; - aCanvasData->mFormat = mGfxFormat; - return true; -} + const auto size = + nsIntSize(AssertedCast(mWidth), AssertedCast(mHeight)); + if (mSwapChain->mSize != size) { + const auto gfxFormat = mSwapChain->mGfxFormat; + dom::GPUSwapChainDescriptor desc; + desc.mFormat = static_cast(mSwapChain->mFormat); + desc.mUsage = mSwapChain->mUsage; + desc.mDevice = mSwapChain->GetParent(); -wr::ImageDescriptor CanvasContext::MakeImageDescriptor() const { - const layers::RGBDescriptor rgbDesc(mGfxSize, mGfxFormat, false); - const auto targetStride = layers::ImageDataSerializer::GetRGBStride(rgbDesc); - const bool preferCompositorSurface = true; - return wr::ImageDescriptor(mGfxSize, targetStride, mGfxFormat, - wr::OpacityType::HasAlphaChannel, - preferCompositorSurface); + mSwapChain->Destroy(mExternalImageId); + mExternalImageId = + layers::CompositorManagerChild::GetInstance()->GetNextExternalImageId(); + + dom::GPUExtent3DDict extent; + extent.mWidth = size.width; + extent.mHeight = size.height; + extent.mDepthOrArrayLayers = 1; + mSwapChain = new SwapChain(desc, extent, mExternalImageId, gfxFormat); + } + + aCanvasData->mGpuBridge = mSwapChain->GetParent()->GetBridge().get(); + aCanvasData->mGpuTextureId = mSwapChain->GetCurrentTexture()->mId; + aCanvasData->mExternalImageId = mExternalImageId; + aCanvasData->mFormat = mSwapChain->mGfxFormat; + return true; } } // namespace webgpu diff --git a/dom/webgpu/CanvasContext.h b/dom/webgpu/CanvasContext.h index 3519791c9468..c04a9d712499 100644 --- a/dom/webgpu/CanvasContext.h +++ b/dom/webgpu/CanvasContext.h @@ -9,12 +9,12 @@ #include "nsICanvasRenderingContextInternal.h" #include "nsWrapperCache.h" #include "ObjectModel.h" +#include "SwapChain.h" #include "mozilla/webrender/WebRenderAPI.h" namespace mozilla { namespace dom { class Promise; -struct GPUCanvasConfiguration; enum class GPUTextureFormat : uint8_t; } // namespace dom namespace layers { @@ -22,6 +22,7 @@ class WebRenderLocalCanvasData; }; namespace webgpu { class Adapter; +class SwapChain; class Texture; class CanvasContext final : public nsICanvasRenderingContextInternal, @@ -40,13 +41,13 @@ class CanvasContext final : public nsICanvasRenderingContextInternal, JSObject* WrapObject(JSContext* aCx, JS::Handle aGivenProto) override; + void RemoveSwapChain(); + Maybe GetImageKey() const; wr::ImageKey CreateImageKey(layers::RenderRootStateManager* aManager); bool UpdateWebRenderLocalCanvasData( layers::WebRenderLocalCanvasData* aCanvasData); - wr::ImageDescriptor MakeImageDescriptor() const; - wr::ExternalImageId mExternalImageId; public: // nsICanvasRenderingContextInternal @@ -97,19 +98,14 @@ class CanvasContext final : public nsICanvasRenderingContextInternal, bool IsContextCleanForFrameCapture() override { return false; } public: - void Configure(const dom::GPUCanvasConfiguration& aDesc); - void Unconfigure(); - - dom::GPUTextureFormat GetPreferredFormat(Adapter& aAdapter) const; - RefPtr GetCurrentTexture(); + dom::GPUTextureFormat GetSwapChainPreferredFormat(Adapter& aAdapter) const; + RefPtr ConfigureSwapChain(const dom::GPUSwapChainDescriptor& aDesc, + ErrorResult& aRv); private: uint32_t mWidth = 0, mHeight = 0; - RefPtr mBridge; - RefPtr mTexture; - gfx::SurfaceFormat mGfxFormat = gfx::SurfaceFormat::R8G8B8A8; - gfx::IntSize mGfxSize; + RefPtr mSwapChain; RefPtr mRenderRootStateManager; Maybe mImageKey; }; diff --git a/dom/webgpu/CompilationMessage.h b/dom/webgpu/CompilationMessage.h index b5c41c3d494f..7015905f84bf 100644 --- a/dom/webgpu/CompilationMessage.h +++ b/dom/webgpu/CompilationMessage.h @@ -22,8 +22,6 @@ class CompilationMessage final : public nsWrapperCache, dom::GPUCompilationMessageType mType = dom::GPUCompilationMessageType::Error; uint64_t mLineNum = 0; uint64_t mLinePos = 0; - uint64_t mOffset = 0; - uint64_t mLength = 0; public: GPU_DECL_CYCLE_COLLECTION(CompilationMessage) @@ -33,8 +31,6 @@ class CompilationMessage final : public nsWrapperCache, dom::GPUCompilationMessageType Type() const { return mType; } uint64_t LineNum() const { return mLineNum; } uint64_t LinePos() const { return mLinePos; } - uint64_t Offset() const { return mOffset; } - uint64_t Length() const { return mLength; } private: explicit CompilationMessage(CompilationInfo* const aParent); diff --git a/dom/webgpu/Device.cpp b/dom/webgpu/Device.cpp index be8f7aff6091..024db9424310 100644 --- a/dom/webgpu/Device.cpp +++ b/dom/webgpu/Device.cpp @@ -228,28 +228,13 @@ already_AddRefed Device::CreateRenderPipeline( } already_AddRefed Device::InitSwapChain( - const dom::GPUCanvasConfiguration& aDesc, - wr::ExternalImageId aExternalImageId, gfx::SurfaceFormat aFormat, - gfx::IntSize* aCanvasSize) { - gfx::IntSize size = *aCanvasSize; - if (aDesc.mSize.WasPassed()) { - const auto& descSize = aDesc.mSize.Value(); - if (descSize.IsRangeEnforcedUnsignedLongSequence()) { - const auto& seq = descSize.GetAsRangeEnforcedUnsignedLongSequence(); - // TODO: add a check for `seq.Length()` - size.width = AssertedCast(seq[0]); - size.height = AssertedCast(seq[1]); - } else if (descSize.IsGPUExtent3DDict()) { - const auto& dict = descSize.GetAsGPUExtent3DDict(); - size.width = AssertedCast(dict.mWidth); - size.height = AssertedCast(dict.mHeight); - } else { - MOZ_CRASH("Unexpected union"); - } - } - *aCanvasSize = size; - - const layers::RGBDescriptor rgbDesc(size, aFormat, false); + const dom::GPUSwapChainDescriptor& aDesc, + const dom::GPUExtent3DDict& aExtent3D, wr::ExternalImageId aExternalImageId, + gfx::SurfaceFormat aFormat) { + const layers::RGBDescriptor rgbDesc( + gfx::IntSize(AssertedCast(aExtent3D.mWidth), + AssertedCast(aExtent3D.mHeight)), + aFormat, false); // buffer count doesn't matter much, will be created on demand const size_t maxBufferCount = 10; mBridge->DeviceCreateSwapChain(mId, rgbDesc, maxBufferCount, @@ -257,10 +242,7 @@ already_AddRefed Device::InitSwapChain( dom::GPUTextureDescriptor desc; desc.mDimension = dom::GPUTextureDimension::_2d; - auto& sizeDict = desc.mSize.SetAsGPUExtent3DDict(); - sizeDict.mWidth = size.width; - sizeDict.mHeight = size.height; - sizeDict.mDepthOrArrayLayers = 1; + desc.mSize.SetAsGPUExtent3DDict() = aExtent3D; desc.mFormat = aDesc.mFormat; desc.mMipLevelCount = 1; desc.mSampleCount = 1; diff --git a/dom/webgpu/Device.h b/dom/webgpu/Device.h index a13449632da4..507a189d1c58 100644 --- a/dom/webgpu/Device.h +++ b/dom/webgpu/Device.h @@ -35,7 +35,7 @@ struct GPUComputePipelineDescriptor; struct GPURenderBundleEncoderDescriptor; struct GPURenderPipelineDescriptor; struct GPUCommandEncoderDescriptor; -struct GPUCanvasConfiguration; +struct GPUSwapChainDescriptor; class EventHandlerNonNull; class Promise; @@ -90,9 +90,9 @@ class Device final : public DOMEventTargetHelper { void UnmapBuffer(RawId aId, ipc::Shmem&& aShmem, bool aFlush, bool aKeepShmem); already_AddRefed InitSwapChain( - const dom::GPUCanvasConfiguration& aDesc, - wr::ExternalImageId aExternalImageId, gfx::SurfaceFormat aFormat, - gfx::IntSize* aDefaultSize); + const dom::GPUSwapChainDescriptor& aDesc, + const dom::GPUExtent3DDict& aExtent3D, + wr::ExternalImageId aExternalImageId, gfx::SurfaceFormat aFormat); private: ~Device(); diff --git a/dom/webgpu/RenderPassEncoder.cpp b/dom/webgpu/RenderPassEncoder.cpp index a4d6e7621bcd..69b81beef732 100644 --- a/dom/webgpu/RenderPassEncoder.cpp +++ b/dom/webgpu/RenderPassEncoder.cpp @@ -40,7 +40,7 @@ ffi::WGPUStoreOp ConvertStoreOp(const dom::GPUStoreOp& aOp) { switch (aOp) { case dom::GPUStoreOp::Store: return ffi::WGPUStoreOp_Store; - case dom::GPUStoreOp::Discard: + case dom::GPUStoreOp::Clear: return ffi::WGPUStoreOp_Clear; default: MOZ_CRASH("Unexpected load op"); diff --git a/dom/webgpu/SwapChain.cpp b/dom/webgpu/SwapChain.cpp new file mode 100644 index 000000000000..798d6dc70746 --- /dev/null +++ b/dom/webgpu/SwapChain.cpp @@ -0,0 +1,52 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ + +#include "SwapChain.h" +#include "Texture.h" +#include "mozilla/dom/WebGPUBinding.h" +#include "ipc/WebGPUChild.h" + +namespace mozilla { +namespace webgpu { + +GPU_IMPL_CYCLE_COLLECTION(SwapChain, mParent, mTexture) +GPU_IMPL_JS_WRAP(SwapChain) + +SwapChain::SwapChain(const dom::GPUSwapChainDescriptor& aDesc, + const dom::GPUExtent3DDict& aExtent3D, + wr::ExternalImageId aExternalImageId, + gfx::SurfaceFormat aFormat) + : ChildOf(aDesc.mDevice), + mGfxFormat(aFormat), + mFormat(static_cast(aDesc.mFormat)), + mUsage(aDesc.mUsage), + mSize(aExtent3D.mWidth, aExtent3D.mHeight), + mTexture(aDesc.mDevice->InitSwapChain(aDesc, aExtent3D, aExternalImageId, + aFormat)) {} + +SwapChain::~SwapChain() { Cleanup(); } + +void SwapChain::Cleanup() { + if (mValid) { + mValid = false; + } +} + +RefPtr SwapChain::GetParent() const { return mParent; } + +void SwapChain::Destroy(wr::ExternalImageId aExternalImageId) { + if (mValid && mParent && mParent->GetBridge()) { + mValid = false; + auto bridge = mParent->GetBridge(); + if (bridge && bridge->IsOpen()) { + bridge->SendSwapChainDestroy(aExternalImageId); + } + } +} + +RefPtr SwapChain::GetCurrentTexture() { return mTexture; } + +} // namespace webgpu +} // namespace mozilla diff --git a/dom/webgpu/SwapChain.h b/dom/webgpu/SwapChain.h new file mode 100644 index 000000000000..886571f5729a --- /dev/null +++ b/dom/webgpu/SwapChain.h @@ -0,0 +1,54 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ + +#ifndef GPU_SwapChain_H_ +#define GPU_SwapChain_H_ + +#include "nsWrapperCache.h" +#include "ObjectModel.h" +#include "mozilla/webrender/WebRenderAPI.h" + +namespace mozilla { +namespace dom { +struct GPUExtent3DDict; +struct GPUSwapChainDescriptor; +} // namespace dom +namespace webgpu { + +class Device; +class Texture; + +class SwapChain final : public ObjectBase, public ChildOf { + public: + GPU_DECL_CYCLE_COLLECTION(SwapChain) + GPU_DECL_JS_WRAP(SwapChain) + + SwapChain(const dom::GPUSwapChainDescriptor& aDesc, + const dom::GPUExtent3DDict& aExtent3D, + wr::ExternalImageId aExternalImageId, gfx::SurfaceFormat aFormat); + + RefPtr GetParent() const; + void Destroy(wr::ExternalImageId aExternalImageId); + + const gfx::SurfaceFormat mGfxFormat; + const uint8_t + mFormat; // This is `dom::GPUTextureFormat` but without the includes + const uint32_t mUsage; + const nsIntSize mSize; + + private: + virtual ~SwapChain(); + void Cleanup(); + + RefPtr mTexture; + + public: + RefPtr GetCurrentTexture(); +}; + +} // namespace webgpu +} // namespace mozilla + +#endif // GPU_SwapChain_H_ diff --git a/dom/webgpu/ipc/WebGPUChild.cpp b/dom/webgpu/ipc/WebGPUChild.cpp index f8fcc3c1840a..a05f7e3829ba 100644 --- a/dom/webgpu/ipc/WebGPUChild.cpp +++ b/dom/webgpu/ipc/WebGPUChild.cpp @@ -208,8 +208,8 @@ Maybe WebGPUChild::AdapterRequestDevice( ffi::WGPUDeviceDescriptor desc = {}; ffi::wgpu_client_fill_default_limits(&desc.limits); - if (aDesc.mRequiredLimits.WasPassed()) { - for (const auto& entry : aDesc.mRequiredLimits.Value().Entries()) { + if (aDesc.mNonGuaranteedLimits.WasPassed()) { + for (const auto& entry : aDesc.mNonGuaranteedLimits.Value().Entries()) { Unused << entry; // TODO } /*desc.limits.max_bind_groups = lim.mMaxBindGroups; diff --git a/dom/webgpu/moz.build b/dom/webgpu/moz.build index 2f41ee412c34..b9b6e1337a9c 100644 --- a/dom/webgpu/moz.build +++ b/dom/webgpu/moz.build @@ -17,6 +17,7 @@ DIRS += [] h_and_cpp = [ "Adapter", "AdapterFeatures", + "AdapterLimits", "BindGroup", "BindGroupLayout", "Buffer", @@ -41,7 +42,7 @@ h_and_cpp = [ "RenderPipeline", "Sampler", "ShaderModule", - "SupportedLimits", + "SwapChain", "Texture", "TextureView", "ValidationError", diff --git a/dom/webidl/WebGPU.webidl b/dom/webidl/WebGPU.webidl index 1bbbbbbdb04e..f44e9ee3c39a 100644 --- a/dom/webidl/WebGPU.webidl +++ b/dom/webidl/WebGPU.webidl @@ -91,8 +91,8 @@ interface GPUAdapterFeatures { }; dictionary GPUDeviceDescriptor { - sequence requiredFeatures = []; - record requiredLimits; + sequence nonGuaranteedFeatures = []; + record nonGuaranteedLimits; }; enum GPUFeatureName { @@ -106,7 +106,7 @@ enum GPUFeatureName { [Pref="dom.webgpu.enabled", Exposed=Window] -interface GPUSupportedLimits { +interface GPUAdapterLimits { readonly attribute unsigned long maxTextureDimension1D; readonly attribute unsigned long maxTextureDimension2D; readonly attribute unsigned long maxTextureDimension3D; @@ -131,8 +131,7 @@ interface GPUSupportedLimits { interface GPUAdapter { readonly attribute DOMString name; [SameObject] readonly attribute GPUAdapterFeatures features; - [SameObject] readonly attribute GPUSupportedLimits limits; - readonly attribute boolean isSoftware; + [SameObject] readonly attribute GPUAdapterLimits limits; [NewObject] Promise requestDevice(optional GPUDeviceDescriptor descriptor = {}); @@ -529,11 +528,12 @@ dictionary GPUTextureBindingLayout { }; enum GPUStorageTextureAccess { + "read-only", "write-only", }; dictionary GPUStorageTextureBindingLayout { - GPUStorageTextureAccess access = "write-only"; + required GPUStorageTextureAccess access; required GPUTextureFormat format; GPUTextureViewDimension viewDimension = "2d"; }; @@ -599,8 +599,6 @@ interface GPUCompilationMessage { readonly attribute GPUCompilationMessageType type; readonly attribute unsigned long long lineNum; readonly attribute unsigned long long linePos; - readonly attribute unsigned long long offset; - readonly attribute unsigned long long length; }; [Pref="dom.webgpu.enabled", @@ -696,7 +694,7 @@ enum GPUVertexFormat { "sint32x4", }; -enum GPUVertexStepMode { +enum GPUInputStepMode { "vertex", "instance", }; @@ -709,7 +707,7 @@ dictionary GPUVertexAttribute { dictionary GPUVertexBufferLayout { required GPUSize64 arrayStride; - GPUVertexStepMode stepMode = "vertex"; + GPUInputStepMode stepMode = "vertex"; required sequence attributes; }; @@ -872,7 +870,7 @@ enum GPULoadOp { enum GPUStoreOp { "store", - "discard" + "clear" }; dictionary GPURenderPassColorAttachment { @@ -1067,9 +1065,10 @@ GPURenderBundle includes GPUObjectBase; dictionary GPURenderBundleDescriptor : GPUObjectDescriptorBase { }; -dictionary GPURenderBundleEncoderDescriptor : GPURenderPassLayout { - boolean depthReadOnly = false; - boolean stencilReadOnly = false; +dictionary GPURenderBundleEncoderDescriptor : GPUObjectDescriptorBase { + required sequence colorFormats; + GPUTextureFormat depthStencilFormat; + GPUSize32 sampleCount = 1; }; [Pref="dom.webgpu.enabled", @@ -1081,14 +1080,8 @@ GPURenderBundleEncoder includes GPUObjectBase; GPURenderBundleEncoder includes GPUProgrammablePassEncoder; GPURenderBundleEncoder includes GPURenderEncoderBase; -dictionary GPURenderPassLayout: GPUObjectDescriptorBase { - required sequence colorFormats; - GPUTextureFormat depthStencilFormat; - GPUSize32 sampleCount = 1; -}; - // **************************************************************************** -// OTHER (Canvas, Query, Queue, Device) +// OTHER (Query, Queue, SwapChain, Device) // **************************************************************************** // Query set @@ -1149,13 +1142,18 @@ interface GPUQueue { }; GPUQueue includes GPUObjectBase; -dictionary GPUCanvasConfiguration { +[Pref="dom.webgpu.enabled", + Exposed=Window] +interface GPUSwapChain { + GPUTexture getCurrentTexture(); +}; +GPUSwapChain includes GPUObjectBase; + +dictionary GPUSwapChainDescriptor : GPUObjectDescriptorBase { required GPUDevice device; required GPUTextureFormat format; GPUTextureUsageFlags usage = 0x10; //GPUTextureUsage.OUTPUT_ATTACHMENT - //GPUPredefinedColorSpace colorSpace = "srgb"; //TODO GPUCanvasCompositingAlphaMode compositingAlphaMode = "opaque"; - GPUExtent3D size; }; enum GPUCanvasCompositingAlphaMode { @@ -1166,11 +1164,10 @@ enum GPUCanvasCompositingAlphaMode { [Pref="dom.webgpu.enabled", Exposed=Window] interface GPUCanvasContext { - // Calling configure() a second time invalidates the previous one, + // Calling configureSwapChain a second time invalidates the previous one, // and all of the textures it's produced. - void configure(GPUCanvasConfiguration descriptor); - void unconfigure(); + [Throws] + GPUSwapChain configureSwapChain(GPUSwapChainDescriptor descriptor); - GPUTextureFormat getPreferredFormat(GPUAdapter adapter); - GPUTexture getCurrentTexture(); + GPUTextureFormat getSwapChainPreferredFormat(GPUAdapter adapter); }; diff --git a/gfx/wgpu/.gitattributes b/gfx/wgpu/.gitattributes deleted file mode 100644 index 149b5351f266..000000000000 --- a/gfx/wgpu/.gitattributes +++ /dev/null @@ -1,2 +0,0 @@ -*.mtl binary -*.obj binary diff --git a/gfx/wgpu/.github/ISSUE_TEMPLATE/config.yml b/gfx/wgpu/.github/ISSUE_TEMPLATE/config.yml deleted file mode 100644 index 2d630869871b..000000000000 --- a/gfx/wgpu/.github/ISSUE_TEMPLATE/config.yml +++ /dev/null @@ -1,8 +0,0 @@ -blank_issues_enabled: false -contact_links: - - name: Issues with shaders - url: https://github.com/gfx-rs/naga/issues/new/choose - about: Issues with or enhancements for the shader translation. - - name: Question about wgpu - url: https://github.com/gfx-rs/wgpu-rs/discussions/new - about: Any questions about how to use wgpu should go here. diff --git a/gfx/wgpu/.github/ISSUE_TEMPLATE/other.md b/gfx/wgpu/.github/ISSUE_TEMPLATE/other.md index ea2d879744c6..52ef2e8a4717 100644 --- a/gfx/wgpu/.github/ISSUE_TEMPLATE/other.md +++ b/gfx/wgpu/.github/ISSUE_TEMPLATE/other.md @@ -4,4 +4,7 @@ about: Strange things you want to tell us title: '' labels: question assignees: '' + --- + + diff --git a/gfx/wgpu/.github/pull_request_template.md b/gfx/wgpu/.github/pull_request_template.md index 1fd37e13470b..55be6c377a8e 100644 --- a/gfx/wgpu/.github/pull_request_template.md +++ b/gfx/wgpu/.github/pull_request_template.md @@ -6,3 +6,12 @@ _Describe what problem this is solving, and how it's solved._ **Testing** _Explain how this change is tested._ + diff --git a/gfx/wgpu/.github/workflows/ci.yml b/gfx/wgpu/.github/workflows/ci.yml index 4961a9fed638..4988231b179a 100644 --- a/gfx/wgpu/.github/workflows/ci.yml +++ b/gfx/wgpu/.github/workflows/ci.yml @@ -34,18 +34,13 @@ jobs: - name: Additional core features run: cargo check --manifest-path wgpu-core/Cargo.toml --features trace --target ${{ env.TARGET }} - wasm: + webgl_build: name: Web Assembly runs-on: ubuntu-18.04 - env: - RUSTFLAGS: --cfg=web_sys_unstable_apis steps: - - uses: actions/checkout@v2 - - run: rustup target add wasm32-unknown-unknown - - name: Check WebGPU - run: cargo check --all-targets --target=wasm32-unknown-unknown - - name: Check WebGL - run: cargo check --all-targets --target=wasm32-unknown-unknown --features webgl + - uses: actions/checkout@v2 + - run: rustup target add wasm32-unknown-unknown + - run: cargo build --manifest-path wgpu-core/Cargo.toml --target wasm32-unknown-unknown build: name: ${{ matrix.name }} @@ -122,20 +117,6 @@ jobs: - if: matrix.channel == 'nightly' run: cargo test -- --nocapture - docs: - runs-on: [ubuntu-18.04] - steps: - - uses: actions/checkout@v2 - - name: Install latest nightly - uses: actions-rs/toolchain@v1 - with: - toolchain: nightly - override: true - continue-on-error: true - - name: cargo doc - run: cargo --version; cargo doc --no-deps - continue-on-error: true - lint: name: Clippy runs-on: ubuntu-latest @@ -151,7 +132,3 @@ jobs: with: command: clippy args: -- -D warnings - - uses: actions-rs/cargo@v1 - with: - command: fmt - args: -- --check diff --git a/gfx/wgpu/.github/workflows/docs.yml b/gfx/wgpu/.github/workflows/docs.yml deleted file mode 100644 index 8aaf466b86d1..000000000000 --- a/gfx/wgpu/.github/workflows/docs.yml +++ /dev/null @@ -1,45 +0,0 @@ -name: Documentation - -on: - push: - branches: - - master - -jobs: - build: - runs-on: ubuntu-latest - - steps: - - name: Checkout the code - uses: actions/checkout@v2 - with: - persist-credentials: false - - - name: Install latest nightly - uses: actions-rs/toolchain@v1 - with: - toolchain: nightly - override: true - continue-on-error: true - - - name: Add EGL for OpenGL - run: | - sudo apt-get update -y -qq - sudo apt-get install -y -qq libegl1-mesa-dev - - - name: Build the docs (nightly) - run: | - cargo +nightly doc --lib --all-features - - - name: Build the docs (stable) - run: cargo +stable doc --lib --all-features - if: ${{ failure() }} - - - name: Deploy the docs - uses: JamesIves/github-pages-deploy-action@releases/v3 - with: - ACCESS_TOKEN: ${{ secrets.WEB_DEPLOY }} - FOLDER: target/doc - REPOSITORY_NAME: gfx-rs/wgpu-rs.github.io - BRANCH: master - TARGET_FOLDER: doc diff --git a/gfx/wgpu/.github/workflows/publish.yml b/gfx/wgpu/.github/workflows/publish.yml deleted file mode 100644 index b1ebd4c662c9..000000000000 --- a/gfx/wgpu/.github/workflows/publish.yml +++ /dev/null @@ -1,47 +0,0 @@ -name: Publish - -on: - push: - branches: - - gecko - -env: - RUSTFLAGS: --cfg=web_sys_unstable_apis - -jobs: - build: - runs-on: ubuntu-latest - - steps: - - name: Checkout the code - uses: actions/checkout@v2 - with: - persist-credentials: false - - - name: Install Rust WASM toolchain - uses: actions-rs/toolchain@v1 - with: - toolchain: stable - target: wasm32-unknown-unknown - - - name: Build the examples - run: cargo build --release --target wasm32-unknown-unknown --examples - - - name: Install wasm-bindgen-cli - run: cargo install wasm-bindgen-cli - - - name: Generate JS bindings for the examples - run: | - for i in target/wasm32-unknown-unknown/release/examples/*.wasm; - do - wasm-bindgen --no-typescript --out-dir target/generated --web "$i"; - done - - - name: Deploy the examples - uses: JamesIves/github-pages-deploy-action@releases/v3 - with: - ACCESS_TOKEN: ${{ secrets.WEB_DEPLOY }} - FOLDER: target/generated - REPOSITORY_NAME: gfx-rs/wgpu-rs.github.io - BRANCH: master - TARGET_FOLDER: examples/wasm diff --git a/gfx/wgpu/.gitignore b/gfx/wgpu/.gitignore index 2f516ba67f64..c5b453f50391 100644 --- a/gfx/wgpu/.gitignore +++ b/gfx/wgpu/.gitignore @@ -1,17 +1,8 @@ -# Generated by Cargo -# will have compiled files and executables -/target/ - -# These are backup files generated by rustfmt +/target **/*.rs.bk - -# Other +#Cargo.lock .fuse_hidden* .DS_Store - -# IDE/Editor configuration files .vscode +.vs .idea - -# Output from capture example -wgpu/red.png diff --git a/gfx/wgpu/CHANGELOG.md b/gfx/wgpu/CHANGELOG.md index 6cf53a68bdf4..00fa30458ee8 100644 --- a/gfx/wgpu/CHANGELOG.md +++ b/gfx/wgpu/CHANGELOG.md @@ -1,56 +1,4 @@ # Change Log -## wgpu-core-0.9.2 - - fix `Features::TEXTURE_SPECIFIC_FORMAT_FEATURES` not being supported for rendertargets - -## wgpu-core-0.9.1 (2021-07-13) - - fix buffer inits delayed by a frame - - fix query resolves to initialize buffers - - fix pipeline statistics stride - - fix the check for maximum query count - -## v0.9 (2021-06-18) - - Updated: - - naga to `v0.5`. - - Added: - - `Features::VERTEX_WRITABLE_STORAGE`. - - `Features::CLEAR_COMMANDS` which allows you to use `cmd_buf.clear_texture` and `cmd_buf.clear_buffer`. - - Changed: - - Updated default storage buffer/image limit to `8` from `4`. - - Fixed: - - `Buffer::get_mapped_range` can now have a range of zero. - - Fixed output spirv requiring the "kernal" capability. - - Fixed segfault due to improper drop order. - - Fixed incorrect dynamic stencil reference for Replace ops. - - Fixed tracking of temporary resources. - - Stopped unconditionally adding cubemap flags when the backend doesn't support cubemaps. - - Validation: - - Ensure that if resources are viewed from the vertex stage, they are read only unless `Features::VERTEX_WRITABLE_STORAGE` is true. - - Ensure storage class (i.e. storage vs uniform) is consistent between the shader and the pipeline layout. - - Error when a color texture is used as a depth/stencil texture. - - Check that pipeline output formats are logical - - Added shader label to log messages if validation fails. - - Tracing: - - Make renderpasses show up in the trace before they are run. - - Docs: - - Fix typo in `PowerPreference::LowPower` description. - - Player: - - Automatically start and stop RenderDoc captures. - - Examples: - - Handle winit's unconditional exception. - - Internal: - - Merged wgpu-rs and wgpu back into a single repository. - - The tracker was split into two different stateful/stateless trackers to reduce overhead. - - Added code coverage testing - - CI can now test on lavapipe - - Add missing extern "C" in wgpu-core on `wgpu_render_pass_execute_bundles` - - Fix incorrect function name `wgpu_render_pass_bundle_indexed_indirect` to `wgpu_render_bundle_draw_indexed_indirect`. - -## wgpu-types-0.8.1 (2021-06-08) - - fix dynamic stencil reference for Replace ops - -## v0.8.1 (2021-05-06) - - fix SPIR-V generation from WGSL, which was broken due to "Kernel" capability - - validate buffer storage classes ## v0.8 (2021-04-29) - Naga is used by default to translate shaders, SPIRV-Cross is optional behind `cross` feature @@ -60,7 +8,7 @@ - conservative rasterization (native-only) - buffer resource indexing (native-only) - API adjustments to the spec: - - Renamed `RenderPassColorAttachmentDescriptor` to `RenderPassColorAttachment`: + - Renamed `RenderPassDepthStencilAttachmentDescriptor` to `RenderPassDepthStencilAttachment`: - Renamed the `attachment` member to `view` - Renamed `RenderPassDepthStencilAttachmentDescriptor` to `RenderPassDepthStencilAttachment`: - Renamed the `attachment` member to `view` @@ -91,7 +39,7 @@ - interpolation qualifiers - allow vertex components to be underspecified -## wgpu-core-0.7.1 (2021-02-25) +## v0.7.1 (2021-02-25) - expose `wgc::device::queue` sub-module in public - fix the indexed buffer check - fix command allocator race condition @@ -100,12 +48,9 @@ - Major API changes: - `RenderPipelineDescriptor` - `BindingType` - - new `ShaderModuleDescriptor` - - new `RenderEncoder` - Features: - (beta) WGSL support, including the ability to bypass SPIR-V entirely - (beta) implicit bind group layout support - - better error messages - timestamp and pipeline statistics queries - ETC2 and ASTC compressed textures - (beta) targeting WASM with WebGL backend @@ -120,9 +65,6 @@ - render pipeline descriptor - vertex buffers -### wgpu-0.6.2 (2020-11-24) - - don't panic in the staging belt if the channel is dropped - ## v0.6 (2020-08-17) - Crates: - C API is moved to [another repository](https://github.com/gfx-rs/wgpu-native) @@ -150,28 +92,28 @@ - bind group matching to the layout - experimental shader interface matching with Naga -## wgpu-core-0.5.6 (2020-07-09) +## v0.5.6 (2020-07-09) - add debug markers support -## wgpu-core-0.5.5 (2020-05-20) +## v0.5.5 (2020-05-20) - fix destruction of adapters, swap chains, and bind group layouts - fix command pool leak with temporary threads - improve assertion messages - implement `From` for `TextureComponentType` -## wgpu-core-0.5.4 (2020-04-24) +## v0.5.4 (2020-04-24) - fix memory management of staging buffers -## wgpu-core-0.5.3 (2020-04-18) +## v0.5.3 (2020-04-18) - fix reading access to storage textures - another fix to layout transitions for swapchain images -## wgpu-core-0.5.2 (2020-04-15) +## v0.5.2 (2020-04-15) - fix read-only storage flags - fix pipeline layout life time - improve various assert messages -## wgpu-core-0.5.1 (2020-04-10) +## v0.5.1 (2020-04-10) - fix tracking of swapchain images that are used multiple times in a command buffer - fix tracking of initial usage of a resource across a command buffer @@ -196,13 +138,13 @@ - unmapping dropped buffers - better error messages on misused swapchain frames -## wgpu-core-0.4.3 (2020-01-20) +## v0.4.3 (2020-01-20) - improved swap chain error handling -## wgpu-core-0.4.2 (2019-12-15) +## v0.4.2 (2019-12-15) - fixed render pass transitions -## wgpu-core-0.4.1 (2019-11-28) +## v0.4.1 (2019-11-28) - fixed depth/stencil transitions - fixed dynamic offset iteration @@ -216,10 +158,10 @@ - Validation: - buffer and texture usage -## wgpu-core-0.3.3 (2019-08-22) +## v0.3.3 (2019-08-22) - fixed instance creation on Windows -## wgpu-core-0.3.1 (2019-08-21) +## v0.3.1 (2019-08-21) - fixed pipeline barriers that aren't transitions ## v0.3 (2019-08-21) @@ -242,16 +184,16 @@ - bind group buffer ranges - required stencil reference, blend color -## wgpu-core-0.2.6 (2019-04-04) +## v0.2.6 (2019-04-04) - fixed frame acquisition GPU waits -## wgpu-core-0.2.5 (2019-03-31) +## v0.2.5 (2019-03-31) - fixed submission tracking - added support for blend colors - fixed bind group compatibility at the gfx-hal level - validating the bind groups and blend colors -## wgpu-core-0.2.3 (2019-03-20) +## v0.2.3 (2019-03-20) - fixed vertex format mapping - fixed building with "empty" backend on Windows - bumped the default descriptor pool size diff --git a/gfx/wgpu/Cargo.lock b/gfx/wgpu/Cargo.lock index 719fc7611f28..64d5d66f2243 100644 --- a/gfx/wgpu/Cargo.lock +++ b/gfx/wgpu/Cargo.lock @@ -10,15 +10,15 @@ checksum = "d9fe5e32de01730eb1f6b7f5b51c17e03e2325bf40a74f754f04f130043affff" [[package]] name = "ahash" -version = "0.4.7" +version = "0.4.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "739f4a8db6605981345c5654f3a85b056ce52f37a39d34da03f25bf2151ea16e" +checksum = "f6789e291be47ace86a60303502173d84af8327e3627ecf334356ee0f87a164c" [[package]] name = "aho-corasick" -version = "0.7.18" +version = "0.7.15" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1e37cfd5e7657ada45f742d6e99ca5788580b5c529dc78faf11ece6dc702656f" +checksum = "7404febffaa47dac81aa44dba71523c9d069b1bdc50a77db41195149e17f68e5" dependencies = [ "memchr", ] @@ -47,9 +47,9 @@ dependencies = [ [[package]] name = "ash" -version = "0.32.1" +version = "0.32.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "06063a002a77d2734631db74e8f4ce7148b77fe522e6bca46f2ae7774fd48112" +checksum = "77ea56be8250318e64923c7e65b82a35b5c29dfb6dc1c7d1c0b288c4b1bbb084" dependencies = [ "libloading 0.7.0", ] @@ -73,9 +73,9 @@ checksum = "cdb031dd78e28731d87d56cc8ffef4a8f36ca26c38fe2de700543e627f8a464a" [[package]] name = "base64" -version = "0.13.0" +version = "0.12.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "904dfeac50f3cdaba28fc6f57fdcddb75f49ed61346676a78c4ffe55877802fd" +checksum = "3441f0f7b02788e948e47f457ca01f1d7e6d92c693bc132c22b087d3141c03ff" [[package]] name = "bit-set" @@ -88,9 +88,9 @@ dependencies = [ [[package]] name = "bit-vec" -version = "0.6.3" +version = "0.6.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "349f9b6a179ed607305526ca489b34ad0a41aed5f7980fa90eb03160b69598fb" +checksum = "5f0dc55f2d8a1a85650ac47858bb001b4c0dd73d79e3c455a842925e68d29cd3" [[package]] name = "bitflags" @@ -106,15 +106,15 @@ checksum = "0d8c1fef690941d3e7788d328517591fecc684c084084702d6ff1641e993699a" [[package]] name = "bumpalo" -version = "3.7.0" +version = "3.4.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9c59e7af012c713f529e7a3ee57ce9b31ddd858d4b512923602f74608b009631" +checksum = "2e8c087f005730276d1096a652e92a8bacee2e2472bcc9715a74d2bec38b5820" [[package]] name = "byteorder" -version = "1.4.3" +version = "1.3.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "14c189c53d098945499cdfa7ecc63567cf3886b3332b312a5b4585d8d3a6a610" +checksum = "08c48aae112d48ed9f069b33538ea9e3e90aa263cfa3d1c24309612b1f7472de" [[package]] name = "calloop" @@ -123,14 +123,14 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "0b036167e76041694579972c28cf4877b4f92da222560ddb49008937b6a6727c" dependencies = [ "log", - "nix 0.18.0", + "nix", ] [[package]] name = "cc" -version = "1.0.68" +version = "1.0.62" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4a72c244c1ff497a746a7e1fb3d14bd08420ecda70c8f25c7112f2781652d787" +checksum = "f1770ced377336a88a67c473594ccc14eca6f4559217c34f64aac8f83d641b40" dependencies = [ "jobserver", ] @@ -153,6 +153,15 @@ version = "0.1.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "fd16c4719339c4530435d38e511904438d07cce7950afa3718a84ac36c10e89e" +[[package]] +name = "cloudabi" +version = "0.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4344512281c643ae7638bbabc3af17a11307803ec8f0fcad9fae512a8bf36467" +dependencies = [ + "bitflags", +] + [[package]] name = "cocoa" version = "0.24.0" @@ -330,9 +339,9 @@ dependencies = [ [[package]] name = "derivative" -version = "2.2.0" +version = "2.1.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "fcc3dd5e9e9c0b295d6e1e4d811fb6f157d5ffd784b8d202fc62eac8035a770b" +checksum = "cb582b60359da160a9477ee80f15c8d784c477e69c217ef2cdd4169c24ea380f" dependencies = [ "proc-macro2", "quote", @@ -351,16 +360,7 @@ version = "0.4.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "b11f15d1e3268f140f68d390637d5e76d849782d971ae7063e0da69fe9709a76" dependencies = [ - "libloading 0.6.7", -] - -[[package]] -name = "dlib" -version = "0.5.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ac1b7517328c04c2aa68422fc60a41b92208182142ed04a25879c26c8f878794" -dependencies = [ - "libloading 0.7.0", + "libloading 0.6.5", ] [[package]] @@ -369,15 +369,6 @@ version = "1.2.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "9ea835d29036a4087793836fa931b08837ad5e957da9e23886b29586fb9b6650" -[[package]] -name = "drm-fourcc" -version = "2.1.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ebbf3a5ed4671aabffefce172ff43d69c1f27dd2c6aea28e5212a70f32ada0cf" -dependencies = [ - "serde", -] - [[package]] name = "dummy" version = "0.1.0" @@ -387,9 +378,9 @@ dependencies = [ [[package]] name = "env_logger" -version = "0.8.4" +version = "0.8.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a19187fea3ac7e84da7dacf48de0c45d63c6a76f9490dae389aead16c243fce3" +checksum = "f26ecb66b4bdca6c1409b40fb255eefc2bd4f6d135dab3c3124f80ffa2a9661e" dependencies = [ "atty", "humantime", @@ -398,16 +389,6 @@ dependencies = [ "termcolor", ] -[[package]] -name = "external-memory" -version = "0.0.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e4dfe8d292b014422776a8c516862d2bff8a81b223a4461dfdc45f3862dc9d39" -dependencies = [ - "bitflags", - "drm-fourcc", -] - [[package]] name = "fixedbitset" version = "0.2.0" @@ -462,22 +443,21 @@ dependencies = [ [[package]] name = "generator" -version = "0.6.25" +version = "0.6.23" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "061d3be1afec479d56fa3bd182bf966c7999ec175fcfdb87ac14d417241366c6" +checksum = "8cdc09201b2e8ca1b19290cf7e65de2246b8e91fb6874279722189c4de7b94dc" dependencies = [ "cc", "libc", "log", - "rustversion", + "rustc_version", "winapi 0.3.9", ] [[package]] name = "gfx-auxil" -version = "0.10.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1694991b11d642680e82075a75c7c2bd75556b805efa7660b705689f05b1ab1c" +version = "0.9.0" +source = "git+https://github.com/gfx-rs/gfx?rev=27a1dae3796d33d23812f2bb8c7e3b5aea18b521#27a1dae3796d33d23812f2bb8c7e3b5aea18b521" dependencies = [ "fxhash", "gfx-hal", @@ -486,15 +466,13 @@ dependencies = [ [[package]] name = "gfx-backend-dx11" -version = "0.9.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8f9e453baf3aaef2b0c354ce0b3d63d76402e406a59b64b7182d123cfa6635ae" +version = "0.8.0" +source = "git+https://github.com/gfx-rs/gfx?rev=27a1dae3796d33d23812f2bb8c7e3b5aea18b521#27a1dae3796d33d23812f2bb8c7e3b5aea18b521" dependencies = [ "arrayvec", "bitflags", "gfx-auxil", "gfx-hal", - "gfx-renderdoc", "libloading 0.7.0", "log", "parking_lot", @@ -509,9 +487,8 @@ dependencies = [ [[package]] name = "gfx-backend-dx12" -version = "0.9.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "61f09e9d8c2aa69e9a21eb83c0f5d1a286c6d37da011f796e550d180b08090ce" +version = "0.8.0" +source = "git+https://github.com/gfx-rs/gfx?rev=27a1dae3796d33d23812f2bb8c7e3b5aea18b521#27a1dae3796d33d23812f2bb8c7e3b5aea18b521" dependencies = [ "arrayvec", "bit-set", @@ -519,7 +496,6 @@ dependencies = [ "d3d12", "gfx-auxil", "gfx-hal", - "gfx-renderdoc", "log", "parking_lot", "range-alloc", @@ -532,9 +508,8 @@ dependencies = [ [[package]] name = "gfx-backend-empty" -version = "0.9.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "29c8f813c47791918aa00dc9c9ddf961d23fa8c2a5d869e6cb8ea84f944820f4" +version = "0.8.0" +source = "git+https://github.com/gfx-rs/gfx?rev=27a1dae3796d33d23812f2bb8c7e3b5aea18b521#27a1dae3796d33d23812f2bb8c7e3b5aea18b521" dependencies = [ "gfx-hal", "log", @@ -543,16 +518,14 @@ dependencies = [ [[package]] name = "gfx-backend-metal" -version = "0.9.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "76c10e91bbe274122a9bf196ac16a57dd2db67cd7c4299eeb962503aebacc013" +version = "0.8.1" +source = "git+https://github.com/gfx-rs/gfx?rev=27a1dae3796d33d23812f2bb8c7e3b5aea18b521#27a1dae3796d33d23812f2bb8c7e3b5aea18b521" dependencies = [ "arrayvec", "bitflags", "block", "cocoa-foundation", "copyless", - "core-graphics-types", "foreign-types", "fxhash", "gfx-auxil", @@ -571,55 +544,42 @@ dependencies = [ [[package]] name = "gfx-backend-vulkan" -version = "0.9.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a9861ec855acbbc65c0e4f966d761224886e811dc2c6d413a4776e9293d0e5c0" +version = "0.8.0" +source = "git+https://github.com/gfx-rs/gfx?rev=27a1dae3796d33d23812f2bb8c7e3b5aea18b521#27a1dae3796d33d23812f2bb8c7e3b5aea18b521" dependencies = [ "arrayvec", "ash", "byteorder", "core-graphics-types", "gfx-hal", - "gfx-renderdoc", "inplace_it", + "libloading 0.7.0", "log", "naga", "objc", "parking_lot", "raw-window-handle", + "renderdoc-sys", "smallvec", "winapi 0.3.9", ] [[package]] name = "gfx-hal" -version = "0.9.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7fbb575ea793dd0507b3082f4f2cde62dc9f3cebd98f5cd49ba2a4da97a976fd" +version = "0.8.0" +source = "git+https://github.com/gfx-rs/gfx?rev=27a1dae3796d33d23812f2bb8c7e3b5aea18b521#27a1dae3796d33d23812f2bb8c7e3b5aea18b521" dependencies = [ "bitflags", - "external-memory", "naga", "raw-window-handle", "thiserror", ] -[[package]] -name = "gfx-renderdoc" -version = "0.1.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c8027995e247e2426d3a00d13f5191dd56c314bff02dc4b54cbf727f1ba9c40a" -dependencies = [ - "libloading 0.7.0", - "log", - "renderdoc-sys", -] - [[package]] name = "gpu-alloc" -version = "0.4.7" +version = "0.4.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "cbc1b6ca374e81862526786d9cb42357ce03706ed1b8761730caafd02ab91f3a" +checksum = "8159cab119e2c6947476a8b941d478c8de4a1ce050d92c55903f8d0192ccacda" dependencies = [ "bitflags", "gpu-alloc-types", @@ -665,9 +625,9 @@ dependencies = [ [[package]] name = "hermit-abi" -version = "0.1.18" +version = "0.1.17" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "322f4de77956e22ed0e5032c359a0f1273f1f7f0d79bfa3b8ffbc730d7fbcc5c" +checksum = "5aca5565f760fb5b220e499d72710ed156fdb74e631659e99377d9ebfbd13ae8" dependencies = [ "libc", ] @@ -686,9 +646,9 @@ checksum = "b9e0384b61958566e926dc50660321d12159025e767c18e043daf26b70104c39" [[package]] name = "indexmap" -version = "1.6.2" +version = "1.6.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "824845a0bf897a9042383849b02c1bc219c2383772efcd5c6f9766fa4b81aef3" +checksum = "55e2e4c765aa53a0424761bf9f41aa7a6ac1efa87238f59560640e27fca028f2" dependencies = [ "autocfg", "hashbrown", @@ -702,9 +662,9 @@ checksum = "90953f308a79fe6d62a4643e51f848fbfddcd05975a38e69fdf4ab86a7baf7ca" [[package]] name = "instant" -version = "0.1.9" +version = "0.1.8" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "61124eeebbd69b8190558df225adf7e4caafce0d743919e5d6b19652314ec5ec" +checksum = "cb1fc4429a33e1f80d41dc9fea4d108a88bec1de8053878898ae448a0b52f613" dependencies = [ "cfg-if 1.0.0", ] @@ -726,18 +686,18 @@ checksum = "8eaf4bc02d17cbdd7ff4c7438cafcdf7fb9a4613313ad11b4f8fefe7d3fa0130" [[package]] name = "jobserver" -version = "0.1.22" +version = "0.1.21" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "972f5ae5d1cb9c6ae417789196c803205313edde988685da5e3aae0827b9e7fd" +checksum = "5c71313ebb9439f74b00d9d2dcec36440beaf57a6aa0623068441dd7cd81a7f2" dependencies = [ "libc", ] [[package]] name = "js-sys" -version = "0.3.51" +version = "0.3.37" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "83bdfbace3a0e81a4253f73b49e960b053e396a11012cbd49b9b74d6a2b67062" +checksum = "6a27d435371a2fa5b6d2b028a74bbdb1234f308da363226a2854ca3ff8ba7055" dependencies = [ "wasm-bindgen", ] @@ -766,15 +726,15 @@ checksum = "830d08ce1d1d941e6b30645f1a0eb5643013d835ce3779a5fc208261dbe10f55" [[package]] name = "libc" -version = "0.2.97" +version = "0.2.80" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "12b8adadd720df158f4d70dfe7ccc6adb0472d7c55ca83445f6a5ab3e36f8fb6" +checksum = "4d58d1b70b004888f764dfbf6a26a3b0342a1632d33968e4a179d8011c760614" [[package]] name = "libloading" -version = "0.6.7" +version = "0.6.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "351a32417a12d5f7e82c368a66781e307834dae04c6ce0cd4456d52989229883" +checksum = "1090080fe06ec2648d0da3881d9453d97e71a45f00eb179af7fdd7e3f686fdb0" dependencies = [ "cfg-if 1.0.0", "winapi 0.3.9", @@ -792,20 +752,20 @@ dependencies = [ [[package]] name = "lock_api" -version = "0.4.4" +version = "0.4.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0382880606dff6d15c9476c416d18690b72742aa7b605bb6dd6ec9030fbf07eb" +checksum = "28247cc5a5be2f05fbcd76dd0cf2c7d3b5400cb978a28042abcd4fa0b3f8261c" dependencies = [ "scopeguard", ] [[package]] name = "log" -version = "0.4.14" +version = "0.4.11" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "51b9bbe6c47d51fc3e1a9b945965946b4c44142ab8792c50835a980d362c2710" +checksum = "4fabed175da42fed1fa0746b0ea71f412aa9d35e76e95e59b192c64b9dc2bf8b" dependencies = [ - "cfg-if 1.0.0", + "cfg-if 0.1.10", ] [[package]] @@ -836,9 +796,9 @@ checksum = "60302e4db3a61da70c0cb7991976248362f30319e88850c487b9b95bbf059e00" [[package]] name = "memchr" -version = "2.4.0" +version = "2.3.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b16bd47d9e329435e309c58469fe0791c2d0d1ba96ec0954152a5ae2b04387dc" +checksum = "0ee1c47aaa256ecabcaea351eae4a9b01ef39ed810004e298d2511ed284b1525" [[package]] name = "memmap2" @@ -851,13 +811,13 @@ dependencies = [ [[package]] name = "metal" -version = "0.23.0" +version = "0.22.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "79d7d769f1c104b8388294d6594d491d2e21240636f5f94d37f8a0f3d7904450" +checksum = "1c12e48c737ee9a55e8bb2352bcde588f79ae308d3529ee888f7cc0f469b5777" dependencies = [ "bitflags", "block", - "core-graphics-types", + "cocoa-foundation", "foreign-types", "log", "objc", @@ -865,9 +825,9 @@ dependencies = [ [[package]] name = "mio" -version = "0.6.23" +version = "0.6.22" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4afd66f5b91bf2a3bc13fad0e21caedac168ca4c707504e75585648ae80e4cc4" +checksum = "fce347092656428bc8eaf6201042cb551b8d67855af7374542a92a0fbfcac430" dependencies = [ "cfg-if 0.1.10", "fuchsia-zircon", @@ -896,9 +856,9 @@ dependencies = [ [[package]] name = "miow" -version = "0.2.2" +version = "0.2.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ebd808424166322d4a38da87083bfddd3ac4c131334ed55856112eb06d46944d" +checksum = "8c1f2f3b1cf331de6896aabf6e9d55dca90356cc9960cca7eaaf408a355ae919" dependencies = [ "kernel32-sys", "net2", @@ -908,9 +868,8 @@ dependencies = [ [[package]] name = "naga" -version = "0.5.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ef670817eef03d356d5a509ea275e7dd3a78ea9e24261ea3cb2dfed1abb08f64" +version = "0.4.0" +source = "git+https://github.com/gfx-rs/naga?tag=gfx-25#057d03ad86f18e3bb3866b20901d8d4e892dd3d6" dependencies = [ "bit-set", "bitflags", @@ -971,9 +930,9 @@ checksum = "c44922cb3dbb1c70b5e5f443d63b64363a898564d739ba5198e3a9138442868d" [[package]] name = "net2" -version = "0.2.37" +version = "0.2.35" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "391630d12b68002ae1e25e8f974306474966550ad82dac6886fb8910c19568ae" +checksum = "3ebc3ec692ed7c9a255596c67808dee269f64655d8baf7b4f0638e51ba1d6853" dependencies = [ "cfg-if 0.1.10", "libc", @@ -992,23 +951,11 @@ dependencies = [ "libc", ] -[[package]] -name = "nix" -version = "0.20.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "fa9b4819da1bc61c0ea48b63b7bc8604064dd43013e7cc325df098d49cd7c18a" -dependencies = [ - "bitflags", - "cc", - "cfg-if 1.0.0", - "libc", -] - [[package]] name = "nom" -version = "6.1.2" +version = "6.0.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e7413f999671bd4745a7b624bd370a569fb6bc574b23c83a3c5ed2e453f3d5e2" +checksum = "88034cfd6b4a0d54dd14f4a507eceee36c0b70e5a02236c4e4df571102be17f0" dependencies = [ "memchr", "version_check", @@ -1066,9 +1013,9 @@ dependencies = [ [[package]] name = "once_cell" -version = "1.8.0" +version = "1.5.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "692fcb63b64b1758029e0a96ee63e049ce8c5948587f2f7208df04625e5f6b56" +checksum = "13bd41f508810a131401606d54ac32a467c97172d74ba7662562ebba5ad07fa0" [[package]] name = "owned_ttf_parser" @@ -1081,9 +1028,9 @@ dependencies = [ [[package]] name = "parking_lot" -version = "0.11.1" +version = "0.11.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6d7744ac029df22dca6284efe4e898991d28e3085c706c972bcd7da4a27a15eb" +checksum = "a4893845fa2ca272e647da5d0e46660a314ead9c2fdd9a883aabc32e481a8733" dependencies = [ "instant", "lock_api", @@ -1092,11 +1039,12 @@ dependencies = [ [[package]] name = "parking_lot_core" -version = "0.8.3" +version = "0.8.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "fa7a782938e745763fe6907fc6ba86946d72f49fe7e21de074e08128a99fb018" +checksum = "c361aa727dd08437f2f1447be8b59a33b0edd15e0fcee698f935613d9efbca9b" dependencies = [ - "cfg-if 1.0.0", + "cfg-if 0.1.10", + "cloudabi", "instant", "libc", "redox_syscall", @@ -1151,24 +1099,24 @@ dependencies = [ [[package]] name = "proc-macro2" -version = "1.0.27" +version = "1.0.24" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f0d8caf72986c1a598726adc988bb5984792ef84f5ee5aa50209145ee8077038" +checksum = "1e0704ee1a7e00d7bb417d0770ea303c1bccbabf0ef1667dae92b5967f5f8a71" dependencies = [ "unicode-xid", ] [[package]] name = "profiling" -version = "1.0.2" +version = "1.0.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0a7c000c0ce9d9bb94c0fbacdf20e5087fbe652c556ffb2c9387d980e17d51fb" +checksum = "8ae26a2370f2cf9a28cd570017aee216af883f02230ef8aea27b77f90e5654ef" [[package]] name = "quote" -version = "1.0.9" +version = "1.0.7" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c3d0b9745dc2debf507c8422de05d7226cc1f0644216dfdfead988f9b1ab32a7" +checksum = "aa563d17ecb180e500da1cfd2b028310ac758de548efdd203e18f283af693f37" dependencies = [ "proc-macro2", ] @@ -1176,8 +1124,7 @@ dependencies = [ [[package]] name = "range-alloc" version = "0.1.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "63e935c45e09cc6dcf00d2f0b2d630a58f4095320223d47fc68918722f0538b6" +source = "git+https://github.com/gfx-rs/gfx?rev=27a1dae3796d33d23812f2bb8c7e3b5aea18b521#27a1dae3796d33d23812f2bb8c7e3b5aea18b521" [[package]] name = "raw-window-handle" @@ -1190,29 +1137,27 @@ dependencies = [ [[package]] name = "redox_syscall" -version = "0.2.9" +version = "0.1.57" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5ab49abadf3f9e1c4bc499e8845e152ad87d2ad2d30371841171169e9d75feee" -dependencies = [ - "bitflags", -] +checksum = "41cc0f7e4d5d4544e8861606a285bb08d3e70712ccc7d2b84d7c0ccfaf4b05ce" [[package]] name = "regex" -version = "1.5.4" +version = "1.4.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d07a8629359eb56f1e2fb1652bb04212c072a87ba68546a04065d525673ac461" +checksum = "38cf2c13ed4745de91a5eb834e11c00bcc3709e773173b2ce4c56c9fbde04b9c" dependencies = [ "aho-corasick", "memchr", "regex-syntax", + "thread_local", ] [[package]] name = "regex-syntax" -version = "0.6.25" +version = "0.6.21" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f497285884f3fcff424ffc933e56d7cbca511def0c9831a7f9b5f6153e3cc89b" +checksum = "3b181ba2dcf07aaccad5448e8ead58db5b742cf85dfe035e2227f137a539a189" [[package]] name = "renderdoc-sys" @@ -1222,9 +1167,9 @@ checksum = "f1382d1f0a252c4bf97dc20d979a2fdd05b024acd7c2ed0f7595d7817666a157" [[package]] name = "ron" -version = "0.6.4" +version = "0.6.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "064ea8613fb712a19faf920022ec8ddf134984f100090764a4e1d768f3827f1f" +checksum = "f8a58080b7bb83b2ea28c3b7a9a994fd5e310330b7c8ca5258d99b98128ecfe4" dependencies = [ "base64", "bitflags", @@ -1240,6 +1185,15 @@ dependencies = [ "petgraph", ] +[[package]] +name = "rustc_version" +version = "0.2.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "138e3e0acb6c9fb258b19b67cb8abd63c00679d2851805ea151465464fe9030a" +dependencies = [ + "semver", +] + [[package]] name = "rusttype" version = "0.9.2" @@ -1250,12 +1204,6 @@ dependencies = [ "owned_ttf_parser", ] -[[package]] -name = "rustversion" -version = "1.0.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "61b3909d758bb75c79f23d4736fac9433868679d3ad2ea7a61e3c25cfda9a088" - [[package]] name = "same-file" version = "1.0.6" @@ -1278,19 +1226,34 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "d29ab0c6d3fc0ee92fe66e2d99f700eab17a8d57d1c1d3b748380fb20baa78cd" [[package]] -name = "serde" -version = "1.0.126" +name = "semver" +version = "0.9.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ec7505abeacaec74ae4778d9d9328fe5a5d04253220a85c4ee022239fc996d03" +checksum = "1d7eb9ef2c18661902cc47e535f9bc51b78acd254da71d375c2f6720d9a40403" +dependencies = [ + "semver-parser", +] + +[[package]] +name = "semver-parser" +version = "0.7.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "388a1df253eca08550bef6c72392cfe7c30914bf41df5269b68cbd6ff8f570a3" + +[[package]] +name = "serde" +version = "1.0.117" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b88fa983de7720629c9387e9f517353ed404164b1e482c970a90c1a4aaf7dc1a" dependencies = [ "serde_derive", ] [[package]] name = "serde_derive" -version = "1.0.126" +version = "1.0.117" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "963a7dbc9895aeac7ac90e74f34a5d5261828f79df35cbed41e10189d3804d43" +checksum = "cbd1ae72adb44aab48f325a02444a5fc079349a8d804c1fc922aed3f7454c74e" dependencies = [ "proc-macro2", "quote", @@ -1299,30 +1262,30 @@ dependencies = [ [[package]] name = "slab" -version = "0.4.3" +version = "0.4.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f173ac3d1a7e3b28003f40de0b5ce7fe2710f9b9dc3fc38664cebee46b3b6527" +checksum = "c111b5bd5695e56cffe5129854aa230b39c93a305372fdbb2668ca2394eea9f8" [[package]] name = "smallvec" -version = "1.6.1" +version = "1.4.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "fe0f37c9e8f3c5a4a66ad655a93c74daac4ad00c441533bf5c6e7990bb42604e" +checksum = "fbee7696b84bbf3d89a1c2eccff0850e3047ed46bfcd2e92c29a2d074d57e252" [[package]] name = "smithay-client-toolkit" -version = "0.12.3" +version = "0.12.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4750c76fd5d3ac95fa3ed80fe667d6a3d8590a960e5b575b98eea93339a80b80" +checksum = "316e13a3eb853ce7bf72ad3530dc186cb2005c57c521ef5f4ada5ee4eed74de6" dependencies = [ "andrew", "bitflags", "calloop", - "dlib 0.4.2", + "dlib", "lazy_static", "log", "memmap2", - "nix 0.18.0", + "nix", "wayland-client", "wayland-cursor", "wayland-protocols", @@ -1330,9 +1293,9 @@ dependencies = [ [[package]] name = "spirv_cross" -version = "0.23.1" +version = "0.23.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "60647fadbf83c4a72f0d7ea67a7ca3a81835cf442b8deae5c134c3e0055b2e14" +checksum = "06db6bd7b6518f761593783e2896eefe55e90455efc5f44511078ce0426ed418" dependencies = [ "cc", "js-sys", @@ -1366,9 +1329,9 @@ checksum = "6446ced80d6c486436db5c078dde11a9f73d42b57fb273121e160b84f63d894c" [[package]] name = "syn" -version = "1.0.73" +version = "1.0.48" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f71489ff30030d2ae598524f61326b902466f72a0fb1a8564c001cc63425bcc7" +checksum = "cc371affeffc477f42a221a1e4297aedcea33d47d19b61455588bd9d8f6b19ac" dependencies = [ "proc-macro2", "quote", @@ -1377,33 +1340,42 @@ dependencies = [ [[package]] name = "termcolor" -version = "1.1.2" +version = "1.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2dfed899f0eb03f32ee8c6a0aabdb8a7949659e3466561fc0adf54e26d88c5f4" +checksum = "bb6bfa289a4d7c5766392812c0a1f4c1ba45afa1ad47803c11e1f407d846d75f" dependencies = [ "winapi-util", ] [[package]] name = "thiserror" -version = "1.0.25" +version = "1.0.22" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "fa6f76457f59514c7eeb4e59d891395fab0b2fd1d40723ae737d64153392e9c6" +checksum = "0e9ae34b84616eedaaf1e9dd6026dbe00dcafa92aa0c8077cb69df1fcfe5e53e" dependencies = [ "thiserror-impl", ] [[package]] name = "thiserror-impl" -version = "1.0.25" +version = "1.0.22" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8a36768c0fbf1bb15eca10defa29526bda730a2376c2ab4393ccfa16fb1a318d" +checksum = "9ba20f23e85b10754cd195504aebf6a27e2e6cbe28c17778a0c930724628dd56" dependencies = [ "proc-macro2", "quote", "syn", ] +[[package]] +name = "thread_local" +version = "1.0.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d40c6d1b69745a6ec6fb1ca717914848da4b44ae29d9b3080cbee91d72a69b14" +dependencies = [ + "lazy_static", +] + [[package]] name = "thunderdome" version = "0.4.1" @@ -1412,9 +1384,9 @@ checksum = "87b4947742c93ece24a0032141d9caa3d853752e694a57e35029dd2bd08673e0" [[package]] name = "toml" -version = "0.5.8" +version = "0.5.7" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a31142970826733df8241ef35dc040ef98c679ab14d7c3e54d827099b3acecaa" +checksum = "75cf45bb0bef80604d001caaec0d09da99611b3c0fd39d3080468875cdb65645" dependencies = [ "serde", ] @@ -1433,21 +1405,21 @@ checksum = "9337591893a19b88d8d87f2cec1e73fad5cdfd10e5a6f349f498ad6ea2ffb1e3" [[package]] name = "unicode-xid" -version = "0.2.2" +version = "0.2.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8ccb82d61f80a663efe1f787a51b16b5a51e3314d6ac365b08639f52387b33f3" +checksum = "f7fe0bb3479651439c9112f72b6c505038574c9fbb575ed1bf3b797fa39dd564" [[package]] name = "version_check" -version = "0.9.3" +version = "0.9.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5fecdca9a5291cc2b8dcf7dc02453fee791a280f3743cb0905f8822ae463b3fe" +checksum = "b5a972e5669d67ba988ce3dc826706fb0a8b01471c088cb0b6110b805cc36aed" [[package]] name = "walkdir" -version = "2.3.2" +version = "2.3.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "808cf2735cd4b6866113f648b791c6adc5714537bc222d9347bb203386ffda56" +checksum = "777182bc735b6424e1a57516d35ed72cb8019d85c8c9bf536dccb3445c1a2f7d" dependencies = [ "same-file", "winapi 0.3.9", @@ -1456,19 +1428,19 @@ dependencies = [ [[package]] name = "wasm-bindgen" -version = "0.2.74" +version = "0.2.60" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d54ee1d4ed486f78874278e63e4069fc1ab9f6a18ca492076ffb90c5eb2997fd" +checksum = "2cc57ce05287f8376e998cbddfb4c8cb43b84a7ec55cf4551d7c00eef317a47f" dependencies = [ - "cfg-if 1.0.0", + "cfg-if 0.1.10", "wasm-bindgen-macro", ] [[package]] name = "wasm-bindgen-backend" -version = "0.2.74" +version = "0.2.60" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3b33f6a0694ccfea53d94db8b2ed1c3a8a4c86dd936b13b9f0a15ec4a451b900" +checksum = "d967d37bf6c16cca2973ca3af071d0a2523392e4a594548155d89a678f4237cd" dependencies = [ "bumpalo", "lazy_static", @@ -1481,9 +1453,9 @@ dependencies = [ [[package]] name = "wasm-bindgen-macro" -version = "0.2.74" +version = "0.2.60" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "088169ca61430fe1e58b8096c24975251700e7b1f6fd91cc9d59b04fb9b18bd4" +checksum = "8bd151b63e1ea881bb742cd20e1d6127cef28399558f3b5d415289bc41eee3a4" dependencies = [ "quote", "wasm-bindgen-macro-support", @@ -1491,9 +1463,9 @@ dependencies = [ [[package]] name = "wasm-bindgen-macro-support" -version = "0.2.74" +version = "0.2.60" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "be2241542ff3d9f241f5e2cb6dd09b37efe786df8851c54957683a49f0987a97" +checksum = "d68a5b36eef1be7868f668632863292e37739656a80fc4b9acec7b0bd35a4931" dependencies = [ "proc-macro2", "quote", @@ -1504,20 +1476,20 @@ dependencies = [ [[package]] name = "wasm-bindgen-shared" -version = "0.2.74" +version = "0.2.60" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d7cff876b8f18eed75a66cf49b65e7f967cb354a7aa16003fb55dbfd25b44b4f" +checksum = "daf76fe7d25ac79748a37538b7daeed1c7a6867c92d3245c12c6222e4a20d639" [[package]] name = "wayland-client" -version = "0.28.5" +version = "0.28.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "06ca44d86554b85cf449f1557edc6cc7da935cc748c8e4bf1c507cbd43bae02c" +checksum = "bdbdbe01d03b2267809f3ed99495b37395387fde789e0f2ebb78e8b43f75b6d7" dependencies = [ "bitflags", "downcast-rs", "libc", - "nix 0.20.0", + "nix", "scoped-tls", "wayland-commons", "wayland-scanner", @@ -1526,11 +1498,11 @@ dependencies = [ [[package]] name = "wayland-commons" -version = "0.28.5" +version = "0.28.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8bd75ae380325dbcff2707f0cd9869827ea1d2d6d534cff076858d3f0460fd5a" +checksum = "480450f76717edd64ad04a4426280d737fc3d10a236b982df7b1aee19f0e2d56" dependencies = [ - "nix 0.20.0", + "nix", "once_cell", "smallvec", "wayland-sys", @@ -1538,20 +1510,20 @@ dependencies = [ [[package]] name = "wayland-cursor" -version = "0.28.5" +version = "0.28.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b37e5455ec72f5de555ec39b5c3704036ac07c2ecd50d0bffe02d5fe2d4e65ab" +checksum = "d6eb122c160223a7660feeaf949d0100281d1279acaaed3720eb3c9894496e5f" dependencies = [ - "nix 0.20.0", + "nix", "wayland-client", "xcursor", ] [[package]] name = "wayland-protocols" -version = "0.28.5" +version = "0.28.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "95df3317872bcf9eec096c864b69aa4769a1d5d6291a5b513f8ba0af0efbd52c" +checksum = "319a82b4d3054dd25acc32d9aee0f84fa95b63bc983fffe4703b6b8d47e01a30" dependencies = [ "bitflags", "wayland-client", @@ -1561,9 +1533,9 @@ dependencies = [ [[package]] name = "wayland-scanner" -version = "0.28.5" +version = "0.28.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "389d680d7bd67512dc9c37f39560224327038deb0f0e8d33f870900441b68720" +checksum = "7010ba5767b3fcd350decc59055390b4ebe6bd1b9279a9feb1f1888987f1133d" dependencies = [ "proc-macro2", "quote", @@ -1572,18 +1544,18 @@ dependencies = [ [[package]] name = "wayland-sys" -version = "0.28.5" +version = "0.28.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2907bd297eef464a95ba9349ea771611771aa285b932526c633dc94d5400a8e2" +checksum = "6793834e0c35d11fd96a97297abe03d37be627e1847da52e17d7e0e3b51cc099" dependencies = [ - "dlib 0.5.0", + "dlib", "lazy_static", "pkg-config", ] [[package]] name = "wgpu-core" -version = "0.9.2" +version = "0.8.0" dependencies = [ "arrayvec", "bitflags", @@ -1613,7 +1585,7 @@ dependencies = [ [[package]] name = "wgpu-types" -version = "0.9.0" +version = "0.8.0" dependencies = [ "bitflags", "serde", diff --git a/gfx/wgpu/Cargo.toml b/gfx/wgpu/Cargo.toml index 0f3c6c82c657..d329d430a84d 100644 --- a/gfx/wgpu/Cargo.toml +++ b/gfx/wgpu/Cargo.toml @@ -2,31 +2,18 @@ members = [ "dummy", "player", - #"wgpu", "wgpu-core", "wgpu-types", ] -default-members = ["player"] + +[patch."https://github.com/gfx-rs/gfx"] +#hal = { package = "gfx-hal", path = "../gfx/src/hal" } +#gfx-backend-vulkan = { path = "../gfx/src/backend/vulkan", features = ["naga"] } +#gfx-backend-metal = { path = "../gfx/src/backend/metal", features = ["naga"] } +#gfx-backend-gl = { path = "../gfx/src/backend/gl", features = ["naga"] } +#gfx-backend-dx12 = { path = "../gfx/src/backend/dx12" } +#gfx-backend-dx11 = { path = "../gfx/src/backend/dx11" } +#gfx-backend-empty = { path = "../gfx/src/backend/empty" } [patch."https://github.com/gfx-rs/naga"] #naga = { path = "../naga" } - -[patch."https://github.com/zakarumych/gpu-descriptor"] -#gpu-descriptor = { path = "../gpu-descriptor/gpu-descriptor" } - -[patch."https://github.com/zakarumych/gpu-alloc"] -#gpu-alloc = { path = "../gpu-alloc/gpu-alloc" } - -[patch."https://github.com/gfx-rs/gfx"] -#gfx-hal = { path = "../gfx/src/hal" } -#gfx-backend-empty = { path = "../gfx/src/backend/empty" } -#gfx-backend-vulkan = { path = "../gfx/src/backend/vulkan" } -#gfx-backend-gl = { path = "../gfx/src/backend/gl" } -#gfx-backend-dx12 = { path = "../gfx/src/backend/dx12" } -#gfx-backend-dx11 = { path = "../gfx/src/backend/dx11" } -#gfx-backend-metal = { path = "../gfx/src/backend/metal" } - -[patch.crates-io] -#web-sys = { path = "../wasm-bindgen/crates/web-sys" } -#js-sys = { path = "../wasm-bindgen/crates/js-sys" } -#wasm-bindgen = { path = "../wasm-bindgen" } diff --git a/gfx/wgpu/LICENSE b/gfx/wgpu/LICENSE new file mode 100644 index 000000000000..a612ad9813b0 --- /dev/null +++ b/gfx/wgpu/LICENSE @@ -0,0 +1,373 @@ +Mozilla Public License Version 2.0 +================================== + +1. Definitions +-------------- + +1.1. "Contributor" + means each individual or legal entity that creates, contributes to + the creation of, or owns Covered Software. + +1.2. "Contributor Version" + means the combination of the Contributions of others (if any) used + by a Contributor and that particular Contributor's Contribution. + +1.3. "Contribution" + means Covered Software of a particular Contributor. + +1.4. "Covered Software" + means Source Code Form to which the initial Contributor has attached + the notice in Exhibit A, the Executable Form of such Source Code + Form, and Modifications of such Source Code Form, in each case + including portions thereof. + +1.5. "Incompatible With Secondary Licenses" + means + + (a) that the initial Contributor has attached the notice described + in Exhibit B to the Covered Software; or + + (b) that the Covered Software was made available under the terms of + version 1.1 or earlier of the License, but not also under the + terms of a Secondary License. + +1.6. "Executable Form" + means any form of the work other than Source Code Form. + +1.7. "Larger Work" + means a work that combines Covered Software with other material, in + a separate file or files, that is not Covered Software. + +1.8. "License" + means this document. + +1.9. "Licensable" + means having the right to grant, to the maximum extent possible, + whether at the time of the initial grant or subsequently, any and + all of the rights conveyed by this License. + +1.10. "Modifications" + means any of the following: + + (a) any file in Source Code Form that results from an addition to, + deletion from, or modification of the contents of Covered + Software; or + + (b) any new file in Source Code Form that contains any Covered + Software. + +1.11. "Patent Claims" of a Contributor + means any patent claim(s), including without limitation, method, + process, and apparatus claims, in any patent Licensable by such + Contributor that would be infringed, but for the grant of the + License, by the making, using, selling, offering for sale, having + made, import, or transfer of either its Contributions or its + Contributor Version. + +1.12. "Secondary License" + means either the GNU General Public License, Version 2.0, the GNU + Lesser General Public License, Version 2.1, the GNU Affero General + Public License, Version 3.0, or any later versions of those + licenses. + +1.13. "Source Code Form" + means the form of the work preferred for making modifications. + +1.14. "You" (or "Your") + means an individual or a legal entity exercising rights under this + License. For legal entities, "You" includes any entity that + controls, is controlled by, or is under common control with You. For + purposes of this definition, "control" means (a) the power, direct + or indirect, to cause the direction or management of such entity, + whether by contract or otherwise, or (b) ownership of more than + fifty percent (50%) of the outstanding shares or beneficial + ownership of such entity. + +2. License Grants and Conditions +-------------------------------- + +2.1. Grants + +Each Contributor hereby grants You a world-wide, royalty-free, +non-exclusive license: + +(a) under intellectual property rights (other than patent or trademark) + Licensable by such Contributor to use, reproduce, make available, + modify, display, perform, distribute, and otherwise exploit its + Contributions, either on an unmodified basis, with Modifications, or + as part of a Larger Work; and + +(b) under Patent Claims of such Contributor to make, use, sell, offer + for sale, have made, import, and otherwise transfer either its + Contributions or its Contributor Version. + +2.2. Effective Date + +The licenses granted in Section 2.1 with respect to any Contribution +become effective for each Contribution on the date the Contributor first +distributes such Contribution. + +2.3. Limitations on Grant Scope + +The licenses granted in this Section 2 are the only rights granted under +this License. No additional rights or licenses will be implied from the +distribution or licensing of Covered Software under this License. +Notwithstanding Section 2.1(b) above, no patent license is granted by a +Contributor: + +(a) for any code that a Contributor has removed from Covered Software; + or + +(b) for infringements caused by: (i) Your and any other third party's + modifications of Covered Software, or (ii) the combination of its + Contributions with other software (except as part of its Contributor + Version); or + +(c) under Patent Claims infringed by Covered Software in the absence of + its Contributions. + +This License does not grant any rights in the trademarks, service marks, +or logos of any Contributor (except as may be necessary to comply with +the notice requirements in Section 3.4). + +2.4. Subsequent Licenses + +No Contributor makes additional grants as a result of Your choice to +distribute the Covered Software under a subsequent version of this +License (see Section 10.2) or under the terms of a Secondary License (if +permitted under the terms of Section 3.3). + +2.5. Representation + +Each Contributor represents that the Contributor believes its +Contributions are its original creation(s) or it has sufficient rights +to grant the rights to its Contributions conveyed by this License. + +2.6. Fair Use + +This License is not intended to limit any rights You have under +applicable copyright doctrines of fair use, fair dealing, or other +equivalents. + +2.7. Conditions + +Sections 3.1, 3.2, 3.3, and 3.4 are conditions of the licenses granted +in Section 2.1. + +3. Responsibilities +------------------- + +3.1. Distribution of Source Form + +All distribution of Covered Software in Source Code Form, including any +Modifications that You create or to which You contribute, must be under +the terms of this License. You must inform recipients that the Source +Code Form of the Covered Software is governed by the terms of this +License, and how they can obtain a copy of this License. You may not +attempt to alter or restrict the recipients' rights in the Source Code +Form. + +3.2. Distribution of Executable Form + +If You distribute Covered Software in Executable Form then: + +(a) such Covered Software must also be made available in Source Code + Form, as described in Section 3.1, and You must inform recipients of + the Executable Form how they can obtain a copy of such Source Code + Form by reasonable means in a timely manner, at a charge no more + than the cost of distribution to the recipient; and + +(b) You may distribute such Executable Form under the terms of this + License, or sublicense it under different terms, provided that the + license for the Executable Form does not attempt to limit or alter + the recipients' rights in the Source Code Form under this License. + +3.3. Distribution of a Larger Work + +You may create and distribute a Larger Work under terms of Your choice, +provided that You also comply with the requirements of this License for +the Covered Software. If the Larger Work is a combination of Covered +Software with a work governed by one or more Secondary Licenses, and the +Covered Software is not Incompatible With Secondary Licenses, this +License permits You to additionally distribute such Covered Software +under the terms of such Secondary License(s), so that the recipient of +the Larger Work may, at their option, further distribute the Covered +Software under the terms of either this License or such Secondary +License(s). + +3.4. Notices + +You may not remove or alter the substance of any license notices +(including copyright notices, patent notices, disclaimers of warranty, +or limitations of liability) contained within the Source Code Form of +the Covered Software, except that You may alter any license notices to +the extent required to remedy known factual inaccuracies. + +3.5. Application of Additional Terms + +You may choose to offer, and to charge a fee for, warranty, support, +indemnity or liability obligations to one or more recipients of Covered +Software. However, You may do so only on Your own behalf, and not on +behalf of any Contributor. You must make it absolutely clear that any +such warranty, support, indemnity, or liability obligation is offered by +You alone, and You hereby agree to indemnify every Contributor for any +liability incurred by such Contributor as a result of warranty, support, +indemnity or liability terms You offer. You may include additional +disclaimers of warranty and limitations of liability specific to any +jurisdiction. + +4. Inability to Comply Due to Statute or Regulation +--------------------------------------------------- + +If it is impossible for You to comply with any of the terms of this +License with respect to some or all of the Covered Software due to +statute, judicial order, or regulation then You must: (a) comply with +the terms of this License to the maximum extent possible; and (b) +describe the limitations and the code they affect. Such description must +be placed in a text file included with all distributions of the Covered +Software under this License. Except to the extent prohibited by statute +or regulation, such description must be sufficiently detailed for a +recipient of ordinary skill to be able to understand it. + +5. Termination +-------------- + +5.1. The rights granted under this License will terminate automatically +if You fail to comply with any of its terms. However, if You become +compliant, then the rights granted under this License from a particular +Contributor are reinstated (a) provisionally, unless and until such +Contributor explicitly and finally terminates Your grants, and (b) on an +ongoing basis, if such Contributor fails to notify You of the +non-compliance by some reasonable means prior to 60 days after You have +come back into compliance. Moreover, Your grants from a particular +Contributor are reinstated on an ongoing basis if such Contributor +notifies You of the non-compliance by some reasonable means, this is the +first time You have received notice of non-compliance with this License +from such Contributor, and You become compliant prior to 30 days after +Your receipt of the notice. + +5.2. If You initiate litigation against any entity by asserting a patent +infringement claim (excluding declaratory judgment actions, +counter-claims, and cross-claims) alleging that a Contributor Version +directly or indirectly infringes any patent, then the rights granted to +You by any and all Contributors for the Covered Software under Section +2.1 of this License shall terminate. + +5.3. In the event of termination under Sections 5.1 or 5.2 above, all +end user license agreements (excluding distributors and resellers) which +have been validly granted by You or Your distributors under this License +prior to termination shall survive termination. + +************************************************************************ +* * +* 6. Disclaimer of Warranty * +* ------------------------- * +* * +* Covered Software is provided under this License on an "as is" * +* basis, without warranty of any kind, either expressed, implied, or * +* statutory, including, without limitation, warranties that the * +* Covered Software is free of defects, merchantable, fit for a * +* particular purpose or non-infringing. The entire risk as to the * +* quality and performance of the Covered Software is with You. * +* Should any Covered Software prove defective in any respect, You * +* (not any Contributor) assume the cost of any necessary servicing, * +* repair, or correction. This disclaimer of warranty constitutes an * +* essential part of this License. No use of any Covered Software is * +* authorized under this License except under this disclaimer. * +* * +************************************************************************ + +************************************************************************ +* * +* 7. Limitation of Liability * +* -------------------------- * +* * +* Under no circumstances and under no legal theory, whether tort * +* (including negligence), contract, or otherwise, shall any * +* Contributor, or anyone who distributes Covered Software as * +* permitted above, be liable to You for any direct, indirect, * +* special, incidental, or consequential damages of any character * +* including, without limitation, damages for lost profits, loss of * +* goodwill, work stoppage, computer failure or malfunction, or any * +* and all other commercial damages or losses, even if such party * +* shall have been informed of the possibility of such damages. This * +* limitation of liability shall not apply to liability for death or * +* personal injury resulting from such party's negligence to the * +* extent applicable law prohibits such limitation. Some * +* jurisdictions do not allow the exclusion or limitation of * +* incidental or consequential damages, so this exclusion and * +* limitation may not apply to You. * +* * +************************************************************************ + +8. Litigation +------------- + +Any litigation relating to this License may be brought only in the +courts of a jurisdiction where the defendant maintains its principal +place of business and such litigation shall be governed by laws of that +jurisdiction, without reference to its conflict-of-law provisions. +Nothing in this Section shall prevent a party's ability to bring +cross-claims or counter-claims. + +9. Miscellaneous +---------------- + +This License represents the complete agreement concerning the subject +matter hereof. If any provision of this License is held to be +unenforceable, such provision shall be reformed only to the extent +necessary to make it enforceable. Any law or regulation which provides +that the language of a contract shall be construed against the drafter +shall not be used to construe this License against a Contributor. + +10. Versions of the License +--------------------------- + +10.1. New Versions + +Mozilla Foundation is the license steward. Except as provided in Section +10.3, no one other than the license steward has the right to modify or +publish new versions of this License. Each version will be given a +distinguishing version number. + +10.2. Effect of New Versions + +You may distribute the Covered Software under the terms of the version +of the License under which You originally received the Covered Software, +or under the terms of any subsequent version published by the license +steward. + +10.3. Modified Versions + +If you create software not governed by this License, and you want to +create a new license for such software, you may create and use a +modified version of this License if you rename the license and remove +any references to the name of the license steward (except to note that +such modified license differs from this License). + +10.4. Distributing Source Code Form that is Incompatible With Secondary +Licenses + +If You choose to distribute Source Code Form that is Incompatible With +Secondary Licenses under the terms of this version of the License, the +notice described in Exhibit B of this License must be attached. + +Exhibit A - Source Code Form License Notice +------------------------------------------- + + This Source Code Form is subject to the terms of the Mozilla Public + License, v. 2.0. If a copy of the MPL was not distributed with this + file, You can obtain one at http://mozilla.org/MPL/2.0/. + +If it is not possible or desirable to put the notice in a particular +file, then You may include the notice in a location (such as a LICENSE +file in a relevant directory) where a recipient would be likely to look +for such a notice. + +You may add additional accurate notices of copyright ownership. + +Exhibit B - "Incompatible With Secondary Licenses" Notice +--------------------------------------------------------- + + This Source Code Form is "Incompatible With Secondary Licenses", as + defined by the Mozilla Public License, v. 2.0. diff --git a/gfx/wgpu/LICENSE.APACHE b/gfx/wgpu/LICENSE.APACHE deleted file mode 100644 index d9a10c0d8e86..000000000000 --- a/gfx/wgpu/LICENSE.APACHE +++ /dev/null @@ -1,176 +0,0 @@ - Apache License - Version 2.0, January 2004 - http://www.apache.org/licenses/ - - TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION - - 1. Definitions. - - "License" shall mean the terms and conditions for use, reproduction, - and distribution as defined by Sections 1 through 9 of this document. - - "Licensor" shall mean the copyright owner or entity authorized by - the copyright owner that is granting the License. - - "Legal Entity" shall mean the union of the acting entity and all - other entities that control, are controlled by, or are under common - control with that entity. For the purposes of this definition, - "control" means (i) the power, direct or indirect, to cause the - direction or management of such entity, whether by contract or - otherwise, or (ii) ownership of fifty percent (50%) or more of the - outstanding shares, or (iii) beneficial ownership of such entity. - - "You" (or "Your") shall mean an individual or Legal Entity - exercising permissions granted by this License. - - "Source" form shall mean the preferred form for making modifications, - including but not limited to software source code, documentation - source, and configuration files. - - "Object" form shall mean any form resulting from mechanical - transformation or translation of a Source form, including but - not limited to compiled object code, generated documentation, - and conversions to other media types. - - "Work" shall mean the work of authorship, whether in Source or - Object form, made available under the License, as indicated by a - copyright notice that is included in or attached to the work - (an example is provided in the Appendix below). - - "Derivative Works" shall mean any work, whether in Source or Object - form, that is based on (or derived from) the Work and for which the - editorial revisions, annotations, elaborations, or other modifications - represent, as a whole, an original work of authorship. For the purposes - of this License, Derivative Works shall not include works that remain - separable from, or merely link (or bind by name) to the interfaces of, - the Work and Derivative Works thereof. - - "Contribution" shall mean any work of authorship, including - the original version of the Work and any modifications or additions - to that Work or Derivative Works thereof, that is intentionally - submitted to Licensor for inclusion in the Work by the copyright owner - or by an individual or Legal Entity authorized to submit on behalf of - the copyright owner. For the purposes of this definition, "submitted" - means any form of electronic, verbal, or written communication sent - to the Licensor or its representatives, including but not limited to - communication on electronic mailing lists, source code control systems, - and issue tracking systems that are managed by, or on behalf of, the - Licensor for the purpose of discussing and improving the Work, but - excluding communication that is conspicuously marked or otherwise - designated in writing by the copyright owner as "Not a Contribution." - - "Contributor" shall mean Licensor and any individual or Legal Entity - on behalf of whom a Contribution has been received by Licensor and - subsequently incorporated within the Work. - - 2. Grant of Copyright License. Subject to the terms and conditions of - this License, each Contributor hereby grants to You a perpetual, - worldwide, non-exclusive, no-charge, royalty-free, irrevocable - copyright license to reproduce, prepare Derivative Works of, - publicly display, publicly perform, sublicense, and distribute the - Work and such Derivative Works in Source or Object form. - - 3. Grant of Patent License. Subject to the terms and conditions of - this License, each Contributor hereby grants to You a perpetual, - worldwide, non-exclusive, no-charge, royalty-free, irrevocable - (except as stated in this section) patent license to make, have made, - use, offer to sell, sell, import, and otherwise transfer the Work, - where such license applies only to those patent claims licensable - by such Contributor that are necessarily infringed by their - Contribution(s) alone or by combination of their Contribution(s) - with the Work to which such Contribution(s) was submitted. If You - institute patent litigation against any entity (including a - cross-claim or counterclaim in a lawsuit) alleging that the Work - or a Contribution incorporated within the Work constitutes direct - or contributory patent infringement, then any patent licenses - granted to You under this License for that Work shall terminate - as of the date such litigation is filed. - - 4. Redistribution. You may reproduce and distribute copies of the - Work or Derivative Works thereof in any medium, with or without - modifications, and in Source or Object form, provided that You - meet the following conditions: - - (a) You must give any other recipients of the Work or - Derivative Works a copy of this License; and - - (b) You must cause any modified files to carry prominent notices - stating that You changed the files; and - - (c) You must retain, in the Source form of any Derivative Works - that You distribute, all copyright, patent, trademark, and - attribution notices from the Source form of the Work, - excluding those notices that do not pertain to any part of - the Derivative Works; and - - (d) If the Work includes a "NOTICE" text file as part of its - distribution, then any Derivative Works that You distribute must - include a readable copy of the attribution notices contained - within such NOTICE file, excluding those notices that do not - pertain to any part of the Derivative Works, in at least one - of the following places: within a NOTICE text file distributed - as part of the Derivative Works; within the Source form or - documentation, if provided along with the Derivative Works; or, - within a display generated by the Derivative Works, if and - wherever such third-party notices normally appear. The contents - of the NOTICE file are for informational purposes only and - do not modify the License. You may add Your own attribution - notices within Derivative Works that You distribute, alongside - or as an addendum to the NOTICE text from the Work, provided - that such additional attribution notices cannot be construed - as modifying the License. - - You may add Your own copyright statement to Your modifications and - may provide additional or different license terms and conditions - for use, reproduction, or distribution of Your modifications, or - for any such Derivative Works as a whole, provided Your use, - reproduction, and distribution of the Work otherwise complies with - the conditions stated in this License. - - 5. Submission of Contributions. Unless You explicitly state otherwise, - any Contribution intentionally submitted for inclusion in the Work - by You to the Licensor shall be under the terms and conditions of - this License, without any additional terms or conditions. - Notwithstanding the above, nothing herein shall supersede or modify - the terms of any separate license agreement you may have executed - with Licensor regarding such Contributions. - - 6. Trademarks. This License does not grant permission to use the trade - names, trademarks, service marks, or product names of the Licensor, - except as required for reasonable and customary use in describing the - origin of the Work and reproducing the content of the NOTICE file. - - 7. Disclaimer of Warranty. Unless required by applicable law or - agreed to in writing, Licensor provides the Work (and each - Contributor provides its Contributions) on an "AS IS" BASIS, - WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or - implied, including, without limitation, any warranties or conditions - of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A - PARTICULAR PURPOSE. You are solely responsible for determining the - appropriateness of using or redistributing the Work and assume any - risks associated with Your exercise of permissions under this License. - - 8. Limitation of Liability. In no event and under no legal theory, - whether in tort (including negligence), contract, or otherwise, - unless required by applicable law (such as deliberate and grossly - negligent acts) or agreed to in writing, shall any Contributor be - liable to You for damages, including any direct, indirect, special, - incidental, or consequential damages of any character arising as a - result of this License or out of the use or inability to use the - Work (including but not limited to damages for loss of goodwill, - work stoppage, computer failure or malfunction, or any and all - other commercial damages or losses), even if such Contributor - has been advised of the possibility of such damages. - - 9. Accepting Warranty or Additional Liability. While redistributing - the Work or Derivative Works thereof, You may choose to offer, - and charge a fee for, acceptance of support, warranty, indemnity, - or other liability obligations and/or rights consistent with this - License. However, in accepting such obligations, You may act only - on Your own behalf and on Your sole responsibility, not on behalf - of any other Contributor, and only if You agree to indemnify, - defend, and hold each Contributor harmless for any liability - incurred by, or claims asserted against, such Contributor by reason - of your accepting any such warranty or additional liability. - - END OF TERMS AND CONDITIONS diff --git a/gfx/wgpu/LICENSE.MIT b/gfx/wgpu/LICENSE.MIT deleted file mode 100644 index 4699691b8ed3..000000000000 --- a/gfx/wgpu/LICENSE.MIT +++ /dev/null @@ -1,21 +0,0 @@ -MIT License - -Copyright (c) 2021 The gfx-rs developers - -Permission is hereby granted, free of charge, to any person obtaining a copy -of this software and associated documentation files (the "Software"), to deal -in the Software without restriction, including without limitation the rights -to use, copy, modify, merge, publish, distribute, sublicense, and/or sell -copies of the Software, and to permit persons to whom the Software is -furnished to do so, subject to the following conditions: - -The above copyright notice and this permission notice shall be included in all -copies or substantial portions of the Software. - -THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE -AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, -OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE -SOFTWARE. diff --git a/gfx/wgpu/README.md b/gfx/wgpu/README.md index 6f5b2c6b9dd7..ce9078ba0975 100644 --- a/gfx/wgpu/README.md +++ b/gfx/wgpu/README.md @@ -1,23 +1,24 @@ +This github project is mirrored in "gfx/wgpu" of [Mozilla-central](https://hg.mozilla.org/mozilla-central/file/tip/gfx/wgpu). +Issues and pull requests are welcome, but some bidirectional synchronization may be involved. + # wgpu [![Matrix](https://img.shields.io/badge/Dev_Matrix-%23wgpu%3Amatrix.org-blueviolet.svg)](https://matrix.to/#/#wgpu:matrix.org) [![Matrix](https://img.shields.io/badge/User_Matrix-%23wgpu--users%3Amatrix.org-blueviolet.svg)](https://matrix.to/#/#wgpu-users:matrix.org) [![Build Status](https://github.com/gfx-rs/wgpu/workflows/CI/badge.svg)](https://github.com/gfx-rs/wgpu/actions) [![codecov.io](https://codecov.io/gh/gfx-rs/wgpu/branch/master/graph/badge.svg?token=84qJTesmeS)](https://codecov.io/gh/gfx-rs/wgpu) -This is an implementation of [WebGPU](https://www.w3.org/community/gpu/) API in Rust, targeting both native and the Web. -It's written in Rust and is based on [gfx-hal](https://github.com/gfx-rs/gfx) with help of [gpu-alloc](https://github.com/zakarumych/gpu-alloc) and [gpu-descriptor](https://github.com/zakarumych/gpu-descriptor). See the upstream [WebGPU specification](https://gpuweb.github.io/gpuweb/) (work in progress). +This is the core logic of an experimental [WebGPU](https://www.w3.org/community/gpu/) implementation. It's written in Rust and is based on [gfx-hal](https://github.com/gfx-rs/gfx) with help of [gpu-alloc](https://github.com/zakarumych/gpu-alloc) and [gpu-descriptor](https://github.com/zakarumych/gpu-descriptor). See the upstream [WebGPU specification](https://gpuweb.github.io/gpuweb/) (work in progress). -The repository hosts the following parts: +The implementation consists of the following parts: - - [![Crates.io](https://img.shields.io/crates/v/wgpu.svg?label=wgpu)](https://crates.io/crates/wgpu) [![docs.rs](https://docs.rs/wgpu/badge.svg)](https://docs.rs/wgpu/) - public Rust API for users - [![Crates.io](https://img.shields.io/crates/v/wgpu-core.svg?label=wgpu-core)](https://crates.io/crates/wgpu-core) [![docs.rs](https://docs.rs/wgpu-core/badge.svg)](https://docs.rs/wgpu-core/) - internal Rust API for WebGPU implementations to use - [![Crates.io](https://img.shields.io/crates/v/wgpu-types.svg?label=wgpu-types)](https://crates.io/crates/wgpu-types) [![docs.rs](https://docs.rs/wgpu-types/badge.svg)](https://docs.rs/wgpu-types/) - Rust types shared between `wgpu-core` and `wgpu-rs` - `player` - standalone application for replaying the API traces, uses `winit` -Rust examples can be found at `wgpu/examples`. `wgpu` is a default member, so you can run the examples directly from the root, e.g. `cargo run --example boids`. - +This repository contains the core of `wgpu`, and is not usable directly by applications. +If you are looking for the user-facing Rust API, you need [wgpu-rs](https://github.com/gfx-rs/wgpu-rs). If you are looking for the native implementation or bindings to the API in other languages, you need [wgpu-native](https://github.com/gfx-rs/wgpu-native). ## Supported Platforms diff --git a/gfx/wgpu/bors.toml b/gfx/wgpu/bors.toml index 729709bdf9a5..2cbeb4c7a713 100644 --- a/gfx/wgpu/bors.toml +++ b/gfx/wgpu/bors.toml @@ -7,6 +7,4 @@ status = [ "Ubuntu Nightly", "Windows Stable", "Windows Nightly", - "Web Assembly", - #"Clippy", ] diff --git a/gfx/wgpu/dummy/Cargo.toml b/gfx/wgpu/dummy/Cargo.toml index 40d1cfe696d9..37de0d18003d 100644 --- a/gfx/wgpu/dummy/Cargo.toml +++ b/gfx/wgpu/dummy/Cargo.toml @@ -5,7 +5,6 @@ authors = [ "Dzmitry Malyshau ", ] edition = "2018" -license = "MIT OR Apache-2.0" publish = false [features] diff --git a/gfx/wgpu/player/Cargo.toml b/gfx/wgpu/player/Cargo.toml index 3a675495bd86..70b39a492e1e 100644 --- a/gfx/wgpu/player/Cargo.toml +++ b/gfx/wgpu/player/Cargo.toml @@ -9,7 +9,7 @@ description = "WebGPU trace player" homepage = "https://github.com/gfx-rs/wgpu" repository = "https://github.com/gfx-rs/wgpu" keywords = ["graphics"] -license = "MIT OR Apache-2.0" +license = "MPL-2.0" publish = false [features] diff --git a/gfx/wgpu/player/src/lib.rs b/gfx/wgpu/player/src/lib.rs index 91adf5e91a69..85fd8e9bf6bd 100644 --- a/gfx/wgpu/player/src/lib.rs +++ b/gfx/wgpu/player/src/lib.rs @@ -88,7 +88,7 @@ impl GlobalPlay for wgc::hub::Global { dst, subresource_range, } => self - .command_encoder_clear_image::(encoder, dst, &subresource_range) + .command_encoder_clear_image::(encoder, dst, subresource_range) .unwrap(), trace::Command::WriteTimestamp { query_set_id, diff --git a/gfx/wgpu/player/tests/data/all.ron b/gfx/wgpu/player/tests/data/all.ron index c96b96c3889b..e519d793c3cc 100644 --- a/gfx/wgpu/player/tests/data/all.ron +++ b/gfx/wgpu/player/tests/data/all.ron @@ -5,7 +5,6 @@ "buffer-copy.ron", "clear-buffer-image.ron", "buffer-zero-init.ron", - "pipeline-statistics-query.ron", "quad.ron", ], ) \ No newline at end of file diff --git a/gfx/wgpu/player/tests/data/pipeline-statistics-query.ron b/gfx/wgpu/player/tests/data/pipeline-statistics-query.ron deleted file mode 100644 index c765b9d5864f..000000000000 --- a/gfx/wgpu/player/tests/data/pipeline-statistics-query.ron +++ /dev/null @@ -1,81 +0,0 @@ -( - features: (bits: 0x0000_0000_0000_0008), // PIPELINE_STATISTICS_QUERY - expectations: [ - ( - name: "Queried number of compute invocations is correct", - buffer: (index: 0, epoch: 1), - offset: 0, - data: Raw([0x2A, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00]), - ), - ], - actions: [ - CreatePipelineLayout(Id(0, 1, Empty), ( - label: Some("empty"), - bind_group_layouts: [], - push_constant_ranges: [], - )), - CreateShaderModule( - id: Id(0, 1, Empty), - desc: ( - label: None, - flags: (bits: 3), - ), - data: "empty.wgsl", - ), - CreateComputePipeline( - id: Id(0, 1, Empty), - desc: ( - label: None, - layout: Some(Id(0, 1, Empty)), - stage: ( - module: Id(0, 1, Empty), - entry_point: "main", - ), - ), - ), - CreateQuerySet( - id: Id(0, 1, Empty), - desc: ( - label: Some("Compute Invocation QuerySet"), - count: 2, - ty: PipelineStatistics((bits: 0x10)), // COMPUTE_SHADER_INVOCATIONS - ), - ), - CreateBuffer( - Id(0, 1, Empty), - ( - label: Some("Compute Invocation Result Buffer"), - size: 8, - usage: ( - bits: 9, // COPY_DST | MAP_READ - ), - mapped_at_creation: false, - ), - ), - Submit(1, [ - RunComputePass( - base: ( - commands: [ - SetPipeline(Id(0, 1, Empty)), - BeginPipelineStatisticsQuery( - query_set_id: Id(0, 1, Empty), - query_index: 0, - ), - Dispatch((2, 3, 7,)), - EndPipelineStatisticsQuery, - ], - dynamic_offsets: [], - string_data: [], - push_constant_data: [], - ), - ), - ResolveQuerySet( - query_set_id: Id(0, 1, Empty), - start_query: 0, - query_count: 1, - destination: Id(0, 1, Empty), - destination_offset: 0, - ) - ]), - ], -) diff --git a/gfx/wgpu/wgpu-core/Cargo.toml b/gfx/wgpu/wgpu-core/Cargo.toml index a36b1575d33c..66c5ac018478 100644 --- a/gfx/wgpu/wgpu-core/Cargo.toml +++ b/gfx/wgpu/wgpu-core/Cargo.toml @@ -1,13 +1,13 @@ [package] name = "wgpu-core" -version = "0.9.2" +version = "0.8.0" authors = ["wgpu developers"] edition = "2018" description = "WebGPU core logic on gfx-hal" homepage = "https://github.com/gfx-rs/wgpu" repository = "https://github.com/gfx-rs/wgpu" keywords = ["graphics"] -license = "MIT OR Apache-2.0" +license = "MPL-2.0" [lib] @@ -39,33 +39,34 @@ thiserror = "1" gpu-alloc = "0.4" gpu-descriptor = "0.1" -hal = { package = "gfx-hal", version = "0.9" } -gfx-backend-empty = { version = "0.9" } +hal = { package = "gfx-hal", git = "https://github.com/gfx-rs/gfx", rev = "27a1dae3796d33d23812f2bb8c7e3b5aea18b521" } +gfx-backend-empty = { git = "https://github.com/gfx-rs/gfx", rev = "27a1dae3796d33d23812f2bb8c7e3b5aea18b521" } [target.'cfg(all(not(target_arch = "wasm32"), all(unix, not(target_os = "ios"), not(target_os = "macos"))))'.dependencies] -gfx-backend-vulkan = { version = "0.9", features = ["naga"] } -#gfx-backend-gl = { version = "0.9 } +gfx-backend-vulkan = { git = "https://github.com/gfx-rs/gfx", rev = "27a1dae3796d33d23812f2bb8c7e3b5aea18b521", features = ["naga"] } +#gfx-backend-gl = { git = "https://github.com/gfx-rs/gfx", rev = "27a1dae3796d33d23812f2bb8c7e3b5aea18b521" } [target.'cfg(all(not(target_arch = "wasm32"), any(target_os = "ios", target_os = "macos")))'.dependencies] -gfx-backend-metal = { version = "0.9" } +gfx-backend-metal = { git = "https://github.com/gfx-rs/gfx", rev = "27a1dae3796d33d23812f2bb8c7e3b5aea18b521" } #TODO: could also depend on gfx-backend-vulkan for Vulkan Portability [target.'cfg(all(not(target_arch = "wasm32"), windows))'.dependencies] -gfx-backend-dx12 = { version = "0.9" } -gfx-backend-dx11 = { version = "0.9" } -gfx-backend-vulkan = { version = "0.9", features = ["naga"] } +gfx-backend-dx12 = { git = "https://github.com/gfx-rs/gfx", rev = "27a1dae3796d33d23812f2bb8c7e3b5aea18b521" } +gfx-backend-dx11 = { git = "https://github.com/gfx-rs/gfx", rev = "27a1dae3796d33d23812f2bb8c7e3b5aea18b521" } +gfx-backend-vulkan = { git = "https://github.com/gfx-rs/gfx", rev = "27a1dae3796d33d23812f2bb8c7e3b5aea18b521", features = ["naga"] } [target.'cfg(target_arch = "wasm32")'.dependencies] -#gfx-backend-gl = { version = "0.9" } +#gfx-backend-gl = { git = "https://github.com/gfx-rs/gfx", rev = "27a1dae3796d33d23812f2bb8c7e3b5aea18b521" } [dependencies.naga] -version = "0.5" +git = "https://github.com/gfx-rs/naga" +tag = "gfx-25" features = ["spv-in", "spv-out", "wgsl-in"] [dependencies.wgt] path = "../wgpu-types" package = "wgpu-types" -version = "0.9" +version = "0.8" [dev-dependencies] loom = "0.3" diff --git a/gfx/wgpu/wgpu-core/src/command/clear.rs b/gfx/wgpu/wgpu-core/src/command/clear.rs index 1698613dda83..847fe43bcba9 100644 --- a/gfx/wgpu/wgpu-core/src/command/clear.rs +++ b/gfx/wgpu/wgpu-core/src/command/clear.rs @@ -169,7 +169,7 @@ impl Global { &self, command_encoder_id: CommandEncoderId, dst: TextureId, - subresource_range: &ImageSubresourceRange, + subresource_range: ImageSubresourceRange, ) -> Result<(), ClearError> { profiling::scope!("CommandEncoder::clear_image"); diff --git a/gfx/wgpu/wgpu-core/src/command/query.rs b/gfx/wgpu/wgpu-core/src/command/query.rs index 7052efbfb28f..f3e7fe320b4a 100644 --- a/gfx/wgpu/wgpu-core/src/command/query.rs +++ b/gfx/wgpu/wgpu-core/src/command/query.rs @@ -11,7 +11,6 @@ use crate::{ device::all_buffer_stages, hub::{GfxBackend, Global, GlobalIdentityHandlerFactory, Storage, Token}, id::{self, Id, TypedId}, - memory_init_tracker::{MemoryInitKind, MemoryInitTrackerAction}, resource::{BufferUse, QuerySet}, track::UseExtendError, Epoch, FastHashMap, Index, @@ -382,11 +381,7 @@ impl Global { .into()); } - let elements_per_query = match query_set.desc.ty { - wgt::QueryType::PipelineStatistics(ps) => ps.bits().count_ones(), - wgt::QueryType::Timestamp => 1, - }; - let stride = elements_per_query * wgt::QUERY_SIZE; + let stride = query_set.elements * wgt::QUERY_SIZE; let bytes_used = (stride * query_count) as BufferAddress; let buffer_start_offset = destination_offset; @@ -404,17 +399,6 @@ impl Global { .into()); } - cmd_buf.buffer_memory_init_actions.extend( - dst_buffer - .initialization_status - .check(buffer_start_offset..buffer_end_offset) - .map(|range| MemoryInitTrackerAction { - id: destination, - range, - kind: MemoryInitKind::ImplicitlyInitialized, - }), - ); - unsafe { cmd_buf_raw.pipeline_barrier( all_buffer_stages()..hal::pso::PipelineStage::TRANSFER, diff --git a/gfx/wgpu/wgpu-core/src/device/life.rs b/gfx/wgpu/wgpu-core/src/device/life.rs index 9bda67e87922..2a7d3b1b3553 100644 --- a/gfx/wgpu/wgpu-core/src/device/life.rs +++ b/gfx/wgpu/wgpu-core/src/device/life.rs @@ -253,6 +253,7 @@ impl LifetimeTracker { &mut self, index: SubmissionIndex, fence: B::Fence, + new_suspects: &SuspectedResources, temp_resources: impl Iterator, alloc::MemoryBlock)>, ) { let mut last_resources = NonReferencedResources::new(); @@ -273,6 +274,7 @@ impl LifetimeTracker { .drain(..) .map(|stored| stored.value), ); + self.suspected_resources.extend(new_suspects); self.active.alloc().init(ActiveSubmission { index, diff --git a/gfx/wgpu/wgpu-core/src/device/mod.rs b/gfx/wgpu/wgpu-core/src/device/mod.rs index cf43aecf76a7..8d1db631cd76 100644 --- a/gfx/wgpu/wgpu-core/src/device/mod.rs +++ b/gfx/wgpu/wgpu-core/src/device/mod.rs @@ -427,10 +427,6 @@ impl Device { profiling::scope!("maintain", "Device"); let mut life_tracker = self.lock_life(token); - life_tracker - .suspected_resources - .extend(&self.temp_suspected); - life_tracker.triage_suspected( hub, &self.trackers, @@ -514,8 +510,6 @@ impl Device { self.lock_life(&mut token) .suspected_resources .extend(&self.temp_suspected); - - self.temp_suspected.clear(); } fn create_buffer( @@ -627,8 +621,8 @@ impl Device { ) -> Result, resource::CreateTextureError> { debug_assert_eq!(self_id.backend(), B::VARIANT); - let format_features = self - .describe_format_features(adapter, desc.format) + let format_desc = desc.format.describe(); + self.require_features(format_desc.required_features) .map_err(|error| resource::CreateTextureError::MissingFeatures(desc.format, error))?; // Ensure `D24Plus` textures cannot be copied @@ -648,6 +642,15 @@ impl Device { return Err(resource::CreateTextureError::EmptyUsage); } + let format_features = if self + .features + .contains(wgt::Features::TEXTURE_ADAPTER_SPECIFIC_FORMAT_FEATURES) + { + adapter.get_texture_format_features(desc.format) + } else { + format_desc.guaranteed_format_features + }; + let missing_allowed_usages = desc.usage - format_features.allowed_usages; if !missing_allowed_usages.is_empty() { return Err(resource::CreateTextureError::InvalidUsages( @@ -2103,7 +2106,6 @@ impl Device { fn create_render_pipeline( &self, self_id: id::DeviceId, - adapter: &crate::instance::Adapter, desc: &pipeline::RenderPipelineDescriptor, implicit_context: Option, hub: &Hub, @@ -2253,64 +2255,11 @@ impl Device { targets: Vec::with_capacity(color_states.len()), }; for (i, cs) in color_states.iter().enumerate() { - let error = loop { - let format_features = self.describe_format_features(adapter, cs.format)?; - if !format_features - .allowed_usages - .contains(wgt::TextureUsage::RENDER_ATTACHMENT) - { - break Some(pipeline::ColorStateError::FormatNotRenderable(cs.format)); - } - if cs.blend.is_some() && !format_features.filterable { - break Some(pipeline::ColorStateError::FormatNotBlendable(cs.format)); - } - let hal_format = conv::map_texture_format(cs.format, self.private_features); - if !hal_format - .surface_desc() - .aspects - .contains(hal::format::Aspects::COLOR) - { - break Some(pipeline::ColorStateError::FormatNotColor(cs.format)); - } - - match conv::map_color_target_state(cs) { - Ok(bt) => blender.targets.push(bt), - Err(e) => break Some(e), - } - break None; - }; - if let Some(e) = error { - return Err(pipeline::CreateRenderPipelineError::ColorState(i as u8, e)); - } + let bt = conv::map_color_target_state(cs) + .map_err(|error| pipeline::CreateRenderPipelineError::ColorState(i as u8, error))?; + blender.targets.push(bt); } - if let Some(ds) = depth_stencil_state { - let error = loop { - if !self - .describe_format_features(adapter, ds.format)? - .allowed_usages - .contains(wgt::TextureUsage::RENDER_ATTACHMENT) - { - break Some(pipeline::DepthStencilStateError::FormatNotRenderable( - ds.format, - )); - } - let hal_format = conv::map_texture_format(ds.format, self.private_features); - let aspects = hal_format.surface_desc().aspects; - if ds.is_depth_enabled() && !aspects.contains(hal::format::Aspects::DEPTH) { - break Some(pipeline::DepthStencilStateError::FormatNotDepth(ds.format)); - } - if ds.stencil.is_enabled() && !aspects.contains(hal::format::Aspects::STENCIL) { - break Some(pipeline::DepthStencilStateError::FormatNotStencil( - ds.format, - )); - } - break None; - }; - if let Some(e) = error { - return Err(pipeline::CreateRenderPipelineError::DepthStencilState(e)); - } - } let depth_stencil = depth_stencil_state .map(conv::map_depth_stencil_state) .unwrap_or_default(); @@ -2631,24 +2580,6 @@ impl Device { Ok(pipeline) } - fn describe_format_features( - &self, - adapter: &crate::instance::Adapter, - format: TextureFormat, - ) -> Result { - let format_desc = format.describe(); - self.require_features(format_desc.required_features)?; - - if self - .features - .contains(wgt::Features::TEXTURE_ADAPTER_SPECIFIC_FORMAT_FEATURES) - { - Ok(adapter.get_texture_format_features(format)) - } else { - Ok(format_desc.guaranteed_format_features) - } - } - fn wait_for_submit( &self, submission_index: SubmissionIndex, @@ -2684,7 +2615,7 @@ impl Device { return Err(Error::ZeroCount); } - if desc.count > wgt::QUERY_SET_MAX_QUERIES { + if desc.count >= wgt::QUERY_SET_MAX_QUERIES { return Err(Error::TooManyQueries { count: desc.count, maximum: wgt::QUERY_SET_MAX_QUERIES, @@ -4200,14 +4131,12 @@ impl Global { let fid = hub.render_pipelines.prepare(id_in); let implicit_context = implicit_pipeline_ids.map(|ipi| ipi.prepare(&hub)); - let (adapter_guard, mut token) = hub.adapters.read(&mut token); let (device_guard, mut token) = hub.devices.read(&mut token); let error = loop { let device = match device_guard.get(device_id) { Ok(device) => device, Err(_) => break DeviceError::Invalid.into(), }; - let adapter = &adapter_guard[device.adapter_id.value]; #[cfg(feature = "trace")] if let Some(ref trace) = device.trace { trace.lock().add(trace::Action::CreateRenderPipeline { @@ -4219,7 +4148,6 @@ impl Global { let pipeline = match device.create_render_pipeline( device_id, - adapter, desc, implicit_context, &hub, diff --git a/gfx/wgpu/wgpu-core/src/device/queue.rs b/gfx/wgpu/wgpu-core/src/device/queue.rs index d9a9e48a9b16..d05beaafdad0 100644 --- a/gfx/wgpu/wgpu-core/src/device/queue.rs +++ b/gfx/wgpu/wgpu-core/src/device/queue.rs @@ -601,11 +601,12 @@ impl Global { let device = device_guard .get_mut(queue_id) .map_err(|_| DeviceError::Invalid)?; + let pending_write_command_buffer = device.pending_writes.finish(); device.temp_suspected.clear(); device.active_submission_index += 1; let submit_index = device.active_submission_index; - let (fence, pending_write_command_buffer) = { + let fence = { let mut signal_swapchain_semaphores = SmallVec::<[_; 1]>::new(); let (mut swap_chain_guard, mut token) = hub.swap_chains.write(&mut token); let (mut command_buffer_guard, mut token) = hub.command_buffers.write(&mut token); @@ -757,9 +758,6 @@ impl Global { } } - // Finish pending writes. Don't do this earlier since buffer init may lead to additional writes (see initialize_buffer_memory). - let pending_write_command_buffer = device.pending_writes.finish(); - // now prepare the GPU submission let mut fence = device .raw @@ -788,7 +786,7 @@ impl Global { Some(&mut fence), ); } - (fence, pending_write_command_buffer) + fence }; if let Some(comb_raw) = pending_write_command_buffer { @@ -803,12 +801,11 @@ impl Global { Err(WaitIdleError::StuckGpu) => return Err(QueueSubmitError::StuckGpu), }; - device.temp_suspected.clear(); - profiling::scope!("cleanup"); super::Device::lock_life_internal(&device.life_tracker, &mut token).track_submission( submit_index, fence, + &device.temp_suspected, device.pending_writes.temp_resources.drain(..), ); diff --git a/gfx/wgpu/wgpu-core/src/pipeline.rs b/gfx/wgpu/wgpu-core/src/pipeline.rs index 05adbcd35593..a770ba2e57c9 100644 --- a/gfx/wgpu/wgpu-core/src/pipeline.rs +++ b/gfx/wgpu/wgpu-core/src/pipeline.rs @@ -202,12 +202,6 @@ pub struct RenderPipelineDescriptor<'a> { pub enum ColorStateError { #[error("output is missing")] Missing, - #[error("format {0:?} is not renderable")] - FormatNotRenderable(wgt::TextureFormat), - #[error("format {0:?} is not blendable")] - FormatNotBlendable(wgt::TextureFormat), - #[error("format {0:?} does not have a color aspect")] - FormatNotColor(wgt::TextureFormat), #[error("output format {pipeline} is incompatible with the shader {shader}")] IncompatibleFormat { pipeline: validation::NumericType, @@ -217,16 +211,6 @@ pub enum ColorStateError { InvalidMinMaxBlendFactors(wgt::BlendComponent), } -#[derive(Clone, Debug, Error)] -pub enum DepthStencilStateError { - #[error("format {0:?} is not renderable")] - FormatNotRenderable(wgt::TextureFormat), - #[error("format {0:?} does not have a depth aspect, but depth test/write is enabled")] - FormatNotDepth(wgt::TextureFormat), - #[error("format {0:?} does not have a stencil aspect, but stencil test/write is enabled")] - FormatNotStencil(wgt::TextureFormat), -} - #[derive(Clone, Debug, Error)] pub enum CreateRenderPipelineError { #[error(transparent)] @@ -237,8 +221,6 @@ pub enum CreateRenderPipelineError { Implicit(#[from] ImplicitLayoutError), #[error("color state [{0}] is invalid")] ColorState(u8, #[source] ColorStateError), - #[error("depth/stencil state is invalid")] - DepthStencilState(#[from] DepthStencilStateError), #[error("invalid sample count {0}")] InvalidSampleCount(u32), #[error("the number of vertex buffers {given} exceeds the limit {limit}")] diff --git a/gfx/wgpu/wgpu-core/src/track/mod.rs b/gfx/wgpu/wgpu-core/src/track/mod.rs index 872e161885fe..e61765969bb6 100644 --- a/gfx/wgpu/wgpu-core/src/track/mod.rs +++ b/gfx/wgpu/wgpu-core/src/track/mod.rs @@ -250,7 +250,7 @@ impl ResourceTracker { Entry::Occupied(e) => { if e.get().ref_count.load() == 1 { let res = e.remove(); - assert_eq!(res.epoch, epoch, "Epoch mismatch for {:?}", id); + assert_eq!(res.epoch, epoch); true } else { false diff --git a/gfx/wgpu/wgpu-core/src/validation.rs b/gfx/wgpu/wgpu-core/src/validation.rs index ffcb8b063dd5..56812c67c826 100644 --- a/gfx/wgpu/wgpu-core/src/validation.rs +++ b/gfx/wgpu/wgpu-core/src/validation.rs @@ -799,7 +799,7 @@ impl Interface { }; let ty = match module.types[var.ty].inner { naga::TypeInner::Struct { - top_level: true, + level: naga::StructLevel::Root, members: _, span, } => ResourceType::Buffer { diff --git a/gfx/wgpu/wgpu-types/Cargo.toml b/gfx/wgpu/wgpu-types/Cargo.toml index e5610b607c22..b81ec7d17ce4 100644 --- a/gfx/wgpu/wgpu-types/Cargo.toml +++ b/gfx/wgpu/wgpu-types/Cargo.toml @@ -1,13 +1,13 @@ [package] name = "wgpu-types" -version = "0.9.0" +version = "0.8.0" authors = ["wgpu developers"] edition = "2018" description = "WebGPU types" homepage = "https://github.com/gfx-rs/wgpu" repository = "https://github.com/gfx-rs/wgpu" keywords = ["graphics"] -license = "MIT OR Apache-2.0" +license = "MPL-2.0" [lib] diff --git a/gfx/wgpu_bindings/src/lib.rs b/gfx/wgpu_bindings/src/lib.rs index d86bbc6dd43f..d30242f5310e 100644 --- a/gfx/wgpu_bindings/src/lib.rs +++ b/gfx/wgpu_bindings/src/lib.rs @@ -62,7 +62,7 @@ impl ByteBuf { #[derive(serde::Serialize, serde::Deserialize)] pub struct AdapterInformation { id: id::AdapterId, - ty: wgt::DeviceType, + //inner: wgt::AdapterInfo, //TODO: not C-friendly limits: wgt::Limits, features: wgt::Features, } diff --git a/gfx/wgpu_bindings/src/server.rs b/gfx/wgpu_bindings/src/server.rs index fe6d7a236b3b..43b708ada243 100644 --- a/gfx/wgpu_bindings/src/server.rs +++ b/gfx/wgpu_bindings/src/server.rs @@ -123,10 +123,9 @@ pub unsafe extern "C" fn wgpu_server_adapter_pack_info( let mut data = Vec::new(); match self_id { Some(id) => { - let raw_info = gfx_select!(id => global.adapter_get_info(id)).unwrap(); let info = AdapterInformation { id, - ty: raw_info.device_type, + //inner: gfx_select!(self_id => global.adapter_get_info(self_id)).unwrap(), limits: gfx_select!(id => global.adapter_limits(id)).unwrap(), features: gfx_select!(id => global.adapter_features(id)).unwrap(), }; @@ -462,7 +461,7 @@ impl GlobalExt for Global { } CommandEncoderAction::ClearImage { dst, - ref subresource_range, + subresource_range, } => { if let Err(err) = self.command_encoder_clear_image::(self_id, dst, subresource_range) diff --git a/layout/generic/nsHTMLCanvasFrame.cpp b/layout/generic/nsHTMLCanvasFrame.cpp index c290355fed1f..030ea28d82fe 100644 --- a/layout/generic/nsHTMLCanvasFrame.cpp +++ b/layout/generic/nsHTMLCanvasFrame.cpp @@ -195,8 +195,24 @@ class nsDisplayCanvas final : public nsPaintedDisplayItem { return true; } - const wr::ImageDescriptor imageDesc = - canvasContext->MakeImageDescriptor(); + nsIntSize canvasSizeInPx = canvasFrame->GetCanvasSize(); + IntrinsicSize intrinsicSize = + IntrinsicSizeFromCanvasSize(canvasSizeInPx); + AspectRatio intrinsicRatio = + IntrinsicRatioFromCanvasSize(canvasSizeInPx); + nsRect area = + mFrame->GetContentRectRelativeToSelf() + ToReferenceFrame(); + nsRect dest = nsLayoutUtils::ComputeObjectDestRect( + area, intrinsicSize, intrinsicRatio, mFrame->StylePosition()); + LayoutDeviceRect bounds = LayoutDeviceRect::FromAppUnits( + dest, mFrame->PresContext()->AppUnitsPerDevPixel()); + + const RGBDescriptor rgbDesc(canvasSizeInPx, canvasData->mFormat, false); + const auto targetStride = ImageDataSerializer::GetRGBStride(rgbDesc); + const bool preferCompositorSurface = true; + const wr::ImageDescriptor imageDesc( + canvasSizeInPx, targetStride, canvasData->mFormat, + wr::OpacityType::HasAlphaChannel, preferCompositorSurface); wr::ImageKey imageKey; auto imageKeyMaybe = canvasContext->GetImageKey(); @@ -211,23 +227,10 @@ class nsDisplayCanvas final : public nsPaintedDisplayItem { imageKey, imageDesc); } - { - nsIntSize canvasSizeInPx = canvasFrame->GetCanvasSize(); - IntrinsicSize intrinsicSize = - IntrinsicSizeFromCanvasSize(canvasSizeInPx); - AspectRatio intrinsicRatio = - IntrinsicRatioFromCanvasSize(canvasSizeInPx); - nsRect area = - mFrame->GetContentRectRelativeToSelf() + ToReferenceFrame(); - nsRect dest = nsLayoutUtils::ComputeObjectDestRect( - area, intrinsicSize, intrinsicRatio, mFrame->StylePosition()); - LayoutDeviceRect bounds = LayoutDeviceRect::FromAppUnits( - dest, mFrame->PresContext()->AppUnitsPerDevPixel()); - mozilla::wr::ImageRendering rendering = wr::ToImageRendering( - nsLayoutUtils::GetSamplingFilterForFrame(mFrame)); - aBuilder.PushImage(wr::ToLayoutRect(bounds), wr::ToLayoutRect(bounds), - !BackfaceIsHidden(), rendering, imageKey); - } + mozilla::wr::ImageRendering rendering = wr::ToImageRendering( + nsLayoutUtils::GetSamplingFilterForFrame(mFrame)); + aBuilder.PushImage(wr::ToLayoutRect(bounds), wr::ToLayoutRect(bounds), + !BackfaceIsHidden(), rendering, imageKey); canvasData->mDescriptor = imageDesc; canvasData->mImageKey = imageKey; diff --git a/taskcluster/ci/toolchain/misc.yml b/taskcluster/ci/toolchain/misc.yml index 5a86ddcf92cc..bbc9ac96d9e1 100644 --- a/taskcluster/ci/toolchain/misc.yml +++ b/taskcluster/ci/toolchain/misc.yml @@ -216,8 +216,7 @@ wgpu-deps: fetch: - android-rs-glue toolchain: - # this requires resolver=2 in Naga - - linux64-rust-1.51 # whatever m-c is built with + - linux64-rust-1.47 # whatever m-c is built with linux64-liblowercase: description: "liblowercase" diff --git a/third_party/rust/drm-fourcc/.cargo-checksum.json b/third_party/rust/drm-fourcc/.cargo-checksum.json deleted file mode 100644 index 92e66d7de1b1..000000000000 --- a/third_party/rust/drm-fourcc/.cargo-checksum.json +++ /dev/null @@ -1 +0,0 @@ -{"files":{"Cargo.toml":"05f9a9cfba4c06202203421e396de95b0d6b2aacf6b88b64bd4dddcea92d9e86","README.md":"51afa4a71ce2aea69ed2c928b7abc6fcb6e361ba0f17b00c869456dd0a4de12f","build.rs":"d01ecd12113126de35dd710e840c651a4a6a1c72ef54233c44bab471a7838a46","src/as_enum.rs":"246f73c278aa45e3e7b10667c60d8d7399a43c52e9f1a05cf7c779747aa271ca","src/consts.rs":"0e542102e07b9df79b2edf3fdbb96e98b17cfb78383e56f4fabe37632654cc1f","src/lib.rs":"0c354a1f03eeae89c3022befde5249a19532d9eac2883673aa9397027f6abb51"},"package":"ebbf3a5ed4671aabffefce172ff43d69c1f27dd2c6aea28e5212a70f32ada0cf"} \ No newline at end of file diff --git a/third_party/rust/drm-fourcc/Cargo.toml b/third_party/rust/drm-fourcc/Cargo.toml deleted file mode 100644 index 3a20e9b1bd30..000000000000 --- a/third_party/rust/drm-fourcc/Cargo.toml +++ /dev/null @@ -1,37 +0,0 @@ -# THIS FILE IS AUTOMATICALLY GENERATED BY CARGO -# -# When uploading crates to the registry Cargo will automatically -# "normalize" Cargo.toml files for maximal compatibility -# with all versions of Cargo and also rewrite `path` dependencies -# to registry (e.g., crates.io) dependencies -# -# If you believe there's an error in this file please file an -# issue against the rust-lang/cargo repository. If you're -# editing this file be aware that the upstream Cargo.toml -# will likely look very different (and much more reasonable) - -[package] -edition = "2018" -name = "drm-fourcc" -version = "2.1.1" -authors = ["Daniel Franklin "] -description = "Provides an enum with every valid Direct Rendering Manager (DRM) format fourcc" -keywords = ["drm", "fourcc", "linux"] -categories = ["os::linux-apis"] -license = "MIT" -repository = "https://github.com/danielzfranklin/drm-fourcc-rs" -[dependencies.serde] -version = "1.0.125" -features = ["derive"] -optional = true -[build-dependencies.bindgen] -version = "0.57.0" -optional = true - -[build-dependencies.regex] -version = "1.4.3" -optional = true - -[features] -build_bindings = ["regex", "bindgen"] -default = [] diff --git a/third_party/rust/drm-fourcc/README.md b/third_party/rust/drm-fourcc/README.md deleted file mode 100644 index cd0804fba06c..000000000000 --- a/third_party/rust/drm-fourcc/README.md +++ /dev/null @@ -1,43 +0,0 @@ -# drm-fourcc - -[![Crates.io](https://img.shields.io/crates/v/drm-fourcc)](https://crates.io/crates/drm-fourcc) -![MIT Licensed](https://img.shields.io/crates/l/drm-fourcc) - -Provides an enums representing every pixel format and format modifier supported -by DRM (as of kernel version 5.10.0). - -A [fourcc][fourcc_wiki] is four bytes of ascii representing some data format. This enum contains -every fourcc representing a pixel format supported by [DRM][drm_wiki], the Linux Direct -Rendering Manager. - -To get the bytes of the fourcc representing the format, cast to `u32`. - -```rust -assert_eq!(DrmFourcc::Xrgb8888 as u32, 875713112); -``` - -To get the string form of the fourcc, use [`DrmFourcc::string_form`]. - -```rust -assert_eq!(DrmFourcc::Xrgb8888.string_form(), "XR24"); -``` - -We also provide a type for representing a fourcc/modifier pair - -```rust -let format = DrmFormat { - code: DrmFourcc::Xrgb8888, - modifier: DrmModifier::Linear, -}; -``` - -The enums are autogenerated from the [canonical list][canonical] in the Linux source code. - -## Contributors - -- [Daniel Franklin](https://github.com/danielzfranklin) -- [Victor Brekenfeld](https://github.com/Drakulix) - -[fourcc_wiki]: https://en.wikipedia.org/wiki/FourCC -[drm_wiki]: https://en.wikipedia.org/wiki/Direct_Rendering_Managerz -[canonical]: https://github.com/torvalds/linux/blame/master/include/uapi/drm/drm_fourcc.h diff --git a/third_party/rust/drm-fourcc/build.rs b/third_party/rust/drm-fourcc/build.rs deleted file mode 100644 index de7075a4bcf4..000000000000 --- a/third_party/rust/drm-fourcc/build.rs +++ /dev/null @@ -1,244 +0,0 @@ -#[cfg(not(feature = "build_bindings"))] -fn main() { - println!("cargo:rerun-if-changed=build.rs"); // never rerun -} - -#[cfg(feature = "build_bindings")] -fn main() { - println!("cargo:rerun-if-changed=build.rs"); // avoids double-build when we output into src - generate::generate().unwrap(); -} - -#[cfg(feature = "build_bindings")] -mod generate { - use std::error::Error; - use std::io::Write; - use std::process::{Command, Stdio}; - - use regex::Regex; - use std::env; - use std::fs::File; - use std::path::Path; - - static CONST_PREFIX: &'static str = "DRM_FOURCC_"; - - pub fn generate() -> Result<(), Box> { - let out_dir = env::var("OUT_DIR").unwrap(); - let wrapper_path = Path::new(&out_dir).join("wrapper.h"); - - // First get all the macros in drm_fourcc.h - - let mut cmd = Command::new("clang") - .arg("-E") // run pre-processor only - .arg("-dM") // output all macros defined - .arg("-") // take input from stdin - .stdin(Stdio::piped()) - .stdout(Stdio::piped()) - .spawn()?; - - { - let stdin = cmd.stdin.as_mut().expect("failed to open stdin"); - stdin.write_all(b"#include \n")?; - } - - let result = cmd.wait_with_output()?; - let stdout = String::from_utf8(result.stdout)?; - if !result.status.success() { - panic!("Clang failed with output: {}", stdout) - } - - // Then get the names of the format macros - - let fmt_re = Regex::new(r"^\s*#define (?PDRM_FORMAT_(?P[A-Z0-9_]+)) ")?; - let format_names: Vec<(&str, &str)> = stdout - .lines() - .filter_map(|line| { - if line.contains("DRM_FORMAT_RESERVED") || line.contains("INVALID") || line.contains("_MOD_") { - return None; - } - - fmt_re.captures(line).map(|caps| { - let full = caps.name("full").unwrap().as_str(); - let short = caps.name("short").unwrap().as_str(); - - (full, short) - }) - }) - .collect(); - - let vendor_re = Regex::new(r"^\s*#define (?PDRM_FORMAT_MOD_VENDOR_(?P[A-Z0-9_]+)) ")?; - let vendor_names: Vec<(&str, &str)> = stdout - .lines() - .filter_map(|line| { - if line.contains("DRM_FORMAT_MOD_VENDOR_NONE") { - return None; - } - - vendor_re.captures(line).map(|caps| { - let full = caps.name("full").unwrap().as_str(); - let short = caps.name("short").unwrap().as_str(); - - (full, short) - }) - }) - .collect(); - - let mod_re = Regex::new(r"^\s*#define (?P(DRM|I915)_FORMAT_MOD_(?P[A-Z0-9_]+)) ")?; - let modifier_names: Vec<(&str, String)> = stdout - .lines() - .filter_map(|line| { - if line.contains("DRM_FORMAT_MOD_NONE") - || line.contains("DRM_FORMAT_MOD_RESERVED") - || line.contains("VENDOR") - || line.contains("ARM_TYPE") // grrr.. - { - return None; - } - - mod_re.captures(line).map(|caps| { - let full = caps.name("full").unwrap().as_str(); - let short = caps.name("short").unwrap().as_str(); - - (full, if full.contains("I915") { - format!("I915_{}", short) - } else { - String::from(short) - }) - }) - }) - .collect(); - - // Then create a file with a variable defined for every format macro - - let mut wrapper = File::create(&wrapper_path)?; - - wrapper.write_all(b"#include \n")?; - wrapper.write_all(b"#include \n")?; - - for (full, short) in &format_names { - writeln!(wrapper, "uint32_t {}{} = {};\n", CONST_PREFIX, short, full)?; - } - for (full, short) in &vendor_names { - writeln!(wrapper, "uint8_t {}{} = {};\n", CONST_PREFIX, short, full)?; - } - for (full, short) in &modifier_names { - writeln!(wrapper, "uint64_t {}{} = {};\n", CONST_PREFIX, short, full)?; - } - - wrapper.flush()?; - - // Then generate bindings from that file - bindgen::builder() - .header(wrapper_path.as_os_str().to_str().unwrap()) - .whitelist_var("DRM_FOURCC_.*") - .generate() - .unwrap() - .write_to_file("src/consts.rs")?; - - // Then generate our enums - fn write_enum(as_enum: &mut File, name: &str, repr: &str, names: Vec<(&str, &str)>) -> Result<(), std::io::Error> { - as_enum.write_all(b"#[derive(Copy, Clone, Eq, PartialEq, Hash)]")?; - as_enum.write_all( - b"#[cfg_attr(feature = \"serde\", derive(serde::Serialize, serde::Deserialize))]", - )?; - writeln!(as_enum, "#[repr({})]", repr)?; - writeln!(as_enum, "pub enum {} {{", name)?; - - let members: Vec<(String, String)> = names - .iter() - .map(|(_, short)| { - ( - enum_member_case(short), - format!("consts::{}{}", CONST_PREFIX, short), - ) - }) - .collect(); - - for (member, value) in &members { - writeln!(as_enum, "{} = {},", member, value)?; - } - - as_enum.write_all(b"}\n")?; - - writeln!(as_enum, "impl {} {{", name)?; - writeln!(as_enum, "pub(crate) fn from_{}(n: {}) -> Option {{\n", repr, repr)?; - as_enum.write_all(b"match n {\n")?; - - for (member, value) in &members { - writeln!(as_enum, "{} => Some(Self::{}),", value, member)?; - } - - writeln!(as_enum, "_ => None")?; - as_enum.write_all(b"}}}\n")?; - - Ok(()) - } - - let as_enum_path = "src/as_enum.rs"; - { - let mut as_enum = File::create(as_enum_path)?; - - as_enum.write_all(b"// Automatically generated by build.rs\n")?; - as_enum.write_all(b"use crate::consts;")?; - - write_enum(&mut as_enum, "DrmFourcc", "u32", format_names)?; - - as_enum.write_all(b"#[derive(Debug)]")?; - write_enum(&mut as_enum, "DrmVendor", "u8", vendor_names)?; - - // modifiers can overlap - - as_enum.write_all(b"#[derive(Debug, Copy, Clone)]")?; - as_enum.write_all( - b"#[cfg_attr(feature = \"serde\", derive(serde::Serialize, serde::Deserialize))]", - )?; - as_enum.write_all(b"pub enum DrmModifier {\n")?; - - let modifier_members: Vec<(String, String)> = modifier_names - .iter() - .map(|(_, short)| { - ( - enum_member_case(short), - format!("consts::{}{}", CONST_PREFIX, short), - ) - }) - .collect(); - for (member, _) in &modifier_members { - writeln!(as_enum, "{},", member)?; - } - as_enum.write_all(b"Unrecognized(u64)")?; - - as_enum.write_all(b"}\n")?; - - as_enum.write_all(b"impl DrmModifier {\n")?; - as_enum.write_all(b"pub(crate) fn from_u64(n: u64) -> Self {\n")?; - as_enum.write_all(b"#[allow(unreachable_patterns)]\n")?; - as_enum.write_all(b"match n {\n")?; - - for (member, value) in &modifier_members { - writeln!(as_enum, "{} => Self::{},", value, member)?; - } - as_enum.write_all(b"x => Self::Unrecognized(x)\n")?; - - as_enum.write_all(b"}}\n")?; - as_enum.write_all(b"pub(crate) fn into_u64(&self) -> u64 {\n")?; - as_enum.write_all(b"match self {\n")?; - - for (member, value) in &modifier_members { - writeln!(as_enum, "Self::{} => {},", member, value)?; - } - as_enum.write_all(b"Self::Unrecognized(x) => *x,\n")?; - - as_enum.write_all(b"}}}\n")?; - } - - Command::new("rustfmt").arg(as_enum_path).spawn()?.wait()?; - - Ok(()) - } - - fn enum_member_case(s: &str) -> String { - let (first, rest) = s.split_at(1); - format!("{}{}", first, rest.to_ascii_lowercase()) - } -} diff --git a/third_party/rust/drm-fourcc/src/as_enum.rs b/third_party/rust/drm-fourcc/src/as_enum.rs deleted file mode 100644 index 0fc1d27de7b8..000000000000 --- a/third_party/rust/drm-fourcc/src/as_enum.rs +++ /dev/null @@ -1,369 +0,0 @@ -// Automatically generated by build.rs -use crate::consts; -#[derive(Copy, Clone, Eq, PartialEq, Hash)] -#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))] -#[repr(u32)] -pub enum DrmFourcc { - Abgr1555 = consts::DRM_FOURCC_ABGR1555, - Abgr16161616f = consts::DRM_FOURCC_ABGR16161616F, - Abgr2101010 = consts::DRM_FOURCC_ABGR2101010, - Abgr4444 = consts::DRM_FOURCC_ABGR4444, - Abgr8888 = consts::DRM_FOURCC_ABGR8888, - Argb1555 = consts::DRM_FOURCC_ARGB1555, - Argb16161616f = consts::DRM_FOURCC_ARGB16161616F, - Argb2101010 = consts::DRM_FOURCC_ARGB2101010, - Argb4444 = consts::DRM_FOURCC_ARGB4444, - Argb8888 = consts::DRM_FOURCC_ARGB8888, - Axbxgxrx106106106106 = consts::DRM_FOURCC_AXBXGXRX106106106106, - Ayuv = consts::DRM_FOURCC_AYUV, - Bgr233 = consts::DRM_FOURCC_BGR233, - Bgr565 = consts::DRM_FOURCC_BGR565, - Bgr565_a8 = consts::DRM_FOURCC_BGR565_A8, - Bgr888 = consts::DRM_FOURCC_BGR888, - Bgr888_a8 = consts::DRM_FOURCC_BGR888_A8, - Bgra1010102 = consts::DRM_FOURCC_BGRA1010102, - Bgra4444 = consts::DRM_FOURCC_BGRA4444, - Bgra5551 = consts::DRM_FOURCC_BGRA5551, - Bgra8888 = consts::DRM_FOURCC_BGRA8888, - Bgrx1010102 = consts::DRM_FOURCC_BGRX1010102, - Bgrx4444 = consts::DRM_FOURCC_BGRX4444, - Bgrx5551 = consts::DRM_FOURCC_BGRX5551, - Bgrx8888 = consts::DRM_FOURCC_BGRX8888, - Bgrx8888_a8 = consts::DRM_FOURCC_BGRX8888_A8, - Big_endian = consts::DRM_FOURCC_BIG_ENDIAN, - C8 = consts::DRM_FOURCC_C8, - Gr1616 = consts::DRM_FOURCC_GR1616, - Gr88 = consts::DRM_FOURCC_GR88, - Nv12 = consts::DRM_FOURCC_NV12, - Nv15 = consts::DRM_FOURCC_NV15, - Nv16 = consts::DRM_FOURCC_NV16, - Nv21 = consts::DRM_FOURCC_NV21, - Nv24 = consts::DRM_FOURCC_NV24, - Nv42 = consts::DRM_FOURCC_NV42, - Nv61 = consts::DRM_FOURCC_NV61, - P010 = consts::DRM_FOURCC_P010, - P012 = consts::DRM_FOURCC_P012, - P016 = consts::DRM_FOURCC_P016, - P210 = consts::DRM_FOURCC_P210, - Q401 = consts::DRM_FOURCC_Q401, - Q410 = consts::DRM_FOURCC_Q410, - R16 = consts::DRM_FOURCC_R16, - R8 = consts::DRM_FOURCC_R8, - Rg1616 = consts::DRM_FOURCC_RG1616, - Rg88 = consts::DRM_FOURCC_RG88, - Rgb332 = consts::DRM_FOURCC_RGB332, - Rgb565 = consts::DRM_FOURCC_RGB565, - Rgb565_a8 = consts::DRM_FOURCC_RGB565_A8, - Rgb888 = consts::DRM_FOURCC_RGB888, - Rgb888_a8 = consts::DRM_FOURCC_RGB888_A8, - Rgba1010102 = consts::DRM_FOURCC_RGBA1010102, - Rgba4444 = consts::DRM_FOURCC_RGBA4444, - Rgba5551 = consts::DRM_FOURCC_RGBA5551, - Rgba8888 = consts::DRM_FOURCC_RGBA8888, - Rgbx1010102 = consts::DRM_FOURCC_RGBX1010102, - Rgbx4444 = consts::DRM_FOURCC_RGBX4444, - Rgbx5551 = consts::DRM_FOURCC_RGBX5551, - Rgbx8888 = consts::DRM_FOURCC_RGBX8888, - Rgbx8888_a8 = consts::DRM_FOURCC_RGBX8888_A8, - Uyvy = consts::DRM_FOURCC_UYVY, - Vuy101010 = consts::DRM_FOURCC_VUY101010, - Vuy888 = consts::DRM_FOURCC_VUY888, - Vyuy = consts::DRM_FOURCC_VYUY, - X0l0 = consts::DRM_FOURCC_X0L0, - X0l2 = consts::DRM_FOURCC_X0L2, - Xbgr1555 = consts::DRM_FOURCC_XBGR1555, - Xbgr16161616f = consts::DRM_FOURCC_XBGR16161616F, - Xbgr2101010 = consts::DRM_FOURCC_XBGR2101010, - Xbgr4444 = consts::DRM_FOURCC_XBGR4444, - Xbgr8888 = consts::DRM_FOURCC_XBGR8888, - Xbgr8888_a8 = consts::DRM_FOURCC_XBGR8888_A8, - Xrgb1555 = consts::DRM_FOURCC_XRGB1555, - Xrgb16161616f = consts::DRM_FOURCC_XRGB16161616F, - Xrgb2101010 = consts::DRM_FOURCC_XRGB2101010, - Xrgb4444 = consts::DRM_FOURCC_XRGB4444, - Xrgb8888 = consts::DRM_FOURCC_XRGB8888, - Xrgb8888_a8 = consts::DRM_FOURCC_XRGB8888_A8, - Xvyu12_16161616 = consts::DRM_FOURCC_XVYU12_16161616, - Xvyu16161616 = consts::DRM_FOURCC_XVYU16161616, - Xvyu2101010 = consts::DRM_FOURCC_XVYU2101010, - Xyuv8888 = consts::DRM_FOURCC_XYUV8888, - Y0l0 = consts::DRM_FOURCC_Y0L0, - Y0l2 = consts::DRM_FOURCC_Y0L2, - Y210 = consts::DRM_FOURCC_Y210, - Y212 = consts::DRM_FOURCC_Y212, - Y216 = consts::DRM_FOURCC_Y216, - Y410 = consts::DRM_FOURCC_Y410, - Y412 = consts::DRM_FOURCC_Y412, - Y416 = consts::DRM_FOURCC_Y416, - Yuv410 = consts::DRM_FOURCC_YUV410, - Yuv411 = consts::DRM_FOURCC_YUV411, - Yuv420 = consts::DRM_FOURCC_YUV420, - Yuv420_10bit = consts::DRM_FOURCC_YUV420_10BIT, - Yuv420_8bit = consts::DRM_FOURCC_YUV420_8BIT, - Yuv422 = consts::DRM_FOURCC_YUV422, - Yuv444 = consts::DRM_FOURCC_YUV444, - Yuyv = consts::DRM_FOURCC_YUYV, - Yvu410 = consts::DRM_FOURCC_YVU410, - Yvu411 = consts::DRM_FOURCC_YVU411, - Yvu420 = consts::DRM_FOURCC_YVU420, - Yvu422 = consts::DRM_FOURCC_YVU422, - Yvu444 = consts::DRM_FOURCC_YVU444, - Yvyu = consts::DRM_FOURCC_YVYU, -} -impl DrmFourcc { - pub(crate) fn from_u32(n: u32) -> Option { - match n { - consts::DRM_FOURCC_ABGR1555 => Some(Self::Abgr1555), - consts::DRM_FOURCC_ABGR16161616F => Some(Self::Abgr16161616f), - consts::DRM_FOURCC_ABGR2101010 => Some(Self::Abgr2101010), - consts::DRM_FOURCC_ABGR4444 => Some(Self::Abgr4444), - consts::DRM_FOURCC_ABGR8888 => Some(Self::Abgr8888), - consts::DRM_FOURCC_ARGB1555 => Some(Self::Argb1555), - consts::DRM_FOURCC_ARGB16161616F => Some(Self::Argb16161616f), - consts::DRM_FOURCC_ARGB2101010 => Some(Self::Argb2101010), - consts::DRM_FOURCC_ARGB4444 => Some(Self::Argb4444), - consts::DRM_FOURCC_ARGB8888 => Some(Self::Argb8888), - consts::DRM_FOURCC_AXBXGXRX106106106106 => Some(Self::Axbxgxrx106106106106), - consts::DRM_FOURCC_AYUV => Some(Self::Ayuv), - consts::DRM_FOURCC_BGR233 => Some(Self::Bgr233), - consts::DRM_FOURCC_BGR565 => Some(Self::Bgr565), - consts::DRM_FOURCC_BGR565_A8 => Some(Self::Bgr565_a8), - consts::DRM_FOURCC_BGR888 => Some(Self::Bgr888), - consts::DRM_FOURCC_BGR888_A8 => Some(Self::Bgr888_a8), - consts::DRM_FOURCC_BGRA1010102 => Some(Self::Bgra1010102), - consts::DRM_FOURCC_BGRA4444 => Some(Self::Bgra4444), - consts::DRM_FOURCC_BGRA5551 => Some(Self::Bgra5551), - consts::DRM_FOURCC_BGRA8888 => Some(Self::Bgra8888), - consts::DRM_FOURCC_BGRX1010102 => Some(Self::Bgrx1010102), - consts::DRM_FOURCC_BGRX4444 => Some(Self::Bgrx4444), - consts::DRM_FOURCC_BGRX5551 => Some(Self::Bgrx5551), - consts::DRM_FOURCC_BGRX8888 => Some(Self::Bgrx8888), - consts::DRM_FOURCC_BGRX8888_A8 => Some(Self::Bgrx8888_a8), - consts::DRM_FOURCC_BIG_ENDIAN => Some(Self::Big_endian), - consts::DRM_FOURCC_C8 => Some(Self::C8), - consts::DRM_FOURCC_GR1616 => Some(Self::Gr1616), - consts::DRM_FOURCC_GR88 => Some(Self::Gr88), - consts::DRM_FOURCC_NV12 => Some(Self::Nv12), - consts::DRM_FOURCC_NV15 => Some(Self::Nv15), - consts::DRM_FOURCC_NV16 => Some(Self::Nv16), - consts::DRM_FOURCC_NV21 => Some(Self::Nv21), - consts::DRM_FOURCC_NV24 => Some(Self::Nv24), - consts::DRM_FOURCC_NV42 => Some(Self::Nv42), - consts::DRM_FOURCC_NV61 => Some(Self::Nv61), - consts::DRM_FOURCC_P010 => Some(Self::P010), - consts::DRM_FOURCC_P012 => Some(Self::P012), - consts::DRM_FOURCC_P016 => Some(Self::P016), - consts::DRM_FOURCC_P210 => Some(Self::P210), - consts::DRM_FOURCC_Q401 => Some(Self::Q401), - consts::DRM_FOURCC_Q410 => Some(Self::Q410), - consts::DRM_FOURCC_R16 => Some(Self::R16), - consts::DRM_FOURCC_R8 => Some(Self::R8), - consts::DRM_FOURCC_RG1616 => Some(Self::Rg1616), - consts::DRM_FOURCC_RG88 => Some(Self::Rg88), - consts::DRM_FOURCC_RGB332 => Some(Self::Rgb332), - consts::DRM_FOURCC_RGB565 => Some(Self::Rgb565), - consts::DRM_FOURCC_RGB565_A8 => Some(Self::Rgb565_a8), - consts::DRM_FOURCC_RGB888 => Some(Self::Rgb888), - consts::DRM_FOURCC_RGB888_A8 => Some(Self::Rgb888_a8), - consts::DRM_FOURCC_RGBA1010102 => Some(Self::Rgba1010102), - consts::DRM_FOURCC_RGBA4444 => Some(Self::Rgba4444), - consts::DRM_FOURCC_RGBA5551 => Some(Self::Rgba5551), - consts::DRM_FOURCC_RGBA8888 => Some(Self::Rgba8888), - consts::DRM_FOURCC_RGBX1010102 => Some(Self::Rgbx1010102), - consts::DRM_FOURCC_RGBX4444 => Some(Self::Rgbx4444), - consts::DRM_FOURCC_RGBX5551 => Some(Self::Rgbx5551), - consts::DRM_FOURCC_RGBX8888 => Some(Self::Rgbx8888), - consts::DRM_FOURCC_RGBX8888_A8 => Some(Self::Rgbx8888_a8), - consts::DRM_FOURCC_UYVY => Some(Self::Uyvy), - consts::DRM_FOURCC_VUY101010 => Some(Self::Vuy101010), - consts::DRM_FOURCC_VUY888 => Some(Self::Vuy888), - consts::DRM_FOURCC_VYUY => Some(Self::Vyuy), - consts::DRM_FOURCC_X0L0 => Some(Self::X0l0), - consts::DRM_FOURCC_X0L2 => Some(Self::X0l2), - consts::DRM_FOURCC_XBGR1555 => Some(Self::Xbgr1555), - consts::DRM_FOURCC_XBGR16161616F => Some(Self::Xbgr16161616f), - consts::DRM_FOURCC_XBGR2101010 => Some(Self::Xbgr2101010), - consts::DRM_FOURCC_XBGR4444 => Some(Self::Xbgr4444), - consts::DRM_FOURCC_XBGR8888 => Some(Self::Xbgr8888), - consts::DRM_FOURCC_XBGR8888_A8 => Some(Self::Xbgr8888_a8), - consts::DRM_FOURCC_XRGB1555 => Some(Self::Xrgb1555), - consts::DRM_FOURCC_XRGB16161616F => Some(Self::Xrgb16161616f), - consts::DRM_FOURCC_XRGB2101010 => Some(Self::Xrgb2101010), - consts::DRM_FOURCC_XRGB4444 => Some(Self::Xrgb4444), - consts::DRM_FOURCC_XRGB8888 => Some(Self::Xrgb8888), - consts::DRM_FOURCC_XRGB8888_A8 => Some(Self::Xrgb8888_a8), - consts::DRM_FOURCC_XVYU12_16161616 => Some(Self::Xvyu12_16161616), - consts::DRM_FOURCC_XVYU16161616 => Some(Self::Xvyu16161616), - consts::DRM_FOURCC_XVYU2101010 => Some(Self::Xvyu2101010), - consts::DRM_FOURCC_XYUV8888 => Some(Self::Xyuv8888), - consts::DRM_FOURCC_Y0L0 => Some(Self::Y0l0), - consts::DRM_FOURCC_Y0L2 => Some(Self::Y0l2), - consts::DRM_FOURCC_Y210 => Some(Self::Y210), - consts::DRM_FOURCC_Y212 => Some(Self::Y212), - consts::DRM_FOURCC_Y216 => Some(Self::Y216), - consts::DRM_FOURCC_Y410 => Some(Self::Y410), - consts::DRM_FOURCC_Y412 => Some(Self::Y412), - consts::DRM_FOURCC_Y416 => Some(Self::Y416), - consts::DRM_FOURCC_YUV410 => Some(Self::Yuv410), - consts::DRM_FOURCC_YUV411 => Some(Self::Yuv411), - consts::DRM_FOURCC_YUV420 => Some(Self::Yuv420), - consts::DRM_FOURCC_YUV420_10BIT => Some(Self::Yuv420_10bit), - consts::DRM_FOURCC_YUV420_8BIT => Some(Self::Yuv420_8bit), - consts::DRM_FOURCC_YUV422 => Some(Self::Yuv422), - consts::DRM_FOURCC_YUV444 => Some(Self::Yuv444), - consts::DRM_FOURCC_YUYV => Some(Self::Yuyv), - consts::DRM_FOURCC_YVU410 => Some(Self::Yvu410), - consts::DRM_FOURCC_YVU411 => Some(Self::Yvu411), - consts::DRM_FOURCC_YVU420 => Some(Self::Yvu420), - consts::DRM_FOURCC_YVU422 => Some(Self::Yvu422), - consts::DRM_FOURCC_YVU444 => Some(Self::Yvu444), - consts::DRM_FOURCC_YVYU => Some(Self::Yvyu), - _ => None, - } - } -} -#[derive(Debug, Copy, Clone, Eq, PartialEq, Hash)] -#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))] -#[repr(u8)] -pub enum DrmVendor { - Allwinner = consts::DRM_FOURCC_ALLWINNER, - Amd = consts::DRM_FOURCC_AMD, - Amlogic = consts::DRM_FOURCC_AMLOGIC, - Arm = consts::DRM_FOURCC_ARM, - Broadcom = consts::DRM_FOURCC_BROADCOM, - Intel = consts::DRM_FOURCC_INTEL, - Nvidia = consts::DRM_FOURCC_NVIDIA, - Qcom = consts::DRM_FOURCC_QCOM, - Samsung = consts::DRM_FOURCC_SAMSUNG, - Vivante = consts::DRM_FOURCC_VIVANTE, -} -impl DrmVendor { - pub(crate) fn from_u8(n: u8) -> Option { - match n { - consts::DRM_FOURCC_ALLWINNER => Some(Self::Allwinner), - consts::DRM_FOURCC_AMD => Some(Self::Amd), - consts::DRM_FOURCC_AMLOGIC => Some(Self::Amlogic), - consts::DRM_FOURCC_ARM => Some(Self::Arm), - consts::DRM_FOURCC_BROADCOM => Some(Self::Broadcom), - consts::DRM_FOURCC_INTEL => Some(Self::Intel), - consts::DRM_FOURCC_NVIDIA => Some(Self::Nvidia), - consts::DRM_FOURCC_QCOM => Some(Self::Qcom), - consts::DRM_FOURCC_SAMSUNG => Some(Self::Samsung), - consts::DRM_FOURCC_VIVANTE => Some(Self::Vivante), - _ => None, - } - } -} -#[derive(Debug, Copy, Clone)] -#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))] -pub enum DrmModifier { - Allwinner_tiled, - Broadcom_sand128, - Broadcom_sand256, - Broadcom_sand32, - Broadcom_sand64, - Broadcom_uif, - Broadcom_vc4_t_tiled, - Generic_16_16_tile, - Invalid, - Linear, - Nvidia_16bx2_block_eight_gob, - Nvidia_16bx2_block_four_gob, - Nvidia_16bx2_block_one_gob, - Nvidia_16bx2_block_sixteen_gob, - Nvidia_16bx2_block_thirtytwo_gob, - Nvidia_16bx2_block_two_gob, - Nvidia_tegra_tiled, - Qcom_compressed, - Samsung_16_16_tile, - Samsung_64_32_tile, - Vivante_split_super_tiled, - Vivante_split_tiled, - Vivante_super_tiled, - Vivante_tiled, - I915_x_tiled, - I915_y_tiled, - I915_y_tiled_ccs, - I915_y_tiled_gen12_mc_ccs, - I915_y_tiled_gen12_rc_ccs, - Unrecognized(u64), -} -impl DrmModifier { - pub(crate) fn from_u64(n: u64) -> Self { - #[allow(unreachable_patterns)] - match n { - consts::DRM_FOURCC_ALLWINNER_TILED => Self::Allwinner_tiled, - consts::DRM_FOURCC_BROADCOM_SAND128 => Self::Broadcom_sand128, - consts::DRM_FOURCC_BROADCOM_SAND256 => Self::Broadcom_sand256, - consts::DRM_FOURCC_BROADCOM_SAND32 => Self::Broadcom_sand32, - consts::DRM_FOURCC_BROADCOM_SAND64 => Self::Broadcom_sand64, - consts::DRM_FOURCC_BROADCOM_UIF => Self::Broadcom_uif, - consts::DRM_FOURCC_BROADCOM_VC4_T_TILED => Self::Broadcom_vc4_t_tiled, - consts::DRM_FOURCC_GENERIC_16_16_TILE => Self::Generic_16_16_tile, - consts::DRM_FOURCC_INVALID => Self::Invalid, - consts::DRM_FOURCC_LINEAR => Self::Linear, - consts::DRM_FOURCC_NVIDIA_16BX2_BLOCK_EIGHT_GOB => Self::Nvidia_16bx2_block_eight_gob, - consts::DRM_FOURCC_NVIDIA_16BX2_BLOCK_FOUR_GOB => Self::Nvidia_16bx2_block_four_gob, - consts::DRM_FOURCC_NVIDIA_16BX2_BLOCK_ONE_GOB => Self::Nvidia_16bx2_block_one_gob, - consts::DRM_FOURCC_NVIDIA_16BX2_BLOCK_SIXTEEN_GOB => { - Self::Nvidia_16bx2_block_sixteen_gob - } - consts::DRM_FOURCC_NVIDIA_16BX2_BLOCK_THIRTYTWO_GOB => { - Self::Nvidia_16bx2_block_thirtytwo_gob - } - consts::DRM_FOURCC_NVIDIA_16BX2_BLOCK_TWO_GOB => Self::Nvidia_16bx2_block_two_gob, - consts::DRM_FOURCC_NVIDIA_TEGRA_TILED => Self::Nvidia_tegra_tiled, - consts::DRM_FOURCC_QCOM_COMPRESSED => Self::Qcom_compressed, - consts::DRM_FOURCC_SAMSUNG_16_16_TILE => Self::Samsung_16_16_tile, - consts::DRM_FOURCC_SAMSUNG_64_32_TILE => Self::Samsung_64_32_tile, - consts::DRM_FOURCC_VIVANTE_SPLIT_SUPER_TILED => Self::Vivante_split_super_tiled, - consts::DRM_FOURCC_VIVANTE_SPLIT_TILED => Self::Vivante_split_tiled, - consts::DRM_FOURCC_VIVANTE_SUPER_TILED => Self::Vivante_super_tiled, - consts::DRM_FOURCC_VIVANTE_TILED => Self::Vivante_tiled, - consts::DRM_FOURCC_I915_X_TILED => Self::I915_x_tiled, - consts::DRM_FOURCC_I915_Y_TILED => Self::I915_y_tiled, - consts::DRM_FOURCC_I915_Y_TILED_CCS => Self::I915_y_tiled_ccs, - consts::DRM_FOURCC_I915_Y_TILED_GEN12_MC_CCS => Self::I915_y_tiled_gen12_mc_ccs, - consts::DRM_FOURCC_I915_Y_TILED_GEN12_RC_CCS => Self::I915_y_tiled_gen12_rc_ccs, - x => Self::Unrecognized(x), - } - } - pub(crate) fn into_u64(&self) -> u64 { - match self { - Self::Allwinner_tiled => consts::DRM_FOURCC_ALLWINNER_TILED, - Self::Broadcom_sand128 => consts::DRM_FOURCC_BROADCOM_SAND128, - Self::Broadcom_sand256 => consts::DRM_FOURCC_BROADCOM_SAND256, - Self::Broadcom_sand32 => consts::DRM_FOURCC_BROADCOM_SAND32, - Self::Broadcom_sand64 => consts::DRM_FOURCC_BROADCOM_SAND64, - Self::Broadcom_uif => consts::DRM_FOURCC_BROADCOM_UIF, - Self::Broadcom_vc4_t_tiled => consts::DRM_FOURCC_BROADCOM_VC4_T_TILED, - Self::Generic_16_16_tile => consts::DRM_FOURCC_GENERIC_16_16_TILE, - Self::Invalid => consts::DRM_FOURCC_INVALID, - Self::Linear => consts::DRM_FOURCC_LINEAR, - Self::Nvidia_16bx2_block_eight_gob => consts::DRM_FOURCC_NVIDIA_16BX2_BLOCK_EIGHT_GOB, - Self::Nvidia_16bx2_block_four_gob => consts::DRM_FOURCC_NVIDIA_16BX2_BLOCK_FOUR_GOB, - Self::Nvidia_16bx2_block_one_gob => consts::DRM_FOURCC_NVIDIA_16BX2_BLOCK_ONE_GOB, - Self::Nvidia_16bx2_block_sixteen_gob => { - consts::DRM_FOURCC_NVIDIA_16BX2_BLOCK_SIXTEEN_GOB - } - Self::Nvidia_16bx2_block_thirtytwo_gob => { - consts::DRM_FOURCC_NVIDIA_16BX2_BLOCK_THIRTYTWO_GOB - } - Self::Nvidia_16bx2_block_two_gob => consts::DRM_FOURCC_NVIDIA_16BX2_BLOCK_TWO_GOB, - Self::Nvidia_tegra_tiled => consts::DRM_FOURCC_NVIDIA_TEGRA_TILED, - Self::Qcom_compressed => consts::DRM_FOURCC_QCOM_COMPRESSED, - Self::Samsung_16_16_tile => consts::DRM_FOURCC_SAMSUNG_16_16_TILE, - Self::Samsung_64_32_tile => consts::DRM_FOURCC_SAMSUNG_64_32_TILE, - Self::Vivante_split_super_tiled => consts::DRM_FOURCC_VIVANTE_SPLIT_SUPER_TILED, - Self::Vivante_split_tiled => consts::DRM_FOURCC_VIVANTE_SPLIT_TILED, - Self::Vivante_super_tiled => consts::DRM_FOURCC_VIVANTE_SUPER_TILED, - Self::Vivante_tiled => consts::DRM_FOURCC_VIVANTE_TILED, - Self::I915_x_tiled => consts::DRM_FOURCC_I915_X_TILED, - Self::I915_y_tiled => consts::DRM_FOURCC_I915_Y_TILED, - Self::I915_y_tiled_ccs => consts::DRM_FOURCC_I915_Y_TILED_CCS, - Self::I915_y_tiled_gen12_mc_ccs => consts::DRM_FOURCC_I915_Y_TILED_GEN12_MC_CCS, - Self::I915_y_tiled_gen12_rc_ccs => consts::DRM_FOURCC_I915_Y_TILED_GEN12_RC_CCS, - Self::Unrecognized(x) => *x, - } - } -} diff --git a/third_party/rust/drm-fourcc/src/consts.rs b/third_party/rust/drm-fourcc/src/consts.rs deleted file mode 100644 index b46e089e9e5e..000000000000 --- a/third_party/rust/drm-fourcc/src/consts.rs +++ /dev/null @@ -1,149 +0,0 @@ -/* automatically generated by rust-bindgen 0.57.0 */ - -pub type __uint8_t = ::std::os::raw::c_uchar; -pub type __uint32_t = ::std::os::raw::c_uint; -pub type __uint64_t = ::std::os::raw::c_ulong; -pub const DRM_FOURCC_ABGR1555: u32 = 892420673; -pub const DRM_FOURCC_ABGR16161616F: u32 = 1211384385; -pub const DRM_FOURCC_ABGR2101010: u32 = 808665665; -pub const DRM_FOURCC_ABGR4444: u32 = 842089025; -pub const DRM_FOURCC_ABGR8888: u32 = 875708993; -pub const DRM_FOURCC_ARGB1555: u32 = 892424769; -pub const DRM_FOURCC_ARGB16161616F: u32 = 1211388481; -pub const DRM_FOURCC_ARGB2101010: u32 = 808669761; -pub const DRM_FOURCC_ARGB4444: u32 = 842093121; -pub const DRM_FOURCC_ARGB8888: u32 = 875713089; -pub const DRM_FOURCC_AXBXGXRX106106106106: u32 = 808534593; -pub const DRM_FOURCC_AYUV: u32 = 1448433985; -pub const DRM_FOURCC_BGR233: u32 = 944916290; -pub const DRM_FOURCC_BGR565: u32 = 909199170; -pub const DRM_FOURCC_BGR565_A8: u32 = 943797570; -pub const DRM_FOURCC_BGR888: u32 = 875710274; -pub const DRM_FOURCC_BGR888_A8: u32 = 943798338; -pub const DRM_FOURCC_BGRA1010102: u32 = 808665410; -pub const DRM_FOURCC_BGRA4444: u32 = 842088770; -pub const DRM_FOURCC_BGRA5551: u32 = 892420418; -pub const DRM_FOURCC_BGRA8888: u32 = 875708738; -pub const DRM_FOURCC_BGRX1010102: u32 = 808671298; -pub const DRM_FOURCC_BGRX4444: u32 = 842094658; -pub const DRM_FOURCC_BGRX5551: u32 = 892426306; -pub const DRM_FOURCC_BGRX8888: u32 = 875714626; -pub const DRM_FOURCC_BGRX8888_A8: u32 = 943806530; -pub const DRM_FOURCC_BIG_ENDIAN: u32 = 2147483648; -pub const DRM_FOURCC_C8: u32 = 538982467; -pub const DRM_FOURCC_GR1616: u32 = 842224199; -pub const DRM_FOURCC_GR88: u32 = 943215175; -pub const DRM_FOURCC_NV12: u32 = 842094158; -pub const DRM_FOURCC_NV15: u32 = 892425806; -pub const DRM_FOURCC_NV16: u32 = 909203022; -pub const DRM_FOURCC_NV21: u32 = 825382478; -pub const DRM_FOURCC_NV24: u32 = 875714126; -pub const DRM_FOURCC_NV42: u32 = 842290766; -pub const DRM_FOURCC_NV61: u32 = 825644622; -pub const DRM_FOURCC_P010: u32 = 808530000; -pub const DRM_FOURCC_P012: u32 = 842084432; -pub const DRM_FOURCC_P016: u32 = 909193296; -pub const DRM_FOURCC_P210: u32 = 808530512; -pub const DRM_FOURCC_Q401: u32 = 825242705; -pub const DRM_FOURCC_Q410: u32 = 808531025; -pub const DRM_FOURCC_R16: u32 = 540422482; -pub const DRM_FOURCC_R8: u32 = 538982482; -pub const DRM_FOURCC_RG1616: u32 = 842221394; -pub const DRM_FOURCC_RG88: u32 = 943212370; -pub const DRM_FOURCC_RGB332: u32 = 943867730; -pub const DRM_FOURCC_RGB565: u32 = 909199186; -pub const DRM_FOURCC_RGB565_A8: u32 = 943797586; -pub const DRM_FOURCC_RGB888: u32 = 875710290; -pub const DRM_FOURCC_RGB888_A8: u32 = 943798354; -pub const DRM_FOURCC_RGBA1010102: u32 = 808665426; -pub const DRM_FOURCC_RGBA4444: u32 = 842088786; -pub const DRM_FOURCC_RGBA5551: u32 = 892420434; -pub const DRM_FOURCC_RGBA8888: u32 = 875708754; -pub const DRM_FOURCC_RGBX1010102: u32 = 808671314; -pub const DRM_FOURCC_RGBX4444: u32 = 842094674; -pub const DRM_FOURCC_RGBX5551: u32 = 892426322; -pub const DRM_FOURCC_RGBX8888: u32 = 875714642; -pub const DRM_FOURCC_RGBX8888_A8: u32 = 943806546; -pub const DRM_FOURCC_UYVY: u32 = 1498831189; -pub const DRM_FOURCC_VUY101010: u32 = 808670550; -pub const DRM_FOURCC_VUY888: u32 = 875713878; -pub const DRM_FOURCC_VYUY: u32 = 1498765654; -pub const DRM_FOURCC_X0L0: u32 = 810299480; -pub const DRM_FOURCC_X0L2: u32 = 843853912; -pub const DRM_FOURCC_XBGR1555: u32 = 892420696; -pub const DRM_FOURCC_XBGR16161616F: u32 = 1211384408; -pub const DRM_FOURCC_XBGR2101010: u32 = 808665688; -pub const DRM_FOURCC_XBGR4444: u32 = 842089048; -pub const DRM_FOURCC_XBGR8888: u32 = 875709016; -pub const DRM_FOURCC_XBGR8888_A8: u32 = 943800920; -pub const DRM_FOURCC_XRGB1555: u32 = 892424792; -pub const DRM_FOURCC_XRGB16161616F: u32 = 1211388504; -pub const DRM_FOURCC_XRGB2101010: u32 = 808669784; -pub const DRM_FOURCC_XRGB4444: u32 = 842093144; -pub const DRM_FOURCC_XRGB8888: u32 = 875713112; -pub const DRM_FOURCC_XRGB8888_A8: u32 = 943805016; -pub const DRM_FOURCC_XVYU12_16161616: u32 = 909334104; -pub const DRM_FOURCC_XVYU16161616: u32 = 942954072; -pub const DRM_FOURCC_XVYU2101010: u32 = 808670808; -pub const DRM_FOURCC_XYUV8888: u32 = 1448434008; -pub const DRM_FOURCC_Y0L0: u32 = 810299481; -pub const DRM_FOURCC_Y0L2: u32 = 843853913; -pub const DRM_FOURCC_Y210: u32 = 808530521; -pub const DRM_FOURCC_Y212: u32 = 842084953; -pub const DRM_FOURCC_Y216: u32 = 909193817; -pub const DRM_FOURCC_Y410: u32 = 808531033; -pub const DRM_FOURCC_Y412: u32 = 842085465; -pub const DRM_FOURCC_Y416: u32 = 909194329; -pub const DRM_FOURCC_YUV410: u32 = 961959257; -pub const DRM_FOURCC_YUV411: u32 = 825316697; -pub const DRM_FOURCC_YUV420: u32 = 842093913; -pub const DRM_FOURCC_YUV420_10BIT: u32 = 808539481; -pub const DRM_FOURCC_YUV420_8BIT: u32 = 942691673; -pub const DRM_FOURCC_YUV422: u32 = 909202777; -pub const DRM_FOURCC_YUV444: u32 = 875713881; -pub const DRM_FOURCC_YUYV: u32 = 1448695129; -pub const DRM_FOURCC_YVU410: u32 = 961893977; -pub const DRM_FOURCC_YVU411: u32 = 825316953; -pub const DRM_FOURCC_YVU420: u32 = 842094169; -pub const DRM_FOURCC_YVU422: u32 = 909203033; -pub const DRM_FOURCC_YVU444: u32 = 875714137; -pub const DRM_FOURCC_YVYU: u32 = 1431918169; -pub const DRM_FOURCC_ALLWINNER: u8 = 9; -pub const DRM_FOURCC_AMD: u8 = 2; -pub const DRM_FOURCC_AMLOGIC: u8 = 10; -pub const DRM_FOURCC_ARM: u8 = 8; -pub const DRM_FOURCC_BROADCOM: u8 = 7; -pub const DRM_FOURCC_INTEL: u8 = 1; -pub const DRM_FOURCC_NVIDIA: u8 = 3; -pub const DRM_FOURCC_QCOM: u8 = 5; -pub const DRM_FOURCC_SAMSUNG: u8 = 4; -pub const DRM_FOURCC_VIVANTE: u8 = 6; -pub const DRM_FOURCC_ALLWINNER_TILED: u64 = 648518346341351425; -pub const DRM_FOURCC_BROADCOM_SAND128: u64 = 504403158265495556; -pub const DRM_FOURCC_BROADCOM_SAND256: u64 = 504403158265495557; -pub const DRM_FOURCC_BROADCOM_SAND32: u64 = 504403158265495554; -pub const DRM_FOURCC_BROADCOM_SAND64: u64 = 504403158265495555; -pub const DRM_FOURCC_BROADCOM_UIF: u64 = 504403158265495558; -pub const DRM_FOURCC_BROADCOM_VC4_T_TILED: u64 = 504403158265495553; -pub const DRM_FOURCC_GENERIC_16_16_TILE: u64 = 288230376151711746; -pub const DRM_FOURCC_INVALID: u64 = 72057594037927935; -pub const DRM_FOURCC_LINEAR: u64 = 0; -pub const DRM_FOURCC_NVIDIA_16BX2_BLOCK_EIGHT_GOB: u64 = 216172782113783827; -pub const DRM_FOURCC_NVIDIA_16BX2_BLOCK_FOUR_GOB: u64 = 216172782113783826; -pub const DRM_FOURCC_NVIDIA_16BX2_BLOCK_ONE_GOB: u64 = 216172782113783824; -pub const DRM_FOURCC_NVIDIA_16BX2_BLOCK_SIXTEEN_GOB: u64 = 216172782113783828; -pub const DRM_FOURCC_NVIDIA_16BX2_BLOCK_THIRTYTWO_GOB: u64 = 216172782113783829; -pub const DRM_FOURCC_NVIDIA_16BX2_BLOCK_TWO_GOB: u64 = 216172782113783825; -pub const DRM_FOURCC_NVIDIA_TEGRA_TILED: u64 = 216172782113783809; -pub const DRM_FOURCC_QCOM_COMPRESSED: u64 = 360287970189639681; -pub const DRM_FOURCC_SAMSUNG_16_16_TILE: u64 = 288230376151711746; -pub const DRM_FOURCC_SAMSUNG_64_32_TILE: u64 = 288230376151711745; -pub const DRM_FOURCC_VIVANTE_SPLIT_SUPER_TILED: u64 = 432345564227567620; -pub const DRM_FOURCC_VIVANTE_SPLIT_TILED: u64 = 432345564227567619; -pub const DRM_FOURCC_VIVANTE_SUPER_TILED: u64 = 432345564227567618; -pub const DRM_FOURCC_VIVANTE_TILED: u64 = 432345564227567617; -pub const DRM_FOURCC_I915_X_TILED: u64 = 72057594037927937; -pub const DRM_FOURCC_I915_Y_TILED: u64 = 72057594037927938; -pub const DRM_FOURCC_I915_Y_TILED_CCS: u64 = 72057594037927940; -pub const DRM_FOURCC_I915_Y_TILED_GEN12_MC_CCS: u64 = 72057594037927943; -pub const DRM_FOURCC_I915_Y_TILED_GEN12_RC_CCS: u64 = 72057594037927942; diff --git a/third_party/rust/drm-fourcc/src/lib.rs b/third_party/rust/drm-fourcc/src/lib.rs deleted file mode 100644 index 9e9ac8c135f2..000000000000 --- a/third_party/rust/drm-fourcc/src/lib.rs +++ /dev/null @@ -1,370 +0,0 @@ -#![allow(non_camel_case_types)] -#![warn(clippy::cargo)] - -//! [`DrmFourcc`] is an enum representing every pixel format supported by DRM -//! (as of kernel version 5.10.0). -//! -//! A [fourcc][fourcc_wiki] is four bytes of ascii representing some data format. This enum contains -//! every fourcc representing a pixel format supported by [DRM][drm_wiki], the Linux Direct -//! Rendering Manager. The names of pixel formats generally provide clues as to -//! how they work, for more information you may find -//! [this guide][drm_format_guide] helpful. -//! -//! To get the bytes of the fourcc representing the format, cast to `u32`. -//! -//! ``` -//! # use drm_fourcc::DrmFourcc; -//! assert_eq!(DrmFourcc::Xrgb8888 as u32, 875713112); -//! ``` -//! -//! To get the string form of the fourcc, use [`DrmFourcc::string_form`]. -//! -//! ``` -//! # use drm_fourcc::DrmFourcc; -//! assert_eq!(DrmFourcc::Xrgb8888.string_form(), "XR24"); -//! ``` -//! -//! -//! We also provide a type for representing a fourcc/modifier pair -//! -//! ``` -//! # use drm_fourcc::{DrmFormat, DrmFourcc, DrmModifier}; -//! let format = DrmFormat { -//! code: DrmFourcc::Xrgb8888, -//! modifier: DrmModifier::Linear, -//! }; -//! ``` -//! -//! The enums are autogenerated from the [canonical list][canonical] in the Linux source code. -//! -//! ## Features -//! - `serde` - Derive Serialize/Deserialize where it makes sense -//! - `build_bindings` - Re-generate autogenerated code. Useful if you need varients added in a -//! more recent kernel version. -//! -//! [fourcc_wiki]: https://en.wikipedia.org/wiki/FourCC -//! [drm_wiki]: https://en.wikipedia.org/wiki/Direct_Rendering_Managerz -//! [canonical]: https://github.com/torvalds/linux/blame/master/include/uapi/drm/drm_fourcc.h -//! [drm_format_guide]: https://afrantzis.com/pixel-format-guide/drm.html - -use std::convert::TryFrom; -use std::error::Error; -use std::fmt; -use std::fmt::{Debug, Display, Formatter}; -use std::hash::{Hash, Hasher}; - -pub use as_enum::{DrmFourcc, DrmModifier, DrmVendor}; - -mod as_enum; -mod consts; - -#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)] -#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))] -pub struct DrmFormat { - pub code: DrmFourcc, - pub modifier: DrmModifier, -} - -impl DrmFourcc { - /// Get the string representation of the format's fourcc. - pub fn string_form(&self) -> String { - fourcc_string_form(*self as u32).expect("Must be valid fourcc") - } -} - -impl Debug for DrmFourcc { - fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result { - f.debug_tuple("DrmFourcc") - .field(&self.string_form()) - .finish() - } -} - -impl Display for DrmFourcc { - fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result { - Debug::fmt(self, f) - } -} - -impl TryFrom for DrmFourcc { - type Error = UnrecognizedFourcc; - - /// Convert from an u32 - /// - /// ``` - /// # use drm_fourcc::DrmFourcc; - /// # use std::convert::TryFrom; - /// assert_eq!(DrmFourcc::try_from(875710274).unwrap(), DrmFourcc::Bgr888); - /// - /// assert!(DrmFourcc::try_from(0).is_err()); - /// - /// // If the u32 is in the valid format to be a fourcc, you can see its string form - /// assert_eq!(DrmFourcc::try_from(828601953).unwrap_err().string_form(), Some("avc1".to_string())); - /// ``` - fn try_from(value: u32) -> Result { - Self::from_u32(value).ok_or(UnrecognizedFourcc(value)) - } -} - -/// Wraps some u32 that isn't a DRM fourcc we recognize -/// -/// ``` -/// # use drm_fourcc::{DrmFourcc, UnrecognizedFourcc}; -/// # use std::convert::TryFrom; -/// // Get the u32 -/// assert_eq!(UnrecognizedFourcc(42).0, 42); -/// -/// // Get the string form -/// assert_eq!(UnrecognizedFourcc(828601953).string_form(), Some("avc1".to_string())); -/// assert_eq!(UnrecognizedFourcc(0).string_form(), None); -/// ``` -#[derive(Copy, Clone, Eq, PartialEq)] -#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))] -pub struct UnrecognizedFourcc(pub u32); - -impl UnrecognizedFourcc { - /// If the u32 is in a valid format to be a fourcc, get its string form. - pub fn string_form(&self) -> Option { - fourcc_string_form(self.0) - } -} - -impl Debug for UnrecognizedFourcc { - fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result { - let mut debug = &mut f.debug_tuple("UnrecognizedFourcc"); - - if let Some(string_form) = self.string_form() { - debug = debug.field(&string_form); - } - - debug.field(&self.0).finish() - } -} - -impl Display for UnrecognizedFourcc { - fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result { - Debug::fmt(&self, f) - } -} - -impl Error for UnrecognizedFourcc {} - -fn fourcc_string_form(fourcc: u32) -> Option { - let string = String::from_utf8(fourcc.to_le_bytes().to_vec()).ok()?; - - let mut out = String::new(); - - let chars: Vec = string.chars().collect(); - let (start, end) = chars.split_at(2); - - // first two bytes must be characters - for char in start { - if char.is_ascii_alphanumeric() { - out.push(*char); - } else { - return None; - } - } - - // last two are allowed to be null - for char in end { - if *char == '\0' { - out.push(' '); - } else { - out.push(*char); - } - } - - Some(out) -} - -impl TryFrom for DrmVendor { - type Error = UnrecognizedVendor; - - /// Convert from an u8 - /// - /// ``` - /// # use drm_fourcc::DrmVendor; - /// # use std::convert::TryFrom; - /// assert_eq!(DrmVendor::try_from(2).unwrap(), DrmVendor::Amd); - /// - /// assert!(DrmVendor::try_from(0).is_err()); - /// ``` - fn try_from(value: u8) -> Result { - Self::from_u8(value).ok_or(UnrecognizedVendor(value)) - } -} - -/// Wraps some u8 that isn't a DRM vendor we recognize -/// -/// ``` -/// # use drm_fourcc::{DrmVendor, UnrecognizedVendor}; -/// # use std::convert::TryFrom; -/// // Get the u8 -/// assert_eq!(UnrecognizedVendor(42).0, 42); -/// ``` -#[derive(Debug, Copy, Clone, Eq, PartialEq)] -#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))] -pub struct UnrecognizedVendor(pub u8); - -impl Display for UnrecognizedVendor { - fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result { - Debug::fmt(&self, f) - } -} - -impl Error for UnrecognizedVendor {} - -impl From for DrmModifier { - /// Convert from an u64 - /// - /// ``` - /// # use drm_fourcc::DrmModifier; - /// assert_eq!(DrmModifier::from(0), DrmModifier::Linear); - /// ``` - fn from(value: u64) -> Self { - Self::from_u64(value) - } -} - -/// Wraps some u64 that isn't a DRM modifier we recognize -/// -/// ``` -/// # use drm_fourcc::{DrmModifier, UnrecognizedModifier}; -/// # use std::convert::TryFrom; -/// // Get the u64 -/// assert_eq!(UnrecognizedModifier(42).0, 42); -/// ``` -#[derive(Debug, Copy, Clone, Eq, PartialEq)] -#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))] -pub struct UnrecognizedModifier(pub u64); - -impl Display for UnrecognizedModifier { - fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result { - Debug::fmt(&self, f) - } -} - -impl Error for UnrecognizedModifier {} - -impl UnrecognizedModifier { - /// Get the vendor of the unrecognized modifier, if any - /// - /// ``` - /// # use drm_fourcc::{DrmModifier, DrmVendor, UnrecognizedModifier, UnrecognizedVendor}; - /// assert_eq!(UnrecognizedModifier(216172782113783827).vendor(), Ok(Some(DrmVendor::Nvidia))); - /// assert_eq!(UnrecognizedModifier(2).vendor(), Ok(None)); - /// assert_eq!(UnrecognizedModifier(8646911284551352320).vendor(), Err(UnrecognizedVendor(120))); - /// ``` - pub fn vendor(&self) -> Result, UnrecognizedVendor> { - let vendor = (self.0 >> 56) as u8; - if vendor == 0 { - Ok(None) - } else { - DrmVendor::try_from(vendor).map(|x| Some(x)) - } - } -} - -impl Into for DrmModifier { - /// Convert to an u64 - /// - /// ``` - /// # use drm_fourcc::DrmModifier; - /// assert_eq!(0u64, DrmModifier::Linear.into()); - /// ``` - fn into(self) -> u64 { - self.into_u64() - } -} - -impl PartialEq for DrmModifier { - fn eq(&self, other: &Self) -> bool { - self.into_u64() == other.into_u64() - } -} -impl Eq for DrmModifier {} - -impl PartialEq for DrmModifier { - fn eq(&self, other: &u64) -> bool { - &self.into_u64() == other - } -} - -impl Hash for DrmModifier { - fn hash(&self, state: &mut H) { - self.into_u64().hash(state); - } -} - -impl DrmModifier { - /// Get the vendor of the modifier, if any - /// - /// ``` - /// # use drm_fourcc::{DrmModifier, DrmVendor, UnrecognizedVendor}; - /// assert_eq!(DrmModifier::I915_x_tiled.vendor(), Ok(Some(DrmVendor::Intel))); - /// assert_eq!(DrmModifier::Linear.vendor(), Ok(None)); - /// assert_eq!(DrmModifier::Unrecognized(8646911284551352320).vendor(), Err(UnrecognizedVendor(120))); - /// ``` - pub fn vendor(&self) -> Result, UnrecognizedVendor> { - let vendor = (self.into_u64() >> 56) as u8; - if vendor == 0 { - Ok(None) - } else { - DrmVendor::try_from(vendor).map(|x| Some(x)) - } - } -} - -#[cfg(test)] -pub mod tests { - use super::*; - - #[test] - fn a_specific_var_has_correct_value() { - assert_eq!(consts::DRM_FOURCC_AYUV, 1448433985); - } - - #[test] - fn enum_member_casts_to_const() { - assert_eq!( - DrmFourcc::Xrgb8888 as u32, - consts::DRM_FOURCC_XRGB8888 as u32 - ); - } - - #[test] - fn enum_member_has_correct_string_format() { - assert_eq!(DrmFourcc::Xrgb8888.string_form(), "XR24"); - } - - #[test] - fn fourcc_string_form_handles_valid() { - assert_eq!(fourcc_string_form(875713112).unwrap(), "XR24"); - assert_eq!(fourcc_string_form(828601953).unwrap(), "avc1"); - assert_eq!(fourcc_string_form(0x316376).unwrap(), "vc1 "); - } - - #[test] - fn unrecognized_handles_valid_fourcc() { - assert_eq!( - format!("{}", UnrecognizedFourcc(828601953)), - "UnrecognizedFourcc(\"avc1\", 828601953)" - ); - } - - #[test] - fn unrecognized_handles_invalid_fourcc() { - assert_eq!( - format!("{}", UnrecognizedFourcc(0)), - "UnrecognizedFourcc(0)" - ); - } - - #[test] - fn can_clone_result() { - let a = DrmFourcc::try_from(0); - let b = a; - assert_eq!(a, b); - } -} diff --git a/third_party/rust/external-memory/.cargo-checksum.json b/third_party/rust/external-memory/.cargo-checksum.json deleted file mode 100644 index efa7a558c356..000000000000 --- a/third_party/rust/external-memory/.cargo-checksum.json +++ /dev/null @@ -1 +0,0 @@ -{"files":{"Cargo.toml":"3b75de4bf665b60c9fda3f35ca306b056a4717a528b64a857e37fe4905e6cd78","src/fd.rs":"ec0ef055ae91ce42ad48667b09fadffc06ffb24ab79330be62a932d7a501d1ae","src/handle.rs":"f8c0abafee759e6802c73b2366ea0fbabd315bf9258b2399bf20542a5773baca","src/lib.rs":"dc18d38876f40868d3c624af260276b88e42113cdf103a4fbcd47ba1e29ea2e5","src/ptr.rs":"c6492930679f24e4f5fd9ac9221183fdd0f35ecfb1e78ea09ec65c5cd6083241"},"package":"e4dfe8d292b014422776a8c516862d2bff8a81b223a4461dfdc45f3862dc9d39"} \ No newline at end of file diff --git a/third_party/rust/external-memory/Cargo.toml b/third_party/rust/external-memory/Cargo.toml deleted file mode 100644 index a9e55df3ca19..000000000000 --- a/third_party/rust/external-memory/Cargo.toml +++ /dev/null @@ -1,32 +0,0 @@ -# THIS FILE IS AUTOMATICALLY GENERATED BY CARGO -# -# When uploading crates to the registry Cargo will automatically -# "normalize" Cargo.toml files for maximal compatibility -# with all versions of Cargo and also rewrite `path` dependencies -# to registry (e.g., crates.io) dependencies -# -# If you believe there's an error in this file please file an -# issue against the rust-lang/cargo repository. If you're -# editing this file be aware that the upstream Cargo.toml -# will likely look very different (and much more reasonable) - -[package] -edition = "2018" -name = "external-memory" -version = "0.0.1" -authors = ["The Gfx-rs Developers"] -description = "Generic external memory structures used by gfx-rs backends" -homepage = "https://github.com/gfx-rs/gfx" -keywords = ["external-memory"] -categories = ["external-memory"] -license = "MIT OR Apache-2.0" -repository = "https://github.com/gfx-rs/gfx" - -[lib] -name = "external_memory" -[dependencies.bitflags] -version = "1.0" - -[dependencies.drm-fourcc] -version = "2.1.1" -features = ["serde"] diff --git a/third_party/rust/external-memory/src/fd.rs b/third_party/rust/external-memory/src/fd.rs deleted file mode 100644 index 188a92a1adfd..000000000000 --- a/third_party/rust/external-memory/src/fd.rs +++ /dev/null @@ -1,20 +0,0 @@ -#[derive(Debug)] -/// Unix file descriptor. -pub struct Fd(i32); -impl From for Fd { - fn from(fd: i32) -> Self { - Self(fd) - } -} -#[cfg(unix)] -impl std::os::unix::io::AsRawFd for Fd { - fn as_raw_fd(&self) -> std::os::unix::io::RawFd { - self.0 - } -} -impl std::ops::Deref for Fd { - type Target = i32; - fn deref(&self) -> &Self::Target { - &self.0 - } -} diff --git a/third_party/rust/external-memory/src/handle.rs b/third_party/rust/external-memory/src/handle.rs deleted file mode 100644 index f791155eb18d..000000000000 --- a/third_party/rust/external-memory/src/handle.rs +++ /dev/null @@ -1,20 +0,0 @@ -#[derive(Debug)] -/// Windows handle. -pub struct Handle(*mut std::ffi::c_void); -impl From<*mut std::ffi::c_void> for Handle { - fn from(ptr: *mut std::ffi::c_void) -> Self { - Self(ptr) - } -} -#[cfg(windows)] -impl std::os::windows::io::AsRawHandle for Handle { - fn as_raw_handle(&self) -> std::os::windows::raw::HANDLE { - self.0 - } -} -impl std::ops::Deref for Handle { - type Target = *mut std::ffi::c_void; - fn deref(&self) -> &Self::Target { - &self.0 - } -} diff --git a/third_party/rust/external-memory/src/lib.rs b/third_party/rust/external-memory/src/lib.rs deleted file mode 100644 index 80154f7d28a0..000000000000 --- a/third_party/rust/external-memory/src/lib.rs +++ /dev/null @@ -1,686 +0,0 @@ -#[cfg(any(unix, doc))] -mod fd; -#[cfg(any(unix, doc))] -pub use fd::*; - -#[cfg(any(windows, doc))] -mod handle; -#[cfg(any(windows, doc))] -pub use handle::*; - -mod ptr; -pub use ptr::*; - -pub use drm_fourcc::DrmModifier; -use std::ops::Range; - -/// Representation of an os specific memory. -pub enum PlatformMemory { - #[cfg(any(unix, doc))] - /// Unix file descriptor. - Fd(Fd), - #[cfg(any(windows, doc))] - /// Windows handle. - Handle(Handle), - /// Host pointer. - Ptr(Ptr), -} -impl PlatformMemory { - #[cfg(any(unix, doc))] - pub fn fd(&self)->Option<&Fd> { - match self { - Self::Fd(fd)=>Some(fd), - #[cfg(windows)] - Self::Handle(_)=>None, - Self::Ptr(_)=>None, - } - } - #[cfg(any(windows, doc))] - pub fn handle(&self)->Option<&Handle> { - match self { - Self::Handle(handle)=>Some(handle), - #[cfg(unix)] - Self::Fd(_)=>None, - Self::Ptr(_)=>None, - } - } - pub fn ptr(&self)->Option<&Ptr> { - match self { - Self::Ptr(ptr)=>Some(ptr), - #[cfg(unix)] - Self::Fd(_)=>None, - #[cfg(windows)] - Self::Handle(_)=>None - } - } -} - -#[cfg(any(unix, doc))] -impl std::convert::TryInto for PlatformMemory{ - type Error = &'static str; - fn try_into(self) -> Result{ - match self { - Self::Fd(fd)=>Ok(fd), - #[cfg(windows)] - Self::Handle(_)=>Err("PlatformMemory does not contain an fd"), - Self::Ptr(_)=>Err("PlatformMemory does not contain an fd"), - } - } -} - -#[cfg(any(windows, doc))] -impl std::convert::TryInto for PlatformMemory{ - type Error = &'static str; - fn try_into(self) -> Result{ - match self { - Self::Handle(handle)=>Ok(handle), - #[cfg(unix)] - Self::Fd(_)=>Err("PlatformMemory does not contain an handle"), - Self::Ptr(_)=>Err("PlatformMemory does not contain an handle"), - } - } -} - - -impl std::convert::TryInto for PlatformMemory{ - type Error = &'static str; - fn try_into(self) -> Result{ - match self { - Self::Ptr(ptr)=>Ok(ptr), - #[cfg(unix)] - Self::Fd(_)=>Err("PlatformMemory does not contain a ptr"), - #[cfg(windows)] - Self::Handle(_)=>Err("PlatformMemory does not contain a ptr") - } - } -} - -#[cfg(any(unix,docs))] -impl From for PlatformMemory { - fn from(fd: Fd) -> Self { - Self::Fd(fd) - } -} - -#[cfg(any(windows,doc))] -impl From for PlatformMemory { - fn from(handle: Handle) -> Self { - Self::Handle(handle) - } -} - -impl From for PlatformMemory { - fn from(ptr: Ptr) -> Self { - Self::Ptr(ptr) - } -} - -/// Representation of os specific memory types. -pub enum PlatformMemoryType { - #[cfg(any(unix, doc))] - Fd, - #[cfg(any(windows, doc))] - Handle, - Ptr, -} - - -bitflags::bitflags!( - /// External memory type flags. - #[cfg_attr(feature = "serde", derive(Serialize, Deserialize))] - pub struct ExternalMemoryTypeFlags: u32 { - #[cfg(any(unix,doc))] - /// This is supported on Unix only. - /// Specifies a POSIX file descriptor handle that has only limited valid usage outside of Vulkan and other compatible APIs. - /// It must be compatible with the POSIX system calls dup, dup2, close, and the non-standard system call dup3. - /// Additionally, it must be transportable over a socket using an SCM_RIGHTS control message. - /// It owns a reference to the underlying memory resource represented by its memory object. - const OPAQUE_FD = 0x00000001; - #[cfg(any(windows,doc))] - /// This is supported on Windows only. - /// Specifies an NT handle that has only limited valid usage outside of Vulkan and other compatible APIs. - /// It must be compatible with the functions DuplicateHandle, CloseHandle, CompareObjectHandles, GetHandleInformation, and SetHandleInformation. - /// It owns a reference to the underlying memory resource represented by its memory object. - const OPAQUE_WIN32 = 0x00000002; - #[cfg(any(windows,doc))] - /// This is supported on Windows only. - /// Specifies a global share handle that has only limited valid usage outside of Vulkan and other compatible APIs. - /// It is not compatible with any native APIs. - /// It does not own a reference to the underlying memory resource represented by its memory object, and will therefore become invalid when all the memory objects with it are destroyed. - const OPAQUE_WIN32_KMT = 0x00000004; - #[cfg(any(windows,doc))] - /// This is supported on Windows only. - /// Specifies an NT handle returned by IDXGIResource1::CreateSharedHandle referring to a Direct3D 10 or 11 texture resource. - /// It owns a reference to the memory used by the Direct3D resource. - const D3D11_TEXTURE = 0x00000008; - #[cfg(any(windows,doc))] - /// This is supported on Windows only. - /// Specifies a global share handle returned by IDXGIResource::GetSharedHandle referring to a Direct3D 10 or 11 texture resource. - /// It does not own a reference to the underlying Direct3D resource, and will therefore become invalid when all the memory objects and Direct3D resources associated with it are destroyed. - const D3D11_TEXTURE_KMT = 0x00000010; - #[cfg(any(windows,doc))] - /// This is supported on Windows only. - /// Specifies an NT handle returned by ID3D12Device::CreateSharedHandle referring to a Direct3D 12 heap resource. - /// It owns a reference to the resources used by the Direct3D heap. - const D3D12_HEAP = 0x00000020; - #[cfg(any(windows,doc))] - /// This is supported on Windows only. - /// Specifies an NT handle returned by ID3D12Device::CreateSharedHandle referring to a Direct3D 12 committed resource. - /// It owns a reference to the memory used by the Direct3D resource. - const D3D12_RESOURCE = 0x00000040; - #[cfg(any(target_os = "linux",target_os = "android",doc))] - /// This is supported on Linux or Android only. - /// Is a file descriptor for a Linux dma_buf. - /// It owns a reference to the underlying memory resource represented by its memory object. - const DMA_BUF = 0x00000200; - #[cfg(any(target_os = "android",doc))] - /// This is supported on Android only. - /// Specifies an AHardwareBuffer object defined by the Android NDK. See Android Hardware Buffers for more details of this handle type. - const ANDROID_HARDWARE_BUFFER = 0x00000400; - /// Specifies a host pointer returned by a host memory allocation command. - /// It does not own a reference to the underlying memory resource, and will therefore become invalid if the host memory is freed. - const HOST_ALLOCATION = 0x00000080; - /// Specifies a host pointer to host mapped foreign memory. - /// It does not own a reference to the underlying memory resource, and will therefore become invalid if the foreign memory is unmapped or otherwise becomes no longer available. - const HOST_MAPPED_FOREIGN_MEMORY = 0x00000100; - } -); - -impl From for ExternalMemoryTypeFlags { - fn from(external_memory_type: ExternalMemoryType) -> Self { - match external_memory_type { - #[cfg(unix)] - ExternalMemoryType::OpaqueFd => Self::OPAQUE_FD, - #[cfg(windows)] - ExternalMemoryType::OpaqueWin32 => Self::OPAQUE_WIN32, - #[cfg(windows)] - ExternalMemoryType::OpaqueWin32Kmt => Self::OPAQUE_WIN32_KMT, - #[cfg(windows)] - ExternalMemoryType::D3D11Texture => Self::D3D11_TEXTURE, - #[cfg(windows)] - ExternalMemoryType::D3D11TextureKmt => Self::D3D11_TEXTURE_KMT, - #[cfg(windows)] - ExternalMemoryType::D3D12Heap => Self::D3D12_HEAP, - #[cfg(windows)] - ExternalMemoryType::D3D12Resource => Self::D3D12_RESOURCE, - #[cfg(any(target_os = "linux", target_os = "android", doc))] - ExternalMemoryType::DmaBuf => Self::DMA_BUF, - #[cfg(target_os = "android")] - ExternalMemoryType::AndroidHardwareBuffer => Self::ANDROID_HARDWARE_BUFFER, - ExternalMemoryType::HostAllocation => Self::HOST_ALLOCATION, - ExternalMemoryType::HostMappedForeignMemory => Self::HOST_MAPPED_FOREIGN_MEMORY, - } - } -} - -impl From for ExternalMemoryTypeFlags { - fn from(external_memory_type: ExternalImageMemoryType) -> Self { - match external_memory_type { - #[cfg(unix)] - ExternalImageMemoryType::OpaqueFd => Self::OPAQUE_FD, - #[cfg(windows)] - ExternalImageMemoryType::OpaqueWin32 => Self::OPAQUE_WIN32, - #[cfg(windows)] - ExternalImageMemoryType::OpaqueWin32Kmt => Self::OPAQUE_WIN32_KMT, - #[cfg(windows)] - ExternalImageMemoryType::D3D11Texture => Self::D3D11_TEXTURE, - #[cfg(windows)] - ExternalImageMemoryType::D3D11TextureKmt => Self::D3D11_TEXTURE_KMT, - #[cfg(windows)] - ExternalImageMemoryType::D3D12Heap => Self::D3D12_HEAP, - #[cfg(windows)] - ExternalImageMemoryType::D3D12Resource => Self::D3D12_RESOURCE, - #[cfg(any(target_os = "linux", target_os = "android", doc))] - ExternalImageMemoryType::DmaBuf(_) => Self::DMA_BUF, - #[cfg(target_os = "android")] - ExternalImageMemoryType::AndroidHardwareBuffer => Self::ANDROID_HARDWARE_BUFFER, - ExternalImageMemoryType::HostAllocation => Self::HOST_ALLOCATION, - ExternalImageMemoryType::HostMappedForeignMemory => Self::HOST_MAPPED_FOREIGN_MEMORY, - } - } -} - -/// External memory types. -#[derive(Clone, Copy, Debug, PartialEq)] -pub enum ExternalMemoryType { - #[cfg(any(unix, doc))] - /// This is supported on Unix only. - /// Same as [ExternalMemoryTypeFlags::OPAQUE_FD][ExternalMemoryTypeFlags::OPAQUE_FD]. - OpaqueFd, - #[cfg(any(windows, doc))] - /// This is supported on Windows only. - /// Same as [ExternalMemoryTypeFlags::OPAQUE_WIN32][ExternalMemoryTypeFlags::OPAQUE_WIN32]. - OpaqueWin32, - #[cfg(any(windows, doc))] - /// This is supported on Windows only. - /// Same as [ExternalMemoryTypeFlags::OPAQUE_WIN32_KMT][ExternalMemoryTypeFlags::OPAQUE_WIN32_KMT]. - OpaqueWin32Kmt, - #[cfg(any(windows, doc))] - /// This is supported on Windows only. - /// Same as [ExternalMemoryTypeFlags::D3D11_TEXTURE][ExternalMemoryTypeFlags::D3D11_TEXTURE]. - D3D11Texture, - #[cfg(any(windows, doc))] - /// This is supported on Windows only. - /// Same as [ExternalMemoryTypeFlags::D3D11_TEXTURE_KMT][ExternalMemoryTypeFlags::D3D11_TEXTURE_KMT]. - D3D11TextureKmt, - #[cfg(any(windows, doc))] - /// This is supported on Windows only. - /// Same as [ExternalMemoryTypeFlags::D3D12_HEAP][ExternalMemoryTypeFlags::D3D12_HEAP]. - D3D12Heap, - #[cfg(any(windows, doc))] - /// This is supported on Windows only. - /// Same as [ExternalMemoryTypeFlags::D3D12_RESOURCE][ExternalMemoryTypeFlags::D3D12_RESOURCE]. - D3D12Resource, - #[cfg(any(target_os = "linux", target_os = "android", doc))] - /// This is supported on Linux or Android only. - /// Same as [ExternalMemoryTypeFlags::DMA_BUF][ExternalMemoryTypeFlags::DMA_BUF]. - DmaBuf, - #[cfg(any(target_os = "android", doc))] - /// This is supported on Android only. - /// Same as [ExternalMemoryTypeFlags::ANDROID_HARDWARE_BUFFER][ExternalMemoryTypeFlags::ANDROID_HARDWARE_BUFFER]. - AndroidHardwareBuffer, - /// Same as [ExternalMemoryTypeFlags::HOST_ALLOCATION][ExternalMemoryTypeFlags::HOST_ALLOCATION]. - HostAllocation, - /// Same as [ExternalMemoryTypeFlags::HOST_MAPPED_FOREIGN_MEMORY][ExternalMemoryTypeFlags::HOST_MAPPED_FOREIGN_MEMORY]. - HostMappedForeignMemory, -} - -impl Into for ExternalMemoryType { - fn into(self) -> PlatformMemoryType { - match self { - #[cfg(unix)] - ExternalMemoryType::OpaqueFd => PlatformMemoryType::Fd, - #[cfg(windows)] - ExternalMemoryType::OpaqueWin32 => PlatformMemoryType::Handle, - #[cfg(windows)] - ExternalMemoryType::OpaqueWin32Kmt => PlatformMemoryType::Handle, - #[cfg(windows)] - ExternalMemoryType::D3D11Texture => PlatformMemoryType::Handle, - #[cfg(windows)] - ExternalMemoryType::D3D11TextureKmt => PlatformMemoryType::Handle, - #[cfg(windows)] - ExternalMemoryType::D3D12Heap => PlatformMemoryType::Handle, - #[cfg(windows)] - ExternalMemoryType::D3D12Resource => PlatformMemoryType::Handle, - #[cfg(any(target_os = "linux", target_os = "android", doc))] - ExternalMemoryType::DmaBuf => PlatformMemoryType::Fd, - #[cfg(any(target_os = "android", doc))] - ExternalMemoryType::AndroidHardwareBuffer => PlatformMemoryType::Fd, - ExternalMemoryType::HostAllocation => PlatformMemoryType::Ptr, - ExternalMemoryType::HostMappedForeignMemory => PlatformMemoryType::Ptr, - } - } -} - -/// Representation of an external memory for buffer creation. -#[derive(Debug)] -pub enum ExternalBufferMemory { - #[cfg(unix)] - /// This is supported on Unix only. - /// Same as [ExternalMemoryTypeFlags::OPAQUE_FD][ExternalMemoryTypeFlags::OPAQUE_FD] while holding a [Fd][Fd]. - OpaqueFd(Fd), - #[cfg(windows)] - /// This is supported on Windows only. - /// Same as [ExternalMemoryTypeFlags::OPAQUE_WIN32][ExternalMemoryTypeFlags::OPAQUE_WIN32] while holding a [Handle][Handle]. - OpaqueWin32(Handle), - #[cfg(windows)] - /// This is supported on Windows only. - /// Same as [ExternalMemoryTypeFlags::OPAQUE_WIN32_KMT][ExternalMemoryTypeFlags::OPAQUE_WIN32_KMT] while holding a [Handle][Handle]. - OpaqueWin32Kmt(Handle), - #[cfg(windows)] - /// This is supported on Windows only. - /// Same as [ExternalMemoryTypeFlags::D3D11_TEXTURE][ExternalMemoryTypeFlags::D3D11_TEXTURE] while holding a [Handle][Handle]. - D3D11Texture(Handle), - #[cfg(windows)] - /// This is supported on Windows only. - /// Same as [ExternalMemoryTypeFlags::D3D11_TEXTURE_KMT][ExternalMemoryTypeFlags::D3D11_TEXTURE_KMT] while holding a [Handle][Handle]. - D3D11TextureKmt(Handle), - #[cfg(windows)] - /// This is supported on Windows only. - /// Same as [ExternalMemoryTypeFlags::D3D12_HEAP][ExternalMemoryTypeFlags::D3D12_HEAP] while holding a [Handle][Handle]. - D3D12Heap(Handle), - #[cfg(windows)] - /// This is supported on Windows only. - /// Same as [ExternalMemoryTypeFlags::D3D12_RESOURCE][ExternalMemoryTypeFlags::D3D12_RESOURCE] while holding a [Handle][Handle]. - D3D12Resource(Handle), - #[cfg(any(target_os = "linux", target_os = "android"))] - /// This is supported on Linux or Android only. - /// Same as [ExternalMemoryTypeFlags::DMA_BUF][ExternalMemoryTypeFlags::DMA_BUF] while holding a [Fd][Fd]. - DmaBuf(Fd), - #[cfg(any(target_os = "android"))] - /// This is supported on Android only. - /// Same as [ExternalMemoryTypeFlags::ANDROID_HARDWARE_BUFFER][ExternalMemoryTypeFlags::ANDROID_HARDWARE_BUFFER] while holding a [Fd][Fd]. - AndroidHardwareBuffer(Fd), - /// Same as [ExternalMemoryTypeFlags::HOST_ALLOCATION][ExternalMemoryTypeFlags::HOST_ALLOCATION] while holding a [Ptr][Ptr]. - HostAllocation(Ptr), - /// Same as [ExternalMemoryTypeFlags::HOST_MAPPED_FOREIGN_MEMORY][ExternalMemoryTypeFlags::HOST_MAPPED_FOREIGN_MEMORY] while holding a [Ptr][Ptr]. - HostMappedForeignMemory(Ptr), -} -impl ExternalBufferMemory { - /// Get the [ExternalMemoryType][ExternalMemoryType] from this enum. - pub fn external_memory_type(&self) -> ExternalMemoryType { - match self { - #[cfg(unix)] - Self::OpaqueFd(_) => ExternalMemoryType::OpaqueFd, - #[cfg(windows)] - Self::OpaqueWin32(_) => ExternalMemoryType::OpaqueWin32, - #[cfg(windows)] - Self::OpaqueWin32Kmt(_) => ExternalMemoryType::OpaqueWin32Kmt, - #[cfg(windows)] - Self::D3D11Texture(_) => ExternalMemoryType::D3D11Texture, - #[cfg(windows)] - Self::D3D11TextureKmt(_) => ExternalMemoryType::D3D11TextureKmt, - #[cfg(windows)] - Self::D3D12Heap(_) => ExternalMemoryType::D3D12Heap, - #[cfg(windows)] - Self::D3D12Resource(_) => ExternalMemoryType::D3D12Resource, - #[cfg(any(target_os = "linux", target_os = "android"))] - Self::DmaBuf(_) => ExternalMemoryType::DmaBuf, - #[cfg(target_os = "android")] - Self::AndroidHardwareBuffer(_) => ExternalMemoryType::AndroidHardwareBuffer, - Self::HostAllocation(_) => ExternalMemoryType::HostAllocation, - Self::HostMappedForeignMemory(_) => ExternalMemoryType::HostMappedForeignMemory, - } - } - - /// Get the [PlatformMemoryType][PlatformMemoryType] from this enum. - pub fn platform_memory_type(&self) -> PlatformMemoryType { - match self { - #[cfg(unix)] - Self::OpaqueFd(_) => PlatformMemoryType::Fd, - #[cfg(windows)] - Self::OpaqueWin32(_) => PlatformMemoryType::Handle, - #[cfg(windows)] - Self::OpaqueWin32Kmt(_) => PlatformMemoryType::Handle, - #[cfg(windows)] - Self::D3D11Texture(_) => PlatformMemoryType::Handle, - #[cfg(windows)] - Self::D3D11TextureKmt(_) => PlatformMemoryType::Handle, - #[cfg(windows)] - Self::D3D12Heap(_) => PlatformMemoryType::Handle, - #[cfg(windows)] - Self::D3D12Resource(_) => PlatformMemoryType::Handle, - #[cfg(any(target_os = "linux", target_os = "android"))] - Self::DmaBuf(_) => PlatformMemoryType::Fd, - #[cfg(target_os = "android")] - Self::AndroidHardwareBuffer(_) => PlatformMemoryType::Fd, - Self::HostAllocation(_) => PlatformMemoryType::Ptr, - Self::HostMappedForeignMemory(_) => PlatformMemoryType::Ptr, - } - } - - #[cfg(unix)] - /// Get the associated unix file descriptor as ([Fd][Fd]). - pub fn fd(&self) -> Option<&Fd> { - match self { - Self::OpaqueFd(fd) => Some(fd), - #[cfg(any(target_os = "linux", target_os = "android"))] - Self::DmaBuf(fd) => Some(fd), - #[cfg(target_os = "android")] - Self::AndroidHardwareBuffer(fd) => Some(fd), - _ => None, - } - } - #[cfg(windows)] - /// Get the associated windows handle as ([Handle][Handle]). - pub fn handle(&self) -> Option<&Handle> { - match self { - Self::OpaqueWin32(handle) => Some(handle), - Self::OpaqueWin32Kmt(handle) => Some(handle), - Self::D3D11Texture(handle) => Some(handle), - Self::D3D11TextureKmt(handle) => Some(handle), - Self::D3D12Heap(handle) => Some(handle), - Self::D3D12Resource(handle) => Some(handle), - _ => None, - } - } - - /// Get the associated host pointer as ([Ptr][Ptr]). - pub fn ptr(&self) -> Option<&Ptr> { - match self { - Self::HostAllocation(ptr) => Some(ptr), - Self::HostMappedForeignMemory(ptr) => Some(ptr), - // Without this on non unix or windows platform, this will trigger error for unreachable pattern - #[allow(unreachable_patterns)] - _ => None, - } - } -} - -/// Representation of an external memory type for buffers. -pub type ExternalBufferMemoryType = ExternalMemoryType; - - -/// Representation of an external memory for image creation. -#[derive(Debug)] -pub enum ExternalImageMemory { - #[cfg(unix)] - /// This is supported on Unix only. - /// Same as [ExternalMemoryTypeFlags::OPAQUE_FD][ExternalMemoryTypeFlags::OPAQUE_FD] while holding a [Fd][Fd]. - OpaqueFd(Fd), - #[cfg(windows)] - /// This is supported on Windows only. - /// Same as [ExternalMemoryTypeFlags::OPAQUE_WIN32][ExternalMemoryTypeFlags::OPAQUE_WIN32] while holding a [Handle][Handle]. - OpaqueWin32(Handle), - #[cfg(windows)] - /// This is supported on Windows only. - /// Same as [ExternalMemoryTypeFlags::OPAQUE_WIN32_KMT][ExternalMemoryTypeFlags::OPAQUE_WIN32_KMT] while holding a [Handle][Handle]. - OpaqueWin32Kmt(Handle), - #[cfg(windows)] - /// This is supported on Windows only. - /// Same as [ExternalMemoryTypeFlags::D3D11_TEXTURE][ExternalMemoryTypeFlags::D3D11_TEXTURE] while holding a [Handle][Handle]. - D3D11Texture(Handle), - #[cfg(windows)] - /// This is supported on Windows only. - /// Same as [ExternalMemoryTypeFlags::D3D11_TEXTURE_KMT][ExternalMemoryTypeFlags::D3D11_TEXTURE_KMT] while holding a [Handle][Handle]. - D3D11TextureKmt(Handle), - #[cfg(windows)] - /// This is supported on Windows only. - /// Same as [ExternalMemoryTypeFlags::D3D12_HEAP][ExternalMemoryTypeFlags::D3D12_HEAP] while holding a [Handle][Handle]. - D3D12Heap(Handle), - #[cfg(windows)] - /// This is supported on Windows only. - /// Same as [ExternalMemoryTypeFlags::D3D12_RESOURCE][ExternalMemoryTypeFlags::D3D12_RESOURCE] while holding a [Handle][Handle]. - D3D12Resource(Handle), - #[cfg(any(target_os = "linux", target_os = "android"))] - /// This is supported on Linux or Android only. - /// Same as [ExternalMemoryTypeFlags::DMA_BUF][ExternalMemoryTypeFlags::DMA_BUF] while holding a [Fd][Fd]. - DmaBuf(Fd, Option), - #[cfg(any(target_os = "android"))] - /// This is supported on Android only. - /// Same as [ExternalMemoryTypeFlags::ANDROID_HARDWARE_BUFFER][ExternalMemoryTypeFlags::ANDROID_HARDWARE_BUFFER] while holding a [Fd][Fd]. - AndroidHardwareBuffer(Fd), - /// Same as [ExternalMemoryTypeFlags::HOST_ALLOCATION][ExternalMemoryTypeFlags::HOST_ALLOCATION] while holding a [Ptr][Ptr]. - HostAllocation(Ptr), - /// Same as [ExternalMemoryTypeFlags::HOST_MAPPED_FOREIGN_MEMORY][ExternalMemoryTypeFlags::HOST_MAPPED_FOREIGN_MEMORY] while holding a [Ptr][Ptr]. - HostMappedForeignMemory(Ptr), -} -impl ExternalImageMemory { - /// Get the [ExternalMemoryType][ExternalMemoryType] from this enum. - pub fn external_memory_type(&self) -> ExternalMemoryType { - match self { - #[cfg(unix)] - Self::OpaqueFd(_) => ExternalMemoryType::OpaqueFd, - #[cfg(windows)] - Self::OpaqueWin32(_) => ExternalMemoryType::OpaqueWin32, - #[cfg(windows)] - Self::OpaqueWin32Kmt(_) => ExternalMemoryType::OpaqueWin32Kmt, - #[cfg(windows)] - Self::D3D11Texture(_) => ExternalMemoryType::D3D11Texture, - #[cfg(windows)] - Self::D3D11TextureKmt(_) => ExternalMemoryType::D3D11TextureKmt, - #[cfg(windows)] - Self::D3D12Heap(_) => ExternalMemoryType::D3D12Heap, - #[cfg(windows)] - Self::D3D12Resource(_) => ExternalMemoryType::D3D12Resource, - #[cfg(any(target_os = "linux", target_os = "android"))] - Self::DmaBuf(_, _) => ExternalMemoryType::DmaBuf, - #[cfg(target_os = "android")] - Self::AndroidHardwareBuffer(_) => ExternalMemoryType::AndroidHardwareBuffer, - Self::HostAllocation(_) => ExternalMemoryType::HostAllocation, - Self::HostMappedForeignMemory(_) => ExternalMemoryType::HostMappedForeignMemory, - } - } - - /// Get the [PlatformMemoryType][PlatformMemoryType] from this enum. - pub fn platform_memory_type(&self) -> PlatformMemoryType { - match self { - #[cfg(unix)] - Self::OpaqueFd(_) => PlatformMemoryType::Fd, - #[cfg(windows)] - Self::OpaqueWin32(_) => PlatformMemoryType::Handle, - #[cfg(windows)] - Self::OpaqueWin32Kmt(_) => PlatformMemoryType::Handle, - #[cfg(windows)] - Self::D3D11Texture(_) => PlatformMemoryType::Handle, - #[cfg(windows)] - Self::D3D11TextureKmt(_) => PlatformMemoryType::Handle, - #[cfg(windows)] - Self::D3D12Heap(_) => PlatformMemoryType::Handle, - #[cfg(windows)] - Self::D3D12Resource(_) => PlatformMemoryType::Handle, - #[cfg(any(target_os = "linux", target_os = "android"))] - Self::DmaBuf(_, _) => PlatformMemoryType::Fd, - #[cfg(target_os = "android")] - Self::AndroidHardwareBuffer(_) => PlatformMemoryType::Fd, - Self::HostAllocation(_) => PlatformMemoryType::Ptr, - Self::HostMappedForeignMemory(_) => PlatformMemoryType::Ptr, - } - } - - #[cfg(unix)] - /// Get the associated unix file descriptor as ([Fd][Fd]). - pub fn fd(&self) -> Option<&Fd> { - match self { - Self::OpaqueFd(fd) => Some(fd), - #[cfg(any(target_os = "linux", target_os = "android"))] - Self::DmaBuf(fd, _drm_format_properties) => Some(fd), - #[cfg(target_os = "android")] - Self::AndroidHardwareBuffer(fd) => Some(fd), - _ => None, - } - } - - #[cfg(windows)] - /// Get the associated windows handle as ([Handle][Handle]). - pub fn handle(&self) -> Option<&Handle> { - match self { - Self::OpaqueWin32(handle) => Some(handle), - Self::OpaqueWin32Kmt(handle) => Some(handle), - Self::D3D11Texture(handle) => Some(handle), - Self::D3D11TextureKmt(handle) => Some(handle), - Self::D3D12Heap(handle) => Some(handle), - Self::D3D12Resource(handle) => Some(handle), - _ => None, - } - } - - /// Get the associated host pointer as ([Ptr][Ptr]). - pub fn ptr(&self) -> Option<&Ptr> { - match self { - Self::HostAllocation(ptr) => Some(ptr), - Self::HostMappedForeignMemory(ptr) => Some(ptr), - // Without this on non unix or windows platform, this will trigger error for unreachable pattern - #[allow(unreachable_patterns)] - _ => None, - } - } -} - - -/// Representation of an external memory type for images. -#[derive(Clone, Debug, PartialEq)] -pub enum ExternalImageMemoryType { - #[cfg(any(unix, doc))] - /// This is supported on Unix only. - /// Same as [ExternalMemoryTypeFlags::OPAQUE_FD][ExternalMemoryTypeFlags::OPAQUE_FD] - OpaqueFd, - #[cfg(any(windows, doc))] - /// This is supported on Windows only. - /// Same as [ExternalMemoryTypeFlags::OPAQUE_WIN32][ExternalMemoryTypeFlags::OPAQUE_WIN32] - OpaqueWin32, - #[cfg(any(windows, doc))] - /// This is supported on Windows only. - /// Same as [ExternalMemoryTypeFlags::OPAQUE_WIN32_KMT][ExternalMemoryTypeFlags::OPAQUE_WIN32_KMT] - OpaqueWin32Kmt, - #[cfg(any(windows, doc))] - /// This is supported on Windows only. - /// Same as [ExternalMemoryTypeFlags::D3D11_TEXTURE][ExternalMemoryTypeFlags::D3D11_TEXTURE] - D3D11Texture, - #[cfg(any(windows, doc))] - /// This is supported on Windows only. - /// Same as [ExternalMemoryTypeFlags::D3D11_TEXTURE_KMT][ExternalMemoryTypeFlags::D3D11_TEXTURE_KMT] - D3D11TextureKmt, - #[cfg(any(windows, doc))] - /// This is supported on Windows only. - /// Same as [ExternalMemoryTypeFlags::D3D12_HEAP][ExternalMemoryTypeFlags::D3D12_HEAP] - D3D12Heap, - #[cfg(any(windows, doc))] - /// This is supported on Windows only. - /// Same as [ExternalMemoryTypeFlags::D3D12_RESOURCE][ExternalMemoryTypeFlags::D3D12_RESOURCE] - D3D12Resource, - #[cfg(any(target_os = "linux", target_os = "android", doc))] - /// This is supported on Linux or Android only. - /// Same as [ExternalMemoryTypeFlags::DMA_BUF][ExternalMemoryTypeFlags::DMA_BUF] - DmaBuf(Vec), - #[cfg(any(target_os = "android", doc))] - /// This is supported on Android only. - /// Same as [ExternalMemoryTypeFlags::ANDROID_HARDWARE_BUFFER][ExternalMemoryTypeFlags::ANDROID_HARDWARE_BUFFER] - AndroidHardwareBuffer, - /// Same as [ExternalMemoryTypeFlags::HOST_ALLOCATION][ExternalMemoryTypeFlags::HOST_ALLOCATION] - HostAllocation, - /// Same as [ExternalMemoryTypeFlags::HOST_MAPPED_FOREIGN_MEMORY][ExternalMemoryTypeFlags::HOST_MAPPED_FOREIGN_MEMORY] - HostMappedForeignMemory, -} -impl ExternalImageMemoryType { - /// Get the [ExternalMemoryType][ExternalMemoryType] from this enum. - pub fn external_memory_type(&self) -> ExternalMemoryType { - match self { - #[cfg(unix)] - Self::OpaqueFd => ExternalMemoryType::OpaqueFd, - #[cfg(windows)] - Self::OpaqueWin32 => ExternalMemoryType::OpaqueWin32, - #[cfg(windows)] - Self::OpaqueWin32Kmt => ExternalMemoryType::OpaqueWin32Kmt, - #[cfg(windows)] - Self::D3D11Texture => ExternalMemoryType::D3D11Texture, - #[cfg(windows)] - Self::D3D11TextureKmt => ExternalMemoryType::D3D11TextureKmt, - #[cfg(windows)] - Self::D3D12Heap => ExternalMemoryType::D3D12Heap, - #[cfg(windows)] - Self::D3D12Resource => ExternalMemoryType::D3D12Resource, - #[cfg(any(target_os = "linux", target_os = "android"))] - Self::DmaBuf(_) => ExternalMemoryType::DmaBuf, - #[cfg(target_os = "android")] - Self::AndroidHardwareBuffer => ExternalMemoryType::AndroidHardwareBuffer, - Self::HostAllocation => ExternalMemoryType::HostAllocation, - Self::HostMappedForeignMemory => ExternalMemoryType::HostMappedForeignMemory, - } - } -} - -type Offset = u64; - -/// Footprint of a plane layout in memory. -#[derive(Debug, Clone, PartialEq, Eq, Hash)] -#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))] -pub struct PlaneLayout { - /// Byte slice occupied by the subresource. - pub slice: Range, - /// Byte distance between rows. - pub row_pitch: Offset, - /// Byte distance between array layers. - pub array_pitch: Offset, - /// Byte distance between depth slices. - pub depth_pitch: Offset, -} - -/// Description of drm format properties used to create an image using drm format modifier. -#[derive(Debug, Clone, PartialEq, Eq, Hash)] -pub struct DrmFormatImageProperties { - /// Drm format modifier - pub drm_modifier: DrmModifier, - /// Plane subresource layouts - pub plane_layouts: Vec, -} diff --git a/third_party/rust/external-memory/src/ptr.rs b/third_party/rust/external-memory/src/ptr.rs deleted file mode 100644 index 0eac8ee19512..000000000000 --- a/third_party/rust/external-memory/src/ptr.rs +++ /dev/null @@ -1,20 +0,0 @@ -#[derive(Debug)] -/// Pointer to a host allocated memory. -pub struct Ptr(*mut std::ffi::c_void); -impl Ptr { - /// Get the inner ptr - pub fn as_raw_ptr(&self) -> *mut std::ffi::c_void { - self.0 - } -} -impl From<*mut T> for Ptr { - fn from(ptr: *mut T) -> Self { - Self(unsafe { std::mem::transmute(ptr) }) - } -} -impl std::ops::Deref for Ptr { - type Target = *mut std::ffi::c_void; - fn deref(&self) -> &Self::Target { - &self.0 - } -} diff --git a/third_party/rust/gfx-auxil/.cargo-checksum.json b/third_party/rust/gfx-auxil/.cargo-checksum.json index c80d95e37a7e..5e1e17a573a4 100644 --- a/third_party/rust/gfx-auxil/.cargo-checksum.json +++ b/third_party/rust/gfx-auxil/.cargo-checksum.json @@ -1 +1 @@ -{"files":{"Cargo.toml":"617d82a8d7a530be460d69af00830a3e1e4b807ff173d262910bec3947a561b6","src/lib.rs":"f64e02aec86b3c391e85c41ea4f4cb9eb0c5868f67e5461261e15e866df5ee4d"},"package":"1694991b11d642680e82075a75c7c2bd75556b805efa7660b705689f05b1ab1c"} \ No newline at end of file +{"files":{"Cargo.toml":"535760a4454046c81aa0563dae1fb2effa8f0fcf83286023956bd2f4d3e52096","src/lib.rs":"f64e02aec86b3c391e85c41ea4f4cb9eb0c5868f67e5461261e15e866df5ee4d"},"package":null} \ No newline at end of file diff --git a/third_party/rust/gfx-auxil/Cargo.toml b/third_party/rust/gfx-auxil/Cargo.toml index aad4675c6a74..4ad366e9641a 100644 --- a/third_party/rust/gfx-auxil/Cargo.toml +++ b/third_party/rust/gfx-auxil/Cargo.toml @@ -1,36 +1,20 @@ -# THIS FILE IS AUTOMATICALLY GENERATED BY CARGO -# -# When uploading crates to the registry Cargo will automatically -# "normalize" Cargo.toml files for maximal compatibility -# with all versions of Cargo and also rewrite `path` dependencies -# to registry (e.g., crates.io) dependencies -# -# If you believe there's an error in this file please file an -# issue against the rust-lang/cargo repository. If you're -# editing this file be aware that the upstream Cargo.toml -# will likely look very different (and much more reasonable) - [package] -edition = "2018" name = "gfx-auxil" -version = "0.10.0" -authors = ["The Gfx-rs Developers"] +version = "0.9.0" description = "Implementation details shared between gfx-rs backends" homepage = "https://github.com/gfx-rs/gfx" -documentation = "https://docs.rs/gfx-auxil" +repository = "https://github.com/gfx-rs/gfx" keywords = ["graphics", "gamedev"] license = "MIT OR Apache-2.0" -repository = "https://github.com/gfx-rs/gfx" +authors = ["The Gfx-rs Developers"] +documentation = "https://docs.rs/gfx-auxil" +workspace = "../../../" +edition = "2018" + +[dependencies] +hal = { path = "../../hal", version = "0.8", package = "gfx-hal" } +fxhash = "0.2.1" +spirv_cross = { version = "0.23", optional = true } [lib] name = "gfx_auxil" -[dependencies.fxhash] -version = "0.2.1" - -[dependencies.hal] -version = "0.9" -package = "gfx-hal" - -[dependencies.spirv_cross] -version = "0.23" -optional = true diff --git a/third_party/rust/gfx-backend-dx11/.cargo-checksum.json b/third_party/rust/gfx-backend-dx11/.cargo-checksum.json index 850aedcbcf1e..1e0753a62ff9 100644 --- a/third_party/rust/gfx-backend-dx11/.cargo-checksum.json +++ b/third_party/rust/gfx-backend-dx11/.cargo-checksum.json @@ -1 +1 @@ -{"files":{"Cargo.toml":"df37fdf32a1d711ca73c4bca6a398b7e1cdf4bb0407fcaaf480fa0bdc80a6266","README.md":"0d04b2e4806fccef6e0588d5e57517af3256b4d9607aa638b1b4fdf83eef399b","shaders/blit.hlsl":"a00c57d25b6704a57cd17923c5b7a47608b3ab17b96e7e2ab1172283dc841194","shaders/clear.hlsl":"9b6747a76dabe37ff8e069cdbb8a9c22f6cf71a6d3041d358cd1569d1bb8e10f","shaders/copy.hlsl":"2a5cf365b7b4eb514c75add8e35fb8dd1cd0b3d84996ceaf814ed5097ba77661","src/conv.rs":"c89d30799e1e1eed70dcf5218369d4aa339495a43ffeb0c6b979973b1e8291d1","src/debug.rs":"f4d9d4f38172c59540bc54c8f12602e4a4b2c8ce724e76cce3e9ef12b42003ac","src/device.rs":"b5dd4337407e7032ddd0d81efe3aa1df7b5f6d9031289764ece8af8f6926849e","src/dxgi.rs":"6cdd2537bc0f3b1a2b143a4560886063b4c645be2fcaef75fa1527f79694b722","src/internal.rs":"8484ed62db469021f8c55998c189cef50b7aa02ea8643117789c16051f318b12","src/lib.rs":"c2bdbbe0f590cc98e1a18076ff3f4818f59c795985bfc036a9d435c671d78df6","src/shader.rs":"8d7481cc1fea0f96ccdad285c60bc41e202eb6982426df7d9a447af07c043792"},"package":"8f9e453baf3aaef2b0c354ce0b3d63d76402e406a59b64b7182d123cfa6635ae"} \ No newline at end of file +{"files":{"Cargo.toml":"be8e7fbb8c331567551acca53e466f8670f1e0689c879ef5068932fa9ea7512f","README.md":"13b17f879e00fec29de8d7ba7c24163e9eddc9c5ce173464c48762653b3041ce","shaders/blit.hlsl":"92a8b404ee956ceff2728ec8dd68969fba4c32a79f4d879f069a294f245a867c","shaders/clear.hlsl":"b715a0d8ccebd858531de845fdb3f1b31f25d3f62266238cd1d417006a07957c","shaders/copy.hlsl":"c71b2df8691068d450d3216b54a5a0315f1e17bcd75aec3b4f46b5e3521945c3","src/conv.rs":"436fa263d869024018e54e4f6fd5c0bcaec2424deed871bc97f9854779dac53e","src/debug.rs":"60a2a7cac9cbced6385f560d7b171c4a8eb9a644d8ac219671e1e0fcc8a2439f","src/device.rs":"453186b234d8b85e8d3b7c3e298ed5046d6935c5affd5091e90143040145c4bd","src/dxgi.rs":"7265f4a24b48013c67aac33d456c2aae210b9d597170721738833015fba974bf","src/internal.rs":"0f5ebadf821a532c3820bd86d344bd9db09e466a70aa8cc50c4776058674a5ff","src/lib.rs":"7c1c9e054b7cc0e8ba585ae11f098b79e97de8d94d824cacc2c30169924f778c","src/shader.rs":"0c3c2475578cd322ab6c737732dec00d0893924f3d397f3d326e53f3bc90e9e5"},"package":null} \ No newline at end of file diff --git a/third_party/rust/gfx-backend-dx11/Cargo.toml b/third_party/rust/gfx-backend-dx11/Cargo.toml index ed4d1e88e3e5..928cf1a7f108 100644 --- a/third_party/rust/gfx-backend-dx11/Cargo.toml +++ b/third_party/rust/gfx-backend-dx11/Cargo.toml @@ -1,81 +1,40 @@ -# THIS FILE IS AUTOMATICALLY GENERATED BY CARGO -# -# When uploading crates to the registry Cargo will automatically -# "normalize" Cargo.toml files for maximal compatibility -# with all versions of Cargo and also rewrite `path` dependencies -# to registry (e.g., crates.io) dependencies -# -# If you believe there's an error in this file please file an -# issue against the rust-lang/cargo repository. If you're -# editing this file be aware that the upstream Cargo.toml -# will likely look very different (and much more reasonable) - [package] -edition = "2018" name = "gfx-backend-dx11" -version = "0.9.0" -authors = ["The Gfx-rs Developers"] +version = "0.8.0" description = "DirectX-11 API backend for gfx-rs" homepage = "https://github.com/gfx-rs/gfx" -documentation = "https://docs.rs/gfx-backend-dx11" -readme = "README.md" +repository = "https://github.com/gfx-rs/gfx" keywords = ["graphics", "gamedev"] license = "MIT OR Apache-2.0" -repository = "https://github.com/gfx-rs/gfx" -[package.metadata.docs.rs] -default-target = "x86_64-pc-windows-msvc" - -[lib] -name = "gfx_backend_dx11" -[dependencies.arrayvec] -version = "0.5" - -[dependencies.auxil] -version = "0.10" -features = ["spirv_cross"] -package = "gfx-auxil" - -[dependencies.bitflags] -version = "1" - -[dependencies.gfx-renderdoc] -version = "0.1.0" - -[dependencies.hal] -version = "0.9" -package = "gfx-hal" - -[dependencies.libloading] -version = "0.7" - -[dependencies.log] -version = "0.4" - -[dependencies.parking_lot] -version = "0.11" - -[dependencies.range-alloc] -version = "0.1" - -[dependencies.raw-window-handle] -version = "0.3" - -[dependencies.smallvec] -version = "1.0" - -[dependencies.spirv_cross] -version = "0.23" -features = ["hlsl"] - -[dependencies.thunderdome] -version = "0.4" - -[dependencies.winapi] -version = "0.3" -features = ["basetsd", "d3d11", "d3d11_1", "d3d11sdklayers", "d3dcommon", "d3dcompiler", "dxgi1_2", "dxgi1_3", "dxgi1_4", "dxgi1_5", "dxgiformat", "dxgitype", "handleapi", "minwindef", "synchapi", "unknwnbase", "winbase", "windef", "winerror", "winnt", "winuser"] - -[dependencies.wio] -version = "0.2" +authors = ["The Gfx-rs Developers"] +readme = "README.md" +documentation = "https://docs.rs/gfx-backend-dx11" +workspace = "../../.." +edition = "2018" [features] default = [] + +[lib] +name = "gfx_backend_dx11" + +[dependencies] +arrayvec = "0.5" +auxil = { path = "../../auxil/auxil", version = "0.9", package = "gfx-auxil", features = ["spirv_cross"] } +hal = { path = "../../hal", version = "0.8", package = "gfx-hal" } +range-alloc = { path = "../../auxil/range-alloc", version = "0.1" } +bitflags = "1" +libloading = "0.7" +log = "0.4" +smallvec = "1.0" +spirv_cross = { version = "0.23", features = ["hlsl"] } +thunderdome = "0.4" +parking_lot = "0.11" +winapi = { version = "0.3", features = ["basetsd","d3d11", "d3d11_1", "d3d11sdklayers", "d3dcommon","d3dcompiler","dxgi1_2","dxgi1_3","dxgi1_4", "dxgi1_5", "dxgiformat","dxgitype","handleapi","minwindef","synchapi","unknwnbase","winbase","windef","winerror","winnt","winuser"] } +wio = "0.2" +raw-window-handle = "0.3" + +# This forces docs.rs to build the crate on windows, otherwise the build fails +# and we get no docs at all. +[package.metadata.docs.rs] +default-target = "x86_64-pc-windows-msvc" diff --git a/third_party/rust/gfx-backend-dx11/README.md b/third_party/rust/gfx-backend-dx11/README.md index 4f67fb6745a5..25e3815b95c1 100644 --- a/third_party/rust/gfx-backend-dx11/README.md +++ b/third_party/rust/gfx-backend-dx11/README.md @@ -1,23 +1,23 @@ -# gfx_device_dx11 - -[DX11](https://msdn.microsoft.com/en-us/library/windows/desktop/ff476080(v=vs.85).aspx) backend for gfx. - -## Normalized Coordinates - -Render | Depth | Texture --------|-------|-------- -![render_coordinates](../../../info/gl_render_coordinates.png) | ![depth_coordinates](../../../info/dx_depth_coordinates.png) | ![texture_coordinates](../../../info/dx_texture_coordinates.png) - -## Binding Model - -Writable storage bindings: - 1. Binding: 0 .. `D3D11_PS_CS_UAV_REGISTER_COUNT` - -Other bindings: - 1. Shader stage: vs, fs, cs - 2. Register: constant buffers (CBV), shader resources (SRV), samplers - 3. Binding (tight) - -## Mirroring - -TODO +# gfx_device_dx11 + +[DX11](https://msdn.microsoft.com/en-us/library/windows/desktop/ff476080(v=vs.85).aspx) backend for gfx. + +## Normalized Coordinates + +Render | Depth | Texture +-------|-------|-------- +![render_coordinates](../../../info/gl_render_coordinates.png) | ![depth_coordinates](../../../info/dx_depth_coordinates.png) | ![texture_coordinates](../../../info/dx_texture_coordinates.png) + +## Binding Model + +Writable storage bindings: + 1. Binding: 0 .. `D3D11_PS_CS_UAV_REGISTER_COUNT` + +Other bindings: + 1. Shader stage: vs, fs, cs + 2. Register: constant buffers (CBV), shader resources (SRV), samplers + 3. Binding (tight) + +## Mirroring + +TODO diff --git a/third_party/rust/gfx-backend-dx11/shaders/blit.hlsl b/third_party/rust/gfx-backend-dx11/shaders/blit.hlsl index 4bdd006abed7..f627e5c9b92e 100644 --- a/third_party/rust/gfx-backend-dx11/shaders/blit.hlsl +++ b/third_party/rust/gfx-backend-dx11/shaders/blit.hlsl @@ -1,63 +1,63 @@ -cbuffer Region : register(b0) { - float2 offset; - float2 extent; - float z; - float level; -}; - -struct VsOutput { - float4 pos: SV_POSITION; - float4 uv: TEXCOORD0; -}; - -// Create a screen filling triangle -VsOutput vs_blit_2d(uint id: SV_VertexID) { - float2 coord = float2((id << 1) & 2, id & 2); - VsOutput output = { - float4(float2(-1.0, 1.0) + coord * float2(2.0, -2.0), 0.0, 1.0), - float4(offset + coord * extent, z, level) - }; - return output; -} - -SamplerState BlitSampler : register(s0); - -Texture2DArray BlitSrc_Uint : register(t0); -Texture2DArray BlitSrc_Sint : register(t0); -Texture2DArray BlitSrc_Float : register(t0); - -// TODO: get rid of GetDimensions call -uint4 Nearest_Uint(float4 uv) -{ - float4 size; - BlitSrc_Uint.GetDimensions(0, size.x, size.y, size.z, size.w); - - float2 pix = uv.xy * size.xy; - - return BlitSrc_Uint.Load(int4(int2(pix), uv.zw)); -} - -int4 Nearest_Sint(float4 uv) -{ - float4 size; - BlitSrc_Sint.GetDimensions(0, size.x, size.y, size.z, size.w); - - float2 pix = uv.xy * size.xy; - - return BlitSrc_Sint.Load(int4(int2(pix), uv.zw)); -} - -uint4 ps_blit_2d_uint(VsOutput input) : SV_Target -{ - return Nearest_Uint(input.uv); -} - -int4 ps_blit_2d_int(VsOutput input) : SV_Target -{ - return Nearest_Sint(input.uv); -} - -float4 ps_blit_2d_float(VsOutput input) : SV_Target -{ - return BlitSrc_Float.SampleLevel(BlitSampler, input.uv.xyz, input.uv.w); -} +cbuffer Region : register(b0) { + float2 offset; + float2 extent; + float z; + float level; +}; + +struct VsOutput { + float4 pos: SV_POSITION; + float4 uv: TEXCOORD0; +}; + +// Create a screen filling triangle +VsOutput vs_blit_2d(uint id: SV_VertexID) { + float2 coord = float2((id << 1) & 2, id & 2); + VsOutput output = { + float4(float2(-1.0, 1.0) + coord * float2(2.0, -2.0), 0.0, 1.0), + float4(offset + coord * extent, z, level) + }; + return output; +} + +SamplerState BlitSampler : register(s0); + +Texture2DArray BlitSrc_Uint : register(t0); +Texture2DArray BlitSrc_Sint : register(t0); +Texture2DArray BlitSrc_Float : register(t0); + +// TODO: get rid of GetDimensions call +uint4 Nearest_Uint(float4 uv) +{ + float4 size; + BlitSrc_Uint.GetDimensions(0, size.x, size.y, size.z, size.w); + + float2 pix = uv.xy * size.xy; + + return BlitSrc_Uint.Load(int4(int2(pix), uv.zw)); +} + +int4 Nearest_Sint(float4 uv) +{ + float4 size; + BlitSrc_Sint.GetDimensions(0, size.x, size.y, size.z, size.w); + + float2 pix = uv.xy * size.xy; + + return BlitSrc_Sint.Load(int4(int2(pix), uv.zw)); +} + +uint4 ps_blit_2d_uint(VsOutput input) : SV_Target +{ + return Nearest_Uint(input.uv); +} + +int4 ps_blit_2d_int(VsOutput input) : SV_Target +{ + return Nearest_Sint(input.uv); +} + +float4 ps_blit_2d_float(VsOutput input) : SV_Target +{ + return BlitSrc_Float.SampleLevel(BlitSampler, input.uv.xyz, input.uv.w); +} diff --git a/third_party/rust/gfx-backend-dx11/shaders/clear.hlsl b/third_party/rust/gfx-backend-dx11/shaders/clear.hlsl index 3f8f3a4e87dd..5c91fe6b4900 100644 --- a/third_party/rust/gfx-backend-dx11/shaders/clear.hlsl +++ b/third_party/rust/gfx-backend-dx11/shaders/clear.hlsl @@ -1,22 +1,22 @@ -cbuffer ClearColorF32 : register(b0) { float4 ClearF32; }; -cbuffer ClearColorU32 : register(b0) { uint4 ClearU32; }; -cbuffer ClearColorI32 : register(b0) { int4 ClearI32; }; -cbuffer ClearColorDepth : register(b0) { float ClearDepth; }; - -// fullscreen triangle -float4 vs_partial_clear(uint id : SV_VertexID) : SV_Position -{ - return float4( - float(id / 2) * 4.0 - 1.0, - float(id % 2) * 4.0 - 1.0, - 0.0, - 1.0 - ); -} - -// TODO: send constants through VS as flat attributes -float4 ps_partial_clear_float() : SV_Target0 { return ClearF32; } -uint4 ps_partial_clear_uint() : SV_Target0 { return ClearU32; } -int4 ps_partial_clear_int() : SV_Target0 { return ClearI32; } -float ps_partial_clear_depth() : SV_Depth { return ClearDepth; } -void ps_partial_clear_stencil() { } +cbuffer ClearColorF32 : register(b0) { float4 ClearF32; }; +cbuffer ClearColorU32 : register(b0) { uint4 ClearU32; }; +cbuffer ClearColorI32 : register(b0) { int4 ClearI32; }; +cbuffer ClearColorDepth : register(b0) { float ClearDepth; }; + +// fullscreen triangle +float4 vs_partial_clear(uint id : SV_VertexID) : SV_Position +{ + return float4( + float(id / 2) * 4.0 - 1.0, + float(id % 2) * 4.0 - 1.0, + 0.0, + 1.0 + ); +} + +// TODO: send constants through VS as flat attributes +float4 ps_partial_clear_float() : SV_Target0 { return ClearF32; } +uint4 ps_partial_clear_uint() : SV_Target0 { return ClearU32; } +int4 ps_partial_clear_int() : SV_Target0 { return ClearI32; } +float ps_partial_clear_depth() : SV_Depth { return ClearDepth; } +void ps_partial_clear_stencil() { } diff --git a/third_party/rust/gfx-backend-dx11/shaders/copy.hlsl b/third_party/rust/gfx-backend-dx11/shaders/copy.hlsl index dbba95dc9823..f8b5d8523b9f 100644 --- a/third_party/rust/gfx-backend-dx11/shaders/copy.hlsl +++ b/third_party/rust/gfx-backend-dx11/shaders/copy.hlsl @@ -1,615 +1,615 @@ -struct BufferCopy { - uint4 SrcDst; -}; - -struct ImageCopy { - uint4 Src; - uint4 Dst; -}; - -struct BufferImageCopy { - // x=offset, yz=size - uint4 BufferVars; - uint4 ImageOffset; - uint4 ImageExtent; - uint4 ImageSize; -}; - -cbuffer CopyConstants : register(b0) { - BufferCopy BufferCopies; - ImageCopy ImageCopies; - BufferImageCopy BufferImageCopies; -}; - - -uint3 GetDestBounds() -{ - return min( - BufferImageCopies.ImageOffset + BufferImageCopies.ImageExtent, - BufferImageCopies.ImageSize - ); -} - -uint3 GetImageCopyDst(uint3 dispatch_thread_id) -{ - return uint3(ImageCopies.Dst.xy + dispatch_thread_id.xy, ImageCopies.Dst.z); -} - -uint3 GetImageCopySrc(uint3 dispatch_thread_id) -{ - return uint3(ImageCopies.Src.xy + dispatch_thread_id.xy, ImageCopies.Src.z); -} - -uint3 GetImageDst(uint3 dispatch_thread_id) -{ - return uint3(BufferImageCopies.ImageOffset.xy + dispatch_thread_id.xy, BufferImageCopies.ImageOffset.z); -} - -uint3 GetImageSrc(uint3 dispatch_thread_id) -{ - return uint3(BufferImageCopies.ImageOffset.xy + dispatch_thread_id.xy, BufferImageCopies.ImageOffset.z); -} - -uint GetBufferDst128(uint3 dispatch_thread_id) -{ - return BufferImageCopies.BufferVars.x + dispatch_thread_id.x * 16 + dispatch_thread_id.y * 16 * max(BufferImageCopies.BufferVars.y, BufferImageCopies.ImageExtent.x); -} -uint GetBufferSrc128(uint3 dispatch_thread_id) -{ - return BufferImageCopies.BufferVars.x + dispatch_thread_id.x * 16 + dispatch_thread_id.y * 16 * max(BufferImageCopies.BufferVars.y, BufferImageCopies.ImageExtent.x); -} - -uint GetBufferDst64(uint3 dispatch_thread_id) -{ - return BufferImageCopies.BufferVars.x + dispatch_thread_id.x * 8 + dispatch_thread_id.y * 8 * max(BufferImageCopies.BufferVars.y, BufferImageCopies.ImageExtent.x); -} -uint GetBufferSrc64(uint3 dispatch_thread_id) -{ - return BufferImageCopies.BufferVars.x + dispatch_thread_id.x * 8 + dispatch_thread_id.y * 8 * max(BufferImageCopies.BufferVars.y, BufferImageCopies.ImageExtent.x); -} - -uint GetBufferDst32(uint3 dispatch_thread_id) -{ - return BufferImageCopies.BufferVars.x + dispatch_thread_id.x * 4 + dispatch_thread_id.y * 4 * max(BufferImageCopies.BufferVars.y, BufferImageCopies.ImageExtent.x); -} -uint GetBufferSrc32(uint3 dispatch_thread_id) -{ - return BufferImageCopies.BufferVars.x + dispatch_thread_id.x * 4 + dispatch_thread_id.y * 4 * max(BufferImageCopies.BufferVars.y, BufferImageCopies.ImageExtent.x); -} - -uint GetBufferDst16(uint3 dispatch_thread_id) -{ - return BufferImageCopies.BufferVars.x + dispatch_thread_id.x * 4 + dispatch_thread_id.y * 2 * max(BufferImageCopies.BufferVars.y, BufferImageCopies.ImageExtent.x); -} -uint GetBufferSrc16(uint3 dispatch_thread_id) -{ - return BufferImageCopies.BufferVars.x + dispatch_thread_id.x * 4 + dispatch_thread_id.y * 2 * max(BufferImageCopies.BufferVars.y, BufferImageCopies.ImageExtent.x); -} - -uint GetBufferDst8(uint3 dispatch_thread_id) -{ - return BufferImageCopies.BufferVars.x + dispatch_thread_id.x * 4 + dispatch_thread_id.y * max(BufferImageCopies.BufferVars.y, BufferImageCopies.ImageExtent.x); -} -uint GetBufferSrc8(uint3 dispatch_thread_id) -{ - return BufferImageCopies.BufferVars.x + dispatch_thread_id.x * 4 + dispatch_thread_id.y * max(BufferImageCopies.BufferVars.y, BufferImageCopies.ImageExtent.x); -} - - -uint4 Uint32ToUint8x4(uint data) -{ - return (data >> uint4(0, 8, 16, 24)) & 0xFF; -} - -uint2 Uint32ToUint16x2(uint data) -{ - return (data >> uint2(0, 16)) & 0xFFFF; -} - -uint Uint8x4ToUint32(uint4 data) -{ - return dot(min(data, 0xFF), 1 << uint4(0, 8, 16, 24)); -} - -uint Uint16x2ToUint32(uint2 data) -{ - return dot(min(data, 0xFFFF), 1 << uint2(0, 16)); -} - -uint2 Uint16ToUint8x2(uint data) -{ - return (data >> uint2(0, 8)) & 0xFF; -} - -uint Uint8x2ToUint16(uint2 data) -{ - return dot(min(data, 0xFF), 1 << uint2(0, 8)); -} - -uint4 Float4ToUint8x4(float4 data) -{ - return uint4(data * 255 + .5f); -} - -// Buffers are always R32-aligned -ByteAddressBuffer BufferCopySrc : register(t0); -RWByteAddressBuffer BufferCopyDst : register(u0); - -RWTexture1DArray Image1CopyDstR : register(u0); -RWTexture1DArray Image1CopyDstRg : register(u0); -RWTexture1DArray Image1CopyDstRgba : register(u0); - -Texture2DArray Image2CopySrc : register(t0); -RWTexture2DArray Image2CopyDstR : register(u0); -RWTexture2DArray Image2CopyDstRg : register(u0); -RWTexture2DArray Image2CopyDstRgba : register(u0); - -Texture2DArray ImageCopy2SrcBgra : register(t0); - -// Image<->Image copies -[numthreads(1, 1, 1)] -void cs_copy_image2d_r8g8_image2d_r16(uint3 dispatch_thread_id : SV_DispatchThreadID) -{ - uint3 dst_idx = GetImageCopyDst(dispatch_thread_id); - uint3 src_idx = GetImageCopySrc(dispatch_thread_id); - - Image2CopyDstR[dst_idx] = Uint8x2ToUint16(Image2CopySrc[src_idx]); -} - -[numthreads(1, 1, 1)] -void cs_copy_image2d_r16_image2d_r8g8(uint3 dispatch_thread_id : SV_DispatchThreadID) -{ - uint3 dst_idx = GetImageCopyDst(dispatch_thread_id); - uint3 src_idx = GetImageCopySrc(dispatch_thread_id); - - Image2CopyDstRg[dst_idx] = Uint16ToUint8x2(Image2CopySrc[src_idx]); -} - -[numthreads(1, 1, 1)] -void cs_copy_image2d_r8g8b8a8_image2d_r32(uint3 dispatch_thread_id : SV_DispatchThreadID) -{ - uint3 dst_idx = GetImageCopyDst(dispatch_thread_id); - uint3 src_idx = GetImageCopySrc(dispatch_thread_id); - - Image2CopyDstR[dst_idx] = Uint8x4ToUint32(Image2CopySrc[src_idx]); -} - -[numthreads(1, 1, 1)] -void cs_copy_image2d_r8g8b8a8_image2d_r16g16(uint3 dispatch_thread_id : SV_DispatchThreadID) -{ - uint3 dst_idx = GetImageCopyDst(dispatch_thread_id); - uint3 src_idx = GetImageCopySrc(dispatch_thread_id); - - Image2CopyDstRg[dst_idx] = Uint32ToUint16x2(Uint8x4ToUint32(Image2CopySrc[src_idx])); -} - -[numthreads(1, 1, 1)] -void cs_copy_image2d_r16g16_image2d_r32(uint3 dispatch_thread_id : SV_DispatchThreadID) -{ - uint3 dst_idx = GetImageCopyDst(dispatch_thread_id); - uint3 src_idx = GetImageCopySrc(dispatch_thread_id); - - Image2CopyDstR[dst_idx] = Uint16x2ToUint32(Image2CopySrc[src_idx]); -} - -[numthreads(1, 1, 1)] -void cs_copy_image2d_r16g16_image2d_r8g8b8a8(uint3 dispatch_thread_id : SV_DispatchThreadID) -{ - uint3 dst_idx = GetImageCopyDst(dispatch_thread_id); - uint3 src_idx = GetImageCopySrc(dispatch_thread_id); - - Image2CopyDstRgba[dst_idx] = Uint32ToUint8x4(Uint16x2ToUint32(Image2CopySrc[src_idx])); -} - -[numthreads(1, 1, 1)] -void cs_copy_image2d_r32_image2d_r16g16(uint3 dispatch_thread_id : SV_DispatchThreadID) -{ - uint3 dst_idx = GetImageCopyDst(dispatch_thread_id); - uint3 src_idx = GetImageCopySrc(dispatch_thread_id); - - Image2CopyDstRg[dst_idx] = Uint32ToUint16x2(Image2CopySrc[src_idx]); -} - -[numthreads(1, 1, 1)] -void cs_copy_image2d_r32_image2d_r8g8b8a8(uint3 dispatch_thread_id : SV_DispatchThreadID) -{ - uint3 dst_idx = GetImageCopyDst(dispatch_thread_id); - uint3 src_idx = GetImageCopySrc(dispatch_thread_id); - - Image2CopyDstRgba[dst_idx] = Uint32ToUint8x4(Image2CopySrc[src_idx]); -} - -//#define COPY_1D_NUM_THREAD 64 //TODO -#define COPY_2D_NUM_THREAD_X 8 -#define COPY_2D_NUM_THREAD_Y 8 - -// Buffer<->Image copies - -// R32G32B32A32 -[numthreads(COPY_2D_NUM_THREAD_X, COPY_2D_NUM_THREAD_Y, 1)] -void cs_copy_buffer_image2d_r32g32b32a32(uint3 dispatch_thread_id : SV_DispatchThreadID) { - uint3 dst_idx = GetImageDst(dispatch_thread_id); - uint3 bounds = GetDestBounds(); - if (dst_idx.x >= bounds.x || dst_idx.y >= bounds.y) { - return; - } - - uint src_idx = GetBufferSrc128(dispatch_thread_id); - - Image2CopyDstRgba[dst_idx] = uint4( - BufferCopySrc.Load(src_idx), - BufferCopySrc.Load(src_idx + 1 * 4), - BufferCopySrc.Load(src_idx + 2 * 4), - BufferCopySrc.Load(src_idx + 3 * 4) - ); -} - -[numthreads(COPY_2D_NUM_THREAD_X, COPY_2D_NUM_THREAD_Y, 1)] -void cs_copy_image2d_r32g32b32a32_buffer(uint3 dispatch_thread_id : SV_DispatchThreadID) { - uint3 src_idx = GetImageSrc(dispatch_thread_id); - uint3 bounds = GetDestBounds(); - if (src_idx.x >= bounds.x || src_idx.y >= bounds.y) { - return; - } - - uint4 data = Image2CopySrc[src_idx]; - uint dst_idx = GetBufferDst128(dispatch_thread_id); - - BufferCopyDst.Store(dst_idx, data.x); - BufferCopyDst.Store(dst_idx + 1 * 4, data.y); - BufferCopyDst.Store(dst_idx + 2 * 4, data.z); - BufferCopyDst.Store(dst_idx + 3 * 4, data.w); -} - -// R32G32 -[numthreads(COPY_2D_NUM_THREAD_X, COPY_2D_NUM_THREAD_Y, 1)] -void cs_copy_buffer_image2d_r32g32(uint3 dispatch_thread_id : SV_DispatchThreadID) { - uint3 dst_idx = GetImageDst(dispatch_thread_id); - uint3 bounds = GetDestBounds(); - if (dst_idx.x >= bounds.x || dst_idx.y >= bounds.y) { - return; - } - - uint src_idx = GetBufferSrc64(dispatch_thread_id); - - Image2CopyDstRg[dst_idx] = uint2( - BufferCopySrc.Load(src_idx), - BufferCopySrc.Load(src_idx + 1 * 4) - ); -} - -[numthreads(COPY_2D_NUM_THREAD_X, COPY_2D_NUM_THREAD_Y, 1)] -void cs_copy_image2d_r32g32_buffer(uint3 dispatch_thread_id : SV_DispatchThreadID) { - uint3 src_idx = GetImageSrc(dispatch_thread_id); - uint3 bounds = GetDestBounds(); - if (src_idx.x >= bounds.x || src_idx.y >= bounds.y) { - return; - } - - uint2 data = Image2CopySrc[src_idx].rg; - uint dst_idx = GetBufferDst64(dispatch_thread_id); - - BufferCopyDst.Store(dst_idx , data.x); - BufferCopyDst.Store(dst_idx + 1 * 4, data.y); -} - -// R16G16B16A16 -[numthreads(COPY_2D_NUM_THREAD_X, COPY_2D_NUM_THREAD_Y, 1)] -void cs_copy_buffer_image2d_r16g16b16a16(uint3 dispatch_thread_id : SV_DispatchThreadID) { - uint3 dst_idx = GetImageDst(dispatch_thread_id); - uint3 bounds = GetDestBounds(); - if (dst_idx.x >= bounds.x || dst_idx.y >= bounds.y) { - return; - } - - uint src_idx = GetBufferSrc64(dispatch_thread_id); - - Image2CopyDstRgba[dst_idx] = uint4( - Uint32ToUint16x2(BufferCopySrc.Load(src_idx)), - Uint32ToUint16x2(BufferCopySrc.Load(src_idx + 1 * 4)) - ); -} - -[numthreads(COPY_2D_NUM_THREAD_X, COPY_2D_NUM_THREAD_Y, 1)] -void cs_copy_image2d_r16g16b16a16_buffer(uint3 dispatch_thread_id : SV_DispatchThreadID) { - uint3 src_idx = GetImageSrc(dispatch_thread_id); - uint3 bounds = GetDestBounds(); - if (src_idx.x >= bounds.x || src_idx.y >= bounds.y) { - return; - } - - uint4 data = Image2CopySrc[src_idx]; - uint dst_idx = GetBufferDst64(dispatch_thread_id); - - BufferCopyDst.Store(dst_idx, Uint16x2ToUint32(data.xy)); - BufferCopyDst.Store(dst_idx + 1 * 4, Uint16x2ToUint32(data.zw)); -} - -// R32 -[numthreads(COPY_2D_NUM_THREAD_X, 1, 1)] -void cs_copy_buffer_image1d_r32(uint3 dispatch_thread_id : SV_DispatchThreadID) { - uint3 dst_idx = GetImageDst(dispatch_thread_id); - uint3 bounds = GetDestBounds(); - if (dst_idx.x >= bounds.x) { - return; - } - - uint src_idx = GetBufferSrc32(dispatch_thread_id); - - Image1CopyDstR[dst_idx.xz] = BufferCopySrc.Load(src_idx); -} -[numthreads(COPY_2D_NUM_THREAD_X, COPY_2D_NUM_THREAD_Y, 1)] -void cs_copy_buffer_image2d_r32(uint3 dispatch_thread_id : SV_DispatchThreadID) { - uint3 dst_idx = GetImageDst(dispatch_thread_id); - uint3 bounds = GetDestBounds(); - if (dst_idx.x >= bounds.x || dst_idx.y >= bounds.y) { - return; - } - - uint src_idx = GetBufferSrc32(dispatch_thread_id); - - Image2CopyDstR[dst_idx] = BufferCopySrc.Load(src_idx); -} - -[numthreads(COPY_2D_NUM_THREAD_X, COPY_2D_NUM_THREAD_Y, 1)] -void cs_copy_image2d_r32_buffer(uint3 dispatch_thread_id : SV_DispatchThreadID) { - uint3 src_idx = GetImageSrc(dispatch_thread_id); - uint3 bounds = GetDestBounds(); - if (src_idx.x >= bounds.x || src_idx.y >= bounds.y) { - return; - } - - uint dst_idx = GetBufferDst32(dispatch_thread_id); - - BufferCopyDst.Store(dst_idx, Image2CopySrc[src_idx].r); -} - -// R16G16 -[numthreads(COPY_2D_NUM_THREAD_X, COPY_2D_NUM_THREAD_Y, 1)] -void cs_copy_buffer_image2d_r16g16(uint3 dispatch_thread_id : SV_DispatchThreadID) { - uint3 dst_idx = GetImageDst(dispatch_thread_id); - uint3 bounds = GetDestBounds(); - if (dst_idx.x >= bounds.x || dst_idx.y >= bounds.y) { - return; - } - - uint src_idx = GetBufferSrc32(dispatch_thread_id); - - Image2CopyDstRg[dst_idx] = Uint32ToUint16x2(BufferCopySrc.Load(src_idx)); -} - -[numthreads(COPY_2D_NUM_THREAD_X, COPY_2D_NUM_THREAD_Y, 1)] -void cs_copy_image2d_r16g16_buffer(uint3 dispatch_thread_id : SV_DispatchThreadID) { - uint3 src_idx = GetImageSrc(dispatch_thread_id); - uint3 bounds = GetDestBounds(); - if (src_idx.x >= bounds.x || src_idx.y >= bounds.y) { - return; - } - - uint dst_idx = GetBufferDst32(dispatch_thread_id); - - BufferCopyDst.Store(dst_idx, Uint16x2ToUint32(Image2CopySrc[src_idx].xy)); -} - -// R8G8B8A8 -[numthreads(COPY_2D_NUM_THREAD_X, 1, 1)] -void cs_copy_buffer_image1d_r8g8b8a8(uint3 dispatch_thread_id : SV_DispatchThreadID) { - uint3 dst_idx = GetImageDst(dispatch_thread_id); - uint3 bounds = GetDestBounds(); - if (dst_idx.x >= bounds.x) { - return; - } - - uint src_idx = GetBufferSrc32(dispatch_thread_id); - - Image1CopyDstRgba[dst_idx.xz] = Uint32ToUint8x4(BufferCopySrc.Load(src_idx)); -} -[numthreads(COPY_2D_NUM_THREAD_X, COPY_2D_NUM_THREAD_Y, 1)] -void cs_copy_buffer_image2d_r8g8b8a8(uint3 dispatch_thread_id : SV_DispatchThreadID) { - uint3 dst_idx = GetImageDst(dispatch_thread_id); - uint3 bounds = GetDestBounds(); - if (dst_idx.x >= bounds.x || dst_idx.y >= bounds.y) { - return; - } - - uint src_idx = GetBufferSrc32(dispatch_thread_id); - - Image2CopyDstRgba[dst_idx] = Uint32ToUint8x4(BufferCopySrc.Load(src_idx)); -} - -[numthreads(COPY_2D_NUM_THREAD_X, COPY_2D_NUM_THREAD_Y, 1)] -void cs_copy_image2d_r8g8b8a8_buffer(uint3 dispatch_thread_id : SV_DispatchThreadID) { - uint3 src_idx = GetImageSrc(dispatch_thread_id); - uint3 bounds = GetDestBounds(); - if (src_idx.x >= bounds.x || src_idx.y >= bounds.y) { - return; - } - - uint dst_idx = GetBufferDst32(dispatch_thread_id); - - BufferCopyDst.Store(dst_idx, Uint8x4ToUint32(Image2CopySrc[src_idx])); -} - -// B8G8R8A8 -[numthreads(COPY_2D_NUM_THREAD_X, COPY_2D_NUM_THREAD_Y, 1)] -void cs_copy_image2d_b8g8r8a8_buffer(uint3 dispatch_thread_id : SV_DispatchThreadID) { - uint3 src_idx = GetImageSrc(dispatch_thread_id); - uint3 bounds = GetDestBounds(); - if (src_idx.x >= bounds.x || src_idx.y >= bounds.y) { - return; - } - - uint dst_idx = GetBufferDst32(dispatch_thread_id); - - BufferCopyDst.Store(dst_idx, Uint8x4ToUint32(Float4ToUint8x4(ImageCopy2SrcBgra[src_idx].bgra))); -} - -// R16 -[numthreads(COPY_2D_NUM_THREAD_X, COPY_2D_NUM_THREAD_Y, 1)] -void cs_copy_buffer_image2d_r16(uint3 dispatch_thread_id : SV_DispatchThreadID) { - uint3 dst_idx = GetImageDst(uint3(2, 1, 0) * dispatch_thread_id); - uint3 bounds = GetDestBounds(); - if (dst_idx.x >= bounds.x || dst_idx.y >= bounds.y) { - return; - } - - uint src_idx = GetBufferSrc16(dispatch_thread_id); - uint2 data = Uint32ToUint16x2(BufferCopySrc.Load(src_idx)); - - uint remaining_x = bounds.x - dst_idx.x; - - if (remaining_x >= 2) { - Image2CopyDstR[dst_idx + uint3(1, 0, 0)] = data.y; - } - if (remaining_x >= 1) { - Image2CopyDstR[dst_idx + uint3(0, 0, 0)] = data.x; - } -} - -[numthreads(COPY_2D_NUM_THREAD_X, COPY_2D_NUM_THREAD_Y, 1)] -void cs_copy_image2d_r16_buffer(uint3 dispatch_thread_id : SV_DispatchThreadID) { - uint3 src_idx = GetImageSrc(uint3(2, 1, 0) * dispatch_thread_id); - uint3 bounds = GetDestBounds(); - if (src_idx.x >= bounds.x || src_idx.y >= bounds.y) { - return; - } - - uint dst_idx = GetBufferDst16(dispatch_thread_id); - - uint upper = Image2CopySrc[src_idx].r; - uint lower = Image2CopySrc[src_idx + uint3(1, 0, 0)].r; - - BufferCopyDst.Store(dst_idx, Uint16x2ToUint32(uint2(upper, lower))); -} - -// R8G8 -[numthreads(COPY_2D_NUM_THREAD_X, 1, 1)] -void cs_copy_buffer_image1d_r8g8(uint3 dispatch_thread_id : SV_DispatchThreadID) { - uint3 dst_idx = GetImageDst(uint3(2, 1, 0) * dispatch_thread_id); - uint3 bounds = GetDestBounds(); - if (dst_idx.x >= bounds.x) { - return; - } - - uint src_idx = GetBufferSrc16(dispatch_thread_id); - - uint4 data = Uint32ToUint8x4(BufferCopySrc.Load(src_idx)); - - uint remaining_x = bounds.x - dst_idx.x; - - if (remaining_x >= 2) { - Image1CopyDstRg[dst_idx.xz + uint2(1, 0)] = data.zw; - } - if (remaining_x >= 1) { - Image1CopyDstRg[dst_idx.xz + uint2(0, 0)] = data.xy; - } -} - -[numthreads(COPY_2D_NUM_THREAD_X, COPY_2D_NUM_THREAD_Y, 1)] -void cs_copy_buffer_image2d_r8g8(uint3 dispatch_thread_id : SV_DispatchThreadID) { - uint3 dst_idx = GetImageDst(uint3(2, 1, 0) * dispatch_thread_id); - uint3 bounds = GetDestBounds(); - if (dst_idx.x >= bounds.x || dst_idx.y >= bounds.y) { - return; - } - - uint src_idx = GetBufferSrc16(dispatch_thread_id); - - uint4 data = Uint32ToUint8x4(BufferCopySrc.Load(src_idx)); - - uint remaining_x = bounds.x - dst_idx.x; - - if (remaining_x >= 2) { - Image2CopyDstRg[dst_idx + uint3(1, 0, 0)] = data.zw; - } - if (remaining_x >= 1) { - Image2CopyDstRg[dst_idx + uint3(0, 0, 0)] = data.xy; - } -} - -[numthreads(COPY_2D_NUM_THREAD_X, COPY_2D_NUM_THREAD_Y, 1)] -void cs_copy_image2d_r8g8_buffer(uint3 dispatch_thread_id : SV_DispatchThreadID) { - uint3 src_idx = GetImageSrc(uint3(2, 1, 0) * dispatch_thread_id); - uint3 bounds = GetDestBounds(); - if (src_idx.x >= bounds.x || src_idx.y >= bounds.y) { - return; - } - - uint dst_idx = GetBufferDst16(dispatch_thread_id); - - uint2 lower = Image2CopySrc[src_idx].xy; - uint2 upper = Image2CopySrc[src_idx + uint3(1, 0, 0)].xy; - - BufferCopyDst.Store(dst_idx, Uint8x4ToUint32(uint4(lower.x, lower.y, upper.x, upper.y))); -} - -// R8 -[numthreads(COPY_2D_NUM_THREAD_X, 1, 1)] -void cs_copy_buffer_image1d_r8(uint3 dispatch_thread_id : SV_DispatchThreadID) { - uint3 dst_idx = GetImageDst(uint3(4, 1, 0) * dispatch_thread_id); - uint3 bounds = GetDestBounds(); - if (dst_idx.x >= bounds.x) { - return; - } - - uint src_idx = GetBufferSrc8(dispatch_thread_id); - uint4 data = Uint32ToUint8x4(BufferCopySrc.Load(src_idx)); - - uint remaining_x = bounds.x - dst_idx.x; - - if (remaining_x >= 4) { - Image1CopyDstR[dst_idx.xz + uint2(3, 0)] = data.w; - } - if (remaining_x >= 3) { - Image1CopyDstR[dst_idx.xz + uint2(2, 0)] = data.z; - } - if (remaining_x >= 2) { - Image1CopyDstR[dst_idx.xz + uint2(1, 0)] = data.y; - } - if (remaining_x >= 1) { - Image1CopyDstR[dst_idx.xz + uint2(0, 0)] = data.x; - } -} -[numthreads(COPY_2D_NUM_THREAD_X, COPY_2D_NUM_THREAD_Y, 1)] -void cs_copy_buffer_image2d_r8(uint3 dispatch_thread_id : SV_DispatchThreadID) { - uint3 dst_idx = GetImageDst(uint3(4, 1, 0) * dispatch_thread_id); - uint3 bounds = GetDestBounds(); - if (dst_idx.x >= bounds.x || dst_idx.y >= bounds.y) { - return; - } - - uint src_idx = GetBufferSrc8(dispatch_thread_id); - uint4 data = Uint32ToUint8x4(BufferCopySrc.Load(src_idx)); - - uint remaining_x = bounds.x - dst_idx.x; - - if (remaining_x >= 4) { - Image2CopyDstR[dst_idx + uint3(3, 0, 0)] = data.w; - } - if (remaining_x >= 3) { - Image2CopyDstR[dst_idx + uint3(2, 0, 0)] = data.z; - } - if (remaining_x >= 2) { - Image2CopyDstR[dst_idx + uint3(1, 0, 0)] = data.y; - } - if (remaining_x >= 1) { - Image2CopyDstR[dst_idx + uint3(0, 0, 0)] = data.x; - } -} -[numthreads(COPY_2D_NUM_THREAD_X, COPY_2D_NUM_THREAD_Y, 1)] -void cs_copy_image2d_r8_buffer(uint3 dispatch_thread_id : SV_DispatchThreadID) { - uint3 src_idx = GetImageSrc(uint3(4, 1, 0) * dispatch_thread_id); - uint3 bounds = GetDestBounds(); - if (src_idx.x >= bounds.x || src_idx.y >= bounds.y) { - return; - } - - uint dst_idx = GetBufferDst8(dispatch_thread_id); - - BufferCopyDst.Store(dst_idx, Uint8x4ToUint32(uint4( - Image2CopySrc[src_idx].r, - Image2CopySrc[src_idx + uint3(1, 0, 0)].r, - Image2CopySrc[src_idx + uint3(2, 0, 0)].r, - Image2CopySrc[src_idx + uint3(3, 0, 0)].r - ))); -} +struct BufferCopy { + uint4 SrcDst; +}; + +struct ImageCopy { + uint4 Src; + uint4 Dst; +}; + +struct BufferImageCopy { + // x=offset, yz=size + uint4 BufferVars; + uint4 ImageOffset; + uint4 ImageExtent; + uint4 ImageSize; +}; + +cbuffer CopyConstants : register(b0) { + BufferCopy BufferCopies; + ImageCopy ImageCopies; + BufferImageCopy BufferImageCopies; +}; + + +uint3 GetDestBounds() +{ + return min( + BufferImageCopies.ImageOffset + BufferImageCopies.ImageExtent, + BufferImageCopies.ImageSize + ); +} + +uint3 GetImageCopyDst(uint3 dispatch_thread_id) +{ + return uint3(ImageCopies.Dst.xy + dispatch_thread_id.xy, ImageCopies.Dst.z); +} + +uint3 GetImageCopySrc(uint3 dispatch_thread_id) +{ + return uint3(ImageCopies.Src.xy + dispatch_thread_id.xy, ImageCopies.Src.z); +} + +uint3 GetImageDst(uint3 dispatch_thread_id) +{ + return uint3(BufferImageCopies.ImageOffset.xy + dispatch_thread_id.xy, BufferImageCopies.ImageOffset.z); +} + +uint3 GetImageSrc(uint3 dispatch_thread_id) +{ + return uint3(BufferImageCopies.ImageOffset.xy + dispatch_thread_id.xy, BufferImageCopies.ImageOffset.z); +} + +uint GetBufferDst128(uint3 dispatch_thread_id) +{ + return BufferImageCopies.BufferVars.x + dispatch_thread_id.x * 16 + dispatch_thread_id.y * 16 * max(BufferImageCopies.BufferVars.y, BufferImageCopies.ImageExtent.x); +} +uint GetBufferSrc128(uint3 dispatch_thread_id) +{ + return BufferImageCopies.BufferVars.x + dispatch_thread_id.x * 16 + dispatch_thread_id.y * 16 * max(BufferImageCopies.BufferVars.y, BufferImageCopies.ImageExtent.x); +} + +uint GetBufferDst64(uint3 dispatch_thread_id) +{ + return BufferImageCopies.BufferVars.x + dispatch_thread_id.x * 8 + dispatch_thread_id.y * 8 * max(BufferImageCopies.BufferVars.y, BufferImageCopies.ImageExtent.x); +} +uint GetBufferSrc64(uint3 dispatch_thread_id) +{ + return BufferImageCopies.BufferVars.x + dispatch_thread_id.x * 8 + dispatch_thread_id.y * 8 * max(BufferImageCopies.BufferVars.y, BufferImageCopies.ImageExtent.x); +} + +uint GetBufferDst32(uint3 dispatch_thread_id) +{ + return BufferImageCopies.BufferVars.x + dispatch_thread_id.x * 4 + dispatch_thread_id.y * 4 * max(BufferImageCopies.BufferVars.y, BufferImageCopies.ImageExtent.x); +} +uint GetBufferSrc32(uint3 dispatch_thread_id) +{ + return BufferImageCopies.BufferVars.x + dispatch_thread_id.x * 4 + dispatch_thread_id.y * 4 * max(BufferImageCopies.BufferVars.y, BufferImageCopies.ImageExtent.x); +} + +uint GetBufferDst16(uint3 dispatch_thread_id) +{ + return BufferImageCopies.BufferVars.x + dispatch_thread_id.x * 4 + dispatch_thread_id.y * 2 * max(BufferImageCopies.BufferVars.y, BufferImageCopies.ImageExtent.x); +} +uint GetBufferSrc16(uint3 dispatch_thread_id) +{ + return BufferImageCopies.BufferVars.x + dispatch_thread_id.x * 4 + dispatch_thread_id.y * 2 * max(BufferImageCopies.BufferVars.y, BufferImageCopies.ImageExtent.x); +} + +uint GetBufferDst8(uint3 dispatch_thread_id) +{ + return BufferImageCopies.BufferVars.x + dispatch_thread_id.x * 4 + dispatch_thread_id.y * max(BufferImageCopies.BufferVars.y, BufferImageCopies.ImageExtent.x); +} +uint GetBufferSrc8(uint3 dispatch_thread_id) +{ + return BufferImageCopies.BufferVars.x + dispatch_thread_id.x * 4 + dispatch_thread_id.y * max(BufferImageCopies.BufferVars.y, BufferImageCopies.ImageExtent.x); +} + + +uint4 Uint32ToUint8x4(uint data) +{ + return (data >> uint4(0, 8, 16, 24)) & 0xFF; +} + +uint2 Uint32ToUint16x2(uint data) +{ + return (data >> uint2(0, 16)) & 0xFFFF; +} + +uint Uint8x4ToUint32(uint4 data) +{ + return dot(min(data, 0xFF), 1 << uint4(0, 8, 16, 24)); +} + +uint Uint16x2ToUint32(uint2 data) +{ + return dot(min(data, 0xFFFF), 1 << uint2(0, 16)); +} + +uint2 Uint16ToUint8x2(uint data) +{ + return (data >> uint2(0, 8)) & 0xFF; +} + +uint Uint8x2ToUint16(uint2 data) +{ + return dot(min(data, 0xFF), 1 << uint2(0, 8)); +} + +uint4 Float4ToUint8x4(float4 data) +{ + return uint4(data * 255 + .5f); +} + +// Buffers are always R32-aligned +ByteAddressBuffer BufferCopySrc : register(t0); +RWByteAddressBuffer BufferCopyDst : register(u0); + +RWTexture1DArray Image1CopyDstR : register(u0); +RWTexture1DArray Image1CopyDstRg : register(u0); +RWTexture1DArray Image1CopyDstRgba : register(u0); + +Texture2DArray Image2CopySrc : register(t0); +RWTexture2DArray Image2CopyDstR : register(u0); +RWTexture2DArray Image2CopyDstRg : register(u0); +RWTexture2DArray Image2CopyDstRgba : register(u0); + +Texture2DArray ImageCopy2SrcBgra : register(t0); + +// Image<->Image copies +[numthreads(1, 1, 1)] +void cs_copy_image2d_r8g8_image2d_r16(uint3 dispatch_thread_id : SV_DispatchThreadID) +{ + uint3 dst_idx = GetImageCopyDst(dispatch_thread_id); + uint3 src_idx = GetImageCopySrc(dispatch_thread_id); + + Image2CopyDstR[dst_idx] = Uint8x2ToUint16(Image2CopySrc[src_idx]); +} + +[numthreads(1, 1, 1)] +void cs_copy_image2d_r16_image2d_r8g8(uint3 dispatch_thread_id : SV_DispatchThreadID) +{ + uint3 dst_idx = GetImageCopyDst(dispatch_thread_id); + uint3 src_idx = GetImageCopySrc(dispatch_thread_id); + + Image2CopyDstRg[dst_idx] = Uint16ToUint8x2(Image2CopySrc[src_idx]); +} + +[numthreads(1, 1, 1)] +void cs_copy_image2d_r8g8b8a8_image2d_r32(uint3 dispatch_thread_id : SV_DispatchThreadID) +{ + uint3 dst_idx = GetImageCopyDst(dispatch_thread_id); + uint3 src_idx = GetImageCopySrc(dispatch_thread_id); + + Image2CopyDstR[dst_idx] = Uint8x4ToUint32(Image2CopySrc[src_idx]); +} + +[numthreads(1, 1, 1)] +void cs_copy_image2d_r8g8b8a8_image2d_r16g16(uint3 dispatch_thread_id : SV_DispatchThreadID) +{ + uint3 dst_idx = GetImageCopyDst(dispatch_thread_id); + uint3 src_idx = GetImageCopySrc(dispatch_thread_id); + + Image2CopyDstRg[dst_idx] = Uint32ToUint16x2(Uint8x4ToUint32(Image2CopySrc[src_idx])); +} + +[numthreads(1, 1, 1)] +void cs_copy_image2d_r16g16_image2d_r32(uint3 dispatch_thread_id : SV_DispatchThreadID) +{ + uint3 dst_idx = GetImageCopyDst(dispatch_thread_id); + uint3 src_idx = GetImageCopySrc(dispatch_thread_id); + + Image2CopyDstR[dst_idx] = Uint16x2ToUint32(Image2CopySrc[src_idx]); +} + +[numthreads(1, 1, 1)] +void cs_copy_image2d_r16g16_image2d_r8g8b8a8(uint3 dispatch_thread_id : SV_DispatchThreadID) +{ + uint3 dst_idx = GetImageCopyDst(dispatch_thread_id); + uint3 src_idx = GetImageCopySrc(dispatch_thread_id); + + Image2CopyDstRgba[dst_idx] = Uint32ToUint8x4(Uint16x2ToUint32(Image2CopySrc[src_idx])); +} + +[numthreads(1, 1, 1)] +void cs_copy_image2d_r32_image2d_r16g16(uint3 dispatch_thread_id : SV_DispatchThreadID) +{ + uint3 dst_idx = GetImageCopyDst(dispatch_thread_id); + uint3 src_idx = GetImageCopySrc(dispatch_thread_id); + + Image2CopyDstRg[dst_idx] = Uint32ToUint16x2(Image2CopySrc[src_idx]); +} + +[numthreads(1, 1, 1)] +void cs_copy_image2d_r32_image2d_r8g8b8a8(uint3 dispatch_thread_id : SV_DispatchThreadID) +{ + uint3 dst_idx = GetImageCopyDst(dispatch_thread_id); + uint3 src_idx = GetImageCopySrc(dispatch_thread_id); + + Image2CopyDstRgba[dst_idx] = Uint32ToUint8x4(Image2CopySrc[src_idx]); +} + +//#define COPY_1D_NUM_THREAD 64 //TODO +#define COPY_2D_NUM_THREAD_X 8 +#define COPY_2D_NUM_THREAD_Y 8 + +// Buffer<->Image copies + +// R32G32B32A32 +[numthreads(COPY_2D_NUM_THREAD_X, COPY_2D_NUM_THREAD_Y, 1)] +void cs_copy_buffer_image2d_r32g32b32a32(uint3 dispatch_thread_id : SV_DispatchThreadID) { + uint3 dst_idx = GetImageDst(dispatch_thread_id); + uint3 bounds = GetDestBounds(); + if (dst_idx.x >= bounds.x || dst_idx.y >= bounds.y) { + return; + } + + uint src_idx = GetBufferSrc128(dispatch_thread_id); + + Image2CopyDstRgba[dst_idx] = uint4( + BufferCopySrc.Load(src_idx), + BufferCopySrc.Load(src_idx + 1 * 4), + BufferCopySrc.Load(src_idx + 2 * 4), + BufferCopySrc.Load(src_idx + 3 * 4) + ); +} + +[numthreads(COPY_2D_NUM_THREAD_X, COPY_2D_NUM_THREAD_Y, 1)] +void cs_copy_image2d_r32g32b32a32_buffer(uint3 dispatch_thread_id : SV_DispatchThreadID) { + uint3 src_idx = GetImageSrc(dispatch_thread_id); + uint3 bounds = GetDestBounds(); + if (src_idx.x >= bounds.x || src_idx.y >= bounds.y) { + return; + } + + uint4 data = Image2CopySrc[src_idx]; + uint dst_idx = GetBufferDst128(dispatch_thread_id); + + BufferCopyDst.Store(dst_idx, data.x); + BufferCopyDst.Store(dst_idx + 1 * 4, data.y); + BufferCopyDst.Store(dst_idx + 2 * 4, data.z); + BufferCopyDst.Store(dst_idx + 3 * 4, data.w); +} + +// R32G32 +[numthreads(COPY_2D_NUM_THREAD_X, COPY_2D_NUM_THREAD_Y, 1)] +void cs_copy_buffer_image2d_r32g32(uint3 dispatch_thread_id : SV_DispatchThreadID) { + uint3 dst_idx = GetImageDst(dispatch_thread_id); + uint3 bounds = GetDestBounds(); + if (dst_idx.x >= bounds.x || dst_idx.y >= bounds.y) { + return; + } + + uint src_idx = GetBufferSrc64(dispatch_thread_id); + + Image2CopyDstRg[dst_idx] = uint2( + BufferCopySrc.Load(src_idx), + BufferCopySrc.Load(src_idx + 1 * 4) + ); +} + +[numthreads(COPY_2D_NUM_THREAD_X, COPY_2D_NUM_THREAD_Y, 1)] +void cs_copy_image2d_r32g32_buffer(uint3 dispatch_thread_id : SV_DispatchThreadID) { + uint3 src_idx = GetImageSrc(dispatch_thread_id); + uint3 bounds = GetDestBounds(); + if (src_idx.x >= bounds.x || src_idx.y >= bounds.y) { + return; + } + + uint2 data = Image2CopySrc[src_idx].rg; + uint dst_idx = GetBufferDst64(dispatch_thread_id); + + BufferCopyDst.Store(dst_idx , data.x); + BufferCopyDst.Store(dst_idx + 1 * 4, data.y); +} + +// R16G16B16A16 +[numthreads(COPY_2D_NUM_THREAD_X, COPY_2D_NUM_THREAD_Y, 1)] +void cs_copy_buffer_image2d_r16g16b16a16(uint3 dispatch_thread_id : SV_DispatchThreadID) { + uint3 dst_idx = GetImageDst(dispatch_thread_id); + uint3 bounds = GetDestBounds(); + if (dst_idx.x >= bounds.x || dst_idx.y >= bounds.y) { + return; + } + + uint src_idx = GetBufferSrc64(dispatch_thread_id); + + Image2CopyDstRgba[dst_idx] = uint4( + Uint32ToUint16x2(BufferCopySrc.Load(src_idx)), + Uint32ToUint16x2(BufferCopySrc.Load(src_idx + 1 * 4)) + ); +} + +[numthreads(COPY_2D_NUM_THREAD_X, COPY_2D_NUM_THREAD_Y, 1)] +void cs_copy_image2d_r16g16b16a16_buffer(uint3 dispatch_thread_id : SV_DispatchThreadID) { + uint3 src_idx = GetImageSrc(dispatch_thread_id); + uint3 bounds = GetDestBounds(); + if (src_idx.x >= bounds.x || src_idx.y >= bounds.y) { + return; + } + + uint4 data = Image2CopySrc[src_idx]; + uint dst_idx = GetBufferDst64(dispatch_thread_id); + + BufferCopyDst.Store(dst_idx, Uint16x2ToUint32(data.xy)); + BufferCopyDst.Store(dst_idx + 1 * 4, Uint16x2ToUint32(data.zw)); +} + +// R32 +[numthreads(COPY_2D_NUM_THREAD_X, 1, 1)] +void cs_copy_buffer_image1d_r32(uint3 dispatch_thread_id : SV_DispatchThreadID) { + uint3 dst_idx = GetImageDst(dispatch_thread_id); + uint3 bounds = GetDestBounds(); + if (dst_idx.x >= bounds.x) { + return; + } + + uint src_idx = GetBufferSrc32(dispatch_thread_id); + + Image1CopyDstR[dst_idx.xz] = BufferCopySrc.Load(src_idx); +} +[numthreads(COPY_2D_NUM_THREAD_X, COPY_2D_NUM_THREAD_Y, 1)] +void cs_copy_buffer_image2d_r32(uint3 dispatch_thread_id : SV_DispatchThreadID) { + uint3 dst_idx = GetImageDst(dispatch_thread_id); + uint3 bounds = GetDestBounds(); + if (dst_idx.x >= bounds.x || dst_idx.y >= bounds.y) { + return; + } + + uint src_idx = GetBufferSrc32(dispatch_thread_id); + + Image2CopyDstR[dst_idx] = BufferCopySrc.Load(src_idx); +} + +[numthreads(COPY_2D_NUM_THREAD_X, COPY_2D_NUM_THREAD_Y, 1)] +void cs_copy_image2d_r32_buffer(uint3 dispatch_thread_id : SV_DispatchThreadID) { + uint3 src_idx = GetImageSrc(dispatch_thread_id); + uint3 bounds = GetDestBounds(); + if (src_idx.x >= bounds.x || src_idx.y >= bounds.y) { + return; + } + + uint dst_idx = GetBufferDst32(dispatch_thread_id); + + BufferCopyDst.Store(dst_idx, Image2CopySrc[src_idx].r); +} + +// R16G16 +[numthreads(COPY_2D_NUM_THREAD_X, COPY_2D_NUM_THREAD_Y, 1)] +void cs_copy_buffer_image2d_r16g16(uint3 dispatch_thread_id : SV_DispatchThreadID) { + uint3 dst_idx = GetImageDst(dispatch_thread_id); + uint3 bounds = GetDestBounds(); + if (dst_idx.x >= bounds.x || dst_idx.y >= bounds.y) { + return; + } + + uint src_idx = GetBufferSrc32(dispatch_thread_id); + + Image2CopyDstRg[dst_idx] = Uint32ToUint16x2(BufferCopySrc.Load(src_idx)); +} + +[numthreads(COPY_2D_NUM_THREAD_X, COPY_2D_NUM_THREAD_Y, 1)] +void cs_copy_image2d_r16g16_buffer(uint3 dispatch_thread_id : SV_DispatchThreadID) { + uint3 src_idx = GetImageSrc(dispatch_thread_id); + uint3 bounds = GetDestBounds(); + if (src_idx.x >= bounds.x || src_idx.y >= bounds.y) { + return; + } + + uint dst_idx = GetBufferDst32(dispatch_thread_id); + + BufferCopyDst.Store(dst_idx, Uint16x2ToUint32(Image2CopySrc[src_idx].xy)); +} + +// R8G8B8A8 +[numthreads(COPY_2D_NUM_THREAD_X, 1, 1)] +void cs_copy_buffer_image1d_r8g8b8a8(uint3 dispatch_thread_id : SV_DispatchThreadID) { + uint3 dst_idx = GetImageDst(dispatch_thread_id); + uint3 bounds = GetDestBounds(); + if (dst_idx.x >= bounds.x) { + return; + } + + uint src_idx = GetBufferSrc32(dispatch_thread_id); + + Image1CopyDstRgba[dst_idx.xz] = Uint32ToUint8x4(BufferCopySrc.Load(src_idx)); +} +[numthreads(COPY_2D_NUM_THREAD_X, COPY_2D_NUM_THREAD_Y, 1)] +void cs_copy_buffer_image2d_r8g8b8a8(uint3 dispatch_thread_id : SV_DispatchThreadID) { + uint3 dst_idx = GetImageDst(dispatch_thread_id); + uint3 bounds = GetDestBounds(); + if (dst_idx.x >= bounds.x || dst_idx.y >= bounds.y) { + return; + } + + uint src_idx = GetBufferSrc32(dispatch_thread_id); + + Image2CopyDstRgba[dst_idx] = Uint32ToUint8x4(BufferCopySrc.Load(src_idx)); +} + +[numthreads(COPY_2D_NUM_THREAD_X, COPY_2D_NUM_THREAD_Y, 1)] +void cs_copy_image2d_r8g8b8a8_buffer(uint3 dispatch_thread_id : SV_DispatchThreadID) { + uint3 src_idx = GetImageSrc(dispatch_thread_id); + uint3 bounds = GetDestBounds(); + if (src_idx.x >= bounds.x || src_idx.y >= bounds.y) { + return; + } + + uint dst_idx = GetBufferDst32(dispatch_thread_id); + + BufferCopyDst.Store(dst_idx, Uint8x4ToUint32(Image2CopySrc[src_idx])); +} + +// B8G8R8A8 +[numthreads(COPY_2D_NUM_THREAD_X, COPY_2D_NUM_THREAD_Y, 1)] +void cs_copy_image2d_b8g8r8a8_buffer(uint3 dispatch_thread_id : SV_DispatchThreadID) { + uint3 src_idx = GetImageSrc(dispatch_thread_id); + uint3 bounds = GetDestBounds(); + if (src_idx.x >= bounds.x || src_idx.y >= bounds.y) { + return; + } + + uint dst_idx = GetBufferDst32(dispatch_thread_id); + + BufferCopyDst.Store(dst_idx, Uint8x4ToUint32(Float4ToUint8x4(ImageCopy2SrcBgra[src_idx].bgra))); +} + +// R16 +[numthreads(COPY_2D_NUM_THREAD_X, COPY_2D_NUM_THREAD_Y, 1)] +void cs_copy_buffer_image2d_r16(uint3 dispatch_thread_id : SV_DispatchThreadID) { + uint3 dst_idx = GetImageDst(uint3(2, 1, 0) * dispatch_thread_id); + uint3 bounds = GetDestBounds(); + if (dst_idx.x >= bounds.x || dst_idx.y >= bounds.y) { + return; + } + + uint src_idx = GetBufferSrc16(dispatch_thread_id); + uint2 data = Uint32ToUint16x2(BufferCopySrc.Load(src_idx)); + + uint remaining_x = bounds.x - dst_idx.x; + + if (remaining_x >= 2) { + Image2CopyDstR[dst_idx + uint3(1, 0, 0)] = data.y; + } + if (remaining_x >= 1) { + Image2CopyDstR[dst_idx + uint3(0, 0, 0)] = data.x; + } +} + +[numthreads(COPY_2D_NUM_THREAD_X, COPY_2D_NUM_THREAD_Y, 1)] +void cs_copy_image2d_r16_buffer(uint3 dispatch_thread_id : SV_DispatchThreadID) { + uint3 src_idx = GetImageSrc(uint3(2, 1, 0) * dispatch_thread_id); + uint3 bounds = GetDestBounds(); + if (src_idx.x >= bounds.x || src_idx.y >= bounds.y) { + return; + } + + uint dst_idx = GetBufferDst16(dispatch_thread_id); + + uint upper = Image2CopySrc[src_idx].r; + uint lower = Image2CopySrc[src_idx + uint3(1, 0, 0)].r; + + BufferCopyDst.Store(dst_idx, Uint16x2ToUint32(uint2(upper, lower))); +} + +// R8G8 +[numthreads(COPY_2D_NUM_THREAD_X, 1, 1)] +void cs_copy_buffer_image1d_r8g8(uint3 dispatch_thread_id : SV_DispatchThreadID) { + uint3 dst_idx = GetImageDst(uint3(2, 1, 0) * dispatch_thread_id); + uint3 bounds = GetDestBounds(); + if (dst_idx.x >= bounds.x) { + return; + } + + uint src_idx = GetBufferSrc16(dispatch_thread_id); + + uint4 data = Uint32ToUint8x4(BufferCopySrc.Load(src_idx)); + + uint remaining_x = bounds.x - dst_idx.x; + + if (remaining_x >= 2) { + Image1CopyDstRg[dst_idx.xz + uint2(1, 0)] = data.zw; + } + if (remaining_x >= 1) { + Image1CopyDstRg[dst_idx.xz + uint2(0, 0)] = data.xy; + } +} + +[numthreads(COPY_2D_NUM_THREAD_X, COPY_2D_NUM_THREAD_Y, 1)] +void cs_copy_buffer_image2d_r8g8(uint3 dispatch_thread_id : SV_DispatchThreadID) { + uint3 dst_idx = GetImageDst(uint3(2, 1, 0) * dispatch_thread_id); + uint3 bounds = GetDestBounds(); + if (dst_idx.x >= bounds.x || dst_idx.y >= bounds.y) { + return; + } + + uint src_idx = GetBufferSrc16(dispatch_thread_id); + + uint4 data = Uint32ToUint8x4(BufferCopySrc.Load(src_idx)); + + uint remaining_x = bounds.x - dst_idx.x; + + if (remaining_x >= 2) { + Image2CopyDstRg[dst_idx + uint3(1, 0, 0)] = data.zw; + } + if (remaining_x >= 1) { + Image2CopyDstRg[dst_idx + uint3(0, 0, 0)] = data.xy; + } +} + +[numthreads(COPY_2D_NUM_THREAD_X, COPY_2D_NUM_THREAD_Y, 1)] +void cs_copy_image2d_r8g8_buffer(uint3 dispatch_thread_id : SV_DispatchThreadID) { + uint3 src_idx = GetImageSrc(uint3(2, 1, 0) * dispatch_thread_id); + uint3 bounds = GetDestBounds(); + if (src_idx.x >= bounds.x || src_idx.y >= bounds.y) { + return; + } + + uint dst_idx = GetBufferDst16(dispatch_thread_id); + + uint2 lower = Image2CopySrc[src_idx].xy; + uint2 upper = Image2CopySrc[src_idx + uint3(1, 0, 0)].xy; + + BufferCopyDst.Store(dst_idx, Uint8x4ToUint32(uint4(lower.x, lower.y, upper.x, upper.y))); +} + +// R8 +[numthreads(COPY_2D_NUM_THREAD_X, 1, 1)] +void cs_copy_buffer_image1d_r8(uint3 dispatch_thread_id : SV_DispatchThreadID) { + uint3 dst_idx = GetImageDst(uint3(4, 1, 0) * dispatch_thread_id); + uint3 bounds = GetDestBounds(); + if (dst_idx.x >= bounds.x) { + return; + } + + uint src_idx = GetBufferSrc8(dispatch_thread_id); + uint4 data = Uint32ToUint8x4(BufferCopySrc.Load(src_idx)); + + uint remaining_x = bounds.x - dst_idx.x; + + if (remaining_x >= 4) { + Image1CopyDstR[dst_idx.xz + uint2(3, 0)] = data.w; + } + if (remaining_x >= 3) { + Image1CopyDstR[dst_idx.xz + uint2(2, 0)] = data.z; + } + if (remaining_x >= 2) { + Image1CopyDstR[dst_idx.xz + uint2(1, 0)] = data.y; + } + if (remaining_x >= 1) { + Image1CopyDstR[dst_idx.xz + uint2(0, 0)] = data.x; + } +} +[numthreads(COPY_2D_NUM_THREAD_X, COPY_2D_NUM_THREAD_Y, 1)] +void cs_copy_buffer_image2d_r8(uint3 dispatch_thread_id : SV_DispatchThreadID) { + uint3 dst_idx = GetImageDst(uint3(4, 1, 0) * dispatch_thread_id); + uint3 bounds = GetDestBounds(); + if (dst_idx.x >= bounds.x || dst_idx.y >= bounds.y) { + return; + } + + uint src_idx = GetBufferSrc8(dispatch_thread_id); + uint4 data = Uint32ToUint8x4(BufferCopySrc.Load(src_idx)); + + uint remaining_x = bounds.x - dst_idx.x; + + if (remaining_x >= 4) { + Image2CopyDstR[dst_idx + uint3(3, 0, 0)] = data.w; + } + if (remaining_x >= 3) { + Image2CopyDstR[dst_idx + uint3(2, 0, 0)] = data.z; + } + if (remaining_x >= 2) { + Image2CopyDstR[dst_idx + uint3(1, 0, 0)] = data.y; + } + if (remaining_x >= 1) { + Image2CopyDstR[dst_idx + uint3(0, 0, 0)] = data.x; + } +} +[numthreads(COPY_2D_NUM_THREAD_X, COPY_2D_NUM_THREAD_Y, 1)] +void cs_copy_image2d_r8_buffer(uint3 dispatch_thread_id : SV_DispatchThreadID) { + uint3 src_idx = GetImageSrc(uint3(4, 1, 0) * dispatch_thread_id); + uint3 bounds = GetDestBounds(); + if (src_idx.x >= bounds.x || src_idx.y >= bounds.y) { + return; + } + + uint dst_idx = GetBufferDst8(dispatch_thread_id); + + BufferCopyDst.Store(dst_idx, Uint8x4ToUint32(uint4( + Image2CopySrc[src_idx].r, + Image2CopySrc[src_idx + uint3(1, 0, 0)].r, + Image2CopySrc[src_idx + uint3(2, 0, 0)].r, + Image2CopySrc[src_idx + uint3(3, 0, 0)].r + ))); +} diff --git a/third_party/rust/gfx-backend-dx11/src/conv.rs b/third_party/rust/gfx-backend-dx11/src/conv.rs index 029666fc31b9..f2f99c0a923e 100644 --- a/third_party/rust/gfx-backend-dx11/src/conv.rs +++ b/third_party/rust/gfx-backend-dx11/src/conv.rs @@ -1,874 +1,874 @@ -use auxil::ShaderStage; -use hal::{ - format::{Aspects, Format, FormatDesc}, - image, - pso::{ - BlendDesc, BlendOp, ColorBlendDesc, Comparison, DepthBias, DepthStencilDesc, Face, Factor, - FrontFace, InputAssemblerDesc, Multisampling, PolygonMode, Rasterizer, Rect, Sided, State, - StencilFace, StencilOp, StencilValue, Viewport, - }, - IndexType, -}; - -use spirv_cross::{hlsl, spirv}; - -use winapi::{ - shared::{ - dxgiformat::*, - minwindef::{FALSE, INT, TRUE}, - }, - um::{d3d11::*, d3dcommon::*}, -}; - -use std::mem; - -pub fn map_index_type(ty: IndexType) -> DXGI_FORMAT { - match ty { - IndexType::U16 => DXGI_FORMAT_R16_UINT, - IndexType::U32 => DXGI_FORMAT_R32_UINT, - } -} - -// TODO: add aspect parameter -pub fn viewable_format(format: DXGI_FORMAT) -> DXGI_FORMAT { - match format { - DXGI_FORMAT_D32_FLOAT_S8X24_UINT => DXGI_FORMAT_R32_FLOAT_X8X24_TYPELESS, - DXGI_FORMAT_D32_FLOAT => DXGI_FORMAT_R32_FLOAT, - DXGI_FORMAT_D16_UNORM => DXGI_FORMAT_R16_UNORM, - _ => format, - } -} - -// TODO: stolen from d3d12 backend, maybe share function somehow? -pub fn map_format(format: Format) -> Option { - use hal::format::Format::*; - - let format = match format { - R5g6b5Unorm => DXGI_FORMAT_B5G6R5_UNORM, - R5g5b5a1Unorm => DXGI_FORMAT_B5G5R5A1_UNORM, - R8Unorm => DXGI_FORMAT_R8_UNORM, - R8Snorm => DXGI_FORMAT_R8_SNORM, - R8Uint => DXGI_FORMAT_R8_UINT, - R8Sint => DXGI_FORMAT_R8_SINT, - Rg8Unorm => DXGI_FORMAT_R8G8_UNORM, - Rg8Snorm => DXGI_FORMAT_R8G8_SNORM, - Rg8Uint => DXGI_FORMAT_R8G8_UINT, - Rg8Sint => DXGI_FORMAT_R8G8_SINT, - Rgba8Unorm => DXGI_FORMAT_R8G8B8A8_UNORM, - Rgba8Snorm => DXGI_FORMAT_R8G8B8A8_SNORM, - Rgba8Uint => DXGI_FORMAT_R8G8B8A8_UINT, - Rgba8Sint => DXGI_FORMAT_R8G8B8A8_SINT, - Rgba8Srgb => DXGI_FORMAT_R8G8B8A8_UNORM_SRGB, - Bgra8Unorm => DXGI_FORMAT_B8G8R8A8_UNORM, - Bgra8Srgb => DXGI_FORMAT_B8G8R8A8_UNORM_SRGB, - A2b10g10r10Unorm => DXGI_FORMAT_R10G10B10A2_UNORM, - A2b10g10r10Uint => DXGI_FORMAT_R10G10B10A2_UINT, - R16Unorm => DXGI_FORMAT_R16_UNORM, - R16Snorm => DXGI_FORMAT_R16_SNORM, - R16Uint => DXGI_FORMAT_R16_UINT, - R16Sint => DXGI_FORMAT_R16_SINT, - R16Sfloat => DXGI_FORMAT_R16_FLOAT, - Rg16Unorm => DXGI_FORMAT_R16G16_UNORM, - Rg16Snorm => DXGI_FORMAT_R16G16_SNORM, - Rg16Uint => DXGI_FORMAT_R16G16_UINT, - Rg16Sint => DXGI_FORMAT_R16G16_SINT, - Rg16Sfloat => DXGI_FORMAT_R16G16_FLOAT, - Rgba16Unorm => DXGI_FORMAT_R16G16B16A16_UNORM, - Rgba16Snorm => DXGI_FORMAT_R16G16B16A16_SNORM, - Rgba16Uint => DXGI_FORMAT_R16G16B16A16_UINT, - Rgba16Sint => DXGI_FORMAT_R16G16B16A16_SINT, - Rgba16Sfloat => DXGI_FORMAT_R16G16B16A16_FLOAT, - R32Uint => DXGI_FORMAT_R32_UINT, - R32Sint => DXGI_FORMAT_R32_SINT, - R32Sfloat => DXGI_FORMAT_R32_FLOAT, - Rg32Uint => DXGI_FORMAT_R32G32_UINT, - Rg32Sint => DXGI_FORMAT_R32G32_SINT, - Rg32Sfloat => DXGI_FORMAT_R32G32_FLOAT, - Rgb32Uint => DXGI_FORMAT_R32G32B32_UINT, - Rgb32Sint => DXGI_FORMAT_R32G32B32_SINT, - Rgb32Sfloat => DXGI_FORMAT_R32G32B32_FLOAT, - Rgba32Uint => DXGI_FORMAT_R32G32B32A32_UINT, - Rgba32Sint => DXGI_FORMAT_R32G32B32A32_SINT, - Rgba32Sfloat => DXGI_FORMAT_R32G32B32A32_FLOAT, - B10g11r11Ufloat => DXGI_FORMAT_R11G11B10_FLOAT, - E5b9g9r9Ufloat => DXGI_FORMAT_R9G9B9E5_SHAREDEXP, - D16Unorm => DXGI_FORMAT_D16_UNORM, - D32Sfloat => DXGI_FORMAT_D32_FLOAT, - D32SfloatS8Uint => DXGI_FORMAT_D32_FLOAT_S8X24_UINT, - Bc1RgbUnorm | Bc1RgbaUnorm => DXGI_FORMAT_BC1_UNORM, - Bc1RgbSrgb | Bc1RgbaSrgb => DXGI_FORMAT_BC1_UNORM_SRGB, - Bc2Unorm => DXGI_FORMAT_BC2_UNORM, - Bc2Srgb => DXGI_FORMAT_BC2_UNORM_SRGB, - Bc3Unorm => DXGI_FORMAT_BC3_UNORM, - Bc3Srgb => DXGI_FORMAT_BC3_UNORM_SRGB, - Bc4Unorm => DXGI_FORMAT_BC4_UNORM, - Bc4Snorm => DXGI_FORMAT_BC4_SNORM, - Bc5Unorm => DXGI_FORMAT_BC5_UNORM, - Bc5Snorm => DXGI_FORMAT_BC5_SNORM, - Bc6hUfloat => DXGI_FORMAT_BC6H_UF16, - Bc6hSfloat => DXGI_FORMAT_BC6H_SF16, - Bc7Unorm => DXGI_FORMAT_BC7_UNORM, - Bc7Srgb => DXGI_FORMAT_BC7_UNORM_SRGB, - - _ => return None, - }; - - Some(format) -} - -pub fn map_format_nosrgb(format: Format) -> Option { - // NOTE: DXGI doesn't allow sRGB format on the swapchain, but - // creating RTV of swapchain buffers with sRGB works - match format { - Format::Bgra8Srgb => Some(DXGI_FORMAT_B8G8R8A8_UNORM), - Format::Rgba8Srgb => Some(DXGI_FORMAT_R8G8B8A8_UNORM), - _ => map_format(format), - } -} - -#[derive(Debug, Clone)] -pub struct DecomposedDxgiFormat { - pub typeless: DXGI_FORMAT, - pub srv: Option, - pub rtv: Option, - pub uav: Option, - pub dsv: Option, - // the format we use internally for operating on textures (eg. Rgba8 uses R32 internally for - // copies) - pub copy_uav: Option, - pub copy_srv: Option, -} - -impl DecomposedDxgiFormat { - pub const UNKNOWN: DecomposedDxgiFormat = DecomposedDxgiFormat { - typeless: DXGI_FORMAT_UNKNOWN, - srv: None, - rtv: None, - uav: None, - dsv: None, - copy_uav: None, - copy_srv: None, - }; - - // TODO: we probably want to pass in usage flags or similar to allow for our `typeless_format` - // field to only contain the input format (eg. depth only rather than typeless likely - // improves perf since the driver doesn't need to expose internals) - // - // TODO: we also want aspect for determining depth/stencil - pub fn from_dxgi_format(format: DXGI_FORMAT) -> DecomposedDxgiFormat { - match format { - DXGI_FORMAT_R8G8B8A8_UNORM - | DXGI_FORMAT_R8G8B8A8_SNORM - | DXGI_FORMAT_R8G8B8A8_UINT - | DXGI_FORMAT_R8G8B8A8_SINT - | DXGI_FORMAT_R8G8B8A8_UNORM_SRGB => DecomposedDxgiFormat { - typeless: DXGI_FORMAT_R8G8B8A8_TYPELESS, - srv: Some(format), - rtv: Some(format), - uav: Some(format), - dsv: None, - copy_uav: Some(DXGI_FORMAT_R32_UINT), - copy_srv: Some(DXGI_FORMAT_R8G8B8A8_UINT), - }, - - DXGI_FORMAT_B8G8R8A8_UNORM | DXGI_FORMAT_B8G8R8A8_UNORM_SRGB => DecomposedDxgiFormat { - typeless: DXGI_FORMAT_B8G8R8A8_TYPELESS, - srv: Some(format), - rtv: Some(format), - uav: Some(DXGI_FORMAT_B8G8R8A8_UNORM), - dsv: None, - copy_uav: Some(DXGI_FORMAT_R32_UINT), - copy_srv: Some(DXGI_FORMAT_B8G8R8A8_UNORM), - }, - - DXGI_FORMAT_A8_UNORM => DecomposedDxgiFormat { - typeless: format, - srv: Some(format), - rtv: Some(format), - uav: Some(format), - dsv: None, - copy_uav: Some(format), - copy_srv: Some(format), - }, - - DXGI_FORMAT_R8_UNORM | DXGI_FORMAT_R8_SNORM | DXGI_FORMAT_R8_UINT - | DXGI_FORMAT_R8_SINT => DecomposedDxgiFormat { - typeless: DXGI_FORMAT_R8_TYPELESS, - srv: Some(format), - rtv: Some(format), - uav: Some(format), - dsv: None, - copy_uav: Some(DXGI_FORMAT_R8_UINT), - copy_srv: Some(DXGI_FORMAT_R8_UINT), - }, - - DXGI_FORMAT_R8G8_UNORM - | DXGI_FORMAT_R8G8_SNORM - | DXGI_FORMAT_R8G8_UINT - | DXGI_FORMAT_R8G8_SINT => DecomposedDxgiFormat { - typeless: DXGI_FORMAT_R8G8_TYPELESS, - srv: Some(format), - rtv: Some(format), - uav: Some(format), - dsv: None, - copy_uav: Some(DXGI_FORMAT_R8G8_UINT), - copy_srv: Some(DXGI_FORMAT_R8G8_UINT), - }, - - DXGI_FORMAT_D16_UNORM => DecomposedDxgiFormat { - typeless: DXGI_FORMAT_R16_TYPELESS, - srv: Some(DXGI_FORMAT_R16_FLOAT), - rtv: Some(DXGI_FORMAT_R16_FLOAT), - uav: Some(DXGI_FORMAT_R16_FLOAT), - dsv: Some(format), - copy_uav: Some(DXGI_FORMAT_R16_UINT), - copy_srv: Some(DXGI_FORMAT_R16_UINT), - }, - - DXGI_FORMAT_R16_UNORM - | DXGI_FORMAT_R16_SNORM - | DXGI_FORMAT_R16_UINT - | DXGI_FORMAT_R16_SINT - | DXGI_FORMAT_R16_FLOAT => DecomposedDxgiFormat { - typeless: DXGI_FORMAT_R16_TYPELESS, - srv: Some(format), - rtv: Some(format), - uav: Some(format), - dsv: Some(DXGI_FORMAT_D16_UNORM), - copy_uav: Some(DXGI_FORMAT_R16_UINT), - copy_srv: Some(DXGI_FORMAT_R16_UINT), - }, - - DXGI_FORMAT_R16G16_UNORM - | DXGI_FORMAT_R16G16_SNORM - | DXGI_FORMAT_R16G16_UINT - | DXGI_FORMAT_R16G16_SINT - | DXGI_FORMAT_R16G16_FLOAT => DecomposedDxgiFormat { - typeless: DXGI_FORMAT_R16G16_TYPELESS, - srv: Some(format), - rtv: Some(format), - uav: Some(format), - dsv: None, - copy_uav: Some(DXGI_FORMAT_R32_UINT), - copy_srv: Some(DXGI_FORMAT_R16G16_UINT), - }, - - DXGI_FORMAT_R16G16B16A16_UNORM - | DXGI_FORMAT_R16G16B16A16_SNORM - | DXGI_FORMAT_R16G16B16A16_UINT - | DXGI_FORMAT_R16G16B16A16_SINT - | DXGI_FORMAT_R16G16B16A16_FLOAT => DecomposedDxgiFormat { - typeless: DXGI_FORMAT_R16G16B16A16_TYPELESS, - srv: Some(format), - rtv: Some(format), - uav: Some(format), - dsv: None, - copy_uav: Some(DXGI_FORMAT_R16G16B16A16_UINT), - copy_srv: Some(DXGI_FORMAT_R16G16B16A16_UINT), - }, - - DXGI_FORMAT_D32_FLOAT_S8X24_UINT => DecomposedDxgiFormat { - typeless: DXGI_FORMAT_R32G8X24_TYPELESS, - // TODO: depth or stencil? - srv: Some(DXGI_FORMAT_R32_FLOAT_X8X24_TYPELESS), - rtv: None, - uav: None, - dsv: Some(format), - copy_uav: None, - copy_srv: Some(DXGI_FORMAT_R32_FLOAT_X8X24_TYPELESS), - }, - - DXGI_FORMAT_D32_FLOAT => DecomposedDxgiFormat { - typeless: DXGI_FORMAT_R32_TYPELESS, - srv: Some(DXGI_FORMAT_R32_FLOAT), - rtv: None, - uav: None, - dsv: Some(format), - copy_uav: Some(DXGI_FORMAT_R32_UINT), - copy_srv: Some(DXGI_FORMAT_R32_UINT), - }, - - DXGI_FORMAT_R32_UINT | DXGI_FORMAT_R32_SINT | DXGI_FORMAT_R32_FLOAT => { - DecomposedDxgiFormat { - typeless: DXGI_FORMAT_R32_TYPELESS, - srv: Some(format), - rtv: Some(format), - uav: Some(format), - dsv: Some(DXGI_FORMAT_D32_FLOAT), - copy_uav: Some(DXGI_FORMAT_R32_UINT), - copy_srv: Some(DXGI_FORMAT_R32_UINT), - } - } - - DXGI_FORMAT_R32G32_UINT | DXGI_FORMAT_R32G32_SINT | DXGI_FORMAT_R32G32_FLOAT => { - DecomposedDxgiFormat { - typeless: DXGI_FORMAT_R32G32_TYPELESS, - srv: Some(format), - rtv: Some(format), - uav: Some(format), - dsv: None, - copy_uav: Some(DXGI_FORMAT_R32G32_UINT), - copy_srv: Some(DXGI_FORMAT_R32G32_UINT), - } - } - - // TODO: should we just convert to Rgba32 internally? - DXGI_FORMAT_R32G32B32_UINT - | DXGI_FORMAT_R32G32B32_SINT - | DXGI_FORMAT_R32G32B32_FLOAT => DecomposedDxgiFormat { - typeless: DXGI_FORMAT_R32G32_TYPELESS, - srv: Some(format), - rtv: None, - uav: None, - dsv: None, - copy_uav: Some(DXGI_FORMAT_R32G32B32_UINT), - copy_srv: Some(DXGI_FORMAT_R32G32B32_UINT), - }, - - DXGI_FORMAT_R32G32B32A32_UINT - | DXGI_FORMAT_R32G32B32A32_SINT - | DXGI_FORMAT_R32G32B32A32_FLOAT => DecomposedDxgiFormat { - typeless: DXGI_FORMAT_R32G32B32A32_TYPELESS, - srv: Some(format), - rtv: Some(format), - uav: Some(format), - dsv: None, - copy_uav: Some(DXGI_FORMAT_R32G32B32A32_UINT), - copy_srv: Some(DXGI_FORMAT_R32G32B32A32_UINT), - }, - - DXGI_FORMAT_R10G10B10A2_UNORM | DXGI_FORMAT_R10G10B10A2_UINT => DecomposedDxgiFormat { - typeless: DXGI_FORMAT_R10G10B10A2_TYPELESS, - srv: Some(format), - rtv: Some(format), - uav: Some(format), - dsv: None, - copy_uav: Some(DXGI_FORMAT_R32_UINT), - copy_srv: Some(DXGI_FORMAT_R10G10B10A2_UINT), - }, - - DXGI_FORMAT_R11G11B10_FLOAT => DecomposedDxgiFormat { - typeless: format, - srv: Some(format), - rtv: Some(format), - uav: Some(format), - dsv: None, - copy_uav: Some(format), - copy_srv: Some(format), - }, - - DXGI_FORMAT_R9G9B9E5_SHAREDEXP => DecomposedDxgiFormat { - typeless: format, - srv: Some(format), - rtv: None, - uav: None, - dsv: None, - // NOTE: read only - copy_uav: None, - copy_srv: Some(format), - }, - - DXGI_FORMAT_BC1_UNORM | DXGI_FORMAT_BC1_UNORM_SRGB => DecomposedDxgiFormat { - typeless: DXGI_FORMAT_BC1_TYPELESS, - srv: Some(format), - rtv: None, - uav: None, - dsv: None, - // NOTE: read only - copy_uav: None, - copy_srv: Some(format), - }, - - DXGI_FORMAT_BC2_UNORM | DXGI_FORMAT_BC2_UNORM_SRGB => DecomposedDxgiFormat { - typeless: DXGI_FORMAT_BC2_TYPELESS, - srv: Some(format), - rtv: None, - uav: None, - dsv: None, - // NOTE: read only - copy_uav: None, - copy_srv: Some(format), - }, - - DXGI_FORMAT_BC3_UNORM | DXGI_FORMAT_BC3_UNORM_SRGB => DecomposedDxgiFormat { - typeless: DXGI_FORMAT_BC3_TYPELESS, - srv: Some(format), - rtv: None, - uav: None, - dsv: None, - // NOTE: read only - copy_uav: None, - copy_srv: Some(format), - }, - - DXGI_FORMAT_BC4_UNORM | DXGI_FORMAT_BC4_SNORM => DecomposedDxgiFormat { - typeless: DXGI_FORMAT_BC4_TYPELESS, - srv: Some(format), - rtv: None, - uav: None, - dsv: None, - // NOTE: read only - copy_uav: None, - copy_srv: Some(format), - }, - - DXGI_FORMAT_BC5_UNORM | DXGI_FORMAT_BC5_SNORM => DecomposedDxgiFormat { - typeless: format, - srv: Some(format), - rtv: None, - uav: None, - dsv: None, - // NOTE: read only - copy_uav: None, - copy_srv: Some(format), - }, - - DXGI_FORMAT_BC6H_UF16 | DXGI_FORMAT_BC6H_SF16 => DecomposedDxgiFormat { - typeless: DXGI_FORMAT_BC6H_TYPELESS, - srv: Some(format), - rtv: None, - uav: None, - dsv: None, - // NOTE: read only - copy_uav: None, - copy_srv: Some(format), - }, - - // TODO: srgb craziness - DXGI_FORMAT_BC7_UNORM | DXGI_FORMAT_BC7_UNORM_SRGB => DecomposedDxgiFormat { - typeless: DXGI_FORMAT_BC7_TYPELESS, - srv: Some(format), - rtv: None, - uav: None, - dsv: None, - // NOTE: read only - copy_uav: None, - copy_srv: Some(format), - }, - - _ => unimplemented!(), - } - } -} - -pub fn map_viewport(viewport: &Viewport) -> D3D11_VIEWPORT { - D3D11_VIEWPORT { - TopLeftX: viewport.rect.x as _, - TopLeftY: viewport.rect.y as _, - Width: viewport.rect.w as _, - Height: viewport.rect.h as _, - MinDepth: viewport.depth.start, - MaxDepth: viewport.depth.end, - } -} - -pub fn map_rect(rect: &Rect) -> D3D11_RECT { - D3D11_RECT { - left: rect.x as _, - top: rect.y as _, - right: (rect.x + rect.w) as _, - bottom: (rect.y + rect.h) as _, - } -} - -pub fn map_topology(ia: &InputAssemblerDesc) -> D3D11_PRIMITIVE_TOPOLOGY { - use hal::pso::Primitive::*; - match (ia.primitive, ia.with_adjacency) { - (PointList, false) => D3D_PRIMITIVE_TOPOLOGY_POINTLIST, - (PointList, true) => panic!("Points can't have adjacency info"), - (LineList, false) => D3D_PRIMITIVE_TOPOLOGY_LINELIST, - (LineList, true) => D3D_PRIMITIVE_TOPOLOGY_LINELIST_ADJ, - (LineStrip, false) => D3D_PRIMITIVE_TOPOLOGY_LINESTRIP, - (LineStrip, true) => D3D_PRIMITIVE_TOPOLOGY_LINESTRIP_ADJ, - (TriangleList, false) => D3D_PRIMITIVE_TOPOLOGY_TRIANGLELIST, - (TriangleList, true) => D3D_PRIMITIVE_TOPOLOGY_TRIANGLELIST_ADJ, - (TriangleStrip, false) => D3D_PRIMITIVE_TOPOLOGY_TRIANGLESTRIP, - (TriangleStrip, true) => D3D_PRIMITIVE_TOPOLOGY_TRIANGLESTRIP_ADJ, - (PatchList(num), false) => { - assert!(num != 0); - D3D_PRIMITIVE_TOPOLOGY_1_CONTROL_POINT_PATCHLIST + (num as u32) - 1 - } - (_, true) => panic!("Patches can't have adjacency info"), - } -} - -fn map_fill_mode(mode: PolygonMode) -> D3D11_FILL_MODE { - match mode { - PolygonMode::Fill => D3D11_FILL_SOLID, - PolygonMode::Line => D3D11_FILL_WIREFRAME, - // TODO: return error - _ => unimplemented!(), - } -} - -fn map_cull_mode(mode: Face) -> D3D11_CULL_MODE { - match mode { - Face::NONE => D3D11_CULL_NONE, - Face::FRONT => D3D11_CULL_FRONT, - Face::BACK => D3D11_CULL_BACK, - _ => panic!("Culling both front and back faces is not supported"), - } -} - -pub(crate) fn map_rasterizer_desc( - desc: &Rasterizer, - multisampling_desc: &Option, -) -> D3D11_RASTERIZER_DESC { - let bias = match desc.depth_bias { - //TODO: support dynamic depth bias - Some(State::Static(db)) => db, - Some(_) | None => DepthBias::default(), - }; - if let State::Static(w) = desc.line_width { - super::validate_line_width(w); - } - let multisampled = multisampling_desc.is_some(); - D3D11_RASTERIZER_DESC { - FillMode: map_fill_mode(desc.polygon_mode), - CullMode: map_cull_mode(desc.cull_face), - FrontCounterClockwise: match desc.front_face { - FrontFace::Clockwise => FALSE, - FrontFace::CounterClockwise => TRUE, - }, - DepthBias: bias.const_factor as INT, - DepthBiasClamp: bias.clamp, - SlopeScaledDepthBias: bias.slope_factor, - DepthClipEnable: !desc.depth_clamping as _, - // TODO: - ScissorEnable: TRUE, - MultisampleEnable: multisampled as _, - AntialiasedLineEnable: multisampled as _, - // TODO: conservative raster in >=11.x - } -} - -fn map_blend_factor(factor: Factor) -> D3D11_BLEND { - match factor { - Factor::Zero => D3D11_BLEND_ZERO, - Factor::One => D3D11_BLEND_ONE, - Factor::SrcColor => D3D11_BLEND_SRC_COLOR, - Factor::OneMinusSrcColor => D3D11_BLEND_INV_SRC_COLOR, - Factor::DstColor => D3D11_BLEND_DEST_COLOR, - Factor::OneMinusDstColor => D3D11_BLEND_INV_DEST_COLOR, - Factor::SrcAlpha => D3D11_BLEND_SRC_ALPHA, - Factor::OneMinusSrcAlpha => D3D11_BLEND_INV_SRC_ALPHA, - Factor::DstAlpha => D3D11_BLEND_DEST_ALPHA, - Factor::OneMinusDstAlpha => D3D11_BLEND_INV_DEST_ALPHA, - Factor::ConstColor | Factor::ConstAlpha => D3D11_BLEND_BLEND_FACTOR, - Factor::OneMinusConstColor | Factor::OneMinusConstAlpha => D3D11_BLEND_INV_BLEND_FACTOR, - Factor::SrcAlphaSaturate => D3D11_BLEND_SRC_ALPHA_SAT, - Factor::Src1Color => D3D11_BLEND_SRC1_COLOR, - Factor::OneMinusSrc1Color => D3D11_BLEND_INV_SRC1_COLOR, - Factor::Src1Alpha => D3D11_BLEND_SRC1_ALPHA, - Factor::OneMinusSrc1Alpha => D3D11_BLEND_INV_SRC1_ALPHA, - } -} - -fn map_alpha_blend_factor(factor: Factor) -> D3D11_BLEND { - match factor { - Factor::Zero => D3D11_BLEND_ZERO, - Factor::One => D3D11_BLEND_ONE, - Factor::SrcColor | Factor::SrcAlpha => D3D11_BLEND_SRC_ALPHA, - Factor::DstColor | Factor::DstAlpha => D3D11_BLEND_DEST_ALPHA, - Factor::OneMinusSrcColor | Factor::OneMinusSrcAlpha => D3D11_BLEND_INV_SRC_ALPHA, - Factor::OneMinusDstColor | Factor::OneMinusDstAlpha => D3D11_BLEND_INV_DEST_ALPHA, - Factor::ConstColor | Factor::ConstAlpha => D3D11_BLEND_BLEND_FACTOR, - Factor::OneMinusConstColor | Factor::OneMinusConstAlpha => D3D11_BLEND_INV_BLEND_FACTOR, - Factor::SrcAlphaSaturate => D3D11_BLEND_SRC_ALPHA_SAT, - Factor::Src1Color | Factor::Src1Alpha => D3D11_BLEND_SRC1_ALPHA, - Factor::OneMinusSrc1Color | Factor::OneMinusSrc1Alpha => D3D11_BLEND_INV_SRC1_ALPHA, - } -} - -fn map_blend_op(operation: BlendOp) -> (D3D11_BLEND_OP, D3D11_BLEND, D3D11_BLEND) { - match operation { - BlendOp::Add { src, dst } => ( - D3D11_BLEND_OP_ADD, - map_blend_factor(src), - map_blend_factor(dst), - ), - BlendOp::Sub { src, dst } => ( - D3D11_BLEND_OP_SUBTRACT, - map_blend_factor(src), - map_blend_factor(dst), - ), - BlendOp::RevSub { src, dst } => ( - D3D11_BLEND_OP_REV_SUBTRACT, - map_blend_factor(src), - map_blend_factor(dst), - ), - BlendOp::Min => (D3D11_BLEND_OP_MIN, D3D11_BLEND_ZERO, D3D11_BLEND_ZERO), - BlendOp::Max => (D3D11_BLEND_OP_MAX, D3D11_BLEND_ZERO, D3D11_BLEND_ZERO), - } -} - -fn map_alpha_blend_op(operation: BlendOp) -> (D3D11_BLEND_OP, D3D11_BLEND, D3D11_BLEND) { - match operation { - BlendOp::Add { src, dst } => ( - D3D11_BLEND_OP_ADD, - map_alpha_blend_factor(src), - map_alpha_blend_factor(dst), - ), - BlendOp::Sub { src, dst } => ( - D3D11_BLEND_OP_SUBTRACT, - map_alpha_blend_factor(src), - map_alpha_blend_factor(dst), - ), - BlendOp::RevSub { src, dst } => ( - D3D11_BLEND_OP_REV_SUBTRACT, - map_alpha_blend_factor(src), - map_alpha_blend_factor(dst), - ), - BlendOp::Min => (D3D11_BLEND_OP_MIN, D3D11_BLEND_ZERO, D3D11_BLEND_ZERO), - BlendOp::Max => (D3D11_BLEND_OP_MAX, D3D11_BLEND_ZERO, D3D11_BLEND_ZERO), - } -} - -fn map_blend_targets( - render_target_blends: &[ColorBlendDesc], -) -> [D3D11_RENDER_TARGET_BLEND_DESC; 8] { - let mut targets: [D3D11_RENDER_TARGET_BLEND_DESC; 8] = [unsafe { mem::zeroed() }; 8]; - - for (mut target, color_desc) in targets.iter_mut().zip(render_target_blends.iter()) { - target.RenderTargetWriteMask = color_desc.mask.bits() as _; - if let Some(ref blend) = color_desc.blend { - let (color_op, color_src, color_dst) = map_blend_op(blend.color); - let (alpha_op, alpha_src, alpha_dst) = map_alpha_blend_op(blend.alpha); - target.BlendEnable = TRUE; - target.BlendOp = color_op; - target.SrcBlend = color_src; - target.DestBlend = color_dst; - target.BlendOpAlpha = alpha_op; - target.SrcBlendAlpha = alpha_src; - target.DestBlendAlpha = alpha_dst; - } - } - - targets -} - -pub(crate) fn map_blend_desc( - desc: &BlendDesc, - multisampling: &Option, -) -> D3D11_BLEND_DESC { - D3D11_BLEND_DESC { - AlphaToCoverageEnable: multisampling.as_ref().map_or(false, |m| m.alpha_coverage) as _, - IndependentBlendEnable: TRUE, - RenderTarget: map_blend_targets(&desc.targets), - } -} - -pub fn map_comparison(func: Comparison) -> D3D11_COMPARISON_FUNC { - match func { - Comparison::Never => D3D11_COMPARISON_NEVER, - Comparison::Less => D3D11_COMPARISON_LESS, - Comparison::LessEqual => D3D11_COMPARISON_LESS_EQUAL, - Comparison::Equal => D3D11_COMPARISON_EQUAL, - Comparison::GreaterEqual => D3D11_COMPARISON_GREATER_EQUAL, - Comparison::Greater => D3D11_COMPARISON_GREATER, - Comparison::NotEqual => D3D11_COMPARISON_NOT_EQUAL, - Comparison::Always => D3D11_COMPARISON_ALWAYS, - } -} - -fn map_stencil_op(op: StencilOp) -> D3D11_STENCIL_OP { - match op { - StencilOp::Keep => D3D11_STENCIL_OP_KEEP, - StencilOp::Zero => D3D11_STENCIL_OP_ZERO, - StencilOp::Replace => D3D11_STENCIL_OP_REPLACE, - StencilOp::IncrementClamp => D3D11_STENCIL_OP_INCR_SAT, - StencilOp::IncrementWrap => D3D11_STENCIL_OP_INCR, - StencilOp::DecrementClamp => D3D11_STENCIL_OP_DECR_SAT, - StencilOp::DecrementWrap => D3D11_STENCIL_OP_DECR, - StencilOp::Invert => D3D11_STENCIL_OP_INVERT, - } -} - -fn map_stencil_side(side: &StencilFace) -> D3D11_DEPTH_STENCILOP_DESC { - D3D11_DEPTH_STENCILOP_DESC { - StencilFailOp: map_stencil_op(side.op_fail), - StencilDepthFailOp: map_stencil_op(side.op_depth_fail), - StencilPassOp: map_stencil_op(side.op_pass), - StencilFunc: map_comparison(side.fun), - } -} - -pub(crate) fn map_depth_stencil_desc( - desc: &DepthStencilDesc, -) -> (D3D11_DEPTH_STENCIL_DESC, State, bool) { - let (depth_on, depth_write, depth_func) = match desc.depth { - Some(ref depth) => (TRUE, depth.write, map_comparison(depth.fun)), - None => unsafe { mem::zeroed() }, - }; - - let (stencil_on, front, back, read_mask, write_mask, stencil_ref) = match desc.stencil { - Some(ref stencil) => { - let read_masks = stencil.read_masks.static_or(Sided::new(!0)); - let write_masks = stencil.read_masks.static_or(Sided::new(!0)); - let reference_value = match stencil.reference_values { - State::Static(ref values) => { - if values.front != values.back { - error!("Different reference values for front ({}) and back ({}) of the stencil", - values.front, values.back); - } - State::Static(values.front) - } - State::Dynamic => State::Dynamic, - }; - // TODO: cascade to create_pipeline - if read_masks.front != read_masks.back || write_masks.front != write_masks.back { - error!( - "Different sides are specified for read ({:?} and write ({:?}) stencil masks", - read_masks, write_masks - ); - } - ( - TRUE, - map_stencil_side(&stencil.faces.front), - map_stencil_side(&stencil.faces.back), - read_masks.front, - write_masks.front, - reference_value, - ) - } - None => unsafe { mem::zeroed() }, - }; - - let stencil_read_only = write_mask == 0 - || (front.StencilDepthFailOp == D3D11_STENCIL_OP_KEEP - && front.StencilFailOp == D3D11_STENCIL_OP_KEEP - && front.StencilPassOp == D3D11_STENCIL_OP_KEEP - && back.StencilDepthFailOp == D3D11_STENCIL_OP_KEEP - && back.StencilFailOp == D3D11_STENCIL_OP_KEEP - && back.StencilPassOp == D3D11_STENCIL_OP_KEEP); - let read_only = !depth_write && stencil_read_only; - - ( - D3D11_DEPTH_STENCIL_DESC { - DepthEnable: depth_on, - DepthWriteMask: if depth_write { - D3D11_DEPTH_WRITE_MASK_ALL - } else { - D3D11_DEPTH_WRITE_MASK_ZERO - }, - DepthFunc: depth_func, - StencilEnable: stencil_on, - StencilReadMask: read_mask as _, - StencilWriteMask: write_mask as _, - FrontFace: front, - BackFace: back, - }, - stencil_ref, - read_only, - ) -} - -pub fn _map_execution_model(model: spirv::ExecutionModel) -> ShaderStage { - match model { - spirv::ExecutionModel::Vertex => ShaderStage::Vertex, - spirv::ExecutionModel::Fragment => ShaderStage::Fragment, - spirv::ExecutionModel::Geometry => ShaderStage::Geometry, - spirv::ExecutionModel::GlCompute => ShaderStage::Compute, - spirv::ExecutionModel::TessellationControl => ShaderStage::Hull, - spirv::ExecutionModel::TessellationEvaluation => ShaderStage::Domain, - spirv::ExecutionModel::Kernel => panic!("Kernel is not a valid execution model."), - } -} - -pub fn map_stage(stage: ShaderStage) -> spirv::ExecutionModel { - match stage { - ShaderStage::Vertex => spirv::ExecutionModel::Vertex, - ShaderStage::Fragment => spirv::ExecutionModel::Fragment, - ShaderStage::Geometry => spirv::ExecutionModel::Geometry, - ShaderStage::Compute => spirv::ExecutionModel::GlCompute, - ShaderStage::Hull => spirv::ExecutionModel::TessellationControl, - ShaderStage::Domain => spirv::ExecutionModel::TessellationEvaluation, - ShaderStage::Task | ShaderStage::Mesh => { - panic!("{:?} shader is not supported in DirectX 11", stage) - } - } -} - -pub fn map_wrapping(wrap: image::WrapMode) -> D3D11_TEXTURE_ADDRESS_MODE { - use hal::image::WrapMode as Wm; - match wrap { - Wm::Tile => D3D11_TEXTURE_ADDRESS_WRAP, - Wm::Mirror => D3D11_TEXTURE_ADDRESS_MIRROR, - Wm::Clamp => D3D11_TEXTURE_ADDRESS_CLAMP, - Wm::Border => D3D11_TEXTURE_ADDRESS_BORDER, - Wm::MirrorClamp => D3D11_TEXTURE_ADDRESS_MIRROR_ONCE, - } -} - -fn map_filter_type(filter: image::Filter) -> D3D11_FILTER_TYPE { - match filter { - image::Filter::Nearest => D3D11_FILTER_TYPE_POINT, - image::Filter::Linear => D3D11_FILTER_TYPE_LINEAR, - } -} - -// Hopefully works just as well in d3d11 :) -pub fn map_filter( - mag_filter: image::Filter, - min_filter: image::Filter, - mip_filter: image::Filter, - reduction: D3D11_FILTER_REDUCTION_TYPE, - anisotropy_clamp: Option, -) -> D3D11_FILTER { - let mag = map_filter_type(mag_filter); - let min = map_filter_type(min_filter); - let mip = map_filter_type(mip_filter); - - (min & D3D11_FILTER_TYPE_MASK) << D3D11_MIN_FILTER_SHIFT - | (mag & D3D11_FILTER_TYPE_MASK) << D3D11_MAG_FILTER_SHIFT - | (mip & D3D11_FILTER_TYPE_MASK) << D3D11_MIP_FILTER_SHIFT - | (reduction & D3D11_FILTER_REDUCTION_TYPE_MASK) << D3D11_FILTER_REDUCTION_TYPE_SHIFT - | anisotropy_clamp - .map(|_| D3D11_FILTER_ANISOTROPIC) - .unwrap_or(0) -} - -pub fn map_image_usage( - usage: image::Usage, - format_desc: FormatDesc, - feature_level: u32, -) -> D3D11_BIND_FLAG { - let mut bind = 0; - - if usage.intersects(image::Usage::TRANSFER_SRC | image::Usage::SAMPLED | image::Usage::STORAGE) - { - bind |= D3D11_BIND_SHADER_RESOURCE; - } - - // we cant get RTVs or UAVs on compressed & depth formats - if !format_desc.is_compressed() && !format_desc.aspects.contains(Aspects::DEPTH) { - if usage.intersects(image::Usage::COLOR_ATTACHMENT | image::Usage::TRANSFER_DST) { - bind |= D3D11_BIND_RENDER_TARGET; - } - - // Only add unordered access on the image if we could use compute shaders to copy. - let transfer_compute = - usage.intersects(image::Usage::TRANSFER_DST) && feature_level >= D3D_FEATURE_LEVEL_11_0; - let storage = usage.intersects(image::Usage::STORAGE); - if transfer_compute || storage { - bind |= D3D11_BIND_UNORDERED_ACCESS; - } - } - - if usage.contains(image::Usage::DEPTH_STENCIL_ATTACHMENT) { - bind |= D3D11_BIND_DEPTH_STENCIL; - } - - bind -} - -pub fn map_feature_level_to_shader_model(feature: u32) -> hlsl::ShaderModel { - match feature { - D3D_FEATURE_LEVEL_9_1 | D3D_FEATURE_LEVEL_9_2 => hlsl::ShaderModel::V4_0L9_1, - D3D_FEATURE_LEVEL_9_3 => hlsl::ShaderModel::V4_0L9_3, - D3D_FEATURE_LEVEL_10_0 => hlsl::ShaderModel::V4_0, - D3D_FEATURE_LEVEL_10_1 => hlsl::ShaderModel::V4_1, - D3D_FEATURE_LEVEL_11_0 | D3D_FEATURE_LEVEL_11_1 => hlsl::ShaderModel::V5_0, - _ => unimplemented!(), - } -} +use auxil::ShaderStage; +use hal::{ + format::{Aspects, Format, FormatDesc}, + image, + pso::{ + BlendDesc, BlendOp, ColorBlendDesc, Comparison, DepthBias, DepthStencilDesc, Face, Factor, + FrontFace, InputAssemblerDesc, Multisampling, PolygonMode, Rasterizer, Rect, Sided, State, + StencilFace, StencilOp, StencilValue, Viewport, + }, + IndexType, +}; + +use spirv_cross::{hlsl, spirv}; + +use winapi::{ + shared::{ + dxgiformat::*, + minwindef::{FALSE, INT, TRUE}, + }, + um::{d3d11::*, d3dcommon::*}, +}; + +use std::mem; + +pub fn map_index_type(ty: IndexType) -> DXGI_FORMAT { + match ty { + IndexType::U16 => DXGI_FORMAT_R16_UINT, + IndexType::U32 => DXGI_FORMAT_R32_UINT, + } +} + +// TODO: add aspect parameter +pub fn viewable_format(format: DXGI_FORMAT) -> DXGI_FORMAT { + match format { + DXGI_FORMAT_D32_FLOAT_S8X24_UINT => DXGI_FORMAT_R32_FLOAT_X8X24_TYPELESS, + DXGI_FORMAT_D32_FLOAT => DXGI_FORMAT_R32_FLOAT, + DXGI_FORMAT_D16_UNORM => DXGI_FORMAT_R16_UNORM, + _ => format, + } +} + +// TODO: stolen from d3d12 backend, maybe share function somehow? +pub fn map_format(format: Format) -> Option { + use hal::format::Format::*; + + let format = match format { + R5g6b5Unorm => DXGI_FORMAT_B5G6R5_UNORM, + R5g5b5a1Unorm => DXGI_FORMAT_B5G5R5A1_UNORM, + R8Unorm => DXGI_FORMAT_R8_UNORM, + R8Snorm => DXGI_FORMAT_R8_SNORM, + R8Uint => DXGI_FORMAT_R8_UINT, + R8Sint => DXGI_FORMAT_R8_SINT, + Rg8Unorm => DXGI_FORMAT_R8G8_UNORM, + Rg8Snorm => DXGI_FORMAT_R8G8_SNORM, + Rg8Uint => DXGI_FORMAT_R8G8_UINT, + Rg8Sint => DXGI_FORMAT_R8G8_SINT, + Rgba8Unorm => DXGI_FORMAT_R8G8B8A8_UNORM, + Rgba8Snorm => DXGI_FORMAT_R8G8B8A8_SNORM, + Rgba8Uint => DXGI_FORMAT_R8G8B8A8_UINT, + Rgba8Sint => DXGI_FORMAT_R8G8B8A8_SINT, + Rgba8Srgb => DXGI_FORMAT_R8G8B8A8_UNORM_SRGB, + Bgra8Unorm => DXGI_FORMAT_B8G8R8A8_UNORM, + Bgra8Srgb => DXGI_FORMAT_B8G8R8A8_UNORM_SRGB, + A2b10g10r10Unorm => DXGI_FORMAT_R10G10B10A2_UNORM, + A2b10g10r10Uint => DXGI_FORMAT_R10G10B10A2_UINT, + R16Unorm => DXGI_FORMAT_R16_UNORM, + R16Snorm => DXGI_FORMAT_R16_SNORM, + R16Uint => DXGI_FORMAT_R16_UINT, + R16Sint => DXGI_FORMAT_R16_SINT, + R16Sfloat => DXGI_FORMAT_R16_FLOAT, + Rg16Unorm => DXGI_FORMAT_R16G16_UNORM, + Rg16Snorm => DXGI_FORMAT_R16G16_SNORM, + Rg16Uint => DXGI_FORMAT_R16G16_UINT, + Rg16Sint => DXGI_FORMAT_R16G16_SINT, + Rg16Sfloat => DXGI_FORMAT_R16G16_FLOAT, + Rgba16Unorm => DXGI_FORMAT_R16G16B16A16_UNORM, + Rgba16Snorm => DXGI_FORMAT_R16G16B16A16_SNORM, + Rgba16Uint => DXGI_FORMAT_R16G16B16A16_UINT, + Rgba16Sint => DXGI_FORMAT_R16G16B16A16_SINT, + Rgba16Sfloat => DXGI_FORMAT_R16G16B16A16_FLOAT, + R32Uint => DXGI_FORMAT_R32_UINT, + R32Sint => DXGI_FORMAT_R32_SINT, + R32Sfloat => DXGI_FORMAT_R32_FLOAT, + Rg32Uint => DXGI_FORMAT_R32G32_UINT, + Rg32Sint => DXGI_FORMAT_R32G32_SINT, + Rg32Sfloat => DXGI_FORMAT_R32G32_FLOAT, + Rgb32Uint => DXGI_FORMAT_R32G32B32_UINT, + Rgb32Sint => DXGI_FORMAT_R32G32B32_SINT, + Rgb32Sfloat => DXGI_FORMAT_R32G32B32_FLOAT, + Rgba32Uint => DXGI_FORMAT_R32G32B32A32_UINT, + Rgba32Sint => DXGI_FORMAT_R32G32B32A32_SINT, + Rgba32Sfloat => DXGI_FORMAT_R32G32B32A32_FLOAT, + B10g11r11Ufloat => DXGI_FORMAT_R11G11B10_FLOAT, + E5b9g9r9Ufloat => DXGI_FORMAT_R9G9B9E5_SHAREDEXP, + D16Unorm => DXGI_FORMAT_D16_UNORM, + D32Sfloat => DXGI_FORMAT_D32_FLOAT, + D32SfloatS8Uint => DXGI_FORMAT_D32_FLOAT_S8X24_UINT, + Bc1RgbUnorm | Bc1RgbaUnorm => DXGI_FORMAT_BC1_UNORM, + Bc1RgbSrgb | Bc1RgbaSrgb => DXGI_FORMAT_BC1_UNORM_SRGB, + Bc2Unorm => DXGI_FORMAT_BC2_UNORM, + Bc2Srgb => DXGI_FORMAT_BC2_UNORM_SRGB, + Bc3Unorm => DXGI_FORMAT_BC3_UNORM, + Bc3Srgb => DXGI_FORMAT_BC3_UNORM_SRGB, + Bc4Unorm => DXGI_FORMAT_BC4_UNORM, + Bc4Snorm => DXGI_FORMAT_BC4_SNORM, + Bc5Unorm => DXGI_FORMAT_BC5_UNORM, + Bc5Snorm => DXGI_FORMAT_BC5_SNORM, + Bc6hUfloat => DXGI_FORMAT_BC6H_UF16, + Bc6hSfloat => DXGI_FORMAT_BC6H_SF16, + Bc7Unorm => DXGI_FORMAT_BC7_UNORM, + Bc7Srgb => DXGI_FORMAT_BC7_UNORM_SRGB, + + _ => return None, + }; + + Some(format) +} + +pub fn map_format_nosrgb(format: Format) -> Option { + // NOTE: DXGI doesn't allow sRGB format on the swapchain, but + // creating RTV of swapchain buffers with sRGB works + match format { + Format::Bgra8Srgb => Some(DXGI_FORMAT_B8G8R8A8_UNORM), + Format::Rgba8Srgb => Some(DXGI_FORMAT_R8G8B8A8_UNORM), + _ => map_format(format), + } +} + +#[derive(Debug, Clone)] +pub struct DecomposedDxgiFormat { + pub typeless: DXGI_FORMAT, + pub srv: Option, + pub rtv: Option, + pub uav: Option, + pub dsv: Option, + // the format we use internally for operating on textures (eg. Rgba8 uses R32 internally for + // copies) + pub copy_uav: Option, + pub copy_srv: Option, +} + +impl DecomposedDxgiFormat { + pub const UNKNOWN: DecomposedDxgiFormat = DecomposedDxgiFormat { + typeless: DXGI_FORMAT_UNKNOWN, + srv: None, + rtv: None, + uav: None, + dsv: None, + copy_uav: None, + copy_srv: None, + }; + + // TODO: we probably want to pass in usage flags or similar to allow for our `typeless_format` + // field to only contain the input format (eg. depth only rather than typeless likely + // improves perf since the driver doesn't need to expose internals) + // + // TODO: we also want aspect for determining depth/stencil + pub fn from_dxgi_format(format: DXGI_FORMAT) -> DecomposedDxgiFormat { + match format { + DXGI_FORMAT_R8G8B8A8_UNORM + | DXGI_FORMAT_R8G8B8A8_SNORM + | DXGI_FORMAT_R8G8B8A8_UINT + | DXGI_FORMAT_R8G8B8A8_SINT + | DXGI_FORMAT_R8G8B8A8_UNORM_SRGB => DecomposedDxgiFormat { + typeless: DXGI_FORMAT_R8G8B8A8_TYPELESS, + srv: Some(format), + rtv: Some(format), + uav: Some(format), + dsv: None, + copy_uav: Some(DXGI_FORMAT_R32_UINT), + copy_srv: Some(DXGI_FORMAT_R8G8B8A8_UINT), + }, + + DXGI_FORMAT_B8G8R8A8_UNORM | DXGI_FORMAT_B8G8R8A8_UNORM_SRGB => DecomposedDxgiFormat { + typeless: DXGI_FORMAT_B8G8R8A8_TYPELESS, + srv: Some(format), + rtv: Some(format), + uav: Some(DXGI_FORMAT_B8G8R8A8_UNORM), + dsv: None, + copy_uav: Some(DXGI_FORMAT_R32_UINT), + copy_srv: Some(DXGI_FORMAT_B8G8R8A8_UNORM), + }, + + DXGI_FORMAT_A8_UNORM => DecomposedDxgiFormat { + typeless: format, + srv: Some(format), + rtv: Some(format), + uav: Some(format), + dsv: None, + copy_uav: Some(format), + copy_srv: Some(format), + }, + + DXGI_FORMAT_R8_UNORM | DXGI_FORMAT_R8_SNORM | DXGI_FORMAT_R8_UINT + | DXGI_FORMAT_R8_SINT => DecomposedDxgiFormat { + typeless: DXGI_FORMAT_R8_TYPELESS, + srv: Some(format), + rtv: Some(format), + uav: Some(format), + dsv: None, + copy_uav: Some(DXGI_FORMAT_R8_UINT), + copy_srv: Some(DXGI_FORMAT_R8_UINT), + }, + + DXGI_FORMAT_R8G8_UNORM + | DXGI_FORMAT_R8G8_SNORM + | DXGI_FORMAT_R8G8_UINT + | DXGI_FORMAT_R8G8_SINT => DecomposedDxgiFormat { + typeless: DXGI_FORMAT_R8G8_TYPELESS, + srv: Some(format), + rtv: Some(format), + uav: Some(format), + dsv: None, + copy_uav: Some(DXGI_FORMAT_R8G8_UINT), + copy_srv: Some(DXGI_FORMAT_R8G8_UINT), + }, + + DXGI_FORMAT_D16_UNORM => DecomposedDxgiFormat { + typeless: DXGI_FORMAT_R16_TYPELESS, + srv: Some(DXGI_FORMAT_R16_FLOAT), + rtv: Some(DXGI_FORMAT_R16_FLOAT), + uav: Some(DXGI_FORMAT_R16_FLOAT), + dsv: Some(format), + copy_uav: Some(DXGI_FORMAT_R16_UINT), + copy_srv: Some(DXGI_FORMAT_R16_UINT), + }, + + DXGI_FORMAT_R16_UNORM + | DXGI_FORMAT_R16_SNORM + | DXGI_FORMAT_R16_UINT + | DXGI_FORMAT_R16_SINT + | DXGI_FORMAT_R16_FLOAT => DecomposedDxgiFormat { + typeless: DXGI_FORMAT_R16_TYPELESS, + srv: Some(format), + rtv: Some(format), + uav: Some(format), + dsv: Some(DXGI_FORMAT_D16_UNORM), + copy_uav: Some(DXGI_FORMAT_R16_UINT), + copy_srv: Some(DXGI_FORMAT_R16_UINT), + }, + + DXGI_FORMAT_R16G16_UNORM + | DXGI_FORMAT_R16G16_SNORM + | DXGI_FORMAT_R16G16_UINT + | DXGI_FORMAT_R16G16_SINT + | DXGI_FORMAT_R16G16_FLOAT => DecomposedDxgiFormat { + typeless: DXGI_FORMAT_R16G16_TYPELESS, + srv: Some(format), + rtv: Some(format), + uav: Some(format), + dsv: None, + copy_uav: Some(DXGI_FORMAT_R32_UINT), + copy_srv: Some(DXGI_FORMAT_R16G16_UINT), + }, + + DXGI_FORMAT_R16G16B16A16_UNORM + | DXGI_FORMAT_R16G16B16A16_SNORM + | DXGI_FORMAT_R16G16B16A16_UINT + | DXGI_FORMAT_R16G16B16A16_SINT + | DXGI_FORMAT_R16G16B16A16_FLOAT => DecomposedDxgiFormat { + typeless: DXGI_FORMAT_R16G16B16A16_TYPELESS, + srv: Some(format), + rtv: Some(format), + uav: Some(format), + dsv: None, + copy_uav: Some(DXGI_FORMAT_R16G16B16A16_UINT), + copy_srv: Some(DXGI_FORMAT_R16G16B16A16_UINT), + }, + + DXGI_FORMAT_D32_FLOAT_S8X24_UINT => DecomposedDxgiFormat { + typeless: DXGI_FORMAT_R32G8X24_TYPELESS, + // TODO: depth or stencil? + srv: Some(DXGI_FORMAT_R32_FLOAT_X8X24_TYPELESS), + rtv: None, + uav: None, + dsv: Some(format), + copy_uav: None, + copy_srv: Some(DXGI_FORMAT_R32_FLOAT_X8X24_TYPELESS), + }, + + DXGI_FORMAT_D32_FLOAT => DecomposedDxgiFormat { + typeless: DXGI_FORMAT_R32_TYPELESS, + srv: Some(DXGI_FORMAT_R32_FLOAT), + rtv: None, + uav: None, + dsv: Some(format), + copy_uav: Some(DXGI_FORMAT_R32_UINT), + copy_srv: Some(DXGI_FORMAT_R32_UINT), + }, + + DXGI_FORMAT_R32_UINT | DXGI_FORMAT_R32_SINT | DXGI_FORMAT_R32_FLOAT => { + DecomposedDxgiFormat { + typeless: DXGI_FORMAT_R32_TYPELESS, + srv: Some(format), + rtv: Some(format), + uav: Some(format), + dsv: Some(DXGI_FORMAT_D32_FLOAT), + copy_uav: Some(DXGI_FORMAT_R32_UINT), + copy_srv: Some(DXGI_FORMAT_R32_UINT), + } + } + + DXGI_FORMAT_R32G32_UINT | DXGI_FORMAT_R32G32_SINT | DXGI_FORMAT_R32G32_FLOAT => { + DecomposedDxgiFormat { + typeless: DXGI_FORMAT_R32G32_TYPELESS, + srv: Some(format), + rtv: Some(format), + uav: Some(format), + dsv: None, + copy_uav: Some(DXGI_FORMAT_R32G32_UINT), + copy_srv: Some(DXGI_FORMAT_R32G32_UINT), + } + } + + // TODO: should we just convert to Rgba32 internally? + DXGI_FORMAT_R32G32B32_UINT + | DXGI_FORMAT_R32G32B32_SINT + | DXGI_FORMAT_R32G32B32_FLOAT => DecomposedDxgiFormat { + typeless: DXGI_FORMAT_R32G32_TYPELESS, + srv: Some(format), + rtv: None, + uav: None, + dsv: None, + copy_uav: Some(DXGI_FORMAT_R32G32B32_UINT), + copy_srv: Some(DXGI_FORMAT_R32G32B32_UINT), + }, + + DXGI_FORMAT_R32G32B32A32_UINT + | DXGI_FORMAT_R32G32B32A32_SINT + | DXGI_FORMAT_R32G32B32A32_FLOAT => DecomposedDxgiFormat { + typeless: DXGI_FORMAT_R32G32B32A32_TYPELESS, + srv: Some(format), + rtv: Some(format), + uav: Some(format), + dsv: None, + copy_uav: Some(DXGI_FORMAT_R32G32B32A32_UINT), + copy_srv: Some(DXGI_FORMAT_R32G32B32A32_UINT), + }, + + DXGI_FORMAT_R10G10B10A2_UNORM | DXGI_FORMAT_R10G10B10A2_UINT => DecomposedDxgiFormat { + typeless: DXGI_FORMAT_R10G10B10A2_TYPELESS, + srv: Some(format), + rtv: Some(format), + uav: Some(format), + dsv: None, + copy_uav: Some(DXGI_FORMAT_R32_UINT), + copy_srv: Some(DXGI_FORMAT_R10G10B10A2_UINT), + }, + + DXGI_FORMAT_R11G11B10_FLOAT => DecomposedDxgiFormat { + typeless: format, + srv: Some(format), + rtv: Some(format), + uav: Some(format), + dsv: None, + copy_uav: Some(format), + copy_srv: Some(format), + }, + + DXGI_FORMAT_R9G9B9E5_SHAREDEXP => DecomposedDxgiFormat { + typeless: format, + srv: Some(format), + rtv: None, + uav: None, + dsv: None, + // NOTE: read only + copy_uav: None, + copy_srv: Some(format), + }, + + DXGI_FORMAT_BC1_UNORM | DXGI_FORMAT_BC1_UNORM_SRGB => DecomposedDxgiFormat { + typeless: DXGI_FORMAT_BC1_TYPELESS, + srv: Some(format), + rtv: None, + uav: None, + dsv: None, + // NOTE: read only + copy_uav: None, + copy_srv: Some(format), + }, + + DXGI_FORMAT_BC2_UNORM | DXGI_FORMAT_BC2_UNORM_SRGB => DecomposedDxgiFormat { + typeless: DXGI_FORMAT_BC2_TYPELESS, + srv: Some(format), + rtv: None, + uav: None, + dsv: None, + // NOTE: read only + copy_uav: None, + copy_srv: Some(format), + }, + + DXGI_FORMAT_BC3_UNORM | DXGI_FORMAT_BC3_UNORM_SRGB => DecomposedDxgiFormat { + typeless: DXGI_FORMAT_BC3_TYPELESS, + srv: Some(format), + rtv: None, + uav: None, + dsv: None, + // NOTE: read only + copy_uav: None, + copy_srv: Some(format), + }, + + DXGI_FORMAT_BC4_UNORM | DXGI_FORMAT_BC4_SNORM => DecomposedDxgiFormat { + typeless: DXGI_FORMAT_BC4_TYPELESS, + srv: Some(format), + rtv: None, + uav: None, + dsv: None, + // NOTE: read only + copy_uav: None, + copy_srv: Some(format), + }, + + DXGI_FORMAT_BC5_UNORM | DXGI_FORMAT_BC5_SNORM => DecomposedDxgiFormat { + typeless: format, + srv: Some(format), + rtv: None, + uav: None, + dsv: None, + // NOTE: read only + copy_uav: None, + copy_srv: Some(format), + }, + + DXGI_FORMAT_BC6H_UF16 | DXGI_FORMAT_BC6H_SF16 => DecomposedDxgiFormat { + typeless: DXGI_FORMAT_BC6H_TYPELESS, + srv: Some(format), + rtv: None, + uav: None, + dsv: None, + // NOTE: read only + copy_uav: None, + copy_srv: Some(format), + }, + + // TODO: srgb craziness + DXGI_FORMAT_BC7_UNORM | DXGI_FORMAT_BC7_UNORM_SRGB => DecomposedDxgiFormat { + typeless: DXGI_FORMAT_BC7_TYPELESS, + srv: Some(format), + rtv: None, + uav: None, + dsv: None, + // NOTE: read only + copy_uav: None, + copy_srv: Some(format), + }, + + _ => unimplemented!(), + } + } +} + +pub fn map_viewport(viewport: &Viewport) -> D3D11_VIEWPORT { + D3D11_VIEWPORT { + TopLeftX: viewport.rect.x as _, + TopLeftY: viewport.rect.y as _, + Width: viewport.rect.w as _, + Height: viewport.rect.h as _, + MinDepth: viewport.depth.start, + MaxDepth: viewport.depth.end, + } +} + +pub fn map_rect(rect: &Rect) -> D3D11_RECT { + D3D11_RECT { + left: rect.x as _, + top: rect.y as _, + right: (rect.x + rect.w) as _, + bottom: (rect.y + rect.h) as _, + } +} + +pub fn map_topology(ia: &InputAssemblerDesc) -> D3D11_PRIMITIVE_TOPOLOGY { + use hal::pso::Primitive::*; + match (ia.primitive, ia.with_adjacency) { + (PointList, false) => D3D_PRIMITIVE_TOPOLOGY_POINTLIST, + (PointList, true) => panic!("Points can't have adjacency info"), + (LineList, false) => D3D_PRIMITIVE_TOPOLOGY_LINELIST, + (LineList, true) => D3D_PRIMITIVE_TOPOLOGY_LINELIST_ADJ, + (LineStrip, false) => D3D_PRIMITIVE_TOPOLOGY_LINESTRIP, + (LineStrip, true) => D3D_PRIMITIVE_TOPOLOGY_LINESTRIP_ADJ, + (TriangleList, false) => D3D_PRIMITIVE_TOPOLOGY_TRIANGLELIST, + (TriangleList, true) => D3D_PRIMITIVE_TOPOLOGY_TRIANGLELIST_ADJ, + (TriangleStrip, false) => D3D_PRIMITIVE_TOPOLOGY_TRIANGLESTRIP, + (TriangleStrip, true) => D3D_PRIMITIVE_TOPOLOGY_TRIANGLESTRIP_ADJ, + (PatchList(num), false) => { + assert!(num != 0); + D3D_PRIMITIVE_TOPOLOGY_1_CONTROL_POINT_PATCHLIST + (num as u32) - 1 + } + (_, true) => panic!("Patches can't have adjacency info"), + } +} + +fn map_fill_mode(mode: PolygonMode) -> D3D11_FILL_MODE { + match mode { + PolygonMode::Fill => D3D11_FILL_SOLID, + PolygonMode::Line => D3D11_FILL_WIREFRAME, + // TODO: return error + _ => unimplemented!(), + } +} + +fn map_cull_mode(mode: Face) -> D3D11_CULL_MODE { + match mode { + Face::NONE => D3D11_CULL_NONE, + Face::FRONT => D3D11_CULL_FRONT, + Face::BACK => D3D11_CULL_BACK, + _ => panic!("Culling both front and back faces is not supported"), + } +} + +pub(crate) fn map_rasterizer_desc( + desc: &Rasterizer, + multisampling_desc: &Option, +) -> D3D11_RASTERIZER_DESC { + let bias = match desc.depth_bias { + //TODO: support dynamic depth bias + Some(State::Static(db)) => db, + Some(_) | None => DepthBias::default(), + }; + if let State::Static(w) = desc.line_width { + super::validate_line_width(w); + } + let multisampled = multisampling_desc.is_some(); + D3D11_RASTERIZER_DESC { + FillMode: map_fill_mode(desc.polygon_mode), + CullMode: map_cull_mode(desc.cull_face), + FrontCounterClockwise: match desc.front_face { + FrontFace::Clockwise => FALSE, + FrontFace::CounterClockwise => TRUE, + }, + DepthBias: bias.const_factor as INT, + DepthBiasClamp: bias.clamp, + SlopeScaledDepthBias: bias.slope_factor, + DepthClipEnable: !desc.depth_clamping as _, + // TODO: + ScissorEnable: TRUE, + MultisampleEnable: multisampled as _, + AntialiasedLineEnable: multisampled as _, + // TODO: conservative raster in >=11.x + } +} + +fn map_blend_factor(factor: Factor) -> D3D11_BLEND { + match factor { + Factor::Zero => D3D11_BLEND_ZERO, + Factor::One => D3D11_BLEND_ONE, + Factor::SrcColor => D3D11_BLEND_SRC_COLOR, + Factor::OneMinusSrcColor => D3D11_BLEND_INV_SRC_COLOR, + Factor::DstColor => D3D11_BLEND_DEST_COLOR, + Factor::OneMinusDstColor => D3D11_BLEND_INV_DEST_COLOR, + Factor::SrcAlpha => D3D11_BLEND_SRC_ALPHA, + Factor::OneMinusSrcAlpha => D3D11_BLEND_INV_SRC_ALPHA, + Factor::DstAlpha => D3D11_BLEND_DEST_ALPHA, + Factor::OneMinusDstAlpha => D3D11_BLEND_INV_DEST_ALPHA, + Factor::ConstColor | Factor::ConstAlpha => D3D11_BLEND_BLEND_FACTOR, + Factor::OneMinusConstColor | Factor::OneMinusConstAlpha => D3D11_BLEND_INV_BLEND_FACTOR, + Factor::SrcAlphaSaturate => D3D11_BLEND_SRC_ALPHA_SAT, + Factor::Src1Color => D3D11_BLEND_SRC1_COLOR, + Factor::OneMinusSrc1Color => D3D11_BLEND_INV_SRC1_COLOR, + Factor::Src1Alpha => D3D11_BLEND_SRC1_ALPHA, + Factor::OneMinusSrc1Alpha => D3D11_BLEND_INV_SRC1_ALPHA, + } +} + +fn map_alpha_blend_factor(factor: Factor) -> D3D11_BLEND { + match factor { + Factor::Zero => D3D11_BLEND_ZERO, + Factor::One => D3D11_BLEND_ONE, + Factor::SrcColor | Factor::SrcAlpha => D3D11_BLEND_SRC_ALPHA, + Factor::DstColor | Factor::DstAlpha => D3D11_BLEND_DEST_ALPHA, + Factor::OneMinusSrcColor | Factor::OneMinusSrcAlpha => D3D11_BLEND_INV_SRC_ALPHA, + Factor::OneMinusDstColor | Factor::OneMinusDstAlpha => D3D11_BLEND_INV_DEST_ALPHA, + Factor::ConstColor | Factor::ConstAlpha => D3D11_BLEND_BLEND_FACTOR, + Factor::OneMinusConstColor | Factor::OneMinusConstAlpha => D3D11_BLEND_INV_BLEND_FACTOR, + Factor::SrcAlphaSaturate => D3D11_BLEND_SRC_ALPHA_SAT, + Factor::Src1Color | Factor::Src1Alpha => D3D11_BLEND_SRC1_ALPHA, + Factor::OneMinusSrc1Color | Factor::OneMinusSrc1Alpha => D3D11_BLEND_INV_SRC1_ALPHA, + } +} + +fn map_blend_op(operation: BlendOp) -> (D3D11_BLEND_OP, D3D11_BLEND, D3D11_BLEND) { + match operation { + BlendOp::Add { src, dst } => ( + D3D11_BLEND_OP_ADD, + map_blend_factor(src), + map_blend_factor(dst), + ), + BlendOp::Sub { src, dst } => ( + D3D11_BLEND_OP_SUBTRACT, + map_blend_factor(src), + map_blend_factor(dst), + ), + BlendOp::RevSub { src, dst } => ( + D3D11_BLEND_OP_REV_SUBTRACT, + map_blend_factor(src), + map_blend_factor(dst), + ), + BlendOp::Min => (D3D11_BLEND_OP_MIN, D3D11_BLEND_ZERO, D3D11_BLEND_ZERO), + BlendOp::Max => (D3D11_BLEND_OP_MAX, D3D11_BLEND_ZERO, D3D11_BLEND_ZERO), + } +} + +fn map_alpha_blend_op(operation: BlendOp) -> (D3D11_BLEND_OP, D3D11_BLEND, D3D11_BLEND) { + match operation { + BlendOp::Add { src, dst } => ( + D3D11_BLEND_OP_ADD, + map_alpha_blend_factor(src), + map_alpha_blend_factor(dst), + ), + BlendOp::Sub { src, dst } => ( + D3D11_BLEND_OP_SUBTRACT, + map_alpha_blend_factor(src), + map_alpha_blend_factor(dst), + ), + BlendOp::RevSub { src, dst } => ( + D3D11_BLEND_OP_REV_SUBTRACT, + map_alpha_blend_factor(src), + map_alpha_blend_factor(dst), + ), + BlendOp::Min => (D3D11_BLEND_OP_MIN, D3D11_BLEND_ZERO, D3D11_BLEND_ZERO), + BlendOp::Max => (D3D11_BLEND_OP_MAX, D3D11_BLEND_ZERO, D3D11_BLEND_ZERO), + } +} + +fn map_blend_targets( + render_target_blends: &[ColorBlendDesc], +) -> [D3D11_RENDER_TARGET_BLEND_DESC; 8] { + let mut targets: [D3D11_RENDER_TARGET_BLEND_DESC; 8] = [unsafe { mem::zeroed() }; 8]; + + for (mut target, color_desc) in targets.iter_mut().zip(render_target_blends.iter()) { + target.RenderTargetWriteMask = color_desc.mask.bits() as _; + if let Some(ref blend) = color_desc.blend { + let (color_op, color_src, color_dst) = map_blend_op(blend.color); + let (alpha_op, alpha_src, alpha_dst) = map_alpha_blend_op(blend.alpha); + target.BlendEnable = TRUE; + target.BlendOp = color_op; + target.SrcBlend = color_src; + target.DestBlend = color_dst; + target.BlendOpAlpha = alpha_op; + target.SrcBlendAlpha = alpha_src; + target.DestBlendAlpha = alpha_dst; + } + } + + targets +} + +pub(crate) fn map_blend_desc( + desc: &BlendDesc, + multisampling: &Option, +) -> D3D11_BLEND_DESC { + D3D11_BLEND_DESC { + AlphaToCoverageEnable: multisampling.as_ref().map_or(false, |m| m.alpha_coverage) as _, + IndependentBlendEnable: TRUE, + RenderTarget: map_blend_targets(&desc.targets), + } +} + +pub fn map_comparison(func: Comparison) -> D3D11_COMPARISON_FUNC { + match func { + Comparison::Never => D3D11_COMPARISON_NEVER, + Comparison::Less => D3D11_COMPARISON_LESS, + Comparison::LessEqual => D3D11_COMPARISON_LESS_EQUAL, + Comparison::Equal => D3D11_COMPARISON_EQUAL, + Comparison::GreaterEqual => D3D11_COMPARISON_GREATER_EQUAL, + Comparison::Greater => D3D11_COMPARISON_GREATER, + Comparison::NotEqual => D3D11_COMPARISON_NOT_EQUAL, + Comparison::Always => D3D11_COMPARISON_ALWAYS, + } +} + +fn map_stencil_op(op: StencilOp) -> D3D11_STENCIL_OP { + match op { + StencilOp::Keep => D3D11_STENCIL_OP_KEEP, + StencilOp::Zero => D3D11_STENCIL_OP_ZERO, + StencilOp::Replace => D3D11_STENCIL_OP_REPLACE, + StencilOp::IncrementClamp => D3D11_STENCIL_OP_INCR_SAT, + StencilOp::IncrementWrap => D3D11_STENCIL_OP_INCR, + StencilOp::DecrementClamp => D3D11_STENCIL_OP_DECR_SAT, + StencilOp::DecrementWrap => D3D11_STENCIL_OP_DECR, + StencilOp::Invert => D3D11_STENCIL_OP_INVERT, + } +} + +fn map_stencil_side(side: &StencilFace) -> D3D11_DEPTH_STENCILOP_DESC { + D3D11_DEPTH_STENCILOP_DESC { + StencilFailOp: map_stencil_op(side.op_fail), + StencilDepthFailOp: map_stencil_op(side.op_depth_fail), + StencilPassOp: map_stencil_op(side.op_pass), + StencilFunc: map_comparison(side.fun), + } +} + +pub(crate) fn map_depth_stencil_desc( + desc: &DepthStencilDesc, +) -> (D3D11_DEPTH_STENCIL_DESC, State, bool) { + let (depth_on, depth_write, depth_func) = match desc.depth { + Some(ref depth) => (TRUE, depth.write, map_comparison(depth.fun)), + None => unsafe { mem::zeroed() }, + }; + + let (stencil_on, front, back, read_mask, write_mask, stencil_ref) = match desc.stencil { + Some(ref stencil) => { + let read_masks = stencil.read_masks.static_or(Sided::new(!0)); + let write_masks = stencil.read_masks.static_or(Sided::new(!0)); + let reference_value = match stencil.reference_values { + State::Static(ref values) => { + if values.front != values.back { + error!("Different reference values for front ({}) and back ({}) of the stencil", + values.front, values.back); + } + State::Static(values.front) + } + State::Dynamic => State::Dynamic, + }; + // TODO: cascade to create_pipeline + if read_masks.front != read_masks.back || write_masks.front != write_masks.back { + error!( + "Different sides are specified for read ({:?} and write ({:?}) stencil masks", + read_masks, write_masks + ); + } + ( + TRUE, + map_stencil_side(&stencil.faces.front), + map_stencil_side(&stencil.faces.back), + read_masks.front, + write_masks.front, + reference_value, + ) + } + None => unsafe { mem::zeroed() }, + }; + + let stencil_read_only = write_mask == 0 + || (front.StencilDepthFailOp == D3D11_STENCIL_OP_KEEP + && front.StencilFailOp == D3D11_STENCIL_OP_KEEP + && front.StencilPassOp == D3D11_STENCIL_OP_KEEP + && back.StencilDepthFailOp == D3D11_STENCIL_OP_KEEP + && back.StencilFailOp == D3D11_STENCIL_OP_KEEP + && back.StencilPassOp == D3D11_STENCIL_OP_KEEP); + let read_only = !depth_write && stencil_read_only; + + ( + D3D11_DEPTH_STENCIL_DESC { + DepthEnable: depth_on, + DepthWriteMask: if depth_write { + D3D11_DEPTH_WRITE_MASK_ALL + } else { + D3D11_DEPTH_WRITE_MASK_ZERO + }, + DepthFunc: depth_func, + StencilEnable: stencil_on, + StencilReadMask: read_mask as _, + StencilWriteMask: write_mask as _, + FrontFace: front, + BackFace: back, + }, + stencil_ref, + read_only, + ) +} + +pub fn _map_execution_model(model: spirv::ExecutionModel) -> ShaderStage { + match model { + spirv::ExecutionModel::Vertex => ShaderStage::Vertex, + spirv::ExecutionModel::Fragment => ShaderStage::Fragment, + spirv::ExecutionModel::Geometry => ShaderStage::Geometry, + spirv::ExecutionModel::GlCompute => ShaderStage::Compute, + spirv::ExecutionModel::TessellationControl => ShaderStage::Hull, + spirv::ExecutionModel::TessellationEvaluation => ShaderStage::Domain, + spirv::ExecutionModel::Kernel => panic!("Kernel is not a valid execution model."), + } +} + +pub fn map_stage(stage: ShaderStage) -> spirv::ExecutionModel { + match stage { + ShaderStage::Vertex => spirv::ExecutionModel::Vertex, + ShaderStage::Fragment => spirv::ExecutionModel::Fragment, + ShaderStage::Geometry => spirv::ExecutionModel::Geometry, + ShaderStage::Compute => spirv::ExecutionModel::GlCompute, + ShaderStage::Hull => spirv::ExecutionModel::TessellationControl, + ShaderStage::Domain => spirv::ExecutionModel::TessellationEvaluation, + ShaderStage::Task | ShaderStage::Mesh => { + panic!("{:?} shader is not supported in DirectX 11", stage) + } + } +} + +pub fn map_wrapping(wrap: image::WrapMode) -> D3D11_TEXTURE_ADDRESS_MODE { + use hal::image::WrapMode as Wm; + match wrap { + Wm::Tile => D3D11_TEXTURE_ADDRESS_WRAP, + Wm::Mirror => D3D11_TEXTURE_ADDRESS_MIRROR, + Wm::Clamp => D3D11_TEXTURE_ADDRESS_CLAMP, + Wm::Border => D3D11_TEXTURE_ADDRESS_BORDER, + Wm::MirrorClamp => D3D11_TEXTURE_ADDRESS_MIRROR_ONCE, + } +} + +fn map_filter_type(filter: image::Filter) -> D3D11_FILTER_TYPE { + match filter { + image::Filter::Nearest => D3D11_FILTER_TYPE_POINT, + image::Filter::Linear => D3D11_FILTER_TYPE_LINEAR, + } +} + +// Hopefully works just as well in d3d11 :) +pub fn map_filter( + mag_filter: image::Filter, + min_filter: image::Filter, + mip_filter: image::Filter, + reduction: D3D11_FILTER_REDUCTION_TYPE, + anisotropy_clamp: Option, +) -> D3D11_FILTER { + let mag = map_filter_type(mag_filter); + let min = map_filter_type(min_filter); + let mip = map_filter_type(mip_filter); + + (min & D3D11_FILTER_TYPE_MASK) << D3D11_MIN_FILTER_SHIFT + | (mag & D3D11_FILTER_TYPE_MASK) << D3D11_MAG_FILTER_SHIFT + | (mip & D3D11_FILTER_TYPE_MASK) << D3D11_MIP_FILTER_SHIFT + | (reduction & D3D11_FILTER_REDUCTION_TYPE_MASK) << D3D11_FILTER_REDUCTION_TYPE_SHIFT + | anisotropy_clamp + .map(|_| D3D11_FILTER_ANISOTROPIC) + .unwrap_or(0) +} + +pub fn map_image_usage( + usage: image::Usage, + format_desc: FormatDesc, + feature_level: u32, +) -> D3D11_BIND_FLAG { + let mut bind = 0; + + if usage.intersects(image::Usage::TRANSFER_SRC | image::Usage::SAMPLED | image::Usage::STORAGE) + { + bind |= D3D11_BIND_SHADER_RESOURCE; + } + + // we cant get RTVs or UAVs on compressed & depth formats + if !format_desc.is_compressed() && !format_desc.aspects.contains(Aspects::DEPTH) { + if usage.intersects(image::Usage::COLOR_ATTACHMENT | image::Usage::TRANSFER_DST) { + bind |= D3D11_BIND_RENDER_TARGET; + } + + // Only add unordered access on the image if we could use compute shaders to copy. + let transfer_compute = + usage.intersects(image::Usage::TRANSFER_DST) && feature_level >= D3D_FEATURE_LEVEL_11_0; + let storage = usage.intersects(image::Usage::STORAGE); + if transfer_compute || storage { + bind |= D3D11_BIND_UNORDERED_ACCESS; + } + } + + if usage.contains(image::Usage::DEPTH_STENCIL_ATTACHMENT) { + bind |= D3D11_BIND_DEPTH_STENCIL; + } + + bind +} + +pub fn map_feature_level_to_shader_model(feature: u32) -> hlsl::ShaderModel { + match feature { + D3D_FEATURE_LEVEL_9_1 | D3D_FEATURE_LEVEL_9_2 => hlsl::ShaderModel::V4_0L9_1, + D3D_FEATURE_LEVEL_9_3 => hlsl::ShaderModel::V4_0L9_3, + D3D_FEATURE_LEVEL_10_0 => hlsl::ShaderModel::V4_0, + D3D_FEATURE_LEVEL_10_1 => hlsl::ShaderModel::V4_1, + D3D_FEATURE_LEVEL_11_0 | D3D_FEATURE_LEVEL_11_1 => hlsl::ShaderModel::V5_0, + _ => unimplemented!(), + } +} diff --git a/third_party/rust/gfx-backend-dx11/src/debug.rs b/third_party/rust/gfx-backend-dx11/src/debug.rs index ab8f1556c7ba..0d061de3a99e 100644 --- a/third_party/rust/gfx-backend-dx11/src/debug.rs +++ b/third_party/rust/gfx-backend-dx11/src/debug.rs @@ -1,114 +1,114 @@ -use winapi::um::{d3d11, d3d11_1, d3dcommon}; - -use wio::{com::ComPtr, wide::ToWide}; - -use std::{env, ffi::OsStr, fmt}; - -#[must_use] -pub struct DebugScope { - annotation: ComPtr, -} - -impl DebugScope { - // TODO: Not used currently in release, will be used in the future - #[allow(dead_code)] - pub fn with_name( - context: &ComPtr, - args: fmt::Arguments, - ) -> Option { - let name = format!("{}", args); - - // debugging with visual studio and its ilk *really* doesn't like calling this on a - // deferred context when replaying a capture, compared to renderdoc - if unsafe { context.GetType() } == d3d11::D3D11_DEVICE_CONTEXT_DEFERRED { - // TODO: find a better way to detect either if RD or VS is active debugger - if env::var("GFX_NO_RENDERDOC").is_ok() { - return None; - } - } - - let annotation = context - .cast::() - .unwrap(); - let msg: &OsStr = name.as_ref(); - let msg: Vec = msg.to_wide_null(); - - unsafe { - annotation.BeginEvent(msg.as_ptr() as _); - } - - Some(DebugScope { annotation }) - } -} - -impl Drop for DebugScope { - fn drop(&mut self) { - unsafe { - self.annotation.EndEvent(); - } - } -} - -pub fn debug_marker(context: &ComPtr, name: &str) { - // same here - if unsafe { context.GetType() } == d3d11::D3D11_DEVICE_CONTEXT_DEFERRED { - if env::var("GFX_NO_RENDERDOC").is_ok() { - return; - } - } - - let annotation = context - .cast::() - .unwrap(); - let msg: &OsStr = name.as_ref(); - let msg: Vec = msg.to_wide_null(); - - unsafe { - annotation.SetMarker(msg.as_ptr() as _); - } -} - -pub fn verify_debug_ascii(name: &str) -> bool { - let res = name.is_ascii(); - if !res { - error!("DX11 buffer names must be ASCII"); - } - res -} - -/// Set the debug name of a resource. -/// -/// Must be ASCII. -/// -/// SetPrivateData will copy the data internally so the data doesn't need to live. -pub fn set_debug_name(resource: &d3d11::ID3D11DeviceChild, name: &str) { - unsafe { - resource.SetPrivateData( - (&d3dcommon::WKPDID_D3DDebugObjectName) as *const _, - name.len() as _, - name.as_ptr() as *const _, - ); - } -} - -/// Set the debug name of a resource with a given suffix. -/// -/// Must be ASCII. -/// -/// The given string will be mutated to add the suffix, then restored to it's original state. -/// This saves an allocation. SetPrivateData will copy the data internally so the data doesn't need to live. -pub fn set_debug_name_with_suffix( - resource: &d3d11::ID3D11DeviceChild, - name: &mut String, - suffix: &str, -) { - name.push_str(suffix); - unsafe { - resource.SetPrivateData( - (&d3dcommon::WKPDID_D3DDebugObjectName) as *const _, - name.len() as _, - name.as_ptr() as *const _, - ); - } - name.drain((name.len() - suffix.len())..); -} +use winapi::um::{d3d11, d3d11_1, d3dcommon}; + +use wio::{com::ComPtr, wide::ToWide}; + +use std::{env, ffi::OsStr, fmt}; + +#[must_use] +pub struct DebugScope { + annotation: ComPtr, +} + +impl DebugScope { + // TODO: Not used currently in release, will be used in the future + #[allow(dead_code)] + pub fn with_name( + context: &ComPtr, + args: fmt::Arguments, + ) -> Option { + let name = format!("{}", args); + + // debugging with visual studio and its ilk *really* doesn't like calling this on a + // deferred context when replaying a capture, compared to renderdoc + if unsafe { context.GetType() } == d3d11::D3D11_DEVICE_CONTEXT_DEFERRED { + // TODO: find a better way to detect either if RD or VS is active debugger + if env::var("GFX_NO_RENDERDOC").is_ok() { + return None; + } + } + + let annotation = context + .cast::() + .unwrap(); + let msg: &OsStr = name.as_ref(); + let msg: Vec = msg.to_wide_null(); + + unsafe { + annotation.BeginEvent(msg.as_ptr() as _); + } + + Some(DebugScope { annotation }) + } +} + +impl Drop for DebugScope { + fn drop(&mut self) { + unsafe { + self.annotation.EndEvent(); + } + } +} + +pub fn debug_marker(context: &ComPtr, name: &str) { + // same here + if unsafe { context.GetType() } == d3d11::D3D11_DEVICE_CONTEXT_DEFERRED { + if env::var("GFX_NO_RENDERDOC").is_ok() { + return; + } + } + + let annotation = context + .cast::() + .unwrap(); + let msg: &OsStr = name.as_ref(); + let msg: Vec = msg.to_wide_null(); + + unsafe { + annotation.SetMarker(msg.as_ptr() as _); + } +} + +pub fn verify_debug_ascii(name: &str) -> bool { + let res = name.is_ascii(); + if !res { + error!("DX11 buffer names must be ASCII"); + } + res +} + +/// Set the debug name of a resource. +/// +/// Must be ASCII. +/// +/// SetPrivateData will copy the data internally so the data doesn't need to live. +pub fn set_debug_name(resource: &d3d11::ID3D11DeviceChild, name: &str) { + unsafe { + resource.SetPrivateData( + (&d3dcommon::WKPDID_D3DDebugObjectName) as *const _, + name.len() as _, + name.as_ptr() as *const _, + ); + } +} + +/// Set the debug name of a resource with a given suffix. +/// +/// Must be ASCII. +/// +/// The given string will be mutated to add the suffix, then restored to it's original state. +/// This saves an allocation. SetPrivateData will copy the data internally so the data doesn't need to live. +pub fn set_debug_name_with_suffix( + resource: &d3d11::ID3D11DeviceChild, + name: &mut String, + suffix: &str, +) { + name.push_str(suffix); + unsafe { + resource.SetPrivateData( + (&d3dcommon::WKPDID_D3DDebugObjectName) as *const _, + name.len() as _, + name.as_ptr() as *const _, + ); + } + name.drain((name.len() - suffix.len())..); +} diff --git a/third_party/rust/gfx-backend-dx11/src/device.rs b/third_party/rust/gfx-backend-dx11/src/device.rs index a5e5690af4d4..a6c796a40b83 100644 --- a/third_party/rust/gfx-backend-dx11/src/device.rs +++ b/third_party/rust/gfx-backend-dx11/src/device.rs @@ -1,2567 +1,2469 @@ -use auxil::ShaderStage; -use hal::{ - adapter::MemoryProperties, buffer, device, format, image, memory, pass, pool, pso, - pso::VertexInputRate, query, queue::QueueFamilyId, window, -}; - -use winapi::{ - shared::{dxgi, dxgiformat, dxgitype, minwindef::TRUE, windef::HWND, winerror}, - um::{d3d11, d3d11_1, d3d11sdklayers, d3dcommon}, -}; - -use wio::com::ComPtr; - -use std::{ - fmt, mem, - ops::Range, - ptr, - sync::{Arc, Weak}, -}; - -use parking_lot::{Condvar, Mutex, RwLock}; - -use crate::{ - conv, - debug::{set_debug_name, set_debug_name_with_suffix, verify_debug_ascii}, - internal, shader, Backend, Buffer, BufferView, CommandBuffer, CommandPool, ComputePipeline, - DescriptorContent, DescriptorIndex, DescriptorPool, DescriptorSet, DescriptorSetInfo, - DescriptorSetLayout, Fence, Framebuffer, GraphicsPipeline, Image, ImageView, InternalBuffer, - InternalImage, Memory, MultiStageData, PipelineLayout, QueryPool, RawFence, - RegisterAccumulator, RegisterData, RenderPass, ResourceIndex, Sampler, Semaphore, ShaderModule, - SubpassDesc, ViewInfo, -}; - -//TODO: expose coherent type 0x2 when it's properly supported -const BUFFER_TYPE_MASK: u32 = 0x1 | 0x4; - -struct InputLayout { - raw: ComPtr, - required_bindings: u32, - max_vertex_bindings: u32, - topology: d3d11::D3D11_PRIMITIVE_TOPOLOGY, - vertex_strides: Vec, -} - -#[derive(Clone)] -pub struct DepthStencilState { - pub raw: ComPtr, - pub stencil_ref: pso::State, - pub read_only: bool, -} - -pub struct Device { - raw: ComPtr, - raw1: Option>, - pub(crate) context: ComPtr, - features: hal::Features, - memory_properties: MemoryProperties, - render_doc: gfx_renderdoc::RenderDoc, - pub(crate) internal: Arc, -} - -impl fmt::Debug for Device { - fn fmt(&self, fmt: &mut fmt::Formatter) -> fmt::Result { - fmt.write_str("Device") - } -} - -impl Drop for Device { - fn drop(&mut self) { - if let Ok(debug) = self.raw.cast::() { - unsafe { - debug.ReportLiveDeviceObjects(d3d11sdklayers::D3D11_RLDO_DETAIL); - } - } - } -} - -unsafe impl Send for Device {} -unsafe impl Sync for Device {} - -impl Device { - pub fn new( - device: ComPtr, - device1: Option>, - context: ComPtr, - features: hal::Features, - downlevel: hal::DownlevelProperties, - memory_properties: MemoryProperties, - feature_level: u32, - ) -> Self { - Device { - internal: Arc::new(internal::Internal::new( - &device, - features, - feature_level, - downlevel, - )), - raw: device, - raw1: device1, - context, - features, - memory_properties, - render_doc: Default::default(), - } - } - - pub fn as_raw(&self) -> *mut d3d11::ID3D11Device { - self.raw.as_raw() - } - - fn create_rasterizer_state( - &self, - rasterizer_desc: &pso::Rasterizer, - multisampling_desc: &Option, - ) -> Result, pso::CreationError> { - let mut rasterizer = ptr::null_mut(); - let desc = conv::map_rasterizer_desc(rasterizer_desc, multisampling_desc); - - let hr = unsafe { - self.raw - .CreateRasterizerState(&desc, &mut rasterizer as *mut *mut _ as *mut *mut _) - }; - - if winerror::SUCCEEDED(hr) { - Ok(unsafe { ComPtr::from_raw(rasterizer) }) - } else { - Err(pso::CreationError::Other) - } - } - - fn create_blend_state( - &self, - blend_desc: &pso::BlendDesc, - multisampling: &Option, - ) -> Result, pso::CreationError> { - let mut blend = ptr::null_mut(); - let desc = conv::map_blend_desc(blend_desc, multisampling); - - let hr = unsafe { - self.raw - .CreateBlendState(&desc, &mut blend as *mut *mut _ as *mut *mut _) - }; - - if winerror::SUCCEEDED(hr) { - Ok(unsafe { ComPtr::from_raw(blend) }) - } else { - Err(pso::CreationError::Other) - } - } - - fn create_depth_stencil_state( - &self, - depth_desc: &pso::DepthStencilDesc, - ) -> Result { - let mut depth = ptr::null_mut(); - let (desc, stencil_ref, read_only) = conv::map_depth_stencil_desc(depth_desc); - - let hr = unsafe { - self.raw - .CreateDepthStencilState(&desc, &mut depth as *mut *mut _ as *mut *mut _) - }; - - if winerror::SUCCEEDED(hr) { - Ok(DepthStencilState { - raw: unsafe { ComPtr::from_raw(depth) }, - stencil_ref, - read_only, - }) - } else { - Err(pso::CreationError::Other) - } - } - - fn create_input_layout( - &self, - vs: ComPtr, - vertex_buffers: &[pso::VertexBufferDesc], - attributes: &[pso::AttributeDesc], - input_assembler: &pso::InputAssemblerDesc, - vertex_semantic_remapping: auxil::FastHashMap>, - ) -> Result { - let mut layout = ptr::null_mut(); - - let mut vertex_strides = Vec::new(); - let mut required_bindings = 0u32; - let mut max_vertex_bindings = 0u32; - for buffer in vertex_buffers { - required_bindings |= 1 << buffer.binding as u32; - max_vertex_bindings = max_vertex_bindings.max(1u32 + buffer.binding as u32); - - while vertex_strides.len() <= buffer.binding as usize { - vertex_strides.push(0); - } - - vertex_strides[buffer.binding as usize] = buffer.stride; - } - - // See [`shader::introspect_spirv_vertex_semantic_remapping`] for details of why this is needed. - let semantics: Vec<_> = attributes - .iter() - .map( - |attrib| match vertex_semantic_remapping.get(&attrib.location) { - Some(Some((major, minor))) => { - let name = std::borrow::Cow::Owned(format!("TEXCOORD{}_\0", major)); - let location = *minor; - (name, location) - } - _ => { - let name = std::borrow::Cow::Borrowed("TEXCOORD\0"); - let location = attrib.location; - (name, location) - } - }, - ) - .collect(); - - let input_elements = attributes - .iter() - .zip(semantics.iter()) - .filter_map(|(attrib, (semantic_name, semantic_index))| { - let buffer_desc = match vertex_buffers - .iter() - .find(|buffer_desc| buffer_desc.binding == attrib.binding) - { - Some(buffer_desc) => buffer_desc, - None => { - // TODO: - // error!("Couldn't find associated vertex buffer description {:?}", attrib.binding); - return Some(Err(pso::CreationError::Other)); - } - }; - - let (slot_class, step_rate) = match buffer_desc.rate { - VertexInputRate::Vertex => (d3d11::D3D11_INPUT_PER_VERTEX_DATA, 0), - VertexInputRate::Instance(divisor) => { - (d3d11::D3D11_INPUT_PER_INSTANCE_DATA, divisor) - } - }; - let format = attrib.element.format; - - Some(Ok(d3d11::D3D11_INPUT_ELEMENT_DESC { - SemanticName: semantic_name.as_ptr() as *const _, // Semantic name used by SPIRV-Cross - SemanticIndex: *semantic_index, - Format: match conv::map_format(format) { - Some(fm) => fm, - None => { - // TODO: - // error!("Unable to find DXGI format for {:?}", format); - return Some(Err(pso::CreationError::Other)); - } - }, - InputSlot: attrib.binding as _, - AlignedByteOffset: attrib.element.offset, - InputSlotClass: slot_class, - InstanceDataStepRate: step_rate as _, - })) - }) - .collect::, _>>()?; - - let hr = unsafe { - self.raw.CreateInputLayout( - input_elements.as_ptr(), - input_elements.len() as _, - vs.GetBufferPointer(), - vs.GetBufferSize(), - &mut layout as *mut *mut _ as *mut *mut _, - ) - }; - - if winerror::SUCCEEDED(hr) { - let topology = conv::map_topology(input_assembler); - - Ok(InputLayout { - raw: unsafe { ComPtr::from_raw(layout) }, - required_bindings, - max_vertex_bindings, - topology, - vertex_strides, - }) - } else { - error!("CreateInputLayout error 0x{:X}", hr); - Err(pso::CreationError::Other) - } - } - - fn create_vertex_shader( - &self, - blob: ComPtr, - ) -> Result, pso::CreationError> { - let mut vs = ptr::null_mut(); - - let hr = unsafe { - self.raw.CreateVertexShader( - blob.GetBufferPointer(), - blob.GetBufferSize(), - ptr::null_mut(), - &mut vs as *mut *mut _ as *mut *mut _, - ) - }; - - if winerror::SUCCEEDED(hr) { - Ok(unsafe { ComPtr::from_raw(vs) }) - } else { - Err(pso::CreationError::ShaderCreationError( - pso::ShaderStageFlags::VERTEX, - String::from("Failed to create a vertex shader"), - )) - } - } - - fn create_pixel_shader( - &self, - blob: ComPtr, - ) -> Result, pso::CreationError> { - let mut ps = ptr::null_mut(); - - let hr = unsafe { - self.raw.CreatePixelShader( - blob.GetBufferPointer(), - blob.GetBufferSize(), - ptr::null_mut(), - &mut ps as *mut *mut _ as *mut *mut _, - ) - }; - - if winerror::SUCCEEDED(hr) { - Ok(unsafe { ComPtr::from_raw(ps) }) - } else { - Err(pso::CreationError::ShaderCreationError( - pso::ShaderStageFlags::FRAGMENT, - String::from("Failed to create a pixel shader"), - )) - } - } - - fn create_geometry_shader( - &self, - blob: ComPtr, - ) -> Result, pso::CreationError> { - let mut gs = ptr::null_mut(); - - let hr = unsafe { - self.raw.CreateGeometryShader( - blob.GetBufferPointer(), - blob.GetBufferSize(), - ptr::null_mut(), - &mut gs as *mut *mut _ as *mut *mut _, - ) - }; - - if winerror::SUCCEEDED(hr) { - Ok(unsafe { ComPtr::from_raw(gs) }) - } else { - Err(pso::CreationError::ShaderCreationError( - pso::ShaderStageFlags::GEOMETRY, - String::from("Failed to create a geometry shader"), - )) - } - } - - fn create_hull_shader( - &self, - blob: ComPtr, - ) -> Result, pso::CreationError> { - let mut hs = ptr::null_mut(); - - let hr = unsafe { - self.raw.CreateHullShader( - blob.GetBufferPointer(), - blob.GetBufferSize(), - ptr::null_mut(), - &mut hs as *mut *mut _ as *mut *mut _, - ) - }; - - if winerror::SUCCEEDED(hr) { - Ok(unsafe { ComPtr::from_raw(hs) }) - } else { - Err(pso::CreationError::ShaderCreationError( - pso::ShaderStageFlags::HULL, - String::from("Failed to create a hull shader"), - )) - } - } - - fn create_domain_shader( - &self, - blob: ComPtr, - ) -> Result, pso::CreationError> { - let mut ds = ptr::null_mut(); - - let hr = unsafe { - self.raw.CreateDomainShader( - blob.GetBufferPointer(), - blob.GetBufferSize(), - ptr::null_mut(), - &mut ds as *mut *mut _ as *mut *mut _, - ) - }; - - if winerror::SUCCEEDED(hr) { - Ok(unsafe { ComPtr::from_raw(ds) }) - } else { - Err(pso::CreationError::ShaderCreationError( - pso::ShaderStageFlags::DOMAIN, - String::from("Failed to create a domain shader"), - )) - } - } - - fn create_compute_shader( - &self, - blob: ComPtr, - ) -> Result, pso::CreationError> { - let mut cs = ptr::null_mut(); - - let hr = unsafe { - self.raw.CreateComputeShader( - blob.GetBufferPointer(), - blob.GetBufferSize(), - ptr::null_mut(), - &mut cs as *mut *mut _ as *mut *mut _, - ) - }; - - if winerror::SUCCEEDED(hr) { - Ok(unsafe { ComPtr::from_raw(cs) }) - } else { - Err(pso::CreationError::ShaderCreationError( - pso::ShaderStageFlags::COMPUTE, - String::from("Failed to create a compute shader"), - )) - } - } - - // TODO: fix return type.. - fn extract_entry_point( - stage: ShaderStage, - source: &pso::EntryPoint, - layout: &PipelineLayout, - features: &hal::Features, - device_feature_level: u32, - ) -> Result>, pso::CreationError> { - // TODO: entrypoint stuff - match *source.module { - ShaderModule::Dxbc(ref _shader) => Err(pso::CreationError::ShaderCreationError( - pso::ShaderStageFlags::ALL, - String::from("DXBC modules are not supported yet"), - )), - ShaderModule::Spirv(ref raw_data) => Ok(shader::compile_spirv_entrypoint( - raw_data, - stage, - source, - layout, - features, - device_feature_level, - )?), - } - } - - fn view_image_as_shader_resource( - &self, - info: &ViewInfo, - ) -> Result, image::ViewCreationError> { - let mut desc: d3d11::D3D11_SHADER_RESOURCE_VIEW_DESC = unsafe { mem::zeroed() }; - desc.Format = info.format; - if desc.Format == dxgiformat::DXGI_FORMAT_D32_FLOAT_S8X24_UINT { - desc.Format = dxgiformat::DXGI_FORMAT_R32_FLOAT_X8X24_TYPELESS; - } - - #[allow(non_snake_case)] - let MostDetailedMip = info.levels.start as _; - #[allow(non_snake_case)] - let MipLevels = (info.levels.end - info.levels.start) as _; - #[allow(non_snake_case)] - let FirstArraySlice = info.layers.start as _; - #[allow(non_snake_case)] - let ArraySize = (info.layers.end - info.layers.start) as _; - - match info.view_kind { - image::ViewKind::D1 => { - desc.ViewDimension = d3dcommon::D3D11_SRV_DIMENSION_TEXTURE1D; - *unsafe { desc.u.Texture1D_mut() } = d3d11::D3D11_TEX1D_SRV { - MostDetailedMip, - MipLevels, - } - } - image::ViewKind::D1Array => { - desc.ViewDimension = d3dcommon::D3D11_SRV_DIMENSION_TEXTURE1DARRAY; - *unsafe { desc.u.Texture1DArray_mut() } = d3d11::D3D11_TEX1D_ARRAY_SRV { - MostDetailedMip, - MipLevels, - FirstArraySlice, - ArraySize, - } - } - image::ViewKind::D2 if info.kind.num_samples() > 1 => { - desc.ViewDimension = d3dcommon::D3D11_SRV_DIMENSION_TEXTURE2DMS; - *unsafe { desc.u.Texture2DMS_mut() } = d3d11::D3D11_TEX2DMS_SRV { - UnusedField_NothingToDefine: 0, - } - } - image::ViewKind::D2 => { - desc.ViewDimension = d3dcommon::D3D11_SRV_DIMENSION_TEXTURE2D; - *unsafe { desc.u.Texture2D_mut() } = d3d11::D3D11_TEX2D_SRV { - MostDetailedMip, - MipLevels, - } - } - image::ViewKind::D2Array if info.kind.num_samples() > 1 => { - desc.ViewDimension = d3dcommon::D3D11_SRV_DIMENSION_TEXTURE2DMSARRAY; - *unsafe { desc.u.Texture2DMSArray_mut() } = d3d11::D3D11_TEX2DMS_ARRAY_SRV { - FirstArraySlice, - ArraySize, - } - } - image::ViewKind::D2Array => { - desc.ViewDimension = d3dcommon::D3D11_SRV_DIMENSION_TEXTURE2DARRAY; - *unsafe { desc.u.Texture2DArray_mut() } = d3d11::D3D11_TEX2D_ARRAY_SRV { - MostDetailedMip, - MipLevels, - FirstArraySlice, - ArraySize, - } - } - image::ViewKind::D3 => { - desc.ViewDimension = d3dcommon::D3D11_SRV_DIMENSION_TEXTURE3D; - *unsafe { desc.u.Texture3D_mut() } = d3d11::D3D11_TEX3D_SRV { - MostDetailedMip, - MipLevels, - } - } - image::ViewKind::Cube => { - desc.ViewDimension = d3dcommon::D3D11_SRV_DIMENSION_TEXTURECUBE; - *unsafe { desc.u.TextureCube_mut() } = d3d11::D3D11_TEXCUBE_SRV { - MostDetailedMip, - MipLevels, - } - } - image::ViewKind::CubeArray => { - desc.ViewDimension = d3dcommon::D3D11_SRV_DIMENSION_TEXTURECUBEARRAY; - *unsafe { desc.u.TextureCubeArray_mut() } = d3d11::D3D11_TEXCUBE_ARRAY_SRV { - MostDetailedMip, - MipLevels, - First2DArrayFace: FirstArraySlice, - NumCubes: ArraySize / 6, - } - } - } - - let mut srv = ptr::null_mut(); - let hr = unsafe { - self.raw.CreateShaderResourceView( - info.resource, - &desc, - &mut srv as *mut *mut _ as *mut *mut _, - ) - }; - - if winerror::SUCCEEDED(hr) { - Ok(unsafe { ComPtr::from_raw(srv) }) - } else { - Err(image::ViewCreationError::Unsupported) - } - } - - fn view_image_as_unordered_access( - &self, - info: &ViewInfo, - ) -> Result, image::ViewCreationError> { - let mut desc: d3d11::D3D11_UNORDERED_ACCESS_VIEW_DESC = unsafe { mem::zeroed() }; - desc.Format = info.format; - - #[allow(non_snake_case)] - let MipSlice = info.levels.start as _; - #[allow(non_snake_case)] - let FirstArraySlice = info.layers.start as _; - #[allow(non_snake_case)] - let ArraySize = (info.layers.end - info.layers.start) as _; - - match info.view_kind { - image::ViewKind::D1 => { - desc.ViewDimension = d3d11::D3D11_UAV_DIMENSION_TEXTURE1D; - *unsafe { desc.u.Texture1D_mut() } = d3d11::D3D11_TEX1D_UAV { - MipSlice: info.levels.start as _, - } - } - image::ViewKind::D1Array => { - desc.ViewDimension = d3d11::D3D11_UAV_DIMENSION_TEXTURE1DARRAY; - *unsafe { desc.u.Texture1DArray_mut() } = d3d11::D3D11_TEX1D_ARRAY_UAV { - MipSlice, - FirstArraySlice, - ArraySize, - } - } - image::ViewKind::D2 => { - desc.ViewDimension = d3d11::D3D11_UAV_DIMENSION_TEXTURE2D; - *unsafe { desc.u.Texture2D_mut() } = d3d11::D3D11_TEX2D_UAV { - MipSlice: info.levels.start as _, - } - } - image::ViewKind::D2Array => { - desc.ViewDimension = d3d11::D3D11_UAV_DIMENSION_TEXTURE2DARRAY; - *unsafe { desc.u.Texture2DArray_mut() } = d3d11::D3D11_TEX2D_ARRAY_UAV { - MipSlice, - FirstArraySlice, - ArraySize, - } - } - image::ViewKind::D3 => { - desc.ViewDimension = d3d11::D3D11_UAV_DIMENSION_TEXTURE3D; - *unsafe { desc.u.Texture3D_mut() } = d3d11::D3D11_TEX3D_UAV { - MipSlice, - FirstWSlice: FirstArraySlice, - WSize: ArraySize, - } - } - _ => unimplemented!(), - } - - let mut uav = ptr::null_mut(); - let hr = unsafe { - self.raw.CreateUnorderedAccessView( - info.resource, - &desc, - &mut uav as *mut *mut _ as *mut *mut _, - ) - }; - - if winerror::SUCCEEDED(hr) { - Ok(unsafe { ComPtr::from_raw(uav) }) - } else { - error!("CreateUnorderedAccessView failed: 0x{:x}", hr); - - Err(image::ViewCreationError::Unsupported) - } - } - - pub(crate) fn view_image_as_render_target( - &self, - info: &ViewInfo, - ) -> Result, image::ViewCreationError> { - let mut desc: d3d11::D3D11_RENDER_TARGET_VIEW_DESC = unsafe { mem::zeroed() }; - desc.Format = info.format; - - #[allow(non_snake_case)] - let MipSlice = info.levels.start as _; - #[allow(non_snake_case)] - let FirstArraySlice = info.layers.start as _; - #[allow(non_snake_case)] - let ArraySize = (info.layers.end - info.layers.start) as _; - - match info.view_kind { - image::ViewKind::D1 => { - desc.ViewDimension = d3d11::D3D11_RTV_DIMENSION_TEXTURE1D; - *unsafe { desc.u.Texture1D_mut() } = d3d11::D3D11_TEX1D_RTV { MipSlice } - } - image::ViewKind::D1Array => { - desc.ViewDimension = d3d11::D3D11_RTV_DIMENSION_TEXTURE1DARRAY; - *unsafe { desc.u.Texture1DArray_mut() } = d3d11::D3D11_TEX1D_ARRAY_RTV { - MipSlice, - FirstArraySlice, - ArraySize, - } - } - image::ViewKind::D2 => { - if info.kind.num_samples() > 1 { - desc.ViewDimension = d3d11::D3D11_RTV_DIMENSION_TEXTURE2DMS; - *unsafe { desc.u.Texture2DMS_mut() } = d3d11::D3D11_TEX2DMS_RTV { - UnusedField_NothingToDefine: 0, - } - } else { - desc.ViewDimension = d3d11::D3D11_RTV_DIMENSION_TEXTURE2D; - *unsafe { desc.u.Texture2D_mut() } = d3d11::D3D11_TEX2D_RTV { MipSlice } - } - } - image::ViewKind::D2Array => { - if info.kind.num_samples() > 1 { - desc.ViewDimension = d3d11::D3D11_RTV_DIMENSION_TEXTURE2DMSARRAY; - *unsafe { desc.u.Texture2DMSArray_mut() } = d3d11::D3D11_TEX2DMS_ARRAY_RTV { - FirstArraySlice, - ArraySize, - } - } else { - desc.ViewDimension = d3d11::D3D11_RTV_DIMENSION_TEXTURE2DARRAY; - *unsafe { desc.u.Texture2DArray_mut() } = d3d11::D3D11_TEX2D_ARRAY_RTV { - MipSlice, - FirstArraySlice, - ArraySize, - } - } - } - image::ViewKind::D3 => { - desc.ViewDimension = d3d11::D3D11_RTV_DIMENSION_TEXTURE3D; - *unsafe { desc.u.Texture3D_mut() } = d3d11::D3D11_TEX3D_RTV { - MipSlice, - FirstWSlice: FirstArraySlice, - WSize: ArraySize, - } - } - _ => unimplemented!(), - } - - let mut rtv = ptr::null_mut(); - let hr = unsafe { - self.raw.CreateRenderTargetView( - info.resource, - &desc, - &mut rtv as *mut *mut _ as *mut *mut _, - ) - }; - - if winerror::SUCCEEDED(hr) { - Ok(unsafe { ComPtr::from_raw(rtv) }) - } else { - error!("CreateRenderTargetView failed: 0x{:x}", hr); - - Err(image::ViewCreationError::Unsupported) - } - } - - fn view_image_as_depth_stencil( - &self, - info: &ViewInfo, - read_only_stencil: Option, - ) -> Result, image::ViewCreationError> { - #![allow(non_snake_case)] - - let MipSlice = info.levels.start as _; - let FirstArraySlice = info.layers.start as _; - let ArraySize = (info.layers.end - info.layers.start) as _; - assert_eq!(info.levels.start + 1, info.levels.end); - assert!(info.layers.end <= info.kind.num_layers()); - - let mut desc: d3d11::D3D11_DEPTH_STENCIL_VIEW_DESC = unsafe { mem::zeroed() }; - desc.Format = info.format; - - if let Some(stencil) = read_only_stencil { - desc.Flags = match stencil { - true => d3d11::D3D11_DSV_READ_ONLY_DEPTH | d3d11::D3D11_DSV_READ_ONLY_STENCIL, - false => d3d11::D3D11_DSV_READ_ONLY_DEPTH, - } - } - - match info.view_kind { - image::ViewKind::D2 => { - if info.kind.num_samples() > 1 { - desc.ViewDimension = d3d11::D3D11_DSV_DIMENSION_TEXTURE2DMS; - *unsafe { desc.u.Texture2DMS_mut() } = d3d11::D3D11_TEX2DMS_DSV { - UnusedField_NothingToDefine: 0, - } - } else { - desc.ViewDimension = d3d11::D3D11_DSV_DIMENSION_TEXTURE2D; - *unsafe { desc.u.Texture2D_mut() } = d3d11::D3D11_TEX2D_DSV { MipSlice } - } - } - image::ViewKind::D2Array => { - if info.kind.num_samples() > 1 { - desc.ViewDimension = d3d11::D3D11_DSV_DIMENSION_TEXTURE2DMSARRAY; - *unsafe { desc.u.Texture2DMSArray_mut() } = d3d11::D3D11_TEX2DMS_ARRAY_DSV { - FirstArraySlice, - ArraySize, - } - } else { - desc.ViewDimension = d3d11::D3D11_DSV_DIMENSION_TEXTURE2DARRAY; - *unsafe { desc.u.Texture2DArray_mut() } = d3d11::D3D11_TEX2D_ARRAY_DSV { - MipSlice, - FirstArraySlice, - ArraySize, - } - } - } - image::ViewKind::D1 - | image::ViewKind::D1Array - | image::ViewKind::D3 - | image::ViewKind::Cube - | image::ViewKind::CubeArray => { - warn!( - "3D and cube views are not supported for the image, kind: {:?}", - info.kind - ); - return Err(image::ViewCreationError::BadKind(info.view_kind)); - } - } - - let mut dsv = ptr::null_mut(); - let hr = unsafe { - self.raw.CreateDepthStencilView( - info.resource, - &desc, - &mut dsv as *mut *mut _ as *mut *mut _, - ) - }; - - if winerror::SUCCEEDED(hr) { - Ok(unsafe { ComPtr::from_raw(dsv) }) - } else { - error!("CreateDepthStencilView failed: 0x{:x}", hr); - - Err(image::ViewCreationError::Unsupported) - } - } - - pub(crate) fn create_swapchain_impl( - &self, - config: &window::SwapchainConfig, - window_handle: HWND, - factory: ComPtr, - ) -> Result<(ComPtr, dxgiformat::DXGI_FORMAT), window::SwapchainError> - { - // TODO: use IDXGIFactory2 for >=11.1 - // TODO: this function should be able to fail (Result)? - - debug!("{:#?}", config); - let non_srgb_format = conv::map_format_nosrgb(config.format).unwrap(); - - let mut desc = dxgi::DXGI_SWAP_CHAIN_DESC { - BufferDesc: dxgitype::DXGI_MODE_DESC { - Width: config.extent.width, - Height: config.extent.height, - // TODO: should this grab max value of all monitor hz? vsync - // will clamp to current monitor anyways? - RefreshRate: dxgitype::DXGI_RATIONAL { - Numerator: 1, - Denominator: 60, - }, - Format: non_srgb_format, - ScanlineOrdering: dxgitype::DXGI_MODE_SCANLINE_ORDER_UNSPECIFIED, - Scaling: dxgitype::DXGI_MODE_SCALING_UNSPECIFIED, - }, - SampleDesc: dxgitype::DXGI_SAMPLE_DESC { - Count: 1, - Quality: 0, - }, - BufferUsage: dxgitype::DXGI_USAGE_RENDER_TARGET_OUTPUT, - BufferCount: config.image_count, - OutputWindow: window_handle, - // TODO: - Windowed: TRUE, - // TODO: - SwapEffect: dxgi::DXGI_SWAP_EFFECT_DISCARD, - Flags: 0, - }; - - let dxgi_swapchain = { - let mut swapchain: *mut dxgi::IDXGISwapChain = ptr::null_mut(); - let hr = unsafe { - factory.CreateSwapChain( - self.raw.as_raw() as *mut _, - &mut desc as *mut _, - &mut swapchain as *mut *mut _ as *mut *mut _, - ) - }; - assert_eq!(hr, winerror::S_OK); - - unsafe { ComPtr::from_raw(swapchain) } - }; - Ok((dxgi_swapchain, non_srgb_format)) - } -} - -impl device::Device for Device { - unsafe fn allocate_memory( - &self, - mem_type: hal::MemoryTypeId, - size: u64, - ) -> Result { - let properties = self.memory_properties.memory_types[mem_type.0].properties; - let host_ptr = if properties.contains(hal::memory::Properties::CPU_VISIBLE) { - let mut data = vec![0u8; size as usize]; - let ptr = data.as_mut_ptr(); - mem::forget(data); - ptr - } else { - ptr::null_mut() - }; - Ok(Memory { - properties, - size, - host_ptr, - local_buffers: Arc::new(RwLock::new(thunderdome::Arena::new())), - }) - } - - unsafe fn create_command_pool( - &self, - _family: QueueFamilyId, - _create_flags: pool::CommandPoolCreateFlags, - ) -> Result { - // TODO: - Ok(CommandPool { - device: self.raw.clone(), - device1: self.raw1.clone(), - internal: Arc::clone(&self.internal), - }) - } - - unsafe fn destroy_command_pool(&self, _pool: CommandPool) { - // automatic - } - - unsafe fn create_render_pass<'a, Ia, Is, Id>( - &self, - attachments: Ia, - subpasses: Is, - _dependencies: Id, - ) -> Result - where - Ia: Iterator, - Is: Iterator>, - { - Ok(RenderPass { - attachments: attachments.collect(), - subpasses: subpasses - .map(|desc| SubpassDesc { - color_attachments: desc.colors.to_vec(), - depth_stencil_attachment: desc.depth_stencil.cloned(), - input_attachments: desc.inputs.to_vec(), - resolve_attachments: desc.resolves.to_vec(), - }) - .collect(), - }) - } - - unsafe fn create_pipeline_layout<'a, Is, Ic>( - &self, - set_layouts: Is, - _push_constant_ranges: Ic, - ) -> Result - where - Is: Iterator, - Ic: Iterator)>, - { - let mut res_offsets = MultiStageData::>::default(); - let mut sets = Vec::new(); - for set_layout in set_layouts { - sets.push(DescriptorSetInfo { - bindings: Arc::clone(&set_layout.bindings), - registers: res_offsets.advance(&set_layout.pool_mapping), - }); - } - - res_offsets.map_other(|data| { - // These use <= because this tells us the _next_ register, so maximum usage will be equal to the limit. - // - // Leave one slot for push constants - assert!( - data.c.res_index as u32 - <= d3d11::D3D11_COMMONSHADER_CONSTANT_BUFFER_API_SLOT_COUNT - 1, - "{} bound constant buffers exceeds limit of {}", - data.c.res_index as u32, - d3d11::D3D11_COMMONSHADER_CONSTANT_BUFFER_API_SLOT_COUNT - 1, - ); - assert!( - data.s.res_index as u32 <= d3d11::D3D11_COMMONSHADER_SAMPLER_REGISTER_COUNT, - "{} bound samplers exceeds limit of {}", - data.s.res_index as u32, - d3d11::D3D11_COMMONSHADER_SAMPLER_REGISTER_COUNT, - ); - assert!( - data.t.res_index as u32 <= d3d11::D3D11_COMMONSHADER_INPUT_RESOURCE_REGISTER_COUNT, - "{} bound sampled textures and read-only buffers exceeds limit of {}", - data.t.res_index as u32, - d3d11::D3D11_COMMONSHADER_INPUT_RESOURCE_REGISTER_COUNT, - ); - assert!( - data.u.res_index as u32 <= d3d11::D3D11_PS_CS_UAV_REGISTER_COUNT, - "{} bound storage textures and read-write buffers exceeds limit of {}", - data.u.res_index as u32, - d3d11::D3D11_PS_CS_UAV_REGISTER_COUNT, - ); - }); - - Ok(PipelineLayout { sets }) - } - - unsafe fn create_pipeline_cache( - &self, - _data: Option<&[u8]>, - ) -> Result<(), device::OutOfMemory> { - Ok(()) - } - - unsafe fn get_pipeline_cache_data(&self, _cache: &()) -> Result, device::OutOfMemory> { - //empty - Ok(Vec::new()) - } - - unsafe fn destroy_pipeline_cache(&self, _: ()) { - //empty - } - - unsafe fn merge_pipeline_caches<'a, I>( - &self, - _: &mut (), - _: I, - ) -> Result<(), device::OutOfMemory> - where - I: Iterator, - { - //empty - Ok(()) - } - - unsafe fn create_graphics_pipeline<'a>( - &self, - desc: &pso::GraphicsPipelineDesc<'a, Backend>, - _cache: Option<&()>, - ) -> Result { - let features = &self.features; - let build_shader = - |stage: ShaderStage, source: Option<&pso::EntryPoint<'a, Backend>>| match source { - Some(src) => Self::extract_entry_point( - stage, - src, - desc.layout, - features, - self.internal.device_feature_level, - ), - None => Ok(None), - }; - - let (layout, vs, gs, hs, ds) = match desc.primitive_assembler { - pso::PrimitiveAssemblerDesc::Vertex { - buffers, - attributes, - ref input_assembler, - ref vertex, - ref tessellation, - ref geometry, - } => { - let vertex_semantic_remapping = match vertex.module { - ShaderModule::Spirv(spirv) => { - shader::introspect_spirv_vertex_semantic_remapping(spirv)? - } - _ => unimplemented!(), - }; - - let vs = build_shader(ShaderStage::Vertex, Some(&vertex))?.unwrap(); - let gs = build_shader(ShaderStage::Geometry, geometry.as_ref())?; - - let layout = self.create_input_layout( - vs.clone(), - buffers, - attributes, - input_assembler, - vertex_semantic_remapping, - )?; - - let vs = self.create_vertex_shader(vs)?; - let gs = if let Some(blob) = gs { - Some(self.create_geometry_shader(blob)?) - } else { - None - }; - let (hs, ds) = if let Some(ts) = tessellation { - let hs = build_shader(ShaderStage::Hull, Some(&ts.0))?.unwrap(); - let ds = build_shader(ShaderStage::Domain, Some(&ts.1))?.unwrap(); - - ( - Some(self.create_hull_shader(hs)?), - Some(self.create_domain_shader(ds)?), - ) - } else { - (None, None) - }; - - (layout, vs, gs, hs, ds) - } - pso::PrimitiveAssemblerDesc::Mesh { .. } => { - return Err(pso::CreationError::UnsupportedPipeline) - } - }; - - let ps = build_shader(ShaderStage::Fragment, desc.fragment.as_ref())?; - let ps = if let Some(blob) = ps { - Some(self.create_pixel_shader(blob)?) - } else { - None - }; - - let rasterizer_state = - self.create_rasterizer_state(&desc.rasterizer, &desc.multisampling)?; - let blend_state = self.create_blend_state(&desc.blender, &desc.multisampling)?; - let depth_stencil_state = Some(self.create_depth_stencil_state(&desc.depth_stencil)?); - - match desc.label { - Some(label) if verify_debug_ascii(label) => { - let mut name = label.to_string(); - - set_debug_name_with_suffix(&blend_state, &mut name, " -- Blend State"); - set_debug_name_with_suffix(&rasterizer_state, &mut name, " -- Rasterizer State"); - set_debug_name_with_suffix(&layout.raw, &mut name, " -- Input Layout"); - if let Some(ref dss) = depth_stencil_state { - set_debug_name_with_suffix(&dss.raw, &mut name, " -- Depth Stencil State"); - } - } - _ => {} - } - - Ok(GraphicsPipeline { - vs, - gs, - ds, - hs, - ps, - topology: layout.topology, - input_layout: layout.raw, - rasterizer_state, - blend_state, - depth_stencil_state, - baked_states: desc.baked_states.clone(), - required_bindings: layout.required_bindings, - max_vertex_bindings: layout.max_vertex_bindings, - strides: layout.vertex_strides, - }) - } - - unsafe fn create_compute_pipeline<'a>( - &self, - desc: &pso::ComputePipelineDesc<'a, Backend>, - _cache: Option<&()>, - ) -> Result { - let features = &self.features; - let build_shader = - |stage: ShaderStage, source: Option<&pso::EntryPoint<'a, Backend>>| match source { - Some(src) => Self::extract_entry_point( - stage, - src, - desc.layout, - features, - self.internal.device_feature_level, - ), - None => Ok(None), - }; - - let cs = build_shader(ShaderStage::Compute, Some(&desc.shader))?.unwrap(); - let cs = self.create_compute_shader(cs)?; - - Ok(ComputePipeline { cs }) - } - - unsafe fn create_framebuffer( - &self, - _renderpass: &RenderPass, - _attachments: I, - extent: image::Extent, - ) -> Result { - Ok(Framebuffer { - layers: extent.depth as _, - }) - } - - unsafe fn create_shader_module( - &self, - raw_data: &[u32], - ) -> Result { - Ok(ShaderModule::Spirv(raw_data.into())) - } - - unsafe fn create_buffer( - &self, - size: u64, - usage: buffer::Usage, - _sparse: memory::SparseFlags, - ) -> Result { - use buffer::Usage; - - let mut bind = 0; - - if usage.contains(Usage::UNIFORM) { - bind |= d3d11::D3D11_BIND_CONSTANT_BUFFER; - } - if usage.contains(Usage::VERTEX) { - bind |= d3d11::D3D11_BIND_VERTEX_BUFFER; - } - if usage.contains(Usage::INDEX) { - bind |= d3d11::D3D11_BIND_INDEX_BUFFER; - } - - // TODO: >=11.1 - if usage.intersects( - Usage::UNIFORM_TEXEL | Usage::STORAGE_TEXEL | Usage::TRANSFER_SRC | Usage::STORAGE, - ) { - bind |= d3d11::D3D11_BIND_SHADER_RESOURCE; - } - - if usage.intersects(Usage::TRANSFER_DST | Usage::STORAGE) { - bind |= d3d11::D3D11_BIND_UNORDERED_ACCESS; - } - - // if `D3D11_BIND_CONSTANT_BUFFER` intersects with any other bind flag, we need to handle - // it by creating two buffers. one with `D3D11_BIND_CONSTANT_BUFFER` and one with the rest - let needs_disjoint_cb = bind & d3d11::D3D11_BIND_CONSTANT_BUFFER != 0 - && bind != d3d11::D3D11_BIND_CONSTANT_BUFFER; - - if needs_disjoint_cb { - bind ^= d3d11::D3D11_BIND_CONSTANT_BUFFER; - } - - fn up_align(x: u64, alignment: u64) -> u64 { - (x + alignment - 1) & !(alignment - 1) - } - - // constant buffer size need to be divisible by 16 - let size = if usage.contains(Usage::UNIFORM) { - up_align(size, 16) - } else { - up_align(size, 4) - }; - - Ok(Buffer { - internal: InternalBuffer { - raw: ptr::null_mut(), - disjoint_cb: if needs_disjoint_cb { - Some(ptr::null_mut()) - } else { - None - }, - srv: None, - uav: None, - usage, - debug_name: None, - }, - bound_range: 0..0, - local_memory_arena: Weak::new(), - memory_index: None, - is_coherent: false, - memory_ptr: ptr::null_mut(), - bind, - requirements: memory::Requirements { - size, - alignment: 4, - type_mask: BUFFER_TYPE_MASK, - }, - }) - } - - unsafe fn get_buffer_requirements(&self, buffer: &Buffer) -> memory::Requirements { - buffer.requirements - } - - unsafe fn bind_buffer_memory( - &self, - memory: &Memory, - offset: u64, - buffer: &mut Buffer, - ) -> Result<(), device::BindError> { - debug!( - "usage={:?}, props={:b}", - buffer.internal.usage, memory.properties - ); - - #[allow(non_snake_case)] - let mut MiscFlags = if buffer.bind - & (d3d11::D3D11_BIND_SHADER_RESOURCE | d3d11::D3D11_BIND_UNORDERED_ACCESS) - != 0 - { - d3d11::D3D11_RESOURCE_MISC_BUFFER_ALLOW_RAW_VIEWS - } else { - 0 - }; - - if buffer.internal.usage.contains(buffer::Usage::INDIRECT) { - MiscFlags |= d3d11::D3D11_RESOURCE_MISC_DRAWINDIRECT_ARGS; - } - - let initial_data = if memory.host_ptr.is_null() { - None - } else { - Some(d3d11::D3D11_SUBRESOURCE_DATA { - pSysMem: memory.host_ptr.offset(offset as isize) as *const _, - SysMemPitch: 0, - SysMemSlicePitch: 0, - }) - }; - - //TODO: check `memory.properties.contains(memory::Properties::DEVICE_LOCAL)` ? - let raw = { - // device local memory - let desc = d3d11::D3D11_BUFFER_DESC { - ByteWidth: buffer.requirements.size as _, - Usage: d3d11::D3D11_USAGE_DEFAULT, - BindFlags: buffer.bind, - CPUAccessFlags: 0, - MiscFlags, - StructureByteStride: if buffer.internal.usage.contains(buffer::Usage::TRANSFER_SRC) - { - 4 - } else { - 0 - }, - }; - - let mut raw: *mut d3d11::ID3D11Buffer = ptr::null_mut(); - let hr = self.raw.CreateBuffer( - &desc, - initial_data.as_ref().map_or(ptr::null_mut(), |id| id), - &mut raw as *mut *mut _ as *mut *mut _, - ); - - if !winerror::SUCCEEDED(hr) { - return Err(device::BindError::WrongMemory); - } - - if let Some(ref mut name) = buffer.internal.debug_name { - set_debug_name(&*raw, name); - } - - ComPtr::from_raw(raw) - }; - - let disjoint_cb = if buffer.internal.disjoint_cb.is_some() { - let desc = d3d11::D3D11_BUFFER_DESC { - ByteWidth: buffer.requirements.size as _, - Usage: d3d11::D3D11_USAGE_DEFAULT, - BindFlags: d3d11::D3D11_BIND_CONSTANT_BUFFER, - CPUAccessFlags: 0, - MiscFlags: 0, - StructureByteStride: 0, - }; - - let mut disjoint_raw: *mut d3d11::ID3D11Buffer = ptr::null_mut(); - let hr = self.raw.CreateBuffer( - &desc, - initial_data.as_ref().map_or(ptr::null_mut(), |id| id), - &mut disjoint_raw as *mut *mut _ as *mut *mut _, - ); - - if !winerror::SUCCEEDED(hr) { - return Err(device::BindError::WrongMemory); - } - - if let Some(ref mut name) = buffer.internal.debug_name { - set_debug_name_with_suffix(&*disjoint_raw, name, " -- Constant Buffer"); - } - - Some(disjoint_raw) - } else { - None - }; - - let srv = if buffer.bind & d3d11::D3D11_BIND_SHADER_RESOURCE != 0 { - let mut desc = mem::zeroed::(); - desc.Format = dxgiformat::DXGI_FORMAT_R32_TYPELESS; - desc.ViewDimension = d3dcommon::D3D11_SRV_DIMENSION_BUFFEREX; - *desc.u.BufferEx_mut() = d3d11::D3D11_BUFFEREX_SRV { - FirstElement: 0, - NumElements: buffer.requirements.size as u32 / 4, - Flags: d3d11::D3D11_BUFFEREX_SRV_FLAG_RAW, - }; - - let mut srv: *mut d3d11::ID3D11ShaderResourceView = ptr::null_mut(); - let hr = self.raw.CreateShaderResourceView( - raw.as_raw() as *mut _, - &desc, - &mut srv as *mut *mut _ as *mut *mut _, - ); - - if !winerror::SUCCEEDED(hr) { - error!("CreateShaderResourceView failed: 0x{:x}", hr); - - return Err(device::BindError::WrongMemory); - } - - if let Some(ref mut name) = buffer.internal.debug_name { - set_debug_name_with_suffix(&*srv, name, " -- SRV"); - } - - Some(srv) - } else { - None - }; - - let uav = if buffer.bind & d3d11::D3D11_BIND_UNORDERED_ACCESS != 0 { - let mut desc = mem::zeroed::(); - desc.Format = dxgiformat::DXGI_FORMAT_R32_TYPELESS; - desc.ViewDimension = d3d11::D3D11_UAV_DIMENSION_BUFFER; - *desc.u.Buffer_mut() = d3d11::D3D11_BUFFER_UAV { - FirstElement: 0, - NumElements: buffer.requirements.size as u32 / 4, - Flags: d3d11::D3D11_BUFFER_UAV_FLAG_RAW, - }; - - let mut uav: *mut d3d11::ID3D11UnorderedAccessView = ptr::null_mut(); - let hr = self.raw.CreateUnorderedAccessView( - raw.as_raw() as *mut _, - &desc, - &mut uav as *mut *mut _ as *mut *mut _, - ); - - if !winerror::SUCCEEDED(hr) { - error!("CreateUnorderedAccessView failed: 0x{:x}", hr); - - return Err(device::BindError::WrongMemory); - } - - if let Some(ref mut name) = buffer.internal.debug_name { - set_debug_name_with_suffix(&*uav, name, " -- UAV"); - } - - Some(uav) - } else { - None - }; - - let internal = InternalBuffer { - raw: raw.into_raw(), - disjoint_cb, - srv, - uav, - usage: buffer.internal.usage, - debug_name: buffer.internal.debug_name.take(), - }; - let range = offset..offset + buffer.requirements.size; - - let memory_index = memory.bind_buffer(range.clone(), internal.clone()); - - buffer.internal = internal; - buffer.is_coherent = memory - .properties - .contains(hal::memory::Properties::COHERENT); - buffer.memory_ptr = memory.host_ptr; - buffer.bound_range = range; - buffer.local_memory_arena = Arc::downgrade(&memory.local_buffers); - buffer.memory_index = Some(memory_index); - - Ok(()) - } - - unsafe fn create_buffer_view( - &self, - _buffer: &Buffer, - _format: Option, - _range: buffer::SubRange, - ) -> Result { - unimplemented!() - } - - unsafe fn create_image( - &self, - kind: image::Kind, - mip_levels: image::Level, - format: format::Format, - _tiling: image::Tiling, - usage: image::Usage, - _sparse: memory::SparseFlags, - view_caps: image::ViewCapabilities, - ) -> Result { - let surface_desc = format.base_format().0.desc(); - let bytes_per_texel = surface_desc.bits / 8; - let ext = kind.extent(); - let size = (ext.width * ext.height * ext.depth) as u64 * bytes_per_texel as u64; - - let bind = conv::map_image_usage(usage, surface_desc, self.internal.device_feature_level); - debug!("{:b}", bind); - - Ok(Image { - internal: InternalImage { - raw: ptr::null_mut(), - copy_srv: None, - srv: None, - unordered_access_views: Vec::new(), - depth_stencil_views: Vec::new(), - render_target_views: Vec::new(), - debug_name: None, - }, - decomposed_format: conv::DecomposedDxgiFormat::UNKNOWN, - kind, - mip_levels, - format, - usage, - view_caps, - bind, - requirements: memory::Requirements { - size: size, - alignment: 4, - type_mask: 0x1, // device-local only - }, - }) - } - - unsafe fn get_image_requirements(&self, image: &Image) -> memory::Requirements { - image.requirements - } - - unsafe fn get_image_subresource_footprint( - &self, - _image: &Image, - _sub: image::Subresource, - ) -> image::SubresourceFootprint { - unimplemented!() - } - - unsafe fn bind_image_memory( - &self, - memory: &Memory, - _offset: u64, - image: &mut Image, - ) -> Result<(), device::BindError> { - use image::Usage; - use memory::Properties; - - let base_format = image.format.base_format(); - let format_desc = base_format.0.desc(); - - let compressed = format_desc.is_compressed(); - let depth = image.format.is_depth(); - let stencil = image.format.is_stencil(); - - let (bind, usage, cpu) = if memory.properties == Properties::DEVICE_LOCAL { - (image.bind, d3d11::D3D11_USAGE_DEFAULT, 0) - } else if memory.properties - == (Properties::DEVICE_LOCAL | Properties::CPU_VISIBLE | Properties::CPU_CACHED) - { - ( - image.bind, - d3d11::D3D11_USAGE_DYNAMIC, - d3d11::D3D11_CPU_ACCESS_WRITE, - ) - } else if memory.properties == (Properties::CPU_VISIBLE | Properties::CPU_CACHED) { - ( - 0, - d3d11::D3D11_USAGE_STAGING, - d3d11::D3D11_CPU_ACCESS_READ | d3d11::D3D11_CPU_ACCESS_WRITE, - ) - } else { - unimplemented!() - }; - - let dxgi_format = conv::map_format(image.format).unwrap(); - let decomposed = conv::DecomposedDxgiFormat::from_dxgi_format(dxgi_format); - assert!( - memory.host_ptr.is_null(), - "Images can only be allocated from device-local memory" - ); - let initial_data_ptr = ptr::null_mut(); - - let mut resource = ptr::null_mut(); - let view_kind = match image.kind { - image::Kind::D1(width, layers) => { - let desc = d3d11::D3D11_TEXTURE1D_DESC { - Width: width, - MipLevels: image.mip_levels as _, - ArraySize: layers as _, - Format: decomposed.typeless, - Usage: usage, - BindFlags: bind, - CPUAccessFlags: cpu, - MiscFlags: 0, - }; - - let hr = self.raw.CreateTexture1D( - &desc, - initial_data_ptr, - &mut resource as *mut *mut _ as *mut *mut _, - ); - - if !winerror::SUCCEEDED(hr) { - error!("CreateTexture1D failed: 0x{:x}", hr); - - return Err(device::BindError::WrongMemory); - } - - image::ViewKind::D1Array - } - image::Kind::D2(width, height, layers, samples) => { - let desc = d3d11::D3D11_TEXTURE2D_DESC { - Width: width, - Height: height, - MipLevels: image.mip_levels as _, - ArraySize: layers as _, - Format: decomposed.typeless, - SampleDesc: dxgitype::DXGI_SAMPLE_DESC { - Count: samples as _, - Quality: 0, - }, - Usage: usage, - BindFlags: bind, - CPUAccessFlags: cpu, - MiscFlags: { - let mut flags = 0; - if image.view_caps.contains(image::ViewCapabilities::KIND_CUBE) { - flags |= d3d11::D3D11_RESOURCE_MISC_TEXTURECUBE; - } - flags - }, - }; - - let hr = self.raw.CreateTexture2D( - &desc, - initial_data_ptr, - &mut resource as *mut *mut _ as *mut *mut _, - ); - - if !winerror::SUCCEEDED(hr) { - error!("CreateTexture2D failed: 0x{:x}", hr); - - return Err(device::BindError::WrongMemory); - } - - image::ViewKind::D2Array - } - image::Kind::D3(width, height, depth) => { - let desc = d3d11::D3D11_TEXTURE3D_DESC { - Width: width, - Height: height, - Depth: depth, - MipLevels: image.mip_levels as _, - Format: decomposed.typeless, - Usage: usage, - BindFlags: bind, - CPUAccessFlags: cpu, - MiscFlags: 0, - }; - - let hr = self.raw.CreateTexture3D( - &desc, - initial_data_ptr, - &mut resource as *mut *mut _ as *mut *mut _, - ); - - if !winerror::SUCCEEDED(hr) { - error!("CreateTexture3D failed: 0x{:x}", hr); - - return Err(device::BindError::WrongMemory); - } - - image::ViewKind::D3 - } - }; - - let mut unordered_access_views = Vec::new(); - - if image.usage.contains(Usage::TRANSFER_DST) - && !compressed - && !depth - && self.internal.downlevel.storage_images - { - for mip in 0..image.mip_levels { - let view = ViewInfo { - resource: resource, - kind: image.kind, - caps: image::ViewCapabilities::empty(), - view_kind, - // TODO: we should be using `uav_format` rather than `copy_uav_format`, and share - // the UAVs when the formats are identical - format: decomposed.copy_uav.unwrap(), - levels: mip..(mip + 1), - layers: 0..image.kind.num_layers(), - }; - - let uav = self - .view_image_as_unordered_access(&view) - .map_err(|_| device::BindError::WrongMemory)?; - - if let Some(ref name) = image.internal.debug_name { - set_debug_name(&uav, &format!("{} -- UAV Mip {}", name, mip)); - } - - unordered_access_views.push(uav); - } - } - - let (copy_srv, srv) = if image.usage.contains(image::Usage::TRANSFER_SRC) { - let mut view = ViewInfo { - resource: resource, - kind: image.kind, - caps: image::ViewCapabilities::empty(), - view_kind, - format: decomposed.copy_srv.unwrap(), - levels: 0..image.mip_levels, - layers: 0..image.kind.num_layers(), - }; - - let copy_srv = if !compressed { - Some( - self.view_image_as_shader_resource(&view) - .map_err(|_| device::BindError::WrongMemory)?, - ) - } else { - None - }; - - view.format = decomposed.srv.unwrap(); - - let srv = if !depth && !stencil { - Some( - self.view_image_as_shader_resource(&view) - .map_err(|_| device::BindError::WrongMemory)?, - ) - } else { - None - }; - - (copy_srv, srv) - } else { - (None, None) - }; - - let mut render_target_views = Vec::new(); - - if (image.usage.contains(image::Usage::COLOR_ATTACHMENT) - || image.usage.contains(image::Usage::TRANSFER_DST)) - && !compressed - && !depth - { - for layer in 0..image.kind.num_layers() { - for mip in 0..image.mip_levels { - let view = ViewInfo { - resource, - kind: image.kind, - caps: image::ViewCapabilities::empty(), - view_kind, - format: decomposed.rtv.unwrap(), - levels: mip..(mip + 1), - layers: layer..(layer + 1), - }; - - let rtv = self - .view_image_as_render_target(&view) - .map_err(|_| device::BindError::WrongMemory)?; - - if let Some(ref name) = image.internal.debug_name { - set_debug_name( - &rtv, - &format!("{} -- RTV Mip {} Layer {}", name, mip, layer), - ); - } - - render_target_views.push(rtv); - } - } - }; - - let mut depth_stencil_views = Vec::new(); - - if depth { - for layer in 0..image.kind.num_layers() { - for mip in 0..image.mip_levels { - let view = ViewInfo { - resource, - kind: image.kind, - caps: image::ViewCapabilities::empty(), - view_kind, - format: decomposed.dsv.unwrap(), - levels: mip..(mip + 1), - layers: layer..(layer + 1), - }; - - let dsv = self - .view_image_as_depth_stencil(&view, None) - .map_err(|_| device::BindError::WrongMemory)?; - - if let Some(ref name) = image.internal.debug_name { - set_debug_name( - &dsv, - &format!("{} -- DSV Mip {} Layer {}", name, mip, layer), - ); - } - - depth_stencil_views.push(dsv); - } - } - } - - if let Some(ref mut name) = image.internal.debug_name { - set_debug_name(&*resource, name); - if let Some(ref copy_srv) = copy_srv { - set_debug_name_with_suffix(copy_srv, name, " -- Copy SRV"); - } - if let Some(ref srv) = srv { - set_debug_name_with_suffix(srv, name, " -- SRV"); - } - } - - let internal = InternalImage { - raw: resource, - copy_srv, - srv, - unordered_access_views, - depth_stencil_views, - render_target_views, - debug_name: image.internal.debug_name.take(), - }; - - image.decomposed_format = decomposed; - image.internal = internal; - - Ok(()) - } - - unsafe fn create_image_view( - &self, - image: &Image, - view_kind: image::ViewKind, - format: format::Format, - _swizzle: format::Swizzle, - usage: image::Usage, - range: image::SubresourceRange, - ) -> Result { - let is_array = image.kind.num_layers() > 1; - let num_levels = range.resolve_level_count(image.mip_levels); - let num_layers = range.resolve_layer_count(image.kind.num_layers()); - - let info = ViewInfo { - resource: image.internal.raw, - kind: image.kind, - caps: image.view_caps, - // D3D11 doesn't allow looking at a single slice of an array as a non-array - view_kind: if is_array && view_kind == image::ViewKind::D2 { - image::ViewKind::D2Array - } else if is_array && view_kind == image::ViewKind::D1 { - image::ViewKind::D1Array - } else { - view_kind - }, - format: conv::map_format(format).ok_or(image::ViewCreationError::BadFormat(format))?, - levels: range.level_start..range.level_start + num_levels, - layers: range.layer_start..range.layer_start + num_layers, - }; - - let srv_info = ViewInfo { - format: conv::viewable_format(info.format), - ..info.clone() - }; - - let mut debug_name = image.internal.debug_name.clone(); - - Ok(ImageView { - subresource: d3d11::D3D11CalcSubresource( - 0, - range.layer_start as _, - range.level_start as _, - ), - format, - srv_handle: if usage.intersects(image::Usage::SAMPLED) { - let srv = self.view_image_as_shader_resource(&srv_info)?; - - if let Some(ref mut name) = debug_name { - set_debug_name_with_suffix(&srv, name, " -- SRV"); - } - - Some(srv.into_raw()) - } else { - None - }, - rtv_handle: if usage.contains(image::Usage::COLOR_ATTACHMENT) { - let rtv = self.view_image_as_render_target(&info)?; - - if let Some(ref mut name) = debug_name { - set_debug_name_with_suffix(&rtv, name, " -- RTV"); - } - - Some(rtv.into_raw()) - } else { - None - }, - uav_handle: if usage.contains(image::Usage::STORAGE) { - let uav = self.view_image_as_unordered_access(&info)?; - - if let Some(ref mut name) = debug_name { - set_debug_name_with_suffix(&uav, name, " -- UAV"); - } - - Some(uav.into_raw()) - } else { - None - }, - dsv_handle: if usage.contains(image::Usage::DEPTH_STENCIL_ATTACHMENT) { - if let Some(dsv) = self.view_image_as_depth_stencil(&info, None).ok() { - if let Some(ref mut name) = debug_name { - set_debug_name_with_suffix(&dsv, name, " -- DSV"); - } - - Some(dsv.into_raw()) - } else { - None - } - } else { - None - }, - rodsv_handle: if usage.contains(image::Usage::DEPTH_STENCIL_ATTACHMENT) - && self.internal.downlevel.read_only_depth_stencil - { - if let Some(rodsv) = self - .view_image_as_depth_stencil(&info, Some(image.format.is_stencil())) - .ok() - { - if let Some(ref mut name) = debug_name { - set_debug_name_with_suffix(&rodsv, name, " -- DSV"); - } - - Some(rodsv.into_raw()) - } else { - None - } - } else { - None - }, - owned: true, - }) - } - - unsafe fn create_sampler( - &self, - info: &image::SamplerDesc, - ) -> Result { - assert!(info.normalized); - - let op = match info.comparison { - Some(_) => d3d11::D3D11_FILTER_REDUCTION_TYPE_COMPARISON, - None => d3d11::D3D11_FILTER_REDUCTION_TYPE_STANDARD, - }; - - let desc = d3d11::D3D11_SAMPLER_DESC { - Filter: conv::map_filter( - info.min_filter, - info.mag_filter, - info.mip_filter, - op, - info.anisotropy_clamp, - ), - AddressU: conv::map_wrapping(info.wrap_mode.0), - AddressV: conv::map_wrapping(info.wrap_mode.1), - AddressW: conv::map_wrapping(info.wrap_mode.2), - MipLODBias: info.lod_bias.0, - MaxAnisotropy: info.anisotropy_clamp.map_or(0, |aniso| aniso as u32), - ComparisonFunc: info.comparison.map_or(0, |comp| conv::map_comparison(comp)), - BorderColor: info.border.into(), - MinLOD: info.lod_range.start.0, - MaxLOD: info.lod_range.end.0, - }; - - let mut sampler = ptr::null_mut(); - let hr = self - .raw - .CreateSamplerState(&desc, &mut sampler as *mut *mut _ as *mut *mut _); - - assert_eq!(true, winerror::SUCCEEDED(hr)); - - Ok(Sampler { - sampler_handle: ComPtr::from_raw(sampler), - }) - } - - unsafe fn create_descriptor_pool( - &self, - _max_sets: usize, - ranges: I, - _flags: pso::DescriptorPoolCreateFlags, - ) -> Result - where - I: Iterator, - { - let mut total = RegisterData::default(); - for range in ranges { - let content = DescriptorContent::from(range.ty); - total.add_content_many(content, range.count as DescriptorIndex); - } - - let max_stages = 6; - let count = total.sum() * max_stages; - Ok(DescriptorPool::with_capacity(count)) - } - - unsafe fn create_descriptor_set_layout<'a, I, J>( - &self, - layout_bindings: I, - _immutable_samplers: J, - ) -> Result - where - I: Iterator, - J: Iterator, - { - let mut total = MultiStageData::>::default(); - let mut bindings = layout_bindings.collect::>(); - - for binding in bindings.iter() { - let content = DescriptorContent::from(binding.ty); - // If this binding is used by the graphics pipeline and is a UAV, it belongs to the "Output Merger" - // stage, so we only put them in the fragment stage to save redundant descriptor allocations. - let stage_flags = if content.contains(DescriptorContent::UAV) - && binding - .stage_flags - .intersects(pso::ShaderStageFlags::ALL - pso::ShaderStageFlags::COMPUTE) - { - let mut stage_flags = pso::ShaderStageFlags::FRAGMENT; - stage_flags.set( - pso::ShaderStageFlags::COMPUTE, - binding.stage_flags.contains(pso::ShaderStageFlags::COMPUTE), - ); - stage_flags - } else { - binding.stage_flags - }; - total.add_content_many(content, stage_flags, binding.count as _); - } - - bindings.sort_by_key(|a| a.binding); - - let accum = total.map_register(|count| RegisterAccumulator { - res_index: *count as ResourceIndex, - }); - - Ok(DescriptorSetLayout { - bindings: Arc::new(bindings), - pool_mapping: accum.to_mapping(), - }) - } - - unsafe fn write_descriptor_set<'a, I>(&self, op: pso::DescriptorSetWrite<'a, Backend, I>) - where - I: Iterator>, - { - // Get baseline mapping - let mut mapping = op - .set - .layout - .pool_mapping - .map_register(|mapping| mapping.offset); - - // Iterate over layout bindings until the first binding is found. - let binding_start = op - .set - .layout - .bindings - .iter() - .position(|binding| binding.binding == op.binding) - .unwrap(); - - // If we've skipped layout bindings, we need to add them to get the correct binding offset - for binding in &op.set.layout.bindings[..binding_start] { - let content = DescriptorContent::from(binding.ty); - mapping.add_content_many(content, binding.stage_flags, binding.count as _); - } - - // We start at the given binding index and array index - let mut binding_index = binding_start; - let mut array_index = op.array_offset; - - // If we're skipping array indices in the current binding, we need to add them to get the correct binding offset - if array_index > 0 { - let binding: &pso::DescriptorSetLayoutBinding = &op.set.layout.bindings[binding_index]; - let content = DescriptorContent::from(binding.ty); - mapping.add_content_many(content, binding.stage_flags, array_index as _); - } - - // Iterate over the descriptors, figuring out the corresponding binding, and adding - // it to the set of bindings. - // - // When we hit the end of an array of descriptors and there are still descriptors left - // over, we will spill into writing the next binding. - for descriptor in op.descriptors { - let binding: &pso::DescriptorSetLayoutBinding = &op.set.layout.bindings[binding_index]; - - let handles = match descriptor { - pso::Descriptor::Buffer(buffer, ref _sub) => RegisterData { - c: match buffer.internal.disjoint_cb { - Some(dj_buf) => dj_buf as *mut _, - None => buffer.internal.raw as *mut _, - }, - t: buffer.internal.srv.map_or(ptr::null_mut(), |p| p as *mut _), - u: buffer.internal.uav.map_or(ptr::null_mut(), |p| p as *mut _), - s: ptr::null_mut(), - }, - pso::Descriptor::Image(image, _layout) => RegisterData { - c: ptr::null_mut(), - t: image.srv_handle.map_or(ptr::null_mut(), |h| h as *mut _), - u: image.uav_handle.map_or(ptr::null_mut(), |h| h as *mut _), - s: ptr::null_mut(), - }, - pso::Descriptor::Sampler(sampler) => RegisterData { - c: ptr::null_mut(), - t: ptr::null_mut(), - u: ptr::null_mut(), - s: sampler.sampler_handle.as_raw() as *mut _, - }, - pso::Descriptor::CombinedImageSampler(image, _layout, sampler) => RegisterData { - c: ptr::null_mut(), - t: image.srv_handle.map_or(ptr::null_mut(), |h| h as *mut _), - u: image.uav_handle.map_or(ptr::null_mut(), |h| h as *mut _), - s: sampler.sampler_handle.as_raw() as *mut _, - }, - pso::Descriptor::TexelBuffer(_buffer_view) => unimplemented!(), - }; - - let content = DescriptorContent::from(binding.ty); - if content.contains(DescriptorContent::CBV) { - let offsets = mapping.map_other(|map| map.c); - op.set - .assign_stages(&offsets, binding.stage_flags, handles.c); - }; - if content.contains(DescriptorContent::SRV) { - let offsets = mapping.map_other(|map| map.t); - op.set - .assign_stages(&offsets, binding.stage_flags, handles.t); - }; - if content.contains(DescriptorContent::UAV) { - // If this binding is used by the graphics pipeline and is a UAV, it belongs to the "Output Merger" - // stage, so we only put them in the fragment stage to save redundant descriptor allocations. - let stage_flags = if binding - .stage_flags - .intersects(pso::ShaderStageFlags::ALL - pso::ShaderStageFlags::COMPUTE) - { - let mut stage_flags = pso::ShaderStageFlags::FRAGMENT; - stage_flags.set( - pso::ShaderStageFlags::COMPUTE, - binding.stage_flags.contains(pso::ShaderStageFlags::COMPUTE), - ); - stage_flags - } else { - binding.stage_flags - }; - - let offsets = mapping.map_other(|map| map.u); - op.set.assign_stages(&offsets, stage_flags, handles.u); - }; - if content.contains(DescriptorContent::SAMPLER) { - let offsets = mapping.map_other(|map| map.s); - op.set - .assign_stages(&offsets, binding.stage_flags, handles.s); - }; - - mapping.add_content_many(content, binding.stage_flags, 1); - - array_index += 1; - if array_index >= binding.count { - // We've run out of array to write to, we should overflow to the next binding. - array_index = 0; - binding_index += 1; - } - } - } - - unsafe fn copy_descriptor_set<'a>(&self, _op: pso::DescriptorSetCopy<'a, Backend>) { - unimplemented!() - /* - for offset in 0 .. copy.count { - let (dst_ty, dst_handle_offset, dst_second_handle_offset) = copy - .dst_set - .get_handle_offset(copy.dst_binding + offset as u32); - let (src_ty, src_handle_offset, src_second_handle_offset) = copy - .src_set - .get_handle_offset(copy.src_binding + offset as u32); - assert_eq!(dst_ty, src_ty); - - let dst_handle = copy.dst_set.handles.offset(dst_handle_offset as isize); - let src_handle = copy.dst_set.handles.offset(src_handle_offset as isize); - - match dst_ty { - pso::DescriptorType::Image { - ty: pso::ImageDescriptorType::Sampled { with_sampler: true } - } => { - let dst_second_handle = copy - .dst_set - .handles - .offset(dst_second_handle_offset as isize); - let src_second_handle = copy - .dst_set - .handles - .offset(src_second_handle_offset as isize); - - *dst_handle = *src_handle; - *dst_second_handle = *src_second_handle; - } - _ => *dst_handle = *src_handle, - } - }*/ - } - - unsafe fn map_memory( - &self, - memory: &mut Memory, - segment: memory::Segment, - ) -> Result<*mut u8, device::MapError> { - Ok(memory.host_ptr.offset(segment.offset as isize)) - } - - unsafe fn unmap_memory(&self, _memory: &mut Memory) { - // persistent mapping FTW - } - - unsafe fn flush_mapped_memory_ranges<'a, I>(&self, ranges: I) -> Result<(), device::OutOfMemory> - where - I: Iterator, - { - let _scope = debug_scope!(&self.context, "FlushMappedRanges"); - - // go through every range we wrote to - for (memory, ref segment) in ranges { - let range = memory.resolve(segment); - let _scope = debug_scope!(&self.context, "Range({:?})", range); - memory.flush(&self.context, range); - } - - Ok(()) - } - - unsafe fn invalidate_mapped_memory_ranges<'a, I>( - &self, - ranges: I, - ) -> Result<(), device::OutOfMemory> - where - I: Iterator, - { - let _scope = debug_scope!(&self.context, "InvalidateMappedRanges"); - - // go through every range we want to read from - for (memory, ref segment) in ranges { - let range = memory.resolve(segment); - let _scope = debug_scope!(&self.context, "Range({:?})", range); - memory.invalidate( - &self.context, - range, - self.internal.working_buffer.clone(), - self.internal.working_buffer_size, - ); - } - - Ok(()) - } - - fn create_semaphore(&self) -> Result { - // TODO: - Ok(Semaphore) - } - - fn create_fence(&self, signalled: bool) -> Result { - Ok(Arc::new(RawFence { - mutex: Mutex::new(signalled), - condvar: Condvar::new(), - })) - } - - unsafe fn reset_fence(&self, fence: &mut Fence) -> Result<(), device::OutOfMemory> { - *fence.mutex.lock() = false; - Ok(()) - } - - unsafe fn wait_for_fence( - &self, - fence: &Fence, - timeout_ns: u64, - ) -> Result { - use std::time::{Duration, Instant}; - - debug!("wait_for_fence {:?} for {} ns", fence, timeout_ns); - let mut guard = fence.mutex.lock(); - match timeout_ns { - 0 => Ok(*guard), - 0xFFFFFFFFFFFFFFFF => { - while !*guard { - fence.condvar.wait(&mut guard); - } - Ok(true) - } - _ => { - let total = Duration::from_nanos(timeout_ns as u64); - let now = Instant::now(); - while !*guard { - let duration = match total.checked_sub(now.elapsed()) { - Some(dur) => dur, - None => return Ok(false), - }; - let result = fence.condvar.wait_for(&mut guard, duration); - if result.timed_out() { - return Ok(false); - } - } - Ok(true) - } - } - } - - unsafe fn get_fence_status(&self, fence: &Fence) -> Result { - Ok(*fence.mutex.lock()) - } - - fn create_event(&self) -> Result<(), device::OutOfMemory> { - unimplemented!() - } - - unsafe fn get_event_status(&self, _event: &()) -> Result { - unimplemented!() - } - - unsafe fn set_event(&self, _event: &mut ()) -> Result<(), device::OutOfMemory> { - unimplemented!() - } - - unsafe fn reset_event(&self, _event: &mut ()) -> Result<(), device::OutOfMemory> { - unimplemented!() - } - - unsafe fn free_memory(&self, mut memory: Memory) { - if !memory.host_ptr.is_null() { - let _vec = - Vec::from_raw_parts(memory.host_ptr, memory.size as usize, memory.size as usize); - // let it drop - memory.host_ptr = ptr::null_mut(); - } - for (_, (_range, mut internal)) in memory.local_buffers.write().drain() { - internal.release_resources() - } - } - - unsafe fn create_query_pool( - &self, - _query_ty: query::Type, - _count: query::Id, - ) -> Result { - unimplemented!() - } - - unsafe fn destroy_query_pool(&self, _pool: QueryPool) { - unimplemented!() - } - - unsafe fn get_query_pool_results( - &self, - _pool: &QueryPool, - _queries: Range, - _data: &mut [u8], - _stride: buffer::Stride, - _flags: query::ResultFlags, - ) -> Result { - unimplemented!() - } - - unsafe fn destroy_shader_module(&self, _shader_lib: ShaderModule) {} - - unsafe fn destroy_render_pass(&self, _rp: RenderPass) { - //unimplemented!() - } - - unsafe fn destroy_pipeline_layout(&self, _layout: PipelineLayout) { - //unimplemented!() - } - - unsafe fn destroy_graphics_pipeline(&self, _pipeline: GraphicsPipeline) {} - - unsafe fn destroy_compute_pipeline(&self, _pipeline: ComputePipeline) {} - - unsafe fn destroy_framebuffer(&self, _fb: Framebuffer) {} - - unsafe fn destroy_buffer(&self, buffer: Buffer) { - let mut internal = buffer.internal; - - if internal.raw.is_null() { - return; - } - - let arena_arc = match buffer.local_memory_arena.upgrade() { - Some(arena) => arena, - // Memory is destroyed before the buffer, we've already been destroyed. - None => return, - }; - let mut arena = arena_arc.write(); - - let memory_index = buffer.memory_index.expect("Buffer's memory index unset"); - // Drop the internal stored by the arena on the floor, it owns nothing. - let _ = arena.remove(memory_index); - - // Release all memory owned by this buffer - internal.release_resources(); - } - - unsafe fn destroy_buffer_view(&self, _view: BufferView) { - //unimplemented!() - } - - unsafe fn destroy_image(&self, mut image: Image) { - image.internal.release_resources(); - } - - unsafe fn destroy_image_view(&self, _view: ImageView) { - //unimplemented!() - } - - unsafe fn destroy_sampler(&self, _sampler: Sampler) {} - - unsafe fn destroy_descriptor_pool(&self, _pool: DescriptorPool) { - //unimplemented!() - } - - unsafe fn destroy_descriptor_set_layout(&self, _layout: DescriptorSetLayout) { - //unimplemented!() - } - - unsafe fn destroy_fence(&self, _fence: Fence) { - // unimplemented!() - } - - unsafe fn destroy_semaphore(&self, _semaphore: Semaphore) { - //unimplemented!() - } - - unsafe fn destroy_event(&self, _event: ()) { - //unimplemented!() - } - - fn wait_idle(&self) -> Result<(), device::OutOfMemory> { - Ok(()) - // unimplemented!() - } - - unsafe fn set_image_name(&self, image: &mut Image, name: &str) { - if !verify_debug_ascii(name) { - return; - } - - image.internal.debug_name = Some(name.to_string()); - } - - unsafe fn set_buffer_name(&self, buffer: &mut Buffer, name: &str) { - if !verify_debug_ascii(name) { - return; - } - - buffer.internal.debug_name = Some(name.to_string()); - } - - unsafe fn set_command_buffer_name(&self, command_buffer: &mut CommandBuffer, name: &str) { - if !verify_debug_ascii(name) { - return; - } - - command_buffer.debug_name = Some(name.to_string()); - } - - unsafe fn set_semaphore_name(&self, _semaphore: &mut Semaphore, _name: &str) { - // TODO - } - - unsafe fn set_fence_name(&self, _fence: &mut Fence, _name: &str) { - // TODO - } - - unsafe fn set_framebuffer_name(&self, _framebuffer: &mut Framebuffer, _name: &str) { - // TODO - } - - unsafe fn set_render_pass_name(&self, _render_pass: &mut RenderPass, _name: &str) { - // TODO - } - - unsafe fn set_descriptor_set_name(&self, _descriptor_set: &mut DescriptorSet, _name: &str) { - // TODO - } - - unsafe fn set_descriptor_set_layout_name( - &self, - _descriptor_set_layout: &mut DescriptorSetLayout, - _name: &str, - ) { - // TODO - } - - unsafe fn set_pipeline_layout_name(&self, _pipeline_layout: &mut PipelineLayout, _name: &str) { - // TODO - } - - unsafe fn set_display_power_state( - &self, - _display: &hal::display::Display, - _power_state: &hal::display::control::PowerState, - ) -> Result<(), hal::display::control::DisplayControlError> { - unimplemented!() - } - - unsafe fn register_device_event( - &self, - _device_event: &hal::display::control::DeviceEvent, - _fence: &mut ::Fence, - ) -> Result<(), hal::display::control::DisplayControlError> { - unimplemented!() - } - - unsafe fn register_display_event( - &self, - _display: &hal::display::Display, - _display_event: &hal::display::control::DisplayEvent, - _fence: &mut ::Fence, - ) -> Result<(), hal::display::control::DisplayControlError> { - unimplemented!() - } - - unsafe fn create_allocate_external_buffer( - &self, - _external_memory_type: hal::external_memory::ExternalBufferMemoryType, - _usage: hal::buffer::Usage, - _sparse: hal::memory::SparseFlags, - _type_mask: u32, - _size: u64, - ) -> Result<(Buffer, Memory), hal::external_memory::ExternalResourceError> { - unimplemented!() - } - - unsafe fn import_external_buffer( - &self, - _external_memory: hal::external_memory::ExternalBufferMemory, - _usage: hal::buffer::Usage, - _sparse: hal::memory::SparseFlags, - _type_mask: u32, - _size: u64, - ) -> Result<(Buffer, Memory), hal::external_memory::ExternalResourceError> { - unimplemented!() - } - - unsafe fn create_allocate_external_image( - &self, - _external_memory_type: hal::external_memory::ExternalImageMemoryType, - _kind: image::Kind, - _mip_levels: image::Level, - _format: format::Format, - _tiling: image::Tiling, - _usage: image::Usage, - _sparse: memory::SparseFlags, - _view_caps: image::ViewCapabilities, - _type_mask: u32, - ) -> Result<(Image, Memory), hal::external_memory::ExternalResourceError> { - unimplemented!() - } - - unsafe fn import_external_image( - &self, - _external_memory: hal::external_memory::ExternalImageMemory, - _kind: image::Kind, - _mip_levels: image::Level, - _format: format::Format, - _tiling: image::Tiling, - _usage: image::Usage, - _sparse: memory::SparseFlags, - _view_caps: image::ViewCapabilities, - _type_mask: u32, - ) -> Result<(Image, Memory), hal::external_memory::ExternalResourceError> { - unimplemented!() - } - - unsafe fn export_memory( - &self, - _external_memory_type: hal::external_memory::ExternalMemoryType, - _memory: &Memory, - ) -> Result - { - unimplemented!() - } - - unsafe fn drm_format_modifier(&self, _image: &Image) -> Option { - None - } - - fn start_capture(&self) { - unsafe { - self.render_doc - .start_frame_capture(self.raw.as_raw() as *mut _, ptr::null_mut()) - } - } - - fn stop_capture(&self) { - unsafe { - self.render_doc - .end_frame_capture(self.raw.as_raw() as *mut _, ptr::null_mut()) - } - } -} +use auxil::ShaderStage; +use hal::{ + adapter::MemoryProperties, buffer, device, format, image, memory, pass, pool, pso, + pso::VertexInputRate, query, queue::QueueFamilyId, window, +}; + +use winapi::{ + shared::{dxgi, dxgiformat, dxgitype, minwindef::TRUE, windef::HWND, winerror}, + um::{d3d11, d3d11_1, d3d11sdklayers, d3dcommon}, +}; + +use wio::com::ComPtr; + +use std::{ + fmt, mem, + ops::Range, + ptr, + sync::{Arc, Weak}, +}; + +use parking_lot::{Condvar, Mutex, RwLock}; + +use crate::{ + conv, + debug::{set_debug_name, set_debug_name_with_suffix, verify_debug_ascii}, + internal, shader, Backend, Buffer, BufferView, CommandBuffer, CommandPool, ComputePipeline, + DescriptorContent, DescriptorIndex, DescriptorPool, DescriptorSet, DescriptorSetInfo, + DescriptorSetLayout, Fence, Framebuffer, GraphicsPipeline, Image, ImageView, InternalBuffer, + InternalImage, Memory, MultiStageData, PipelineLayout, QueryPool, RawFence, + RegisterAccumulator, RegisterData, RenderPass, ResourceIndex, Sampler, Semaphore, ShaderModule, + SubpassDesc, ViewInfo, +}; + +//TODO: expose coherent type 0x2 when it's properly supported +const BUFFER_TYPE_MASK: u32 = 0x1 | 0x4; + +struct InputLayout { + raw: ComPtr, + required_bindings: u32, + max_vertex_bindings: u32, + topology: d3d11::D3D11_PRIMITIVE_TOPOLOGY, + vertex_strides: Vec, +} + +#[derive(Clone)] +pub struct DepthStencilState { + pub raw: ComPtr, + pub stencil_ref: pso::State, + pub read_only: bool, +} + +pub struct Device { + raw: ComPtr, + raw1: Option>, + pub(crate) context: ComPtr, + features: hal::Features, + memory_properties: MemoryProperties, + pub(crate) internal: Arc, +} + +impl fmt::Debug for Device { + fn fmt(&self, fmt: &mut fmt::Formatter) -> fmt::Result { + fmt.write_str("Device") + } +} + +impl Drop for Device { + fn drop(&mut self) { + if let Ok(debug) = self.raw.cast::() { + unsafe { + debug.ReportLiveDeviceObjects(d3d11sdklayers::D3D11_RLDO_DETAIL); + } + } + } +} + +unsafe impl Send for Device {} +unsafe impl Sync for Device {} + +impl Device { + pub fn new( + device: ComPtr, + device1: Option>, + context: ComPtr, + features: hal::Features, + downlevel: hal::DownlevelProperties, + memory_properties: MemoryProperties, + feature_level: u32, + ) -> Self { + Device { + internal: Arc::new(internal::Internal::new( + &device, + features, + feature_level, + downlevel, + )), + raw: device, + raw1: device1, + context, + features, + memory_properties, + } + } + + pub fn as_raw(&self) -> *mut d3d11::ID3D11Device { + self.raw.as_raw() + } + + fn create_rasterizer_state( + &self, + rasterizer_desc: &pso::Rasterizer, + multisampling_desc: &Option, + ) -> Result, pso::CreationError> { + let mut rasterizer = ptr::null_mut(); + let desc = conv::map_rasterizer_desc(rasterizer_desc, multisampling_desc); + + let hr = unsafe { + self.raw + .CreateRasterizerState(&desc, &mut rasterizer as *mut *mut _ as *mut *mut _) + }; + + if winerror::SUCCEEDED(hr) { + Ok(unsafe { ComPtr::from_raw(rasterizer) }) + } else { + Err(pso::CreationError::Other) + } + } + + fn create_blend_state( + &self, + blend_desc: &pso::BlendDesc, + multisampling: &Option, + ) -> Result, pso::CreationError> { + let mut blend = ptr::null_mut(); + let desc = conv::map_blend_desc(blend_desc, multisampling); + + let hr = unsafe { + self.raw + .CreateBlendState(&desc, &mut blend as *mut *mut _ as *mut *mut _) + }; + + if winerror::SUCCEEDED(hr) { + Ok(unsafe { ComPtr::from_raw(blend) }) + } else { + Err(pso::CreationError::Other) + } + } + + fn create_depth_stencil_state( + &self, + depth_desc: &pso::DepthStencilDesc, + ) -> Result { + let mut depth = ptr::null_mut(); + let (desc, stencil_ref, read_only) = conv::map_depth_stencil_desc(depth_desc); + + let hr = unsafe { + self.raw + .CreateDepthStencilState(&desc, &mut depth as *mut *mut _ as *mut *mut _) + }; + + if winerror::SUCCEEDED(hr) { + Ok(DepthStencilState { + raw: unsafe { ComPtr::from_raw(depth) }, + stencil_ref, + read_only, + }) + } else { + Err(pso::CreationError::Other) + } + } + + fn create_input_layout( + &self, + vs: ComPtr, + vertex_buffers: &[pso::VertexBufferDesc], + attributes: &[pso::AttributeDesc], + input_assembler: &pso::InputAssemblerDesc, + vertex_semantic_remapping: auxil::FastHashMap>, + ) -> Result { + let mut layout = ptr::null_mut(); + + let mut vertex_strides = Vec::new(); + let mut required_bindings = 0u32; + let mut max_vertex_bindings = 0u32; + for buffer in vertex_buffers { + required_bindings |= 1 << buffer.binding as u32; + max_vertex_bindings = max_vertex_bindings.max(1u32 + buffer.binding as u32); + + while vertex_strides.len() <= buffer.binding as usize { + vertex_strides.push(0); + } + + vertex_strides[buffer.binding as usize] = buffer.stride; + } + + // See [`shader::introspect_spirv_vertex_semantic_remapping`] for details of why this is needed. + let semantics: Vec<_> = attributes + .iter() + .map( + |attrib| match vertex_semantic_remapping.get(&attrib.location) { + Some(Some((major, minor))) => { + let name = std::borrow::Cow::Owned(format!("TEXCOORD{}_\0", major)); + let location = *minor; + (name, location) + } + _ => { + let name = std::borrow::Cow::Borrowed("TEXCOORD\0"); + let location = attrib.location; + (name, location) + } + }, + ) + .collect(); + + let input_elements = attributes + .iter() + .zip(semantics.iter()) + .filter_map(|(attrib, (semantic_name, semantic_index))| { + let buffer_desc = match vertex_buffers + .iter() + .find(|buffer_desc| buffer_desc.binding == attrib.binding) + { + Some(buffer_desc) => buffer_desc, + None => { + // TODO: + // error!("Couldn't find associated vertex buffer description {:?}", attrib.binding); + return Some(Err(pso::CreationError::Other)); + } + }; + + let (slot_class, step_rate) = match buffer_desc.rate { + VertexInputRate::Vertex => (d3d11::D3D11_INPUT_PER_VERTEX_DATA, 0), + VertexInputRate::Instance(divisor) => { + (d3d11::D3D11_INPUT_PER_INSTANCE_DATA, divisor) + } + }; + let format = attrib.element.format; + + Some(Ok(d3d11::D3D11_INPUT_ELEMENT_DESC { + SemanticName: semantic_name.as_ptr() as *const _, // Semantic name used by SPIRV-Cross + SemanticIndex: *semantic_index, + Format: match conv::map_format(format) { + Some(fm) => fm, + None => { + // TODO: + // error!("Unable to find DXGI format for {:?}", format); + return Some(Err(pso::CreationError::Other)); + } + }, + InputSlot: attrib.binding as _, + AlignedByteOffset: attrib.element.offset, + InputSlotClass: slot_class, + InstanceDataStepRate: step_rate as _, + })) + }) + .collect::, _>>()?; + + let hr = unsafe { + self.raw.CreateInputLayout( + input_elements.as_ptr(), + input_elements.len() as _, + vs.GetBufferPointer(), + vs.GetBufferSize(), + &mut layout as *mut *mut _ as *mut *mut _, + ) + }; + + if winerror::SUCCEEDED(hr) { + let topology = conv::map_topology(input_assembler); + + Ok(InputLayout { + raw: unsafe { ComPtr::from_raw(layout) }, + required_bindings, + max_vertex_bindings, + topology, + vertex_strides, + }) + } else { + error!("CreateInputLayout error 0x{:X}", hr); + Err(pso::CreationError::Other) + } + } + + fn create_vertex_shader( + &self, + blob: ComPtr, + ) -> Result, pso::CreationError> { + let mut vs = ptr::null_mut(); + + let hr = unsafe { + self.raw.CreateVertexShader( + blob.GetBufferPointer(), + blob.GetBufferSize(), + ptr::null_mut(), + &mut vs as *mut *mut _ as *mut *mut _, + ) + }; + + if winerror::SUCCEEDED(hr) { + Ok(unsafe { ComPtr::from_raw(vs) }) + } else { + Err(pso::CreationError::ShaderCreationError( + pso::ShaderStageFlags::VERTEX, + String::from("Failed to create a vertex shader"), + )) + } + } + + fn create_pixel_shader( + &self, + blob: ComPtr, + ) -> Result, pso::CreationError> { + let mut ps = ptr::null_mut(); + + let hr = unsafe { + self.raw.CreatePixelShader( + blob.GetBufferPointer(), + blob.GetBufferSize(), + ptr::null_mut(), + &mut ps as *mut *mut _ as *mut *mut _, + ) + }; + + if winerror::SUCCEEDED(hr) { + Ok(unsafe { ComPtr::from_raw(ps) }) + } else { + Err(pso::CreationError::ShaderCreationError( + pso::ShaderStageFlags::FRAGMENT, + String::from("Failed to create a pixel shader"), + )) + } + } + + fn create_geometry_shader( + &self, + blob: ComPtr, + ) -> Result, pso::CreationError> { + let mut gs = ptr::null_mut(); + + let hr = unsafe { + self.raw.CreateGeometryShader( + blob.GetBufferPointer(), + blob.GetBufferSize(), + ptr::null_mut(), + &mut gs as *mut *mut _ as *mut *mut _, + ) + }; + + if winerror::SUCCEEDED(hr) { + Ok(unsafe { ComPtr::from_raw(gs) }) + } else { + Err(pso::CreationError::ShaderCreationError( + pso::ShaderStageFlags::GEOMETRY, + String::from("Failed to create a geometry shader"), + )) + } + } + + fn create_hull_shader( + &self, + blob: ComPtr, + ) -> Result, pso::CreationError> { + let mut hs = ptr::null_mut(); + + let hr = unsafe { + self.raw.CreateHullShader( + blob.GetBufferPointer(), + blob.GetBufferSize(), + ptr::null_mut(), + &mut hs as *mut *mut _ as *mut *mut _, + ) + }; + + if winerror::SUCCEEDED(hr) { + Ok(unsafe { ComPtr::from_raw(hs) }) + } else { + Err(pso::CreationError::ShaderCreationError( + pso::ShaderStageFlags::HULL, + String::from("Failed to create a hull shader"), + )) + } + } + + fn create_domain_shader( + &self, + blob: ComPtr, + ) -> Result, pso::CreationError> { + let mut ds = ptr::null_mut(); + + let hr = unsafe { + self.raw.CreateDomainShader( + blob.GetBufferPointer(), + blob.GetBufferSize(), + ptr::null_mut(), + &mut ds as *mut *mut _ as *mut *mut _, + ) + }; + + if winerror::SUCCEEDED(hr) { + Ok(unsafe { ComPtr::from_raw(ds) }) + } else { + Err(pso::CreationError::ShaderCreationError( + pso::ShaderStageFlags::DOMAIN, + String::from("Failed to create a domain shader"), + )) + } + } + + fn create_compute_shader( + &self, + blob: ComPtr, + ) -> Result, pso::CreationError> { + let mut cs = ptr::null_mut(); + + let hr = unsafe { + self.raw.CreateComputeShader( + blob.GetBufferPointer(), + blob.GetBufferSize(), + ptr::null_mut(), + &mut cs as *mut *mut _ as *mut *mut _, + ) + }; + + if winerror::SUCCEEDED(hr) { + Ok(unsafe { ComPtr::from_raw(cs) }) + } else { + Err(pso::CreationError::ShaderCreationError( + pso::ShaderStageFlags::COMPUTE, + String::from("Failed to create a compute shader"), + )) + } + } + + // TODO: fix return type.. + fn extract_entry_point( + stage: ShaderStage, + source: &pso::EntryPoint, + layout: &PipelineLayout, + features: &hal::Features, + device_feature_level: u32, + ) -> Result>, pso::CreationError> { + // TODO: entrypoint stuff + match *source.module { + ShaderModule::Dxbc(ref _shader) => Err(pso::CreationError::ShaderCreationError( + pso::ShaderStageFlags::ALL, + String::from("DXBC modules are not supported yet"), + )), + ShaderModule::Spirv(ref raw_data) => Ok(shader::compile_spirv_entrypoint( + raw_data, + stage, + source, + layout, + features, + device_feature_level, + )?), + } + } + + fn view_image_as_shader_resource( + &self, + info: &ViewInfo, + ) -> Result, image::ViewCreationError> { + let mut desc: d3d11::D3D11_SHADER_RESOURCE_VIEW_DESC = unsafe { mem::zeroed() }; + desc.Format = info.format; + if desc.Format == dxgiformat::DXGI_FORMAT_D32_FLOAT_S8X24_UINT { + desc.Format = dxgiformat::DXGI_FORMAT_R32_FLOAT_X8X24_TYPELESS; + } + + #[allow(non_snake_case)] + let MostDetailedMip = info.levels.start as _; + #[allow(non_snake_case)] + let MipLevels = (info.levels.end - info.levels.start) as _; + #[allow(non_snake_case)] + let FirstArraySlice = info.layers.start as _; + #[allow(non_snake_case)] + let ArraySize = (info.layers.end - info.layers.start) as _; + + match info.view_kind { + image::ViewKind::D1 => { + desc.ViewDimension = d3dcommon::D3D11_SRV_DIMENSION_TEXTURE1D; + *unsafe { desc.u.Texture1D_mut() } = d3d11::D3D11_TEX1D_SRV { + MostDetailedMip, + MipLevels, + } + } + image::ViewKind::D1Array => { + desc.ViewDimension = d3dcommon::D3D11_SRV_DIMENSION_TEXTURE1DARRAY; + *unsafe { desc.u.Texture1DArray_mut() } = d3d11::D3D11_TEX1D_ARRAY_SRV { + MostDetailedMip, + MipLevels, + FirstArraySlice, + ArraySize, + } + } + image::ViewKind::D2 if info.kind.num_samples() > 1 => { + desc.ViewDimension = d3dcommon::D3D11_SRV_DIMENSION_TEXTURE2DMS; + *unsafe { desc.u.Texture2DMS_mut() } = d3d11::D3D11_TEX2DMS_SRV { + UnusedField_NothingToDefine: 0, + } + } + image::ViewKind::D2 => { + desc.ViewDimension = d3dcommon::D3D11_SRV_DIMENSION_TEXTURE2D; + *unsafe { desc.u.Texture2D_mut() } = d3d11::D3D11_TEX2D_SRV { + MostDetailedMip, + MipLevels, + } + } + image::ViewKind::D2Array if info.kind.num_samples() > 1 => { + desc.ViewDimension = d3dcommon::D3D11_SRV_DIMENSION_TEXTURE2DMSARRAY; + *unsafe { desc.u.Texture2DMSArray_mut() } = d3d11::D3D11_TEX2DMS_ARRAY_SRV { + FirstArraySlice, + ArraySize, + } + } + image::ViewKind::D2Array => { + desc.ViewDimension = d3dcommon::D3D11_SRV_DIMENSION_TEXTURE2DARRAY; + *unsafe { desc.u.Texture2DArray_mut() } = d3d11::D3D11_TEX2D_ARRAY_SRV { + MostDetailedMip, + MipLevels, + FirstArraySlice, + ArraySize, + } + } + image::ViewKind::D3 => { + desc.ViewDimension = d3dcommon::D3D11_SRV_DIMENSION_TEXTURE3D; + *unsafe { desc.u.Texture3D_mut() } = d3d11::D3D11_TEX3D_SRV { + MostDetailedMip, + MipLevels, + } + } + image::ViewKind::Cube => { + desc.ViewDimension = d3dcommon::D3D11_SRV_DIMENSION_TEXTURECUBE; + *unsafe { desc.u.TextureCube_mut() } = d3d11::D3D11_TEXCUBE_SRV { + MostDetailedMip, + MipLevels, + } + } + image::ViewKind::CubeArray => { + desc.ViewDimension = d3dcommon::D3D11_SRV_DIMENSION_TEXTURECUBEARRAY; + *unsafe { desc.u.TextureCubeArray_mut() } = d3d11::D3D11_TEXCUBE_ARRAY_SRV { + MostDetailedMip, + MipLevels, + First2DArrayFace: FirstArraySlice, + NumCubes: ArraySize / 6, + } + } + } + + let mut srv = ptr::null_mut(); + let hr = unsafe { + self.raw.CreateShaderResourceView( + info.resource, + &desc, + &mut srv as *mut *mut _ as *mut *mut _, + ) + }; + + if winerror::SUCCEEDED(hr) { + Ok(unsafe { ComPtr::from_raw(srv) }) + } else { + Err(image::ViewCreationError::Unsupported) + } + } + + fn view_image_as_unordered_access( + &self, + info: &ViewInfo, + ) -> Result, image::ViewCreationError> { + let mut desc: d3d11::D3D11_UNORDERED_ACCESS_VIEW_DESC = unsafe { mem::zeroed() }; + desc.Format = info.format; + + #[allow(non_snake_case)] + let MipSlice = info.levels.start as _; + #[allow(non_snake_case)] + let FirstArraySlice = info.layers.start as _; + #[allow(non_snake_case)] + let ArraySize = (info.layers.end - info.layers.start) as _; + + match info.view_kind { + image::ViewKind::D1 => { + desc.ViewDimension = d3d11::D3D11_UAV_DIMENSION_TEXTURE1D; + *unsafe { desc.u.Texture1D_mut() } = d3d11::D3D11_TEX1D_UAV { + MipSlice: info.levels.start as _, + } + } + image::ViewKind::D1Array => { + desc.ViewDimension = d3d11::D3D11_UAV_DIMENSION_TEXTURE1DARRAY; + *unsafe { desc.u.Texture1DArray_mut() } = d3d11::D3D11_TEX1D_ARRAY_UAV { + MipSlice, + FirstArraySlice, + ArraySize, + } + } + image::ViewKind::D2 => { + desc.ViewDimension = d3d11::D3D11_UAV_DIMENSION_TEXTURE2D; + *unsafe { desc.u.Texture2D_mut() } = d3d11::D3D11_TEX2D_UAV { + MipSlice: info.levels.start as _, + } + } + image::ViewKind::D2Array => { + desc.ViewDimension = d3d11::D3D11_UAV_DIMENSION_TEXTURE2DARRAY; + *unsafe { desc.u.Texture2DArray_mut() } = d3d11::D3D11_TEX2D_ARRAY_UAV { + MipSlice, + FirstArraySlice, + ArraySize, + } + } + image::ViewKind::D3 => { + desc.ViewDimension = d3d11::D3D11_UAV_DIMENSION_TEXTURE3D; + *unsafe { desc.u.Texture3D_mut() } = d3d11::D3D11_TEX3D_UAV { + MipSlice, + FirstWSlice: FirstArraySlice, + WSize: ArraySize, + } + } + _ => unimplemented!(), + } + + let mut uav = ptr::null_mut(); + let hr = unsafe { + self.raw.CreateUnorderedAccessView( + info.resource, + &desc, + &mut uav as *mut *mut _ as *mut *mut _, + ) + }; + + if winerror::SUCCEEDED(hr) { + Ok(unsafe { ComPtr::from_raw(uav) }) + } else { + error!("CreateUnorderedAccessView failed: 0x{:x}", hr); + + Err(image::ViewCreationError::Unsupported) + } + } + + pub(crate) fn view_image_as_render_target( + &self, + info: &ViewInfo, + ) -> Result, image::ViewCreationError> { + let mut desc: d3d11::D3D11_RENDER_TARGET_VIEW_DESC = unsafe { mem::zeroed() }; + desc.Format = info.format; + + #[allow(non_snake_case)] + let MipSlice = info.levels.start as _; + #[allow(non_snake_case)] + let FirstArraySlice = info.layers.start as _; + #[allow(non_snake_case)] + let ArraySize = (info.layers.end - info.layers.start) as _; + + match info.view_kind { + image::ViewKind::D1 => { + desc.ViewDimension = d3d11::D3D11_RTV_DIMENSION_TEXTURE1D; + *unsafe { desc.u.Texture1D_mut() } = d3d11::D3D11_TEX1D_RTV { MipSlice } + } + image::ViewKind::D1Array => { + desc.ViewDimension = d3d11::D3D11_RTV_DIMENSION_TEXTURE1DARRAY; + *unsafe { desc.u.Texture1DArray_mut() } = d3d11::D3D11_TEX1D_ARRAY_RTV { + MipSlice, + FirstArraySlice, + ArraySize, + } + } + image::ViewKind::D2 => { + if info.kind.num_samples() > 1 { + desc.ViewDimension = d3d11::D3D11_RTV_DIMENSION_TEXTURE2DMS; + *unsafe { desc.u.Texture2DMS_mut() } = d3d11::D3D11_TEX2DMS_RTV { + UnusedField_NothingToDefine: 0, + } + } else { + desc.ViewDimension = d3d11::D3D11_RTV_DIMENSION_TEXTURE2D; + *unsafe { desc.u.Texture2D_mut() } = d3d11::D3D11_TEX2D_RTV { MipSlice } + } + } + image::ViewKind::D2Array => { + if info.kind.num_samples() > 1 { + desc.ViewDimension = d3d11::D3D11_RTV_DIMENSION_TEXTURE2DMSARRAY; + *unsafe { desc.u.Texture2DMSArray_mut() } = d3d11::D3D11_TEX2DMS_ARRAY_RTV { + FirstArraySlice, + ArraySize, + } + } else { + desc.ViewDimension = d3d11::D3D11_RTV_DIMENSION_TEXTURE2DARRAY; + *unsafe { desc.u.Texture2DArray_mut() } = d3d11::D3D11_TEX2D_ARRAY_RTV { + MipSlice, + FirstArraySlice, + ArraySize, + } + } + } + image::ViewKind::D3 => { + desc.ViewDimension = d3d11::D3D11_RTV_DIMENSION_TEXTURE3D; + *unsafe { desc.u.Texture3D_mut() } = d3d11::D3D11_TEX3D_RTV { + MipSlice, + FirstWSlice: FirstArraySlice, + WSize: ArraySize, + } + } + _ => unimplemented!(), + } + + let mut rtv = ptr::null_mut(); + let hr = unsafe { + self.raw.CreateRenderTargetView( + info.resource, + &desc, + &mut rtv as *mut *mut _ as *mut *mut _, + ) + }; + + if winerror::SUCCEEDED(hr) { + Ok(unsafe { ComPtr::from_raw(rtv) }) + } else { + error!("CreateRenderTargetView failed: 0x{:x}", hr); + + Err(image::ViewCreationError::Unsupported) + } + } + + fn view_image_as_depth_stencil( + &self, + info: &ViewInfo, + read_only_stencil: Option, + ) -> Result, image::ViewCreationError> { + #![allow(non_snake_case)] + + let MipSlice = info.levels.start as _; + let FirstArraySlice = info.layers.start as _; + let ArraySize = (info.layers.end - info.layers.start) as _; + assert_eq!(info.levels.start + 1, info.levels.end); + assert!(info.layers.end <= info.kind.num_layers()); + + let mut desc: d3d11::D3D11_DEPTH_STENCIL_VIEW_DESC = unsafe { mem::zeroed() }; + desc.Format = info.format; + + if let Some(stencil) = read_only_stencil { + desc.Flags = match stencil { + true => d3d11::D3D11_DSV_READ_ONLY_DEPTH | d3d11::D3D11_DSV_READ_ONLY_STENCIL, + false => d3d11::D3D11_DSV_READ_ONLY_DEPTH, + } + } + + match info.view_kind { + image::ViewKind::D2 => { + if info.kind.num_samples() > 1 { + desc.ViewDimension = d3d11::D3D11_DSV_DIMENSION_TEXTURE2DMS; + *unsafe { desc.u.Texture2DMS_mut() } = d3d11::D3D11_TEX2DMS_DSV { + UnusedField_NothingToDefine: 0, + } + } else { + desc.ViewDimension = d3d11::D3D11_DSV_DIMENSION_TEXTURE2D; + *unsafe { desc.u.Texture2D_mut() } = d3d11::D3D11_TEX2D_DSV { MipSlice } + } + } + image::ViewKind::D2Array => { + if info.kind.num_samples() > 1 { + desc.ViewDimension = d3d11::D3D11_DSV_DIMENSION_TEXTURE2DMSARRAY; + *unsafe { desc.u.Texture2DMSArray_mut() } = d3d11::D3D11_TEX2DMS_ARRAY_DSV { + FirstArraySlice, + ArraySize, + } + } else { + desc.ViewDimension = d3d11::D3D11_DSV_DIMENSION_TEXTURE2DARRAY; + *unsafe { desc.u.Texture2DArray_mut() } = d3d11::D3D11_TEX2D_ARRAY_DSV { + MipSlice, + FirstArraySlice, + ArraySize, + } + } + } + image::ViewKind::D1 + | image::ViewKind::D1Array + | image::ViewKind::D3 + | image::ViewKind::Cube + | image::ViewKind::CubeArray => { + warn!( + "3D and cube views are not supported for the image, kind: {:?}", + info.kind + ); + return Err(image::ViewCreationError::BadKind(info.view_kind)); + } + } + + let mut dsv = ptr::null_mut(); + let hr = unsafe { + self.raw.CreateDepthStencilView( + info.resource, + &desc, + &mut dsv as *mut *mut _ as *mut *mut _, + ) + }; + + if winerror::SUCCEEDED(hr) { + Ok(unsafe { ComPtr::from_raw(dsv) }) + } else { + error!("CreateDepthStencilView failed: 0x{:x}", hr); + + Err(image::ViewCreationError::Unsupported) + } + } + + pub(crate) fn create_swapchain_impl( + &self, + config: &window::SwapchainConfig, + window_handle: HWND, + factory: ComPtr, + ) -> Result<(ComPtr, dxgiformat::DXGI_FORMAT), window::SwapchainError> + { + // TODO: use IDXGIFactory2 for >=11.1 + // TODO: this function should be able to fail (Result)? + + debug!("{:#?}", config); + let non_srgb_format = conv::map_format_nosrgb(config.format).unwrap(); + + let mut desc = dxgi::DXGI_SWAP_CHAIN_DESC { + BufferDesc: dxgitype::DXGI_MODE_DESC { + Width: config.extent.width, + Height: config.extent.height, + // TODO: should this grab max value of all monitor hz? vsync + // will clamp to current monitor anyways? + RefreshRate: dxgitype::DXGI_RATIONAL { + Numerator: 1, + Denominator: 60, + }, + Format: non_srgb_format, + ScanlineOrdering: dxgitype::DXGI_MODE_SCANLINE_ORDER_UNSPECIFIED, + Scaling: dxgitype::DXGI_MODE_SCALING_UNSPECIFIED, + }, + SampleDesc: dxgitype::DXGI_SAMPLE_DESC { + Count: 1, + Quality: 0, + }, + BufferUsage: dxgitype::DXGI_USAGE_RENDER_TARGET_OUTPUT, + BufferCount: config.image_count, + OutputWindow: window_handle, + // TODO: + Windowed: TRUE, + // TODO: + SwapEffect: dxgi::DXGI_SWAP_EFFECT_DISCARD, + Flags: 0, + }; + + let dxgi_swapchain = { + let mut swapchain: *mut dxgi::IDXGISwapChain = ptr::null_mut(); + let hr = unsafe { + factory.CreateSwapChain( + self.raw.as_raw() as *mut _, + &mut desc as *mut _, + &mut swapchain as *mut *mut _ as *mut *mut _, + ) + }; + assert_eq!(hr, winerror::S_OK); + + unsafe { ComPtr::from_raw(swapchain) } + }; + Ok((dxgi_swapchain, non_srgb_format)) + } +} + +impl device::Device for Device { + unsafe fn allocate_memory( + &self, + mem_type: hal::MemoryTypeId, + size: u64, + ) -> Result { + let properties = self.memory_properties.memory_types[mem_type.0].properties; + let host_ptr = if properties.contains(hal::memory::Properties::CPU_VISIBLE) { + let mut data = vec![0u8; size as usize]; + let ptr = data.as_mut_ptr(); + mem::forget(data); + ptr + } else { + ptr::null_mut() + }; + Ok(Memory { + properties, + size, + host_ptr, + local_buffers: Arc::new(RwLock::new(thunderdome::Arena::new())), + }) + } + + unsafe fn create_command_pool( + &self, + _family: QueueFamilyId, + _create_flags: pool::CommandPoolCreateFlags, + ) -> Result { + // TODO: + Ok(CommandPool { + device: self.raw.clone(), + device1: self.raw1.clone(), + internal: Arc::clone(&self.internal), + }) + } + + unsafe fn destroy_command_pool(&self, _pool: CommandPool) { + // automatic + } + + unsafe fn create_render_pass<'a, Ia, Is, Id>( + &self, + attachments: Ia, + subpasses: Is, + _dependencies: Id, + ) -> Result + where + Ia: Iterator, + Is: Iterator>, + { + Ok(RenderPass { + attachments: attachments.collect(), + subpasses: subpasses + .map(|desc| SubpassDesc { + color_attachments: desc.colors.to_vec(), + depth_stencil_attachment: desc.depth_stencil.cloned(), + input_attachments: desc.inputs.to_vec(), + resolve_attachments: desc.resolves.to_vec(), + }) + .collect(), + }) + } + + unsafe fn create_pipeline_layout<'a, Is, Ic>( + &self, + set_layouts: Is, + _push_constant_ranges: Ic, + ) -> Result + where + Is: Iterator, + Ic: Iterator)>, + { + let mut res_offsets = MultiStageData::>::default(); + let mut sets = Vec::new(); + for set_layout in set_layouts { + sets.push(DescriptorSetInfo { + bindings: Arc::clone(&set_layout.bindings), + registers: res_offsets.advance(&set_layout.pool_mapping), + }); + } + + res_offsets.map_other(|data| { + // These use <= because this tells us the _next_ register, so maximum usage will be equal to the limit. + // + // Leave one slot for push constants + assert!( + data.c.res_index as u32 + <= d3d11::D3D11_COMMONSHADER_CONSTANT_BUFFER_API_SLOT_COUNT - 1, + "{} bound constant buffers exceeds limit of {}", + data.c.res_index as u32, + d3d11::D3D11_COMMONSHADER_CONSTANT_BUFFER_API_SLOT_COUNT - 1, + ); + assert!( + data.s.res_index as u32 <= d3d11::D3D11_COMMONSHADER_SAMPLER_REGISTER_COUNT, + "{} bound samplers exceeds limit of {}", + data.s.res_index as u32, + d3d11::D3D11_COMMONSHADER_SAMPLER_REGISTER_COUNT, + ); + assert!( + data.t.res_index as u32 <= d3d11::D3D11_COMMONSHADER_INPUT_RESOURCE_REGISTER_COUNT, + "{} bound sampled textures and read-only buffers exceeds limit of {}", + data.t.res_index as u32, + d3d11::D3D11_COMMONSHADER_INPUT_RESOURCE_REGISTER_COUNT, + ); + assert!( + data.u.res_index as u32 <= d3d11::D3D11_PS_CS_UAV_REGISTER_COUNT, + "{} bound storage textures and read-write buffers exceeds limit of {}", + data.u.res_index as u32, + d3d11::D3D11_PS_CS_UAV_REGISTER_COUNT, + ); + }); + + Ok(PipelineLayout { sets }) + } + + unsafe fn create_pipeline_cache( + &self, + _data: Option<&[u8]>, + ) -> Result<(), device::OutOfMemory> { + Ok(()) + } + + unsafe fn get_pipeline_cache_data(&self, _cache: &()) -> Result, device::OutOfMemory> { + //empty + Ok(Vec::new()) + } + + unsafe fn destroy_pipeline_cache(&self, _: ()) { + //empty + } + + unsafe fn merge_pipeline_caches<'a, I>( + &self, + _: &mut (), + _: I, + ) -> Result<(), device::OutOfMemory> + where + I: Iterator, + { + //empty + Ok(()) + } + + unsafe fn create_graphics_pipeline<'a>( + &self, + desc: &pso::GraphicsPipelineDesc<'a, Backend>, + _cache: Option<&()>, + ) -> Result { + let features = &self.features; + let build_shader = + |stage: ShaderStage, source: Option<&pso::EntryPoint<'a, Backend>>| match source { + Some(src) => Self::extract_entry_point( + stage, + src, + desc.layout, + features, + self.internal.device_feature_level, + ), + None => Ok(None), + }; + + let (layout, vs, gs, hs, ds) = match desc.primitive_assembler { + pso::PrimitiveAssemblerDesc::Vertex { + buffers, + attributes, + ref input_assembler, + ref vertex, + ref tessellation, + ref geometry, + } => { + let vertex_semantic_remapping = match vertex.module { + ShaderModule::Spirv(spirv) => { + shader::introspect_spirv_vertex_semantic_remapping(spirv)? + } + _ => unimplemented!(), + }; + + let vs = build_shader(ShaderStage::Vertex, Some(&vertex))?.unwrap(); + let gs = build_shader(ShaderStage::Geometry, geometry.as_ref())?; + + let layout = self.create_input_layout( + vs.clone(), + buffers, + attributes, + input_assembler, + vertex_semantic_remapping, + )?; + + let vs = self.create_vertex_shader(vs)?; + let gs = if let Some(blob) = gs { + Some(self.create_geometry_shader(blob)?) + } else { + None + }; + let (hs, ds) = if let Some(ts) = tessellation { + let hs = build_shader(ShaderStage::Hull, Some(&ts.0))?.unwrap(); + let ds = build_shader(ShaderStage::Domain, Some(&ts.1))?.unwrap(); + + ( + Some(self.create_hull_shader(hs)?), + Some(self.create_domain_shader(ds)?), + ) + } else { + (None, None) + }; + + (layout, vs, gs, hs, ds) + } + pso::PrimitiveAssemblerDesc::Mesh { .. } => { + return Err(pso::CreationError::UnsupportedPipeline) + } + }; + + let ps = build_shader(ShaderStage::Fragment, desc.fragment.as_ref())?; + let ps = if let Some(blob) = ps { + Some(self.create_pixel_shader(blob)?) + } else { + None + }; + + let rasterizer_state = + self.create_rasterizer_state(&desc.rasterizer, &desc.multisampling)?; + let blend_state = self.create_blend_state(&desc.blender, &desc.multisampling)?; + let depth_stencil_state = Some(self.create_depth_stencil_state(&desc.depth_stencil)?); + + match desc.label { + Some(label) if verify_debug_ascii(label) => { + let mut name = label.to_string(); + + set_debug_name_with_suffix(&blend_state, &mut name, " -- Blend State"); + set_debug_name_with_suffix(&rasterizer_state, &mut name, " -- Rasterizer State"); + set_debug_name_with_suffix(&layout.raw, &mut name, " -- Input Layout"); + if let Some(ref dss) = depth_stencil_state { + set_debug_name_with_suffix(&dss.raw, &mut name, " -- Depth Stencil State"); + } + } + _ => {} + } + + Ok(GraphicsPipeline { + vs, + gs, + ds, + hs, + ps, + topology: layout.topology, + input_layout: layout.raw, + rasterizer_state, + blend_state, + depth_stencil_state, + baked_states: desc.baked_states.clone(), + required_bindings: layout.required_bindings, + max_vertex_bindings: layout.max_vertex_bindings, + strides: layout.vertex_strides, + }) + } + + unsafe fn create_compute_pipeline<'a>( + &self, + desc: &pso::ComputePipelineDesc<'a, Backend>, + _cache: Option<&()>, + ) -> Result { + let features = &self.features; + let build_shader = + |stage: ShaderStage, source: Option<&pso::EntryPoint<'a, Backend>>| match source { + Some(src) => Self::extract_entry_point( + stage, + src, + desc.layout, + features, + self.internal.device_feature_level, + ), + None => Ok(None), + }; + + let cs = build_shader(ShaderStage::Compute, Some(&desc.shader))?.unwrap(); + let cs = self.create_compute_shader(cs)?; + + Ok(ComputePipeline { cs }) + } + + unsafe fn create_framebuffer( + &self, + _renderpass: &RenderPass, + _attachments: I, + extent: image::Extent, + ) -> Result { + Ok(Framebuffer { + layers: extent.depth as _, + }) + } + + unsafe fn create_shader_module( + &self, + raw_data: &[u32], + ) -> Result { + Ok(ShaderModule::Spirv(raw_data.into())) + } + + unsafe fn create_buffer( + &self, + size: u64, + usage: buffer::Usage, + _sparse: memory::SparseFlags, + ) -> Result { + use buffer::Usage; + + let mut bind = 0; + + if usage.contains(Usage::UNIFORM) { + bind |= d3d11::D3D11_BIND_CONSTANT_BUFFER; + } + if usage.contains(Usage::VERTEX) { + bind |= d3d11::D3D11_BIND_VERTEX_BUFFER; + } + if usage.contains(Usage::INDEX) { + bind |= d3d11::D3D11_BIND_INDEX_BUFFER; + } + + // TODO: >=11.1 + if usage.intersects( + Usage::UNIFORM_TEXEL | Usage::STORAGE_TEXEL | Usage::TRANSFER_SRC | Usage::STORAGE, + ) { + bind |= d3d11::D3D11_BIND_SHADER_RESOURCE; + } + + if usage.intersects(Usage::TRANSFER_DST | Usage::STORAGE) { + bind |= d3d11::D3D11_BIND_UNORDERED_ACCESS; + } + + // if `D3D11_BIND_CONSTANT_BUFFER` intersects with any other bind flag, we need to handle + // it by creating two buffers. one with `D3D11_BIND_CONSTANT_BUFFER` and one with the rest + let needs_disjoint_cb = bind & d3d11::D3D11_BIND_CONSTANT_BUFFER != 0 + && bind != d3d11::D3D11_BIND_CONSTANT_BUFFER; + + if needs_disjoint_cb { + bind ^= d3d11::D3D11_BIND_CONSTANT_BUFFER; + } + + fn up_align(x: u64, alignment: u64) -> u64 { + (x + alignment - 1) & !(alignment - 1) + } + + // constant buffer size need to be divisible by 16 + let size = if usage.contains(Usage::UNIFORM) { + up_align(size, 16) + } else { + up_align(size, 4) + }; + + Ok(Buffer { + internal: InternalBuffer { + raw: ptr::null_mut(), + disjoint_cb: if needs_disjoint_cb { + Some(ptr::null_mut()) + } else { + None + }, + srv: None, + uav: None, + usage, + debug_name: None, + }, + bound_range: 0..0, + local_memory_arena: Weak::new(), + memory_index: None, + is_coherent: false, + memory_ptr: ptr::null_mut(), + bind, + requirements: memory::Requirements { + size, + alignment: 4, + type_mask: BUFFER_TYPE_MASK, + }, + }) + } + + unsafe fn get_buffer_requirements(&self, buffer: &Buffer) -> memory::Requirements { + buffer.requirements + } + + unsafe fn bind_buffer_memory( + &self, + memory: &Memory, + offset: u64, + buffer: &mut Buffer, + ) -> Result<(), device::BindError> { + debug!( + "usage={:?}, props={:b}", + buffer.internal.usage, memory.properties + ); + + #[allow(non_snake_case)] + let mut MiscFlags = if buffer.bind + & (d3d11::D3D11_BIND_SHADER_RESOURCE | d3d11::D3D11_BIND_UNORDERED_ACCESS) + != 0 + { + d3d11::D3D11_RESOURCE_MISC_BUFFER_ALLOW_RAW_VIEWS + } else { + 0 + }; + + if buffer.internal.usage.contains(buffer::Usage::INDIRECT) { + MiscFlags |= d3d11::D3D11_RESOURCE_MISC_DRAWINDIRECT_ARGS; + } + + let initial_data = if memory.host_ptr.is_null() { + None + } else { + Some(d3d11::D3D11_SUBRESOURCE_DATA { + pSysMem: memory.host_ptr.offset(offset as isize) as *const _, + SysMemPitch: 0, + SysMemSlicePitch: 0, + }) + }; + + //TODO: check `memory.properties.contains(memory::Properties::DEVICE_LOCAL)` ? + let raw = { + // device local memory + let desc = d3d11::D3D11_BUFFER_DESC { + ByteWidth: buffer.requirements.size as _, + Usage: d3d11::D3D11_USAGE_DEFAULT, + BindFlags: buffer.bind, + CPUAccessFlags: 0, + MiscFlags, + StructureByteStride: if buffer.internal.usage.contains(buffer::Usage::TRANSFER_SRC) + { + 4 + } else { + 0 + }, + }; + + let mut raw: *mut d3d11::ID3D11Buffer = ptr::null_mut(); + let hr = self.raw.CreateBuffer( + &desc, + initial_data.as_ref().map_or(ptr::null_mut(), |id| id), + &mut raw as *mut *mut _ as *mut *mut _, + ); + + if !winerror::SUCCEEDED(hr) { + return Err(device::BindError::WrongMemory); + } + + if let Some(ref mut name) = buffer.internal.debug_name { + set_debug_name(&*raw, name); + } + + ComPtr::from_raw(raw) + }; + + let disjoint_cb = if buffer.internal.disjoint_cb.is_some() { + let desc = d3d11::D3D11_BUFFER_DESC { + ByteWidth: buffer.requirements.size as _, + Usage: d3d11::D3D11_USAGE_DEFAULT, + BindFlags: d3d11::D3D11_BIND_CONSTANT_BUFFER, + CPUAccessFlags: 0, + MiscFlags: 0, + StructureByteStride: 0, + }; + + let mut disjoint_raw: *mut d3d11::ID3D11Buffer = ptr::null_mut(); + let hr = self.raw.CreateBuffer( + &desc, + initial_data.as_ref().map_or(ptr::null_mut(), |id| id), + &mut disjoint_raw as *mut *mut _ as *mut *mut _, + ); + + if !winerror::SUCCEEDED(hr) { + return Err(device::BindError::WrongMemory); + } + + if let Some(ref mut name) = buffer.internal.debug_name { + set_debug_name_with_suffix(&*disjoint_raw, name, " -- Constant Buffer"); + } + + Some(disjoint_raw) + } else { + None + }; + + let srv = if buffer.bind & d3d11::D3D11_BIND_SHADER_RESOURCE != 0 { + let mut desc = mem::zeroed::(); + desc.Format = dxgiformat::DXGI_FORMAT_R32_TYPELESS; + desc.ViewDimension = d3dcommon::D3D11_SRV_DIMENSION_BUFFEREX; + *desc.u.BufferEx_mut() = d3d11::D3D11_BUFFEREX_SRV { + FirstElement: 0, + NumElements: buffer.requirements.size as u32 / 4, + Flags: d3d11::D3D11_BUFFEREX_SRV_FLAG_RAW, + }; + + let mut srv: *mut d3d11::ID3D11ShaderResourceView = ptr::null_mut(); + let hr = self.raw.CreateShaderResourceView( + raw.as_raw() as *mut _, + &desc, + &mut srv as *mut *mut _ as *mut *mut _, + ); + + if !winerror::SUCCEEDED(hr) { + error!("CreateShaderResourceView failed: 0x{:x}", hr); + + return Err(device::BindError::WrongMemory); + } + + if let Some(ref mut name) = buffer.internal.debug_name { + set_debug_name_with_suffix(&*srv, name, " -- SRV"); + } + + Some(srv) + } else { + None + }; + + let uav = if buffer.bind & d3d11::D3D11_BIND_UNORDERED_ACCESS != 0 { + let mut desc = mem::zeroed::(); + desc.Format = dxgiformat::DXGI_FORMAT_R32_TYPELESS; + desc.ViewDimension = d3d11::D3D11_UAV_DIMENSION_BUFFER; + *desc.u.Buffer_mut() = d3d11::D3D11_BUFFER_UAV { + FirstElement: 0, + NumElements: buffer.requirements.size as u32 / 4, + Flags: d3d11::D3D11_BUFFER_UAV_FLAG_RAW, + }; + + let mut uav: *mut d3d11::ID3D11UnorderedAccessView = ptr::null_mut(); + let hr = self.raw.CreateUnorderedAccessView( + raw.as_raw() as *mut _, + &desc, + &mut uav as *mut *mut _ as *mut *mut _, + ); + + if !winerror::SUCCEEDED(hr) { + error!("CreateUnorderedAccessView failed: 0x{:x}", hr); + + return Err(device::BindError::WrongMemory); + } + + if let Some(ref mut name) = buffer.internal.debug_name { + set_debug_name_with_suffix(&*uav, name, " -- UAV"); + } + + Some(uav) + } else { + None + }; + + let internal = InternalBuffer { + raw: raw.into_raw(), + disjoint_cb, + srv, + uav, + usage: buffer.internal.usage, + debug_name: buffer.internal.debug_name.take(), + }; + let range = offset..offset + buffer.requirements.size; + + let memory_index = memory.bind_buffer(range.clone(), internal.clone()); + + buffer.internal = internal; + buffer.is_coherent = memory + .properties + .contains(hal::memory::Properties::COHERENT); + buffer.memory_ptr = memory.host_ptr; + buffer.bound_range = range; + buffer.local_memory_arena = Arc::downgrade(&memory.local_buffers); + buffer.memory_index = Some(memory_index); + + Ok(()) + } + + unsafe fn create_buffer_view( + &self, + _buffer: &Buffer, + _format: Option, + _range: buffer::SubRange, + ) -> Result { + unimplemented!() + } + + unsafe fn create_image( + &self, + kind: image::Kind, + mip_levels: image::Level, + format: format::Format, + _tiling: image::Tiling, + usage: image::Usage, + _sparse: memory::SparseFlags, + view_caps: image::ViewCapabilities, + ) -> Result { + let surface_desc = format.base_format().0.desc(); + let bytes_per_texel = surface_desc.bits / 8; + let ext = kind.extent(); + let size = (ext.width * ext.height * ext.depth) as u64 * bytes_per_texel as u64; + + let bind = conv::map_image_usage(usage, surface_desc, self.internal.device_feature_level); + debug!("{:b}", bind); + + Ok(Image { + internal: InternalImage { + raw: ptr::null_mut(), + copy_srv: None, + srv: None, + unordered_access_views: Vec::new(), + depth_stencil_views: Vec::new(), + render_target_views: Vec::new(), + debug_name: None, + }, + decomposed_format: conv::DecomposedDxgiFormat::UNKNOWN, + kind, + mip_levels, + format, + usage, + view_caps, + bind, + requirements: memory::Requirements { + size: size, + alignment: 4, + type_mask: 0x1, // device-local only + }, + }) + } + + unsafe fn get_image_requirements(&self, image: &Image) -> memory::Requirements { + image.requirements + } + + unsafe fn get_image_subresource_footprint( + &self, + _image: &Image, + _sub: image::Subresource, + ) -> image::SubresourceFootprint { + unimplemented!() + } + + unsafe fn bind_image_memory( + &self, + memory: &Memory, + _offset: u64, + image: &mut Image, + ) -> Result<(), device::BindError> { + use image::Usage; + use memory::Properties; + + let base_format = image.format.base_format(); + let format_desc = base_format.0.desc(); + + let compressed = format_desc.is_compressed(); + let depth = image.format.is_depth(); + let stencil = image.format.is_stencil(); + + let (bind, usage, cpu) = if memory.properties == Properties::DEVICE_LOCAL { + (image.bind, d3d11::D3D11_USAGE_DEFAULT, 0) + } else if memory.properties + == (Properties::DEVICE_LOCAL | Properties::CPU_VISIBLE | Properties::CPU_CACHED) + { + ( + image.bind, + d3d11::D3D11_USAGE_DYNAMIC, + d3d11::D3D11_CPU_ACCESS_WRITE, + ) + } else if memory.properties == (Properties::CPU_VISIBLE | Properties::CPU_CACHED) { + ( + 0, + d3d11::D3D11_USAGE_STAGING, + d3d11::D3D11_CPU_ACCESS_READ | d3d11::D3D11_CPU_ACCESS_WRITE, + ) + } else { + unimplemented!() + }; + + let dxgi_format = conv::map_format(image.format).unwrap(); + let decomposed = conv::DecomposedDxgiFormat::from_dxgi_format(dxgi_format); + assert!( + memory.host_ptr.is_null(), + "Images can only be allocated from device-local memory" + ); + let initial_data_ptr = ptr::null_mut(); + + let mut resource = ptr::null_mut(); + let view_kind = match image.kind { + image::Kind::D1(width, layers) => { + let desc = d3d11::D3D11_TEXTURE1D_DESC { + Width: width, + MipLevels: image.mip_levels as _, + ArraySize: layers as _, + Format: decomposed.typeless, + Usage: usage, + BindFlags: bind, + CPUAccessFlags: cpu, + MiscFlags: 0, + }; + + let hr = self.raw.CreateTexture1D( + &desc, + initial_data_ptr, + &mut resource as *mut *mut _ as *mut *mut _, + ); + + if !winerror::SUCCEEDED(hr) { + error!("CreateTexture1D failed: 0x{:x}", hr); + + return Err(device::BindError::WrongMemory); + } + + image::ViewKind::D1Array + } + image::Kind::D2(width, height, layers, samples) => { + let desc = d3d11::D3D11_TEXTURE2D_DESC { + Width: width, + Height: height, + MipLevels: image.mip_levels as _, + ArraySize: layers as _, + Format: decomposed.typeless, + SampleDesc: dxgitype::DXGI_SAMPLE_DESC { + Count: samples as _, + Quality: 0, + }, + Usage: usage, + BindFlags: bind, + CPUAccessFlags: cpu, + MiscFlags: { + let mut flags = 0; + if image.view_caps.contains(image::ViewCapabilities::KIND_CUBE) { + flags |= d3d11::D3D11_RESOURCE_MISC_TEXTURECUBE; + } + flags + }, + }; + + let hr = self.raw.CreateTexture2D( + &desc, + initial_data_ptr, + &mut resource as *mut *mut _ as *mut *mut _, + ); + + if !winerror::SUCCEEDED(hr) { + error!("CreateTexture2D failed: 0x{:x}", hr); + + return Err(device::BindError::WrongMemory); + } + + image::ViewKind::D2Array + } + image::Kind::D3(width, height, depth) => { + let desc = d3d11::D3D11_TEXTURE3D_DESC { + Width: width, + Height: height, + Depth: depth, + MipLevels: image.mip_levels as _, + Format: decomposed.typeless, + Usage: usage, + BindFlags: bind, + CPUAccessFlags: cpu, + MiscFlags: 0, + }; + + let hr = self.raw.CreateTexture3D( + &desc, + initial_data_ptr, + &mut resource as *mut *mut _ as *mut *mut _, + ); + + if !winerror::SUCCEEDED(hr) { + error!("CreateTexture3D failed: 0x{:x}", hr); + + return Err(device::BindError::WrongMemory); + } + + image::ViewKind::D3 + } + }; + + let mut unordered_access_views = Vec::new(); + + if image.usage.contains(Usage::TRANSFER_DST) + && !compressed + && !depth + && self.internal.downlevel.storage_images + { + for mip in 0..image.mip_levels { + let view = ViewInfo { + resource: resource, + kind: image.kind, + caps: image::ViewCapabilities::empty(), + view_kind, + // TODO: we should be using `uav_format` rather than `copy_uav_format`, and share + // the UAVs when the formats are identical + format: decomposed.copy_uav.unwrap(), + levels: mip..(mip + 1), + layers: 0..image.kind.num_layers(), + }; + + let uav = self + .view_image_as_unordered_access(&view) + .map_err(|_| device::BindError::WrongMemory)?; + + if let Some(ref name) = image.internal.debug_name { + set_debug_name(&uav, &format!("{} -- UAV Mip {}", name, mip)); + } + + unordered_access_views.push(uav); + } + } + + let (copy_srv, srv) = if image.usage.contains(image::Usage::TRANSFER_SRC) { + let mut view = ViewInfo { + resource: resource, + kind: image.kind, + caps: image::ViewCapabilities::empty(), + view_kind, + format: decomposed.copy_srv.unwrap(), + levels: 0..image.mip_levels, + layers: 0..image.kind.num_layers(), + }; + + let copy_srv = if !compressed { + Some( + self.view_image_as_shader_resource(&view) + .map_err(|_| device::BindError::WrongMemory)?, + ) + } else { + None + }; + + view.format = decomposed.srv.unwrap(); + + let srv = if !depth && !stencil { + Some( + self.view_image_as_shader_resource(&view) + .map_err(|_| device::BindError::WrongMemory)?, + ) + } else { + None + }; + + (copy_srv, srv) + } else { + (None, None) + }; + + let mut render_target_views = Vec::new(); + + if (image.usage.contains(image::Usage::COLOR_ATTACHMENT) + || image.usage.contains(image::Usage::TRANSFER_DST)) + && !compressed + && !depth + { + for layer in 0..image.kind.num_layers() { + for mip in 0..image.mip_levels { + let view = ViewInfo { + resource, + kind: image.kind, + caps: image::ViewCapabilities::empty(), + view_kind, + format: decomposed.rtv.unwrap(), + levels: mip..(mip + 1), + layers: layer..(layer + 1), + }; + + let rtv = self + .view_image_as_render_target(&view) + .map_err(|_| device::BindError::WrongMemory)?; + + if let Some(ref name) = image.internal.debug_name { + set_debug_name( + &rtv, + &format!("{} -- RTV Mip {} Layer {}", name, mip, layer), + ); + } + + render_target_views.push(rtv); + } + } + }; + + let mut depth_stencil_views = Vec::new(); + + if depth { + for layer in 0..image.kind.num_layers() { + for mip in 0..image.mip_levels { + let view = ViewInfo { + resource, + kind: image.kind, + caps: image::ViewCapabilities::empty(), + view_kind, + format: decomposed.dsv.unwrap(), + levels: mip..(mip + 1), + layers: layer..(layer + 1), + }; + + let dsv = self + .view_image_as_depth_stencil(&view, None) + .map_err(|_| device::BindError::WrongMemory)?; + + if let Some(ref name) = image.internal.debug_name { + set_debug_name( + &dsv, + &format!("{} -- DSV Mip {} Layer {}", name, mip, layer), + ); + } + + depth_stencil_views.push(dsv); + } + } + } + + if let Some(ref mut name) = image.internal.debug_name { + set_debug_name(&*resource, name); + if let Some(ref copy_srv) = copy_srv { + set_debug_name_with_suffix(copy_srv, name, " -- Copy SRV"); + } + if let Some(ref srv) = srv { + set_debug_name_with_suffix(srv, name, " -- SRV"); + } + } + + let internal = InternalImage { + raw: resource, + copy_srv, + srv, + unordered_access_views, + depth_stencil_views, + render_target_views, + debug_name: image.internal.debug_name.take(), + }; + + image.decomposed_format = decomposed; + image.internal = internal; + + Ok(()) + } + + unsafe fn create_image_view( + &self, + image: &Image, + view_kind: image::ViewKind, + format: format::Format, + _swizzle: format::Swizzle, + usage: image::Usage, + range: image::SubresourceRange, + ) -> Result { + let is_array = image.kind.num_layers() > 1; + let num_levels = range.resolve_level_count(image.mip_levels); + let num_layers = range.resolve_layer_count(image.kind.num_layers()); + + let info = ViewInfo { + resource: image.internal.raw, + kind: image.kind, + caps: image.view_caps, + // D3D11 doesn't allow looking at a single slice of an array as a non-array + view_kind: if is_array && view_kind == image::ViewKind::D2 { + image::ViewKind::D2Array + } else if is_array && view_kind == image::ViewKind::D1 { + image::ViewKind::D1Array + } else { + view_kind + }, + format: conv::map_format(format).ok_or(image::ViewCreationError::BadFormat(format))?, + levels: range.level_start..range.level_start + num_levels, + layers: range.layer_start..range.layer_start + num_layers, + }; + + let srv_info = ViewInfo { + format: conv::viewable_format(info.format), + ..info.clone() + }; + + let mut debug_name = image.internal.debug_name.clone(); + + Ok(ImageView { + subresource: d3d11::D3D11CalcSubresource( + 0, + range.layer_start as _, + range.level_start as _, + ), + format, + srv_handle: if usage.intersects(image::Usage::SAMPLED) { + let srv = self.view_image_as_shader_resource(&srv_info)?; + + if let Some(ref mut name) = debug_name { + set_debug_name_with_suffix(&srv, name, " -- SRV"); + } + + Some(srv.into_raw()) + } else { + None + }, + rtv_handle: if usage.contains(image::Usage::COLOR_ATTACHMENT) { + let rtv = self.view_image_as_render_target(&info)?; + + if let Some(ref mut name) = debug_name { + set_debug_name_with_suffix(&rtv, name, " -- RTV"); + } + + Some(rtv.into_raw()) + } else { + None + }, + uav_handle: if usage.contains(image::Usage::STORAGE) { + let uav = self.view_image_as_unordered_access(&info)?; + + if let Some(ref mut name) = debug_name { + set_debug_name_with_suffix(&uav, name, " -- UAV"); + } + + Some(uav.into_raw()) + } else { + None + }, + dsv_handle: if usage.contains(image::Usage::DEPTH_STENCIL_ATTACHMENT) { + if let Some(dsv) = self.view_image_as_depth_stencil(&info, None).ok() { + if let Some(ref mut name) = debug_name { + set_debug_name_with_suffix(&dsv, name, " -- DSV"); + } + + Some(dsv.into_raw()) + } else { + None + } + } else { + None + }, + rodsv_handle: if usage.contains(image::Usage::DEPTH_STENCIL_ATTACHMENT) + && self.internal.downlevel.read_only_depth_stencil + { + if let Some(rodsv) = self + .view_image_as_depth_stencil(&info, Some(image.format.is_stencil())) + .ok() + { + if let Some(ref mut name) = debug_name { + set_debug_name_with_suffix(&rodsv, name, " -- DSV"); + } + + Some(rodsv.into_raw()) + } else { + None + } + } else { + None + }, + owned: true, + }) + } + + unsafe fn create_sampler( + &self, + info: &image::SamplerDesc, + ) -> Result { + assert!(info.normalized); + + let op = match info.comparison { + Some(_) => d3d11::D3D11_FILTER_REDUCTION_TYPE_COMPARISON, + None => d3d11::D3D11_FILTER_REDUCTION_TYPE_STANDARD, + }; + + let desc = d3d11::D3D11_SAMPLER_DESC { + Filter: conv::map_filter( + info.min_filter, + info.mag_filter, + info.mip_filter, + op, + info.anisotropy_clamp, + ), + AddressU: conv::map_wrapping(info.wrap_mode.0), + AddressV: conv::map_wrapping(info.wrap_mode.1), + AddressW: conv::map_wrapping(info.wrap_mode.2), + MipLODBias: info.lod_bias.0, + MaxAnisotropy: info.anisotropy_clamp.map_or(0, |aniso| aniso as u32), + ComparisonFunc: info.comparison.map_or(0, |comp| conv::map_comparison(comp)), + BorderColor: info.border.into(), + MinLOD: info.lod_range.start.0, + MaxLOD: info.lod_range.end.0, + }; + + let mut sampler = ptr::null_mut(); + let hr = self + .raw + .CreateSamplerState(&desc, &mut sampler as *mut *mut _ as *mut *mut _); + + assert_eq!(true, winerror::SUCCEEDED(hr)); + + Ok(Sampler { + sampler_handle: ComPtr::from_raw(sampler), + }) + } + + unsafe fn create_descriptor_pool( + &self, + _max_sets: usize, + ranges: I, + _flags: pso::DescriptorPoolCreateFlags, + ) -> Result + where + I: Iterator, + { + let mut total = RegisterData::default(); + for range in ranges { + let content = DescriptorContent::from(range.ty); + total.add_content_many(content, range.count as DescriptorIndex); + } + + let max_stages = 6; + let count = total.sum() * max_stages; + Ok(DescriptorPool::with_capacity(count)) + } + + unsafe fn create_descriptor_set_layout<'a, I, J>( + &self, + layout_bindings: I, + _immutable_samplers: J, + ) -> Result + where + I: Iterator, + J: Iterator, + { + let mut total = MultiStageData::>::default(); + let mut bindings = layout_bindings.collect::>(); + + for binding in bindings.iter() { + let content = DescriptorContent::from(binding.ty); + // If this binding is used by the graphics pipeline and is a UAV, it belongs to the "Output Merger" + // stage, so we only put them in the fragment stage to save redundant descriptor allocations. + let stage_flags = if content.contains(DescriptorContent::UAV) + && binding + .stage_flags + .intersects(pso::ShaderStageFlags::ALL - pso::ShaderStageFlags::COMPUTE) + { + let mut stage_flags = pso::ShaderStageFlags::FRAGMENT; + stage_flags.set( + pso::ShaderStageFlags::COMPUTE, + binding.stage_flags.contains(pso::ShaderStageFlags::COMPUTE), + ); + stage_flags + } else { + binding.stage_flags + }; + total.add_content_many(content, stage_flags, binding.count as _); + } + + bindings.sort_by_key(|a| a.binding); + + let accum = total.map_register(|count| RegisterAccumulator { + res_index: *count as ResourceIndex, + }); + + Ok(DescriptorSetLayout { + bindings: Arc::new(bindings), + pool_mapping: accum.to_mapping(), + }) + } + + unsafe fn write_descriptor_set<'a, I>(&self, op: pso::DescriptorSetWrite<'a, Backend, I>) + where + I: Iterator>, + { + // Get baseline mapping + let mut mapping = op + .set + .layout + .pool_mapping + .map_register(|mapping| mapping.offset); + + // Iterate over layout bindings until the first binding is found. + let binding_start = op + .set + .layout + .bindings + .iter() + .position(|binding| binding.binding == op.binding) + .unwrap(); + + // If we've skipped layout bindings, we need to add them to get the correct binding offset + for binding in &op.set.layout.bindings[..binding_start] { + let content = DescriptorContent::from(binding.ty); + mapping.add_content_many(content, binding.stage_flags, binding.count as _); + } + + // We start at the given binding index and array index + let mut binding_index = binding_start; + let mut array_index = op.array_offset; + + // If we're skipping array indices in the current binding, we need to add them to get the correct binding offset + if array_index > 0 { + let binding: &pso::DescriptorSetLayoutBinding = &op.set.layout.bindings[binding_index]; + let content = DescriptorContent::from(binding.ty); + mapping.add_content_many(content, binding.stage_flags, array_index as _); + } + + // Iterate over the descriptors, figuring out the corresponding binding, and adding + // it to the set of bindings. + // + // When we hit the end of an array of descriptors and there are still descriptors left + // over, we will spill into writing the next binding. + for descriptor in op.descriptors { + let binding: &pso::DescriptorSetLayoutBinding = &op.set.layout.bindings[binding_index]; + + let handles = match descriptor { + pso::Descriptor::Buffer(buffer, ref _sub) => RegisterData { + c: match buffer.internal.disjoint_cb { + Some(dj_buf) => dj_buf as *mut _, + None => buffer.internal.raw as *mut _, + }, + t: buffer.internal.srv.map_or(ptr::null_mut(), |p| p as *mut _), + u: buffer.internal.uav.map_or(ptr::null_mut(), |p| p as *mut _), + s: ptr::null_mut(), + }, + pso::Descriptor::Image(image, _layout) => RegisterData { + c: ptr::null_mut(), + t: image.srv_handle.map_or(ptr::null_mut(), |h| h as *mut _), + u: image.uav_handle.map_or(ptr::null_mut(), |h| h as *mut _), + s: ptr::null_mut(), + }, + pso::Descriptor::Sampler(sampler) => RegisterData { + c: ptr::null_mut(), + t: ptr::null_mut(), + u: ptr::null_mut(), + s: sampler.sampler_handle.as_raw() as *mut _, + }, + pso::Descriptor::CombinedImageSampler(image, _layout, sampler) => RegisterData { + c: ptr::null_mut(), + t: image.srv_handle.map_or(ptr::null_mut(), |h| h as *mut _), + u: image.uav_handle.map_or(ptr::null_mut(), |h| h as *mut _), + s: sampler.sampler_handle.as_raw() as *mut _, + }, + pso::Descriptor::TexelBuffer(_buffer_view) => unimplemented!(), + }; + + let content = DescriptorContent::from(binding.ty); + if content.contains(DescriptorContent::CBV) { + let offsets = mapping.map_other(|map| map.c); + op.set + .assign_stages(&offsets, binding.stage_flags, handles.c); + }; + if content.contains(DescriptorContent::SRV) { + let offsets = mapping.map_other(|map| map.t); + op.set + .assign_stages(&offsets, binding.stage_flags, handles.t); + }; + if content.contains(DescriptorContent::UAV) { + // If this binding is used by the graphics pipeline and is a UAV, it belongs to the "Output Merger" + // stage, so we only put them in the fragment stage to save redundant descriptor allocations. + let stage_flags = if binding + .stage_flags + .intersects(pso::ShaderStageFlags::ALL - pso::ShaderStageFlags::COMPUTE) + { + let mut stage_flags = pso::ShaderStageFlags::FRAGMENT; + stage_flags.set( + pso::ShaderStageFlags::COMPUTE, + binding.stage_flags.contains(pso::ShaderStageFlags::COMPUTE), + ); + stage_flags + } else { + binding.stage_flags + }; + + let offsets = mapping.map_other(|map| map.u); + op.set.assign_stages(&offsets, stage_flags, handles.u); + }; + if content.contains(DescriptorContent::SAMPLER) { + let offsets = mapping.map_other(|map| map.s); + op.set + .assign_stages(&offsets, binding.stage_flags, handles.s); + }; + + mapping.add_content_many(content, binding.stage_flags, 1); + + array_index += 1; + if array_index >= binding.count { + // We've run out of array to write to, we should overflow to the next binding. + array_index = 0; + binding_index += 1; + } + } + } + + unsafe fn copy_descriptor_set<'a>(&self, _op: pso::DescriptorSetCopy<'a, Backend>) { + unimplemented!() + /* + for offset in 0 .. copy.count { + let (dst_ty, dst_handle_offset, dst_second_handle_offset) = copy + .dst_set + .get_handle_offset(copy.dst_binding + offset as u32); + let (src_ty, src_handle_offset, src_second_handle_offset) = copy + .src_set + .get_handle_offset(copy.src_binding + offset as u32); + assert_eq!(dst_ty, src_ty); + + let dst_handle = copy.dst_set.handles.offset(dst_handle_offset as isize); + let src_handle = copy.dst_set.handles.offset(src_handle_offset as isize); + + match dst_ty { + pso::DescriptorType::Image { + ty: pso::ImageDescriptorType::Sampled { with_sampler: true } + } => { + let dst_second_handle = copy + .dst_set + .handles + .offset(dst_second_handle_offset as isize); + let src_second_handle = copy + .dst_set + .handles + .offset(src_second_handle_offset as isize); + + *dst_handle = *src_handle; + *dst_second_handle = *src_second_handle; + } + _ => *dst_handle = *src_handle, + } + }*/ + } + + unsafe fn map_memory( + &self, + memory: &mut Memory, + segment: memory::Segment, + ) -> Result<*mut u8, device::MapError> { + Ok(memory.host_ptr.offset(segment.offset as isize)) + } + + unsafe fn unmap_memory(&self, _memory: &mut Memory) { + // persistent mapping FTW + } + + unsafe fn flush_mapped_memory_ranges<'a, I>(&self, ranges: I) -> Result<(), device::OutOfMemory> + where + I: Iterator, + { + let _scope = debug_scope!(&self.context, "FlushMappedRanges"); + + // go through every range we wrote to + for (memory, ref segment) in ranges { + let range = memory.resolve(segment); + let _scope = debug_scope!(&self.context, "Range({:?})", range); + memory.flush(&self.context, range); + } + + Ok(()) + } + + unsafe fn invalidate_mapped_memory_ranges<'a, I>( + &self, + ranges: I, + ) -> Result<(), device::OutOfMemory> + where + I: Iterator, + { + let _scope = debug_scope!(&self.context, "InvalidateMappedRanges"); + + // go through every range we want to read from + for (memory, ref segment) in ranges { + let range = memory.resolve(segment); + let _scope = debug_scope!(&self.context, "Range({:?})", range); + memory.invalidate( + &self.context, + range, + self.internal.working_buffer.clone(), + self.internal.working_buffer_size, + ); + } + + Ok(()) + } + + fn create_semaphore(&self) -> Result { + // TODO: + Ok(Semaphore) + } + + fn create_fence(&self, signalled: bool) -> Result { + Ok(Arc::new(RawFence { + mutex: Mutex::new(signalled), + condvar: Condvar::new(), + })) + } + + unsafe fn reset_fence(&self, fence: &mut Fence) -> Result<(), device::OutOfMemory> { + *fence.mutex.lock() = false; + Ok(()) + } + + unsafe fn wait_for_fence( + &self, + fence: &Fence, + timeout_ns: u64, + ) -> Result { + use std::time::{Duration, Instant}; + + debug!("wait_for_fence {:?} for {} ns", fence, timeout_ns); + let mut guard = fence.mutex.lock(); + match timeout_ns { + 0 => Ok(*guard), + 0xFFFFFFFFFFFFFFFF => { + while !*guard { + fence.condvar.wait(&mut guard); + } + Ok(true) + } + _ => { + let total = Duration::from_nanos(timeout_ns as u64); + let now = Instant::now(); + while !*guard { + let duration = match total.checked_sub(now.elapsed()) { + Some(dur) => dur, + None => return Ok(false), + }; + let result = fence.condvar.wait_for(&mut guard, duration); + if result.timed_out() { + return Ok(false); + } + } + Ok(true) + } + } + } + + unsafe fn get_fence_status(&self, fence: &Fence) -> Result { + Ok(*fence.mutex.lock()) + } + + fn create_event(&self) -> Result<(), device::OutOfMemory> { + unimplemented!() + } + + unsafe fn get_event_status(&self, _event: &()) -> Result { + unimplemented!() + } + + unsafe fn set_event(&self, _event: &mut ()) -> Result<(), device::OutOfMemory> { + unimplemented!() + } + + unsafe fn reset_event(&self, _event: &mut ()) -> Result<(), device::OutOfMemory> { + unimplemented!() + } + + unsafe fn free_memory(&self, mut memory: Memory) { + if !memory.host_ptr.is_null() { + let _vec = + Vec::from_raw_parts(memory.host_ptr, memory.size as usize, memory.size as usize); + // let it drop + memory.host_ptr = ptr::null_mut(); + } + for (_, (_range, mut internal)) in memory.local_buffers.write().drain() { + internal.release_resources() + } + } + + unsafe fn create_query_pool( + &self, + _query_ty: query::Type, + _count: query::Id, + ) -> Result { + unimplemented!() + } + + unsafe fn destroy_query_pool(&self, _pool: QueryPool) { + unimplemented!() + } + + unsafe fn get_query_pool_results( + &self, + _pool: &QueryPool, + _queries: Range, + _data: &mut [u8], + _stride: buffer::Stride, + _flags: query::ResultFlags, + ) -> Result { + unimplemented!() + } + + unsafe fn destroy_shader_module(&self, _shader_lib: ShaderModule) {} + + unsafe fn destroy_render_pass(&self, _rp: RenderPass) { + //unimplemented!() + } + + unsafe fn destroy_pipeline_layout(&self, _layout: PipelineLayout) { + //unimplemented!() + } + + unsafe fn destroy_graphics_pipeline(&self, _pipeline: GraphicsPipeline) {} + + unsafe fn destroy_compute_pipeline(&self, _pipeline: ComputePipeline) {} + + unsafe fn destroy_framebuffer(&self, _fb: Framebuffer) {} + + unsafe fn destroy_buffer(&self, buffer: Buffer) { + let mut internal = buffer.internal; + + if internal.raw.is_null() { + return; + } + + let arena_arc = match buffer.local_memory_arena.upgrade() { + Some(arena) => arena, + // Memory is destroyed before the buffer, we've already been destroyed. + None => return, + }; + let mut arena = arena_arc.write(); + + let memory_index = buffer.memory_index.expect("Buffer's memory index unset"); + // Drop the internal stored by the arena on the floor, it owns nothing. + let _ = arena.remove(memory_index); + + // Release all memory owned by this buffer + internal.release_resources(); + } + + unsafe fn destroy_buffer_view(&self, _view: BufferView) { + //unimplemented!() + } + + unsafe fn destroy_image(&self, mut image: Image) { + image.internal.release_resources(); + } + + unsafe fn destroy_image_view(&self, _view: ImageView) { + //unimplemented!() + } + + unsafe fn destroy_sampler(&self, _sampler: Sampler) {} + + unsafe fn destroy_descriptor_pool(&self, _pool: DescriptorPool) { + //unimplemented!() + } + + unsafe fn destroy_descriptor_set_layout(&self, _layout: DescriptorSetLayout) { + //unimplemented!() + } + + unsafe fn destroy_fence(&self, _fence: Fence) { + // unimplemented!() + } + + unsafe fn destroy_semaphore(&self, _semaphore: Semaphore) { + //unimplemented!() + } + + unsafe fn destroy_event(&self, _event: ()) { + //unimplemented!() + } + + fn wait_idle(&self) -> Result<(), device::OutOfMemory> { + Ok(()) + // unimplemented!() + } + + unsafe fn set_image_name(&self, image: &mut Image, name: &str) { + if !verify_debug_ascii(name) { + return; + } + + image.internal.debug_name = Some(name.to_string()); + } + + unsafe fn set_buffer_name(&self, buffer: &mut Buffer, name: &str) { + if !verify_debug_ascii(name) { + return; + } + + buffer.internal.debug_name = Some(name.to_string()); + } + + unsafe fn set_command_buffer_name(&self, command_buffer: &mut CommandBuffer, name: &str) { + if !verify_debug_ascii(name) { + return; + } + + command_buffer.debug_name = Some(name.to_string()); + } + + unsafe fn set_semaphore_name(&self, _semaphore: &mut Semaphore, _name: &str) { + // TODO + } + + unsafe fn set_fence_name(&self, _fence: &mut Fence, _name: &str) { + // TODO + } + + unsafe fn set_framebuffer_name(&self, _framebuffer: &mut Framebuffer, _name: &str) { + // TODO + } + + unsafe fn set_render_pass_name(&self, _render_pass: &mut RenderPass, _name: &str) { + // TODO + } + + unsafe fn set_descriptor_set_name(&self, _descriptor_set: &mut DescriptorSet, _name: &str) { + // TODO + } + + unsafe fn set_descriptor_set_layout_name( + &self, + _descriptor_set_layout: &mut DescriptorSetLayout, + _name: &str, + ) { + // TODO + } + + unsafe fn set_pipeline_layout_name(&self, _pipeline_layout: &mut PipelineLayout, _name: &str) { + // TODO + } + + fn start_capture(&self) { + //TODO + } + + fn stop_capture(&self) { + //TODO + } +} diff --git a/third_party/rust/gfx-backend-dx11/src/dxgi.rs b/third_party/rust/gfx-backend-dx11/src/dxgi.rs index eb221526ba49..cc0761592598 100644 --- a/third_party/rust/gfx-backend-dx11/src/dxgi.rs +++ b/third_party/rust/gfx-backend-dx11/src/dxgi.rs @@ -1,244 +1,244 @@ -use hal::adapter::{AdapterInfo, DeviceType}; - -use winapi::{ - shared::{ - dxgi, dxgi1_2, dxgi1_3, dxgi1_4, dxgi1_5, - guiddef::{GUID, REFIID}, - winerror, - }, - um::{d3d11, unknwnbase::IUnknown}, - Interface, -}; - -use wio::com::ComPtr; - -use std::{ffi::OsString, mem, os::windows::ffi::OsStringExt, ptr}; - -#[derive(Debug, Copy, Clone)] -pub(crate) enum DxgiVersion { - /// Capable of the following interfaces: - /// * IDXGIObject - /// * IDXGIDeviceSubObject - /// * IDXGIResource - /// * IDXGIKeyedMutex - /// * IDXGISurface - /// * IDXGISurface1 - /// * IDXGIOutput - /// * IDXGISwapChain - /// * IDXGIFactory - /// * IDXGIDevice - /// * IDXGIFactory1 - /// * IDXGIAdapter1 - /// * IDXGIDevice1 - Dxgi1_0, - - /// Capable of the following interfaces: - /// * IDXGIDisplayControl - /// * IDXGIOutputDuplication - /// * IDXGISurface2 - /// * IDXGIResource1 - /// * IDXGIDevice2 - /// * IDXGISwapChain1 - /// * IDXGIFactory2 - /// * IDXGIAdapter2 - /// * IDXGIOutput1 - Dxgi1_2, - - /// Capable of the following interfaces: - /// * IDXGIDevice3 - /// * IDXGISwapChain2 - /// * IDXGIOutput2 - /// * IDXGIDecodeSwapChain - /// * IDXGIFactoryMedia - /// * IDXGISwapChainMedia - /// * IDXGIOutput3 - Dxgi1_3, - - /// Capable of the following interfaces: - /// * IDXGISwapChain3 - /// * IDXGIOutput4 - /// * IDXGIFactory4 - /// * IDXGIAdapter3 - Dxgi1_4, - - /// Capable of the following interfaces: - /// * IDXGIOutput5 - /// * IDXGISwapChain4 - /// * IDXGIDevice4 - /// * IDXGIFactory5 - Dxgi1_5, -} - -type DxgiFun = - unsafe extern "system" fn(REFIID, *mut *mut winapi::ctypes::c_void) -> winerror::HRESULT; - -fn create_dxgi_factory1( - func: &DxgiFun, - guid: &GUID, -) -> Result, winerror::HRESULT> { - let mut factory: *mut IUnknown = ptr::null_mut(); - - let hr = unsafe { func(guid, &mut factory as *mut *mut _ as *mut *mut _) }; - - if winerror::SUCCEEDED(hr) { - Ok(unsafe { ComPtr::from_raw(factory as *mut _) }) - } else { - Err(hr) - } -} - -pub(crate) fn get_dxgi_factory( -) -> Result<(libloading::Library, ComPtr, DxgiVersion), winerror::HRESULT> { - // The returned Com-pointer is only safe to use for the lifetime of the Library. - let library = unsafe { libloading::Library::new("dxgi.dll").map_err(|_| -1)? }; - let func: libloading::Symbol = - unsafe { library.get(b"CreateDXGIFactory1") }.map_err(|_| -1)?; - - // TODO: do we even need `create_dxgi_factory2`? - if let Ok(factory) = create_dxgi_factory1(&func, &dxgi1_5::IDXGIFactory5::uuidof()) { - return Ok((library, factory, DxgiVersion::Dxgi1_5)); - } - - if let Ok(factory) = create_dxgi_factory1(&func, &dxgi1_4::IDXGIFactory4::uuidof()) { - return Ok((library, factory, DxgiVersion::Dxgi1_4)); - } - - if let Ok(factory) = create_dxgi_factory1(&func, &dxgi1_3::IDXGIFactory3::uuidof()) { - return Ok((library, factory, DxgiVersion::Dxgi1_3)); - } - - if let Ok(factory) = create_dxgi_factory1(&func, &dxgi1_2::IDXGIFactory2::uuidof()) { - return Ok((library, factory, DxgiVersion::Dxgi1_2)); - } - - if let Ok(factory) = create_dxgi_factory1(&func, &dxgi::IDXGIFactory1::uuidof()) { - return Ok((library, factory, DxgiVersion::Dxgi1_0)); - } - - // TODO: any reason why above would fail and this wouldnt? - match create_dxgi_factory1(&func, &dxgi::IDXGIFactory::uuidof()) { - Ok(factory) => Ok((library, factory, DxgiVersion::Dxgi1_0)), - Err(hr) => Err(hr), - } -} - -fn enum_adapters1( - idx: u32, - factory: *mut dxgi::IDXGIFactory, -) -> Result, winerror::HRESULT> { - let mut adapter: *mut dxgi::IDXGIAdapter = ptr::null_mut(); - - let hr = unsafe { - (*(factory as *mut dxgi::IDXGIFactory1)) - .EnumAdapters1(idx, &mut adapter as *mut *mut _ as *mut *mut _) - }; - - if winerror::SUCCEEDED(hr) { - Ok(unsafe { ComPtr::from_raw(adapter) }) - } else { - Err(hr) - } -} - -fn build_adapter_info( - description: &[u16], - vendor_id: u32, - device_id: u32, - flags: u32, - video_mem: usize, - device: &ComPtr, -) -> AdapterInfo { - let mut features_options2: d3d11::D3D11_FEATURE_DATA_D3D11_OPTIONS2 = unsafe { mem::zeroed() }; - let discovered_gpu_type = winerror::SUCCEEDED(unsafe { - device.CheckFeatureSupport( - d3d11::D3D11_FEATURE_D3D11_OPTIONS2, - &mut features_options2 as *mut _ as *mut _, - mem::size_of::() as _, - ) - }); - - let device_name = { - let len = description.iter().take_while(|&&c| c != 0).count(); - let name = ::from_wide(&description[..len]); - name.to_string_lossy().into_owned() - }; - - AdapterInfo { - name: device_name, - vendor: vendor_id as usize, - device: device_id as usize, - device_type: if (flags & dxgi::DXGI_ADAPTER_FLAG_SOFTWARE) != 0 { - DeviceType::VirtualGpu - } else if discovered_gpu_type { - if features_options2.UnifiedMemoryArchitecture == 1 { - DeviceType::IntegratedGpu - } else { - DeviceType::DiscreteGpu - } - } else if video_mem <= 512_000_000 { - DeviceType::IntegratedGpu - } else { - DeviceType::DiscreteGpu - }, - } -} - -pub(crate) fn get_adapter_desc( - adapter: &ComPtr, - device: &ComPtr, - version: DxgiVersion, -) -> AdapterInfo { - let adapter = adapter.as_raw(); - - match version { - DxgiVersion::Dxgi1_0 => { - let mut desc: dxgi::DXGI_ADAPTER_DESC1 = unsafe { mem::zeroed() }; - unsafe { - (*(adapter as *mut dxgi::IDXGIAdapter1)).GetDesc1(&mut desc); - } - - build_adapter_info( - &desc.Description, - desc.VendorId, - desc.DeviceId, - desc.Flags, - desc.DedicatedVideoMemory, - device, - ) - } - DxgiVersion::Dxgi1_2 - | DxgiVersion::Dxgi1_3 - | DxgiVersion::Dxgi1_4 - | DxgiVersion::Dxgi1_5 => { - let mut desc: dxgi1_2::DXGI_ADAPTER_DESC2 = unsafe { mem::zeroed() }; - unsafe { - (*(adapter as *mut dxgi1_2::IDXGIAdapter2)).GetDesc2(&mut desc); - } - - build_adapter_info( - &desc.Description, - desc.VendorId, - desc.DeviceId, - desc.Flags, - desc.DedicatedVideoMemory, - device, - ) - } - } -} - -pub(crate) fn get_adapter( - idx: u32, - factory: *mut dxgi::IDXGIFactory, - version: DxgiVersion, -) -> Result, winerror::HRESULT> { - let adapter = match version { - DxgiVersion::Dxgi1_0 - | DxgiVersion::Dxgi1_2 - | DxgiVersion::Dxgi1_3 - | DxgiVersion::Dxgi1_4 - | DxgiVersion::Dxgi1_5 => enum_adapters1(idx, factory)?, - }; - - Ok(adapter) -} +use hal::adapter::{AdapterInfo, DeviceType}; + +use winapi::{ + shared::{ + dxgi, dxgi1_2, dxgi1_3, dxgi1_4, dxgi1_5, + guiddef::{GUID, REFIID}, + winerror, + }, + um::{d3d11, unknwnbase::IUnknown}, + Interface, +}; + +use wio::com::ComPtr; + +use std::{ffi::OsString, mem, os::windows::ffi::OsStringExt, ptr}; + +#[derive(Debug, Copy, Clone)] +pub(crate) enum DxgiVersion { + /// Capable of the following interfaces: + /// * IDXGIObject + /// * IDXGIDeviceSubObject + /// * IDXGIResource + /// * IDXGIKeyedMutex + /// * IDXGISurface + /// * IDXGISurface1 + /// * IDXGIOutput + /// * IDXGISwapChain + /// * IDXGIFactory + /// * IDXGIDevice + /// * IDXGIFactory1 + /// * IDXGIAdapter1 + /// * IDXGIDevice1 + Dxgi1_0, + + /// Capable of the following interfaces: + /// * IDXGIDisplayControl + /// * IDXGIOutputDuplication + /// * IDXGISurface2 + /// * IDXGIResource1 + /// * IDXGIDevice2 + /// * IDXGISwapChain1 + /// * IDXGIFactory2 + /// * IDXGIAdapter2 + /// * IDXGIOutput1 + Dxgi1_2, + + /// Capable of the following interfaces: + /// * IDXGIDevice3 + /// * IDXGISwapChain2 + /// * IDXGIOutput2 + /// * IDXGIDecodeSwapChain + /// * IDXGIFactoryMedia + /// * IDXGISwapChainMedia + /// * IDXGIOutput3 + Dxgi1_3, + + /// Capable of the following interfaces: + /// * IDXGISwapChain3 + /// * IDXGIOutput4 + /// * IDXGIFactory4 + /// * IDXGIAdapter3 + Dxgi1_4, + + /// Capable of the following interfaces: + /// * IDXGIOutput5 + /// * IDXGISwapChain4 + /// * IDXGIDevice4 + /// * IDXGIFactory5 + Dxgi1_5, +} + +type DxgiFun = + unsafe extern "system" fn(REFIID, *mut *mut winapi::ctypes::c_void) -> winerror::HRESULT; + +fn create_dxgi_factory1( + func: &DxgiFun, + guid: &GUID, +) -> Result, winerror::HRESULT> { + let mut factory: *mut IUnknown = ptr::null_mut(); + + let hr = unsafe { func(guid, &mut factory as *mut *mut _ as *mut *mut _) }; + + if winerror::SUCCEEDED(hr) { + Ok(unsafe { ComPtr::from_raw(factory as *mut _) }) + } else { + Err(hr) + } +} + +pub(crate) fn get_dxgi_factory( +) -> Result<(libloading::Library, ComPtr, DxgiVersion), winerror::HRESULT> { + // The returned Com-pointer is only safe to use for the lifetime of the Library. + let library = unsafe { libloading::Library::new("dxgi.dll").map_err(|_| -1)? }; + let func: libloading::Symbol = + unsafe { library.get(b"CreateDXGIFactory1") }.map_err(|_| -1)?; + + // TODO: do we even need `create_dxgi_factory2`? + if let Ok(factory) = create_dxgi_factory1(&func, &dxgi1_5::IDXGIFactory5::uuidof()) { + return Ok((library, factory, DxgiVersion::Dxgi1_5)); + } + + if let Ok(factory) = create_dxgi_factory1(&func, &dxgi1_4::IDXGIFactory4::uuidof()) { + return Ok((library, factory, DxgiVersion::Dxgi1_4)); + } + + if let Ok(factory) = create_dxgi_factory1(&func, &dxgi1_3::IDXGIFactory3::uuidof()) { + return Ok((library, factory, DxgiVersion::Dxgi1_3)); + } + + if let Ok(factory) = create_dxgi_factory1(&func, &dxgi1_2::IDXGIFactory2::uuidof()) { + return Ok((library, factory, DxgiVersion::Dxgi1_2)); + } + + if let Ok(factory) = create_dxgi_factory1(&func, &dxgi::IDXGIFactory1::uuidof()) { + return Ok((library, factory, DxgiVersion::Dxgi1_0)); + } + + // TODO: any reason why above would fail and this wouldnt? + match create_dxgi_factory1(&func, &dxgi::IDXGIFactory::uuidof()) { + Ok(factory) => Ok((library, factory, DxgiVersion::Dxgi1_0)), + Err(hr) => Err(hr), + } +} + +fn enum_adapters1( + idx: u32, + factory: *mut dxgi::IDXGIFactory, +) -> Result, winerror::HRESULT> { + let mut adapter: *mut dxgi::IDXGIAdapter = ptr::null_mut(); + + let hr = unsafe { + (*(factory as *mut dxgi::IDXGIFactory1)) + .EnumAdapters1(idx, &mut adapter as *mut *mut _ as *mut *mut _) + }; + + if winerror::SUCCEEDED(hr) { + Ok(unsafe { ComPtr::from_raw(adapter) }) + } else { + Err(hr) + } +} + +fn build_adapter_info( + description: &[u16], + vendor_id: u32, + device_id: u32, + flags: u32, + video_mem: usize, + device: &ComPtr, +) -> AdapterInfo { + let mut features_options2: d3d11::D3D11_FEATURE_DATA_D3D11_OPTIONS2 = unsafe { mem::zeroed() }; + let discovered_gpu_type = winerror::SUCCEEDED(unsafe { + device.CheckFeatureSupport( + d3d11::D3D11_FEATURE_D3D11_OPTIONS2, + &mut features_options2 as *mut _ as *mut _, + mem::size_of::() as _, + ) + }); + + let device_name = { + let len = description.iter().take_while(|&&c| c != 0).count(); + let name = ::from_wide(&description[..len]); + name.to_string_lossy().into_owned() + }; + + AdapterInfo { + name: device_name, + vendor: vendor_id as usize, + device: device_id as usize, + device_type: if (flags & dxgi::DXGI_ADAPTER_FLAG_SOFTWARE) != 0 { + DeviceType::VirtualGpu + } else if discovered_gpu_type { + if features_options2.UnifiedMemoryArchitecture == 1 { + DeviceType::IntegratedGpu + } else { + DeviceType::DiscreteGpu + } + } else if video_mem <= 512_000_000 { + DeviceType::IntegratedGpu + } else { + DeviceType::DiscreteGpu + }, + } +} + +pub(crate) fn get_adapter_desc( + adapter: &ComPtr, + device: &ComPtr, + version: DxgiVersion, +) -> AdapterInfo { + let adapter = adapter.as_raw(); + + match version { + DxgiVersion::Dxgi1_0 => { + let mut desc: dxgi::DXGI_ADAPTER_DESC1 = unsafe { mem::zeroed() }; + unsafe { + (*(adapter as *mut dxgi::IDXGIAdapter1)).GetDesc1(&mut desc); + } + + build_adapter_info( + &desc.Description, + desc.VendorId, + desc.DeviceId, + desc.Flags, + desc.DedicatedVideoMemory, + device, + ) + } + DxgiVersion::Dxgi1_2 + | DxgiVersion::Dxgi1_3 + | DxgiVersion::Dxgi1_4 + | DxgiVersion::Dxgi1_5 => { + let mut desc: dxgi1_2::DXGI_ADAPTER_DESC2 = unsafe { mem::zeroed() }; + unsafe { + (*(adapter as *mut dxgi1_2::IDXGIAdapter2)).GetDesc2(&mut desc); + } + + build_adapter_info( + &desc.Description, + desc.VendorId, + desc.DeviceId, + desc.Flags, + desc.DedicatedVideoMemory, + device, + ) + } + } +} + +pub(crate) fn get_adapter( + idx: u32, + factory: *mut dxgi::IDXGIFactory, + version: DxgiVersion, +) -> Result, winerror::HRESULT> { + let adapter = match version { + DxgiVersion::Dxgi1_0 + | DxgiVersion::Dxgi1_2 + | DxgiVersion::Dxgi1_3 + | DxgiVersion::Dxgi1_4 + | DxgiVersion::Dxgi1_5 => enum_adapters1(idx, factory)?, + }; + + Ok(adapter) +} diff --git a/third_party/rust/gfx-backend-dx11/src/internal.rs b/third_party/rust/gfx-backend-dx11/src/internal.rs index d73f5cb7b4a5..620dc5cd2e09 100644 --- a/third_party/rust/gfx-backend-dx11/src/internal.rs +++ b/third_party/rust/gfx-backend-dx11/src/internal.rs @@ -1,1563 +1,1563 @@ -use auxil::{FastHashMap, FastHashSet, ShaderStage}; -use hal::{command, image, pso}; - -use winapi::{ - shared::minwindef::UINT, - shared::{ - dxgiformat, - minwindef::{FALSE, TRUE}, - winerror, - }, - um::{d3d11, d3dcommon}, -}; - -use wio::com::ComPtr; - -use std::{mem, ptr}; - -use parking_lot::Mutex; -use smallvec::SmallVec; -use spirv_cross::{self, hlsl::ShaderModel}; - -use crate::{conv, shader, Buffer, Image, RenderPassCache}; - -#[repr(C)] -struct BufferCopy { - src: u32, - dst: u32, - _padding: [u32; 2], -} - -#[repr(C)] -struct ImageCopy { - src: [u32; 4], - dst: [u32; 4], -} - -#[repr(C)] -struct BufferImageCopy { - buffer_offset: u32, - buffer_size: [u32; 2], - _padding: u32, - image_offset: [u32; 4], - image_extent: [u32; 4], - // actual size of the target image - image_size: [u32; 4], -} - -#[repr(C)] -struct BufferImageCopyInfo { - buffer: BufferCopy, - image: ImageCopy, - buffer_image: BufferImageCopy, -} - -#[repr(C)] -struct BlitInfo { - offset: [f32; 2], - extent: [f32; 2], - z: f32, - level: f32, -} - -#[repr(C)] -struct PartialClearInfo { - // transmute between the types, easier than juggling all different kinds of fields.. - data: [u32; 4], -} - -// the threadgroup count we use in our copy shaders -const COPY_THREAD_GROUP_X: u32 = 8; -const COPY_THREAD_GROUP_Y: u32 = 8; - -#[derive(Clone, Debug)] -struct ComputeCopyBuffer { - d1_from_buffer: Option>, - // Buffer -> Image2D - d2_from_buffer: Option>, - // Image2D -> Buffer - d2_into_buffer: ComPtr, - scale: (u32, u32), -} - -#[derive(Debug)] -struct ConstantBuffer { - buffer: ComPtr, -} - -impl ConstantBuffer { - unsafe fn update(&mut self, context: &ComPtr, data: T) { - let mut mapped = mem::zeroed::(); - let hr = context.Map( - self.buffer.as_raw() as _, - 0, - d3d11::D3D11_MAP_WRITE_DISCARD, - 0, - &mut mapped, - ); - assert_eq!(winerror::S_OK, hr); - - ptr::copy(&data, mapped.pData as _, 1); - - context.Unmap(self.buffer.as_raw() as _, 0); - } -} - -#[derive(Debug)] -struct MissingComputeInternal { - // Image<->Image not covered by `CopySubresourceRegion` - cs_copy_image_shaders: FastHashSet<(dxgiformat::DXGI_FORMAT, dxgiformat::DXGI_FORMAT)>, - // Image -> Buffer and Buffer -> Image shaders - cs_copy_buffer_shaders: FastHashSet, -} - -#[derive(Debug)] -struct ComputeInternal { - // Image<->Image not covered by `CopySubresourceRegion` - cs_copy_image_shaders: FastHashMap< - (dxgiformat::DXGI_FORMAT, dxgiformat::DXGI_FORMAT), - ComPtr, - >, - // Image -> Buffer and Buffer -> Image shaders - cs_copy_buffer_shaders: FastHashMap, -} - -#[derive(Debug)] -enum PossibleComputeInternal { - Available(ComputeInternal), - Missing(MissingComputeInternal), -} - -// Holds everything we need for fallback implementations of features that are not in DX. -// -// TODO: make struct fields more modular and group them up in structs depending on if it is a -// fallback version or not (eg. Option), should make struct definition and -// `new` function smaller -#[derive(Debug)] -pub struct Internal { - // partial clearing - vs_partial_clear: ComPtr, - ps_partial_clear_float: ComPtr, - ps_partial_clear_uint: ComPtr, - ps_partial_clear_int: ComPtr, - ps_partial_clear_depth: ComPtr, - ps_partial_clear_stencil: ComPtr, - partial_clear_depth_stencil_state: ComPtr, - partial_clear_depth_state: ComPtr, - partial_clear_stencil_state: ComPtr, - - // blitting - vs_blit_2d: ComPtr, - - sampler_nearest: ComPtr, - sampler_linear: ComPtr, - - ps_blit_2d_uint: ComPtr, - ps_blit_2d_int: ComPtr, - ps_blit_2d_float: ComPtr, - - // all compute shader based workarounds, so they can be None when running on an older version without compute shaders. - compute_internal: PossibleComputeInternal, - - // internal constant buffer that is used by internal shaders - internal_buffer: Mutex, - - // public buffer that is used as intermediate storage for some operations (memory invalidation) - pub working_buffer: ComPtr, - pub working_buffer_size: u64, - - pub constant_buffer_count_buffer: - [UINT; d3d11::D3D11_COMMONSHADER_CONSTANT_BUFFER_API_SLOT_COUNT as _], - - /// Command lists are not supported by graphics card and are being emulated. - /// Requires various workarounds to make things work correctly. - pub command_list_emulation: bool, - - pub device_features: hal::Features, - pub device_feature_level: u32, - - pub downlevel: hal::DownlevelProperties, -} - -fn compile_blob( - src: &[u8], - entrypoint: &str, - stage: ShaderStage, - shader_model: ShaderModel, -) -> ComPtr { - unsafe { - ComPtr::from_raw(shader::compile_hlsl_shader(stage, shader_model, entrypoint, src).unwrap()) - } -} - -fn compile_vs( - device: &ComPtr, - src: &[u8], - entrypoint: &str, - shader_model: ShaderModel, -) -> ComPtr { - let bytecode = compile_blob(src, entrypoint, ShaderStage::Vertex, shader_model); - let mut shader = ptr::null_mut(); - let hr = unsafe { - device.CreateVertexShader( - bytecode.GetBufferPointer(), - bytecode.GetBufferSize(), - ptr::null_mut(), - &mut shader as *mut *mut _ as *mut *mut _, - ) - }; - assert_eq!(true, winerror::SUCCEEDED(hr)); - - unsafe { ComPtr::from_raw(shader) } -} - -fn compile_ps( - device: &ComPtr, - src: &[u8], - entrypoint: &str, - shader_model: ShaderModel, -) -> ComPtr { - let bytecode = compile_blob(src, entrypoint, ShaderStage::Fragment, shader_model); - let mut shader = ptr::null_mut(); - let hr = unsafe { - device.CreatePixelShader( - bytecode.GetBufferPointer(), - bytecode.GetBufferSize(), - ptr::null_mut(), - &mut shader as *mut *mut _ as *mut *mut _, - ) - }; - assert_eq!(true, winerror::SUCCEEDED(hr)); - - unsafe { ComPtr::from_raw(shader) } -} - -fn compile_cs( - device: &ComPtr, - src: &[u8], - entrypoint: &str, - shader_model: ShaderModel, -) -> ComPtr { - let bytecode = compile_blob(src, entrypoint, ShaderStage::Compute, shader_model); - let mut shader = ptr::null_mut(); - let hr = unsafe { - device.CreateComputeShader( - bytecode.GetBufferPointer(), - bytecode.GetBufferSize(), - ptr::null_mut(), - &mut shader as *mut *mut _ as *mut *mut _, - ) - }; - assert_eq!(true, winerror::SUCCEEDED(hr)); - - unsafe { ComPtr::from_raw(shader) } -} - -impl Internal { - pub fn new( - device: &ComPtr, - device_features: hal::Features, - device_feature_level: u32, - downlevel: hal::DownlevelProperties, - ) -> Self { - let internal_buffer = { - let desc = d3d11::D3D11_BUFFER_DESC { - ByteWidth: mem::size_of::() as _, - Usage: d3d11::D3D11_USAGE_DYNAMIC, - BindFlags: d3d11::D3D11_BIND_CONSTANT_BUFFER, - CPUAccessFlags: d3d11::D3D11_CPU_ACCESS_WRITE, - MiscFlags: 0, - StructureByteStride: 0, - }; - - let mut buffer = ptr::null_mut(); - let hr = unsafe { - device.CreateBuffer( - &desc, - ptr::null_mut(), - &mut buffer as *mut *mut _ as *mut *mut _, - ) - }; - assert_eq!(true, winerror::SUCCEEDED(hr)); - - unsafe { ComPtr::from_raw(buffer) } - }; - - let (depth_stencil_state, depth_state, stencil_state) = { - let mut depth_state = ptr::null_mut(); - let mut stencil_state = ptr::null_mut(); - let mut depth_stencil_state = ptr::null_mut(); - - let mut desc = d3d11::D3D11_DEPTH_STENCIL_DESC { - DepthEnable: TRUE, - DepthWriteMask: d3d11::D3D11_DEPTH_WRITE_MASK_ALL, - DepthFunc: d3d11::D3D11_COMPARISON_ALWAYS, - StencilEnable: TRUE, - StencilReadMask: 0, - StencilWriteMask: !0, - FrontFace: d3d11::D3D11_DEPTH_STENCILOP_DESC { - StencilFailOp: d3d11::D3D11_STENCIL_OP_REPLACE, - StencilDepthFailOp: d3d11::D3D11_STENCIL_OP_REPLACE, - StencilPassOp: d3d11::D3D11_STENCIL_OP_REPLACE, - StencilFunc: d3d11::D3D11_COMPARISON_ALWAYS, - }, - BackFace: d3d11::D3D11_DEPTH_STENCILOP_DESC { - StencilFailOp: d3d11::D3D11_STENCIL_OP_REPLACE, - StencilDepthFailOp: d3d11::D3D11_STENCIL_OP_REPLACE, - StencilPassOp: d3d11::D3D11_STENCIL_OP_REPLACE, - StencilFunc: d3d11::D3D11_COMPARISON_ALWAYS, - }, - }; - - let hr = unsafe { - device.CreateDepthStencilState( - &desc, - &mut depth_stencil_state as *mut *mut _ as *mut *mut _, - ) - }; - assert_eq!(winerror::S_OK, hr); - - desc.DepthEnable = TRUE; - desc.StencilEnable = FALSE; - - let hr = unsafe { - device - .CreateDepthStencilState(&desc, &mut depth_state as *mut *mut _ as *mut *mut _) - }; - assert_eq!(winerror::S_OK, hr); - - desc.DepthEnable = FALSE; - desc.StencilEnable = TRUE; - - let hr = unsafe { - device.CreateDepthStencilState( - &desc, - &mut stencil_state as *mut *mut _ as *mut *mut _, - ) - }; - assert_eq!(winerror::S_OK, hr); - - unsafe { - ( - ComPtr::from_raw(depth_stencil_state), - ComPtr::from_raw(depth_state), - ComPtr::from_raw(stencil_state), - ) - } - }; - - let (sampler_nearest, sampler_linear) = { - let mut desc = d3d11::D3D11_SAMPLER_DESC { - Filter: d3d11::D3D11_FILTER_MIN_MAG_MIP_POINT, - AddressU: d3d11::D3D11_TEXTURE_ADDRESS_CLAMP, - AddressV: d3d11::D3D11_TEXTURE_ADDRESS_CLAMP, - AddressW: d3d11::D3D11_TEXTURE_ADDRESS_CLAMP, - MipLODBias: 0f32, - MaxAnisotropy: 0, - ComparisonFunc: 0, - BorderColor: [0f32; 4], - MinLOD: 0f32, - MaxLOD: d3d11::D3D11_FLOAT32_MAX, - }; - - let mut nearest = ptr::null_mut(); - let mut linear = ptr::null_mut(); - - assert_eq!(winerror::S_OK, unsafe { - device.CreateSamplerState(&desc, &mut nearest as *mut *mut _ as *mut *mut _) - }); - - desc.Filter = d3d11::D3D11_FILTER_MIN_MAG_MIP_LINEAR; - - assert_eq!(winerror::S_OK, unsafe { - device.CreateSamplerState(&desc, &mut linear as *mut *mut _ as *mut *mut _) - }); - - unsafe { (ComPtr::from_raw(nearest), ComPtr::from_raw(linear)) } - }; - - let (working_buffer, working_buffer_size) = { - let working_buffer_size = 1 << 16; - - let desc = d3d11::D3D11_BUFFER_DESC { - ByteWidth: working_buffer_size, - Usage: d3d11::D3D11_USAGE_STAGING, - BindFlags: 0, - CPUAccessFlags: d3d11::D3D11_CPU_ACCESS_READ | d3d11::D3D11_CPU_ACCESS_WRITE, - MiscFlags: 0, - StructureByteStride: 0, - }; - let mut working_buffer = ptr::null_mut(); - - assert_eq!(winerror::S_OK, unsafe { - device.CreateBuffer( - &desc, - ptr::null_mut(), - &mut working_buffer as *mut *mut _ as *mut *mut _, - ) - }); - - ( - unsafe { ComPtr::from_raw(working_buffer) }, - working_buffer_size, - ) - }; - - let compute_shaders = if device_feature_level >= d3dcommon::D3D_FEATURE_LEVEL_11_0 { - true - } else { - // FL10 does support compute shaders but we need typed UAVs, which FL10 compute shaders don't support, so we might as well not have them. - false - }; - - let shader_model = conv::map_feature_level_to_shader_model(device_feature_level); - - let compute_internal = if compute_shaders { - let copy_shaders = include_bytes!("../shaders/copy.hlsl"); - let mut cs_copy_image_shaders = FastHashMap::default(); - cs_copy_image_shaders.insert( - ( - dxgiformat::DXGI_FORMAT_R8G8_UINT, - dxgiformat::DXGI_FORMAT_R16_UINT, - ), - compile_cs( - device, - copy_shaders, - "cs_copy_image2d_r8g8_image2d_r16", - shader_model, - ), - ); - cs_copy_image_shaders.insert( - ( - dxgiformat::DXGI_FORMAT_R16_UINT, - dxgiformat::DXGI_FORMAT_R8G8_UINT, - ), - compile_cs( - device, - copy_shaders, - "cs_copy_image2d_r16_image2d_r8g8", - shader_model, - ), - ); - cs_copy_image_shaders.insert( - ( - dxgiformat::DXGI_FORMAT_R8G8B8A8_UINT, - dxgiformat::DXGI_FORMAT_R32_UINT, - ), - compile_cs( - device, - copy_shaders, - "cs_copy_image2d_r8g8b8a8_image2d_r32", - shader_model, - ), - ); - cs_copy_image_shaders.insert( - ( - dxgiformat::DXGI_FORMAT_R8G8B8A8_UINT, - dxgiformat::DXGI_FORMAT_R16G16_UINT, - ), - compile_cs( - device, - copy_shaders, - "cs_copy_image2d_r8g8b8a8_image2d_r16g16", - shader_model, - ), - ); - cs_copy_image_shaders.insert( - ( - dxgiformat::DXGI_FORMAT_R16G16_UINT, - dxgiformat::DXGI_FORMAT_R32_UINT, - ), - compile_cs( - device, - copy_shaders, - "cs_copy_image2d_r16g16_image2d_r32", - shader_model, - ), - ); - cs_copy_image_shaders.insert( - ( - dxgiformat::DXGI_FORMAT_R16G16_UINT, - dxgiformat::DXGI_FORMAT_R8G8B8A8_UINT, - ), - compile_cs( - device, - copy_shaders, - "cs_copy_image2d_r16g16_image2d_r8g8b8a8", - shader_model, - ), - ); - cs_copy_image_shaders.insert( - ( - dxgiformat::DXGI_FORMAT_R32_UINT, - dxgiformat::DXGI_FORMAT_R16G16_UINT, - ), - compile_cs( - device, - copy_shaders, - "cs_copy_image2d_r32_image2d_r16g16", - shader_model, - ), - ); - cs_copy_image_shaders.insert( - ( - dxgiformat::DXGI_FORMAT_R32_UINT, - dxgiformat::DXGI_FORMAT_R8G8B8A8_UINT, - ), - compile_cs( - device, - copy_shaders, - "cs_copy_image2d_r32_image2d_r8g8b8a8", - shader_model, - ), - ); - - let mut cs_copy_buffer_shaders = FastHashMap::default(); - cs_copy_buffer_shaders.insert( - dxgiformat::DXGI_FORMAT_R32G32B32A32_UINT, - ComputeCopyBuffer { - d1_from_buffer: None, - d2_from_buffer: Some(compile_cs( - device, - copy_shaders, - "cs_copy_buffer_image2d_r32g32b32a32", - shader_model, - )), - d2_into_buffer: compile_cs( - device, - copy_shaders, - "cs_copy_image2d_r32g32b32a32_buffer", - shader_model, - ), - scale: (1, 1), - }, - ); - cs_copy_buffer_shaders.insert( - dxgiformat::DXGI_FORMAT_R32G32_UINT, - ComputeCopyBuffer { - d1_from_buffer: None, - d2_from_buffer: Some(compile_cs( - device, - copy_shaders, - "cs_copy_buffer_image2d_r32g32", - shader_model, - )), - d2_into_buffer: compile_cs( - device, - copy_shaders, - "cs_copy_image2d_r32g32_buffer", - shader_model, - ), - scale: (1, 1), - }, - ); - cs_copy_buffer_shaders.insert( - dxgiformat::DXGI_FORMAT_R32_UINT, - ComputeCopyBuffer { - d1_from_buffer: Some(compile_cs( - device, - copy_shaders, - "cs_copy_buffer_image1d_r32", - shader_model, - )), - d2_from_buffer: Some(compile_cs( - device, - copy_shaders, - "cs_copy_buffer_image2d_r32", - shader_model, - )), - d2_into_buffer: compile_cs( - device, - copy_shaders, - "cs_copy_image2d_r32_buffer", - shader_model, - ), - scale: (1, 1), - }, - ); - cs_copy_buffer_shaders.insert( - dxgiformat::DXGI_FORMAT_R16G16B16A16_UINT, - ComputeCopyBuffer { - d1_from_buffer: None, - d2_from_buffer: Some(compile_cs( - device, - copy_shaders, - "cs_copy_buffer_image2d_r16g16b16a16", - shader_model, - )), - d2_into_buffer: compile_cs( - device, - copy_shaders, - "cs_copy_image2d_r16g16b16a16_buffer", - shader_model, - ), - scale: (1, 1), - }, - ); - cs_copy_buffer_shaders.insert( - dxgiformat::DXGI_FORMAT_R16G16_UINT, - ComputeCopyBuffer { - d1_from_buffer: None, - d2_from_buffer: Some(compile_cs( - device, - copy_shaders, - "cs_copy_buffer_image2d_r16g16", - shader_model, - )), - d2_into_buffer: compile_cs( - device, - copy_shaders, - "cs_copy_image2d_r16g16_buffer", - shader_model, - ), - scale: (1, 1), - }, - ); - cs_copy_buffer_shaders.insert( - dxgiformat::DXGI_FORMAT_R16_UINT, - ComputeCopyBuffer { - d1_from_buffer: None, - d2_from_buffer: Some(compile_cs( - device, - copy_shaders, - "cs_copy_buffer_image2d_r16", - shader_model, - )), - d2_into_buffer: compile_cs( - device, - copy_shaders, - "cs_copy_image2d_r16_buffer", - shader_model, - ), - scale: (2, 1), - }, - ); - cs_copy_buffer_shaders.insert( - dxgiformat::DXGI_FORMAT_B8G8R8A8_UNORM, - ComputeCopyBuffer { - d1_from_buffer: None, - d2_from_buffer: None, - d2_into_buffer: compile_cs( - device, - copy_shaders, - "cs_copy_image2d_b8g8r8a8_buffer", - shader_model, - ), - scale: (1, 1), - }, - ); - cs_copy_buffer_shaders.insert( - dxgiformat::DXGI_FORMAT_R8G8B8A8_UINT, - ComputeCopyBuffer { - d1_from_buffer: Some(compile_cs( - device, - copy_shaders, - "cs_copy_buffer_image1d_r8g8b8a8", - shader_model, - )), - d2_from_buffer: Some(compile_cs( - device, - copy_shaders, - "cs_copy_buffer_image2d_r8g8b8a8", - shader_model, - )), - d2_into_buffer: compile_cs( - device, - copy_shaders, - "cs_copy_image2d_r8g8b8a8_buffer", - shader_model, - ), - scale: (1, 1), - }, - ); - cs_copy_buffer_shaders.insert( - dxgiformat::DXGI_FORMAT_R8G8_UINT, - ComputeCopyBuffer { - d1_from_buffer: Some(compile_cs( - device, - copy_shaders, - "cs_copy_buffer_image1d_r8g8", - shader_model, - )), - d2_from_buffer: Some(compile_cs( - device, - copy_shaders, - "cs_copy_buffer_image2d_r8g8", - shader_model, - )), - d2_into_buffer: compile_cs( - device, - copy_shaders, - "cs_copy_image2d_r8g8_buffer", - shader_model, - ), - scale: (2, 1), - }, - ); - cs_copy_buffer_shaders.insert( - dxgiformat::DXGI_FORMAT_R8_UINT, - ComputeCopyBuffer { - d1_from_buffer: Some(compile_cs( - device, - copy_shaders, - "cs_copy_buffer_image1d_r8", - shader_model, - )), - d2_from_buffer: Some(compile_cs( - device, - copy_shaders, - "cs_copy_buffer_image2d_r8", - shader_model, - )), - d2_into_buffer: compile_cs( - device, - copy_shaders, - "cs_copy_image2d_r8_buffer", - shader_model, - ), - scale: (4, 1), - }, - ); - - PossibleComputeInternal::Available(ComputeInternal { - cs_copy_buffer_shaders, - cs_copy_image_shaders, - }) - } else { - let mut cs_copy_image_shaders = FastHashSet::default(); - cs_copy_image_shaders.insert(( - dxgiformat::DXGI_FORMAT_R8G8_UINT, - dxgiformat::DXGI_FORMAT_R16_UINT, - )); - cs_copy_image_shaders.insert(( - dxgiformat::DXGI_FORMAT_R16_UINT, - dxgiformat::DXGI_FORMAT_R8G8_UINT, - )); - cs_copy_image_shaders.insert(( - dxgiformat::DXGI_FORMAT_R8G8B8A8_UINT, - dxgiformat::DXGI_FORMAT_R32_UINT, - )); - cs_copy_image_shaders.insert(( - dxgiformat::DXGI_FORMAT_R8G8B8A8_UINT, - dxgiformat::DXGI_FORMAT_R16G16_UINT, - )); - cs_copy_image_shaders.insert(( - dxgiformat::DXGI_FORMAT_R16G16_UINT, - dxgiformat::DXGI_FORMAT_R32_UINT, - )); - cs_copy_image_shaders.insert(( - dxgiformat::DXGI_FORMAT_R16G16_UINT, - dxgiformat::DXGI_FORMAT_R8G8B8A8_UINT, - )); - cs_copy_image_shaders.insert(( - dxgiformat::DXGI_FORMAT_R32_UINT, - dxgiformat::DXGI_FORMAT_R16G16_UINT, - )); - cs_copy_image_shaders.insert(( - dxgiformat::DXGI_FORMAT_R32_UINT, - dxgiformat::DXGI_FORMAT_R8G8B8A8_UINT, - )); - - let mut cs_copy_buffer_shaders = FastHashSet::default(); - cs_copy_buffer_shaders.insert(dxgiformat::DXGI_FORMAT_R32G32B32A32_UINT); - cs_copy_buffer_shaders.insert(dxgiformat::DXGI_FORMAT_R32G32_UINT); - cs_copy_buffer_shaders.insert(dxgiformat::DXGI_FORMAT_R32_UINT); - cs_copy_buffer_shaders.insert(dxgiformat::DXGI_FORMAT_R16G16B16A16_UINT); - cs_copy_buffer_shaders.insert(dxgiformat::DXGI_FORMAT_R16G16_UINT); - cs_copy_buffer_shaders.insert(dxgiformat::DXGI_FORMAT_R16_UINT); - cs_copy_buffer_shaders.insert(dxgiformat::DXGI_FORMAT_B8G8R8A8_UNORM); - cs_copy_buffer_shaders.insert(dxgiformat::DXGI_FORMAT_R8G8B8A8_UINT); - cs_copy_buffer_shaders.insert(dxgiformat::DXGI_FORMAT_R8G8_UINT); - cs_copy_buffer_shaders.insert(dxgiformat::DXGI_FORMAT_R8_UINT); - - PossibleComputeInternal::Missing(MissingComputeInternal { - cs_copy_image_shaders, - cs_copy_buffer_shaders, - }) - }; - - let mut threading_capability: d3d11::D3D11_FEATURE_DATA_THREADING = - unsafe { mem::zeroed() }; - let hr = unsafe { - device.CheckFeatureSupport( - d3d11::D3D11_FEATURE_THREADING, - &mut threading_capability as *mut _ as *mut _, - mem::size_of::() as _, - ) - }; - assert_eq!(hr, winerror::S_OK); - - let command_list_emulation = !(threading_capability.DriverCommandLists >= 1); - if command_list_emulation { - info!("D3D11 command list emulation is active"); - } - - let clear_shaders = include_bytes!("../shaders/clear.hlsl"); - let blit_shaders = include_bytes!("../shaders/blit.hlsl"); - - Internal { - vs_partial_clear: compile_vs(device, clear_shaders, "vs_partial_clear", shader_model), - ps_partial_clear_float: compile_ps( - device, - clear_shaders, - "ps_partial_clear_float", - shader_model, - ), - ps_partial_clear_uint: compile_ps( - device, - clear_shaders, - "ps_partial_clear_uint", - shader_model, - ), - ps_partial_clear_int: compile_ps( - device, - clear_shaders, - "ps_partial_clear_int", - shader_model, - ), - ps_partial_clear_depth: compile_ps( - device, - clear_shaders, - "ps_partial_clear_depth", - shader_model, - ), - ps_partial_clear_stencil: compile_ps( - device, - clear_shaders, - "ps_partial_clear_stencil", - shader_model, - ), - partial_clear_depth_stencil_state: depth_stencil_state, - partial_clear_depth_state: depth_state, - partial_clear_stencil_state: stencil_state, - - vs_blit_2d: compile_vs(device, blit_shaders, "vs_blit_2d", shader_model), - - sampler_nearest, - sampler_linear, - - ps_blit_2d_uint: compile_ps(device, blit_shaders, "ps_blit_2d_uint", shader_model), - ps_blit_2d_int: compile_ps(device, blit_shaders, "ps_blit_2d_int", shader_model), - ps_blit_2d_float: compile_ps(device, blit_shaders, "ps_blit_2d_float", shader_model), - - compute_internal, - - internal_buffer: Mutex::new(ConstantBuffer { - buffer: internal_buffer, - }), - working_buffer, - working_buffer_size: working_buffer_size as _, - - constant_buffer_count_buffer: [4096_u32; - d3d11::D3D11_COMMONSHADER_CONSTANT_BUFFER_API_SLOT_COUNT as _], - - command_list_emulation, - - device_features, - device_feature_level, - - downlevel, - } - } - - pub fn copy_image_2d( - &self, - context: &ComPtr, - src: &Image, - dst: &Image, - regions: T, - ) where - T: Iterator, - { - let key = ( - src.decomposed_format.copy_srv.unwrap(), - dst.decomposed_format.copy_srv.unwrap(), - ); - - let compute_shader_copy = if let PossibleComputeInternal::Available(ref compute_internal) = - self.compute_internal - { - compute_internal.cs_copy_image_shaders.get(&key) - } else if let PossibleComputeInternal::Missing(ref compute_internal) = self.compute_internal - { - if compute_internal.cs_copy_image_shaders.contains(&key) { - panic!("Tried to copy between images types ({:?} -> {:?}) that require Compute Shaders under FL9 or FL10", src.format, dst.format); - } else { - None - } - } else { - None - }; - - if let Some(shader) = compute_shader_copy { - // Some formats cant go through default path, since they cant - // be cast between formats of different component types (eg. - // Rg16 <-> Rgba8) - - // TODO: subresources - let srv = src.internal.copy_srv.clone().unwrap().as_raw(); - let mut const_buf = self.internal_buffer.lock(); - - unsafe { - context.CSSetShader(shader.as_raw(), ptr::null_mut(), 0); - context.CSSetConstantBuffers(0, 1, &const_buf.buffer.as_raw()); - context.CSSetShaderResources(0, 1, [srv].as_ptr()); - - for info in regions { - let image = ImageCopy { - src: [ - info.src_offset.x as _, - info.src_offset.y as _, - info.src_offset.z as _, - 0, - ], - dst: [ - info.dst_offset.x as _, - info.dst_offset.y as _, - info.dst_offset.z as _, - 0, - ], - }; - const_buf.update( - context, - BufferImageCopyInfo { - image, - ..mem::zeroed() - }, - ); - - let uav = dst.get_uav(info.dst_subresource.level, 0).unwrap().as_raw(); - context.CSSetUnorderedAccessViews(0, 1, [uav].as_ptr(), ptr::null_mut()); - - context.Dispatch(info.extent.width as u32, info.extent.height as u32, 1); - } - - // unbind external resources - context.CSSetShaderResources(0, 1, [ptr::null_mut(); 1].as_ptr()); - context.CSSetUnorderedAccessViews( - 0, - 1, - [ptr::null_mut(); 1].as_ptr(), - ptr::null_mut(), - ); - } - } else { - // Default copy path - for info in regions { - assert_eq!( - src.decomposed_format.typeless, dst.decomposed_format.typeless, - "DX11 backend cannot copy between underlying image formats: {} to {}.", - src.decomposed_format.typeless, dst.decomposed_format.typeless, - ); - - // Formats are the same per above assert, only need to do it for one of the formats - let full_copy_only = - src.format.is_depth() || src.format.is_stencil() || src.kind.num_samples() > 1; - - let copy_box = if full_copy_only { - let offset_zero = info.src_offset.x == 0 - && info.src_offset.y == 0 - && info.src_offset.z == 0 - && info.dst_offset.x == 0 - && info.dst_offset.y == 0 - && info.dst_offset.z == 0; - - let full_extent = info.extent == src.kind.extent(); - - if !offset_zero || !full_extent { - warn!("image to image copies of depth-stencil or multisampled textures must copy the whole resource. Ignoring non-zero offset or non-full extent."); - } - - None - } else { - Some(d3d11::D3D11_BOX { - left: info.src_offset.x as _, - top: info.src_offset.y as _, - front: info.src_offset.z as _, - right: info.src_offset.x as u32 + info.extent.width as u32, - bottom: info.src_offset.y as u32 + info.extent.height as u32, - back: info.src_offset.z as u32 + info.extent.depth as u32, - }) - }; - - // TODO: layer subresources - unsafe { - context.CopySubresourceRegion( - dst.internal.raw, - src.calc_subresource(info.src_subresource.level as _, 0), - info.dst_offset.x as _, - info.dst_offset.y as _, - info.dst_offset.z as _, - src.internal.raw, - dst.calc_subresource(info.dst_subresource.level as _, 0), - copy_box.map_or_else(ptr::null, |b| &b), - ); - } - } - } - } - - pub fn copy_image_to_buffer( - &self, - context: &ComPtr, - src: &Image, - dst: &Buffer, - regions: T, - ) where - T: Iterator, - { - let _scope = debug_scope!( - context, - "Image (format={:?},kind={:?}) => Buffer", - src.format, - src.kind - ); - - let shader = if let PossibleComputeInternal::Available(ref compute_internal) = - self.compute_internal - { - compute_internal - .cs_copy_buffer_shaders - .get(&src.decomposed_format.copy_srv.unwrap()) - .unwrap_or_else(|| panic!("The DX11 backend does not currently support copying from an image of format {:?} to a buffer", src.format)) - .clone() - } else { - // TODO(cwfitzgerald): Can we fall back to a inherent D3D11 method when copying to a CPU only buffer. - panic!("Tried to copy from an image to a buffer under FL9 or FL10"); - }; - - let srv = src.internal.copy_srv.clone().unwrap().as_raw(); - let uav = dst.internal.uav.unwrap(); - let format_desc = src.format.base_format().0.desc(); - let bytes_per_texel = format_desc.bits as u32 / 8; - let mut const_buf = self.internal_buffer.lock(); - - unsafe { - context.CSSetShader(shader.d2_into_buffer.as_raw(), ptr::null_mut(), 0); - context.CSSetConstantBuffers(0, 1, &const_buf.buffer.as_raw()); - - context.CSSetShaderResources(0, 1, [srv].as_ptr()); - context.CSSetUnorderedAccessViews(0, 1, [uav].as_ptr(), ptr::null_mut()); - - for info in regions { - let size = src.kind.extent(); - let buffer_image = BufferImageCopy { - buffer_offset: info.buffer_offset as _, - buffer_size: [info.buffer_width, info.buffer_height], - _padding: 0, - image_offset: [ - info.image_offset.x as _, - info.image_offset.y as _, - (info.image_offset.z + info.image_layers.layers.start as i32) as _, - 0, - ], - image_extent: [ - info.image_extent.width, - info.image_extent.height, - info.image_extent.depth, - 0, - ], - image_size: [size.width, size.height, size.depth, 0], - }; - - const_buf.update( - context, - BufferImageCopyInfo { - buffer_image, - ..mem::zeroed() - }, - ); - - debug_marker!(context, "{:?}", info); - - context.Dispatch( - ((info.image_extent.width + (COPY_THREAD_GROUP_X - 1)) - / COPY_THREAD_GROUP_X - / shader.scale.0) - .max(1), - ((info.image_extent.height + (COPY_THREAD_GROUP_X - 1)) - / COPY_THREAD_GROUP_Y - / shader.scale.1) - .max(1), - 1, - ); - - if let Some(disjoint_cb) = dst.internal.disjoint_cb { - let total_size = info.image_extent.depth - * (info.buffer_height * info.buffer_width * bytes_per_texel); - let copy_box = d3d11::D3D11_BOX { - left: info.buffer_offset as u32, - top: 0, - front: 0, - right: info.buffer_offset as u32 + total_size, - bottom: 1, - back: 1, - }; - - context.CopySubresourceRegion( - disjoint_cb as _, - 0, - info.buffer_offset as _, - 0, - 0, - dst.internal.raw as _, - 0, - ©_box, - ); - } - } - - // unbind external resources - context.CSSetShaderResources(0, 1, [ptr::null_mut(); 1].as_ptr()); - context.CSSetUnorderedAccessViews(0, 1, [ptr::null_mut(); 1].as_ptr(), ptr::null_mut()); - } - } - - pub fn copy_buffer_to_image( - &self, - context: &ComPtr, - src: &Buffer, - dst: &Image, - regions: T, - ) where - T: Iterator, - { - let _scope = debug_scope!( - context, - "Buffer => Image (format={:?},kind={:?})", - dst.format, - dst.kind - ); - - let compute_internal = match self.compute_internal { - PossibleComputeInternal::Available(ref available) => Some(available), - PossibleComputeInternal::Missing(_) => None, - }; - - // NOTE: we have two separate paths for Buffer -> Image transfers. we need to special case - // uploads to compressed formats through `UpdateSubresource` since we cannot get a - // UAV of any compressed format. - - let format_desc = dst.format.base_format().0.desc(); - let is_compressed = format_desc.is_compressed(); - if format_desc.is_compressed() || compute_internal.is_none() { - // we dont really care about non-4x4 block formats.. - if is_compressed { - assert_eq!(format_desc.dim, (4, 4)); - assert!( - !src.memory_ptr.is_null(), - "Only CPU to GPU upload of compressed texture is currently supported" - ); - } else if compute_internal.is_none() { - assert!( - !src.memory_ptr.is_null(), - "Only CPU to GPU upload of textures is supported under FL9 or FL10" - ); - } - - for info in regions { - let bytes_per_texel = format_desc.bits as u32 / 8; - - let bounds = d3d11::D3D11_BOX { - left: info.image_offset.x as _, - top: info.image_offset.y as _, - front: info.image_offset.z as _, - right: info.image_offset.x as u32 + info.image_extent.width, - bottom: info.image_offset.y as u32 + info.image_extent.height, - back: info.image_offset.z as u32 + info.image_extent.depth, - }; - - let row_pitch = - bytes_per_texel * info.image_extent.width / format_desc.dim.0 as u32; - let depth_pitch = row_pitch * info.image_extent.height / format_desc.dim.1 as u32; - - for layer in info.image_layers.layers.clone() { - let layer_offset = layer - info.image_layers.layers.start; - - unsafe { - context.UpdateSubresource( - dst.internal.raw, - dst.calc_subresource(info.image_layers.level as _, layer as _), - &bounds, - src.memory_ptr.offset( - src.bound_range.start as isize - + info.buffer_offset as isize - + depth_pitch as isize * layer_offset as isize, - ) as _, - row_pitch, - depth_pitch, - ); - } - } - } - } else { - let shader = compute_internal - .unwrap() - .cs_copy_buffer_shaders - .get(&dst.decomposed_format.copy_uav.unwrap()) - .unwrap_or_else(|| panic!("The DX11 backend does not currently support copying from a buffer to an image of format {:?}", dst.format)) - .clone(); - - let srv = src.internal.srv.unwrap(); - let mut const_buf = self.internal_buffer.lock(); - let shader_raw = match dst.kind { - image::Kind::D1(..) => shader.d1_from_buffer.unwrap().as_raw(), - image::Kind::D2(..) => shader.d2_from_buffer.unwrap().as_raw(), - image::Kind::D3(..) => panic!("Copies into 3D images are not supported"), - }; - - unsafe { - context.CSSetShader(shader_raw, ptr::null_mut(), 0); - context.CSSetConstantBuffers(0, 1, &const_buf.buffer.as_raw()); - context.CSSetShaderResources(0, 1, [srv].as_ptr()); - - for info in regions { - let size = dst.kind.extent(); - let buffer_image = BufferImageCopy { - buffer_offset: info.buffer_offset as _, - buffer_size: [info.buffer_width, info.buffer_height], - _padding: 0, - image_offset: [ - info.image_offset.x as _, - info.image_offset.y as _, - (info.image_offset.z + info.image_layers.layers.start as i32) as _, - 0, - ], - image_extent: [ - info.image_extent.width, - info.image_extent.height, - info.image_extent.depth, - 0, - ], - image_size: [size.width, size.height, size.depth, 0], - }; - - const_buf.update( - context, - BufferImageCopyInfo { - buffer_image, - ..mem::zeroed() - }, - ); - - debug_marker!(context, "{:?}", info); - - // TODO: multiple layers? do we introduce a stride and do multiple dispatch - // calls or handle this in the shader? (use z component in dispatch call - // - // NOTE: right now our copy UAV is a 2D array, and we set the layer in the - // `update_buffer_image` call above - let uav = dst - .get_uav( - info.image_layers.level, - 0, /*info.image_layers.layers.start*/ - ) - .unwrap() - .as_raw(); - context.CSSetUnorderedAccessViews(0, 1, [uav].as_ptr(), ptr::null_mut()); - - context.Dispatch( - ((info.image_extent.width + (COPY_THREAD_GROUP_X - 1)) - / COPY_THREAD_GROUP_X - / shader.scale.0) - .max(1), - ((info.image_extent.height + (COPY_THREAD_GROUP_X - 1)) - / COPY_THREAD_GROUP_Y - / shader.scale.1) - .max(1), - 1, - ); - } - - // unbind external resources - context.CSSetShaderResources(0, 1, [ptr::null_mut(); 1].as_ptr()); - context.CSSetUnorderedAccessViews( - 0, - 1, - [ptr::null_mut(); 1].as_ptr(), - ptr::null_mut(), - ); - } - } - } - - fn find_blit_shader(&self, src: &Image) -> Option<*mut d3d11::ID3D11PixelShader> { - use crate::format::ChannelType as Ct; - - match src.format.base_format().1 { - Ct::Uint => Some(self.ps_blit_2d_uint.as_raw()), - Ct::Sint => Some(self.ps_blit_2d_int.as_raw()), - Ct::Unorm | Ct::Snorm | Ct::Sfloat | Ct::Srgb => Some(self.ps_blit_2d_float.as_raw()), - Ct::Ufloat | Ct::Uscaled | Ct::Sscaled => None, - } - } - - pub fn blit_2d_image( - &self, - context: &ComPtr, - src: &Image, - dst: &Image, - filter: image::Filter, - regions: T, - ) where - T: Iterator, - { - use std::cmp; - - let _scope = debug_scope!( - context, - "Blit: Image (format={:?},kind={:?}) => Image (format={:?},kind={:?})", - src.format, - src.kind, - dst.format, - dst.kind - ); - - let shader = self.find_blit_shader(src).unwrap(); - - let srv = src.internal.srv.clone().unwrap().as_raw(); - let mut const_buf = self.internal_buffer.lock(); - - unsafe { - context.IASetPrimitiveTopology(d3dcommon::D3D11_PRIMITIVE_TOPOLOGY_TRIANGLELIST); - context.VSSetShader(self.vs_blit_2d.as_raw(), ptr::null_mut(), 0); - context.VSSetConstantBuffers(0, 1, [const_buf.buffer.as_raw()].as_ptr()); - context.PSSetShader(shader, ptr::null_mut(), 0); - context.PSSetShaderResources(0, 1, [srv].as_ptr()); - context.PSSetSamplers( - 0, - 1, - match filter { - image::Filter::Nearest => [self.sampler_nearest.as_raw()], - image::Filter::Linear => [self.sampler_linear.as_raw()], - } - .as_ptr(), - ); - - for info in regions { - let blit_info = { - let (sx, dx) = if info.dst_bounds.start.x > info.dst_bounds.end.x { - ( - info.src_bounds.end.x, - info.src_bounds.start.x - info.src_bounds.end.x, - ) - } else { - ( - info.src_bounds.start.x, - info.src_bounds.end.x - info.src_bounds.start.x, - ) - }; - let (sy, dy) = if info.dst_bounds.start.y > info.dst_bounds.end.y { - ( - info.src_bounds.end.y, - info.src_bounds.start.y - info.src_bounds.end.y, - ) - } else { - ( - info.src_bounds.start.y, - info.src_bounds.end.y - info.src_bounds.start.y, - ) - }; - let image::Extent { width, height, .. } = - src.kind.level_extent(info.src_subresource.level); - BlitInfo { - offset: [sx as f32 / width as f32, sy as f32 / height as f32], - extent: [dx as f32 / width as f32, dy as f32 / height as f32], - z: 0f32, // TODO - level: info.src_subresource.level as _, - } - }; - - const_buf.update(context, blit_info); - - // TODO: more layers - let rtv = dst - .get_rtv( - info.dst_subresource.level, - info.dst_subresource.layers.start, - ) - .unwrap() - .as_raw(); - - context.RSSetViewports( - 1, - [d3d11::D3D11_VIEWPORT { - TopLeftX: cmp::min(info.dst_bounds.start.x, info.dst_bounds.end.x) as _, - TopLeftY: cmp::min(info.dst_bounds.start.y, info.dst_bounds.end.y) as _, - Width: (info.dst_bounds.end.x - info.dst_bounds.start.x).abs() as _, - Height: (info.dst_bounds.end.y - info.dst_bounds.start.y).abs() as _, - MinDepth: 0.0f32, - MaxDepth: 1.0f32, - }] - .as_ptr(), - ); - context.OMSetRenderTargets(1, [rtv].as_ptr(), ptr::null_mut()); - context.Draw(3, 0); - } - - context.PSSetShaderResources(0, 1, [ptr::null_mut()].as_ptr()); - context.OMSetRenderTargets(1, [ptr::null_mut()].as_ptr(), ptr::null_mut()); - } - } - - pub fn clear_attachments( - &self, - context: &ComPtr, - clears: T, - rects: U, - cache: &RenderPassCache, - ) where - T: Iterator, - U: Iterator, - { - use hal::format::ChannelType as Ct; - let _scope = debug_scope!(context, "ClearAttachments"); - - let clear_rects: SmallVec<[pso::ClearRect; 8]> = rects.collect(); - let mut const_buf = self.internal_buffer.lock(); - - unsafe { - context.IASetPrimitiveTopology(d3dcommon::D3D11_PRIMITIVE_TOPOLOGY_TRIANGLELIST); - context.IASetInputLayout(ptr::null_mut()); - context.VSSetShader(self.vs_partial_clear.as_raw(), ptr::null_mut(), 0); - context.PSSetConstantBuffers(0, 1, [const_buf.buffer.as_raw()].as_ptr()); - } - - let subpass = &cache.render_pass.subpasses[cache.current_subpass as usize]; - - for clear in clears { - let _scope = debug_scope!(context, "{:?}", clear); - - match clear { - command::AttachmentClear::Color { index, value } => { - unsafe { - const_buf.update( - context, - PartialClearInfo { - data: mem::transmute(value), - }, - ) - }; - - let attachment = { - let rtv_id = subpass.color_attachments[index]; - &cache.attachments[rtv_id.0].view - }; - - unsafe { - context.OMSetRenderTargets( - 1, - [attachment.rtv_handle.unwrap()].as_ptr(), - ptr::null_mut(), - ); - } - - let shader = match attachment.format.base_format().1 { - Ct::Uint => self.ps_partial_clear_uint.as_raw(), - Ct::Sint => self.ps_partial_clear_int.as_raw(), - _ => self.ps_partial_clear_float.as_raw(), - }; - unsafe { context.PSSetShader(shader, ptr::null_mut(), 0) }; - - for clear_rect in &clear_rects { - let viewport = conv::map_viewport(&pso::Viewport { - rect: clear_rect.rect, - depth: 0f32..1f32, - }); - - debug_marker!(context, "{:?}", clear_rect.rect); - - unsafe { - context.RSSetViewports(1, [viewport].as_ptr()); - context.Draw(3, 0); - } - } - } - command::AttachmentClear::DepthStencil { depth, stencil } => { - unsafe { - const_buf.update( - context, - PartialClearInfo { - data: [ - mem::transmute(depth.unwrap_or(0f32)), - stencil.unwrap_or(0), - 0, - 0, - ], - }, - ) - }; - - let attachment = { - let dsv_id = subpass.depth_stencil_attachment.unwrap(); - &cache.attachments[dsv_id.0].view - }; - - unsafe { - match (depth, stencil) { - (Some(_), Some(stencil)) => { - context.OMSetDepthStencilState( - self.partial_clear_depth_stencil_state.as_raw(), - stencil, - ); - context.PSSetShader( - self.ps_partial_clear_depth.as_raw(), - ptr::null_mut(), - 0, - ); - } - - (Some(_), None) => { - context.OMSetDepthStencilState( - self.partial_clear_depth_state.as_raw(), - 0, - ); - context.PSSetShader( - self.ps_partial_clear_depth.as_raw(), - ptr::null_mut(), - 0, - ); - } - - (None, Some(stencil)) => { - context.OMSetDepthStencilState( - self.partial_clear_stencil_state.as_raw(), - stencil, - ); - context.PSSetShader( - self.ps_partial_clear_stencil.as_raw(), - ptr::null_mut(), - 0, - ); - } - (None, None) => {} - } - - context.OMSetRenderTargets( - 0, - ptr::null_mut(), - attachment.dsv_handle.unwrap(), - ); - context.PSSetShader( - self.ps_partial_clear_depth.as_raw(), - ptr::null_mut(), - 0, - ); - } - - for clear_rect in &clear_rects { - let viewport = conv::map_viewport(&pso::Viewport { - rect: clear_rect.rect, - depth: 0f32..1f32, - }); - - unsafe { - context.RSSetViewports(1, [viewport].as_ptr()); - context.Draw(3, 0); - } - } - } - } - } - } -} +use auxil::{FastHashMap, FastHashSet, ShaderStage}; +use hal::{command, image, pso}; + +use winapi::{ + shared::minwindef::UINT, + shared::{ + dxgiformat, + minwindef::{FALSE, TRUE}, + winerror, + }, + um::{d3d11, d3dcommon}, +}; + +use wio::com::ComPtr; + +use std::{mem, ptr}; + +use parking_lot::Mutex; +use smallvec::SmallVec; +use spirv_cross::{self, hlsl::ShaderModel}; + +use crate::{conv, shader, Buffer, Image, RenderPassCache}; + +#[repr(C)] +struct BufferCopy { + src: u32, + dst: u32, + _padding: [u32; 2], +} + +#[repr(C)] +struct ImageCopy { + src: [u32; 4], + dst: [u32; 4], +} + +#[repr(C)] +struct BufferImageCopy { + buffer_offset: u32, + buffer_size: [u32; 2], + _padding: u32, + image_offset: [u32; 4], + image_extent: [u32; 4], + // actual size of the target image + image_size: [u32; 4], +} + +#[repr(C)] +struct BufferImageCopyInfo { + buffer: BufferCopy, + image: ImageCopy, + buffer_image: BufferImageCopy, +} + +#[repr(C)] +struct BlitInfo { + offset: [f32; 2], + extent: [f32; 2], + z: f32, + level: f32, +} + +#[repr(C)] +struct PartialClearInfo { + // transmute between the types, easier than juggling all different kinds of fields.. + data: [u32; 4], +} + +// the threadgroup count we use in our copy shaders +const COPY_THREAD_GROUP_X: u32 = 8; +const COPY_THREAD_GROUP_Y: u32 = 8; + +#[derive(Clone, Debug)] +struct ComputeCopyBuffer { + d1_from_buffer: Option>, + // Buffer -> Image2D + d2_from_buffer: Option>, + // Image2D -> Buffer + d2_into_buffer: ComPtr, + scale: (u32, u32), +} + +#[derive(Debug)] +struct ConstantBuffer { + buffer: ComPtr, +} + +impl ConstantBuffer { + unsafe fn update(&mut self, context: &ComPtr, data: T) { + let mut mapped = mem::zeroed::(); + let hr = context.Map( + self.buffer.as_raw() as _, + 0, + d3d11::D3D11_MAP_WRITE_DISCARD, + 0, + &mut mapped, + ); + assert_eq!(winerror::S_OK, hr); + + ptr::copy(&data, mapped.pData as _, 1); + + context.Unmap(self.buffer.as_raw() as _, 0); + } +} + +#[derive(Debug)] +struct MissingComputeInternal { + // Image<->Image not covered by `CopySubresourceRegion` + cs_copy_image_shaders: FastHashSet<(dxgiformat::DXGI_FORMAT, dxgiformat::DXGI_FORMAT)>, + // Image -> Buffer and Buffer -> Image shaders + cs_copy_buffer_shaders: FastHashSet, +} + +#[derive(Debug)] +struct ComputeInternal { + // Image<->Image not covered by `CopySubresourceRegion` + cs_copy_image_shaders: FastHashMap< + (dxgiformat::DXGI_FORMAT, dxgiformat::DXGI_FORMAT), + ComPtr, + >, + // Image -> Buffer and Buffer -> Image shaders + cs_copy_buffer_shaders: FastHashMap, +} + +#[derive(Debug)] +enum PossibleComputeInternal { + Available(ComputeInternal), + Missing(MissingComputeInternal), +} + +// Holds everything we need for fallback implementations of features that are not in DX. +// +// TODO: make struct fields more modular and group them up in structs depending on if it is a +// fallback version or not (eg. Option), should make struct definition and +// `new` function smaller +#[derive(Debug)] +pub struct Internal { + // partial clearing + vs_partial_clear: ComPtr, + ps_partial_clear_float: ComPtr, + ps_partial_clear_uint: ComPtr, + ps_partial_clear_int: ComPtr, + ps_partial_clear_depth: ComPtr, + ps_partial_clear_stencil: ComPtr, + partial_clear_depth_stencil_state: ComPtr, + partial_clear_depth_state: ComPtr, + partial_clear_stencil_state: ComPtr, + + // blitting + vs_blit_2d: ComPtr, + + sampler_nearest: ComPtr, + sampler_linear: ComPtr, + + ps_blit_2d_uint: ComPtr, + ps_blit_2d_int: ComPtr, + ps_blit_2d_float: ComPtr, + + // all compute shader based workarounds, so they can be None when running on an older version without compute shaders. + compute_internal: PossibleComputeInternal, + + // internal constant buffer that is used by internal shaders + internal_buffer: Mutex, + + // public buffer that is used as intermediate storage for some operations (memory invalidation) + pub working_buffer: ComPtr, + pub working_buffer_size: u64, + + pub constant_buffer_count_buffer: + [UINT; d3d11::D3D11_COMMONSHADER_CONSTANT_BUFFER_API_SLOT_COUNT as _], + + /// Command lists are not supported by graphics card and are being emulated. + /// Requires various workarounds to make things work correctly. + pub command_list_emulation: bool, + + pub device_features: hal::Features, + pub device_feature_level: u32, + + pub downlevel: hal::DownlevelProperties, +} + +fn compile_blob( + src: &[u8], + entrypoint: &str, + stage: ShaderStage, + shader_model: ShaderModel, +) -> ComPtr { + unsafe { + ComPtr::from_raw(shader::compile_hlsl_shader(stage, shader_model, entrypoint, src).unwrap()) + } +} + +fn compile_vs( + device: &ComPtr, + src: &[u8], + entrypoint: &str, + shader_model: ShaderModel, +) -> ComPtr { + let bytecode = compile_blob(src, entrypoint, ShaderStage::Vertex, shader_model); + let mut shader = ptr::null_mut(); + let hr = unsafe { + device.CreateVertexShader( + bytecode.GetBufferPointer(), + bytecode.GetBufferSize(), + ptr::null_mut(), + &mut shader as *mut *mut _ as *mut *mut _, + ) + }; + assert_eq!(true, winerror::SUCCEEDED(hr)); + + unsafe { ComPtr::from_raw(shader) } +} + +fn compile_ps( + device: &ComPtr, + src: &[u8], + entrypoint: &str, + shader_model: ShaderModel, +) -> ComPtr { + let bytecode = compile_blob(src, entrypoint, ShaderStage::Fragment, shader_model); + let mut shader = ptr::null_mut(); + let hr = unsafe { + device.CreatePixelShader( + bytecode.GetBufferPointer(), + bytecode.GetBufferSize(), + ptr::null_mut(), + &mut shader as *mut *mut _ as *mut *mut _, + ) + }; + assert_eq!(true, winerror::SUCCEEDED(hr)); + + unsafe { ComPtr::from_raw(shader) } +} + +fn compile_cs( + device: &ComPtr, + src: &[u8], + entrypoint: &str, + shader_model: ShaderModel, +) -> ComPtr { + let bytecode = compile_blob(src, entrypoint, ShaderStage::Compute, shader_model); + let mut shader = ptr::null_mut(); + let hr = unsafe { + device.CreateComputeShader( + bytecode.GetBufferPointer(), + bytecode.GetBufferSize(), + ptr::null_mut(), + &mut shader as *mut *mut _ as *mut *mut _, + ) + }; + assert_eq!(true, winerror::SUCCEEDED(hr)); + + unsafe { ComPtr::from_raw(shader) } +} + +impl Internal { + pub fn new( + device: &ComPtr, + device_features: hal::Features, + device_feature_level: u32, + downlevel: hal::DownlevelProperties, + ) -> Self { + let internal_buffer = { + let desc = d3d11::D3D11_BUFFER_DESC { + ByteWidth: mem::size_of::() as _, + Usage: d3d11::D3D11_USAGE_DYNAMIC, + BindFlags: d3d11::D3D11_BIND_CONSTANT_BUFFER, + CPUAccessFlags: d3d11::D3D11_CPU_ACCESS_WRITE, + MiscFlags: 0, + StructureByteStride: 0, + }; + + let mut buffer = ptr::null_mut(); + let hr = unsafe { + device.CreateBuffer( + &desc, + ptr::null_mut(), + &mut buffer as *mut *mut _ as *mut *mut _, + ) + }; + assert_eq!(true, winerror::SUCCEEDED(hr)); + + unsafe { ComPtr::from_raw(buffer) } + }; + + let (depth_stencil_state, depth_state, stencil_state) = { + let mut depth_state = ptr::null_mut(); + let mut stencil_state = ptr::null_mut(); + let mut depth_stencil_state = ptr::null_mut(); + + let mut desc = d3d11::D3D11_DEPTH_STENCIL_DESC { + DepthEnable: TRUE, + DepthWriteMask: d3d11::D3D11_DEPTH_WRITE_MASK_ALL, + DepthFunc: d3d11::D3D11_COMPARISON_ALWAYS, + StencilEnable: TRUE, + StencilReadMask: 0, + StencilWriteMask: !0, + FrontFace: d3d11::D3D11_DEPTH_STENCILOP_DESC { + StencilFailOp: d3d11::D3D11_STENCIL_OP_REPLACE, + StencilDepthFailOp: d3d11::D3D11_STENCIL_OP_REPLACE, + StencilPassOp: d3d11::D3D11_STENCIL_OP_REPLACE, + StencilFunc: d3d11::D3D11_COMPARISON_ALWAYS, + }, + BackFace: d3d11::D3D11_DEPTH_STENCILOP_DESC { + StencilFailOp: d3d11::D3D11_STENCIL_OP_REPLACE, + StencilDepthFailOp: d3d11::D3D11_STENCIL_OP_REPLACE, + StencilPassOp: d3d11::D3D11_STENCIL_OP_REPLACE, + StencilFunc: d3d11::D3D11_COMPARISON_ALWAYS, + }, + }; + + let hr = unsafe { + device.CreateDepthStencilState( + &desc, + &mut depth_stencil_state as *mut *mut _ as *mut *mut _, + ) + }; + assert_eq!(winerror::S_OK, hr); + + desc.DepthEnable = TRUE; + desc.StencilEnable = FALSE; + + let hr = unsafe { + device + .CreateDepthStencilState(&desc, &mut depth_state as *mut *mut _ as *mut *mut _) + }; + assert_eq!(winerror::S_OK, hr); + + desc.DepthEnable = FALSE; + desc.StencilEnable = TRUE; + + let hr = unsafe { + device.CreateDepthStencilState( + &desc, + &mut stencil_state as *mut *mut _ as *mut *mut _, + ) + }; + assert_eq!(winerror::S_OK, hr); + + unsafe { + ( + ComPtr::from_raw(depth_stencil_state), + ComPtr::from_raw(depth_state), + ComPtr::from_raw(stencil_state), + ) + } + }; + + let (sampler_nearest, sampler_linear) = { + let mut desc = d3d11::D3D11_SAMPLER_DESC { + Filter: d3d11::D3D11_FILTER_MIN_MAG_MIP_POINT, + AddressU: d3d11::D3D11_TEXTURE_ADDRESS_CLAMP, + AddressV: d3d11::D3D11_TEXTURE_ADDRESS_CLAMP, + AddressW: d3d11::D3D11_TEXTURE_ADDRESS_CLAMP, + MipLODBias: 0f32, + MaxAnisotropy: 0, + ComparisonFunc: 0, + BorderColor: [0f32; 4], + MinLOD: 0f32, + MaxLOD: d3d11::D3D11_FLOAT32_MAX, + }; + + let mut nearest = ptr::null_mut(); + let mut linear = ptr::null_mut(); + + assert_eq!(winerror::S_OK, unsafe { + device.CreateSamplerState(&desc, &mut nearest as *mut *mut _ as *mut *mut _) + }); + + desc.Filter = d3d11::D3D11_FILTER_MIN_MAG_MIP_LINEAR; + + assert_eq!(winerror::S_OK, unsafe { + device.CreateSamplerState(&desc, &mut linear as *mut *mut _ as *mut *mut _) + }); + + unsafe { (ComPtr::from_raw(nearest), ComPtr::from_raw(linear)) } + }; + + let (working_buffer, working_buffer_size) = { + let working_buffer_size = 1 << 16; + + let desc = d3d11::D3D11_BUFFER_DESC { + ByteWidth: working_buffer_size, + Usage: d3d11::D3D11_USAGE_STAGING, + BindFlags: 0, + CPUAccessFlags: d3d11::D3D11_CPU_ACCESS_READ | d3d11::D3D11_CPU_ACCESS_WRITE, + MiscFlags: 0, + StructureByteStride: 0, + }; + let mut working_buffer = ptr::null_mut(); + + assert_eq!(winerror::S_OK, unsafe { + device.CreateBuffer( + &desc, + ptr::null_mut(), + &mut working_buffer as *mut *mut _ as *mut *mut _, + ) + }); + + ( + unsafe { ComPtr::from_raw(working_buffer) }, + working_buffer_size, + ) + }; + + let compute_shaders = if device_feature_level >= d3dcommon::D3D_FEATURE_LEVEL_11_0 { + true + } else { + // FL10 does support compute shaders but we need typed UAVs, which FL10 compute shaders don't support, so we might as well not have them. + false + }; + + let shader_model = conv::map_feature_level_to_shader_model(device_feature_level); + + let compute_internal = if compute_shaders { + let copy_shaders = include_bytes!("../shaders/copy.hlsl"); + let mut cs_copy_image_shaders = FastHashMap::default(); + cs_copy_image_shaders.insert( + ( + dxgiformat::DXGI_FORMAT_R8G8_UINT, + dxgiformat::DXGI_FORMAT_R16_UINT, + ), + compile_cs( + device, + copy_shaders, + "cs_copy_image2d_r8g8_image2d_r16", + shader_model, + ), + ); + cs_copy_image_shaders.insert( + ( + dxgiformat::DXGI_FORMAT_R16_UINT, + dxgiformat::DXGI_FORMAT_R8G8_UINT, + ), + compile_cs( + device, + copy_shaders, + "cs_copy_image2d_r16_image2d_r8g8", + shader_model, + ), + ); + cs_copy_image_shaders.insert( + ( + dxgiformat::DXGI_FORMAT_R8G8B8A8_UINT, + dxgiformat::DXGI_FORMAT_R32_UINT, + ), + compile_cs( + device, + copy_shaders, + "cs_copy_image2d_r8g8b8a8_image2d_r32", + shader_model, + ), + ); + cs_copy_image_shaders.insert( + ( + dxgiformat::DXGI_FORMAT_R8G8B8A8_UINT, + dxgiformat::DXGI_FORMAT_R16G16_UINT, + ), + compile_cs( + device, + copy_shaders, + "cs_copy_image2d_r8g8b8a8_image2d_r16g16", + shader_model, + ), + ); + cs_copy_image_shaders.insert( + ( + dxgiformat::DXGI_FORMAT_R16G16_UINT, + dxgiformat::DXGI_FORMAT_R32_UINT, + ), + compile_cs( + device, + copy_shaders, + "cs_copy_image2d_r16g16_image2d_r32", + shader_model, + ), + ); + cs_copy_image_shaders.insert( + ( + dxgiformat::DXGI_FORMAT_R16G16_UINT, + dxgiformat::DXGI_FORMAT_R8G8B8A8_UINT, + ), + compile_cs( + device, + copy_shaders, + "cs_copy_image2d_r16g16_image2d_r8g8b8a8", + shader_model, + ), + ); + cs_copy_image_shaders.insert( + ( + dxgiformat::DXGI_FORMAT_R32_UINT, + dxgiformat::DXGI_FORMAT_R16G16_UINT, + ), + compile_cs( + device, + copy_shaders, + "cs_copy_image2d_r32_image2d_r16g16", + shader_model, + ), + ); + cs_copy_image_shaders.insert( + ( + dxgiformat::DXGI_FORMAT_R32_UINT, + dxgiformat::DXGI_FORMAT_R8G8B8A8_UINT, + ), + compile_cs( + device, + copy_shaders, + "cs_copy_image2d_r32_image2d_r8g8b8a8", + shader_model, + ), + ); + + let mut cs_copy_buffer_shaders = FastHashMap::default(); + cs_copy_buffer_shaders.insert( + dxgiformat::DXGI_FORMAT_R32G32B32A32_UINT, + ComputeCopyBuffer { + d1_from_buffer: None, + d2_from_buffer: Some(compile_cs( + device, + copy_shaders, + "cs_copy_buffer_image2d_r32g32b32a32", + shader_model, + )), + d2_into_buffer: compile_cs( + device, + copy_shaders, + "cs_copy_image2d_r32g32b32a32_buffer", + shader_model, + ), + scale: (1, 1), + }, + ); + cs_copy_buffer_shaders.insert( + dxgiformat::DXGI_FORMAT_R32G32_UINT, + ComputeCopyBuffer { + d1_from_buffer: None, + d2_from_buffer: Some(compile_cs( + device, + copy_shaders, + "cs_copy_buffer_image2d_r32g32", + shader_model, + )), + d2_into_buffer: compile_cs( + device, + copy_shaders, + "cs_copy_image2d_r32g32_buffer", + shader_model, + ), + scale: (1, 1), + }, + ); + cs_copy_buffer_shaders.insert( + dxgiformat::DXGI_FORMAT_R32_UINT, + ComputeCopyBuffer { + d1_from_buffer: Some(compile_cs( + device, + copy_shaders, + "cs_copy_buffer_image1d_r32", + shader_model, + )), + d2_from_buffer: Some(compile_cs( + device, + copy_shaders, + "cs_copy_buffer_image2d_r32", + shader_model, + )), + d2_into_buffer: compile_cs( + device, + copy_shaders, + "cs_copy_image2d_r32_buffer", + shader_model, + ), + scale: (1, 1), + }, + ); + cs_copy_buffer_shaders.insert( + dxgiformat::DXGI_FORMAT_R16G16B16A16_UINT, + ComputeCopyBuffer { + d1_from_buffer: None, + d2_from_buffer: Some(compile_cs( + device, + copy_shaders, + "cs_copy_buffer_image2d_r16g16b16a16", + shader_model, + )), + d2_into_buffer: compile_cs( + device, + copy_shaders, + "cs_copy_image2d_r16g16b16a16_buffer", + shader_model, + ), + scale: (1, 1), + }, + ); + cs_copy_buffer_shaders.insert( + dxgiformat::DXGI_FORMAT_R16G16_UINT, + ComputeCopyBuffer { + d1_from_buffer: None, + d2_from_buffer: Some(compile_cs( + device, + copy_shaders, + "cs_copy_buffer_image2d_r16g16", + shader_model, + )), + d2_into_buffer: compile_cs( + device, + copy_shaders, + "cs_copy_image2d_r16g16_buffer", + shader_model, + ), + scale: (1, 1), + }, + ); + cs_copy_buffer_shaders.insert( + dxgiformat::DXGI_FORMAT_R16_UINT, + ComputeCopyBuffer { + d1_from_buffer: None, + d2_from_buffer: Some(compile_cs( + device, + copy_shaders, + "cs_copy_buffer_image2d_r16", + shader_model, + )), + d2_into_buffer: compile_cs( + device, + copy_shaders, + "cs_copy_image2d_r16_buffer", + shader_model, + ), + scale: (2, 1), + }, + ); + cs_copy_buffer_shaders.insert( + dxgiformat::DXGI_FORMAT_B8G8R8A8_UNORM, + ComputeCopyBuffer { + d1_from_buffer: None, + d2_from_buffer: None, + d2_into_buffer: compile_cs( + device, + copy_shaders, + "cs_copy_image2d_b8g8r8a8_buffer", + shader_model, + ), + scale: (1, 1), + }, + ); + cs_copy_buffer_shaders.insert( + dxgiformat::DXGI_FORMAT_R8G8B8A8_UINT, + ComputeCopyBuffer { + d1_from_buffer: Some(compile_cs( + device, + copy_shaders, + "cs_copy_buffer_image1d_r8g8b8a8", + shader_model, + )), + d2_from_buffer: Some(compile_cs( + device, + copy_shaders, + "cs_copy_buffer_image2d_r8g8b8a8", + shader_model, + )), + d2_into_buffer: compile_cs( + device, + copy_shaders, + "cs_copy_image2d_r8g8b8a8_buffer", + shader_model, + ), + scale: (1, 1), + }, + ); + cs_copy_buffer_shaders.insert( + dxgiformat::DXGI_FORMAT_R8G8_UINT, + ComputeCopyBuffer { + d1_from_buffer: Some(compile_cs( + device, + copy_shaders, + "cs_copy_buffer_image1d_r8g8", + shader_model, + )), + d2_from_buffer: Some(compile_cs( + device, + copy_shaders, + "cs_copy_buffer_image2d_r8g8", + shader_model, + )), + d2_into_buffer: compile_cs( + device, + copy_shaders, + "cs_copy_image2d_r8g8_buffer", + shader_model, + ), + scale: (2, 1), + }, + ); + cs_copy_buffer_shaders.insert( + dxgiformat::DXGI_FORMAT_R8_UINT, + ComputeCopyBuffer { + d1_from_buffer: Some(compile_cs( + device, + copy_shaders, + "cs_copy_buffer_image1d_r8", + shader_model, + )), + d2_from_buffer: Some(compile_cs( + device, + copy_shaders, + "cs_copy_buffer_image2d_r8", + shader_model, + )), + d2_into_buffer: compile_cs( + device, + copy_shaders, + "cs_copy_image2d_r8_buffer", + shader_model, + ), + scale: (4, 1), + }, + ); + + PossibleComputeInternal::Available(ComputeInternal { + cs_copy_buffer_shaders, + cs_copy_image_shaders, + }) + } else { + let mut cs_copy_image_shaders = FastHashSet::default(); + cs_copy_image_shaders.insert(( + dxgiformat::DXGI_FORMAT_R8G8_UINT, + dxgiformat::DXGI_FORMAT_R16_UINT, + )); + cs_copy_image_shaders.insert(( + dxgiformat::DXGI_FORMAT_R16_UINT, + dxgiformat::DXGI_FORMAT_R8G8_UINT, + )); + cs_copy_image_shaders.insert(( + dxgiformat::DXGI_FORMAT_R8G8B8A8_UINT, + dxgiformat::DXGI_FORMAT_R32_UINT, + )); + cs_copy_image_shaders.insert(( + dxgiformat::DXGI_FORMAT_R8G8B8A8_UINT, + dxgiformat::DXGI_FORMAT_R16G16_UINT, + )); + cs_copy_image_shaders.insert(( + dxgiformat::DXGI_FORMAT_R16G16_UINT, + dxgiformat::DXGI_FORMAT_R32_UINT, + )); + cs_copy_image_shaders.insert(( + dxgiformat::DXGI_FORMAT_R16G16_UINT, + dxgiformat::DXGI_FORMAT_R8G8B8A8_UINT, + )); + cs_copy_image_shaders.insert(( + dxgiformat::DXGI_FORMAT_R32_UINT, + dxgiformat::DXGI_FORMAT_R16G16_UINT, + )); + cs_copy_image_shaders.insert(( + dxgiformat::DXGI_FORMAT_R32_UINT, + dxgiformat::DXGI_FORMAT_R8G8B8A8_UINT, + )); + + let mut cs_copy_buffer_shaders = FastHashSet::default(); + cs_copy_buffer_shaders.insert(dxgiformat::DXGI_FORMAT_R32G32B32A32_UINT); + cs_copy_buffer_shaders.insert(dxgiformat::DXGI_FORMAT_R32G32_UINT); + cs_copy_buffer_shaders.insert(dxgiformat::DXGI_FORMAT_R32_UINT); + cs_copy_buffer_shaders.insert(dxgiformat::DXGI_FORMAT_R16G16B16A16_UINT); + cs_copy_buffer_shaders.insert(dxgiformat::DXGI_FORMAT_R16G16_UINT); + cs_copy_buffer_shaders.insert(dxgiformat::DXGI_FORMAT_R16_UINT); + cs_copy_buffer_shaders.insert(dxgiformat::DXGI_FORMAT_B8G8R8A8_UNORM); + cs_copy_buffer_shaders.insert(dxgiformat::DXGI_FORMAT_R8G8B8A8_UINT); + cs_copy_buffer_shaders.insert(dxgiformat::DXGI_FORMAT_R8G8_UINT); + cs_copy_buffer_shaders.insert(dxgiformat::DXGI_FORMAT_R8_UINT); + + PossibleComputeInternal::Missing(MissingComputeInternal { + cs_copy_image_shaders, + cs_copy_buffer_shaders, + }) + }; + + let mut threading_capability: d3d11::D3D11_FEATURE_DATA_THREADING = + unsafe { mem::zeroed() }; + let hr = unsafe { + device.CheckFeatureSupport( + d3d11::D3D11_FEATURE_THREADING, + &mut threading_capability as *mut _ as *mut _, + mem::size_of::() as _, + ) + }; + assert_eq!(hr, winerror::S_OK); + + let command_list_emulation = !(threading_capability.DriverCommandLists >= 1); + if command_list_emulation { + info!("D3D11 command list emulation is active"); + } + + let clear_shaders = include_bytes!("../shaders/clear.hlsl"); + let blit_shaders = include_bytes!("../shaders/blit.hlsl"); + + Internal { + vs_partial_clear: compile_vs(device, clear_shaders, "vs_partial_clear", shader_model), + ps_partial_clear_float: compile_ps( + device, + clear_shaders, + "ps_partial_clear_float", + shader_model, + ), + ps_partial_clear_uint: compile_ps( + device, + clear_shaders, + "ps_partial_clear_uint", + shader_model, + ), + ps_partial_clear_int: compile_ps( + device, + clear_shaders, + "ps_partial_clear_int", + shader_model, + ), + ps_partial_clear_depth: compile_ps( + device, + clear_shaders, + "ps_partial_clear_depth", + shader_model, + ), + ps_partial_clear_stencil: compile_ps( + device, + clear_shaders, + "ps_partial_clear_stencil", + shader_model, + ), + partial_clear_depth_stencil_state: depth_stencil_state, + partial_clear_depth_state: depth_state, + partial_clear_stencil_state: stencil_state, + + vs_blit_2d: compile_vs(device, blit_shaders, "vs_blit_2d", shader_model), + + sampler_nearest, + sampler_linear, + + ps_blit_2d_uint: compile_ps(device, blit_shaders, "ps_blit_2d_uint", shader_model), + ps_blit_2d_int: compile_ps(device, blit_shaders, "ps_blit_2d_int", shader_model), + ps_blit_2d_float: compile_ps(device, blit_shaders, "ps_blit_2d_float", shader_model), + + compute_internal, + + internal_buffer: Mutex::new(ConstantBuffer { + buffer: internal_buffer, + }), + working_buffer, + working_buffer_size: working_buffer_size as _, + + constant_buffer_count_buffer: [4096_u32; + d3d11::D3D11_COMMONSHADER_CONSTANT_BUFFER_API_SLOT_COUNT as _], + + command_list_emulation, + + device_features, + device_feature_level, + + downlevel, + } + } + + pub fn copy_image_2d( + &self, + context: &ComPtr, + src: &Image, + dst: &Image, + regions: T, + ) where + T: Iterator, + { + let key = ( + src.decomposed_format.copy_srv.unwrap(), + dst.decomposed_format.copy_srv.unwrap(), + ); + + let compute_shader_copy = if let PossibleComputeInternal::Available(ref compute_internal) = + self.compute_internal + { + compute_internal.cs_copy_image_shaders.get(&key) + } else if let PossibleComputeInternal::Missing(ref compute_internal) = self.compute_internal + { + if compute_internal.cs_copy_image_shaders.contains(&key) { + panic!("Tried to copy between images types ({:?} -> {:?}) that require Compute Shaders under FL9 or FL10", src.format, dst.format); + } else { + None + } + } else { + None + }; + + if let Some(shader) = compute_shader_copy { + // Some formats cant go through default path, since they cant + // be cast between formats of different component types (eg. + // Rg16 <-> Rgba8) + + // TODO: subresources + let srv = src.internal.copy_srv.clone().unwrap().as_raw(); + let mut const_buf = self.internal_buffer.lock(); + + unsafe { + context.CSSetShader(shader.as_raw(), ptr::null_mut(), 0); + context.CSSetConstantBuffers(0, 1, &const_buf.buffer.as_raw()); + context.CSSetShaderResources(0, 1, [srv].as_ptr()); + + for info in regions { + let image = ImageCopy { + src: [ + info.src_offset.x as _, + info.src_offset.y as _, + info.src_offset.z as _, + 0, + ], + dst: [ + info.dst_offset.x as _, + info.dst_offset.y as _, + info.dst_offset.z as _, + 0, + ], + }; + const_buf.update( + context, + BufferImageCopyInfo { + image, + ..mem::zeroed() + }, + ); + + let uav = dst.get_uav(info.dst_subresource.level, 0).unwrap().as_raw(); + context.CSSetUnorderedAccessViews(0, 1, [uav].as_ptr(), ptr::null_mut()); + + context.Dispatch(info.extent.width as u32, info.extent.height as u32, 1); + } + + // unbind external resources + context.CSSetShaderResources(0, 1, [ptr::null_mut(); 1].as_ptr()); + context.CSSetUnorderedAccessViews( + 0, + 1, + [ptr::null_mut(); 1].as_ptr(), + ptr::null_mut(), + ); + } + } else { + // Default copy path + for info in regions { + assert_eq!( + src.decomposed_format.typeless, dst.decomposed_format.typeless, + "DX11 backend cannot copy between underlying image formats: {} to {}.", + src.decomposed_format.typeless, dst.decomposed_format.typeless, + ); + + // Formats are the same per above assert, only need to do it for one of the formats + let full_copy_only = + src.format.is_depth() || src.format.is_stencil() || src.kind.num_samples() > 1; + + let copy_box = if full_copy_only { + let offset_zero = info.src_offset.x == 0 + && info.src_offset.y == 0 + && info.src_offset.z == 0 + && info.dst_offset.x == 0 + && info.dst_offset.y == 0 + && info.dst_offset.z == 0; + + let full_extent = info.extent == src.kind.extent(); + + if !offset_zero || !full_extent { + warn!("image to image copies of depth-stencil or multisampled textures must copy the whole resource. Ignoring non-zero offset or non-full extent."); + } + + None + } else { + Some(d3d11::D3D11_BOX { + left: info.src_offset.x as _, + top: info.src_offset.y as _, + front: info.src_offset.z as _, + right: info.src_offset.x as u32 + info.extent.width as u32, + bottom: info.src_offset.y as u32 + info.extent.height as u32, + back: info.src_offset.z as u32 + info.extent.depth as u32, + }) + }; + + // TODO: layer subresources + unsafe { + context.CopySubresourceRegion( + dst.internal.raw, + src.calc_subresource(info.src_subresource.level as _, 0), + info.dst_offset.x as _, + info.dst_offset.y as _, + info.dst_offset.z as _, + src.internal.raw, + dst.calc_subresource(info.dst_subresource.level as _, 0), + copy_box.map_or_else(ptr::null, |b| &b), + ); + } + } + } + } + + pub fn copy_image_to_buffer( + &self, + context: &ComPtr, + src: &Image, + dst: &Buffer, + regions: T, + ) where + T: Iterator, + { + let _scope = debug_scope!( + context, + "Image (format={:?},kind={:?}) => Buffer", + src.format, + src.kind + ); + + let shader = if let PossibleComputeInternal::Available(ref compute_internal) = + self.compute_internal + { + compute_internal + .cs_copy_buffer_shaders + .get(&src.decomposed_format.copy_srv.unwrap()) + .unwrap_or_else(|| panic!("The DX11 backend does not currently support copying from an image of format {:?} to a buffer", src.format)) + .clone() + } else { + // TODO(cwfitzgerald): Can we fall back to a inherent D3D11 method when copying to a CPU only buffer. + panic!("Tried to copy from an image to a buffer under FL9 or FL10"); + }; + + let srv = src.internal.copy_srv.clone().unwrap().as_raw(); + let uav = dst.internal.uav.unwrap(); + let format_desc = src.format.base_format().0.desc(); + let bytes_per_texel = format_desc.bits as u32 / 8; + let mut const_buf = self.internal_buffer.lock(); + + unsafe { + context.CSSetShader(shader.d2_into_buffer.as_raw(), ptr::null_mut(), 0); + context.CSSetConstantBuffers(0, 1, &const_buf.buffer.as_raw()); + + context.CSSetShaderResources(0, 1, [srv].as_ptr()); + context.CSSetUnorderedAccessViews(0, 1, [uav].as_ptr(), ptr::null_mut()); + + for info in regions { + let size = src.kind.extent(); + let buffer_image = BufferImageCopy { + buffer_offset: info.buffer_offset as _, + buffer_size: [info.buffer_width, info.buffer_height], + _padding: 0, + image_offset: [ + info.image_offset.x as _, + info.image_offset.y as _, + (info.image_offset.z + info.image_layers.layers.start as i32) as _, + 0, + ], + image_extent: [ + info.image_extent.width, + info.image_extent.height, + info.image_extent.depth, + 0, + ], + image_size: [size.width, size.height, size.depth, 0], + }; + + const_buf.update( + context, + BufferImageCopyInfo { + buffer_image, + ..mem::zeroed() + }, + ); + + debug_marker!(context, "{:?}", info); + + context.Dispatch( + ((info.image_extent.width + (COPY_THREAD_GROUP_X - 1)) + / COPY_THREAD_GROUP_X + / shader.scale.0) + .max(1), + ((info.image_extent.height + (COPY_THREAD_GROUP_X - 1)) + / COPY_THREAD_GROUP_Y + / shader.scale.1) + .max(1), + 1, + ); + + if let Some(disjoint_cb) = dst.internal.disjoint_cb { + let total_size = info.image_extent.depth + * (info.buffer_height * info.buffer_width * bytes_per_texel); + let copy_box = d3d11::D3D11_BOX { + left: info.buffer_offset as u32, + top: 0, + front: 0, + right: info.buffer_offset as u32 + total_size, + bottom: 1, + back: 1, + }; + + context.CopySubresourceRegion( + disjoint_cb as _, + 0, + info.buffer_offset as _, + 0, + 0, + dst.internal.raw as _, + 0, + ©_box, + ); + } + } + + // unbind external resources + context.CSSetShaderResources(0, 1, [ptr::null_mut(); 1].as_ptr()); + context.CSSetUnorderedAccessViews(0, 1, [ptr::null_mut(); 1].as_ptr(), ptr::null_mut()); + } + } + + pub fn copy_buffer_to_image( + &self, + context: &ComPtr, + src: &Buffer, + dst: &Image, + regions: T, + ) where + T: Iterator, + { + let _scope = debug_scope!( + context, + "Buffer => Image (format={:?},kind={:?})", + dst.format, + dst.kind + ); + + let compute_internal = match self.compute_internal { + PossibleComputeInternal::Available(ref available) => Some(available), + PossibleComputeInternal::Missing(_) => None, + }; + + // NOTE: we have two separate paths for Buffer -> Image transfers. we need to special case + // uploads to compressed formats through `UpdateSubresource` since we cannot get a + // UAV of any compressed format. + + let format_desc = dst.format.base_format().0.desc(); + let is_compressed = format_desc.is_compressed(); + if format_desc.is_compressed() || compute_internal.is_none() { + // we dont really care about non-4x4 block formats.. + if is_compressed { + assert_eq!(format_desc.dim, (4, 4)); + assert!( + !src.memory_ptr.is_null(), + "Only CPU to GPU upload of compressed texture is currently supported" + ); + } else if compute_internal.is_none() { + assert!( + !src.memory_ptr.is_null(), + "Only CPU to GPU upload of textures is supported under FL9 or FL10" + ); + } + + for info in regions { + let bytes_per_texel = format_desc.bits as u32 / 8; + + let bounds = d3d11::D3D11_BOX { + left: info.image_offset.x as _, + top: info.image_offset.y as _, + front: info.image_offset.z as _, + right: info.image_offset.x as u32 + info.image_extent.width, + bottom: info.image_offset.y as u32 + info.image_extent.height, + back: info.image_offset.z as u32 + info.image_extent.depth, + }; + + let row_pitch = + bytes_per_texel * info.image_extent.width / format_desc.dim.0 as u32; + let depth_pitch = row_pitch * info.image_extent.height / format_desc.dim.1 as u32; + + for layer in info.image_layers.layers.clone() { + let layer_offset = layer - info.image_layers.layers.start; + + unsafe { + context.UpdateSubresource( + dst.internal.raw, + dst.calc_subresource(info.image_layers.level as _, layer as _), + &bounds, + src.memory_ptr.offset( + src.bound_range.start as isize + + info.buffer_offset as isize + + depth_pitch as isize * layer_offset as isize, + ) as _, + row_pitch, + depth_pitch, + ); + } + } + } + } else { + let shader = compute_internal + .unwrap() + .cs_copy_buffer_shaders + .get(&dst.decomposed_format.copy_uav.unwrap()) + .unwrap_or_else(|| panic!("The DX11 backend does not currently support copying from a buffer to an image of format {:?}", dst.format)) + .clone(); + + let srv = src.internal.srv.unwrap(); + let mut const_buf = self.internal_buffer.lock(); + let shader_raw = match dst.kind { + image::Kind::D1(..) => shader.d1_from_buffer.unwrap().as_raw(), + image::Kind::D2(..) => shader.d2_from_buffer.unwrap().as_raw(), + image::Kind::D3(..) => panic!("Copies into 3D images are not supported"), + }; + + unsafe { + context.CSSetShader(shader_raw, ptr::null_mut(), 0); + context.CSSetConstantBuffers(0, 1, &const_buf.buffer.as_raw()); + context.CSSetShaderResources(0, 1, [srv].as_ptr()); + + for info in regions { + let size = dst.kind.extent(); + let buffer_image = BufferImageCopy { + buffer_offset: info.buffer_offset as _, + buffer_size: [info.buffer_width, info.buffer_height], + _padding: 0, + image_offset: [ + info.image_offset.x as _, + info.image_offset.y as _, + (info.image_offset.z + info.image_layers.layers.start as i32) as _, + 0, + ], + image_extent: [ + info.image_extent.width, + info.image_extent.height, + info.image_extent.depth, + 0, + ], + image_size: [size.width, size.height, size.depth, 0], + }; + + const_buf.update( + context, + BufferImageCopyInfo { + buffer_image, + ..mem::zeroed() + }, + ); + + debug_marker!(context, "{:?}", info); + + // TODO: multiple layers? do we introduce a stride and do multiple dispatch + // calls or handle this in the shader? (use z component in dispatch call + // + // NOTE: right now our copy UAV is a 2D array, and we set the layer in the + // `update_buffer_image` call above + let uav = dst + .get_uav( + info.image_layers.level, + 0, /*info.image_layers.layers.start*/ + ) + .unwrap() + .as_raw(); + context.CSSetUnorderedAccessViews(0, 1, [uav].as_ptr(), ptr::null_mut()); + + context.Dispatch( + ((info.image_extent.width + (COPY_THREAD_GROUP_X - 1)) + / COPY_THREAD_GROUP_X + / shader.scale.0) + .max(1), + ((info.image_extent.height + (COPY_THREAD_GROUP_X - 1)) + / COPY_THREAD_GROUP_Y + / shader.scale.1) + .max(1), + 1, + ); + } + + // unbind external resources + context.CSSetShaderResources(0, 1, [ptr::null_mut(); 1].as_ptr()); + context.CSSetUnorderedAccessViews( + 0, + 1, + [ptr::null_mut(); 1].as_ptr(), + ptr::null_mut(), + ); + } + } + } + + fn find_blit_shader(&self, src: &Image) -> Option<*mut d3d11::ID3D11PixelShader> { + use crate::format::ChannelType as Ct; + + match src.format.base_format().1 { + Ct::Uint => Some(self.ps_blit_2d_uint.as_raw()), + Ct::Sint => Some(self.ps_blit_2d_int.as_raw()), + Ct::Unorm | Ct::Snorm | Ct::Sfloat | Ct::Srgb => Some(self.ps_blit_2d_float.as_raw()), + Ct::Ufloat | Ct::Uscaled | Ct::Sscaled => None, + } + } + + pub fn blit_2d_image( + &self, + context: &ComPtr, + src: &Image, + dst: &Image, + filter: image::Filter, + regions: T, + ) where + T: Iterator, + { + use std::cmp; + + let _scope = debug_scope!( + context, + "Blit: Image (format={:?},kind={:?}) => Image (format={:?},kind={:?})", + src.format, + src.kind, + dst.format, + dst.kind + ); + + let shader = self.find_blit_shader(src).unwrap(); + + let srv = src.internal.srv.clone().unwrap().as_raw(); + let mut const_buf = self.internal_buffer.lock(); + + unsafe { + context.IASetPrimitiveTopology(d3dcommon::D3D11_PRIMITIVE_TOPOLOGY_TRIANGLELIST); + context.VSSetShader(self.vs_blit_2d.as_raw(), ptr::null_mut(), 0); + context.VSSetConstantBuffers(0, 1, [const_buf.buffer.as_raw()].as_ptr()); + context.PSSetShader(shader, ptr::null_mut(), 0); + context.PSSetShaderResources(0, 1, [srv].as_ptr()); + context.PSSetSamplers( + 0, + 1, + match filter { + image::Filter::Nearest => [self.sampler_nearest.as_raw()], + image::Filter::Linear => [self.sampler_linear.as_raw()], + } + .as_ptr(), + ); + + for info in regions { + let blit_info = { + let (sx, dx) = if info.dst_bounds.start.x > info.dst_bounds.end.x { + ( + info.src_bounds.end.x, + info.src_bounds.start.x - info.src_bounds.end.x, + ) + } else { + ( + info.src_bounds.start.x, + info.src_bounds.end.x - info.src_bounds.start.x, + ) + }; + let (sy, dy) = if info.dst_bounds.start.y > info.dst_bounds.end.y { + ( + info.src_bounds.end.y, + info.src_bounds.start.y - info.src_bounds.end.y, + ) + } else { + ( + info.src_bounds.start.y, + info.src_bounds.end.y - info.src_bounds.start.y, + ) + }; + let image::Extent { width, height, .. } = + src.kind.level_extent(info.src_subresource.level); + BlitInfo { + offset: [sx as f32 / width as f32, sy as f32 / height as f32], + extent: [dx as f32 / width as f32, dy as f32 / height as f32], + z: 0f32, // TODO + level: info.src_subresource.level as _, + } + }; + + const_buf.update(context, blit_info); + + // TODO: more layers + let rtv = dst + .get_rtv( + info.dst_subresource.level, + info.dst_subresource.layers.start, + ) + .unwrap() + .as_raw(); + + context.RSSetViewports( + 1, + [d3d11::D3D11_VIEWPORT { + TopLeftX: cmp::min(info.dst_bounds.start.x, info.dst_bounds.end.x) as _, + TopLeftY: cmp::min(info.dst_bounds.start.y, info.dst_bounds.end.y) as _, + Width: (info.dst_bounds.end.x - info.dst_bounds.start.x).abs() as _, + Height: (info.dst_bounds.end.y - info.dst_bounds.start.y).abs() as _, + MinDepth: 0.0f32, + MaxDepth: 1.0f32, + }] + .as_ptr(), + ); + context.OMSetRenderTargets(1, [rtv].as_ptr(), ptr::null_mut()); + context.Draw(3, 0); + } + + context.PSSetShaderResources(0, 1, [ptr::null_mut()].as_ptr()); + context.OMSetRenderTargets(1, [ptr::null_mut()].as_ptr(), ptr::null_mut()); + } + } + + pub fn clear_attachments( + &self, + context: &ComPtr, + clears: T, + rects: U, + cache: &RenderPassCache, + ) where + T: Iterator, + U: Iterator, + { + use hal::format::ChannelType as Ct; + let _scope = debug_scope!(context, "ClearAttachments"); + + let clear_rects: SmallVec<[pso::ClearRect; 8]> = rects.collect(); + let mut const_buf = self.internal_buffer.lock(); + + unsafe { + context.IASetPrimitiveTopology(d3dcommon::D3D11_PRIMITIVE_TOPOLOGY_TRIANGLELIST); + context.IASetInputLayout(ptr::null_mut()); + context.VSSetShader(self.vs_partial_clear.as_raw(), ptr::null_mut(), 0); + context.PSSetConstantBuffers(0, 1, [const_buf.buffer.as_raw()].as_ptr()); + } + + let subpass = &cache.render_pass.subpasses[cache.current_subpass as usize]; + + for clear in clears { + let _scope = debug_scope!(context, "{:?}", clear); + + match clear { + command::AttachmentClear::Color { index, value } => { + unsafe { + const_buf.update( + context, + PartialClearInfo { + data: mem::transmute(value), + }, + ) + }; + + let attachment = { + let rtv_id = subpass.color_attachments[index]; + &cache.attachments[rtv_id.0].view + }; + + unsafe { + context.OMSetRenderTargets( + 1, + [attachment.rtv_handle.unwrap()].as_ptr(), + ptr::null_mut(), + ); + } + + let shader = match attachment.format.base_format().1 { + Ct::Uint => self.ps_partial_clear_uint.as_raw(), + Ct::Sint => self.ps_partial_clear_int.as_raw(), + _ => self.ps_partial_clear_float.as_raw(), + }; + unsafe { context.PSSetShader(shader, ptr::null_mut(), 0) }; + + for clear_rect in &clear_rects { + let viewport = conv::map_viewport(&pso::Viewport { + rect: clear_rect.rect, + depth: 0f32..1f32, + }); + + debug_marker!(context, "{:?}", clear_rect.rect); + + unsafe { + context.RSSetViewports(1, [viewport].as_ptr()); + context.Draw(3, 0); + } + } + } + command::AttachmentClear::DepthStencil { depth, stencil } => { + unsafe { + const_buf.update( + context, + PartialClearInfo { + data: [ + mem::transmute(depth.unwrap_or(0f32)), + stencil.unwrap_or(0), + 0, + 0, + ], + }, + ) + }; + + let attachment = { + let dsv_id = subpass.depth_stencil_attachment.unwrap(); + &cache.attachments[dsv_id.0].view + }; + + unsafe { + match (depth, stencil) { + (Some(_), Some(stencil)) => { + context.OMSetDepthStencilState( + self.partial_clear_depth_stencil_state.as_raw(), + stencil, + ); + context.PSSetShader( + self.ps_partial_clear_depth.as_raw(), + ptr::null_mut(), + 0, + ); + } + + (Some(_), None) => { + context.OMSetDepthStencilState( + self.partial_clear_depth_state.as_raw(), + 0, + ); + context.PSSetShader( + self.ps_partial_clear_depth.as_raw(), + ptr::null_mut(), + 0, + ); + } + + (None, Some(stencil)) => { + context.OMSetDepthStencilState( + self.partial_clear_stencil_state.as_raw(), + stencil, + ); + context.PSSetShader( + self.ps_partial_clear_stencil.as_raw(), + ptr::null_mut(), + 0, + ); + } + (None, None) => {} + } + + context.OMSetRenderTargets( + 0, + ptr::null_mut(), + attachment.dsv_handle.unwrap(), + ); + context.PSSetShader( + self.ps_partial_clear_depth.as_raw(), + ptr::null_mut(), + 0, + ); + } + + for clear_rect in &clear_rects { + let viewport = conv::map_viewport(&pso::Viewport { + rect: clear_rect.rect, + depth: 0f32..1f32, + }); + + unsafe { + context.RSSetViewports(1, [viewport].as_ptr()); + context.Draw(3, 0); + } + } + } + } + } + } +} diff --git a/third_party/rust/gfx-backend-dx11/src/lib.rs b/third_party/rust/gfx-backend-dx11/src/lib.rs index c35a167cfb38..2422679eec93 100644 --- a/third_party/rust/gfx-backend-dx11/src/lib.rs +++ b/third_party/rust/gfx-backend-dx11/src/lib.rs @@ -1,4528 +1,4457 @@ -/*! -# DX11 backend internals. - -## Pipeline Layout - -In D3D11 there are tables of CBVs, SRVs, UAVs, and samplers. - -Each descriptor type can take 1 or two of those entry points. - -The descriptor pool is just and array of handles, belonging to descriptor set 1, descriptor set 2, etc. -Each range of descriptors in a descriptor set area of the pool is split into shader stages, -which in turn is split into CBS/SRV/UAV/Sampler parts. That allows binding a descriptor set as a list -of continuous descriptor ranges (per type, per shader stage). - -!*/ - -//#[deny(missing_docs)] - -#[macro_use] -extern crate bitflags; -#[macro_use] -extern crate log; - -use crate::{debug::set_debug_name, device::DepthStencilState}; -use auxil::ShaderStage; -use hal::{ - adapter, buffer, command, display, format, image, memory, pass, pso, query, queue, window, - DrawCount, IndexCount, IndexType, InstanceCount, TaskCount, VertexCount, VertexOffset, - WorkGroupCount, -}; -use range_alloc::RangeAllocator; -use smallvec::SmallVec; - -use winapi::{ - shared::{ - dxgi::{IDXGIAdapter, IDXGIFactory, IDXGISwapChain}, - dxgiformat, - minwindef::{FALSE, HMODULE, UINT}, - windef::{HWND, RECT}, - winerror, - }, - um::{d3d11, d3d11_1, d3dcommon, winuser::GetClientRect}, - Interface as _, -}; - -use wio::com::ComPtr; - -use arrayvec::ArrayVec; -use parking_lot::{Condvar, Mutex, RwLock}; - -use std::{ - borrow::Borrow, - cell::RefCell, - fmt, mem, - ops::Range, - os::raw::c_void, - ptr, - sync::{Arc, Weak}, -}; - -macro_rules! debug_scope { - ($context:expr, $($arg:tt)+) => ({ - #[cfg(debug_assertions)] - { - $crate::debug::DebugScope::with_name( - $context, - format_args!($($arg)+), - ) - } - #[cfg(not(debug_assertions))] - { - () - } - }); -} - -macro_rules! debug_marker { - ($context:expr, $($arg:tt)+) => ({ - #[cfg(debug_assertions)] - { - $crate::debug::debug_marker( - $context, - &format!($($arg)+), - ); - } - }); -} - -mod conv; -mod debug; -mod device; -mod dxgi; -mod internal; -mod shader; - -type CreateFun = unsafe extern "system" fn( - *mut IDXGIAdapter, - UINT, - HMODULE, - UINT, - *const UINT, - UINT, - UINT, - *mut *mut d3d11::ID3D11Device, - *mut UINT, - *mut *mut d3d11::ID3D11DeviceContext, -) -> winerror::HRESULT; - -#[derive(Clone)] -pub(crate) struct ViewInfo { - resource: *mut d3d11::ID3D11Resource, - kind: image::Kind, - caps: image::ViewCapabilities, - view_kind: image::ViewKind, - format: dxgiformat::DXGI_FORMAT, - levels: Range, - layers: Range, -} - -impl fmt::Debug for ViewInfo { - fn fmt(&self, fmt: &mut fmt::Formatter) -> fmt::Result { - fmt.write_str("ViewInfo") - } -} - -#[derive(Debug)] -pub struct Instance { - pub(crate) factory: ComPtr, - pub(crate) dxgi_version: dxgi::DxgiVersion, - library_d3d11: Arc, - library_dxgi: libloading::Library, -} - -unsafe impl Send for Instance {} -unsafe impl Sync for Instance {} - -impl Instance { - pub fn create_surface_from_hwnd(&self, hwnd: *mut c_void) -> Surface { - Surface { - factory: self.factory.clone(), - wnd_handle: hwnd as *mut _, - presentation: None, - } - } -} - -unsafe fn check_feature_support( - device: &d3d11::ID3D11Device, - feature: d3d11::D3D11_FEATURE, -) -> T { - let mut value = mem::zeroed::(); - let ret = device.CheckFeatureSupport( - feature, - &mut value as *mut _ as *mut _, - mem::size_of::() as _, - ); - assert_eq!(ret, winerror::S_OK); - value -} - -fn get_features( - device: ComPtr, - feature_level: d3dcommon::D3D_FEATURE_LEVEL, -) -> ( - hal::Features, - hal::DownlevelProperties, - hal::PerformanceCaveats, -) { - let mut features = hal::Features::empty() - | hal::Features::ROBUST_BUFFER_ACCESS // TODO: verify - | hal::Features::INSTANCE_RATE - | hal::Features::INDEPENDENT_BLENDING // TODO: verify - | hal::Features::SAMPLER_BORDER_COLOR - | hal::Features::SAMPLER_MIP_LOD_BIAS - | hal::Features::SAMPLER_MIRROR_CLAMP_EDGE - | hal::Features::SAMPLER_ANISOTROPY - | hal::Features::DEPTH_CLAMP - | hal::Features::NDC_Y_UP; - - let mut downlevel = hal::DownlevelProperties::default(); - let performance = hal::PerformanceCaveats::default(); - - if d3dcommon::D3D_FEATURE_LEVEL_9_1 <= feature_level - && feature_level < d3dcommon::D3D_FEATURE_LEVEL_9_3 - { - let d3d9_features: d3d11::D3D11_FEATURE_DATA_D3D9_OPTIONS = - unsafe { check_feature_support(&device, d3d11::D3D11_FEATURE_D3D9_OPTIONS) }; - downlevel.non_power_of_two_mipmapped_textures = - d3d9_features.FullNonPow2TextureSupport != 0; - } - - if d3dcommon::D3D_FEATURE_LEVEL_10_0 <= feature_level - && feature_level < d3dcommon::D3D_FEATURE_LEVEL_11_0 - { - let compute_support: d3d11::D3D11_FEATURE_DATA_D3D10_X_HARDWARE_OPTIONS = unsafe { - check_feature_support(&device, d3d11::D3D11_FEATURE_D3D10_X_HARDWARE_OPTIONS) - }; - downlevel.compute_shaders = - compute_support.ComputeShaders_Plus_RawAndStructuredBuffers_Via_Shader_4_x != 0 - } - - if feature_level >= d3dcommon::D3D_FEATURE_LEVEL_10_0 { - features |= hal::Features::TEXTURE_DESCRIPTOR_ARRAY - | hal::Features::FULL_DRAW_INDEX_U32 - | hal::Features::GEOMETRY_SHADER; - downlevel.shader_model = hal::DownlevelShaderModel::ShaderModel4; - downlevel.non_power_of_two_mipmapped_textures = true; - } - - if feature_level >= d3dcommon::D3D_FEATURE_LEVEL_10_1 { - features |= hal::Features::IMAGE_CUBE_ARRAY; - } - - if feature_level >= d3dcommon::D3D_FEATURE_LEVEL_11_0 { - features |= hal::Features::VERTEX_STORES_AND_ATOMICS - | hal::Features::FRAGMENT_STORES_AND_ATOMICS - | hal::Features::FORMAT_BC - | hal::Features::TESSELLATION_SHADER - | hal::Features::DRAW_INDIRECT_FIRST_INSTANCE; - - downlevel.compute_shaders = true; - downlevel.shader_model = hal::DownlevelShaderModel::ShaderModel5; - downlevel.storage_images = true; - downlevel.read_only_depth_stencil = true; - downlevel.device_local_image_copies = true; - } - - if feature_level >= d3dcommon::D3D_FEATURE_LEVEL_11_1 { - features |= hal::Features::LOGIC_OP; // TODO: Optional at 10_0 -> 11_0 - } - - (features, downlevel, performance) -} - -const MAX_PUSH_CONSTANT_SIZE: usize = 256; - -fn get_limits(feature_level: d3dcommon::D3D_FEATURE_LEVEL) -> hal::Limits { - let max_texture_uv_dimension = match feature_level { - d3dcommon::D3D_FEATURE_LEVEL_9_1 | d3dcommon::D3D_FEATURE_LEVEL_9_2 => 2048, - d3dcommon::D3D_FEATURE_LEVEL_9_3 => 4096, - d3dcommon::D3D_FEATURE_LEVEL_10_0 | d3dcommon::D3D_FEATURE_LEVEL_10_1 => 8192, - d3dcommon::D3D_FEATURE_LEVEL_11_0 | d3dcommon::D3D_FEATURE_LEVEL_11_1 | _ => 16384, - }; - - let max_texture_w_dimension = match feature_level { - d3dcommon::D3D_FEATURE_LEVEL_9_1 - | d3dcommon::D3D_FEATURE_LEVEL_9_2 - | d3dcommon::D3D_FEATURE_LEVEL_9_3 => 256, - d3dcommon::D3D_FEATURE_LEVEL_10_0 - | d3dcommon::D3D_FEATURE_LEVEL_10_1 - | d3dcommon::D3D_FEATURE_LEVEL_11_0 - | d3dcommon::D3D_FEATURE_LEVEL_11_1 - | _ => 2048, - }; - - let max_texture_cube_dimension = match feature_level { - d3dcommon::D3D_FEATURE_LEVEL_9_1 | d3dcommon::D3D_FEATURE_LEVEL_9_2 => 512, - _ => max_texture_uv_dimension, - }; - - let max_image_uav = 2; - let max_buffer_uav = d3d11::D3D11_PS_CS_UAV_REGISTER_COUNT - max_image_uav; - - let max_input_slots = match feature_level { - d3dcommon::D3D_FEATURE_LEVEL_9_1 - | d3dcommon::D3D_FEATURE_LEVEL_9_2 - | d3dcommon::D3D_FEATURE_LEVEL_9_3 - | d3dcommon::D3D_FEATURE_LEVEL_10_0 => 16, - d3dcommon::D3D_FEATURE_LEVEL_10_1 - | d3dcommon::D3D_FEATURE_LEVEL_11_0 - | d3dcommon::D3D_FEATURE_LEVEL_11_1 - | _ => 32, - }; - - let max_color_attachments = match feature_level { - d3dcommon::D3D_FEATURE_LEVEL_9_1 - | d3dcommon::D3D_FEATURE_LEVEL_9_2 - | d3dcommon::D3D_FEATURE_LEVEL_9_3 - | d3dcommon::D3D_FEATURE_LEVEL_10_0 => 4, - d3dcommon::D3D_FEATURE_LEVEL_10_1 - | d3dcommon::D3D_FEATURE_LEVEL_11_0 - | d3dcommon::D3D_FEATURE_LEVEL_11_1 - | _ => 8, - }; - - // https://docs.microsoft.com/en-us/windows/win32/api/d3d11/nf-d3d11-id3d11device-checkmultisamplequalitylevels#remarks - // for more information. - let max_samples = match feature_level { - d3dcommon::D3D_FEATURE_LEVEL_9_1 - | d3dcommon::D3D_FEATURE_LEVEL_9_2 - | d3dcommon::D3D_FEATURE_LEVEL_9_3 - | d3dcommon::D3D_FEATURE_LEVEL_10_0 => 0b0001, // Conservative, MSAA isn't required. - d3dcommon::D3D_FEATURE_LEVEL_10_1 => 0b0101, // Optimistic, 4xMSAA is required on all formats _but_ RGBA32. - d3dcommon::D3D_FEATURE_LEVEL_11_0 | d3dcommon::D3D_FEATURE_LEVEL_11_1 | _ => 0b1101, // Optimistic, 8xMSAA and 4xMSAA is required on all formats _but_ RGBA32 which requires 4x. - }; - - let max_constant_buffers = d3d11::D3D11_COMMONSHADER_CONSTANT_BUFFER_API_SLOT_COUNT - 1; - - let ( - max_compute_work_group_count_z, - max_compute_work_group_size_xy, - max_compute_work_group_size_z, - ) = match feature_level { - d3dcommon::D3D_FEATURE_LEVEL_10_0 | d3dcommon::D3D_FEATURE_LEVEL_10_1 => { - (1, d3d11::D3D11_CS_4_X_THREAD_GROUP_MAX_X, 1) - } - d3dcommon::D3D_FEATURE_LEVEL_11_0 | d3dcommon::D3D_FEATURE_LEVEL_11_1 => ( - d3d11::D3D11_CS_DISPATCH_MAX_THREAD_GROUPS_PER_DIMENSION, - d3d11::D3D11_CS_THREAD_GROUP_MAX_X, - d3d11::D3D11_CS_THREAD_GROUP_MAX_Z, - ), - _ => (0, 0, 0), - }; - - let max_compute_shared_memory_size = match feature_level { - d3dcommon::D3D_FEATURE_LEVEL_10_0 | d3dcommon::D3D_FEATURE_LEVEL_10_1 => 4096 * 4, // This doesn't have an equiv SM4 constant :\ - d3dcommon::D3D_FEATURE_LEVEL_11_0 | d3dcommon::D3D_FEATURE_LEVEL_11_1 => { - d3d11::D3D11_CS_TGSM_REGISTER_COUNT * 4 - } - _ => 0, - } as _; - - hal::Limits { - max_image_1d_size: max_texture_uv_dimension, - max_image_2d_size: max_texture_uv_dimension, - max_image_3d_size: max_texture_w_dimension, - max_image_cube_size: max_texture_cube_dimension, - max_image_array_layers: max_texture_cube_dimension as _, - descriptor_limits: hal::DescriptorLimits { - max_per_stage_descriptor_samplers: d3d11::D3D11_COMMONSHADER_SAMPLER_SLOT_COUNT as _, - // Leave top buffer for push constants - max_per_stage_descriptor_uniform_buffers: max_constant_buffers as _, - max_per_stage_descriptor_storage_buffers: max_buffer_uav, - max_per_stage_descriptor_sampled_images: - d3d11::D3D11_COMMONSHADER_INPUT_RESOURCE_REGISTER_COUNT as _, - max_per_stage_descriptor_storage_images: max_image_uav, - max_descriptor_set_uniform_buffers_dynamic: max_constant_buffers as _, - max_descriptor_set_storage_buffers_dynamic: 0, // TODO: Implement dynamic offsets for storage buffers - ..hal::DescriptorLimits::default() // TODO - }, - max_bound_descriptor_sets: pso::DescriptorSetIndex::MAX, - max_texel_elements: max_texture_uv_dimension as _, //TODO - max_patch_size: d3d11::D3D11_IA_PATCH_MAX_CONTROL_POINT_COUNT as _, - max_viewports: d3d11::D3D11_VIEWPORT_AND_SCISSORRECT_OBJECT_COUNT_PER_PIPELINE as _, - max_viewport_dimensions: [d3d11::D3D11_VIEWPORT_BOUNDS_MAX; 2], - max_framebuffer_extent: hal::image::Extent { - //TODO - width: 4096, - height: 4096, - depth: 1, - }, - max_compute_shared_memory_size, - max_compute_work_group_count: [ - d3d11::D3D11_CS_DISPATCH_MAX_THREAD_GROUPS_PER_DIMENSION, - d3d11::D3D11_CS_DISPATCH_MAX_THREAD_GROUPS_PER_DIMENSION, - max_compute_work_group_count_z, - ], - max_compute_work_group_invocations: d3d11::D3D11_CS_THREAD_GROUP_MAX_THREADS_PER_GROUP as _, - max_compute_work_group_size: [ - max_compute_work_group_size_xy, - max_compute_work_group_size_xy, - max_compute_work_group_size_z, - ], // TODO - max_vertex_input_attribute_offset: 255, // TODO - max_vertex_input_attributes: max_input_slots, - max_vertex_input_binding_stride: d3d11::D3D11_REQ_MULTI_ELEMENT_STRUCTURE_SIZE_IN_BYTES - as _, - max_vertex_input_bindings: d3d11::D3D11_IA_VERTEX_INPUT_RESOURCE_SLOT_COUNT as _, // TODO: verify same as attributes - max_vertex_output_components: d3d11::D3D11_VS_OUTPUT_REGISTER_COUNT as _, // TODO - min_texel_buffer_offset_alignment: 1, // TODO - min_uniform_buffer_offset_alignment: 16, - min_storage_buffer_offset_alignment: 16, // TODO - framebuffer_color_sample_counts: max_samples, - framebuffer_depth_sample_counts: max_samples, - framebuffer_stencil_sample_counts: max_samples, - max_color_attachments, - buffer_image_granularity: 1, - non_coherent_atom_size: 1, // TODO - max_sampler_anisotropy: 16.0, - optimal_buffer_copy_offset_alignment: 1, // TODO - // buffer -> image and image -> buffer paths use compute shaders that, at maximum, read 4 pixels from the buffer - // at a time, so need an alignment of at least 4. - optimal_buffer_copy_pitch_alignment: 4, - min_vertex_input_binding_stride_alignment: 1, - max_push_constants_size: MAX_PUSH_CONSTANT_SIZE, - max_uniform_buffer_range: 1 << 16, - ..hal::Limits::default() //TODO - } -} - -fn get_format_properties( - device: ComPtr, -) -> [format::Properties; format::NUM_FORMATS] { - let mut format_properties: [format::Properties; format::NUM_FORMATS] = - unsafe { std::mem::zeroed() }; - format_properties - .iter_mut() - .for_each(|format_properties| *format_properties = format::Properties::default()); - for (i, props) in &mut format_properties.iter_mut().enumerate().skip(1) { - let format: format::Format = unsafe { mem::transmute(i as u32) }; - - let dxgi_format = match conv::map_format(format) { - Some(format) => format, - None => continue, - }; - - let mut support = d3d11::D3D11_FEATURE_DATA_FORMAT_SUPPORT { - InFormat: dxgi_format, - OutFormatSupport: 0, - }; - let mut support_2 = d3d11::D3D11_FEATURE_DATA_FORMAT_SUPPORT2 { - InFormat: dxgi_format, - OutFormatSupport2: 0, - }; - - let hr = unsafe { - device.CheckFeatureSupport( - d3d11::D3D11_FEATURE_FORMAT_SUPPORT, - &mut support as *mut _ as *mut _, - mem::size_of::() as UINT, - ) - }; - if hr != winerror::S_OK { - warn!("Format {:?} can't check the features-1: 0x{:x}", format, hr); - continue; - } - - let can_buffer = 0 != support.OutFormatSupport & d3d11::D3D11_FORMAT_SUPPORT_BUFFER; - let can_image = 0 - != support.OutFormatSupport - & (d3d11::D3D11_FORMAT_SUPPORT_TEXTURE1D - | d3d11::D3D11_FORMAT_SUPPORT_TEXTURE2D - | d3d11::D3D11_FORMAT_SUPPORT_TEXTURE3D - | d3d11::D3D11_FORMAT_SUPPORT_TEXTURECUBE); - let can_linear = can_image && !format.surface_desc().is_compressed(); - if can_image { - props.optimal_tiling |= format::ImageFeature::TRANSFER_SRC - | format::ImageFeature::TRANSFER_DST - | format::ImageFeature::SAMPLED - | format::ImageFeature::BLIT_SRC; - } - if can_linear { - props.linear_tiling |= format::ImageFeature::TRANSFER_SRC - | format::ImageFeature::TRANSFER_DST - | format::ImageFeature::SAMPLED - | format::ImageFeature::BLIT_SRC; - } - if support.OutFormatSupport & d3d11::D3D11_FORMAT_SUPPORT_IA_VERTEX_BUFFER != 0 { - props.buffer_features |= format::BufferFeature::VERTEX; - } - if support.OutFormatSupport & d3d11::D3D11_FORMAT_SUPPORT_SHADER_SAMPLE != 0 { - props.optimal_tiling |= format::ImageFeature::SAMPLED_LINEAR; - } - if support.OutFormatSupport & d3d11::D3D11_FORMAT_SUPPORT_RENDER_TARGET != 0 { - props.optimal_tiling |= - format::ImageFeature::COLOR_ATTACHMENT | format::ImageFeature::BLIT_DST; - if can_linear { - props.linear_tiling |= - format::ImageFeature::COLOR_ATTACHMENT | format::ImageFeature::BLIT_DST; - } - } - if support.OutFormatSupport & d3d11::D3D11_FORMAT_SUPPORT_BLENDABLE != 0 { - props.optimal_tiling |= format::ImageFeature::COLOR_ATTACHMENT_BLEND; - } - if support.OutFormatSupport & d3d11::D3D11_FORMAT_SUPPORT_DEPTH_STENCIL != 0 { - props.optimal_tiling |= format::ImageFeature::DEPTH_STENCIL_ATTACHMENT; - } - if support.OutFormatSupport & d3d11::D3D11_FORMAT_SUPPORT_SHADER_LOAD != 0 { - //TODO: check d3d12::D3D12_FORMAT_SUPPORT2_UAV_TYPED_LOAD ? - if can_buffer { - props.buffer_features |= format::BufferFeature::UNIFORM_TEXEL; - } - } - - let hr = unsafe { - device.CheckFeatureSupport( - d3d11::D3D11_FEATURE_FORMAT_SUPPORT2, - &mut support_2 as *mut _ as *mut _, - mem::size_of::() as UINT, - ) - }; - if hr != winerror::S_OK { - warn!("Format {:?} can't check the features-2: 0x{:X}", format, hr); - continue; - } - if support_2.OutFormatSupport2 & d3d11::D3D11_FORMAT_SUPPORT2_UAV_ATOMIC_ADD != 0 { - //TODO: other atomic flags? - if can_buffer { - props.buffer_features |= format::BufferFeature::STORAGE_TEXEL_ATOMIC; - } - if can_image { - props.optimal_tiling |= format::ImageFeature::STORAGE_ATOMIC; - } - } - if support_2.OutFormatSupport2 & d3d11::D3D11_FORMAT_SUPPORT2_UAV_TYPED_STORE != 0 { - if can_buffer { - props.buffer_features |= format::BufferFeature::STORAGE_TEXEL; - } - if can_image { - // Since read-only storage is exposed as SRV, we can guarantee read-only storage without checking D3D11_FORMAT_SUPPORT2_UAV_TYPED_LOAD first. - props.optimal_tiling |= format::ImageFeature::STORAGE; - - if support_2.OutFormatSupport2 & d3d11::D3D11_FORMAT_SUPPORT2_UAV_TYPED_LOAD != 0 { - props.optimal_tiling |= format::ImageFeature::STORAGE_READ_WRITE; - } - } - } - } - - format_properties -} - -impl hal::Instance for Instance { - fn create(_: &str, _: u32) -> Result { - // TODO: get the latest factory we can find - - match dxgi::get_dxgi_factory() { - Ok((library_dxgi, factory, dxgi_version)) => { - info!("DXGI version: {:?}", dxgi_version); - let library_d3d11 = Arc::new(unsafe { - libloading::Library::new("d3d11.dll").map_err(|_| hal::UnsupportedBackend)? - }); - Ok(Instance { - factory, - dxgi_version, - library_d3d11, - library_dxgi, - }) - } - Err(hr) => { - info!("Failed on factory creation: {:?}", hr); - Err(hal::UnsupportedBackend) - } - } - } - - fn enumerate_adapters(&self) -> Vec> { - let mut adapters = Vec::new(); - let mut idx = 0; - - let func: libloading::Symbol = - match unsafe { self.library_d3d11.get(b"D3D11CreateDevice") } { - Ok(func) => func, - Err(e) => { - error!("Unable to get device creation function: {:?}", e); - return Vec::new(); - } - }; - - while let Ok(adapter) = dxgi::get_adapter(idx, self.factory.as_raw(), self.dxgi_version) { - idx += 1; - - use hal::memory::Properties; - - // TODO: move into function? - let (device, feature_level) = { - let feature_level = get_feature_level(&func, adapter.as_raw()); - - let mut device = ptr::null_mut(); - let hr = unsafe { - func( - adapter.as_raw() as *mut _, - d3dcommon::D3D_DRIVER_TYPE_UNKNOWN, - ptr::null_mut(), - 0, - [feature_level].as_ptr(), - 1, - d3d11::D3D11_SDK_VERSION, - &mut device as *mut *mut _ as *mut *mut _, - ptr::null_mut(), - ptr::null_mut(), - ) - }; - - if !winerror::SUCCEEDED(hr) { - continue; - } - - ( - unsafe { ComPtr::::from_raw(device) }, - feature_level, - ) - }; - - let memory_properties = adapter::MemoryProperties { - memory_types: vec![ - adapter::MemoryType { - properties: Properties::DEVICE_LOCAL, - heap_index: 0, - }, - adapter::MemoryType { - properties: Properties::CPU_VISIBLE - | Properties::COHERENT - | Properties::CPU_CACHED, - heap_index: 1, - }, - adapter::MemoryType { - properties: Properties::CPU_VISIBLE | Properties::CPU_CACHED, - heap_index: 1, - }, - ], - // TODO: would using *VideoMemory and *SystemMemory from - // DXGI_ADAPTER_DESC be too optimistic? :) - memory_heaps: vec![ - adapter::MemoryHeap { - size: !0, - flags: memory::HeapFlags::DEVICE_LOCAL, - }, - adapter::MemoryHeap { - size: !0, - flags: memory::HeapFlags::empty(), - }, - ], - }; - - let info = dxgi::get_adapter_desc(&adapter, &device, self.dxgi_version); - let limits = get_limits(feature_level); - let (features, downlevel, performance_caveats) = - get_features(device.clone(), feature_level); - let format_properties = get_format_properties(device.clone()); - - let physical_device = PhysicalDevice { - adapter, - library_d3d11: Arc::clone(&self.library_d3d11), - features, - properties: hal::PhysicalDeviceProperties { - limits, - dynamic_pipeline_states: hal::DynamicStates::VIEWPORT - | hal::DynamicStates::SCISSOR - | hal::DynamicStates::BLEND_CONSTANTS - | hal::DynamicStates::DEPTH_BOUNDS - | hal::DynamicStates::STENCIL_REFERENCE, - downlevel, - performance_caveats, - ..hal::PhysicalDeviceProperties::default() - }, - memory_properties, - format_properties, - }; - - info!("{:#?}", info); - - adapters.push(adapter::Adapter { - info, - physical_device, - queue_families: vec![QueueFamily], - }); - } - - adapters - } - - unsafe fn create_surface( - &self, - has_handle: &impl raw_window_handle::HasRawWindowHandle, - ) -> Result { - match has_handle.raw_window_handle() { - raw_window_handle::RawWindowHandle::Windows(handle) => { - Ok(self.create_surface_from_hwnd(handle.hwnd)) - } - _ => Err(hal::window::InitError::UnsupportedWindowHandle), - } - } - - unsafe fn destroy_surface(&self, _surface: Surface) { - // TODO: Implement Surface cleanup - } - - unsafe fn create_display_plane_surface( - &self, - _display_plane: &display::DisplayPlane, - _plane_stack_index: u32, - _transformation: display::SurfaceTransform, - _alpha: display::DisplayPlaneAlpha, - _image_extent: hal::window::Extent2D, - ) -> Result { - unimplemented!(); - } -} - -pub struct PhysicalDevice { - adapter: ComPtr, - library_d3d11: Arc, - features: hal::Features, - properties: hal::PhysicalDeviceProperties, - memory_properties: adapter::MemoryProperties, - format_properties: [format::Properties; format::NUM_FORMATS], -} - -impl fmt::Debug for PhysicalDevice { - fn fmt(&self, fmt: &mut fmt::Formatter) -> fmt::Result { - fmt.write_str("PhysicalDevice") - } -} - -unsafe impl Send for PhysicalDevice {} -unsafe impl Sync for PhysicalDevice {} - -// TODO: does the adapter we get earlier matter for feature level? -fn get_feature_level(func: &CreateFun, adapter: *mut IDXGIAdapter) -> d3dcommon::D3D_FEATURE_LEVEL { - let requested_feature_levels = [ - d3dcommon::D3D_FEATURE_LEVEL_11_1, - d3dcommon::D3D_FEATURE_LEVEL_11_0, - d3dcommon::D3D_FEATURE_LEVEL_10_1, - d3dcommon::D3D_FEATURE_LEVEL_10_0, - d3dcommon::D3D_FEATURE_LEVEL_9_3, - d3dcommon::D3D_FEATURE_LEVEL_9_2, - d3dcommon::D3D_FEATURE_LEVEL_9_1, - ]; - - let mut feature_level = d3dcommon::D3D_FEATURE_LEVEL_9_1; - let hr = unsafe { - func( - adapter, - d3dcommon::D3D_DRIVER_TYPE_UNKNOWN, - ptr::null_mut(), - 0, - requested_feature_levels[..].as_ptr(), - requested_feature_levels.len() as _, - d3d11::D3D11_SDK_VERSION, - ptr::null_mut(), - &mut feature_level as *mut _, - ptr::null_mut(), - ) - }; - - if !winerror::SUCCEEDED(hr) { - // if there is no 11.1 runtime installed, requesting - // `D3D_FEATURE_LEVEL_11_1` will return E_INVALIDARG so we just retry - // without that - if hr == winerror::E_INVALIDARG { - let hr = unsafe { - func( - adapter, - d3dcommon::D3D_DRIVER_TYPE_UNKNOWN, - ptr::null_mut(), - 0, - requested_feature_levels[1..].as_ptr(), - (requested_feature_levels.len() - 1) as _, - d3d11::D3D11_SDK_VERSION, - ptr::null_mut(), - &mut feature_level as *mut _, - ptr::null_mut(), - ) - }; - - if !winerror::SUCCEEDED(hr) { - // TODO: device might not support any feature levels? - unimplemented!(); - } - } - } - - feature_level -} - -// TODO: PhysicalDevice -impl adapter::PhysicalDevice for PhysicalDevice { - unsafe fn open( - &self, - families: &[(&QueueFamily, &[queue::QueuePriority])], - requested_features: hal::Features, - ) -> Result, hal::device::CreationError> { - let func: libloading::Symbol = - self.library_d3d11.get(b"D3D11CreateDevice").unwrap(); - - let (device, cxt, feature_level) = { - if !self.features().contains(requested_features) { - return Err(hal::device::CreationError::MissingFeature); - } - - let feature_level = get_feature_level(&func, self.adapter.as_raw()); - let mut returned_level = d3dcommon::D3D_FEATURE_LEVEL_9_1; - - #[cfg(debug_assertions)] - let create_flags = d3d11::D3D11_CREATE_DEVICE_DEBUG; - #[cfg(not(debug_assertions))] - let create_flags = 0; - - // TODO: request debug device only on debug config? - let mut device: *mut d3d11::ID3D11Device = ptr::null_mut(); - let mut cxt = ptr::null_mut(); - let hr = func( - self.adapter.as_raw() as *mut _, - d3dcommon::D3D_DRIVER_TYPE_UNKNOWN, - ptr::null_mut(), - create_flags, - [feature_level].as_ptr(), - 1, - d3d11::D3D11_SDK_VERSION, - &mut device as *mut *mut _ as *mut *mut _, - &mut returned_level as *mut _, - &mut cxt as *mut *mut _ as *mut *mut _, - ); - - // NOTE: returns error if adapter argument is non-null and driver - // type is not unknown; or if debug device is requested but not - // present - if !winerror::SUCCEEDED(hr) { - if cfg!(debug_assertions) { - log::warn!( - "Unable to create a debug device. Trying to recreate a device without D3D11_CREATE_DEVICE_DEBUG flag. More info:\n{}\n{}", - "https://docs.microsoft.com/en-us/windows/win32/direct3d11/overviews-direct3d-11-devices-layers#debug-layer", - "https://github.com/gfx-rs/gfx/issues/3112" - ); - - let hr = func( - self.adapter.as_raw() as *mut _, - d3dcommon::D3D_DRIVER_TYPE_UNKNOWN, - ptr::null_mut(), - 0, - [feature_level].as_ptr(), - 1, - d3d11::D3D11_SDK_VERSION, - &mut device as *mut *mut _ as *mut *mut _, - &mut returned_level as *mut _, - &mut cxt as *mut *mut _ as *mut *mut _, - ); - - if !winerror::SUCCEEDED(hr) { - return Err(hal::device::CreationError::InitializationFailed); - } - } else { - return Err(hal::device::CreationError::InitializationFailed); - } - } - - info!( - "feature level={:x}=FL{}_{}", - feature_level, - feature_level >> 12, - feature_level >> 8 & 0xF - ); - - ( - ComPtr::from_raw(device), - ComPtr::from_raw(cxt), - feature_level, - ) - }; - - let device1 = device.cast::().ok(); - - let device = device::Device::new( - device, - device1, - cxt, - requested_features, - self.properties.downlevel, - self.memory_properties.clone(), - feature_level, - ); - - // TODO: deferred context => 1 cxt/queue? - let queue_groups = families - .iter() - .map(|&(_family, prio)| { - assert_eq!(prio.len(), 1); - let mut group = queue::QueueGroup::new(queue::QueueFamilyId(0)); - - // TODO: multiple queues? - let queue = Queue { - context: device.context.clone(), - }; - group.add_queue(queue); - group - }) - .collect(); - - Ok(adapter::Gpu { - device, - queue_groups, - }) - } - - fn format_properties(&self, fmt: Option) -> format::Properties { - let idx = fmt.map(|fmt| fmt as usize).unwrap_or(0); - self.format_properties[idx].clone() - } - - fn image_format_properties( - &self, - format: format::Format, - dimensions: u8, - tiling: image::Tiling, - usage: image::Usage, - view_caps: image::ViewCapabilities, - ) -> Option { - conv::map_format(format)?; //filter out unknown formats - - let supported_usage = { - use hal::image::Usage as U; - let format_props = &self.format_properties[format as usize]; - let props = match tiling { - image::Tiling::Optimal => format_props.optimal_tiling, - image::Tiling::Linear => format_props.linear_tiling, - }; - let mut flags = U::empty(); - // Note: these checks would have been nicer if we had explicit BLIT usage - if props.contains(format::ImageFeature::BLIT_SRC) { - flags |= U::TRANSFER_SRC; - } - if props.contains(format::ImageFeature::BLIT_DST) { - flags |= U::TRANSFER_DST; - } - if props.contains(format::ImageFeature::SAMPLED) { - flags |= U::SAMPLED; - } - if props.contains(format::ImageFeature::STORAGE) { - flags |= U::STORAGE; - } - if props.contains(format::ImageFeature::COLOR_ATTACHMENT) { - flags |= U::COLOR_ATTACHMENT; - } - if props.contains(format::ImageFeature::DEPTH_STENCIL_ATTACHMENT) { - flags |= U::DEPTH_STENCIL_ATTACHMENT; - } - flags - }; - if !supported_usage.contains(usage) { - return None; - } - - let max_resource_size = - (d3d11::D3D11_REQ_RESOURCE_SIZE_IN_MEGABYTES_EXPRESSION_A_TERM as usize) << 20; - Some(match tiling { - image::Tiling::Optimal => image::FormatProperties { - max_extent: match dimensions { - 1 => image::Extent { - width: d3d11::D3D11_REQ_TEXTURE1D_U_DIMENSION, - height: 1, - depth: 1, - }, - 2 => image::Extent { - width: d3d11::D3D11_REQ_TEXTURE2D_U_OR_V_DIMENSION, - height: d3d11::D3D11_REQ_TEXTURE2D_U_OR_V_DIMENSION, - depth: 1, - }, - 3 => image::Extent { - width: d3d11::D3D11_REQ_TEXTURE3D_U_V_OR_W_DIMENSION, - height: d3d11::D3D11_REQ_TEXTURE3D_U_V_OR_W_DIMENSION, - depth: d3d11::D3D11_REQ_TEXTURE3D_U_V_OR_W_DIMENSION, - }, - _ => return None, - }, - max_levels: d3d11::D3D11_REQ_MIP_LEVELS as _, - max_layers: match dimensions { - 1 => d3d11::D3D11_REQ_TEXTURE1D_ARRAY_AXIS_DIMENSION as _, - 2 => d3d11::D3D11_REQ_TEXTURE2D_ARRAY_AXIS_DIMENSION as _, - _ => return None, - }, - sample_count_mask: if dimensions == 2 - && !view_caps.contains(image::ViewCapabilities::KIND_CUBE) - && (usage.contains(image::Usage::COLOR_ATTACHMENT) - | usage.contains(image::Usage::DEPTH_STENCIL_ATTACHMENT)) - { - 0x3F //TODO: use D3D12_FEATURE_DATA_FORMAT_SUPPORT - } else { - 0x1 - }, - max_resource_size, - }, - image::Tiling::Linear => image::FormatProperties { - max_extent: match dimensions { - 2 => image::Extent { - width: d3d11::D3D11_REQ_TEXTURE2D_U_OR_V_DIMENSION, - height: d3d11::D3D11_REQ_TEXTURE2D_U_OR_V_DIMENSION, - depth: 1, - }, - _ => return None, - }, - max_levels: 1, - max_layers: 1, - sample_count_mask: 0x1, - max_resource_size, - }, - }) - } - - fn memory_properties(&self) -> adapter::MemoryProperties { - self.memory_properties.clone() - } - - fn external_buffer_properties( - &self, - _usage: hal::buffer::Usage, - _sparse: hal::memory::SparseFlags, - _memory_type: hal::external_memory::ExternalMemoryType, - ) -> hal::external_memory::ExternalMemoryProperties { - unimplemented!() - } - - fn external_image_properties( - &self, - _format: hal::format::Format, - _dimensions: u8, - _tiling: hal::image::Tiling, - _usage: hal::image::Usage, - _view_caps: hal::image::ViewCapabilities, - _memory_type: hal::external_memory::ExternalMemoryType, - ) -> Result< - hal::external_memory::ExternalMemoryProperties, - hal::external_memory::ExternalImagePropertiesError, - > { - unimplemented!() - } - - fn features(&self) -> hal::Features { - self.features - } - - fn properties(&self) -> hal::PhysicalDeviceProperties { - self.properties - } - - unsafe fn enumerate_displays(&self) -> Vec> { - unimplemented!(); - } - - unsafe fn enumerate_compatible_planes( - &self, - _display: &display::Display, - ) -> Vec { - unimplemented!(); - } - - unsafe fn create_display_mode( - &self, - _display: &display::Display, - _resolution: (u32, u32), - _refresh_rate: u32, - ) -> Result, display::DisplayModeError> { - unimplemented!(); - } - - unsafe fn create_display_plane<'a>( - &self, - _display: &'a display::DisplayMode, - _plane: &'a display::Plane, - ) -> Result, hal::device::OutOfMemory> { - unimplemented!(); - } -} - -struct Presentation { - swapchain: ComPtr, - view: ComPtr, - format: format::Format, - size: window::Extent2D, - mode: window::PresentMode, - image: Arc, - is_init: bool, -} - -pub struct Surface { - pub(crate) factory: ComPtr, - wnd_handle: HWND, - presentation: Option, -} - -impl fmt::Debug for Surface { - fn fmt(&self, fmt: &mut fmt::Formatter) -> fmt::Result { - fmt.write_str("Surface") - } -} - -unsafe impl Send for Surface {} -unsafe impl Sync for Surface {} - -impl window::Surface for Surface { - fn supports_queue_family(&self, _queue_family: &QueueFamily) -> bool { - true - } - - fn capabilities(&self, _physical_device: &PhysicalDevice) -> window::SurfaceCapabilities { - let current_extent = unsafe { - let mut rect: RECT = mem::zeroed(); - assert_ne!( - 0, - GetClientRect(self.wnd_handle as *mut _, &mut rect as *mut RECT) - ); - Some(window::Extent2D { - width: (rect.right - rect.left) as u32, - height: (rect.bottom - rect.top) as u32, - }) - }; - - // TODO: flip swap effects require dx11.1/windows8 - // NOTE: some swap effects affect msaa capabilities.. - // TODO: _DISCARD swap effects can only have one image? - window::SurfaceCapabilities { - present_modes: window::PresentMode::IMMEDIATE | window::PresentMode::FIFO, - composite_alpha_modes: window::CompositeAlphaMode::OPAQUE, //TODO - image_count: 1..=16, // TODO: - current_extent, - extents: window::Extent2D { - width: 16, - height: 16, - }..=window::Extent2D { - width: 4096, - height: 4096, - }, - max_image_layers: 1, - usage: image::Usage::COLOR_ATTACHMENT, - } - } - - fn supported_formats(&self, _physical_device: &PhysicalDevice) -> Option> { - Some(vec![ - format::Format::Bgra8Srgb, - format::Format::Bgra8Unorm, - format::Format::Rgba8Srgb, - format::Format::Rgba8Unorm, - format::Format::A2b10g10r10Unorm, - format::Format::Rgba16Sfloat, - ]) - } -} - -#[derive(Debug)] -pub struct SwapchainImage { - image: Arc, - view: ImageView, -} -impl Borrow for SwapchainImage { - fn borrow(&self) -> &Image { - &*self.image - } -} -impl Borrow for SwapchainImage { - fn borrow(&self) -> &ImageView { - &self.view - } -} - -impl window::PresentationSurface for Surface { - type SwapchainImage = SwapchainImage; - - unsafe fn configure_swapchain( - &mut self, - device: &device::Device, - config: window::SwapchainConfig, - ) -> Result<(), window::SwapchainError> { - assert!(image::Usage::COLOR_ATTACHMENT.contains(config.image_usage)); - - let swapchain = match self.presentation.take() { - Some(present) => { - if present.format == config.format && present.size == config.extent { - self.presentation = Some(present); - return Ok(()); - } - let non_srgb_format = conv::map_format_nosrgb(config.format).unwrap(); - - // Delete the existing view into the swapchain buffers. - drop(present.view); - - // We must also delete the image data. - // - // This should not panic as all images must be deleted before - let mut present_image = Arc::try_unwrap(present.image).expect( - "Not all acquired images were deleted before the swapchain was reconfigured.", - ); - present_image.internal.release_resources(); - - let result = present.swapchain.ResizeBuffers( - config.image_count, - config.extent.width, - config.extent.height, - non_srgb_format, - 0, - ); - if result != winerror::S_OK { - error!("ResizeBuffers failed with 0x{:x}", result as u32); - return Err(window::SwapchainError::WindowInUse); - } - present.swapchain - } - None => { - let (swapchain, _) = - device.create_swapchain_impl(&config, self.wnd_handle, self.factory.clone())?; - swapchain - } - }; - - // Disable automatic Alt+Enter handling by DXGI. - const DXGI_MWA_NO_WINDOW_CHANGES: u32 = 1; - const DXGI_MWA_NO_ALT_ENTER: u32 = 2; - self.factory.MakeWindowAssociation( - self.wnd_handle, - DXGI_MWA_NO_WINDOW_CHANGES | DXGI_MWA_NO_ALT_ENTER, - ); - - let mut resource: *mut d3d11::ID3D11Resource = ptr::null_mut(); - assert_eq!( - winerror::S_OK, - swapchain.GetBuffer( - 0 as _, - &d3d11::ID3D11Resource::uuidof(), - &mut resource as *mut *mut _ as *mut *mut _, - ) - ); - set_debug_name(&*resource, "Swapchain Image"); - - let kind = image::Kind::D2(config.extent.width, config.extent.height, 1, 1); - let format = conv::map_format(config.format).unwrap(); - let decomposed_format = conv::DecomposedDxgiFormat::from_dxgi_format(format); - - let view_info = ViewInfo { - resource, - kind, - caps: image::ViewCapabilities::empty(), - view_kind: image::ViewKind::D2, - format: decomposed_format.rtv.unwrap(), - levels: 0..1, - layers: 0..1, - }; - let view = device.view_image_as_render_target(&view_info).unwrap(); - set_debug_name(&view, "Swapchain Image View"); - - self.presentation = Some(Presentation { - swapchain, - view, - format: config.format, - size: config.extent, - mode: config.present_mode, - image: Arc::new(Image { - kind, - usage: config.image_usage, - format: config.format, - view_caps: image::ViewCapabilities::empty(), - decomposed_format, - mip_levels: 1, - internal: InternalImage { - raw: resource, - copy_srv: None, //TODO - srv: None, //TODO - unordered_access_views: Vec::new(), - depth_stencil_views: Vec::new(), - render_target_views: Vec::new(), - debug_name: None, - }, - bind: conv::map_image_usage( - config.image_usage, - config.format.surface_desc(), - device.internal.device_feature_level, - ), - requirements: memory::Requirements { - size: 0, - alignment: 1, - type_mask: 0, - }, - }), - is_init: true, - }); - Ok(()) - } - - unsafe fn unconfigure_swapchain(&mut self, _device: &device::Device) { - self.presentation = None; - } - - unsafe fn acquire_image( - &mut self, - _timeout_ns: u64, //TODO: use the timeout - ) -> Result<(SwapchainImage, Option), window::AcquireError> { - let present = self.presentation.as_ref().unwrap(); - let swapchain_image = SwapchainImage { - image: Arc::clone(&present.image), - view: ImageView { - subresource: d3d11::D3D11CalcSubresource(0, 0, 1), - format: present.format, - rtv_handle: Some(present.view.as_raw()), - dsv_handle: None, - srv_handle: None, - uav_handle: None, - rodsv_handle: None, - owned: false, - }, - }; - Ok((swapchain_image, None)) - } -} - -#[derive(Debug, Clone, Copy)] -pub struct QueueFamily; - -impl queue::QueueFamily for QueueFamily { - fn queue_type(&self) -> queue::QueueType { - queue::QueueType::General - } - fn max_queues(&self) -> usize { - 1 - } - fn id(&self) -> queue::QueueFamilyId { - queue::QueueFamilyId(0) - } - fn supports_sparse_binding(&self) -> bool { - false - } -} - -#[derive(Clone)] -pub struct Queue { - context: ComPtr, -} - -impl fmt::Debug for Queue { - fn fmt(&self, fmt: &mut fmt::Formatter) -> fmt::Result { - fmt.write_str("Queue") - } -} - -unsafe impl Send for Queue {} -unsafe impl Sync for Queue {} - -impl queue::Queue for Queue { - unsafe fn submit<'a, Ic, Iw, Is>( - &mut self, - command_buffers: Ic, - _wait_semaphores: Iw, - _signal_semaphores: Is, - fence: Option<&mut Fence>, - ) where - Ic: Iterator, - { - let _scope = debug_scope!(&self.context, "Submit(fence={:?})", fence); - for cmd_buf in command_buffers { - let _scope = debug_scope!( - &self.context, - "CommandBuffer ({}/{})", - cmd_buf.flush_coherent_memory.len(), - cmd_buf.invalidate_coherent_memory.len() - ); - - { - let _scope = debug_scope!(&self.context, "Pre-Exec: Flush"); - for sync in &cmd_buf.flush_coherent_memory { - sync.do_flush(&self.context); - } - } - self.context - .ExecuteCommandList(cmd_buf.as_raw_list().as_raw(), FALSE); - { - let _scope = debug_scope!(&self.context, "Post-Exec: Invalidate"); - for sync in &cmd_buf.invalidate_coherent_memory { - sync.do_invalidate(&self.context); - } - } - } - - if let Some(fence) = fence { - *fence.mutex.lock() = true; - fence.condvar.notify_all(); - } - } - - unsafe fn present( - &mut self, - surface: &mut Surface, - _image: SwapchainImage, - _wait_semaphore: Option<&mut Semaphore>, - ) -> Result, window::PresentError> { - let mut presentation = surface.presentation.as_mut().unwrap(); - let (interval, flags) = match presentation.mode { - window::PresentMode::IMMEDIATE => (0, 0), - //Note: this ends up not presenting anything for some reason - //window::PresentMode::MAILBOX if !presentation.is_init => (1, DXGI_PRESENT_DO_NOT_SEQUENCE), - window::PresentMode::FIFO => (1, 0), - _ => (0, 0), - }; - presentation.is_init = false; - presentation.swapchain.Present(interval, flags); - Ok(None) - } - - fn wait_idle(&mut self) -> Result<(), hal::device::OutOfMemory> { - // unimplemented!() - Ok(()) - } - - fn timestamp_period(&self) -> f32 { - 1.0 - } -} - -#[derive(Debug)] -pub struct AttachmentInfo { - subpass_id: Option, - view: ImageView, - clear_color: Option<(usize, command::ClearColor)>, - clear_depth: Option, - clear_stencil: Option, -} - -#[derive(Debug)] -pub struct RenderPassCache { - pub render_pass: RenderPass, - pub attachments: Vec, - pub target_rect: pso::Rect, - pub num_layers: image::Layer, - pub current_subpass: pass::SubpassId, -} - -impl RenderPassCache { - pub fn start_subpass( - &mut self, - internal: &internal::Internal, - context: &ComPtr, - cache: &mut CommandBufferState, - ) { - let mut clears = Vec::new(); - for at in self.attachments.iter() { - if at.subpass_id == Some(self.current_subpass) { - if let Some((index, value)) = at.clear_color { - clears.push(command::AttachmentClear::Color { index, value }); - } - if at.clear_depth.is_some() || at.clear_stencil.is_some() { - clears.push(command::AttachmentClear::DepthStencil { - depth: at.clear_depth, - stencil: at.clear_stencil, - }); - } - } - } - - cache.dirty_flag.insert( - DirtyStateFlag::GRAPHICS_PIPELINE - | DirtyStateFlag::DEPTH_STENCIL_STATE - | DirtyStateFlag::PIPELINE_PS - | DirtyStateFlag::VIEWPORTS - | DirtyStateFlag::RENDER_TARGETS_AND_UAVS, - ); - internal.clear_attachments( - context, - clears.into_iter(), - Some(pso::ClearRect { - rect: self.target_rect, - layers: 0..1, - }) - .into_iter(), - &self, - ); - - let subpass = &self.render_pass.subpasses[self.current_subpass as usize]; - let color_views = subpass - .color_attachments - .iter() - .map(|&(id, _)| self.attachments[id].view.rtv_handle.unwrap()) - .collect::>(); - let (ds_view, rods_view) = match subpass.depth_stencil_attachment { - Some((id, _)) => { - let attachment = &self.attachments[id].view; - let ds_view = attachment.dsv_handle.unwrap(); - - (Some(ds_view), attachment.rodsv_handle) - } - None => (None, None), - }; - - cache.set_render_targets(&color_views, ds_view, rods_view); - cache.bind(context); - } - - fn resolve_msaa(&mut self, context: &ComPtr) { - let subpass: &SubpassDesc = &self.render_pass.subpasses[self.current_subpass as usize]; - - for (&(color_id, _), &(resolve_id, _)) in subpass - .color_attachments - .iter() - .zip(subpass.resolve_attachments.iter()) - { - if color_id == pass::ATTACHMENT_UNUSED || resolve_id == pass::ATTACHMENT_UNUSED { - continue; - } - - let color_view = &self.attachments[color_id].view; - let resolve_view = &self.attachments[resolve_id].view; - - let mut color_resource: *mut d3d11::ID3D11Resource = ptr::null_mut(); - let mut resolve_resource: *mut d3d11::ID3D11Resource = ptr::null_mut(); - - unsafe { - (&*color_view - .rtv_handle - .expect("Framebuffer must have COLOR_ATTACHMENT usage")) - .GetResource(&mut color_resource as *mut *mut _); - (&*resolve_view - .rtv_handle - .expect("Resolve texture must have COLOR_ATTACHMENT usage")) - .GetResource(&mut resolve_resource as *mut *mut _); - - context.ResolveSubresource( - resolve_resource, - resolve_view.subresource, - color_resource, - color_view.subresource, - conv::map_format(color_view.format).unwrap(), - ); - - (&*color_resource).Release(); - (&*resolve_resource).Release(); - } - } - } - - pub fn next_subpass(&mut self, context: &ComPtr) { - self.resolve_msaa(context); - self.current_subpass += 1; - } -} - -bitflags! { - struct DirtyStateFlag : u32 { - const RENDER_TARGETS_AND_UAVS = (1 << 1); - const VERTEX_BUFFERS = (1 << 2); - const GRAPHICS_PIPELINE = (1 << 3); - const PIPELINE_GS = (1 << 4); - const PIPELINE_HS = (1 << 5); - const PIPELINE_DS = (1 << 6); - const PIPELINE_PS = (1 << 7); - const VIEWPORTS = (1 << 8); - const BLEND_STATE = (1 << 9); - const DEPTH_STENCIL_STATE = (1 << 10); - } -} - -pub struct CommandBufferState { - dirty_flag: DirtyStateFlag, - - render_target_len: u32, - render_targets: [*mut d3d11::ID3D11RenderTargetView; 8], - uav_len: u32, - uavs: [*mut d3d11::ID3D11UnorderedAccessView; d3d11::D3D11_PS_CS_UAV_REGISTER_COUNT as _], - depth_target: Option<*mut d3d11::ID3D11DepthStencilView>, - readonly_depth_target: Option<*mut d3d11::ID3D11DepthStencilView>, - depth_target_read_only: bool, - graphics_pipeline: Option, - - // a bitmask that keeps track of what vertex buffer bindings have been "bound" into - // our vec - bound_bindings: u32, - // a bitmask that hold the required binding slots to be bound for the currently - // bound pipeline - required_bindings: Option, - // the highest binding number in currently bound pipeline - max_bindings: Option, - viewports: Vec, - vertex_buffers: Vec<*mut d3d11::ID3D11Buffer>, - vertex_offsets: Vec, - vertex_strides: Vec, - blend_factor: Option<[f32; 4]>, - // we can only support one face (rather, both faces must have the same value) - stencil_ref: Option, - stencil_read_mask: Option, - stencil_write_mask: Option, - current_blend: Option<*mut d3d11::ID3D11BlendState>, -} - -impl fmt::Debug for CommandBufferState { - fn fmt(&self, fmt: &mut fmt::Formatter) -> fmt::Result { - fmt.write_str("CommandBufferState") - } -} - -impl CommandBufferState { - fn new() -> Self { - CommandBufferState { - dirty_flag: DirtyStateFlag::empty(), - render_target_len: 0, - render_targets: [ptr::null_mut(); 8], - uav_len: 0, - uavs: [ptr::null_mut(); 8], - depth_target: None, - readonly_depth_target: None, - depth_target_read_only: false, - graphics_pipeline: None, - bound_bindings: 0, - required_bindings: None, - max_bindings: None, - viewports: Vec::new(), - vertex_buffers: Vec::new(), - vertex_offsets: Vec::new(), - vertex_strides: Vec::new(), - blend_factor: None, - stencil_ref: None, - stencil_read_mask: None, - stencil_write_mask: None, - current_blend: None, - } - } - - fn clear(&mut self) { - self.render_target_len = 0; - self.uav_len = 0; - self.depth_target = None; - self.readonly_depth_target = None; - self.depth_target_read_only = false; - self.graphics_pipeline = None; - self.bound_bindings = 0; - self.required_bindings = None; - self.max_bindings = None; - self.viewports.clear(); - self.vertex_buffers.clear(); - self.vertex_offsets.clear(); - self.vertex_strides.clear(); - self.blend_factor = None; - self.stencil_ref = None; - self.stencil_read_mask = None; - self.stencil_write_mask = None; - self.current_blend = None; - } - - pub fn set_vertex_buffer( - &mut self, - index: usize, - offset: u32, - buffer: *mut d3d11::ID3D11Buffer, - ) { - self.bound_bindings |= 1 << index as u32; - - if index >= self.vertex_buffers.len() { - self.vertex_buffers.push(buffer); - self.vertex_offsets.push(offset); - } else { - self.vertex_buffers[index] = buffer; - self.vertex_offsets[index] = offset; - } - - self.dirty_flag.insert(DirtyStateFlag::VERTEX_BUFFERS); - } - - pub fn bind_vertex_buffers(&mut self, context: &ComPtr) { - if !self.dirty_flag.contains(DirtyStateFlag::VERTEX_BUFFERS) { - return; - } - - if let Some(binding_count) = self.max_bindings { - if self.vertex_buffers.len() >= binding_count as usize - && self.vertex_strides.len() >= binding_count as usize - { - unsafe { - context.IASetVertexBuffers( - 0, - binding_count, - self.vertex_buffers.as_ptr(), - self.vertex_strides.as_ptr(), - self.vertex_offsets.as_ptr(), - ); - } - - self.dirty_flag.remove(DirtyStateFlag::VERTEX_BUFFERS); - } - } - } - - pub fn set_viewports(&mut self, viewports: &[d3d11::D3D11_VIEWPORT]) { - self.viewports.clear(); - self.viewports.extend(viewports); - - self.dirty_flag.insert(DirtyStateFlag::VIEWPORTS); - } - - pub fn bind_viewports(&mut self, context: &ComPtr) { - if !self.dirty_flag.contains(DirtyStateFlag::VIEWPORTS) { - return; - } - - if let Some(ref pipeline) = self.graphics_pipeline { - if let Some(ref viewport) = pipeline.baked_states.viewport { - unsafe { - context.RSSetViewports(1, [conv::map_viewport(viewport)].as_ptr()); - } - } else { - unsafe { - context.RSSetViewports(self.viewports.len() as u32, self.viewports.as_ptr()); - } - } - } else { - unsafe { - context.RSSetViewports(self.viewports.len() as u32, self.viewports.as_ptr()); - } - } - - self.dirty_flag.remove(DirtyStateFlag::VIEWPORTS); - } - - pub fn set_render_targets( - &mut self, - render_targets: &[*mut d3d11::ID3D11RenderTargetView], - depth_target: Option<*mut d3d11::ID3D11DepthStencilView>, - readonly_depth_target: Option<*mut d3d11::ID3D11DepthStencilView>, - ) { - for (idx, &rt) in render_targets.iter().enumerate() { - self.render_targets[idx] = rt; - } - - self.render_target_len = render_targets.len() as u32; - self.depth_target = depth_target; - self.readonly_depth_target = readonly_depth_target; - - self.dirty_flag - .insert(DirtyStateFlag::RENDER_TARGETS_AND_UAVS); - } - - pub fn bind_render_targets(&mut self, context: &ComPtr) { - if !self - .dirty_flag - .contains(DirtyStateFlag::RENDER_TARGETS_AND_UAVS) - { - return; - } - - let depth_target = if self.depth_target_read_only { - self.readonly_depth_target - } else { - self.depth_target - } - .unwrap_or(ptr::null_mut()); - - let uav_start_index = d3d11::D3D11_PS_CS_UAV_REGISTER_COUNT - self.uav_len; - - unsafe { - if self.uav_len > 0 { - context.OMSetRenderTargetsAndUnorderedAccessViews( - self.render_target_len, - self.render_targets.as_ptr(), - depth_target, - uav_start_index, - self.uav_len, - &self.uavs[uav_start_index as usize] as *const *mut _, - ptr::null(), - ) - } else { - context.OMSetRenderTargets( - self.render_target_len, - self.render_targets.as_ptr(), - depth_target, - ) - }; - } - - self.dirty_flag - .remove(DirtyStateFlag::RENDER_TARGETS_AND_UAVS); - } - - pub fn set_blend_factor(&mut self, factor: [f32; 4]) { - self.blend_factor = Some(factor); - - self.dirty_flag.insert(DirtyStateFlag::BLEND_STATE); - } - - pub fn bind_blend_state(&mut self, context: &ComPtr) { - if let Some(blend) = self.current_blend { - let blend_color = if let Some(ref pipeline) = self.graphics_pipeline { - pipeline - .baked_states - .blend_constants - .or(self.blend_factor) - .unwrap_or([0f32; 4]) - } else { - self.blend_factor.unwrap_or([0f32; 4]) - }; - - // TODO: MSAA - unsafe { - context.OMSetBlendState(blend, &blend_color, !0); - } - - self.dirty_flag.remove(DirtyStateFlag::BLEND_STATE); - } - } - - pub fn set_stencil_ref(&mut self, value: pso::StencilValue) { - self.stencil_ref = Some(value); - self.dirty_flag.insert(DirtyStateFlag::DEPTH_STENCIL_STATE); - } - - pub fn bind_depth_stencil_state(&mut self, context: &ComPtr) { - if !self - .dirty_flag - .contains(DirtyStateFlag::DEPTH_STENCIL_STATE) - { - return; - } - - let pipeline = match self.graphics_pipeline { - Some(ref pipeline) => pipeline, - None => return, - }; - - if let Some(ref state) = pipeline.depth_stencil_state { - let stencil_ref = state.stencil_ref.static_or(self.stencil_ref.unwrap_or(0)); - - unsafe { - context.OMSetDepthStencilState(state.raw.as_raw(), stencil_ref); - } - } - - self.dirty_flag.remove(DirtyStateFlag::DEPTH_STENCIL_STATE) - } - - pub fn set_graphics_pipeline(&mut self, pipeline: GraphicsPipeline) { - let prev = self.graphics_pipeline.take(); - - let mut prev_has_ps = false; - let mut prev_has_gs = false; - let mut prev_has_ds = false; - let mut prev_has_hs = false; - if let Some(p) = prev { - prev_has_ps = p.ps.is_some(); - prev_has_gs = p.gs.is_some(); - prev_has_ds = p.ds.is_some(); - prev_has_hs = p.hs.is_some(); - } - - if prev_has_ps || pipeline.ps.is_some() { - self.dirty_flag.insert(DirtyStateFlag::PIPELINE_PS); - } - if prev_has_gs || pipeline.gs.is_some() { - self.dirty_flag.insert(DirtyStateFlag::PIPELINE_GS); - } - if prev_has_ds || pipeline.ds.is_some() { - self.dirty_flag.insert(DirtyStateFlag::PIPELINE_DS); - } - if prev_has_hs || pipeline.hs.is_some() { - self.dirty_flag.insert(DirtyStateFlag::PIPELINE_HS); - } - - // If we don't have depth stencil state, we use the old value, so we don't bother changing anything. - let depth_target_read_only = pipeline - .depth_stencil_state - .as_ref() - .map_or(self.depth_target_read_only, |ds| ds.read_only); - - if self.depth_target_read_only != depth_target_read_only { - self.depth_target_read_only = depth_target_read_only; - self.dirty_flag - .insert(DirtyStateFlag::RENDER_TARGETS_AND_UAVS); - } - - self.dirty_flag - .insert(DirtyStateFlag::GRAPHICS_PIPELINE | DirtyStateFlag::DEPTH_STENCIL_STATE); - - self.graphics_pipeline = Some(pipeline); - } - - pub fn bind_graphics_pipeline(&mut self, context: &ComPtr) { - if !self.dirty_flag.contains(DirtyStateFlag::GRAPHICS_PIPELINE) { - return; - } - - if let Some(ref pipeline) = self.graphics_pipeline { - self.vertex_strides.clear(); - self.vertex_strides.extend(&pipeline.strides); - - self.required_bindings = Some(pipeline.required_bindings); - self.max_bindings = Some(pipeline.max_vertex_bindings); - }; - - self.bind_vertex_buffers(context); - - if let Some(ref pipeline) = self.graphics_pipeline { - unsafe { - context.IASetPrimitiveTopology(pipeline.topology); - context.IASetInputLayout(pipeline.input_layout.as_raw()); - - context.VSSetShader(pipeline.vs.as_raw(), ptr::null_mut(), 0); - - if self.dirty_flag.contains(DirtyStateFlag::PIPELINE_PS) { - let ps = pipeline - .ps - .as_ref() - .map_or(ptr::null_mut(), |ps| ps.as_raw()); - context.PSSetShader(ps, ptr::null_mut(), 0); - - self.dirty_flag.remove(DirtyStateFlag::PIPELINE_PS) - } - - if self.dirty_flag.contains(DirtyStateFlag::PIPELINE_GS) { - let gs = pipeline - .gs - .as_ref() - .map_or(ptr::null_mut(), |gs| gs.as_raw()); - context.GSSetShader(gs, ptr::null_mut(), 0); - - self.dirty_flag.remove(DirtyStateFlag::PIPELINE_GS) - } - - if self.dirty_flag.contains(DirtyStateFlag::PIPELINE_HS) { - let hs = pipeline - .hs - .as_ref() - .map_or(ptr::null_mut(), |hs| hs.as_raw()); - context.HSSetShader(hs, ptr::null_mut(), 0); - - self.dirty_flag.remove(DirtyStateFlag::PIPELINE_HS) - } - - if self.dirty_flag.contains(DirtyStateFlag::PIPELINE_DS) { - let ds = pipeline - .ds - .as_ref() - .map_or(ptr::null_mut(), |ds| ds.as_raw()); - context.DSSetShader(ds, ptr::null_mut(), 0); - - self.dirty_flag.remove(DirtyStateFlag::PIPELINE_DS) - } - - context.RSSetState(pipeline.rasterizer_state.as_raw()); - if let Some(ref viewport) = pipeline.baked_states.viewport { - context.RSSetViewports(1, [conv::map_viewport(viewport)].as_ptr()); - } - if let Some(ref scissor) = pipeline.baked_states.scissor { - context.RSSetScissorRects(1, [conv::map_rect(&scissor)].as_ptr()); - } - - self.current_blend = Some(pipeline.blend_state.as_raw()); - } - }; - - self.bind_blend_state(context); - self.bind_depth_stencil_state(context); - - self.dirty_flag.remove(DirtyStateFlag::GRAPHICS_PIPELINE); - } - - pub fn bind(&mut self, context: &ComPtr) { - self.bind_render_targets(context); - self.bind_graphics_pipeline(context); - self.bind_vertex_buffers(context); - self.bind_viewports(context); - } -} - -type PerConstantBufferVec = - ArrayVec<[T; d3d11::D3D11_COMMONSHADER_CONSTANT_BUFFER_API_SLOT_COUNT as _]>; - -fn generate_graphics_dynamic_constant_buffer_offsets<'a>( - bindings: impl IntoIterator, - offset_iter: &mut impl Iterator, - context1_some: bool, -) -> (PerConstantBufferVec, PerConstantBufferVec) { - let mut vs_offsets = ArrayVec::new(); - let mut fs_offsets = ArrayVec::new(); - - let mut exists_dynamic_constant_buffer = false; - - for binding in bindings { - match binding.ty { - pso::DescriptorType::Buffer { - format: - pso::BufferDescriptorFormat::Structured { - dynamic_offset: true, - }, - ty: pso::BufferDescriptorType::Uniform, - } => { - let offset = offset_iter.next().unwrap(); - - if binding.stage_flags.contains(pso::ShaderStageFlags::VERTEX) { - vs_offsets.push(offset / 16) - }; - - if binding - .stage_flags - .contains(pso::ShaderStageFlags::FRAGMENT) - { - fs_offsets.push(offset / 16) - }; - exists_dynamic_constant_buffer = true; - } - pso::DescriptorType::Buffer { - format: - pso::BufferDescriptorFormat::Structured { - dynamic_offset: false, - }, - ty: pso::BufferDescriptorType::Uniform, - } => { - if binding.stage_flags.contains(pso::ShaderStageFlags::VERTEX) { - vs_offsets.push(0) - }; - - if binding - .stage_flags - .contains(pso::ShaderStageFlags::FRAGMENT) - { - fs_offsets.push(0) - }; - } - pso::DescriptorType::Buffer { - ty: pso::BufferDescriptorType::Storage { .. }, - format: - pso::BufferDescriptorFormat::Structured { - dynamic_offset: true, - }, - } => { - // TODO: Storage buffer offsets require new buffer views with correct sizes. - // Might also require D3D11_BUFFEREX_SRV to act like RBA is happening. - let _ = offset_iter.next().unwrap(); - warn!("Dynamic offsets into storage buffers are currently unsupported on DX11."); - } - _ => {} - } - } - - if exists_dynamic_constant_buffer && !context1_some { - warn!("D3D11.1 runtime required for dynamic offsets into constant buffers. Offsets will be ignored."); - } - - (vs_offsets, fs_offsets) -} - -fn generate_compute_dynamic_constant_buffer_offsets<'a>( - bindings: impl IntoIterator, - offset_iter: &mut impl Iterator, - context1_some: bool, -) -> PerConstantBufferVec { - let mut cs_offsets = ArrayVec::new(); - - let mut exists_dynamic_constant_buffer = false; - - for binding in bindings { - match binding.ty { - pso::DescriptorType::Buffer { - format: - pso::BufferDescriptorFormat::Structured { - dynamic_offset: true, - }, - ty: pso::BufferDescriptorType::Uniform, - } => { - let offset = offset_iter.next().unwrap(); - - if binding.stage_flags.contains(pso::ShaderStageFlags::COMPUTE) { - cs_offsets.push(offset / 16) - }; - - exists_dynamic_constant_buffer = true; - } - pso::DescriptorType::Buffer { - format: - pso::BufferDescriptorFormat::Structured { - dynamic_offset: false, - }, - ty: pso::BufferDescriptorType::Uniform, - } => { - if binding.stage_flags.contains(pso::ShaderStageFlags::COMPUTE) { - cs_offsets.push(0) - }; - } - pso::DescriptorType::Buffer { - ty: pso::BufferDescriptorType::Storage { .. }, - format: - pso::BufferDescriptorFormat::Structured { - dynamic_offset: true, - }, - } => { - // TODO: Storage buffer offsets require new buffer views with correct sizes. - // Might also require D3D11_BUFFEREX_SRV to act like RBA is happening. - let _ = offset_iter.next().unwrap(); - warn!("Dynamic offsets into storage buffers are currently unsupported on DX11."); - } - _ => {} - } - } - - if exists_dynamic_constant_buffer && !context1_some { - warn!("D3D11.1 runtime required for dynamic offsets into constant buffers. Offsets will be ignored."); - } - - cs_offsets -} - -pub struct CommandBuffer { - internal: Arc, - context: ComPtr, - context1: Option>, - list: RefCell>>, - - // since coherent memory needs to be synchronized at submission, we need to gather up all - // coherent resources that are used in the command buffer and flush/invalidate them accordingly - // before executing. - flush_coherent_memory: Vec, - invalidate_coherent_memory: Vec, - - // holds information about the active render pass - render_pass_cache: Option, - - // Have to update entire push constant buffer at once, keep whole buffer data local. - push_constant_data: [u32; MAX_PUSH_CONSTANT_SIZE / 4], - push_constant_buffer: ComPtr, - - cache: CommandBufferState, - - one_time_submit: bool, - - debug_name: Option, - debug_scopes: Vec>, -} - -impl fmt::Debug for CommandBuffer { - fn fmt(&self, fmt: &mut fmt::Formatter) -> fmt::Result { - fmt.write_str("CommandBuffer") - } -} - -unsafe impl Send for CommandBuffer {} -unsafe impl Sync for CommandBuffer {} - -impl CommandBuffer { - fn create_deferred( - device: ComPtr, - device1: Option<&d3d11_1::ID3D11Device1>, - internal: Arc, - ) -> Self { - let (context, context1) = if let Some(device1) = device1 { - let mut context1: *mut d3d11_1::ID3D11DeviceContext1 = ptr::null_mut(); - let hr = unsafe { device1.CreateDeferredContext1(0, &mut context1 as *mut *mut _) }; - assert_eq!(hr, winerror::S_OK); - - let context1 = unsafe { ComPtr::from_raw(context1) }; - let context = context1.cast::().unwrap(); - - (context, Some(context1)) - } else { - let mut context: *mut d3d11::ID3D11DeviceContext = ptr::null_mut(); - let hr = unsafe { device.CreateDeferredContext(0, &mut context as *mut *mut _) }; - assert_eq!(hr, winerror::S_OK); - - let context = unsafe { ComPtr::from_raw(context) }; - - (context, None) - }; - - let push_constant_buffer = { - let desc = d3d11::D3D11_BUFFER_DESC { - ByteWidth: MAX_PUSH_CONSTANT_SIZE as _, - Usage: d3d11::D3D11_USAGE_DEFAULT, - BindFlags: d3d11::D3D11_BIND_CONSTANT_BUFFER, - CPUAccessFlags: 0, - MiscFlags: 0, - StructureByteStride: 0, - }; - - let mut buffer: *mut d3d11::ID3D11Buffer = ptr::null_mut(); - let hr = unsafe { - device.CreateBuffer(&desc as *const _, ptr::null_mut(), &mut buffer as *mut _) - }; - - assert_eq!(hr, winerror::S_OK); - - unsafe { ComPtr::from_raw(buffer) } - }; - - let push_constant_data = [0_u32; 64]; - - CommandBuffer { - internal, - context, - context1, - list: RefCell::new(None), - flush_coherent_memory: Vec::new(), - invalidate_coherent_memory: Vec::new(), - render_pass_cache: None, - push_constant_data, - push_constant_buffer, - cache: CommandBufferState::new(), - one_time_submit: false, - debug_name: None, - debug_scopes: Vec::new(), - } - } - - fn as_raw_list(&self) -> ComPtr { - if self.one_time_submit { - self.list.replace(None).unwrap() - } else { - self.list.borrow().clone().unwrap() - } - } - - fn defer_coherent_flush(&mut self, buffer: &Buffer) { - if !self - .flush_coherent_memory - .iter() - .any(|m| m.buffer == buffer.internal.raw) - { - self.flush_coherent_memory.push(MemoryFlush { - host_memory: buffer.memory_ptr, - sync_range: SyncRange::Whole, - buffer: buffer.internal.raw, - }); - } - } - - fn defer_coherent_invalidate(&mut self, buffer: &Buffer) { - if !self - .invalidate_coherent_memory - .iter() - .any(|m| m.buffer == buffer.internal.raw) - { - self.invalidate_coherent_memory.push(MemoryInvalidate { - working_buffer: Some(self.internal.working_buffer.clone()), - working_buffer_size: self.internal.working_buffer_size, - host_memory: buffer.memory_ptr, - host_sync_range: buffer.bound_range.clone(), - buffer_sync_range: buffer.bound_range.clone(), - buffer: buffer.internal.raw, - }); - } - } - - fn reset(&mut self) { - self.flush_coherent_memory.clear(); - self.invalidate_coherent_memory.clear(); - self.render_pass_cache = None; - self.cache.clear(); - self.debug_scopes.clear(); - } -} - -impl command::CommandBuffer for CommandBuffer { - unsafe fn begin( - &mut self, - flags: command::CommandBufferFlags, - _info: command::CommandBufferInheritanceInfo, - ) { - self.one_time_submit = flags.contains(command::CommandBufferFlags::ONE_TIME_SUBMIT); - self.reset(); - - // Push constants are at the top register to allow them to be bound only once. - let raw_push_constant_buffer = self.push_constant_buffer.as_raw(); - self.context.VSSetConstantBuffers( - d3d11::D3D11_COMMONSHADER_CONSTANT_BUFFER_API_SLOT_COUNT - 1, - 1, - &raw_push_constant_buffer as *const _, - ); - self.context.PSSetConstantBuffers( - d3d11::D3D11_COMMONSHADER_CONSTANT_BUFFER_API_SLOT_COUNT - 1, - 1, - &raw_push_constant_buffer as *const _, - ); - self.context.CSSetConstantBuffers( - d3d11::D3D11_COMMONSHADER_CONSTANT_BUFFER_API_SLOT_COUNT - 1, - 1, - &raw_push_constant_buffer as *const _, - ); - } - - unsafe fn finish(&mut self) { - let mut list: *mut d3d11::ID3D11CommandList = ptr::null_mut(); - let hr = self - .context - .FinishCommandList(FALSE, &mut list as *mut *mut _); - assert_eq!(hr, winerror::S_OK); - - if let Some(ref name) = self.debug_name { - set_debug_name(&*list, name); - } - - self.list.replace(Some(ComPtr::from_raw(list))); - } - - unsafe fn reset(&mut self, _release_resources: bool) { - self.reset(); - } - - unsafe fn begin_render_pass<'a, T>( - &mut self, - render_pass: &RenderPass, - framebuffer: &Framebuffer, - target_rect: pso::Rect, - attachment_infos: T, - _first_subpass: command::SubpassContents, - ) where - T: Iterator>, - { - use pass::AttachmentLoadOp as Alo; - - let mut attachments = Vec::new(); - - for (idx, (info, attachment)) in attachment_infos - .zip(render_pass.attachments.iter()) - .enumerate() - { - let format = attachment.format.unwrap(); - - let mut at = AttachmentInfo { - subpass_id: render_pass - .subpasses - .iter() - .position(|sp| sp.is_using(idx)) - .map(|i| i as pass::SubpassId), - view: info.image_view.clone(), - clear_color: None, - clear_depth: None, - clear_stencil: None, - }; - - if attachment.ops.load == Alo::Clear { - if format.is_depth() { - at.clear_depth = Some(info.clear_value.depth_stencil.depth); - } else { - at.clear_color = Some((idx, info.clear_value.color)); - } - } - if attachment.stencil_ops.load == Alo::Clear { - at.clear_stencil = Some(info.clear_value.depth_stencil.stencil); - } - - attachments.push(at); - } - - self.render_pass_cache = Some(RenderPassCache { - render_pass: render_pass.clone(), - attachments, - target_rect, - num_layers: framebuffer.layers, - current_subpass: 0, - }); - - if let Some(ref mut current_render_pass) = self.render_pass_cache { - current_render_pass.start_subpass(&self.internal, &self.context, &mut self.cache); - } - } - - unsafe fn next_subpass(&mut self, _contents: command::SubpassContents) { - if let Some(ref mut current_render_pass) = self.render_pass_cache { - current_render_pass.next_subpass(&self.context); - current_render_pass.start_subpass(&self.internal, &self.context, &mut self.cache); - } - } - - unsafe fn end_render_pass(&mut self) { - if let Some(ref mut current_render_pass) = self.render_pass_cache { - current_render_pass.resolve_msaa(&self.context); - } - - self.context - .OMSetRenderTargets(8, [ptr::null_mut(); 8].as_ptr(), ptr::null_mut()); - - self.render_pass_cache = None; - } - - unsafe fn pipeline_barrier<'a, T>( - &mut self, - _stages: Range, - _dependencies: memory::Dependencies, - _barriers: T, - ) where - T: Iterator>, - { - // TODO: should we track and assert on resource states? - // unimplemented!() - } - - unsafe fn clear_image( - &mut self, - image: &Image, - _: image::Layout, - value: command::ClearValue, - subresource_ranges: T, - ) where - T: Iterator, - { - for range in subresource_ranges { - let num_levels = range.resolve_level_count(image.mip_levels); - let num_layers = range.resolve_layer_count(image.kind.num_layers()); - - let mut depth_stencil_flags = 0; - if range.aspects.contains(format::Aspects::DEPTH) { - depth_stencil_flags |= d3d11::D3D11_CLEAR_DEPTH; - } - if range.aspects.contains(format::Aspects::STENCIL) { - depth_stencil_flags |= d3d11::D3D11_CLEAR_STENCIL; - } - - // TODO: clear Int/Uint depending on format - for rel_layer in 0..num_layers { - for rel_level in 0..num_levels { - let level = range.level_start + rel_level; - let layer = range.layer_start + rel_layer; - if range.aspects.contains(format::Aspects::COLOR) { - self.context.ClearRenderTargetView( - image.get_rtv(level, layer).unwrap().as_raw(), - &value.color.float32, - ); - } else { - self.context.ClearDepthStencilView( - image.get_dsv(level, layer).unwrap().as_raw(), - depth_stencil_flags, - value.depth_stencil.depth, - value.depth_stencil.stencil as _, - ); - } - } - } - } - } - - unsafe fn clear_attachments(&mut self, clears: T, rects: U) - where - T: Iterator, - U: Iterator, - { - if let Some(ref pass) = self.render_pass_cache { - self.cache.dirty_flag.insert( - DirtyStateFlag::GRAPHICS_PIPELINE - | DirtyStateFlag::DEPTH_STENCIL_STATE - | DirtyStateFlag::PIPELINE_PS - | DirtyStateFlag::VIEWPORTS - | DirtyStateFlag::RENDER_TARGETS_AND_UAVS, - ); - self.internal - .clear_attachments(&self.context, clears, rects, pass); - self.cache.bind(&self.context); - } else { - panic!("`clear_attachments` can only be called inside a renderpass") - } - } - - unsafe fn resolve_image( - &mut self, - _src: &Image, - _src_layout: image::Layout, - _dst: &Image, - _dst_layout: image::Layout, - _regions: T, - ) where - T: Iterator, - { - unimplemented!() - } - - unsafe fn blit_image( - &mut self, - src: &Image, - _src_layout: image::Layout, - dst: &Image, - _dst_layout: image::Layout, - filter: image::Filter, - regions: T, - ) where - T: Iterator, - { - self.cache - .dirty_flag - .insert(DirtyStateFlag::GRAPHICS_PIPELINE | DirtyStateFlag::PIPELINE_PS); - - self.internal - .blit_2d_image(&self.context, src, dst, filter, regions); - - self.cache.bind(&self.context); - } - - unsafe fn bind_index_buffer(&mut self, buffer: &Buffer, sub: buffer::SubRange, ty: IndexType) { - self.context.IASetIndexBuffer( - buffer.internal.raw, - conv::map_index_type(ty), - sub.offset as u32, - ); - } - - unsafe fn bind_vertex_buffers<'a, T>(&mut self, first_binding: pso::BufferIndex, buffers: T) - where - T: Iterator, - { - for (i, (buf, sub)) in buffers.enumerate() { - let idx = i + first_binding as usize; - - if buf.is_coherent { - self.defer_coherent_flush(buf); - } - - self.cache - .set_vertex_buffer(idx, sub.offset as u32, buf.internal.raw); - } - - self.cache.bind_vertex_buffers(&self.context); - } - - unsafe fn set_viewports(&mut self, _first_viewport: u32, viewports: T) - where - T: Iterator, - { - let viewports = viewports - .map(|ref vp| conv::map_viewport(vp)) - .collect::>(); - - // TODO: DX only lets us set all VPs at once, so cache in slice? - self.cache.set_viewports(&viewports); - self.cache.bind_viewports(&self.context); - } - - unsafe fn set_scissors(&mut self, _first_scissor: u32, scissors: T) - where - T: Iterator, - { - let scissors = scissors.map(|ref r| conv::map_rect(r)).collect::>(); - - // TODO: same as for viewports - self.context - .RSSetScissorRects(scissors.len() as _, scissors.as_ptr()); - } - - unsafe fn set_blend_constants(&mut self, color: pso::ColorValue) { - self.cache.set_blend_factor(color); - self.cache.bind_blend_state(&self.context); - } - - unsafe fn set_stencil_reference(&mut self, _faces: pso::Face, value: pso::StencilValue) { - self.cache.set_stencil_ref(value); - self.cache.bind_depth_stencil_state(&self.context); - } - - unsafe fn set_stencil_read_mask(&mut self, _faces: pso::Face, value: pso::StencilValue) { - self.cache.stencil_read_mask = Some(value); - } - - unsafe fn set_stencil_write_mask(&mut self, _faces: pso::Face, value: pso::StencilValue) { - self.cache.stencil_write_mask = Some(value); - } - - unsafe fn set_depth_bounds(&mut self, _bounds: Range) { - unimplemented!() - } - - unsafe fn set_line_width(&mut self, width: f32) { - validate_line_width(width); - } - - unsafe fn set_depth_bias(&mut self, _depth_bias: pso::DepthBias) { - // TODO: - // unimplemented!() - } - - unsafe fn bind_graphics_pipeline(&mut self, pipeline: &GraphicsPipeline) { - self.cache.set_graphics_pipeline(pipeline.clone()); - self.cache.bind(&self.context); - } - - unsafe fn bind_graphics_descriptor_sets<'a, I, J>( - &mut self, - layout: &PipelineLayout, - first_set: usize, - sets: I, - offsets: J, - ) where - I: Iterator, - J: Iterator, - { - let _scope = debug_scope!(&self.context, "BindGraphicsDescriptorSets"); - - // TODO: find a better solution to invalidating old bindings.. - if self.internal.downlevel.compute_shaders { - let cs_uavs = if self.internal.device_feature_level <= d3dcommon::D3D_FEATURE_LEVEL_11_0 - { - 1 - } else { - d3d11::D3D11_PS_CS_UAV_REGISTER_COUNT - }; - let nulls = [ptr::null_mut(); d3d11::D3D11_PS_CS_UAV_REGISTER_COUNT as usize]; - self.context - .CSSetUnorderedAccessViews(0, cs_uavs, nulls.as_ptr(), ptr::null_mut()); - } - - let mut offset_iter = offsets; - - for (set, info) in sets.zip(&layout.sets[first_set..]) { - { - let coherent_buffers = set.coherent_buffers.lock(); - for sync in coherent_buffers.flush_coherent_buffers.borrow().iter() { - // TODO: merge sync range if a flush already exists - if !self - .flush_coherent_memory - .iter() - .any(|m| m.buffer == sync.device_buffer) - { - self.flush_coherent_memory.push(MemoryFlush { - host_memory: sync.host_ptr, - sync_range: sync.range.clone(), - buffer: sync.device_buffer, - }); - } - } - - for sync in coherent_buffers.invalidate_coherent_buffers.borrow().iter() { - if !self - .invalidate_coherent_memory - .iter() - .any(|m| m.buffer == sync.device_buffer) - { - self.invalidate_coherent_memory.push(MemoryInvalidate { - working_buffer: Some(self.internal.working_buffer.clone()), - working_buffer_size: self.internal.working_buffer_size, - host_memory: sync.host_ptr, - host_sync_range: sync.range.clone(), - buffer_sync_range: sync.range.clone(), - buffer: sync.device_buffer, - }); - } - } - } - - let (vs_offsets, fs_offsets) = generate_graphics_dynamic_constant_buffer_offsets( - &*set.layout.bindings, - &mut offset_iter, - self.context1.is_some(), - ); - - if let Some(rd) = info.registers.vs.c.as_some() { - let start_slot = rd.res_index as u32; - let num_buffers = rd.count as u32; - let constant_buffers = set.handles.offset(rd.pool_offset as isize); - if let Some(ref context1) = self.context1 { - // This call with offsets won't work right with command list emulation - // unless we reset the first and last constant buffers to null. - if self.internal.command_list_emulation { - let null_cbuf = [ptr::null_mut::()]; - context1.VSSetConstantBuffers(start_slot, 1, &null_cbuf as *const _); - if num_buffers > 1 { - context1.VSSetConstantBuffers( - start_slot + num_buffers - 1, - 1, - &null_cbuf as *const _, - ); - } - } - - // TODO: This should be the actual buffer length for RBA purposes, - // but that information isn't easily accessible here. - context1.VSSetConstantBuffers1( - start_slot, - num_buffers, - constant_buffers as *const *mut _, - vs_offsets.as_ptr(), - self.internal.constant_buffer_count_buffer.as_ptr(), - ); - } else { - self.context.VSSetConstantBuffers( - start_slot, - num_buffers, - constant_buffers as *const *mut _, - ); - } - } - if let Some(rd) = info.registers.vs.t.as_some() { - self.context.VSSetShaderResources( - rd.res_index as u32, - rd.count as u32, - set.handles.offset(rd.pool_offset as isize) as *const *mut _, - ); - } - if let Some(rd) = info.registers.vs.s.as_some() { - self.context.VSSetSamplers( - rd.res_index as u32, - rd.count as u32, - set.handles.offset(rd.pool_offset as isize) as *const *mut _, - ); - } - - if let Some(rd) = info.registers.ps.c.as_some() { - let start_slot = rd.res_index as u32; - let num_buffers = rd.count as u32; - let constant_buffers = set.handles.offset(rd.pool_offset as isize); - if let Some(ref context1) = self.context1 { - // This call with offsets won't work right with command list emulation - // unless we reset the first and last constant buffers to null. - if self.internal.command_list_emulation { - let null_cbuf = [ptr::null_mut::()]; - context1.PSSetConstantBuffers(start_slot, 1, &null_cbuf as *const _); - if num_buffers > 1 { - context1.PSSetConstantBuffers( - start_slot + num_buffers - 1, - 1, - &null_cbuf as *const _, - ); - } - } - - context1.PSSetConstantBuffers1( - start_slot, - num_buffers, - constant_buffers as *const *mut _, - fs_offsets.as_ptr(), - self.internal.constant_buffer_count_buffer.as_ptr(), - ); - } else { - self.context.PSSetConstantBuffers( - start_slot, - num_buffers, - constant_buffers as *const *mut _, - ); - } - } - if let Some(rd) = info.registers.ps.t.as_some() { - self.context.PSSetShaderResources( - rd.res_index as u32, - rd.count as u32, - set.handles.offset(rd.pool_offset as isize) as *const *mut _, - ); - } - if let Some(rd) = info.registers.ps.s.as_some() { - self.context.PSSetSamplers( - rd.res_index as u32, - rd.count as u32, - set.handles.offset(rd.pool_offset as isize) as *const *mut _, - ); - } - - // UAVs going to the graphics pipeline are always treated as pixel shader bindings. - if let Some(rd) = info.registers.ps.u.as_some() { - // We bind UAVs in inverse order from the top to prevent invalidation - // when the render target count changes. - for idx in (0..(rd.count)).rev() { - let ptr = (*set.handles.offset(rd.pool_offset as isize + idx as isize)).0; - let uav_register = d3d11::D3D11_PS_CS_UAV_REGISTER_COUNT - - 1 - - rd.res_index as u32 - - idx as u32; - self.cache.uavs[uav_register as usize] = ptr as *mut _; - } - self.cache.uav_len = (rd.res_index + rd.count) as u32; - self.cache - .dirty_flag - .insert(DirtyStateFlag::RENDER_TARGETS_AND_UAVS); - } - } - - self.cache.bind_render_targets(&self.context); - } - - unsafe fn bind_compute_pipeline(&mut self, pipeline: &ComputePipeline) { - self.context - .CSSetShader(pipeline.cs.as_raw(), ptr::null_mut(), 0); - } - - unsafe fn bind_compute_descriptor_sets<'a, I, J>( - &mut self, - layout: &PipelineLayout, - first_set: usize, - sets: I, - offsets: J, - ) where - I: Iterator, - J: Iterator, - { - let _scope = debug_scope!(&self.context, "BindComputeDescriptorSets"); - - let nulls = [ptr::null_mut(); d3d11::D3D11_PS_CS_UAV_REGISTER_COUNT as usize]; - self.context.CSSetUnorderedAccessViews( - 0, - d3d11::D3D11_PS_CS_UAV_REGISTER_COUNT, - nulls.as_ptr(), - ptr::null_mut(), - ); - - let mut offset_iter = offsets; - - for (set, info) in sets.zip(&layout.sets[first_set..]) { - { - let coherent_buffers = set.coherent_buffers.lock(); - for sync in coherent_buffers.flush_coherent_buffers.borrow().iter() { - if !self - .flush_coherent_memory - .iter() - .any(|m| m.buffer == sync.device_buffer) - { - self.flush_coherent_memory.push(MemoryFlush { - host_memory: sync.host_ptr, - sync_range: sync.range.clone(), - buffer: sync.device_buffer, - }); - } - } - - for sync in coherent_buffers.invalidate_coherent_buffers.borrow().iter() { - if !self - .invalidate_coherent_memory - .iter() - .any(|m| m.buffer == sync.device_buffer) - { - self.invalidate_coherent_memory.push(MemoryInvalidate { - working_buffer: Some(self.internal.working_buffer.clone()), - working_buffer_size: self.internal.working_buffer_size, - host_memory: sync.host_ptr, - host_sync_range: sync.range.clone(), - buffer_sync_range: sync.range.clone(), - buffer: sync.device_buffer, - }); - } - } - } - - let cs_offsets = generate_compute_dynamic_constant_buffer_offsets( - &*set.layout.bindings, - &mut offset_iter, - self.context1.is_some(), - ); - - if let Some(rd) = info.registers.cs.c.as_some() { - let start_slot = rd.res_index as u32; - let num_buffers = rd.count as u32; - let constant_buffers = set.handles.offset(rd.pool_offset as isize); - if let Some(ref context1) = self.context1 { - // This call with offsets won't work right with command list emulation - // unless we reset the first and last constant buffers to null. - if self.internal.command_list_emulation { - let null_cbuf = [ptr::null_mut::()]; - context1.CSSetConstantBuffers(start_slot, 1, &null_cbuf as *const _); - if num_buffers > 1 { - context1.CSSetConstantBuffers( - start_slot + num_buffers - 1, - 1, - &null_cbuf as *const _, - ); - } - } - - // TODO: This should be the actual buffer length for RBA purposes, - // but that information isn't easily accessible here. - context1.CSSetConstantBuffers1( - start_slot, - num_buffers, - constant_buffers as *const *mut _, - cs_offsets.as_ptr(), - self.internal.constant_buffer_count_buffer.as_ptr(), - ); - } else { - self.context.CSSetConstantBuffers( - start_slot, - num_buffers, - constant_buffers as *const *mut _, - ); - } - } - if let Some(rd) = info.registers.cs.t.as_some() { - self.context.CSSetShaderResources( - rd.res_index as u32, - rd.count as u32, - set.handles.offset(rd.pool_offset as isize) as *const *mut _, - ); - } - if let Some(rd) = info.registers.cs.u.as_some() { - self.context.CSSetUnorderedAccessViews( - rd.res_index as u32, - rd.count as u32, - set.handles.offset(rd.pool_offset as isize) as *const *mut _, - ptr::null_mut(), - ); - } - if let Some(rd) = info.registers.cs.s.as_some() { - self.context.CSSetSamplers( - rd.res_index as u32, - rd.count as u32, - set.handles.offset(rd.pool_offset as isize) as *const *mut _, - ); - } - } - } - - unsafe fn dispatch(&mut self, count: WorkGroupCount) { - self.context.Dispatch(count[0], count[1], count[2]); - } - - unsafe fn dispatch_indirect(&mut self, _buffer: &Buffer, _offset: buffer::Offset) { - unimplemented!() - } - - unsafe fn fill_buffer(&mut self, buffer: &Buffer, sub: buffer::SubRange, data: u32) { - let mut device: *mut d3d11::ID3D11Device = mem::zeroed(); - self.context.GetDevice(&mut device as *mut _); - let device = ComPtr::from_raw(device); - - assert_eq!( - sub.offset % 4, - 0, - "Buffer sub range offset must be multiple of 4" - ); - if let Some(size) = sub.size { - assert_eq!(size % 4, 0, "Buffer sub range size must be multiple of 4"); - } - - // TODO: expose this requirement to the user to enable avoiding the unaligned fill for - // performance - // FirstElement must be a multiple of 4 (since each element is 4 bytes and the - // offset needs to be a multiple of 16 bytes) - let element_offset = sub.offset as u32 / 4; - let num_elements = sub.size.unwrap_or(buffer.requirements.size) as u32 / 4; - - fn up_align(x: u32, alignment: u32) -> u32 { - (x + alignment - 1) & !(alignment - 1) - } - - let aligned_element_offset = up_align(element_offset, 4); - let unaligned_num_elements = (aligned_element_offset - element_offset).min(num_elements); - let aligned_num_elements = num_elements - unaligned_num_elements; - - // Use ClearUnorderedAccessViewUint to fill from the 16 byte aligned offset - if aligned_num_elements > 0 { - let mut desc: d3d11::D3D11_UNORDERED_ACCESS_VIEW_DESC = mem::zeroed(); - desc.Format = dxgiformat::DXGI_FORMAT_R32_TYPELESS; - desc.ViewDimension = d3d11::D3D11_UAV_DIMENSION_BUFFER; - *desc.u.Buffer_mut() = d3d11::D3D11_BUFFER_UAV { - FirstElement: aligned_element_offset, - NumElements: aligned_num_elements, - Flags: d3d11::D3D11_BUFFER_UAV_FLAG_RAW, - }; - - let mut uav: *mut d3d11::ID3D11UnorderedAccessView = ptr::null_mut(); - let hr = device.CreateUnorderedAccessView( - buffer.internal.raw as *mut _, - &desc, - &mut uav as *mut *mut _ as *mut *mut _, - ); - - if !winerror::SUCCEEDED(hr) { - panic!("fill_buffer failed to make UAV failed: 0x{:x}", hr); - } - - let uav = ComPtr::from_raw(uav); - - self.context - .ClearUnorderedAccessViewUint(uav.as_raw(), &[data; 4]); - } - - // If there is unaligned portion at the beginning of the sub region - // create a new buffer with the fill data to copy into this region - if unaligned_num_elements > 0 { - debug_assert!( - unaligned_num_elements < 4, - "The number of unaligned elements is {} but it should be less than 4", - unaligned_num_elements - ); - - let initial_data = [data; 4]; - - let initial_data = d3d11::D3D11_SUBRESOURCE_DATA { - pSysMem: initial_data.as_ptr() as *const _, - SysMemPitch: 0, - SysMemSlicePitch: 0, - }; - - // TODO: consider using a persistent buffer like the working_buffer - let desc = d3d11::D3D11_BUFFER_DESC { - ByteWidth: core::mem::size_of_val(&initial_data) as _, - Usage: d3d11::D3D11_USAGE_DEFAULT, - BindFlags: 0, - CPUAccessFlags: 0, - MiscFlags: 0, - StructureByteStride: 0, - }; - let mut temp_buffer = ptr::null_mut::(); - - assert_eq!( - winerror::S_OK, - device.CreateBuffer( - &desc, - &initial_data, - &mut temp_buffer as *mut *mut _ as *mut *mut _, - ) - ); - - let temp_buffer = ComPtr::from_raw(temp_buffer); - - let src_box = d3d11::D3D11_BOX { - left: 0, - top: 0, - front: 0, - right: (unaligned_num_elements * core::mem::size_of::() as u32) as _, - bottom: 1, - back: 1, - }; - - self.context.CopySubresourceRegion( - buffer.internal.raw as _, - 0, - sub.offset as _, // offset in bytes - 0, - 0, - temp_buffer.as_raw() as _, - 0, - &src_box, - ); - } - } - - unsafe fn update_buffer(&mut self, _buffer: &Buffer, _offset: buffer::Offset, _data: &[u8]) { - unimplemented!() - } - - unsafe fn copy_buffer(&mut self, src: &Buffer, dst: &Buffer, regions: T) - where - T: Iterator, - { - if src.is_coherent { - self.defer_coherent_flush(src); - } - - for info in regions { - let src_box = d3d11::D3D11_BOX { - left: info.src as _, - top: 0, - front: 0, - right: (info.src + info.size) as _, - bottom: 1, - back: 1, - }; - - self.context.CopySubresourceRegion( - dst.internal.raw as _, - 0, - info.dst as _, - 0, - 0, - src.internal.raw as _, - 0, - &src_box, - ); - - if let Some(disjoint_cb) = dst.internal.disjoint_cb { - self.context.CopySubresourceRegion( - disjoint_cb as _, - 0, - info.dst as _, - 0, - 0, - src.internal.raw as _, - 0, - &src_box, - ); - } - } - } - - unsafe fn copy_image( - &mut self, - src: &Image, - _: image::Layout, - dst: &Image, - _: image::Layout, - regions: T, - ) where - T: Iterator, - { - self.internal - .copy_image_2d(&self.context, src, dst, regions); - } - - unsafe fn copy_buffer_to_image( - &mut self, - buffer: &Buffer, - image: &Image, - _: image::Layout, - regions: T, - ) where - T: Iterator, - { - if buffer.is_coherent { - self.defer_coherent_flush(buffer); - } - - self.internal - .copy_buffer_to_image(&self.context, buffer, image, regions); - } - - unsafe fn copy_image_to_buffer( - &mut self, - image: &Image, - _: image::Layout, - buffer: &Buffer, - regions: T, - ) where - T: Iterator, - { - if buffer.is_coherent { - self.defer_coherent_invalidate(buffer); - } - - self.internal - .copy_image_to_buffer(&self.context, image, buffer, regions); - } - - unsafe fn draw(&mut self, vertices: Range, instances: Range) { - self.context.DrawInstanced( - vertices.end - vertices.start, - instances.end - instances.start, - vertices.start, - instances.start, - ); - } - - unsafe fn draw_indexed( - &mut self, - indices: Range, - base_vertex: VertexOffset, - instances: Range, - ) { - self.context.DrawIndexedInstanced( - indices.end - indices.start, - instances.end - instances.start, - indices.start, - base_vertex, - instances.start, - ); - } - - unsafe fn draw_indirect( - &mut self, - buffer: &Buffer, - offset: buffer::Offset, - draw_count: DrawCount, - _stride: buffer::Stride, - ) { - assert_eq!(draw_count, 1, "DX11 doesn't support MULTI_DRAW_INDIRECT"); - self.context - .DrawInstancedIndirect(buffer.internal.raw, offset as _); - } - - unsafe fn draw_indexed_indirect( - &mut self, - buffer: &Buffer, - offset: buffer::Offset, - draw_count: DrawCount, - _stride: buffer::Stride, - ) { - assert_eq!(draw_count, 1, "DX11 doesn't support MULTI_DRAW_INDIRECT"); - self.context - .DrawIndexedInstancedIndirect(buffer.internal.raw, offset as _); - } - - unsafe fn draw_indirect_count( - &mut self, - _buffer: &Buffer, - _offset: buffer::Offset, - _count_buffer: &Buffer, - _count_buffer_offset: buffer::Offset, - _max_draw_count: u32, - _stride: buffer::Stride, - ) { - panic!("DX11 doesn't support DRAW_INDIRECT_COUNT") - } - - unsafe fn draw_indexed_indirect_count( - &mut self, - _buffer: &Buffer, - _offset: buffer::Offset, - _count_buffer: &Buffer, - _count_buffer_offset: buffer::Offset, - _max_draw_count: u32, - _stride: buffer::Stride, - ) { - panic!("DX11 doesn't support DRAW_INDIRECT_COUNT") - } - - unsafe fn draw_mesh_tasks(&mut self, _: TaskCount, _: TaskCount) { - panic!("DX11 doesn't support MESH_SHADERS") - } - - unsafe fn draw_mesh_tasks_indirect( - &mut self, - _: &Buffer, - _: buffer::Offset, - _: hal::DrawCount, - _: buffer::Stride, - ) { - panic!("DX11 doesn't support MESH_SHADERS") - } - - unsafe fn draw_mesh_tasks_indirect_count( - &mut self, - _: &Buffer, - _: buffer::Offset, - _: &Buffer, - _: buffer::Offset, - _: hal::DrawCount, - _: buffer::Stride, - ) { - panic!("DX11 doesn't support MESH_SHADERS") - } - - unsafe fn set_event(&mut self, _: &(), _: pso::PipelineStage) { - unimplemented!() - } - - unsafe fn reset_event(&mut self, _: &(), _: pso::PipelineStage) { - unimplemented!() - } - - unsafe fn wait_events<'a, I, J>(&mut self, _: I, _: Range, _: J) - where - I: Iterator, - J: Iterator>, - { - unimplemented!() - } - - unsafe fn begin_query(&mut self, _query: query::Query, _flags: query::ControlFlags) { - unimplemented!() - } - - unsafe fn end_query(&mut self, _query: query::Query) { - unimplemented!() - } - - unsafe fn reset_query_pool(&mut self, _pool: &QueryPool, _queries: Range) { - unimplemented!() - } - - unsafe fn copy_query_pool_results( - &mut self, - _pool: &QueryPool, - _queries: Range, - _buffer: &Buffer, - _offset: buffer::Offset, - _stride: buffer::Stride, - _flags: query::ResultFlags, - ) { - unimplemented!() - } - - unsafe fn write_timestamp(&mut self, _: pso::PipelineStage, _query: query::Query) { - unimplemented!() - } - - unsafe fn push_graphics_constants( - &mut self, - _layout: &PipelineLayout, - _stages: pso::ShaderStageFlags, - offset: u32, - constants: &[u32], - ) { - let start = (offset / 4) as usize; - let end = start + constants.len(); - - self.push_constant_data[start..end].copy_from_slice(constants); - - self.context.UpdateSubresource( - self.push_constant_buffer.as_raw() as *mut _, - 0, - ptr::null(), - self.push_constant_data.as_ptr() as *const _, - MAX_PUSH_CONSTANT_SIZE as _, - 1, - ); - } - - unsafe fn push_compute_constants( - &mut self, - _layout: &PipelineLayout, - offset: u32, - constants: &[u32], - ) { - let start = (offset / 4) as usize; - let end = start + constants.len(); - - self.push_constant_data[start..end].copy_from_slice(constants); - - self.context.UpdateSubresource( - self.push_constant_buffer.as_raw() as *mut _, - 0, - ptr::null(), - self.push_constant_data.as_ptr() as *const _, - MAX_PUSH_CONSTANT_SIZE as _, - 1, - ); - } - - unsafe fn execute_commands<'a, T>(&mut self, _buffers: T) - where - T: Iterator, - { - unimplemented!() - } - - unsafe fn insert_debug_marker(&mut self, name: &str, _color: u32) { - debug::debug_marker(&self.context, &format!("{}", name)) - } - unsafe fn begin_debug_marker(&mut self, _name: &str, _color: u32) { - // TODO: This causes everything after this to be part of this scope, why? - // self.debug_scopes.push(debug::DebugScope::with_name(&self.context, format_args!("{}", name))) - } - unsafe fn end_debug_marker(&mut self) { - // self.debug_scopes.pop(); - } -} - -#[derive(Clone, Debug)] -enum SyncRange { - Whole, - Partial(Range), -} - -#[derive(Debug)] -pub struct MemoryFlush { - host_memory: *const u8, - sync_range: SyncRange, - buffer: *mut d3d11::ID3D11Buffer, -} - -pub struct MemoryInvalidate { - working_buffer: Option>, - working_buffer_size: u64, - host_memory: *mut u8, - host_sync_range: Range, - buffer_sync_range: Range, - buffer: *mut d3d11::ID3D11Buffer, -} - -impl fmt::Debug for MemoryInvalidate { - fn fmt(&self, fmt: &mut fmt::Formatter) -> fmt::Result { - fmt.write_str("MemoryInvalidate") - } -} - -fn intersection(a: &Range, b: &Range) -> Option> { - let r = a.start.max(b.start)..a.end.min(b.end); - if r.start < r.end { - Some(r) - } else { - None - } -} - -impl MemoryFlush { - fn do_flush(&self, context: &ComPtr) { - let src = self.host_memory; - - debug_marker!(context, "Flush({:?})", self.sync_range); - let region = match self.sync_range { - SyncRange::Partial(ref range) if range.start < range.end => Some(d3d11::D3D11_BOX { - left: range.start as u32, - top: 0, - front: 0, - right: range.end as u32, - bottom: 1, - back: 1, - }), - _ => None, - }; - - unsafe { - context.UpdateSubresource( - self.buffer as _, - 0, - region.as_ref().map_or(ptr::null(), |r| r), - src as _, - 0, - 0, - ); - } - } -} - -impl MemoryInvalidate { - fn download( - &self, - context: &ComPtr, - buffer: *mut d3d11::ID3D11Buffer, - host_range: Range, - buffer_range: Range, - ) { - // Range doesn't impl `len` for some bizzare reason relating to underflow - debug_assert_eq!( - host_range.end - host_range.start, - buffer_range.end - buffer_range.start - ); - - unsafe { - context.CopySubresourceRegion( - self.working_buffer.clone().unwrap().as_raw() as _, - 0, - 0, - 0, - 0, - buffer as _, - 0, - &d3d11::D3D11_BOX { - left: buffer_range.start as _, - top: 0, - front: 0, - right: buffer_range.end as _, - bottom: 1, - back: 1, - }, - ); - - // copy over to our vec - let dst = self.host_memory.offset(host_range.start as isize); - let src = self.map(&context); - ptr::copy(src, dst, (host_range.end - host_range.start) as usize); - self.unmap(&context); - } - } - - fn do_invalidate(&self, context: &ComPtr) { - let stride = self.working_buffer_size; - let len = self.host_sync_range.end - self.host_sync_range.start; - let chunks = len / stride; - let remainder = len % stride; - - // we split up the copies into chunks the size of our working buffer - for i in 0..chunks { - let host_offset = self.host_sync_range.start + i * stride; - let host_range = host_offset..(host_offset + stride); - let buffer_offset = self.buffer_sync_range.start + i * stride; - let buffer_range = buffer_offset..(buffer_offset + stride); - - self.download(context, self.buffer, host_range, buffer_range); - } - - if remainder != 0 { - let host_offset = self.host_sync_range.start + chunks * stride; - let host_range = host_offset..self.host_sync_range.end; - let buffer_offset = self.buffer_sync_range.start + chunks * stride; - let buffer_range = buffer_offset..self.buffer_sync_range.end; - - debug_assert!(host_range.end - host_range.start <= stride); - debug_assert!(buffer_range.end - buffer_range.start <= stride); - - self.download(context, self.buffer, host_range, buffer_range); - } - } - - fn map(&self, context: &ComPtr) -> *mut u8 { - assert_eq!(self.working_buffer.is_some(), true); - - unsafe { - let mut map = mem::zeroed(); - let hr = context.Map( - self.working_buffer.clone().unwrap().as_raw() as _, - 0, - d3d11::D3D11_MAP_READ, - 0, - &mut map, - ); - - assert_eq!(hr, winerror::S_OK); - - map.pData as _ - } - } - - fn unmap(&self, context: &ComPtr) { - unsafe { - context.Unmap(self.working_buffer.clone().unwrap().as_raw() as _, 0); - } - } -} - -type LocalResourceArena = thunderdome::Arena<(Range, T)>; - -// Since we dont have any heaps to work with directly, Beverytime we bind a -// buffer/image to memory we allocate a dx11 resource and assign it a range. -// -// `HOST_VISIBLE` memory gets a `Vec` which covers the entire memory -// range. This forces us to only expose non-coherent memory, as this -// abstraction acts as a "cache" since the "staging buffer" vec is disjoint -// from all the dx11 resources we store in the struct. -pub struct Memory { - properties: memory::Properties, - size: u64, - - // pointer to staging memory, if it's HOST_VISIBLE - host_ptr: *mut u8, - - // list of all buffers bound to this memory - local_buffers: Arc>>, -} - -impl fmt::Debug for Memory { - fn fmt(&self, fmt: &mut fmt::Formatter) -> fmt::Result { - fmt.write_str("Memory") - } -} - -unsafe impl Send for Memory {} -unsafe impl Sync for Memory {} - -impl Memory { - pub fn resolve(&self, segment: &memory::Segment) -> Range { - segment.offset..segment.size.map_or(self.size, |s| segment.offset + s) - } - - pub fn bind_buffer(&self, range: Range, buffer: InternalBuffer) -> thunderdome::Index { - let mut local_buffers = self.local_buffers.write(); - local_buffers.insert((range, buffer)) - } - - pub fn flush(&self, context: &ComPtr, range: Range) { - use buffer::Usage; - - for (_, &(ref buffer_range, ref buffer)) in self.local_buffers.read().iter() { - let range = match intersection(&range, &buffer_range) { - Some(r) => r, - None => continue, - }; - // we need to handle 3 cases for updating buffers: - // - // 1. if our buffer was created as a `UNIFORM` buffer *and* other usage flags, we - // also have a disjoint buffer which only has `D3D11_BIND_CONSTANT_BUFFER` due - // to DX11 limitation. we then need to update both the original buffer and the - // disjoint one with the *whole* range - // - // 2. if our buffer was created with *only* `UNIFORM` usage we need to upload - // the whole range - // - // 3. the general case, without any `UNIFORM` usage has no restrictions on - // partial updates, so we upload the specified range - // - if let Some(disjoint) = buffer.disjoint_cb { - MemoryFlush { - host_memory: unsafe { self.host_ptr.offset(buffer_range.start as _) }, - sync_range: SyncRange::Whole, - buffer: disjoint, - } - .do_flush(&context); - } - - let mem_flush = if buffer.usage == Usage::UNIFORM { - MemoryFlush { - host_memory: unsafe { self.host_ptr.offset(buffer_range.start as _) }, - sync_range: SyncRange::Whole, - buffer: buffer.raw, - } - } else { - let local_start = range.start - buffer_range.start; - let local_end = range.end - buffer_range.start; - - MemoryFlush { - host_memory: unsafe { self.host_ptr.offset(range.start as _) }, - sync_range: SyncRange::Partial(local_start..local_end), - buffer: buffer.raw, - } - }; - - mem_flush.do_flush(&context) - } - } - - pub fn invalidate( - &self, - context: &ComPtr, - range: Range, - working_buffer: ComPtr, - working_buffer_size: u64, - ) { - for (_, &(ref buffer_range, ref buffer)) in self.local_buffers.read().iter() { - if let Some(range) = intersection(&range, &buffer_range) { - let buffer_start_offset = range.start - buffer_range.start; - let buffer_end_offset = range.end - buffer_range.start; - - let buffer_sync_range = buffer_start_offset..buffer_end_offset; - - MemoryInvalidate { - working_buffer: Some(working_buffer.clone()), - working_buffer_size, - host_memory: self.host_ptr, - host_sync_range: range.clone(), - buffer_sync_range: buffer_sync_range, - buffer: buffer.raw, - } - .do_invalidate(&context); - } - } - } -} - -#[derive(Debug)] -pub struct CommandPool { - device: ComPtr, - device1: Option>, - internal: Arc, -} - -unsafe impl Send for CommandPool {} -unsafe impl Sync for CommandPool {} - -impl hal::pool::CommandPool for CommandPool { - unsafe fn reset(&mut self, _release_resources: bool) { - //unimplemented!() - } - - unsafe fn allocate_one(&mut self, _level: command::Level) -> CommandBuffer { - CommandBuffer::create_deferred( - self.device.clone(), - self.device1.as_deref(), - Arc::clone(&self.internal), - ) - } - - unsafe fn free(&mut self, _cbufs: I) - where - I: Iterator, - { - // TODO: - // unimplemented!() - } -} - -/// Similarily to dx12 backend, we can handle either precompiled dxbc or spirv -pub enum ShaderModule { - Dxbc(Vec), - Spirv(Vec), -} - -// TODO: temporary -impl fmt::Debug for ShaderModule { - fn fmt(&self, f: &mut ::std::fmt::Formatter) -> ::std::fmt::Result { - write!(f, "{}", "ShaderModule { ... }") - } -} - -unsafe impl Send for ShaderModule {} -unsafe impl Sync for ShaderModule {} - -#[derive(Clone, Debug)] -pub struct SubpassDesc { - pub color_attachments: Vec, - pub depth_stencil_attachment: Option, - pub input_attachments: Vec, - pub resolve_attachments: Vec, -} - -impl SubpassDesc { - pub(crate) fn is_using(&self, at_id: pass::AttachmentId) -> bool { - self.color_attachments - .iter() - .chain(self.depth_stencil_attachment.iter()) - .chain(self.input_attachments.iter()) - .chain(self.resolve_attachments.iter()) - .any(|&(id, _)| id == at_id) - } -} - -#[derive(Clone, Debug)] -pub struct RenderPass { - pub attachments: Vec, - pub subpasses: Vec, -} - -#[derive(Clone, Debug)] -pub struct Framebuffer { - layers: image::Layer, -} - -#[derive(Clone, Debug)] -pub struct InternalBuffer { - raw: *mut d3d11::ID3D11Buffer, - // TODO: need to sync between `raw` and `disjoint_cb`, same way as we do with - // `MemoryFlush/Invalidate` - disjoint_cb: Option<*mut d3d11::ID3D11Buffer>, // if unbound this buffer might be null. - srv: Option<*mut d3d11::ID3D11ShaderResourceView>, - uav: Option<*mut d3d11::ID3D11UnorderedAccessView>, - usage: buffer::Usage, - debug_name: Option, -} - -impl InternalBuffer { - unsafe fn release_resources(&mut self) { - (&*self.raw).Release(); - self.raw = ptr::null_mut(); - self.disjoint_cb.take().map(|cb| (&*cb).Release()); - self.uav.take().map(|uav| (&*uav).Release()); - self.srv.take().map(|srv| (&*srv).Release()); - self.usage = buffer::Usage::empty(); - self.debug_name = None; - } -} - -pub struct Buffer { - internal: InternalBuffer, - is_coherent: bool, - memory_ptr: *mut u8, // null if unbound or non-cpu-visible - bound_range: Range, // 0 if unbound - /// Handle to the Memory arena storing this buffer. - local_memory_arena: Weak>>, - /// Index into the above memory arena. - /// - /// Once memory is bound to a buffer, this should never be None. - memory_index: Option, - requirements: memory::Requirements, - bind: d3d11::D3D11_BIND_FLAG, -} - -impl fmt::Debug for Buffer { - fn fmt(&self, fmt: &mut fmt::Formatter) -> fmt::Result { - fmt.write_str("Buffer") - } -} - -unsafe impl Send for Buffer {} -unsafe impl Sync for Buffer {} - -#[derive(Debug)] -pub struct BufferView; - -pub struct Image { - kind: image::Kind, - usage: image::Usage, - format: format::Format, - view_caps: image::ViewCapabilities, - decomposed_format: conv::DecomposedDxgiFormat, - mip_levels: image::Level, - internal: InternalImage, - bind: d3d11::D3D11_BIND_FLAG, - requirements: memory::Requirements, -} - -impl fmt::Debug for Image { - fn fmt(&self, fmt: &mut fmt::Formatter) -> fmt::Result { - fmt.write_str("Image") - } -} - -pub struct InternalImage { - raw: *mut d3d11::ID3D11Resource, - copy_srv: Option>, - srv: Option>, - - /// Contains UAVs for all subresources - unordered_access_views: Vec>, - - /// Contains DSVs for all subresources - depth_stencil_views: Vec>, - - /// Contains RTVs for all subresources - render_target_views: Vec>, - - debug_name: Option, -} - -impl InternalImage { - unsafe fn release_resources(&mut self) { - (&*self.raw).Release(); - self.copy_srv = None; - self.srv = None; - self.unordered_access_views.clear(); - self.depth_stencil_views.clear(); - self.render_target_views.clear(); - } -} - -impl fmt::Debug for InternalImage { - fn fmt(&self, fmt: &mut fmt::Formatter) -> fmt::Result { - fmt.write_str("InternalImage") - } -} - -unsafe impl Send for Image {} -unsafe impl Sync for Image {} - -impl Image { - fn calc_subresource(&self, mip_level: UINT, layer: UINT) -> UINT { - mip_level + (layer * self.mip_levels as UINT) - } - - fn get_uav( - &self, - mip_level: image::Level, - _layer: image::Layer, - ) -> Option<&ComPtr> { - self.internal - .unordered_access_views - .get(self.calc_subresource(mip_level as _, 0) as usize) - } - - fn get_dsv( - &self, - mip_level: image::Level, - layer: image::Layer, - ) -> Option<&ComPtr> { - self.internal - .depth_stencil_views - .get(self.calc_subresource(mip_level as _, layer as _) as usize) - } - - fn get_rtv( - &self, - mip_level: image::Level, - layer: image::Layer, - ) -> Option<&ComPtr> { - self.internal - .render_target_views - .get(self.calc_subresource(mip_level as _, layer as _) as usize) - } -} - -pub struct ImageView { - subresource: UINT, - format: format::Format, - rtv_handle: Option<*mut d3d11::ID3D11RenderTargetView>, - srv_handle: Option<*mut d3d11::ID3D11ShaderResourceView>, - dsv_handle: Option<*mut d3d11::ID3D11DepthStencilView>, - rodsv_handle: Option<*mut d3d11::ID3D11DepthStencilView>, - uav_handle: Option<*mut d3d11::ID3D11UnorderedAccessView>, - owned: bool, -} - -impl Clone for ImageView { - fn clone(&self) -> Self { - Self { - subresource: self.subresource, - format: self.format, - rtv_handle: self.rtv_handle.clone(), - srv_handle: self.srv_handle.clone(), - dsv_handle: self.dsv_handle.clone(), - rodsv_handle: self.rodsv_handle.clone(), - uav_handle: self.uav_handle.clone(), - owned: false, - } - } -} - -impl Drop for ImageView { - fn drop(&mut self) { - if self.owned { - if let Some(rtv) = self.rtv_handle.take() { - unsafe { (&*rtv).Release() }; - } - if let Some(srv) = self.srv_handle.take() { - unsafe { (&*srv).Release() }; - } - if let Some(dsv) = self.dsv_handle.take() { - unsafe { (&*dsv).Release() }; - } - if let Some(rodsv) = self.rodsv_handle.take() { - unsafe { (&*rodsv).Release() }; - } - if let Some(uav) = self.uav_handle.take() { - unsafe { (&*uav).Release() }; - } - } - } -} - -impl fmt::Debug for ImageView { - fn fmt(&self, fmt: &mut fmt::Formatter) -> fmt::Result { - fmt.write_str("ImageView") - } -} - -unsafe impl Send for ImageView {} -unsafe impl Sync for ImageView {} - -pub struct Sampler { - sampler_handle: ComPtr, -} - -impl fmt::Debug for Sampler { - fn fmt(&self, fmt: &mut fmt::Formatter) -> fmt::Result { - fmt.write_str("Sampler") - } -} - -unsafe impl Send for Sampler {} -unsafe impl Sync for Sampler {} - -pub struct ComputePipeline { - cs: ComPtr, -} - -impl fmt::Debug for ComputePipeline { - fn fmt(&self, fmt: &mut fmt::Formatter) -> fmt::Result { - fmt.write_str("ComputePipeline") - } -} - -unsafe impl Send for ComputePipeline {} -unsafe impl Sync for ComputePipeline {} - -/// NOTE: some objects are hashed internally and reused when created with the -/// same params[0], need to investigate which interfaces this applies -/// to. -/// -/// [0]: https://msdn.microsoft.com/en-us/library/windows/desktop/ff476500(v=vs.85).aspx -#[derive(Clone)] -pub struct GraphicsPipeline { - vs: ComPtr, - gs: Option>, - hs: Option>, - ds: Option>, - ps: Option>, - topology: d3d11::D3D11_PRIMITIVE_TOPOLOGY, - input_layout: ComPtr, - rasterizer_state: ComPtr, - blend_state: ComPtr, - depth_stencil_state: Option, - baked_states: pso::BakedStates, - required_bindings: u32, - max_vertex_bindings: u32, - strides: Vec, -} - -impl fmt::Debug for GraphicsPipeline { - fn fmt(&self, fmt: &mut fmt::Formatter) -> fmt::Result { - fmt.write_str("GraphicsPipeline") - } -} - -unsafe impl Send for GraphicsPipeline {} -unsafe impl Sync for GraphicsPipeline {} - -type ResourceIndex = u8; -type DescriptorIndex = u16; - -#[derive(Clone, Debug, Default)] -struct RegisterData { - // CBV - c: T, - // SRV - t: T, - // UAV - u: T, - // Sampler - s: T, -} - -impl RegisterData { - fn map U>(&self, fun: F) -> RegisterData { - RegisterData { - c: fun(&self.c), - t: fun(&self.t), - u: fun(&self.u), - s: fun(&self.s), - } - } -} - -impl RegisterData { - fn add_content_many(&mut self, content: DescriptorContent, many: DescriptorIndex) { - if content.contains(DescriptorContent::CBV) { - self.c += many; - } - if content.contains(DescriptorContent::SRV) { - self.t += many; - } - if content.contains(DescriptorContent::UAV) { - self.u += many; - } - if content.contains(DescriptorContent::SAMPLER) { - self.s += many; - } - } - - fn sum(&self) -> DescriptorIndex { - self.c + self.t + self.u + self.s - } -} - -#[derive(Clone, Debug, Default)] -struct MultiStageData { - vs: T, - ps: T, - cs: T, -} - -impl MultiStageData { - fn select(self, stage: ShaderStage) -> T { - match stage { - ShaderStage::Vertex => self.vs, - ShaderStage::Fragment => self.ps, - ShaderStage::Compute => self.cs, - _ => panic!("Unsupported stage {:?}", stage), - } - } -} - -impl MultiStageData> { - fn map_register U>(&self, fun: F) -> MultiStageData> { - MultiStageData { - vs: self.vs.map(&fun), - ps: self.ps.map(&fun), - cs: self.cs.map(&fun), - } - } - - fn map_other) -> U>(&self, fun: F) -> MultiStageData { - MultiStageData { - vs: fun(&self.vs), - ps: fun(&self.ps), - cs: fun(&self.cs), - } - } -} - -impl MultiStageData> { - fn add_content_many( - &mut self, - content: DescriptorContent, - stages: pso::ShaderStageFlags, - count: DescriptorIndex, - ) { - if stages.contains(pso::ShaderStageFlags::VERTEX) { - self.vs.add_content_many(content, count); - } - if stages.contains(pso::ShaderStageFlags::FRAGMENT) { - self.ps.add_content_many(content, count); - } - if stages.contains(pso::ShaderStageFlags::COMPUTE) { - self.cs.add_content_many(content, count); - } - } - - fn sum(&self) -> DescriptorIndex { - self.vs.sum() + self.ps.sum() + self.cs.sum() - } -} - -#[derive(Clone, Debug, Default)] -struct RegisterPoolMapping { - offset: DescriptorIndex, - count: ResourceIndex, -} - -#[derive(Clone, Debug, Default)] -struct RegisterInfo { - res_index: ResourceIndex, - pool_offset: DescriptorIndex, - count: ResourceIndex, -} - -impl RegisterInfo { - fn as_some(&self) -> Option<&Self> { - if self.count == 0 { - None - } else { - Some(self) - } - } -} - -#[derive(Clone, Debug, Default)] -struct RegisterAccumulator { - res_index: ResourceIndex, -} - -impl RegisterAccumulator { - fn to_mapping(&self, cur_offset: &mut DescriptorIndex) -> RegisterPoolMapping { - let offset = *cur_offset; - *cur_offset += self.res_index as DescriptorIndex; - - RegisterPoolMapping { - offset, - count: self.res_index, - } - } - - fn advance(&mut self, mapping: &RegisterPoolMapping) -> RegisterInfo { - let res_index = self.res_index; - self.res_index += mapping.count; - RegisterInfo { - res_index, - pool_offset: mapping.offset, - count: mapping.count, - } - } -} - -impl RegisterData { - fn to_mapping(&self, pool_offset: &mut DescriptorIndex) -> RegisterData { - RegisterData { - c: self.c.to_mapping(pool_offset), - t: self.t.to_mapping(pool_offset), - u: self.u.to_mapping(pool_offset), - s: self.s.to_mapping(pool_offset), - } - } - - fn advance( - &mut self, - mapping: &RegisterData, - ) -> RegisterData { - RegisterData { - c: self.c.advance(&mapping.c), - t: self.t.advance(&mapping.t), - u: self.u.advance(&mapping.u), - s: self.s.advance(&mapping.s), - } - } -} - -impl MultiStageData> { - fn to_mapping(&self) -> MultiStageData> { - let mut pool_offset = 0; - MultiStageData { - vs: self.vs.to_mapping(&mut pool_offset), - ps: self.ps.to_mapping(&mut pool_offset), - cs: self.cs.to_mapping(&mut pool_offset), - } - } - - fn advance( - &mut self, - mapping: &MultiStageData>, - ) -> MultiStageData> { - MultiStageData { - vs: self.vs.advance(&mapping.vs), - ps: self.ps.advance(&mapping.ps), - cs: self.cs.advance(&mapping.cs), - } - } -} - -#[derive(Clone, Debug)] -struct DescriptorSetInfo { - bindings: Arc>, - registers: MultiStageData>, -} - -impl DescriptorSetInfo { - fn find_register( - &self, - stage: ShaderStage, - binding_index: pso::DescriptorBinding, - ) -> Option<(DescriptorContent, RegisterData)> { - let mut res_offsets = self - .registers - .map_register(|info| info.res_index as DescriptorIndex) - .select(stage); - for binding in self.bindings.iter() { - if !binding.stage_flags.contains(stage.to_flag()) { - continue; - } - let content = DescriptorContent::from(binding.ty); - if binding.binding == binding_index { - return Some((content, res_offsets.map(|offset| *offset as ResourceIndex))); - } - res_offsets.add_content_many(content, 1); - } - None - } - - fn find_uav_register( - &self, - stage: ShaderStage, - binding_index: pso::DescriptorBinding, - ) -> (DescriptorContent, RegisterData) { - // Look only where uavs are stored for that stage. - let register_stage = if stage == ShaderStage::Compute { - stage - } else { - ShaderStage::Fragment - }; - - let mut res_offsets = self - .registers - .map_register(|info| info.res_index as DescriptorIndex) - .select(register_stage); - for binding in self.bindings.iter() { - // We don't care what stage they're in, only if they are UAVs or not. - let content = DescriptorContent::from(binding.ty); - if !content.contains(DescriptorContent::UAV) { - continue; - } - if binding.binding == binding_index { - return (content, res_offsets.map(|offset| *offset as ResourceIndex)); - } - res_offsets.add_content_many(content, 1); - } - panic!("Unable to find binding {:?}", binding_index); - } -} - -/// The pipeline layout holds optimized (less api calls) ranges of objects for all descriptor sets -/// belonging to the pipeline object. -#[derive(Debug)] -pub struct PipelineLayout { - sets: Vec, -} - -/// The descriptor set layout contains mappings from a given binding to the offset in our -/// descriptor pool storage and what type of descriptor it is (combined image sampler takes up two -/// handles). -#[derive(Debug)] -pub struct DescriptorSetLayout { - bindings: Arc>, - pool_mapping: MultiStageData>, -} - -#[derive(Debug)] -struct CoherentBufferFlushRange { - device_buffer: *mut d3d11::ID3D11Buffer, - host_ptr: *mut u8, - range: SyncRange, -} - -#[derive(Debug)] -struct CoherentBufferInvalidateRange { - device_buffer: *mut d3d11::ID3D11Buffer, - host_ptr: *mut u8, - range: Range, -} - -#[derive(Debug)] -struct CoherentBuffers { - // descriptor set writes containing coherent resources go into these vecs and are added to the - // command buffers own Vec on binding the set. - flush_coherent_buffers: RefCell>, - invalidate_coherent_buffers: RefCell>, -} - -impl CoherentBuffers { - fn _add_flush(&self, old: *mut d3d11::ID3D11Buffer, buffer: &Buffer) { - let new = buffer.internal.raw; - - if old != new { - let mut buffers = self.flush_coherent_buffers.borrow_mut(); - - let pos = buffers.iter().position(|sync| old == sync.device_buffer); - - let sync_range = CoherentBufferFlushRange { - device_buffer: new, - host_ptr: buffer.memory_ptr, - range: SyncRange::Whole, - }; - - if let Some(pos) = pos { - buffers[pos] = sync_range; - } else { - buffers.push(sync_range); - } - - if let Some(disjoint) = buffer.internal.disjoint_cb { - let pos = buffers - .iter() - .position(|sync| disjoint == sync.device_buffer); - - let sync_range = CoherentBufferFlushRange { - device_buffer: disjoint, - host_ptr: buffer.memory_ptr, - range: SyncRange::Whole, - }; - - if let Some(pos) = pos { - buffers[pos] = sync_range; - } else { - buffers.push(sync_range); - } - } - } - } - - fn _add_invalidate(&self, old: *mut d3d11::ID3D11Buffer, buffer: &Buffer) { - let new = buffer.internal.raw; - - if old != new { - let mut buffers = self.invalidate_coherent_buffers.borrow_mut(); - - let pos = buffers.iter().position(|sync| old == sync.device_buffer); - - let sync_range = CoherentBufferInvalidateRange { - device_buffer: new, - host_ptr: buffer.memory_ptr, - range: buffer.bound_range.clone(), - }; - - if let Some(pos) = pos { - buffers[pos] = sync_range; - } else { - buffers.push(sync_range); - } - } - } -} - -/// Newtype around a common interface that all bindable resources inherit from. -#[derive(Debug, Copy, Clone)] -#[repr(transparent)] -struct Descriptor(*mut d3d11::ID3D11DeviceChild); - -bitflags! { - /// A set of D3D11 descriptor types that need to be associated - /// with a single gfx-hal `DescriptorType`. - #[derive(Default)] - pub struct DescriptorContent: u8 { - const CBV = 0x1; - const SRV = 0x2; - const UAV = 0x4; - const SAMPLER = 0x8; - /// Indicates if the descriptor is a dynamic uniform/storage buffer. - /// Important as dynamic buffers are implemented as root descriptors. - const DYNAMIC = 0x10; - } -} - -impl From for DescriptorContent { - fn from(ty: pso::DescriptorType) -> Self { - use hal::pso::{ - BufferDescriptorFormat as Bdf, BufferDescriptorType as Bdt, DescriptorType as Dt, - ImageDescriptorType as Idt, - }; - match ty { - Dt::Sampler => DescriptorContent::SAMPLER, - Dt::Image { - ty: Idt::Sampled { with_sampler: true }, - } => DescriptorContent::SRV | DescriptorContent::SAMPLER, - Dt::Image { - ty: Idt::Sampled { - with_sampler: false, - }, - } - | Dt::InputAttachment => DescriptorContent::SRV, - Dt::Image { - ty: Idt::Storage { .. }, - } => DescriptorContent::UAV, - Dt::Buffer { - ty: Bdt::Uniform, - format: - Bdf::Structured { - dynamic_offset: true, - }, - } => DescriptorContent::CBV | DescriptorContent::DYNAMIC, - Dt::Buffer { - ty: Bdt::Uniform, .. - } => DescriptorContent::CBV, - Dt::Buffer { - ty: Bdt::Storage { read_only: true }, - format: - Bdf::Structured { - dynamic_offset: true, - }, - } => DescriptorContent::SRV | DescriptorContent::DYNAMIC, - Dt::Buffer { - ty: Bdt::Storage { read_only: false }, - format: - Bdf::Structured { - dynamic_offset: true, - }, - } => DescriptorContent::UAV | DescriptorContent::DYNAMIC, - Dt::Buffer { - ty: Bdt::Storage { read_only: true }, - .. - } => DescriptorContent::SRV, - Dt::Buffer { - ty: Bdt::Storage { read_only: false }, - .. - } => DescriptorContent::UAV, - } - } -} - -pub struct DescriptorSet { - offset: DescriptorIndex, - len: DescriptorIndex, - handles: *mut Descriptor, - coherent_buffers: Mutex, - layout: DescriptorSetLayout, -} - -impl fmt::Debug for DescriptorSet { - fn fmt(&self, fmt: &mut fmt::Formatter) -> fmt::Result { - fmt.write_str("DescriptorSet") - } -} - -unsafe impl Send for DescriptorSet {} -unsafe impl Sync for DescriptorSet {} - -impl DescriptorSet { - fn _add_flush(&self, old: *mut d3d11::ID3D11Buffer, buffer: &Buffer) { - let new = buffer.internal.raw; - - if old != new { - self.coherent_buffers.lock()._add_flush(old, buffer); - } - } - - fn _add_invalidate(&self, old: *mut d3d11::ID3D11Buffer, buffer: &Buffer) { - let new = buffer.internal.raw; - - if old != new { - self.coherent_buffers.lock()._add_invalidate(old, buffer); - } - } - - unsafe fn assign(&self, offset: DescriptorIndex, value: *mut d3d11::ID3D11DeviceChild) { - *self.handles.offset(offset as isize) = Descriptor(value); - } - - unsafe fn assign_stages( - &self, - offsets: &MultiStageData, - stages: pso::ShaderStageFlags, - value: *mut d3d11::ID3D11DeviceChild, - ) { - if stages.contains(pso::ShaderStageFlags::VERTEX) { - self.assign(offsets.vs, value); - } - if stages.contains(pso::ShaderStageFlags::FRAGMENT) { - self.assign(offsets.ps, value); - } - if stages.contains(pso::ShaderStageFlags::COMPUTE) { - self.assign(offsets.cs, value); - } - } -} - -#[derive(Debug)] -pub struct DescriptorPool { - //TODO: do we need this in the pool? - // if the sets owned their data, we could make this just `Vec` - handles: Vec, - allocator: RangeAllocator, -} - -unsafe impl Send for DescriptorPool {} -unsafe impl Sync for DescriptorPool {} - -impl DescriptorPool { - fn with_capacity(size: DescriptorIndex) -> Self { - DescriptorPool { - handles: vec![Descriptor(ptr::null_mut()); size as usize], - allocator: RangeAllocator::new(0..size), - } - } -} - -impl pso::DescriptorPool for DescriptorPool { - unsafe fn allocate_one( - &mut self, - layout: &DescriptorSetLayout, - ) -> Result { - let len = layout - .pool_mapping - .map_register(|mapping| mapping.count as DescriptorIndex) - .sum() - .max(1); - - self.allocator - .allocate_range(len) - .map(|range| { - for handle in &mut self.handles[range.start as usize..range.end as usize] { - *handle = Descriptor(ptr::null_mut()); - } - - DescriptorSet { - offset: range.start, - len, - handles: self.handles.as_mut_ptr().offset(range.start as _), - coherent_buffers: Mutex::new(CoherentBuffers { - flush_coherent_buffers: RefCell::new(Vec::new()), - invalidate_coherent_buffers: RefCell::new(Vec::new()), - }), - layout: DescriptorSetLayout { - bindings: Arc::clone(&layout.bindings), - pool_mapping: layout.pool_mapping.clone(), - }, - } - }) - .map_err(|_| pso::AllocationError::OutOfPoolMemory) - } - - unsafe fn free(&mut self, descriptor_sets: I) - where - I: Iterator, - { - for set in descriptor_sets { - self.allocator - .free_range(set.offset..(set.offset + set.len)) - } - } - - unsafe fn reset(&mut self) { - self.allocator.reset(); - } -} - -#[derive(Debug)] -pub struct RawFence { - mutex: Mutex, - condvar: Condvar, -} - -pub type Fence = Arc; - -#[derive(Debug)] -pub struct Semaphore; -#[derive(Debug)] -pub struct QueryPool; - -#[derive(Copy, Clone, Debug, Eq, Hash, PartialEq)] -pub enum Backend {} -impl hal::Backend for Backend { - type Instance = Instance; - type PhysicalDevice = PhysicalDevice; - type Device = device::Device; - type Surface = Surface; - - type QueueFamily = QueueFamily; - type Queue = Queue; - type CommandBuffer = CommandBuffer; - - type Memory = Memory; - type CommandPool = CommandPool; - - type ShaderModule = ShaderModule; - type RenderPass = RenderPass; - type Framebuffer = Framebuffer; - - type Buffer = Buffer; - type BufferView = BufferView; - type Image = Image; - - type ImageView = ImageView; - type Sampler = Sampler; - - type ComputePipeline = ComputePipeline; - type GraphicsPipeline = GraphicsPipeline; - type PipelineLayout = PipelineLayout; - type PipelineCache = (); - type DescriptorSetLayout = DescriptorSetLayout; - type DescriptorPool = DescriptorPool; - type DescriptorSet = DescriptorSet; - - type Fence = Fence; - type Semaphore = Semaphore; - type Event = (); - type QueryPool = QueryPool; - - type Display = (); - type DisplayMode = (); -} - -fn validate_line_width(width: f32) { - // Note from the Vulkan spec: - // > If the wide lines feature is not enabled, lineWidth must be 1.0 - // Simply assert and no-op because DX11 never exposes `Features::LINE_WIDTH` - assert_eq!(width, 1.0); -} +/*! +# DX11 backend internals. + +## Pipeline Layout + +In D3D11 there are tables of CBVs, SRVs, UAVs, and samplers. + +Each descriptor type can take 1 or two of those entry points. + +The descriptor pool is just and array of handles, belonging to descriptor set 1, descriptor set 2, etc. +Each range of descriptors in a descriptor set area of the pool is split into shader stages, +which in turn is split into CBS/SRV/UAV/Sampler parts. That allows binding a descriptor set as a list +of continuous descriptor ranges (per type, per shader stage). + +!*/ + +//#[deny(missing_docs)] + +#[macro_use] +extern crate bitflags; +#[macro_use] +extern crate log; + +use crate::{debug::set_debug_name, device::DepthStencilState}; +use auxil::ShaderStage; +use hal::{ + adapter, buffer, command, format, image, memory, pass, pso, query, queue, window, DrawCount, + IndexCount, IndexType, InstanceCount, TaskCount, VertexCount, VertexOffset, WorkGroupCount, +}; +use range_alloc::RangeAllocator; +use smallvec::SmallVec; + +use winapi::{ + shared::{ + dxgi::{IDXGIAdapter, IDXGIFactory, IDXGISwapChain}, + dxgiformat, + minwindef::{FALSE, HMODULE, UINT}, + windef::{HWND, RECT}, + winerror, + }, + um::{d3d11, d3d11_1, d3dcommon, winuser::GetClientRect}, + Interface as _, +}; + +use wio::com::ComPtr; + +use arrayvec::ArrayVec; +use parking_lot::{Condvar, Mutex, RwLock}; + +use std::{ + borrow::Borrow, + cell::RefCell, + fmt, mem, + ops::Range, + os::raw::c_void, + ptr, + sync::{Arc, Weak}, +}; + +macro_rules! debug_scope { + ($context:expr, $($arg:tt)+) => ({ + #[cfg(debug_assertions)] + { + $crate::debug::DebugScope::with_name( + $context, + format_args!($($arg)+), + ) + } + #[cfg(not(debug_assertions))] + { + () + } + }); +} + +macro_rules! debug_marker { + ($context:expr, $($arg:tt)+) => ({ + #[cfg(debug_assertions)] + { + $crate::debug::debug_marker( + $context, + &format!($($arg)+), + ); + } + }); +} + +mod conv; +mod debug; +mod device; +mod dxgi; +mod internal; +mod shader; + +type CreateFun = unsafe extern "system" fn( + *mut IDXGIAdapter, + UINT, + HMODULE, + UINT, + *const UINT, + UINT, + UINT, + *mut *mut d3d11::ID3D11Device, + *mut UINT, + *mut *mut d3d11::ID3D11DeviceContext, +) -> winerror::HRESULT; + +#[derive(Clone)] +pub(crate) struct ViewInfo { + resource: *mut d3d11::ID3D11Resource, + kind: image::Kind, + caps: image::ViewCapabilities, + view_kind: image::ViewKind, + format: dxgiformat::DXGI_FORMAT, + levels: Range, + layers: Range, +} + +impl fmt::Debug for ViewInfo { + fn fmt(&self, fmt: &mut fmt::Formatter) -> fmt::Result { + fmt.write_str("ViewInfo") + } +} + +#[derive(Debug)] +pub struct Instance { + pub(crate) factory: ComPtr, + pub(crate) dxgi_version: dxgi::DxgiVersion, + library_d3d11: Arc, + library_dxgi: libloading::Library, +} + +unsafe impl Send for Instance {} +unsafe impl Sync for Instance {} + +impl Instance { + pub fn create_surface_from_hwnd(&self, hwnd: *mut c_void) -> Surface { + Surface { + factory: self.factory.clone(), + wnd_handle: hwnd as *mut _, + presentation: None, + } + } +} + +unsafe fn check_feature_support( + device: &d3d11::ID3D11Device, + feature: d3d11::D3D11_FEATURE, +) -> T { + let mut value = mem::zeroed::(); + let ret = device.CheckFeatureSupport( + feature, + &mut value as *mut _ as *mut _, + mem::size_of::() as _, + ); + assert_eq!(ret, winerror::S_OK); + value +} + +fn get_features( + device: ComPtr, + feature_level: d3dcommon::D3D_FEATURE_LEVEL, +) -> ( + hal::Features, + hal::DownlevelProperties, + hal::PerformanceCaveats, +) { + let mut features = hal::Features::empty() + | hal::Features::ROBUST_BUFFER_ACCESS // TODO: verify + | hal::Features::INSTANCE_RATE + | hal::Features::INDEPENDENT_BLENDING // TODO: verify + | hal::Features::SAMPLER_BORDER_COLOR + | hal::Features::SAMPLER_MIP_LOD_BIAS + | hal::Features::SAMPLER_MIRROR_CLAMP_EDGE + | hal::Features::SAMPLER_ANISOTROPY + | hal::Features::DEPTH_CLAMP + | hal::Features::NDC_Y_UP; + + let mut downlevel = hal::DownlevelProperties::default(); + let performance = hal::PerformanceCaveats::default(); + + if d3dcommon::D3D_FEATURE_LEVEL_9_1 <= feature_level + && feature_level < d3dcommon::D3D_FEATURE_LEVEL_9_3 + { + let d3d9_features: d3d11::D3D11_FEATURE_DATA_D3D9_OPTIONS = + unsafe { check_feature_support(&device, d3d11::D3D11_FEATURE_D3D9_OPTIONS) }; + downlevel.non_power_of_two_mipmapped_textures = + d3d9_features.FullNonPow2TextureSupport != 0; + } + + if d3dcommon::D3D_FEATURE_LEVEL_10_0 <= feature_level + && feature_level < d3dcommon::D3D_FEATURE_LEVEL_11_0 + { + let compute_support: d3d11::D3D11_FEATURE_DATA_D3D10_X_HARDWARE_OPTIONS = unsafe { + check_feature_support(&device, d3d11::D3D11_FEATURE_D3D10_X_HARDWARE_OPTIONS) + }; + downlevel.compute_shaders = + compute_support.ComputeShaders_Plus_RawAndStructuredBuffers_Via_Shader_4_x != 0 + } + + if feature_level >= d3dcommon::D3D_FEATURE_LEVEL_10_0 { + features |= hal::Features::TEXTURE_DESCRIPTOR_ARRAY + | hal::Features::FULL_DRAW_INDEX_U32 + | hal::Features::GEOMETRY_SHADER; + downlevel.shader_model = hal::DownlevelShaderModel::ShaderModel4; + downlevel.non_power_of_two_mipmapped_textures = true; + } + + if feature_level >= d3dcommon::D3D_FEATURE_LEVEL_10_1 { + features |= hal::Features::IMAGE_CUBE_ARRAY; + } + + if feature_level >= d3dcommon::D3D_FEATURE_LEVEL_11_0 { + features |= hal::Features::VERTEX_STORES_AND_ATOMICS + | hal::Features::FRAGMENT_STORES_AND_ATOMICS + | hal::Features::FORMAT_BC + | hal::Features::TESSELLATION_SHADER + | hal::Features::DRAW_INDIRECT_FIRST_INSTANCE; + + downlevel.compute_shaders = true; + downlevel.shader_model = hal::DownlevelShaderModel::ShaderModel5; + downlevel.storage_images = true; + downlevel.read_only_depth_stencil = true; + downlevel.device_local_image_copies = true; + } + + if feature_level >= d3dcommon::D3D_FEATURE_LEVEL_11_1 { + features |= hal::Features::LOGIC_OP; // TODO: Optional at 10_0 -> 11_0 + } + + (features, downlevel, performance) +} + +const MAX_PUSH_CONSTANT_SIZE: usize = 256; + +fn get_limits(feature_level: d3dcommon::D3D_FEATURE_LEVEL) -> hal::Limits { + let max_texture_uv_dimension = match feature_level { + d3dcommon::D3D_FEATURE_LEVEL_9_1 | d3dcommon::D3D_FEATURE_LEVEL_9_2 => 2048, + d3dcommon::D3D_FEATURE_LEVEL_9_3 => 4096, + d3dcommon::D3D_FEATURE_LEVEL_10_0 | d3dcommon::D3D_FEATURE_LEVEL_10_1 => 8192, + d3dcommon::D3D_FEATURE_LEVEL_11_0 | d3dcommon::D3D_FEATURE_LEVEL_11_1 | _ => 16384, + }; + + let max_texture_w_dimension = match feature_level { + d3dcommon::D3D_FEATURE_LEVEL_9_1 + | d3dcommon::D3D_FEATURE_LEVEL_9_2 + | d3dcommon::D3D_FEATURE_LEVEL_9_3 => 256, + d3dcommon::D3D_FEATURE_LEVEL_10_0 + | d3dcommon::D3D_FEATURE_LEVEL_10_1 + | d3dcommon::D3D_FEATURE_LEVEL_11_0 + | d3dcommon::D3D_FEATURE_LEVEL_11_1 + | _ => 2048, + }; + + let max_texture_cube_dimension = match feature_level { + d3dcommon::D3D_FEATURE_LEVEL_9_1 | d3dcommon::D3D_FEATURE_LEVEL_9_2 => 512, + _ => max_texture_uv_dimension, + }; + + let max_image_uav = 2; + let max_buffer_uav = d3d11::D3D11_PS_CS_UAV_REGISTER_COUNT - max_image_uav; + + let max_input_slots = match feature_level { + d3dcommon::D3D_FEATURE_LEVEL_9_1 + | d3dcommon::D3D_FEATURE_LEVEL_9_2 + | d3dcommon::D3D_FEATURE_LEVEL_9_3 + | d3dcommon::D3D_FEATURE_LEVEL_10_0 => 16, + d3dcommon::D3D_FEATURE_LEVEL_10_1 + | d3dcommon::D3D_FEATURE_LEVEL_11_0 + | d3dcommon::D3D_FEATURE_LEVEL_11_1 + | _ => 32, + }; + + let max_color_attachments = match feature_level { + d3dcommon::D3D_FEATURE_LEVEL_9_1 + | d3dcommon::D3D_FEATURE_LEVEL_9_2 + | d3dcommon::D3D_FEATURE_LEVEL_9_3 + | d3dcommon::D3D_FEATURE_LEVEL_10_0 => 4, + d3dcommon::D3D_FEATURE_LEVEL_10_1 + | d3dcommon::D3D_FEATURE_LEVEL_11_0 + | d3dcommon::D3D_FEATURE_LEVEL_11_1 + | _ => 8, + }; + + // https://docs.microsoft.com/en-us/windows/win32/api/d3d11/nf-d3d11-id3d11device-checkmultisamplequalitylevels#remarks + // for more information. + let max_samples = match feature_level { + d3dcommon::D3D_FEATURE_LEVEL_9_1 + | d3dcommon::D3D_FEATURE_LEVEL_9_2 + | d3dcommon::D3D_FEATURE_LEVEL_9_3 + | d3dcommon::D3D_FEATURE_LEVEL_10_0 => 0b0001, // Conservative, MSAA isn't required. + d3dcommon::D3D_FEATURE_LEVEL_10_1 => 0b0101, // Optimistic, 4xMSAA is required on all formats _but_ RGBA32. + d3dcommon::D3D_FEATURE_LEVEL_11_0 | d3dcommon::D3D_FEATURE_LEVEL_11_1 | _ => 0b1101, // Optimistic, 8xMSAA and 4xMSAA is required on all formats _but_ RGBA32 which requires 4x. + }; + + let max_constant_buffers = d3d11::D3D11_COMMONSHADER_CONSTANT_BUFFER_API_SLOT_COUNT - 1; + + let ( + max_compute_work_group_count_z, + max_compute_work_group_size_xy, + max_compute_work_group_size_z, + ) = match feature_level { + d3dcommon::D3D_FEATURE_LEVEL_10_0 | d3dcommon::D3D_FEATURE_LEVEL_10_1 => { + (1, d3d11::D3D11_CS_4_X_THREAD_GROUP_MAX_X, 1) + } + d3dcommon::D3D_FEATURE_LEVEL_11_0 | d3dcommon::D3D_FEATURE_LEVEL_11_1 => ( + d3d11::D3D11_CS_DISPATCH_MAX_THREAD_GROUPS_PER_DIMENSION, + d3d11::D3D11_CS_THREAD_GROUP_MAX_X, + d3d11::D3D11_CS_THREAD_GROUP_MAX_Z, + ), + _ => (0, 0, 0), + }; + + let max_compute_shared_memory_size = match feature_level { + d3dcommon::D3D_FEATURE_LEVEL_10_0 | d3dcommon::D3D_FEATURE_LEVEL_10_1 => 4096 * 4, // This doesn't have an equiv SM4 constant :\ + d3dcommon::D3D_FEATURE_LEVEL_11_0 | d3dcommon::D3D_FEATURE_LEVEL_11_1 => { + d3d11::D3D11_CS_TGSM_REGISTER_COUNT * 4 + } + _ => 0, + } as _; + + hal::Limits { + max_image_1d_size: max_texture_uv_dimension, + max_image_2d_size: max_texture_uv_dimension, + max_image_3d_size: max_texture_w_dimension, + max_image_cube_size: max_texture_cube_dimension, + max_image_array_layers: max_texture_cube_dimension as _, + descriptor_limits: hal::DescriptorLimits { + max_per_stage_descriptor_samplers: d3d11::D3D11_COMMONSHADER_SAMPLER_SLOT_COUNT as _, + // Leave top buffer for push constants + max_per_stage_descriptor_uniform_buffers: max_constant_buffers as _, + max_per_stage_descriptor_storage_buffers: max_buffer_uav, + max_per_stage_descriptor_sampled_images: + d3d11::D3D11_COMMONSHADER_INPUT_RESOURCE_REGISTER_COUNT as _, + max_per_stage_descriptor_storage_images: max_image_uav, + max_descriptor_set_uniform_buffers_dynamic: max_constant_buffers as _, + max_descriptor_set_storage_buffers_dynamic: 0, // TODO: Implement dynamic offsets for storage buffers + ..hal::DescriptorLimits::default() // TODO + }, + max_bound_descriptor_sets: pso::DescriptorSetIndex::MAX, + max_texel_elements: max_texture_uv_dimension as _, //TODO + max_patch_size: d3d11::D3D11_IA_PATCH_MAX_CONTROL_POINT_COUNT as _, + max_viewports: d3d11::D3D11_VIEWPORT_AND_SCISSORRECT_OBJECT_COUNT_PER_PIPELINE as _, + max_viewport_dimensions: [d3d11::D3D11_VIEWPORT_BOUNDS_MAX; 2], + max_framebuffer_extent: hal::image::Extent { + //TODO + width: 4096, + height: 4096, + depth: 1, + }, + max_compute_shared_memory_size, + max_compute_work_group_count: [ + d3d11::D3D11_CS_DISPATCH_MAX_THREAD_GROUPS_PER_DIMENSION, + d3d11::D3D11_CS_DISPATCH_MAX_THREAD_GROUPS_PER_DIMENSION, + max_compute_work_group_count_z, + ], + max_compute_work_group_invocations: d3d11::D3D11_CS_THREAD_GROUP_MAX_THREADS_PER_GROUP as _, + max_compute_work_group_size: [ + max_compute_work_group_size_xy, + max_compute_work_group_size_xy, + max_compute_work_group_size_z, + ], // TODO + max_vertex_input_attribute_offset: 255, // TODO + max_vertex_input_attributes: max_input_slots, + max_vertex_input_binding_stride: d3d11::D3D11_REQ_MULTI_ELEMENT_STRUCTURE_SIZE_IN_BYTES + as _, + max_vertex_input_bindings: d3d11::D3D11_IA_VERTEX_INPUT_RESOURCE_SLOT_COUNT as _, // TODO: verify same as attributes + max_vertex_output_components: d3d11::D3D11_VS_OUTPUT_REGISTER_COUNT as _, // TODO + min_texel_buffer_offset_alignment: 1, // TODO + min_uniform_buffer_offset_alignment: 16, + min_storage_buffer_offset_alignment: 16, // TODO + framebuffer_color_sample_counts: max_samples, + framebuffer_depth_sample_counts: max_samples, + framebuffer_stencil_sample_counts: max_samples, + max_color_attachments, + buffer_image_granularity: 1, + non_coherent_atom_size: 1, // TODO + max_sampler_anisotropy: 16.0, + optimal_buffer_copy_offset_alignment: 1, // TODO + // buffer -> image and image -> buffer paths use compute shaders that, at maximum, read 4 pixels from the buffer + // at a time, so need an alignment of at least 4. + optimal_buffer_copy_pitch_alignment: 4, + min_vertex_input_binding_stride_alignment: 1, + max_push_constants_size: MAX_PUSH_CONSTANT_SIZE, + max_uniform_buffer_range: 1 << 16, + ..hal::Limits::default() //TODO + } +} + +fn get_format_properties( + device: ComPtr, +) -> [format::Properties; format::NUM_FORMATS] { + let mut format_properties = [format::Properties::default(); format::NUM_FORMATS]; + for (i, props) in &mut format_properties.iter_mut().enumerate().skip(1) { + let format: format::Format = unsafe { mem::transmute(i as u32) }; + + let dxgi_format = match conv::map_format(format) { + Some(format) => format, + None => continue, + }; + + let mut support = d3d11::D3D11_FEATURE_DATA_FORMAT_SUPPORT { + InFormat: dxgi_format, + OutFormatSupport: 0, + }; + let mut support_2 = d3d11::D3D11_FEATURE_DATA_FORMAT_SUPPORT2 { + InFormat: dxgi_format, + OutFormatSupport2: 0, + }; + + let hr = unsafe { + device.CheckFeatureSupport( + d3d11::D3D11_FEATURE_FORMAT_SUPPORT, + &mut support as *mut _ as *mut _, + mem::size_of::() as UINT, + ) + }; + if hr != winerror::S_OK { + warn!("Format {:?} can't check the features-1: 0x{:x}", format, hr); + continue; + } + + let can_buffer = 0 != support.OutFormatSupport & d3d11::D3D11_FORMAT_SUPPORT_BUFFER; + let can_image = 0 + != support.OutFormatSupport + & (d3d11::D3D11_FORMAT_SUPPORT_TEXTURE1D + | d3d11::D3D11_FORMAT_SUPPORT_TEXTURE2D + | d3d11::D3D11_FORMAT_SUPPORT_TEXTURE3D + | d3d11::D3D11_FORMAT_SUPPORT_TEXTURECUBE); + let can_linear = can_image && !format.surface_desc().is_compressed(); + if can_image { + props.optimal_tiling |= format::ImageFeature::TRANSFER_SRC + | format::ImageFeature::TRANSFER_DST + | format::ImageFeature::SAMPLED + | format::ImageFeature::BLIT_SRC; + } + if can_linear { + props.linear_tiling |= format::ImageFeature::TRANSFER_SRC + | format::ImageFeature::TRANSFER_DST + | format::ImageFeature::SAMPLED + | format::ImageFeature::BLIT_SRC; + } + if support.OutFormatSupport & d3d11::D3D11_FORMAT_SUPPORT_IA_VERTEX_BUFFER != 0 { + props.buffer_features |= format::BufferFeature::VERTEX; + } + if support.OutFormatSupport & d3d11::D3D11_FORMAT_SUPPORT_SHADER_SAMPLE != 0 { + props.optimal_tiling |= format::ImageFeature::SAMPLED_LINEAR; + } + if support.OutFormatSupport & d3d11::D3D11_FORMAT_SUPPORT_RENDER_TARGET != 0 { + props.optimal_tiling |= + format::ImageFeature::COLOR_ATTACHMENT | format::ImageFeature::BLIT_DST; + if can_linear { + props.linear_tiling |= + format::ImageFeature::COLOR_ATTACHMENT | format::ImageFeature::BLIT_DST; + } + } + if support.OutFormatSupport & d3d11::D3D11_FORMAT_SUPPORT_BLENDABLE != 0 { + props.optimal_tiling |= format::ImageFeature::COLOR_ATTACHMENT_BLEND; + } + if support.OutFormatSupport & d3d11::D3D11_FORMAT_SUPPORT_DEPTH_STENCIL != 0 { + props.optimal_tiling |= format::ImageFeature::DEPTH_STENCIL_ATTACHMENT; + } + if support.OutFormatSupport & d3d11::D3D11_FORMAT_SUPPORT_SHADER_LOAD != 0 { + //TODO: check d3d12::D3D12_FORMAT_SUPPORT2_UAV_TYPED_LOAD ? + if can_buffer { + props.buffer_features |= format::BufferFeature::UNIFORM_TEXEL; + } + } + + let hr = unsafe { + device.CheckFeatureSupport( + d3d11::D3D11_FEATURE_FORMAT_SUPPORT2, + &mut support_2 as *mut _ as *mut _, + mem::size_of::() as UINT, + ) + }; + if hr != winerror::S_OK { + warn!("Format {:?} can't check the features-2: 0x{:X}", format, hr); + continue; + } + if support_2.OutFormatSupport2 & d3d11::D3D11_FORMAT_SUPPORT2_UAV_ATOMIC_ADD != 0 { + //TODO: other atomic flags? + if can_buffer { + props.buffer_features |= format::BufferFeature::STORAGE_TEXEL_ATOMIC; + } + if can_image { + props.optimal_tiling |= format::ImageFeature::STORAGE_ATOMIC; + } + } + if support_2.OutFormatSupport2 & d3d11::D3D11_FORMAT_SUPPORT2_UAV_TYPED_STORE != 0 { + if can_buffer { + props.buffer_features |= format::BufferFeature::STORAGE_TEXEL; + } + if can_image { + // Since read-only storage is exposed as SRV, we can guarantee read-only storage without checking D3D11_FORMAT_SUPPORT2_UAV_TYPED_LOAD first. + props.optimal_tiling |= format::ImageFeature::STORAGE; + + if support_2.OutFormatSupport2 & d3d11::D3D11_FORMAT_SUPPORT2_UAV_TYPED_LOAD != 0 { + props.optimal_tiling |= format::ImageFeature::STORAGE_READ_WRITE; + } + } + } + } + + format_properties +} + +impl hal::Instance for Instance { + fn create(_: &str, _: u32) -> Result { + // TODO: get the latest factory we can find + + match dxgi::get_dxgi_factory() { + Ok((library_dxgi, factory, dxgi_version)) => { + info!("DXGI version: {:?}", dxgi_version); + let library_d3d11 = Arc::new(unsafe { + libloading::Library::new("d3d11.dll").map_err(|_| hal::UnsupportedBackend)? + }); + Ok(Instance { + factory, + dxgi_version, + library_d3d11, + library_dxgi, + }) + } + Err(hr) => { + info!("Failed on factory creation: {:?}", hr); + Err(hal::UnsupportedBackend) + } + } + } + + fn enumerate_adapters(&self) -> Vec> { + let mut adapters = Vec::new(); + let mut idx = 0; + + let func: libloading::Symbol = + match unsafe { self.library_d3d11.get(b"D3D11CreateDevice") } { + Ok(func) => func, + Err(e) => { + error!("Unable to get device creation function: {:?}", e); + return Vec::new(); + } + }; + + while let Ok(adapter) = dxgi::get_adapter(idx, self.factory.as_raw(), self.dxgi_version) { + idx += 1; + + use hal::memory::Properties; + + // TODO: move into function? + let (device, feature_level) = { + let feature_level = get_feature_level(&func, adapter.as_raw()); + + let mut device = ptr::null_mut(); + let hr = unsafe { + func( + adapter.as_raw() as *mut _, + d3dcommon::D3D_DRIVER_TYPE_UNKNOWN, + ptr::null_mut(), + 0, + [feature_level].as_ptr(), + 1, + d3d11::D3D11_SDK_VERSION, + &mut device as *mut *mut _ as *mut *mut _, + ptr::null_mut(), + ptr::null_mut(), + ) + }; + + if !winerror::SUCCEEDED(hr) { + continue; + } + + ( + unsafe { ComPtr::::from_raw(device) }, + feature_level, + ) + }; + + let memory_properties = adapter::MemoryProperties { + memory_types: vec![ + adapter::MemoryType { + properties: Properties::DEVICE_LOCAL, + heap_index: 0, + }, + adapter::MemoryType { + properties: Properties::CPU_VISIBLE + | Properties::COHERENT + | Properties::CPU_CACHED, + heap_index: 1, + }, + adapter::MemoryType { + properties: Properties::CPU_VISIBLE | Properties::CPU_CACHED, + heap_index: 1, + }, + ], + // TODO: would using *VideoMemory and *SystemMemory from + // DXGI_ADAPTER_DESC be too optimistic? :) + memory_heaps: vec![ + adapter::MemoryHeap { + size: !0, + flags: memory::HeapFlags::DEVICE_LOCAL, + }, + adapter::MemoryHeap { + size: !0, + flags: memory::HeapFlags::empty(), + }, + ], + }; + + let info = dxgi::get_adapter_desc(&adapter, &device, self.dxgi_version); + let limits = get_limits(feature_level); + let (features, downlevel, performance_caveats) = + get_features(device.clone(), feature_level); + let format_properties = get_format_properties(device.clone()); + + let physical_device = PhysicalDevice { + adapter, + library_d3d11: Arc::clone(&self.library_d3d11), + features, + properties: hal::PhysicalDeviceProperties { + limits, + dynamic_pipeline_states: hal::DynamicStates::VIEWPORT + | hal::DynamicStates::SCISSOR + | hal::DynamicStates::BLEND_CONSTANTS + | hal::DynamicStates::DEPTH_BOUNDS + | hal::DynamicStates::STENCIL_REFERENCE, + downlevel, + performance_caveats, + ..hal::PhysicalDeviceProperties::default() + }, + memory_properties, + format_properties, + }; + + info!("{:#?}", info); + + adapters.push(adapter::Adapter { + info, + physical_device, + queue_families: vec![QueueFamily], + }); + } + + adapters + } + + unsafe fn create_surface( + &self, + has_handle: &impl raw_window_handle::HasRawWindowHandle, + ) -> Result { + match has_handle.raw_window_handle() { + raw_window_handle::RawWindowHandle::Windows(handle) => { + Ok(self.create_surface_from_hwnd(handle.hwnd)) + } + _ => Err(hal::window::InitError::UnsupportedWindowHandle), + } + } + + unsafe fn destroy_surface(&self, _surface: Surface) { + // TODO: Implement Surface cleanup + } +} + +pub struct PhysicalDevice { + adapter: ComPtr, + library_d3d11: Arc, + features: hal::Features, + properties: hal::PhysicalDeviceProperties, + memory_properties: adapter::MemoryProperties, + format_properties: [format::Properties; format::NUM_FORMATS], +} + +impl fmt::Debug for PhysicalDevice { + fn fmt(&self, fmt: &mut fmt::Formatter) -> fmt::Result { + fmt.write_str("PhysicalDevice") + } +} + +unsafe impl Send for PhysicalDevice {} +unsafe impl Sync for PhysicalDevice {} + +// TODO: does the adapter we get earlier matter for feature level? +fn get_feature_level(func: &CreateFun, adapter: *mut IDXGIAdapter) -> d3dcommon::D3D_FEATURE_LEVEL { + let requested_feature_levels = [ + d3dcommon::D3D_FEATURE_LEVEL_11_1, + d3dcommon::D3D_FEATURE_LEVEL_11_0, + d3dcommon::D3D_FEATURE_LEVEL_10_1, + d3dcommon::D3D_FEATURE_LEVEL_10_0, + d3dcommon::D3D_FEATURE_LEVEL_9_3, + d3dcommon::D3D_FEATURE_LEVEL_9_2, + d3dcommon::D3D_FEATURE_LEVEL_9_1, + ]; + + let mut feature_level = d3dcommon::D3D_FEATURE_LEVEL_9_1; + let hr = unsafe { + func( + adapter, + d3dcommon::D3D_DRIVER_TYPE_UNKNOWN, + ptr::null_mut(), + 0, + requested_feature_levels[..].as_ptr(), + requested_feature_levels.len() as _, + d3d11::D3D11_SDK_VERSION, + ptr::null_mut(), + &mut feature_level as *mut _, + ptr::null_mut(), + ) + }; + + if !winerror::SUCCEEDED(hr) { + // if there is no 11.1 runtime installed, requesting + // `D3D_FEATURE_LEVEL_11_1` will return E_INVALIDARG so we just retry + // without that + if hr == winerror::E_INVALIDARG { + let hr = unsafe { + func( + adapter, + d3dcommon::D3D_DRIVER_TYPE_UNKNOWN, + ptr::null_mut(), + 0, + requested_feature_levels[1..].as_ptr(), + (requested_feature_levels.len() - 1) as _, + d3d11::D3D11_SDK_VERSION, + ptr::null_mut(), + &mut feature_level as *mut _, + ptr::null_mut(), + ) + }; + + if !winerror::SUCCEEDED(hr) { + // TODO: device might not support any feature levels? + unimplemented!(); + } + } + } + + feature_level +} + +// TODO: PhysicalDevice +impl adapter::PhysicalDevice for PhysicalDevice { + unsafe fn open( + &self, + families: &[(&QueueFamily, &[queue::QueuePriority])], + requested_features: hal::Features, + ) -> Result, hal::device::CreationError> { + let func: libloading::Symbol = + self.library_d3d11.get(b"D3D11CreateDevice").unwrap(); + + let (device, cxt, feature_level) = { + if !self.features().contains(requested_features) { + return Err(hal::device::CreationError::MissingFeature); + } + + let feature_level = get_feature_level(&func, self.adapter.as_raw()); + let mut returned_level = d3dcommon::D3D_FEATURE_LEVEL_9_1; + + #[cfg(debug_assertions)] + let create_flags = d3d11::D3D11_CREATE_DEVICE_DEBUG; + #[cfg(not(debug_assertions))] + let create_flags = 0; + + // TODO: request debug device only on debug config? + let mut device: *mut d3d11::ID3D11Device = ptr::null_mut(); + let mut cxt = ptr::null_mut(); + let hr = func( + self.adapter.as_raw() as *mut _, + d3dcommon::D3D_DRIVER_TYPE_UNKNOWN, + ptr::null_mut(), + create_flags, + [feature_level].as_ptr(), + 1, + d3d11::D3D11_SDK_VERSION, + &mut device as *mut *mut _ as *mut *mut _, + &mut returned_level as *mut _, + &mut cxt as *mut *mut _ as *mut *mut _, + ); + + // NOTE: returns error if adapter argument is non-null and driver + // type is not unknown; or if debug device is requested but not + // present + if !winerror::SUCCEEDED(hr) { + if cfg!(debug_assertions) { + log::warn!( + "Unable to create a debug device. Trying to recreate a device without D3D11_CREATE_DEVICE_DEBUG flag. More info:\n{}\n{}", + "https://docs.microsoft.com/en-us/windows/win32/direct3d11/overviews-direct3d-11-devices-layers#debug-layer", + "https://github.com/gfx-rs/gfx/issues/3112" + ); + + let hr = func( + self.adapter.as_raw() as *mut _, + d3dcommon::D3D_DRIVER_TYPE_UNKNOWN, + ptr::null_mut(), + 0, + [feature_level].as_ptr(), + 1, + d3d11::D3D11_SDK_VERSION, + &mut device as *mut *mut _ as *mut *mut _, + &mut returned_level as *mut _, + &mut cxt as *mut *mut _ as *mut *mut _, + ); + + if !winerror::SUCCEEDED(hr) { + return Err(hal::device::CreationError::InitializationFailed); + } + } else { + return Err(hal::device::CreationError::InitializationFailed); + } + } + + info!( + "feature level={:x}=FL{}_{}", + feature_level, + feature_level >> 12, + feature_level >> 8 & 0xF + ); + + ( + ComPtr::from_raw(device), + ComPtr::from_raw(cxt), + feature_level, + ) + }; + + let device1 = device.cast::().ok(); + + let device = device::Device::new( + device, + device1, + cxt, + requested_features, + self.properties.downlevel, + self.memory_properties.clone(), + feature_level, + ); + + // TODO: deferred context => 1 cxt/queue? + let queue_groups = families + .iter() + .map(|&(_family, prio)| { + assert_eq!(prio.len(), 1); + let mut group = queue::QueueGroup::new(queue::QueueFamilyId(0)); + + // TODO: multiple queues? + let queue = Queue { + context: device.context.clone(), + }; + group.add_queue(queue); + group + }) + .collect(); + + Ok(adapter::Gpu { + device, + queue_groups, + }) + } + + fn format_properties(&self, fmt: Option) -> format::Properties { + let idx = fmt.map(|fmt| fmt as usize).unwrap_or(0); + self.format_properties[idx] + } + + fn image_format_properties( + &self, + format: format::Format, + dimensions: u8, + tiling: image::Tiling, + usage: image::Usage, + view_caps: image::ViewCapabilities, + ) -> Option { + conv::map_format(format)?; //filter out unknown formats + + let supported_usage = { + use hal::image::Usage as U; + let format_props = &self.format_properties[format as usize]; + let props = match tiling { + image::Tiling::Optimal => format_props.optimal_tiling, + image::Tiling::Linear => format_props.linear_tiling, + }; + let mut flags = U::empty(); + // Note: these checks would have been nicer if we had explicit BLIT usage + if props.contains(format::ImageFeature::BLIT_SRC) { + flags |= U::TRANSFER_SRC; + } + if props.contains(format::ImageFeature::BLIT_DST) { + flags |= U::TRANSFER_DST; + } + if props.contains(format::ImageFeature::SAMPLED) { + flags |= U::SAMPLED; + } + if props.contains(format::ImageFeature::STORAGE) { + flags |= U::STORAGE; + } + if props.contains(format::ImageFeature::COLOR_ATTACHMENT) { + flags |= U::COLOR_ATTACHMENT; + } + if props.contains(format::ImageFeature::DEPTH_STENCIL_ATTACHMENT) { + flags |= U::DEPTH_STENCIL_ATTACHMENT; + } + flags + }; + if !supported_usage.contains(usage) { + return None; + } + + let max_resource_size = + (d3d11::D3D11_REQ_RESOURCE_SIZE_IN_MEGABYTES_EXPRESSION_A_TERM as usize) << 20; + Some(match tiling { + image::Tiling::Optimal => image::FormatProperties { + max_extent: match dimensions { + 1 => image::Extent { + width: d3d11::D3D11_REQ_TEXTURE1D_U_DIMENSION, + height: 1, + depth: 1, + }, + 2 => image::Extent { + width: d3d11::D3D11_REQ_TEXTURE2D_U_OR_V_DIMENSION, + height: d3d11::D3D11_REQ_TEXTURE2D_U_OR_V_DIMENSION, + depth: 1, + }, + 3 => image::Extent { + width: d3d11::D3D11_REQ_TEXTURE3D_U_V_OR_W_DIMENSION, + height: d3d11::D3D11_REQ_TEXTURE3D_U_V_OR_W_DIMENSION, + depth: d3d11::D3D11_REQ_TEXTURE3D_U_V_OR_W_DIMENSION, + }, + _ => return None, + }, + max_levels: d3d11::D3D11_REQ_MIP_LEVELS as _, + max_layers: match dimensions { + 1 => d3d11::D3D11_REQ_TEXTURE1D_ARRAY_AXIS_DIMENSION as _, + 2 => d3d11::D3D11_REQ_TEXTURE2D_ARRAY_AXIS_DIMENSION as _, + _ => return None, + }, + sample_count_mask: if dimensions == 2 + && !view_caps.contains(image::ViewCapabilities::KIND_CUBE) + && (usage.contains(image::Usage::COLOR_ATTACHMENT) + | usage.contains(image::Usage::DEPTH_STENCIL_ATTACHMENT)) + { + 0x3F //TODO: use D3D12_FEATURE_DATA_FORMAT_SUPPORT + } else { + 0x1 + }, + max_resource_size, + }, + image::Tiling::Linear => image::FormatProperties { + max_extent: match dimensions { + 2 => image::Extent { + width: d3d11::D3D11_REQ_TEXTURE2D_U_OR_V_DIMENSION, + height: d3d11::D3D11_REQ_TEXTURE2D_U_OR_V_DIMENSION, + depth: 1, + }, + _ => return None, + }, + max_levels: 1, + max_layers: 1, + sample_count_mask: 0x1, + max_resource_size, + }, + }) + } + + fn memory_properties(&self) -> adapter::MemoryProperties { + self.memory_properties.clone() + } + + fn features(&self) -> hal::Features { + self.features + } + + fn properties(&self) -> hal::PhysicalDeviceProperties { + self.properties + } +} + +struct Presentation { + swapchain: ComPtr, + view: ComPtr, + format: format::Format, + size: window::Extent2D, + mode: window::PresentMode, + image: Arc, + is_init: bool, +} + +pub struct Surface { + pub(crate) factory: ComPtr, + wnd_handle: HWND, + presentation: Option, +} + +impl fmt::Debug for Surface { + fn fmt(&self, fmt: &mut fmt::Formatter) -> fmt::Result { + fmt.write_str("Surface") + } +} + +unsafe impl Send for Surface {} +unsafe impl Sync for Surface {} + +impl window::Surface for Surface { + fn supports_queue_family(&self, _queue_family: &QueueFamily) -> bool { + true + } + + fn capabilities(&self, _physical_device: &PhysicalDevice) -> window::SurfaceCapabilities { + let current_extent = unsafe { + let mut rect: RECT = mem::zeroed(); + assert_ne!( + 0, + GetClientRect(self.wnd_handle as *mut _, &mut rect as *mut RECT) + ); + Some(window::Extent2D { + width: (rect.right - rect.left) as u32, + height: (rect.bottom - rect.top) as u32, + }) + }; + + // TODO: flip swap effects require dx11.1/windows8 + // NOTE: some swap effects affect msaa capabilities.. + // TODO: _DISCARD swap effects can only have one image? + window::SurfaceCapabilities { + present_modes: window::PresentMode::IMMEDIATE | window::PresentMode::FIFO, + composite_alpha_modes: window::CompositeAlphaMode::OPAQUE, //TODO + image_count: 1..=16, // TODO: + current_extent, + extents: window::Extent2D { + width: 16, + height: 16, + }..=window::Extent2D { + width: 4096, + height: 4096, + }, + max_image_layers: 1, + usage: image::Usage::COLOR_ATTACHMENT, + } + } + + fn supported_formats(&self, _physical_device: &PhysicalDevice) -> Option> { + Some(vec![ + format::Format::Bgra8Srgb, + format::Format::Bgra8Unorm, + format::Format::Rgba8Srgb, + format::Format::Rgba8Unorm, + format::Format::A2b10g10r10Unorm, + format::Format::Rgba16Sfloat, + ]) + } +} + +#[derive(Debug)] +pub struct SwapchainImage { + image: Arc, + view: ImageView, +} +impl Borrow for SwapchainImage { + fn borrow(&self) -> &Image { + &*self.image + } +} +impl Borrow for SwapchainImage { + fn borrow(&self) -> &ImageView { + &self.view + } +} + +impl window::PresentationSurface for Surface { + type SwapchainImage = SwapchainImage; + + unsafe fn configure_swapchain( + &mut self, + device: &device::Device, + config: window::SwapchainConfig, + ) -> Result<(), window::SwapchainError> { + assert!(image::Usage::COLOR_ATTACHMENT.contains(config.image_usage)); + + let swapchain = match self.presentation.take() { + Some(present) => { + if present.format == config.format && present.size == config.extent { + self.presentation = Some(present); + return Ok(()); + } + let non_srgb_format = conv::map_format_nosrgb(config.format).unwrap(); + + // Delete the existing view into the swapchain buffers. + drop(present.view); + + // We must also delete the image data. + // + // This should not panic as all images must be deleted before + let mut present_image = Arc::try_unwrap(present.image).expect( + "Not all acquired images were deleted before the swapchain was reconfigured.", + ); + present_image.internal.release_resources(); + + let result = present.swapchain.ResizeBuffers( + config.image_count, + config.extent.width, + config.extent.height, + non_srgb_format, + 0, + ); + if result != winerror::S_OK { + error!("ResizeBuffers failed with 0x{:x}", result as u32); + return Err(window::SwapchainError::WindowInUse); + } + present.swapchain + } + None => { + let (swapchain, _) = + device.create_swapchain_impl(&config, self.wnd_handle, self.factory.clone())?; + swapchain + } + }; + + // Disable automatic Alt+Enter handling by DXGI. + const DXGI_MWA_NO_WINDOW_CHANGES: u32 = 1; + const DXGI_MWA_NO_ALT_ENTER: u32 = 2; + self.factory.MakeWindowAssociation( + self.wnd_handle, + DXGI_MWA_NO_WINDOW_CHANGES | DXGI_MWA_NO_ALT_ENTER, + ); + + let mut resource: *mut d3d11::ID3D11Resource = ptr::null_mut(); + assert_eq!( + winerror::S_OK, + swapchain.GetBuffer( + 0 as _, + &d3d11::ID3D11Resource::uuidof(), + &mut resource as *mut *mut _ as *mut *mut _, + ) + ); + set_debug_name(&*resource, "Swapchain Image"); + + let kind = image::Kind::D2(config.extent.width, config.extent.height, 1, 1); + let format = conv::map_format(config.format).unwrap(); + let decomposed_format = conv::DecomposedDxgiFormat::from_dxgi_format(format); + + let view_info = ViewInfo { + resource, + kind, + caps: image::ViewCapabilities::empty(), + view_kind: image::ViewKind::D2, + format: decomposed_format.rtv.unwrap(), + levels: 0..1, + layers: 0..1, + }; + let view = device.view_image_as_render_target(&view_info).unwrap(); + set_debug_name(&view, "Swapchain Image View"); + + self.presentation = Some(Presentation { + swapchain, + view, + format: config.format, + size: config.extent, + mode: config.present_mode, + image: Arc::new(Image { + kind, + usage: config.image_usage, + format: config.format, + view_caps: image::ViewCapabilities::empty(), + decomposed_format, + mip_levels: 1, + internal: InternalImage { + raw: resource, + copy_srv: None, //TODO + srv: None, //TODO + unordered_access_views: Vec::new(), + depth_stencil_views: Vec::new(), + render_target_views: Vec::new(), + debug_name: None, + }, + bind: conv::map_image_usage( + config.image_usage, + config.format.surface_desc(), + device.internal.device_feature_level, + ), + requirements: memory::Requirements { + size: 0, + alignment: 1, + type_mask: 0, + }, + }), + is_init: true, + }); + Ok(()) + } + + unsafe fn unconfigure_swapchain(&mut self, _device: &device::Device) { + self.presentation = None; + } + + unsafe fn acquire_image( + &mut self, + _timeout_ns: u64, //TODO: use the timeout + ) -> Result<(SwapchainImage, Option), window::AcquireError> { + let present = self.presentation.as_ref().unwrap(); + let swapchain_image = SwapchainImage { + image: Arc::clone(&present.image), + view: ImageView { + subresource: d3d11::D3D11CalcSubresource(0, 0, 1), + format: present.format, + rtv_handle: Some(present.view.as_raw()), + dsv_handle: None, + srv_handle: None, + uav_handle: None, + rodsv_handle: None, + owned: false, + }, + }; + Ok((swapchain_image, None)) + } +} + +#[derive(Debug, Clone, Copy)] +pub struct QueueFamily; + +impl queue::QueueFamily for QueueFamily { + fn queue_type(&self) -> queue::QueueType { + queue::QueueType::General + } + fn max_queues(&self) -> usize { + 1 + } + fn id(&self) -> queue::QueueFamilyId { + queue::QueueFamilyId(0) + } + fn supports_sparse_binding(&self) -> bool { + false + } +} + +#[derive(Clone)] +pub struct Queue { + context: ComPtr, +} + +impl fmt::Debug for Queue { + fn fmt(&self, fmt: &mut fmt::Formatter) -> fmt::Result { + fmt.write_str("Queue") + } +} + +unsafe impl Send for Queue {} +unsafe impl Sync for Queue {} + +impl queue::Queue for Queue { + unsafe fn submit<'a, Ic, Iw, Is>( + &mut self, + command_buffers: Ic, + _wait_semaphores: Iw, + _signal_semaphores: Is, + fence: Option<&mut Fence>, + ) where + Ic: Iterator, + { + let _scope = debug_scope!(&self.context, "Submit(fence={:?})", fence); + for cmd_buf in command_buffers { + let _scope = debug_scope!( + &self.context, + "CommandBuffer ({}/{})", + cmd_buf.flush_coherent_memory.len(), + cmd_buf.invalidate_coherent_memory.len() + ); + + { + let _scope = debug_scope!(&self.context, "Pre-Exec: Flush"); + for sync in &cmd_buf.flush_coherent_memory { + sync.do_flush(&self.context); + } + } + self.context + .ExecuteCommandList(cmd_buf.as_raw_list().as_raw(), FALSE); + { + let _scope = debug_scope!(&self.context, "Post-Exec: Invalidate"); + for sync in &cmd_buf.invalidate_coherent_memory { + sync.do_invalidate(&self.context); + } + } + } + + if let Some(fence) = fence { + *fence.mutex.lock() = true; + fence.condvar.notify_all(); + } + } + + unsafe fn present( + &mut self, + surface: &mut Surface, + _image: SwapchainImage, + _wait_semaphore: Option<&mut Semaphore>, + ) -> Result, window::PresentError> { + let mut presentation = surface.presentation.as_mut().unwrap(); + let (interval, flags) = match presentation.mode { + window::PresentMode::IMMEDIATE => (0, 0), + //Note: this ends up not presenting anything for some reason + //window::PresentMode::MAILBOX if !presentation.is_init => (1, DXGI_PRESENT_DO_NOT_SEQUENCE), + window::PresentMode::FIFO => (1, 0), + _ => (0, 0), + }; + presentation.is_init = false; + presentation.swapchain.Present(interval, flags); + Ok(None) + } + + fn wait_idle(&mut self) -> Result<(), hal::device::OutOfMemory> { + // unimplemented!() + Ok(()) + } + + fn timestamp_period(&self) -> f32 { + 1.0 + } +} + +#[derive(Debug)] +pub struct AttachmentInfo { + subpass_id: Option, + view: ImageView, + clear_color: Option<(usize, command::ClearColor)>, + clear_depth: Option, + clear_stencil: Option, +} + +#[derive(Debug)] +pub struct RenderPassCache { + pub render_pass: RenderPass, + pub attachments: Vec, + pub target_rect: pso::Rect, + pub num_layers: image::Layer, + pub current_subpass: pass::SubpassId, +} + +impl RenderPassCache { + pub fn start_subpass( + &mut self, + internal: &internal::Internal, + context: &ComPtr, + cache: &mut CommandBufferState, + ) { + let mut clears = Vec::new(); + for at in self.attachments.iter() { + if at.subpass_id == Some(self.current_subpass) { + if let Some((index, value)) = at.clear_color { + clears.push(command::AttachmentClear::Color { index, value }); + } + if at.clear_depth.is_some() || at.clear_stencil.is_some() { + clears.push(command::AttachmentClear::DepthStencil { + depth: at.clear_depth, + stencil: at.clear_stencil, + }); + } + } + } + + cache.dirty_flag.insert( + DirtyStateFlag::GRAPHICS_PIPELINE + | DirtyStateFlag::DEPTH_STENCIL_STATE + | DirtyStateFlag::PIPELINE_PS + | DirtyStateFlag::VIEWPORTS + | DirtyStateFlag::RENDER_TARGETS_AND_UAVS, + ); + internal.clear_attachments( + context, + clears.into_iter(), + Some(pso::ClearRect { + rect: self.target_rect, + layers: 0..1, + }) + .into_iter(), + &self, + ); + + let subpass = &self.render_pass.subpasses[self.current_subpass as usize]; + let color_views = subpass + .color_attachments + .iter() + .map(|&(id, _)| self.attachments[id].view.rtv_handle.unwrap()) + .collect::>(); + let (ds_view, rods_view) = match subpass.depth_stencil_attachment { + Some((id, _)) => { + let attachment = &self.attachments[id].view; + let ds_view = attachment.dsv_handle.unwrap(); + + (Some(ds_view), attachment.rodsv_handle) + } + None => (None, None), + }; + + cache.set_render_targets(&color_views, ds_view, rods_view); + cache.bind(context); + } + + fn resolve_msaa(&mut self, context: &ComPtr) { + let subpass: &SubpassDesc = &self.render_pass.subpasses[self.current_subpass as usize]; + + for (&(color_id, _), &(resolve_id, _)) in subpass + .color_attachments + .iter() + .zip(subpass.resolve_attachments.iter()) + { + if color_id == pass::ATTACHMENT_UNUSED || resolve_id == pass::ATTACHMENT_UNUSED { + continue; + } + + let color_view = &self.attachments[color_id].view; + let resolve_view = &self.attachments[resolve_id].view; + + let mut color_resource: *mut d3d11::ID3D11Resource = ptr::null_mut(); + let mut resolve_resource: *mut d3d11::ID3D11Resource = ptr::null_mut(); + + unsafe { + (&*color_view + .rtv_handle + .expect("Framebuffer must have COLOR_ATTACHMENT usage")) + .GetResource(&mut color_resource as *mut *mut _); + (&*resolve_view + .rtv_handle + .expect("Resolve texture must have COLOR_ATTACHMENT usage")) + .GetResource(&mut resolve_resource as *mut *mut _); + + context.ResolveSubresource( + resolve_resource, + resolve_view.subresource, + color_resource, + color_view.subresource, + conv::map_format(color_view.format).unwrap(), + ); + + (&*color_resource).Release(); + (&*resolve_resource).Release(); + } + } + } + + pub fn next_subpass(&mut self, context: &ComPtr) { + self.resolve_msaa(context); + self.current_subpass += 1; + } +} + +bitflags! { + struct DirtyStateFlag : u32 { + const RENDER_TARGETS_AND_UAVS = (1 << 1); + const VERTEX_BUFFERS = (1 << 2); + const GRAPHICS_PIPELINE = (1 << 3); + const PIPELINE_GS = (1 << 4); + const PIPELINE_HS = (1 << 5); + const PIPELINE_DS = (1 << 6); + const PIPELINE_PS = (1 << 7); + const VIEWPORTS = (1 << 8); + const BLEND_STATE = (1 << 9); + const DEPTH_STENCIL_STATE = (1 << 10); + } +} + +pub struct CommandBufferState { + dirty_flag: DirtyStateFlag, + + render_target_len: u32, + render_targets: [*mut d3d11::ID3D11RenderTargetView; 8], + uav_len: u32, + uavs: [*mut d3d11::ID3D11UnorderedAccessView; d3d11::D3D11_PS_CS_UAV_REGISTER_COUNT as _], + depth_target: Option<*mut d3d11::ID3D11DepthStencilView>, + readonly_depth_target: Option<*mut d3d11::ID3D11DepthStencilView>, + depth_target_read_only: bool, + graphics_pipeline: Option, + + // a bitmask that keeps track of what vertex buffer bindings have been "bound" into + // our vec + bound_bindings: u32, + // a bitmask that hold the required binding slots to be bound for the currently + // bound pipeline + required_bindings: Option, + // the highest binding number in currently bound pipeline + max_bindings: Option, + viewports: Vec, + vertex_buffers: Vec<*mut d3d11::ID3D11Buffer>, + vertex_offsets: Vec, + vertex_strides: Vec, + blend_factor: Option<[f32; 4]>, + // we can only support one face (rather, both faces must have the same value) + stencil_ref: Option, + stencil_read_mask: Option, + stencil_write_mask: Option, + current_blend: Option<*mut d3d11::ID3D11BlendState>, +} + +impl fmt::Debug for CommandBufferState { + fn fmt(&self, fmt: &mut fmt::Formatter) -> fmt::Result { + fmt.write_str("CommandBufferState") + } +} + +impl CommandBufferState { + fn new() -> Self { + CommandBufferState { + dirty_flag: DirtyStateFlag::empty(), + render_target_len: 0, + render_targets: [ptr::null_mut(); 8], + uav_len: 0, + uavs: [ptr::null_mut(); 8], + depth_target: None, + readonly_depth_target: None, + depth_target_read_only: false, + graphics_pipeline: None, + bound_bindings: 0, + required_bindings: None, + max_bindings: None, + viewports: Vec::new(), + vertex_buffers: Vec::new(), + vertex_offsets: Vec::new(), + vertex_strides: Vec::new(), + blend_factor: None, + stencil_ref: None, + stencil_read_mask: None, + stencil_write_mask: None, + current_blend: None, + } + } + + fn clear(&mut self) { + self.render_target_len = 0; + self.uav_len = 0; + self.depth_target = None; + self.readonly_depth_target = None; + self.depth_target_read_only = false; + self.graphics_pipeline = None; + self.bound_bindings = 0; + self.required_bindings = None; + self.max_bindings = None; + self.viewports.clear(); + self.vertex_buffers.clear(); + self.vertex_offsets.clear(); + self.vertex_strides.clear(); + self.blend_factor = None; + self.stencil_ref = None; + self.stencil_read_mask = None; + self.stencil_write_mask = None; + self.current_blend = None; + } + + pub fn set_vertex_buffer( + &mut self, + index: usize, + offset: u32, + buffer: *mut d3d11::ID3D11Buffer, + ) { + self.bound_bindings |= 1 << index as u32; + + if index >= self.vertex_buffers.len() { + self.vertex_buffers.push(buffer); + self.vertex_offsets.push(offset); + } else { + self.vertex_buffers[index] = buffer; + self.vertex_offsets[index] = offset; + } + + self.dirty_flag.insert(DirtyStateFlag::VERTEX_BUFFERS); + } + + pub fn bind_vertex_buffers(&mut self, context: &ComPtr) { + if !self.dirty_flag.contains(DirtyStateFlag::VERTEX_BUFFERS) { + return; + } + + if let Some(binding_count) = self.max_bindings { + if self.vertex_buffers.len() >= binding_count as usize + && self.vertex_strides.len() >= binding_count as usize + { + unsafe { + context.IASetVertexBuffers( + 0, + binding_count, + self.vertex_buffers.as_ptr(), + self.vertex_strides.as_ptr(), + self.vertex_offsets.as_ptr(), + ); + } + + self.dirty_flag.remove(DirtyStateFlag::VERTEX_BUFFERS); + } + } + } + + pub fn set_viewports(&mut self, viewports: &[d3d11::D3D11_VIEWPORT]) { + self.viewports.clear(); + self.viewports.extend(viewports); + + self.dirty_flag.insert(DirtyStateFlag::VIEWPORTS); + } + + pub fn bind_viewports(&mut self, context: &ComPtr) { + if !self.dirty_flag.contains(DirtyStateFlag::VIEWPORTS) { + return; + } + + if let Some(ref pipeline) = self.graphics_pipeline { + if let Some(ref viewport) = pipeline.baked_states.viewport { + unsafe { + context.RSSetViewports(1, [conv::map_viewport(viewport)].as_ptr()); + } + } else { + unsafe { + context.RSSetViewports(self.viewports.len() as u32, self.viewports.as_ptr()); + } + } + } else { + unsafe { + context.RSSetViewports(self.viewports.len() as u32, self.viewports.as_ptr()); + } + } + + self.dirty_flag.remove(DirtyStateFlag::VIEWPORTS); + } + + pub fn set_render_targets( + &mut self, + render_targets: &[*mut d3d11::ID3D11RenderTargetView], + depth_target: Option<*mut d3d11::ID3D11DepthStencilView>, + readonly_depth_target: Option<*mut d3d11::ID3D11DepthStencilView>, + ) { + for (idx, &rt) in render_targets.iter().enumerate() { + self.render_targets[idx] = rt; + } + + self.render_target_len = render_targets.len() as u32; + self.depth_target = depth_target; + self.readonly_depth_target = readonly_depth_target; + + self.dirty_flag + .insert(DirtyStateFlag::RENDER_TARGETS_AND_UAVS); + } + + pub fn bind_render_targets(&mut self, context: &ComPtr) { + if !self + .dirty_flag + .contains(DirtyStateFlag::RENDER_TARGETS_AND_UAVS) + { + return; + } + + let depth_target = if self.depth_target_read_only { + self.readonly_depth_target + } else { + self.depth_target + } + .unwrap_or(ptr::null_mut()); + + let uav_start_index = d3d11::D3D11_PS_CS_UAV_REGISTER_COUNT - self.uav_len; + + unsafe { + if self.uav_len > 0 { + context.OMSetRenderTargetsAndUnorderedAccessViews( + self.render_target_len, + self.render_targets.as_ptr(), + depth_target, + uav_start_index, + self.uav_len, + &self.uavs[uav_start_index as usize] as *const *mut _, + ptr::null(), + ) + } else { + context.OMSetRenderTargets( + self.render_target_len, + self.render_targets.as_ptr(), + depth_target, + ) + }; + } + + self.dirty_flag + .remove(DirtyStateFlag::RENDER_TARGETS_AND_UAVS); + } + + pub fn set_blend_factor(&mut self, factor: [f32; 4]) { + self.blend_factor = Some(factor); + + self.dirty_flag.insert(DirtyStateFlag::BLEND_STATE); + } + + pub fn bind_blend_state(&mut self, context: &ComPtr) { + if let Some(blend) = self.current_blend { + let blend_color = if let Some(ref pipeline) = self.graphics_pipeline { + pipeline + .baked_states + .blend_constants + .or(self.blend_factor) + .unwrap_or([0f32; 4]) + } else { + self.blend_factor.unwrap_or([0f32; 4]) + }; + + // TODO: MSAA + unsafe { + context.OMSetBlendState(blend, &blend_color, !0); + } + + self.dirty_flag.remove(DirtyStateFlag::BLEND_STATE); + } + } + + pub fn set_stencil_ref(&mut self, value: pso::StencilValue) { + self.stencil_ref = Some(value); + self.dirty_flag.insert(DirtyStateFlag::DEPTH_STENCIL_STATE); + } + + pub fn bind_depth_stencil_state(&mut self, context: &ComPtr) { + if !self + .dirty_flag + .contains(DirtyStateFlag::DEPTH_STENCIL_STATE) + { + return; + } + + let pipeline = match self.graphics_pipeline { + Some(ref pipeline) => pipeline, + None => return, + }; + + if let Some(ref state) = pipeline.depth_stencil_state { + let stencil_ref = state.stencil_ref.static_or(self.stencil_ref.unwrap_or(0)); + + unsafe { + context.OMSetDepthStencilState(state.raw.as_raw(), stencil_ref); + } + } + + self.dirty_flag.remove(DirtyStateFlag::DEPTH_STENCIL_STATE) + } + + pub fn set_graphics_pipeline(&mut self, pipeline: GraphicsPipeline) { + let prev = self.graphics_pipeline.take(); + + let mut prev_has_ps = false; + let mut prev_has_gs = false; + let mut prev_has_ds = false; + let mut prev_has_hs = false; + if let Some(p) = prev { + prev_has_ps = p.ps.is_some(); + prev_has_gs = p.gs.is_some(); + prev_has_ds = p.ds.is_some(); + prev_has_hs = p.hs.is_some(); + } + + if prev_has_ps || pipeline.ps.is_some() { + self.dirty_flag.insert(DirtyStateFlag::PIPELINE_PS); + } + if prev_has_gs || pipeline.gs.is_some() { + self.dirty_flag.insert(DirtyStateFlag::PIPELINE_GS); + } + if prev_has_ds || pipeline.ds.is_some() { + self.dirty_flag.insert(DirtyStateFlag::PIPELINE_DS); + } + if prev_has_hs || pipeline.hs.is_some() { + self.dirty_flag.insert(DirtyStateFlag::PIPELINE_HS); + } + + // If we don't have depth stencil state, we use the old value, so we don't bother changing anything. + let depth_target_read_only = pipeline + .depth_stencil_state + .as_ref() + .map_or(self.depth_target_read_only, |ds| ds.read_only); + + if self.depth_target_read_only != depth_target_read_only { + self.depth_target_read_only = depth_target_read_only; + self.dirty_flag + .insert(DirtyStateFlag::RENDER_TARGETS_AND_UAVS); + } + + self.dirty_flag + .insert(DirtyStateFlag::GRAPHICS_PIPELINE | DirtyStateFlag::DEPTH_STENCIL_STATE); + + self.graphics_pipeline = Some(pipeline); + } + + pub fn bind_graphics_pipeline(&mut self, context: &ComPtr) { + if !self.dirty_flag.contains(DirtyStateFlag::GRAPHICS_PIPELINE) { + return; + } + + if let Some(ref pipeline) = self.graphics_pipeline { + self.vertex_strides.clear(); + self.vertex_strides.extend(&pipeline.strides); + + self.required_bindings = Some(pipeline.required_bindings); + self.max_bindings = Some(pipeline.max_vertex_bindings); + }; + + self.bind_vertex_buffers(context); + + if let Some(ref pipeline) = self.graphics_pipeline { + unsafe { + context.IASetPrimitiveTopology(pipeline.topology); + context.IASetInputLayout(pipeline.input_layout.as_raw()); + + context.VSSetShader(pipeline.vs.as_raw(), ptr::null_mut(), 0); + + if self.dirty_flag.contains(DirtyStateFlag::PIPELINE_PS) { + let ps = pipeline + .ps + .as_ref() + .map_or(ptr::null_mut(), |ps| ps.as_raw()); + context.PSSetShader(ps, ptr::null_mut(), 0); + + self.dirty_flag.remove(DirtyStateFlag::PIPELINE_PS) + } + + if self.dirty_flag.contains(DirtyStateFlag::PIPELINE_GS) { + let gs = pipeline + .gs + .as_ref() + .map_or(ptr::null_mut(), |gs| gs.as_raw()); + context.GSSetShader(gs, ptr::null_mut(), 0); + + self.dirty_flag.remove(DirtyStateFlag::PIPELINE_GS) + } + + if self.dirty_flag.contains(DirtyStateFlag::PIPELINE_HS) { + let hs = pipeline + .hs + .as_ref() + .map_or(ptr::null_mut(), |hs| hs.as_raw()); + context.HSSetShader(hs, ptr::null_mut(), 0); + + self.dirty_flag.remove(DirtyStateFlag::PIPELINE_HS) + } + + if self.dirty_flag.contains(DirtyStateFlag::PIPELINE_DS) { + let ds = pipeline + .ds + .as_ref() + .map_or(ptr::null_mut(), |ds| ds.as_raw()); + context.DSSetShader(ds, ptr::null_mut(), 0); + + self.dirty_flag.remove(DirtyStateFlag::PIPELINE_DS) + } + + context.RSSetState(pipeline.rasterizer_state.as_raw()); + if let Some(ref viewport) = pipeline.baked_states.viewport { + context.RSSetViewports(1, [conv::map_viewport(viewport)].as_ptr()); + } + if let Some(ref scissor) = pipeline.baked_states.scissor { + context.RSSetScissorRects(1, [conv::map_rect(&scissor)].as_ptr()); + } + + self.current_blend = Some(pipeline.blend_state.as_raw()); + } + }; + + self.bind_blend_state(context); + self.bind_depth_stencil_state(context); + + self.dirty_flag.remove(DirtyStateFlag::GRAPHICS_PIPELINE); + } + + pub fn bind(&mut self, context: &ComPtr) { + self.bind_render_targets(context); + self.bind_graphics_pipeline(context); + self.bind_vertex_buffers(context); + self.bind_viewports(context); + } +} + +type PerConstantBufferVec = + ArrayVec<[T; d3d11::D3D11_COMMONSHADER_CONSTANT_BUFFER_API_SLOT_COUNT as _]>; + +fn generate_graphics_dynamic_constant_buffer_offsets<'a>( + bindings: impl IntoIterator, + offset_iter: &mut impl Iterator, + context1_some: bool, +) -> (PerConstantBufferVec, PerConstantBufferVec) { + let mut vs_offsets = ArrayVec::new(); + let mut fs_offsets = ArrayVec::new(); + + let mut exists_dynamic_constant_buffer = false; + + for binding in bindings { + match binding.ty { + pso::DescriptorType::Buffer { + format: + pso::BufferDescriptorFormat::Structured { + dynamic_offset: true, + }, + ty: pso::BufferDescriptorType::Uniform, + } => { + let offset = offset_iter.next().unwrap(); + + if binding.stage_flags.contains(pso::ShaderStageFlags::VERTEX) { + vs_offsets.push(offset / 16) + }; + + if binding + .stage_flags + .contains(pso::ShaderStageFlags::FRAGMENT) + { + fs_offsets.push(offset / 16) + }; + exists_dynamic_constant_buffer = true; + } + pso::DescriptorType::Buffer { + format: + pso::BufferDescriptorFormat::Structured { + dynamic_offset: false, + }, + ty: pso::BufferDescriptorType::Uniform, + } => { + if binding.stage_flags.contains(pso::ShaderStageFlags::VERTEX) { + vs_offsets.push(0) + }; + + if binding + .stage_flags + .contains(pso::ShaderStageFlags::FRAGMENT) + { + fs_offsets.push(0) + }; + } + pso::DescriptorType::Buffer { + ty: pso::BufferDescriptorType::Storage { .. }, + format: + pso::BufferDescriptorFormat::Structured { + dynamic_offset: true, + }, + } => { + // TODO: Storage buffer offsets require new buffer views with correct sizes. + // Might also require D3D11_BUFFEREX_SRV to act like RBA is happening. + let _ = offset_iter.next().unwrap(); + warn!("Dynamic offsets into storage buffers are currently unsupported on DX11."); + } + _ => {} + } + } + + if exists_dynamic_constant_buffer && !context1_some { + warn!("D3D11.1 runtime required for dynamic offsets into constant buffers. Offsets will be ignored."); + } + + (vs_offsets, fs_offsets) +} + +fn generate_compute_dynamic_constant_buffer_offsets<'a>( + bindings: impl IntoIterator, + offset_iter: &mut impl Iterator, + context1_some: bool, +) -> PerConstantBufferVec { + let mut cs_offsets = ArrayVec::new(); + + let mut exists_dynamic_constant_buffer = false; + + for binding in bindings { + match binding.ty { + pso::DescriptorType::Buffer { + format: + pso::BufferDescriptorFormat::Structured { + dynamic_offset: true, + }, + ty: pso::BufferDescriptorType::Uniform, + } => { + let offset = offset_iter.next().unwrap(); + + if binding.stage_flags.contains(pso::ShaderStageFlags::COMPUTE) { + cs_offsets.push(offset / 16) + }; + + exists_dynamic_constant_buffer = true; + } + pso::DescriptorType::Buffer { + format: + pso::BufferDescriptorFormat::Structured { + dynamic_offset: false, + }, + ty: pso::BufferDescriptorType::Uniform, + } => { + if binding.stage_flags.contains(pso::ShaderStageFlags::COMPUTE) { + cs_offsets.push(0) + }; + } + pso::DescriptorType::Buffer { + ty: pso::BufferDescriptorType::Storage { .. }, + format: + pso::BufferDescriptorFormat::Structured { + dynamic_offset: true, + }, + } => { + // TODO: Storage buffer offsets require new buffer views with correct sizes. + // Might also require D3D11_BUFFEREX_SRV to act like RBA is happening. + let _ = offset_iter.next().unwrap(); + warn!("Dynamic offsets into storage buffers are currently unsupported on DX11."); + } + _ => {} + } + } + + if exists_dynamic_constant_buffer && !context1_some { + warn!("D3D11.1 runtime required for dynamic offsets into constant buffers. Offsets will be ignored."); + } + + cs_offsets +} + +pub struct CommandBuffer { + internal: Arc, + context: ComPtr, + context1: Option>, + list: RefCell>>, + + // since coherent memory needs to be synchronized at submission, we need to gather up all + // coherent resources that are used in the command buffer and flush/invalidate them accordingly + // before executing. + flush_coherent_memory: Vec, + invalidate_coherent_memory: Vec, + + // holds information about the active render pass + render_pass_cache: Option, + + // Have to update entire push constant buffer at once, keep whole buffer data local. + push_constant_data: [u32; MAX_PUSH_CONSTANT_SIZE / 4], + push_constant_buffer: ComPtr, + + cache: CommandBufferState, + + one_time_submit: bool, + + debug_name: Option, + debug_scopes: Vec>, +} + +impl fmt::Debug for CommandBuffer { + fn fmt(&self, fmt: &mut fmt::Formatter) -> fmt::Result { + fmt.write_str("CommandBuffer") + } +} + +unsafe impl Send for CommandBuffer {} +unsafe impl Sync for CommandBuffer {} + +impl CommandBuffer { + fn create_deferred( + device: ComPtr, + device1: Option<&d3d11_1::ID3D11Device1>, + internal: Arc, + ) -> Self { + let (context, context1) = if let Some(device1) = device1 { + let mut context1: *mut d3d11_1::ID3D11DeviceContext1 = ptr::null_mut(); + let hr = unsafe { device1.CreateDeferredContext1(0, &mut context1 as *mut *mut _) }; + assert_eq!(hr, winerror::S_OK); + + let context1 = unsafe { ComPtr::from_raw(context1) }; + let context = context1.cast::().unwrap(); + + (context, Some(context1)) + } else { + let mut context: *mut d3d11::ID3D11DeviceContext = ptr::null_mut(); + let hr = unsafe { device.CreateDeferredContext(0, &mut context as *mut *mut _) }; + assert_eq!(hr, winerror::S_OK); + + let context = unsafe { ComPtr::from_raw(context) }; + + (context, None) + }; + + let push_constant_buffer = { + let desc = d3d11::D3D11_BUFFER_DESC { + ByteWidth: MAX_PUSH_CONSTANT_SIZE as _, + Usage: d3d11::D3D11_USAGE_DEFAULT, + BindFlags: d3d11::D3D11_BIND_CONSTANT_BUFFER, + CPUAccessFlags: 0, + MiscFlags: 0, + StructureByteStride: 0, + }; + + let mut buffer: *mut d3d11::ID3D11Buffer = ptr::null_mut(); + let hr = unsafe { + device.CreateBuffer(&desc as *const _, ptr::null_mut(), &mut buffer as *mut _) + }; + + assert_eq!(hr, winerror::S_OK); + + unsafe { ComPtr::from_raw(buffer) } + }; + + let push_constant_data = [0_u32; 64]; + + CommandBuffer { + internal, + context, + context1, + list: RefCell::new(None), + flush_coherent_memory: Vec::new(), + invalidate_coherent_memory: Vec::new(), + render_pass_cache: None, + push_constant_data, + push_constant_buffer, + cache: CommandBufferState::new(), + one_time_submit: false, + debug_name: None, + debug_scopes: Vec::new(), + } + } + + fn as_raw_list(&self) -> ComPtr { + if self.one_time_submit { + self.list.replace(None).unwrap() + } else { + self.list.borrow().clone().unwrap() + } + } + + fn defer_coherent_flush(&mut self, buffer: &Buffer) { + if !self + .flush_coherent_memory + .iter() + .any(|m| m.buffer == buffer.internal.raw) + { + self.flush_coherent_memory.push(MemoryFlush { + host_memory: buffer.memory_ptr, + sync_range: SyncRange::Whole, + buffer: buffer.internal.raw, + }); + } + } + + fn defer_coherent_invalidate(&mut self, buffer: &Buffer) { + if !self + .invalidate_coherent_memory + .iter() + .any(|m| m.buffer == buffer.internal.raw) + { + self.invalidate_coherent_memory.push(MemoryInvalidate { + working_buffer: Some(self.internal.working_buffer.clone()), + working_buffer_size: self.internal.working_buffer_size, + host_memory: buffer.memory_ptr, + host_sync_range: buffer.bound_range.clone(), + buffer_sync_range: buffer.bound_range.clone(), + buffer: buffer.internal.raw, + }); + } + } + + fn reset(&mut self) { + self.flush_coherent_memory.clear(); + self.invalidate_coherent_memory.clear(); + self.render_pass_cache = None; + self.cache.clear(); + self.debug_scopes.clear(); + } +} + +impl command::CommandBuffer for CommandBuffer { + unsafe fn begin( + &mut self, + flags: command::CommandBufferFlags, + _info: command::CommandBufferInheritanceInfo, + ) { + self.one_time_submit = flags.contains(command::CommandBufferFlags::ONE_TIME_SUBMIT); + self.reset(); + + // Push constants are at the top register to allow them to be bound only once. + let raw_push_constant_buffer = self.push_constant_buffer.as_raw(); + self.context.VSSetConstantBuffers( + d3d11::D3D11_COMMONSHADER_CONSTANT_BUFFER_API_SLOT_COUNT - 1, + 1, + &raw_push_constant_buffer as *const _, + ); + self.context.PSSetConstantBuffers( + d3d11::D3D11_COMMONSHADER_CONSTANT_BUFFER_API_SLOT_COUNT - 1, + 1, + &raw_push_constant_buffer as *const _, + ); + self.context.CSSetConstantBuffers( + d3d11::D3D11_COMMONSHADER_CONSTANT_BUFFER_API_SLOT_COUNT - 1, + 1, + &raw_push_constant_buffer as *const _, + ); + } + + unsafe fn finish(&mut self) { + let mut list: *mut d3d11::ID3D11CommandList = ptr::null_mut(); + let hr = self + .context + .FinishCommandList(FALSE, &mut list as *mut *mut _); + assert_eq!(hr, winerror::S_OK); + + if let Some(ref name) = self.debug_name { + set_debug_name(&*list, name); + } + + self.list.replace(Some(ComPtr::from_raw(list))); + } + + unsafe fn reset(&mut self, _release_resources: bool) { + self.reset(); + } + + unsafe fn begin_render_pass<'a, T>( + &mut self, + render_pass: &RenderPass, + framebuffer: &Framebuffer, + target_rect: pso::Rect, + attachment_infos: T, + _first_subpass: command::SubpassContents, + ) where + T: Iterator>, + { + use pass::AttachmentLoadOp as Alo; + + let mut attachments = Vec::new(); + + for (idx, (info, attachment)) in attachment_infos + .zip(render_pass.attachments.iter()) + .enumerate() + { + let format = attachment.format.unwrap(); + + let mut at = AttachmentInfo { + subpass_id: render_pass + .subpasses + .iter() + .position(|sp| sp.is_using(idx)) + .map(|i| i as pass::SubpassId), + view: info.image_view.clone(), + clear_color: None, + clear_depth: None, + clear_stencil: None, + }; + + if attachment.ops.load == Alo::Clear { + if format.is_depth() { + at.clear_depth = Some(info.clear_value.depth_stencil.depth); + } else { + at.clear_color = Some((idx, info.clear_value.color)); + } + } + if attachment.stencil_ops.load == Alo::Clear { + at.clear_stencil = Some(info.clear_value.depth_stencil.stencil); + } + + attachments.push(at); + } + + self.render_pass_cache = Some(RenderPassCache { + render_pass: render_pass.clone(), + attachments, + target_rect, + num_layers: framebuffer.layers, + current_subpass: 0, + }); + + if let Some(ref mut current_render_pass) = self.render_pass_cache { + current_render_pass.start_subpass(&self.internal, &self.context, &mut self.cache); + } + } + + unsafe fn next_subpass(&mut self, _contents: command::SubpassContents) { + if let Some(ref mut current_render_pass) = self.render_pass_cache { + current_render_pass.next_subpass(&self.context); + current_render_pass.start_subpass(&self.internal, &self.context, &mut self.cache); + } + } + + unsafe fn end_render_pass(&mut self) { + if let Some(ref mut current_render_pass) = self.render_pass_cache { + current_render_pass.resolve_msaa(&self.context); + } + + self.context + .OMSetRenderTargets(8, [ptr::null_mut(); 8].as_ptr(), ptr::null_mut()); + + self.render_pass_cache = None; + } + + unsafe fn pipeline_barrier<'a, T>( + &mut self, + _stages: Range, + _dependencies: memory::Dependencies, + _barriers: T, + ) where + T: Iterator>, + { + // TODO: should we track and assert on resource states? + // unimplemented!() + } + + unsafe fn clear_image( + &mut self, + image: &Image, + _: image::Layout, + value: command::ClearValue, + subresource_ranges: T, + ) where + T: Iterator, + { + for range in subresource_ranges { + let num_levels = range.resolve_level_count(image.mip_levels); + let num_layers = range.resolve_layer_count(image.kind.num_layers()); + + let mut depth_stencil_flags = 0; + if range.aspects.contains(format::Aspects::DEPTH) { + depth_stencil_flags |= d3d11::D3D11_CLEAR_DEPTH; + } + if range.aspects.contains(format::Aspects::STENCIL) { + depth_stencil_flags |= d3d11::D3D11_CLEAR_STENCIL; + } + + // TODO: clear Int/Uint depending on format + for rel_layer in 0..num_layers { + for rel_level in 0..num_levels { + let level = range.level_start + rel_level; + let layer = range.layer_start + rel_layer; + if range.aspects.contains(format::Aspects::COLOR) { + self.context.ClearRenderTargetView( + image.get_rtv(level, layer).unwrap().as_raw(), + &value.color.float32, + ); + } else { + self.context.ClearDepthStencilView( + image.get_dsv(level, layer).unwrap().as_raw(), + depth_stencil_flags, + value.depth_stencil.depth, + value.depth_stencil.stencil as _, + ); + } + } + } + } + } + + unsafe fn clear_attachments(&mut self, clears: T, rects: U) + where + T: Iterator, + U: Iterator, + { + if let Some(ref pass) = self.render_pass_cache { + self.cache.dirty_flag.insert( + DirtyStateFlag::GRAPHICS_PIPELINE + | DirtyStateFlag::DEPTH_STENCIL_STATE + | DirtyStateFlag::PIPELINE_PS + | DirtyStateFlag::VIEWPORTS + | DirtyStateFlag::RENDER_TARGETS_AND_UAVS, + ); + self.internal + .clear_attachments(&self.context, clears, rects, pass); + self.cache.bind(&self.context); + } else { + panic!("`clear_attachments` can only be called inside a renderpass") + } + } + + unsafe fn resolve_image( + &mut self, + _src: &Image, + _src_layout: image::Layout, + _dst: &Image, + _dst_layout: image::Layout, + _regions: T, + ) where + T: Iterator, + { + unimplemented!() + } + + unsafe fn blit_image( + &mut self, + src: &Image, + _src_layout: image::Layout, + dst: &Image, + _dst_layout: image::Layout, + filter: image::Filter, + regions: T, + ) where + T: Iterator, + { + self.cache + .dirty_flag + .insert(DirtyStateFlag::GRAPHICS_PIPELINE | DirtyStateFlag::PIPELINE_PS); + + self.internal + .blit_2d_image(&self.context, src, dst, filter, regions); + + self.cache.bind(&self.context); + } + + unsafe fn bind_index_buffer(&mut self, buffer: &Buffer, sub: buffer::SubRange, ty: IndexType) { + self.context.IASetIndexBuffer( + buffer.internal.raw, + conv::map_index_type(ty), + sub.offset as u32, + ); + } + + unsafe fn bind_vertex_buffers<'a, T>(&mut self, first_binding: pso::BufferIndex, buffers: T) + where + T: Iterator, + { + for (i, (buf, sub)) in buffers.enumerate() { + let idx = i + first_binding as usize; + + if buf.is_coherent { + self.defer_coherent_flush(buf); + } + + self.cache + .set_vertex_buffer(idx, sub.offset as u32, buf.internal.raw); + } + + self.cache.bind_vertex_buffers(&self.context); + } + + unsafe fn set_viewports(&mut self, _first_viewport: u32, viewports: T) + where + T: Iterator, + { + let viewports = viewports + .map(|ref vp| conv::map_viewport(vp)) + .collect::>(); + + // TODO: DX only lets us set all VPs at once, so cache in slice? + self.cache.set_viewports(&viewports); + self.cache.bind_viewports(&self.context); + } + + unsafe fn set_scissors(&mut self, _first_scissor: u32, scissors: T) + where + T: Iterator, + { + let scissors = scissors.map(|ref r| conv::map_rect(r)).collect::>(); + + // TODO: same as for viewports + self.context + .RSSetScissorRects(scissors.len() as _, scissors.as_ptr()); + } + + unsafe fn set_blend_constants(&mut self, color: pso::ColorValue) { + self.cache.set_blend_factor(color); + self.cache.bind_blend_state(&self.context); + } + + unsafe fn set_stencil_reference(&mut self, _faces: pso::Face, value: pso::StencilValue) { + self.cache.set_stencil_ref(value); + self.cache.bind_depth_stencil_state(&self.context); + } + + unsafe fn set_stencil_read_mask(&mut self, _faces: pso::Face, value: pso::StencilValue) { + self.cache.stencil_read_mask = Some(value); + } + + unsafe fn set_stencil_write_mask(&mut self, _faces: pso::Face, value: pso::StencilValue) { + self.cache.stencil_write_mask = Some(value); + } + + unsafe fn set_depth_bounds(&mut self, _bounds: Range) { + unimplemented!() + } + + unsafe fn set_line_width(&mut self, width: f32) { + validate_line_width(width); + } + + unsafe fn set_depth_bias(&mut self, _depth_bias: pso::DepthBias) { + // TODO: + // unimplemented!() + } + + unsafe fn bind_graphics_pipeline(&mut self, pipeline: &GraphicsPipeline) { + self.cache.set_graphics_pipeline(pipeline.clone()); + self.cache.bind(&self.context); + } + + unsafe fn bind_graphics_descriptor_sets<'a, I, J>( + &mut self, + layout: &PipelineLayout, + first_set: usize, + sets: I, + offsets: J, + ) where + I: Iterator, + J: Iterator, + { + let _scope = debug_scope!(&self.context, "BindGraphicsDescriptorSets"); + + // TODO: find a better solution to invalidating old bindings.. + if self.internal.downlevel.compute_shaders { + let cs_uavs = if self.internal.device_feature_level <= d3dcommon::D3D_FEATURE_LEVEL_11_0 + { + 1 + } else { + d3d11::D3D11_PS_CS_UAV_REGISTER_COUNT + }; + let nulls = [ptr::null_mut(); d3d11::D3D11_PS_CS_UAV_REGISTER_COUNT as usize]; + self.context + .CSSetUnorderedAccessViews(0, cs_uavs, nulls.as_ptr(), ptr::null_mut()); + } + + let mut offset_iter = offsets; + + for (set, info) in sets.zip(&layout.sets[first_set..]) { + { + let coherent_buffers = set.coherent_buffers.lock(); + for sync in coherent_buffers.flush_coherent_buffers.borrow().iter() { + // TODO: merge sync range if a flush already exists + if !self + .flush_coherent_memory + .iter() + .any(|m| m.buffer == sync.device_buffer) + { + self.flush_coherent_memory.push(MemoryFlush { + host_memory: sync.host_ptr, + sync_range: sync.range.clone(), + buffer: sync.device_buffer, + }); + } + } + + for sync in coherent_buffers.invalidate_coherent_buffers.borrow().iter() { + if !self + .invalidate_coherent_memory + .iter() + .any(|m| m.buffer == sync.device_buffer) + { + self.invalidate_coherent_memory.push(MemoryInvalidate { + working_buffer: Some(self.internal.working_buffer.clone()), + working_buffer_size: self.internal.working_buffer_size, + host_memory: sync.host_ptr, + host_sync_range: sync.range.clone(), + buffer_sync_range: sync.range.clone(), + buffer: sync.device_buffer, + }); + } + } + } + + let (vs_offsets, fs_offsets) = generate_graphics_dynamic_constant_buffer_offsets( + &*set.layout.bindings, + &mut offset_iter, + self.context1.is_some(), + ); + + if let Some(rd) = info.registers.vs.c.as_some() { + let start_slot = rd.res_index as u32; + let num_buffers = rd.count as u32; + let constant_buffers = set.handles.offset(rd.pool_offset as isize); + if let Some(ref context1) = self.context1 { + // This call with offsets won't work right with command list emulation + // unless we reset the first and last constant buffers to null. + if self.internal.command_list_emulation { + let null_cbuf = [ptr::null_mut::()]; + context1.VSSetConstantBuffers(start_slot, 1, &null_cbuf as *const _); + if num_buffers > 1 { + context1.VSSetConstantBuffers( + start_slot + num_buffers - 1, + 1, + &null_cbuf as *const _, + ); + } + } + + // TODO: This should be the actual buffer length for RBA purposes, + // but that information isn't easily accessible here. + context1.VSSetConstantBuffers1( + start_slot, + num_buffers, + constant_buffers as *const *mut _, + vs_offsets.as_ptr(), + self.internal.constant_buffer_count_buffer.as_ptr(), + ); + } else { + self.context.VSSetConstantBuffers( + start_slot, + num_buffers, + constant_buffers as *const *mut _, + ); + } + } + if let Some(rd) = info.registers.vs.t.as_some() { + self.context.VSSetShaderResources( + rd.res_index as u32, + rd.count as u32, + set.handles.offset(rd.pool_offset as isize) as *const *mut _, + ); + } + if let Some(rd) = info.registers.vs.s.as_some() { + self.context.VSSetSamplers( + rd.res_index as u32, + rd.count as u32, + set.handles.offset(rd.pool_offset as isize) as *const *mut _, + ); + } + + if let Some(rd) = info.registers.ps.c.as_some() { + let start_slot = rd.res_index as u32; + let num_buffers = rd.count as u32; + let constant_buffers = set.handles.offset(rd.pool_offset as isize); + if let Some(ref context1) = self.context1 { + // This call with offsets won't work right with command list emulation + // unless we reset the first and last constant buffers to null. + if self.internal.command_list_emulation { + let null_cbuf = [ptr::null_mut::()]; + context1.PSSetConstantBuffers(start_slot, 1, &null_cbuf as *const _); + if num_buffers > 1 { + context1.PSSetConstantBuffers( + start_slot + num_buffers - 1, + 1, + &null_cbuf as *const _, + ); + } + } + + context1.PSSetConstantBuffers1( + start_slot, + num_buffers, + constant_buffers as *const *mut _, + fs_offsets.as_ptr(), + self.internal.constant_buffer_count_buffer.as_ptr(), + ); + } else { + self.context.PSSetConstantBuffers( + start_slot, + num_buffers, + constant_buffers as *const *mut _, + ); + } + } + if let Some(rd) = info.registers.ps.t.as_some() { + self.context.PSSetShaderResources( + rd.res_index as u32, + rd.count as u32, + set.handles.offset(rd.pool_offset as isize) as *const *mut _, + ); + } + if let Some(rd) = info.registers.ps.s.as_some() { + self.context.PSSetSamplers( + rd.res_index as u32, + rd.count as u32, + set.handles.offset(rd.pool_offset as isize) as *const *mut _, + ); + } + + // UAVs going to the graphics pipeline are always treated as pixel shader bindings. + if let Some(rd) = info.registers.ps.u.as_some() { + // We bind UAVs in inverse order from the top to prevent invalidation + // when the render target count changes. + for idx in (0..(rd.count)).rev() { + let ptr = (*set.handles.offset(rd.pool_offset as isize + idx as isize)).0; + let uav_register = d3d11::D3D11_PS_CS_UAV_REGISTER_COUNT + - 1 + - rd.res_index as u32 + - idx as u32; + self.cache.uavs[uav_register as usize] = ptr as *mut _; + } + self.cache.uav_len = (rd.res_index + rd.count) as u32; + self.cache + .dirty_flag + .insert(DirtyStateFlag::RENDER_TARGETS_AND_UAVS); + } + } + + self.cache.bind_render_targets(&self.context); + } + + unsafe fn bind_compute_pipeline(&mut self, pipeline: &ComputePipeline) { + self.context + .CSSetShader(pipeline.cs.as_raw(), ptr::null_mut(), 0); + } + + unsafe fn bind_compute_descriptor_sets<'a, I, J>( + &mut self, + layout: &PipelineLayout, + first_set: usize, + sets: I, + offsets: J, + ) where + I: Iterator, + J: Iterator, + { + let _scope = debug_scope!(&self.context, "BindComputeDescriptorSets"); + + let nulls = [ptr::null_mut(); d3d11::D3D11_PS_CS_UAV_REGISTER_COUNT as usize]; + self.context.CSSetUnorderedAccessViews( + 0, + d3d11::D3D11_PS_CS_UAV_REGISTER_COUNT, + nulls.as_ptr(), + ptr::null_mut(), + ); + + let mut offset_iter = offsets; + + for (set, info) in sets.zip(&layout.sets[first_set..]) { + { + let coherent_buffers = set.coherent_buffers.lock(); + for sync in coherent_buffers.flush_coherent_buffers.borrow().iter() { + if !self + .flush_coherent_memory + .iter() + .any(|m| m.buffer == sync.device_buffer) + { + self.flush_coherent_memory.push(MemoryFlush { + host_memory: sync.host_ptr, + sync_range: sync.range.clone(), + buffer: sync.device_buffer, + }); + } + } + + for sync in coherent_buffers.invalidate_coherent_buffers.borrow().iter() { + if !self + .invalidate_coherent_memory + .iter() + .any(|m| m.buffer == sync.device_buffer) + { + self.invalidate_coherent_memory.push(MemoryInvalidate { + working_buffer: Some(self.internal.working_buffer.clone()), + working_buffer_size: self.internal.working_buffer_size, + host_memory: sync.host_ptr, + host_sync_range: sync.range.clone(), + buffer_sync_range: sync.range.clone(), + buffer: sync.device_buffer, + }); + } + } + } + + let cs_offsets = generate_compute_dynamic_constant_buffer_offsets( + &*set.layout.bindings, + &mut offset_iter, + self.context1.is_some(), + ); + + if let Some(rd) = info.registers.cs.c.as_some() { + let start_slot = rd.res_index as u32; + let num_buffers = rd.count as u32; + let constant_buffers = set.handles.offset(rd.pool_offset as isize); + if let Some(ref context1) = self.context1 { + // This call with offsets won't work right with command list emulation + // unless we reset the first and last constant buffers to null. + if self.internal.command_list_emulation { + let null_cbuf = [ptr::null_mut::()]; + context1.CSSetConstantBuffers(start_slot, 1, &null_cbuf as *const _); + if num_buffers > 1 { + context1.CSSetConstantBuffers( + start_slot + num_buffers - 1, + 1, + &null_cbuf as *const _, + ); + } + } + + // TODO: This should be the actual buffer length for RBA purposes, + // but that information isn't easily accessible here. + context1.CSSetConstantBuffers1( + start_slot, + num_buffers, + constant_buffers as *const *mut _, + cs_offsets.as_ptr(), + self.internal.constant_buffer_count_buffer.as_ptr(), + ); + } else { + self.context.CSSetConstantBuffers( + start_slot, + num_buffers, + constant_buffers as *const *mut _, + ); + } + } + if let Some(rd) = info.registers.cs.t.as_some() { + self.context.CSSetShaderResources( + rd.res_index as u32, + rd.count as u32, + set.handles.offset(rd.pool_offset as isize) as *const *mut _, + ); + } + if let Some(rd) = info.registers.cs.u.as_some() { + self.context.CSSetUnorderedAccessViews( + rd.res_index as u32, + rd.count as u32, + set.handles.offset(rd.pool_offset as isize) as *const *mut _, + ptr::null_mut(), + ); + } + if let Some(rd) = info.registers.cs.s.as_some() { + self.context.CSSetSamplers( + rd.res_index as u32, + rd.count as u32, + set.handles.offset(rd.pool_offset as isize) as *const *mut _, + ); + } + } + } + + unsafe fn dispatch(&mut self, count: WorkGroupCount) { + self.context.Dispatch(count[0], count[1], count[2]); + } + + unsafe fn dispatch_indirect(&mut self, _buffer: &Buffer, _offset: buffer::Offset) { + unimplemented!() + } + + unsafe fn fill_buffer(&mut self, buffer: &Buffer, sub: buffer::SubRange, data: u32) { + let mut device: *mut d3d11::ID3D11Device = mem::zeroed(); + self.context.GetDevice(&mut device as *mut _); + let device = ComPtr::from_raw(device); + + assert_eq!( + sub.offset % 4, + 0, + "Buffer sub range offset must be multiple of 4" + ); + if let Some(size) = sub.size { + assert_eq!(size % 4, 0, "Buffer sub range size must be multiple of 4"); + } + + // TODO: expose this requirement to the user to enable avoiding the unaligned fill for + // performance + // FirstElement must be a multiple of 4 (since each element is 4 bytes and the + // offset needs to be a multiple of 16 bytes) + let element_offset = sub.offset as u32 / 4; + let num_elements = sub.size.unwrap_or(buffer.requirements.size) as u32 / 4; + + fn up_align(x: u32, alignment: u32) -> u32 { + (x + alignment - 1) & !(alignment - 1) + } + + let aligned_element_offset = up_align(element_offset, 4); + let unaligned_num_elements = (aligned_element_offset - element_offset).min(num_elements); + let aligned_num_elements = num_elements - unaligned_num_elements; + + // Use ClearUnorderedAccessViewUint to fill from the 16 byte aligned offset + if aligned_num_elements > 0 { + let mut desc: d3d11::D3D11_UNORDERED_ACCESS_VIEW_DESC = mem::zeroed(); + desc.Format = dxgiformat::DXGI_FORMAT_R32_TYPELESS; + desc.ViewDimension = d3d11::D3D11_UAV_DIMENSION_BUFFER; + *desc.u.Buffer_mut() = d3d11::D3D11_BUFFER_UAV { + FirstElement: aligned_element_offset, + NumElements: aligned_num_elements, + Flags: d3d11::D3D11_BUFFER_UAV_FLAG_RAW, + }; + + let mut uav: *mut d3d11::ID3D11UnorderedAccessView = ptr::null_mut(); + let hr = device.CreateUnorderedAccessView( + buffer.internal.raw as *mut _, + &desc, + &mut uav as *mut *mut _ as *mut *mut _, + ); + + if !winerror::SUCCEEDED(hr) { + panic!("fill_buffer failed to make UAV failed: 0x{:x}", hr); + } + + let uav = ComPtr::from_raw(uav); + + self.context + .ClearUnorderedAccessViewUint(uav.as_raw(), &[data; 4]); + } + + // If there is unaligned portion at the beginning of the sub region + // create a new buffer with the fill data to copy into this region + if unaligned_num_elements > 0 { + debug_assert!( + unaligned_num_elements < 4, + "The number of unaligned elements is {} but it should be less than 4", + unaligned_num_elements + ); + + let initial_data = [data; 4]; + + let initial_data = d3d11::D3D11_SUBRESOURCE_DATA { + pSysMem: initial_data.as_ptr() as *const _, + SysMemPitch: 0, + SysMemSlicePitch: 0, + }; + + // TODO: consider using a persistent buffer like the working_buffer + let desc = d3d11::D3D11_BUFFER_DESC { + ByteWidth: core::mem::size_of_val(&initial_data) as _, + Usage: d3d11::D3D11_USAGE_DEFAULT, + BindFlags: 0, + CPUAccessFlags: 0, + MiscFlags: 0, + StructureByteStride: 0, + }; + let mut temp_buffer = ptr::null_mut::(); + + assert_eq!( + winerror::S_OK, + device.CreateBuffer( + &desc, + &initial_data, + &mut temp_buffer as *mut *mut _ as *mut *mut _, + ) + ); + + let temp_buffer = ComPtr::from_raw(temp_buffer); + + let src_box = d3d11::D3D11_BOX { + left: 0, + top: 0, + front: 0, + right: (unaligned_num_elements * core::mem::size_of::() as u32) as _, + bottom: 1, + back: 1, + }; + + self.context.CopySubresourceRegion( + buffer.internal.raw as _, + 0, + sub.offset as _, // offset in bytes + 0, + 0, + temp_buffer.as_raw() as _, + 0, + &src_box, + ); + } + } + + unsafe fn update_buffer(&mut self, _buffer: &Buffer, _offset: buffer::Offset, _data: &[u8]) { + unimplemented!() + } + + unsafe fn copy_buffer(&mut self, src: &Buffer, dst: &Buffer, regions: T) + where + T: Iterator, + { + if src.is_coherent { + self.defer_coherent_flush(src); + } + + for info in regions { + let src_box = d3d11::D3D11_BOX { + left: info.src as _, + top: 0, + front: 0, + right: (info.src + info.size) as _, + bottom: 1, + back: 1, + }; + + self.context.CopySubresourceRegion( + dst.internal.raw as _, + 0, + info.dst as _, + 0, + 0, + src.internal.raw as _, + 0, + &src_box, + ); + + if let Some(disjoint_cb) = dst.internal.disjoint_cb { + self.context.CopySubresourceRegion( + disjoint_cb as _, + 0, + info.dst as _, + 0, + 0, + src.internal.raw as _, + 0, + &src_box, + ); + } + } + } + + unsafe fn copy_image( + &mut self, + src: &Image, + _: image::Layout, + dst: &Image, + _: image::Layout, + regions: T, + ) where + T: Iterator, + { + self.internal + .copy_image_2d(&self.context, src, dst, regions); + } + + unsafe fn copy_buffer_to_image( + &mut self, + buffer: &Buffer, + image: &Image, + _: image::Layout, + regions: T, + ) where + T: Iterator, + { + if buffer.is_coherent { + self.defer_coherent_flush(buffer); + } + + self.internal + .copy_buffer_to_image(&self.context, buffer, image, regions); + } + + unsafe fn copy_image_to_buffer( + &mut self, + image: &Image, + _: image::Layout, + buffer: &Buffer, + regions: T, + ) where + T: Iterator, + { + if buffer.is_coherent { + self.defer_coherent_invalidate(buffer); + } + + self.internal + .copy_image_to_buffer(&self.context, image, buffer, regions); + } + + unsafe fn draw(&mut self, vertices: Range, instances: Range) { + self.context.DrawInstanced( + vertices.end - vertices.start, + instances.end - instances.start, + vertices.start, + instances.start, + ); + } + + unsafe fn draw_indexed( + &mut self, + indices: Range, + base_vertex: VertexOffset, + instances: Range, + ) { + self.context.DrawIndexedInstanced( + indices.end - indices.start, + instances.end - instances.start, + indices.start, + base_vertex, + instances.start, + ); + } + + unsafe fn draw_indirect( + &mut self, + buffer: &Buffer, + offset: buffer::Offset, + draw_count: DrawCount, + _stride: buffer::Stride, + ) { + assert_eq!(draw_count, 1, "DX11 doesn't support MULTI_DRAW_INDIRECT"); + self.context + .DrawInstancedIndirect(buffer.internal.raw, offset as _); + } + + unsafe fn draw_indexed_indirect( + &mut self, + buffer: &Buffer, + offset: buffer::Offset, + draw_count: DrawCount, + _stride: buffer::Stride, + ) { + assert_eq!(draw_count, 1, "DX11 doesn't support MULTI_DRAW_INDIRECT"); + self.context + .DrawIndexedInstancedIndirect(buffer.internal.raw, offset as _); + } + + unsafe fn draw_indirect_count( + &mut self, + _buffer: &Buffer, + _offset: buffer::Offset, + _count_buffer: &Buffer, + _count_buffer_offset: buffer::Offset, + _max_draw_count: u32, + _stride: buffer::Stride, + ) { + panic!("DX11 doesn't support DRAW_INDIRECT_COUNT") + } + + unsafe fn draw_indexed_indirect_count( + &mut self, + _buffer: &Buffer, + _offset: buffer::Offset, + _count_buffer: &Buffer, + _count_buffer_offset: buffer::Offset, + _max_draw_count: u32, + _stride: buffer::Stride, + ) { + panic!("DX11 doesn't support DRAW_INDIRECT_COUNT") + } + + unsafe fn draw_mesh_tasks(&mut self, _: TaskCount, _: TaskCount) { + panic!("DX11 doesn't support MESH_SHADERS") + } + + unsafe fn draw_mesh_tasks_indirect( + &mut self, + _: &Buffer, + _: buffer::Offset, + _: hal::DrawCount, + _: buffer::Stride, + ) { + panic!("DX11 doesn't support MESH_SHADERS") + } + + unsafe fn draw_mesh_tasks_indirect_count( + &mut self, + _: &Buffer, + _: buffer::Offset, + _: &Buffer, + _: buffer::Offset, + _: hal::DrawCount, + _: buffer::Stride, + ) { + panic!("DX11 doesn't support MESH_SHADERS") + } + + unsafe fn set_event(&mut self, _: &(), _: pso::PipelineStage) { + unimplemented!() + } + + unsafe fn reset_event(&mut self, _: &(), _: pso::PipelineStage) { + unimplemented!() + } + + unsafe fn wait_events<'a, I, J>(&mut self, _: I, _: Range, _: J) + where + I: Iterator, + J: Iterator>, + { + unimplemented!() + } + + unsafe fn begin_query(&mut self, _query: query::Query, _flags: query::ControlFlags) { + unimplemented!() + } + + unsafe fn end_query(&mut self, _query: query::Query) { + unimplemented!() + } + + unsafe fn reset_query_pool(&mut self, _pool: &QueryPool, _queries: Range) { + unimplemented!() + } + + unsafe fn copy_query_pool_results( + &mut self, + _pool: &QueryPool, + _queries: Range, + _buffer: &Buffer, + _offset: buffer::Offset, + _stride: buffer::Stride, + _flags: query::ResultFlags, + ) { + unimplemented!() + } + + unsafe fn write_timestamp(&mut self, _: pso::PipelineStage, _query: query::Query) { + unimplemented!() + } + + unsafe fn push_graphics_constants( + &mut self, + _layout: &PipelineLayout, + _stages: pso::ShaderStageFlags, + offset: u32, + constants: &[u32], + ) { + let start = (offset / 4) as usize; + let end = start + constants.len(); + + self.push_constant_data[start..end].copy_from_slice(constants); + + self.context.UpdateSubresource( + self.push_constant_buffer.as_raw() as *mut _, + 0, + ptr::null(), + self.push_constant_data.as_ptr() as *const _, + MAX_PUSH_CONSTANT_SIZE as _, + 1, + ); + } + + unsafe fn push_compute_constants( + &mut self, + _layout: &PipelineLayout, + offset: u32, + constants: &[u32], + ) { + let start = (offset / 4) as usize; + let end = start + constants.len(); + + self.push_constant_data[start..end].copy_from_slice(constants); + + self.context.UpdateSubresource( + self.push_constant_buffer.as_raw() as *mut _, + 0, + ptr::null(), + self.push_constant_data.as_ptr() as *const _, + MAX_PUSH_CONSTANT_SIZE as _, + 1, + ); + } + + unsafe fn execute_commands<'a, T>(&mut self, _buffers: T) + where + T: Iterator, + { + unimplemented!() + } + + unsafe fn insert_debug_marker(&mut self, name: &str, _color: u32) { + debug::debug_marker(&self.context, &format!("{}", name)) + } + unsafe fn begin_debug_marker(&mut self, _name: &str, _color: u32) { + // TODO: This causes everything after this to be part of this scope, why? + // self.debug_scopes.push(debug::DebugScope::with_name(&self.context, format_args!("{}", name))) + } + unsafe fn end_debug_marker(&mut self) { + // self.debug_scopes.pop(); + } +} + +#[derive(Clone, Debug)] +enum SyncRange { + Whole, + Partial(Range), +} + +#[derive(Debug)] +pub struct MemoryFlush { + host_memory: *const u8, + sync_range: SyncRange, + buffer: *mut d3d11::ID3D11Buffer, +} + +pub struct MemoryInvalidate { + working_buffer: Option>, + working_buffer_size: u64, + host_memory: *mut u8, + host_sync_range: Range, + buffer_sync_range: Range, + buffer: *mut d3d11::ID3D11Buffer, +} + +impl fmt::Debug for MemoryInvalidate { + fn fmt(&self, fmt: &mut fmt::Formatter) -> fmt::Result { + fmt.write_str("MemoryInvalidate") + } +} + +fn intersection(a: &Range, b: &Range) -> Option> { + let r = a.start.max(b.start)..a.end.min(b.end); + if r.start < r.end { + Some(r) + } else { + None + } +} + +impl MemoryFlush { + fn do_flush(&self, context: &ComPtr) { + let src = self.host_memory; + + debug_marker!(context, "Flush({:?})", self.sync_range); + let region = match self.sync_range { + SyncRange::Partial(ref range) if range.start < range.end => Some(d3d11::D3D11_BOX { + left: range.start as u32, + top: 0, + front: 0, + right: range.end as u32, + bottom: 1, + back: 1, + }), + _ => None, + }; + + unsafe { + context.UpdateSubresource( + self.buffer as _, + 0, + region.as_ref().map_or(ptr::null(), |r| r), + src as _, + 0, + 0, + ); + } + } +} + +impl MemoryInvalidate { + fn download( + &self, + context: &ComPtr, + buffer: *mut d3d11::ID3D11Buffer, + host_range: Range, + buffer_range: Range, + ) { + // Range doesn't impl `len` for some bizzare reason relating to underflow + debug_assert_eq!( + host_range.end - host_range.start, + buffer_range.end - buffer_range.start + ); + + unsafe { + context.CopySubresourceRegion( + self.working_buffer.clone().unwrap().as_raw() as _, + 0, + 0, + 0, + 0, + buffer as _, + 0, + &d3d11::D3D11_BOX { + left: buffer_range.start as _, + top: 0, + front: 0, + right: buffer_range.end as _, + bottom: 1, + back: 1, + }, + ); + + // copy over to our vec + let dst = self.host_memory.offset(host_range.start as isize); + let src = self.map(&context); + ptr::copy(src, dst, (host_range.end - host_range.start) as usize); + self.unmap(&context); + } + } + + fn do_invalidate(&self, context: &ComPtr) { + let stride = self.working_buffer_size; + let len = self.host_sync_range.end - self.host_sync_range.start; + let chunks = len / stride; + let remainder = len % stride; + + // we split up the copies into chunks the size of our working buffer + for i in 0..chunks { + let host_offset = self.host_sync_range.start + i * stride; + let host_range = host_offset..(host_offset + stride); + let buffer_offset = self.buffer_sync_range.start + i * stride; + let buffer_range = buffer_offset..(buffer_offset + stride); + + self.download(context, self.buffer, host_range, buffer_range); + } + + if remainder != 0 { + let host_offset = self.host_sync_range.start + chunks * stride; + let host_range = host_offset..self.host_sync_range.end; + let buffer_offset = self.buffer_sync_range.start + chunks * stride; + let buffer_range = buffer_offset..self.buffer_sync_range.end; + + debug_assert!(host_range.end - host_range.start <= stride); + debug_assert!(buffer_range.end - buffer_range.start <= stride); + + self.download(context, self.buffer, host_range, buffer_range); + } + } + + fn map(&self, context: &ComPtr) -> *mut u8 { + assert_eq!(self.working_buffer.is_some(), true); + + unsafe { + let mut map = mem::zeroed(); + let hr = context.Map( + self.working_buffer.clone().unwrap().as_raw() as _, + 0, + d3d11::D3D11_MAP_READ, + 0, + &mut map, + ); + + assert_eq!(hr, winerror::S_OK); + + map.pData as _ + } + } + + fn unmap(&self, context: &ComPtr) { + unsafe { + context.Unmap(self.working_buffer.clone().unwrap().as_raw() as _, 0); + } + } +} + +type LocalResourceArena = thunderdome::Arena<(Range, T)>; + +// Since we dont have any heaps to work with directly, Beverytime we bind a +// buffer/image to memory we allocate a dx11 resource and assign it a range. +// +// `HOST_VISIBLE` memory gets a `Vec` which covers the entire memory +// range. This forces us to only expose non-coherent memory, as this +// abstraction acts as a "cache" since the "staging buffer" vec is disjoint +// from all the dx11 resources we store in the struct. +pub struct Memory { + properties: memory::Properties, + size: u64, + + // pointer to staging memory, if it's HOST_VISIBLE + host_ptr: *mut u8, + + // list of all buffers bound to this memory + local_buffers: Arc>>, +} + +impl fmt::Debug for Memory { + fn fmt(&self, fmt: &mut fmt::Formatter) -> fmt::Result { + fmt.write_str("Memory") + } +} + +unsafe impl Send for Memory {} +unsafe impl Sync for Memory {} + +impl Memory { + pub fn resolve(&self, segment: &memory::Segment) -> Range { + segment.offset..segment.size.map_or(self.size, |s| segment.offset + s) + } + + pub fn bind_buffer(&self, range: Range, buffer: InternalBuffer) -> thunderdome::Index { + let mut local_buffers = self.local_buffers.write(); + local_buffers.insert((range, buffer)) + } + + pub fn flush(&self, context: &ComPtr, range: Range) { + use buffer::Usage; + + for (_, &(ref buffer_range, ref buffer)) in self.local_buffers.read().iter() { + let range = match intersection(&range, &buffer_range) { + Some(r) => r, + None => continue, + }; + // we need to handle 3 cases for updating buffers: + // + // 1. if our buffer was created as a `UNIFORM` buffer *and* other usage flags, we + // also have a disjoint buffer which only has `D3D11_BIND_CONSTANT_BUFFER` due + // to DX11 limitation. we then need to update both the original buffer and the + // disjoint one with the *whole* range + // + // 2. if our buffer was created with *only* `UNIFORM` usage we need to upload + // the whole range + // + // 3. the general case, without any `UNIFORM` usage has no restrictions on + // partial updates, so we upload the specified range + // + if let Some(disjoint) = buffer.disjoint_cb { + MemoryFlush { + host_memory: unsafe { self.host_ptr.offset(buffer_range.start as _) }, + sync_range: SyncRange::Whole, + buffer: disjoint, + } + .do_flush(&context); + } + + let mem_flush = if buffer.usage == Usage::UNIFORM { + MemoryFlush { + host_memory: unsafe { self.host_ptr.offset(buffer_range.start as _) }, + sync_range: SyncRange::Whole, + buffer: buffer.raw, + } + } else { + let local_start = range.start - buffer_range.start; + let local_end = range.end - buffer_range.start; + + MemoryFlush { + host_memory: unsafe { self.host_ptr.offset(range.start as _) }, + sync_range: SyncRange::Partial(local_start..local_end), + buffer: buffer.raw, + } + }; + + mem_flush.do_flush(&context) + } + } + + pub fn invalidate( + &self, + context: &ComPtr, + range: Range, + working_buffer: ComPtr, + working_buffer_size: u64, + ) { + for (_, &(ref buffer_range, ref buffer)) in self.local_buffers.read().iter() { + if let Some(range) = intersection(&range, &buffer_range) { + let buffer_start_offset = range.start - buffer_range.start; + let buffer_end_offset = range.end - buffer_range.start; + + let buffer_sync_range = buffer_start_offset..buffer_end_offset; + + MemoryInvalidate { + working_buffer: Some(working_buffer.clone()), + working_buffer_size, + host_memory: self.host_ptr, + host_sync_range: range.clone(), + buffer_sync_range: buffer_sync_range, + buffer: buffer.raw, + } + .do_invalidate(&context); + } + } + } +} + +#[derive(Debug)] +pub struct CommandPool { + device: ComPtr, + device1: Option>, + internal: Arc, +} + +unsafe impl Send for CommandPool {} +unsafe impl Sync for CommandPool {} + +impl hal::pool::CommandPool for CommandPool { + unsafe fn reset(&mut self, _release_resources: bool) { + //unimplemented!() + } + + unsafe fn allocate_one(&mut self, _level: command::Level) -> CommandBuffer { + CommandBuffer::create_deferred( + self.device.clone(), + self.device1.as_deref(), + Arc::clone(&self.internal), + ) + } + + unsafe fn free(&mut self, _cbufs: I) + where + I: Iterator, + { + // TODO: + // unimplemented!() + } +} + +/// Similarily to dx12 backend, we can handle either precompiled dxbc or spirv +pub enum ShaderModule { + Dxbc(Vec), + Spirv(Vec), +} + +// TODO: temporary +impl fmt::Debug for ShaderModule { + fn fmt(&self, f: &mut ::std::fmt::Formatter) -> ::std::fmt::Result { + write!(f, "{}", "ShaderModule { ... }") + } +} + +unsafe impl Send for ShaderModule {} +unsafe impl Sync for ShaderModule {} + +#[derive(Clone, Debug)] +pub struct SubpassDesc { + pub color_attachments: Vec, + pub depth_stencil_attachment: Option, + pub input_attachments: Vec, + pub resolve_attachments: Vec, +} + +impl SubpassDesc { + pub(crate) fn is_using(&self, at_id: pass::AttachmentId) -> bool { + self.color_attachments + .iter() + .chain(self.depth_stencil_attachment.iter()) + .chain(self.input_attachments.iter()) + .chain(self.resolve_attachments.iter()) + .any(|&(id, _)| id == at_id) + } +} + +#[derive(Clone, Debug)] +pub struct RenderPass { + pub attachments: Vec, + pub subpasses: Vec, +} + +#[derive(Clone, Debug)] +pub struct Framebuffer { + layers: image::Layer, +} + +#[derive(Clone, Debug)] +pub struct InternalBuffer { + raw: *mut d3d11::ID3D11Buffer, + // TODO: need to sync between `raw` and `disjoint_cb`, same way as we do with + // `MemoryFlush/Invalidate` + disjoint_cb: Option<*mut d3d11::ID3D11Buffer>, // if unbound this buffer might be null. + srv: Option<*mut d3d11::ID3D11ShaderResourceView>, + uav: Option<*mut d3d11::ID3D11UnorderedAccessView>, + usage: buffer::Usage, + debug_name: Option, +} + +impl InternalBuffer { + unsafe fn release_resources(&mut self) { + (&*self.raw).Release(); + self.raw = ptr::null_mut(); + self.disjoint_cb.take().map(|cb| (&*cb).Release()); + self.uav.take().map(|uav| (&*uav).Release()); + self.srv.take().map(|srv| (&*srv).Release()); + self.usage = buffer::Usage::empty(); + self.debug_name = None; + } +} + +pub struct Buffer { + internal: InternalBuffer, + is_coherent: bool, + memory_ptr: *mut u8, // null if unbound or non-cpu-visible + bound_range: Range, // 0 if unbound + /// Handle to the Memory arena storing this buffer. + local_memory_arena: Weak>>, + /// Index into the above memory arena. + /// + /// Once memory is bound to a buffer, this should never be None. + memory_index: Option, + requirements: memory::Requirements, + bind: d3d11::D3D11_BIND_FLAG, +} + +impl fmt::Debug for Buffer { + fn fmt(&self, fmt: &mut fmt::Formatter) -> fmt::Result { + fmt.write_str("Buffer") + } +} + +unsafe impl Send for Buffer {} +unsafe impl Sync for Buffer {} + +#[derive(Debug)] +pub struct BufferView; + +pub struct Image { + kind: image::Kind, + usage: image::Usage, + format: format::Format, + view_caps: image::ViewCapabilities, + decomposed_format: conv::DecomposedDxgiFormat, + mip_levels: image::Level, + internal: InternalImage, + bind: d3d11::D3D11_BIND_FLAG, + requirements: memory::Requirements, +} + +impl fmt::Debug for Image { + fn fmt(&self, fmt: &mut fmt::Formatter) -> fmt::Result { + fmt.write_str("Image") + } +} + +pub struct InternalImage { + raw: *mut d3d11::ID3D11Resource, + copy_srv: Option>, + srv: Option>, + + /// Contains UAVs for all subresources + unordered_access_views: Vec>, + + /// Contains DSVs for all subresources + depth_stencil_views: Vec>, + + /// Contains RTVs for all subresources + render_target_views: Vec>, + + debug_name: Option, +} + +impl InternalImage { + unsafe fn release_resources(&mut self) { + (&*self.raw).Release(); + self.copy_srv = None; + self.srv = None; + self.unordered_access_views.clear(); + self.depth_stencil_views.clear(); + self.render_target_views.clear(); + } +} + +impl fmt::Debug for InternalImage { + fn fmt(&self, fmt: &mut fmt::Formatter) -> fmt::Result { + fmt.write_str("InternalImage") + } +} + +unsafe impl Send for Image {} +unsafe impl Sync for Image {} + +impl Image { + fn calc_subresource(&self, mip_level: UINT, layer: UINT) -> UINT { + mip_level + (layer * self.mip_levels as UINT) + } + + fn get_uav( + &self, + mip_level: image::Level, + _layer: image::Layer, + ) -> Option<&ComPtr> { + self.internal + .unordered_access_views + .get(self.calc_subresource(mip_level as _, 0) as usize) + } + + fn get_dsv( + &self, + mip_level: image::Level, + layer: image::Layer, + ) -> Option<&ComPtr> { + self.internal + .depth_stencil_views + .get(self.calc_subresource(mip_level as _, layer as _) as usize) + } + + fn get_rtv( + &self, + mip_level: image::Level, + layer: image::Layer, + ) -> Option<&ComPtr> { + self.internal + .render_target_views + .get(self.calc_subresource(mip_level as _, layer as _) as usize) + } +} + +pub struct ImageView { + subresource: UINT, + format: format::Format, + rtv_handle: Option<*mut d3d11::ID3D11RenderTargetView>, + srv_handle: Option<*mut d3d11::ID3D11ShaderResourceView>, + dsv_handle: Option<*mut d3d11::ID3D11DepthStencilView>, + rodsv_handle: Option<*mut d3d11::ID3D11DepthStencilView>, + uav_handle: Option<*mut d3d11::ID3D11UnorderedAccessView>, + owned: bool, +} + +impl Clone for ImageView { + fn clone(&self) -> Self { + Self { + subresource: self.subresource, + format: self.format, + rtv_handle: self.rtv_handle.clone(), + srv_handle: self.srv_handle.clone(), + dsv_handle: self.dsv_handle.clone(), + rodsv_handle: self.rodsv_handle.clone(), + uav_handle: self.uav_handle.clone(), + owned: false, + } + } +} + +impl Drop for ImageView { + fn drop(&mut self) { + if self.owned { + if let Some(rtv) = self.rtv_handle.take() { + unsafe { (&*rtv).Release() }; + } + if let Some(srv) = self.srv_handle.take() { + unsafe { (&*srv).Release() }; + } + if let Some(dsv) = self.dsv_handle.take() { + unsafe { (&*dsv).Release() }; + } + if let Some(rodsv) = self.rodsv_handle.take() { + unsafe { (&*rodsv).Release() }; + } + if let Some(uav) = self.uav_handle.take() { + unsafe { (&*uav).Release() }; + } + } + } +} + +impl fmt::Debug for ImageView { + fn fmt(&self, fmt: &mut fmt::Formatter) -> fmt::Result { + fmt.write_str("ImageView") + } +} + +unsafe impl Send for ImageView {} +unsafe impl Sync for ImageView {} + +pub struct Sampler { + sampler_handle: ComPtr, +} + +impl fmt::Debug for Sampler { + fn fmt(&self, fmt: &mut fmt::Formatter) -> fmt::Result { + fmt.write_str("Sampler") + } +} + +unsafe impl Send for Sampler {} +unsafe impl Sync for Sampler {} + +pub struct ComputePipeline { + cs: ComPtr, +} + +impl fmt::Debug for ComputePipeline { + fn fmt(&self, fmt: &mut fmt::Formatter) -> fmt::Result { + fmt.write_str("ComputePipeline") + } +} + +unsafe impl Send for ComputePipeline {} +unsafe impl Sync for ComputePipeline {} + +/// NOTE: some objects are hashed internally and reused when created with the +/// same params[0], need to investigate which interfaces this applies +/// to. +/// +/// [0]: https://msdn.microsoft.com/en-us/library/windows/desktop/ff476500(v=vs.85).aspx +#[derive(Clone)] +pub struct GraphicsPipeline { + vs: ComPtr, + gs: Option>, + hs: Option>, + ds: Option>, + ps: Option>, + topology: d3d11::D3D11_PRIMITIVE_TOPOLOGY, + input_layout: ComPtr, + rasterizer_state: ComPtr, + blend_state: ComPtr, + depth_stencil_state: Option, + baked_states: pso::BakedStates, + required_bindings: u32, + max_vertex_bindings: u32, + strides: Vec, +} + +impl fmt::Debug for GraphicsPipeline { + fn fmt(&self, fmt: &mut fmt::Formatter) -> fmt::Result { + fmt.write_str("GraphicsPipeline") + } +} + +unsafe impl Send for GraphicsPipeline {} +unsafe impl Sync for GraphicsPipeline {} + +type ResourceIndex = u8; +type DescriptorIndex = u16; + +#[derive(Clone, Debug, Default)] +struct RegisterData { + // CBV + c: T, + // SRV + t: T, + // UAV + u: T, + // Sampler + s: T, +} + +impl RegisterData { + fn map U>(&self, fun: F) -> RegisterData { + RegisterData { + c: fun(&self.c), + t: fun(&self.t), + u: fun(&self.u), + s: fun(&self.s), + } + } +} + +impl RegisterData { + fn add_content_many(&mut self, content: DescriptorContent, many: DescriptorIndex) { + if content.contains(DescriptorContent::CBV) { + self.c += many; + } + if content.contains(DescriptorContent::SRV) { + self.t += many; + } + if content.contains(DescriptorContent::UAV) { + self.u += many; + } + if content.contains(DescriptorContent::SAMPLER) { + self.s += many; + } + } + + fn sum(&self) -> DescriptorIndex { + self.c + self.t + self.u + self.s + } +} + +#[derive(Clone, Debug, Default)] +struct MultiStageData { + vs: T, + ps: T, + cs: T, +} + +impl MultiStageData { + fn select(self, stage: ShaderStage) -> T { + match stage { + ShaderStage::Vertex => self.vs, + ShaderStage::Fragment => self.ps, + ShaderStage::Compute => self.cs, + _ => panic!("Unsupported stage {:?}", stage), + } + } +} + +impl MultiStageData> { + fn map_register U>(&self, fun: F) -> MultiStageData> { + MultiStageData { + vs: self.vs.map(&fun), + ps: self.ps.map(&fun), + cs: self.cs.map(&fun), + } + } + + fn map_other) -> U>(&self, fun: F) -> MultiStageData { + MultiStageData { + vs: fun(&self.vs), + ps: fun(&self.ps), + cs: fun(&self.cs), + } + } +} + +impl MultiStageData> { + fn add_content_many( + &mut self, + content: DescriptorContent, + stages: pso::ShaderStageFlags, + count: DescriptorIndex, + ) { + if stages.contains(pso::ShaderStageFlags::VERTEX) { + self.vs.add_content_many(content, count); + } + if stages.contains(pso::ShaderStageFlags::FRAGMENT) { + self.ps.add_content_many(content, count); + } + if stages.contains(pso::ShaderStageFlags::COMPUTE) { + self.cs.add_content_many(content, count); + } + } + + fn sum(&self) -> DescriptorIndex { + self.vs.sum() + self.ps.sum() + self.cs.sum() + } +} + +#[derive(Clone, Debug, Default)] +struct RegisterPoolMapping { + offset: DescriptorIndex, + count: ResourceIndex, +} + +#[derive(Clone, Debug, Default)] +struct RegisterInfo { + res_index: ResourceIndex, + pool_offset: DescriptorIndex, + count: ResourceIndex, +} + +impl RegisterInfo { + fn as_some(&self) -> Option<&Self> { + if self.count == 0 { + None + } else { + Some(self) + } + } +} + +#[derive(Clone, Debug, Default)] +struct RegisterAccumulator { + res_index: ResourceIndex, +} + +impl RegisterAccumulator { + fn to_mapping(&self, cur_offset: &mut DescriptorIndex) -> RegisterPoolMapping { + let offset = *cur_offset; + *cur_offset += self.res_index as DescriptorIndex; + + RegisterPoolMapping { + offset, + count: self.res_index, + } + } + + fn advance(&mut self, mapping: &RegisterPoolMapping) -> RegisterInfo { + let res_index = self.res_index; + self.res_index += mapping.count; + RegisterInfo { + res_index, + pool_offset: mapping.offset, + count: mapping.count, + } + } +} + +impl RegisterData { + fn to_mapping(&self, pool_offset: &mut DescriptorIndex) -> RegisterData { + RegisterData { + c: self.c.to_mapping(pool_offset), + t: self.t.to_mapping(pool_offset), + u: self.u.to_mapping(pool_offset), + s: self.s.to_mapping(pool_offset), + } + } + + fn advance( + &mut self, + mapping: &RegisterData, + ) -> RegisterData { + RegisterData { + c: self.c.advance(&mapping.c), + t: self.t.advance(&mapping.t), + u: self.u.advance(&mapping.u), + s: self.s.advance(&mapping.s), + } + } +} + +impl MultiStageData> { + fn to_mapping(&self) -> MultiStageData> { + let mut pool_offset = 0; + MultiStageData { + vs: self.vs.to_mapping(&mut pool_offset), + ps: self.ps.to_mapping(&mut pool_offset), + cs: self.cs.to_mapping(&mut pool_offset), + } + } + + fn advance( + &mut self, + mapping: &MultiStageData>, + ) -> MultiStageData> { + MultiStageData { + vs: self.vs.advance(&mapping.vs), + ps: self.ps.advance(&mapping.ps), + cs: self.cs.advance(&mapping.cs), + } + } +} + +#[derive(Clone, Debug)] +struct DescriptorSetInfo { + bindings: Arc>, + registers: MultiStageData>, +} + +impl DescriptorSetInfo { + fn find_register( + &self, + stage: ShaderStage, + binding_index: pso::DescriptorBinding, + ) -> Option<(DescriptorContent, RegisterData)> { + let mut res_offsets = self + .registers + .map_register(|info| info.res_index as DescriptorIndex) + .select(stage); + for binding in self.bindings.iter() { + if !binding.stage_flags.contains(stage.to_flag()) { + continue; + } + let content = DescriptorContent::from(binding.ty); + if binding.binding == binding_index { + return Some((content, res_offsets.map(|offset| *offset as ResourceIndex))); + } + res_offsets.add_content_many(content, 1); + } + None + } + + fn find_uav_register( + &self, + stage: ShaderStage, + binding_index: pso::DescriptorBinding, + ) -> (DescriptorContent, RegisterData) { + // Look only where uavs are stored for that stage. + let register_stage = if stage == ShaderStage::Compute { + stage + } else { + ShaderStage::Fragment + }; + + let mut res_offsets = self + .registers + .map_register(|info| info.res_index as DescriptorIndex) + .select(register_stage); + for binding in self.bindings.iter() { + // We don't care what stage they're in, only if they are UAVs or not. + let content = DescriptorContent::from(binding.ty); + if !content.contains(DescriptorContent::UAV) { + continue; + } + if binding.binding == binding_index { + return (content, res_offsets.map(|offset| *offset as ResourceIndex)); + } + res_offsets.add_content_many(content, 1); + } + panic!("Unable to find binding {:?}", binding_index); + } +} + +/// The pipeline layout holds optimized (less api calls) ranges of objects for all descriptor sets +/// belonging to the pipeline object. +#[derive(Debug)] +pub struct PipelineLayout { + sets: Vec, +} + +/// The descriptor set layout contains mappings from a given binding to the offset in our +/// descriptor pool storage and what type of descriptor it is (combined image sampler takes up two +/// handles). +#[derive(Debug)] +pub struct DescriptorSetLayout { + bindings: Arc>, + pool_mapping: MultiStageData>, +} + +#[derive(Debug)] +struct CoherentBufferFlushRange { + device_buffer: *mut d3d11::ID3D11Buffer, + host_ptr: *mut u8, + range: SyncRange, +} + +#[derive(Debug)] +struct CoherentBufferInvalidateRange { + device_buffer: *mut d3d11::ID3D11Buffer, + host_ptr: *mut u8, + range: Range, +} + +#[derive(Debug)] +struct CoherentBuffers { + // descriptor set writes containing coherent resources go into these vecs and are added to the + // command buffers own Vec on binding the set. + flush_coherent_buffers: RefCell>, + invalidate_coherent_buffers: RefCell>, +} + +impl CoherentBuffers { + fn _add_flush(&self, old: *mut d3d11::ID3D11Buffer, buffer: &Buffer) { + let new = buffer.internal.raw; + + if old != new { + let mut buffers = self.flush_coherent_buffers.borrow_mut(); + + let pos = buffers.iter().position(|sync| old == sync.device_buffer); + + let sync_range = CoherentBufferFlushRange { + device_buffer: new, + host_ptr: buffer.memory_ptr, + range: SyncRange::Whole, + }; + + if let Some(pos) = pos { + buffers[pos] = sync_range; + } else { + buffers.push(sync_range); + } + + if let Some(disjoint) = buffer.internal.disjoint_cb { + let pos = buffers + .iter() + .position(|sync| disjoint == sync.device_buffer); + + let sync_range = CoherentBufferFlushRange { + device_buffer: disjoint, + host_ptr: buffer.memory_ptr, + range: SyncRange::Whole, + }; + + if let Some(pos) = pos { + buffers[pos] = sync_range; + } else { + buffers.push(sync_range); + } + } + } + } + + fn _add_invalidate(&self, old: *mut d3d11::ID3D11Buffer, buffer: &Buffer) { + let new = buffer.internal.raw; + + if old != new { + let mut buffers = self.invalidate_coherent_buffers.borrow_mut(); + + let pos = buffers.iter().position(|sync| old == sync.device_buffer); + + let sync_range = CoherentBufferInvalidateRange { + device_buffer: new, + host_ptr: buffer.memory_ptr, + range: buffer.bound_range.clone(), + }; + + if let Some(pos) = pos { + buffers[pos] = sync_range; + } else { + buffers.push(sync_range); + } + } + } +} + +/// Newtype around a common interface that all bindable resources inherit from. +#[derive(Debug, Copy, Clone)] +#[repr(transparent)] +struct Descriptor(*mut d3d11::ID3D11DeviceChild); + +bitflags! { + /// A set of D3D11 descriptor types that need to be associated + /// with a single gfx-hal `DescriptorType`. + #[derive(Default)] + pub struct DescriptorContent: u8 { + const CBV = 0x1; + const SRV = 0x2; + const UAV = 0x4; + const SAMPLER = 0x8; + /// Indicates if the descriptor is a dynamic uniform/storage buffer. + /// Important as dynamic buffers are implemented as root descriptors. + const DYNAMIC = 0x10; + } +} + +impl From for DescriptorContent { + fn from(ty: pso::DescriptorType) -> Self { + use hal::pso::{ + BufferDescriptorFormat as Bdf, BufferDescriptorType as Bdt, DescriptorType as Dt, + ImageDescriptorType as Idt, + }; + match ty { + Dt::Sampler => DescriptorContent::SAMPLER, + Dt::Image { + ty: Idt::Sampled { with_sampler: true }, + } => DescriptorContent::SRV | DescriptorContent::SAMPLER, + Dt::Image { + ty: Idt::Sampled { + with_sampler: false, + }, + } + | Dt::InputAttachment => DescriptorContent::SRV, + Dt::Image { + ty: Idt::Storage { .. }, + } => DescriptorContent::UAV, + Dt::Buffer { + ty: Bdt::Uniform, + format: + Bdf::Structured { + dynamic_offset: true, + }, + } => DescriptorContent::CBV | DescriptorContent::DYNAMIC, + Dt::Buffer { + ty: Bdt::Uniform, .. + } => DescriptorContent::CBV, + Dt::Buffer { + ty: Bdt::Storage { read_only: true }, + format: + Bdf::Structured { + dynamic_offset: true, + }, + } => DescriptorContent::SRV | DescriptorContent::DYNAMIC, + Dt::Buffer { + ty: Bdt::Storage { read_only: false }, + format: + Bdf::Structured { + dynamic_offset: true, + }, + } => DescriptorContent::UAV | DescriptorContent::DYNAMIC, + Dt::Buffer { + ty: Bdt::Storage { read_only: true }, + .. + } => DescriptorContent::SRV, + Dt::Buffer { + ty: Bdt::Storage { read_only: false }, + .. + } => DescriptorContent::UAV, + } + } +} + +pub struct DescriptorSet { + offset: DescriptorIndex, + len: DescriptorIndex, + handles: *mut Descriptor, + coherent_buffers: Mutex, + layout: DescriptorSetLayout, +} + +impl fmt::Debug for DescriptorSet { + fn fmt(&self, fmt: &mut fmt::Formatter) -> fmt::Result { + fmt.write_str("DescriptorSet") + } +} + +unsafe impl Send for DescriptorSet {} +unsafe impl Sync for DescriptorSet {} + +impl DescriptorSet { + fn _add_flush(&self, old: *mut d3d11::ID3D11Buffer, buffer: &Buffer) { + let new = buffer.internal.raw; + + if old != new { + self.coherent_buffers.lock()._add_flush(old, buffer); + } + } + + fn _add_invalidate(&self, old: *mut d3d11::ID3D11Buffer, buffer: &Buffer) { + let new = buffer.internal.raw; + + if old != new { + self.coherent_buffers.lock()._add_invalidate(old, buffer); + } + } + + unsafe fn assign(&self, offset: DescriptorIndex, value: *mut d3d11::ID3D11DeviceChild) { + *self.handles.offset(offset as isize) = Descriptor(value); + } + + unsafe fn assign_stages( + &self, + offsets: &MultiStageData, + stages: pso::ShaderStageFlags, + value: *mut d3d11::ID3D11DeviceChild, + ) { + if stages.contains(pso::ShaderStageFlags::VERTEX) { + self.assign(offsets.vs, value); + } + if stages.contains(pso::ShaderStageFlags::FRAGMENT) { + self.assign(offsets.ps, value); + } + if stages.contains(pso::ShaderStageFlags::COMPUTE) { + self.assign(offsets.cs, value); + } + } +} + +#[derive(Debug)] +pub struct DescriptorPool { + //TODO: do we need this in the pool? + // if the sets owned their data, we could make this just `Vec` + handles: Vec, + allocator: RangeAllocator, +} + +unsafe impl Send for DescriptorPool {} +unsafe impl Sync for DescriptorPool {} + +impl DescriptorPool { + fn with_capacity(size: DescriptorIndex) -> Self { + DescriptorPool { + handles: vec![Descriptor(ptr::null_mut()); size as usize], + allocator: RangeAllocator::new(0..size), + } + } +} + +impl pso::DescriptorPool for DescriptorPool { + unsafe fn allocate_one( + &mut self, + layout: &DescriptorSetLayout, + ) -> Result { + let len = layout + .pool_mapping + .map_register(|mapping| mapping.count as DescriptorIndex) + .sum() + .max(1); + + self.allocator + .allocate_range(len) + .map(|range| { + for handle in &mut self.handles[range.start as usize..range.end as usize] { + *handle = Descriptor(ptr::null_mut()); + } + + DescriptorSet { + offset: range.start, + len, + handles: self.handles.as_mut_ptr().offset(range.start as _), + coherent_buffers: Mutex::new(CoherentBuffers { + flush_coherent_buffers: RefCell::new(Vec::new()), + invalidate_coherent_buffers: RefCell::new(Vec::new()), + }), + layout: DescriptorSetLayout { + bindings: Arc::clone(&layout.bindings), + pool_mapping: layout.pool_mapping.clone(), + }, + } + }) + .map_err(|_| pso::AllocationError::OutOfPoolMemory) + } + + unsafe fn free(&mut self, descriptor_sets: I) + where + I: Iterator, + { + for set in descriptor_sets { + self.allocator + .free_range(set.offset..(set.offset + set.len)) + } + } + + unsafe fn reset(&mut self) { + self.allocator.reset(); + } +} + +#[derive(Debug)] +pub struct RawFence { + mutex: Mutex, + condvar: Condvar, +} + +pub type Fence = Arc; + +#[derive(Debug)] +pub struct Semaphore; +#[derive(Debug)] +pub struct QueryPool; + +#[derive(Copy, Clone, Debug, Eq, Hash, PartialEq)] +pub enum Backend {} +impl hal::Backend for Backend { + type Instance = Instance; + type PhysicalDevice = PhysicalDevice; + type Device = device::Device; + type Surface = Surface; + + type QueueFamily = QueueFamily; + type Queue = Queue; + type CommandBuffer = CommandBuffer; + + type Memory = Memory; + type CommandPool = CommandPool; + + type ShaderModule = ShaderModule; + type RenderPass = RenderPass; + type Framebuffer = Framebuffer; + + type Buffer = Buffer; + type BufferView = BufferView; + type Image = Image; + + type ImageView = ImageView; + type Sampler = Sampler; + + type ComputePipeline = ComputePipeline; + type GraphicsPipeline = GraphicsPipeline; + type PipelineLayout = PipelineLayout; + type PipelineCache = (); + type DescriptorSetLayout = DescriptorSetLayout; + type DescriptorPool = DescriptorPool; + type DescriptorSet = DescriptorSet; + + type Fence = Fence; + type Semaphore = Semaphore; + type Event = (); + type QueryPool = QueryPool; +} + +fn validate_line_width(width: f32) { + // Note from the Vulkan spec: + // > If the wide lines feature is not enabled, lineWidth must be 1.0 + // Simply assert and no-op because DX11 never exposes `Features::LINE_WIDTH` + assert_eq!(width, 1.0); +} diff --git a/third_party/rust/gfx-backend-dx11/src/shader.rs b/third_party/rust/gfx-backend-dx11/src/shader.rs index fe3fdca0981d..8741b70ff947 100644 --- a/third_party/rust/gfx-backend-dx11/src/shader.rs +++ b/third_party/rust/gfx-backend-dx11/src/shader.rs @@ -1,473 +1,473 @@ -use std::{ffi, ptr, slice}; - -use spirv_cross::{hlsl, spirv, ErrorCode as SpirvErrorCode}; - -use winapi::{ - shared::winerror, - um::{d3d11, d3dcommon, d3dcompiler}, -}; -use wio::com::ComPtr; - -use auxil::{spirv_cross_specialize_ast, ShaderStage}; -use hal::pso; - -use crate::{conv, Backend, PipelineLayout}; - -/// Emit error during shader module creation. Used if we don't expect an error -/// but might panic due to an exception in SPIRV-Cross. -fn gen_unexpected_error(stage: ShaderStage, err: SpirvErrorCode) -> pso::CreationError { - let msg = match err { - SpirvErrorCode::CompilationError(msg) => msg, - SpirvErrorCode::Unhandled => "Unexpected error".into(), - }; - let error = format!("SPIR-V unexpected error {:?}", msg); - pso::CreationError::ShaderCreationError(stage.to_flag(), error) -} - -/// Emit error during shader module creation. Used if we execute an query command. -fn gen_query_error(stage: ShaderStage, err: SpirvErrorCode) -> pso::CreationError { - let msg = match err { - SpirvErrorCode::CompilationError(msg) => msg, - SpirvErrorCode::Unhandled => "Unknown query error".into(), - }; - let error = format!("SPIR-V query error {:?}", msg); - pso::CreationError::ShaderCreationError(stage.to_flag(), error) -} - -/// Introspects the input attributes of given SPIR-V shader and returns an optional vertex semantic remapping. -/// -/// The returned hashmap has attribute location as a key and an Optional remapping to a two part semantic. -/// -/// eg. -/// `2 -> None` means use default semantic `TEXCOORD2` -/// `2 -> Some((0, 2))` means use two part semantic `TEXCOORD0_2`. This is how matrices are represented by spirv-cross. -/// -/// This is a temporary workaround for https://github.com/KhronosGroup/SPIRV-Cross/issues/1512. -/// -/// This workaround also exists under the same name in the DX12 backend. -pub(crate) fn introspect_spirv_vertex_semantic_remapping( - raw_data: &[u32], -) -> Result>, pso::CreationError> { - const SHADER_STAGE: ShaderStage = ShaderStage::Vertex; - // This is inefficient as we already parse it once before. This is a temporary workaround only called - // on vertex shaders. If this becomes permanent or shows up in profiles, deduplicate these as first course of action. - let ast = parse_spirv(SHADER_STAGE, raw_data)?; - - let mut map = auxil::FastHashMap::default(); - - let inputs = ast - .get_shader_resources() - .map_err(|err| gen_query_error(SHADER_STAGE, err))? - .stage_inputs; - for input in inputs { - let idx = ast - .get_decoration(input.id, spirv::Decoration::Location) - .map_err(|err| gen_query_error(SHADER_STAGE, err))?; - - let ty = ast - .get_type(input.type_id) - .map_err(|err| gen_query_error(SHADER_STAGE, err))?; - - match ty { - spirv::Type::Boolean { columns, .. } - | spirv::Type::Int { columns, .. } - | spirv::Type::UInt { columns, .. } - | spirv::Type::Half { columns, .. } - | spirv::Type::Float { columns, .. } - | spirv::Type::Double { columns, .. } - if columns > 1 => - { - for col in 0..columns { - if let Some(_) = map.insert(idx + col, Some((idx, col))) { - let error = format!( - "Shader has overlapping input attachments at location {}", - idx - ); - return Err(pso::CreationError::ShaderCreationError( - SHADER_STAGE.to_flag(), - error, - )); - } - } - } - _ => { - if let Some(_) = map.insert(idx, None) { - let error = format!( - "Shader has overlapping input attachments at location {}", - idx - ); - return Err(pso::CreationError::ShaderCreationError( - SHADER_STAGE.to_flag(), - error, - )); - } - } - } - } - - Ok(map) -} - -pub(crate) fn compile_spirv_entrypoint( - raw_data: &[u32], - stage: ShaderStage, - source: &pso::EntryPoint, - layout: &PipelineLayout, - features: &hal::Features, - device_feature_level: u32, -) -> Result>, pso::CreationError> { - let mut ast = parse_spirv(stage, raw_data)?; - spirv_cross_specialize_ast(&mut ast, &source.specialization) - .map_err(pso::CreationError::InvalidSpecialization)?; - - patch_spirv_resources(&mut ast, stage, layout)?; - let shader_model = conv::map_feature_level_to_shader_model(device_feature_level); - let shader_code = translate_spirv( - &mut ast, - shader_model, - layout, - stage, - features, - source.entry, - )?; - log::debug!("Generated {:?} shader:\n{}", stage, shader_code,); - - let real_name = ast - .get_cleansed_entry_point_name(source.entry, conv::map_stage(stage)) - .map_err(|err| gen_query_error(stage, err))?; - - let shader = compile_hlsl_shader(stage, shader_model, &real_name, shader_code.as_bytes())?; - Ok(Some(unsafe { ComPtr::from_raw(shader) })) -} - -pub(crate) fn compile_hlsl_shader( - stage: ShaderStage, - shader_model: hlsl::ShaderModel, - entry: &str, - code: &[u8], -) -> Result<*mut d3dcommon::ID3DBlob, pso::CreationError> { - let stage_str = { - let stage = match stage { - ShaderStage::Vertex => "vs", - ShaderStage::Fragment => "ps", - ShaderStage::Compute => "cs", - _ => unimplemented!(), - }; - - let model = match shader_model { - hlsl::ShaderModel::V4_0L9_1 => "4_0_level_9_1", - hlsl::ShaderModel::V4_0L9_3 => "4_0_level_9_3", - hlsl::ShaderModel::V4_0 => "4_0", - hlsl::ShaderModel::V4_1 => "4_1", - hlsl::ShaderModel::V5_0 => "5_0", - // TODO: >= 11.3 - hlsl::ShaderModel::V5_1 => "5_1", - // TODO: >= 12?, no mention of 11 on msdn - hlsl::ShaderModel::V6_0 => "6_0", - _ => unimplemented!(), - }; - - format!("{}_{}\0", stage, model) - }; - - let mut blob = ptr::null_mut(); - let mut error = ptr::null_mut(); - let entry = ffi::CString::new(entry).unwrap(); - let hr = unsafe { - d3dcompiler::D3DCompile( - code.as_ptr() as *const _, - code.len(), - ptr::null(), - ptr::null(), - ptr::null_mut(), - entry.as_ptr() as *const _, - stage_str.as_ptr() as *const i8, - 1, - 0, - &mut blob as *mut *mut _, - &mut error as *mut *mut _, - ) - }; - - if !winerror::SUCCEEDED(hr) { - let error = unsafe { ComPtr::::from_raw(error) }; - let message = unsafe { - let pointer = error.GetBufferPointer(); - let size = error.GetBufferSize(); - let slice = slice::from_raw_parts(pointer as *const u8, size as usize); - String::from_utf8_lossy(slice).into_owned() - }; - error!("D3DCompile error {:x}: {}", hr, message); - Err(pso::CreationError::ShaderCreationError( - stage.to_flag(), - message, - )) - } else { - Ok(blob) - } -} - -fn parse_spirv( - stage: ShaderStage, - raw_data: &[u32], -) -> Result, pso::CreationError> { - let module = spirv::Module::from_words(raw_data); - - match spirv::Ast::parse(&module) { - Ok(ast) => Ok(ast), - Err(err) => { - let msg = match err { - SpirvErrorCode::CompilationError(msg) => msg, - SpirvErrorCode::Unhandled => "Unknown parsing error".into(), - }; - let error = format!("SPIR-V parsing failed: {:?}", msg); - Err(pso::CreationError::ShaderCreationError( - stage.to_flag(), - error, - )) - } - } -} - -fn patch_spirv_resources( - ast: &mut spirv::Ast, - stage: ShaderStage, - layout: &PipelineLayout, -) -> Result<(), pso::CreationError> { - // we remap all `layout(binding = n, set = n)` to a flat space which we get from our - // `PipelineLayout` which knows of all descriptor set layouts - - // With multi-entry-point shaders, we will end up with resources in the SPIRV that aren't part of the provided - // pipeline layout. We need to be tolerant of getting out of bounds set and binding indices in the spirv which we can safely ignore. - - let shader_resources = ast - .get_shader_resources() - .map_err(|err| gen_query_error(stage, err))?; - for image in &shader_resources.separate_images { - let set = ast - .get_decoration(image.id, spirv::Decoration::DescriptorSet) - .map_err(|err| gen_query_error(stage, err))? as usize; - let binding = ast - .get_decoration(image.id, spirv::Decoration::Binding) - .map_err(|err| gen_query_error(stage, err))?; - - let set_ref = match layout.sets.get(set) { - Some(set) => set, - None => continue, - }; - - if let Some((_content, res_index)) = set_ref.find_register(stage, binding) { - ast.set_decoration(image.id, spirv::Decoration::Binding, res_index.t as u32) - .map_err(|err| gen_unexpected_error(stage, err))?; - } - } - - for uniform_buffer in &shader_resources.uniform_buffers { - let set = ast - .get_decoration(uniform_buffer.id, spirv::Decoration::DescriptorSet) - .map_err(|err| gen_query_error(stage, err))? as usize; - let binding = ast - .get_decoration(uniform_buffer.id, spirv::Decoration::Binding) - .map_err(|err| gen_query_error(stage, err))?; - - let set_ref = match layout.sets.get(set) { - Some(set) => set, - None => continue, - }; - - if let Some((_content, res_index)) = set_ref.find_register(stage, binding) { - ast.set_decoration( - uniform_buffer.id, - spirv::Decoration::Binding, - res_index.c as u32, - ) - .map_err(|err| gen_unexpected_error(stage, err))?; - } - } - - for storage_buffer in &shader_resources.storage_buffers { - let set = ast - .get_decoration(storage_buffer.id, spirv::Decoration::DescriptorSet) - .map_err(|err| gen_query_error(stage, err))? as usize; - let binding = ast - .get_decoration(storage_buffer.id, spirv::Decoration::Binding) - .map_err(|err| gen_query_error(stage, err))?; - - let set_ref = match layout.sets.get(set) { - Some(set) => set, - None => continue, - }; - - let binding_ref = match set_ref.bindings.get(binding as usize) { - Some(binding) => binding, - None => continue, - }; - - let read_only = match binding_ref.ty { - pso::DescriptorType::Buffer { - ty: pso::BufferDescriptorType::Storage { read_only }, - .. - } => read_only, - _ => unreachable!(), - }; - - let (_content, res_index) = if read_only { - if let Some((_content, res_index)) = set_ref.find_register(stage, binding) { - (_content, res_index) - } else { - continue; - } - } else { - set_ref.find_uav_register(stage, binding) - }; - - // If the binding is read/write, we need to generate a UAV here. - if !read_only { - ast.set_member_decoration(storage_buffer.type_id, 0, spirv::Decoration::NonWritable, 0) - .map_err(|err| gen_unexpected_error(stage, err))?; - } - - let index = if read_only { - res_index.t as u32 - } else if stage == ShaderStage::Compute { - res_index.u as u32 - } else { - d3d11::D3D11_PS_CS_UAV_REGISTER_COUNT - 1 - res_index.u as u32 - }; - - ast.set_decoration(storage_buffer.id, spirv::Decoration::Binding, index) - .map_err(|err| gen_unexpected_error(stage, err))?; - } - - for image in &shader_resources.storage_images { - let set = ast - .get_decoration(image.id, spirv::Decoration::DescriptorSet) - .map_err(|err| gen_query_error(stage, err))? as usize; - let binding = ast - .get_decoration(image.id, spirv::Decoration::Binding) - .map_err(|err| gen_query_error(stage, err))?; - - let set_ref = match layout.sets.get(set) { - Some(set) => set, - None => continue, - }; - - let (_content, res_index) = set_ref.find_uav_register(stage, binding); - - // Read only storage images are generated as UAVs by spirv-cross. - // - // Compute uses bottom up stack, all other stages use top down. - let index = if stage == ShaderStage::Compute { - res_index.u as u32 - } else { - d3d11::D3D11_PS_CS_UAV_REGISTER_COUNT - 1 - res_index.u as u32 - }; - - ast.set_decoration(image.id, spirv::Decoration::Binding, index) - .map_err(|err| gen_unexpected_error(stage, err))?; - } - - for sampler in &shader_resources.separate_samplers { - let set = ast - .get_decoration(sampler.id, spirv::Decoration::DescriptorSet) - .map_err(|err| gen_query_error(stage, err))? as usize; - let binding = ast - .get_decoration(sampler.id, spirv::Decoration::Binding) - .map_err(|err| gen_query_error(stage, err))?; - - let set_ref = match layout.sets.get(set) { - Some(set) => set, - None => continue, - }; - - if let Some((_content, res_index)) = set_ref.find_register(stage, binding) { - ast.set_decoration(sampler.id, spirv::Decoration::Binding, res_index.s as u32) - .map_err(|err| gen_unexpected_error(stage, err))?; - } - } - - for image in &shader_resources.sampled_images { - let set = ast - .get_decoration(image.id, spirv::Decoration::DescriptorSet) - .map_err(|err| gen_query_error(stage, err))? as usize; - let binding = ast - .get_decoration(image.id, spirv::Decoration::Binding) - .map_err(|err| gen_query_error(stage, err))?; - - let set_ref = match layout.sets.get(set) { - Some(set) => set, - None => continue, - }; - - if let Some((_content, res_index)) = set_ref.find_register(stage, binding) { - ast.set_decoration(image.id, spirv::Decoration::Binding, res_index.t as u32) - .map_err(|err| gen_unexpected_error(stage, err))?; - } - } - - assert!( - shader_resources.push_constant_buffers.len() <= 1, - "Only 1 push constant buffer is supported" - ); - for push_constant_buffer in &shader_resources.push_constant_buffers { - ast.set_decoration( - push_constant_buffer.id, - spirv::Decoration::DescriptorSet, - 0, // value doesn't matter, just needs a value - ) - .map_err(|err| gen_unexpected_error(stage, err))?; - ast.set_decoration( - push_constant_buffer.id, - spirv::Decoration::Binding, - d3d11::D3D11_COMMONSHADER_CONSTANT_BUFFER_API_SLOT_COUNT - 1, - ) - .map_err(|err| gen_unexpected_error(stage, err))?; - } - - Ok(()) -} - -fn translate_spirv( - ast: &mut spirv::Ast, - shader_model: hlsl::ShaderModel, - _layout: &PipelineLayout, - stage: ShaderStage, - features: &hal::Features, - entry_point: &str, -) -> Result { - let mut compile_options = hlsl::CompilerOptions::default(); - compile_options.shader_model = shader_model; - compile_options.vertex.invert_y = !features.contains(hal::Features::NDC_Y_UP); - compile_options.force_zero_initialized_variables = true; - compile_options.entry_point = Some((entry_point.to_string(), conv::map_stage(stage))); - - //let stage_flag = stage.into(); - - // TODO: - /*let root_constant_layout = layout - .root_constants - .iter() - .filter_map(|constant| if constant.stages.contains(stage_flag) { - Some(hlsl::RootConstant { - start: constant.range.start * 4, - end: constant.range.end * 4, - binding: constant.range.start, - space: 0, - }) - } else { - None - }) - .collect();*/ - ast.set_compiler_options(&compile_options) - .map_err(|err| gen_unexpected_error(stage, err))?; - //ast.set_root_constant_layout(root_constant_layout) - // .map_err(gen_unexpected_error)?; - ast.compile().map_err(|err| { - let msg = match err { - SpirvErrorCode::CompilationError(msg) => msg, - SpirvErrorCode::Unhandled => "Unknown compile error".into(), - }; - let error = format!("SPIR-V compile failed: {:?}", msg); - pso::CreationError::ShaderCreationError(stage.to_flag(), error) - }) -} +use std::{ffi, ptr, slice}; + +use spirv_cross::{hlsl, spirv, ErrorCode as SpirvErrorCode}; + +use winapi::{ + shared::winerror, + um::{d3d11, d3dcommon, d3dcompiler}, +}; +use wio::com::ComPtr; + +use auxil::{spirv_cross_specialize_ast, ShaderStage}; +use hal::pso; + +use crate::{conv, Backend, PipelineLayout}; + +/// Emit error during shader module creation. Used if we don't expect an error +/// but might panic due to an exception in SPIRV-Cross. +fn gen_unexpected_error(stage: ShaderStage, err: SpirvErrorCode) -> pso::CreationError { + let msg = match err { + SpirvErrorCode::CompilationError(msg) => msg, + SpirvErrorCode::Unhandled => "Unexpected error".into(), + }; + let error = format!("SPIR-V unexpected error {:?}", msg); + pso::CreationError::ShaderCreationError(stage.to_flag(), error) +} + +/// Emit error during shader module creation. Used if we execute an query command. +fn gen_query_error(stage: ShaderStage, err: SpirvErrorCode) -> pso::CreationError { + let msg = match err { + SpirvErrorCode::CompilationError(msg) => msg, + SpirvErrorCode::Unhandled => "Unknown query error".into(), + }; + let error = format!("SPIR-V query error {:?}", msg); + pso::CreationError::ShaderCreationError(stage.to_flag(), error) +} + +/// Introspects the input attributes of given SPIR-V shader and returns an optional vertex semantic remapping. +/// +/// The returned hashmap has attribute location as a key and an Optional remapping to a two part semantic. +/// +/// eg. +/// `2 -> None` means use default semantic `TEXCOORD2` +/// `2 -> Some((0, 2))` means use two part semantic `TEXCOORD0_2`. This is how matrices are represented by spirv-cross. +/// +/// This is a temporary workaround for https://github.com/KhronosGroup/SPIRV-Cross/issues/1512. +/// +/// This workaround also exists under the same name in the DX12 backend. +pub(crate) fn introspect_spirv_vertex_semantic_remapping( + raw_data: &[u32], +) -> Result>, pso::CreationError> { + const SHADER_STAGE: ShaderStage = ShaderStage::Vertex; + // This is inefficient as we already parse it once before. This is a temporary workaround only called + // on vertex shaders. If this becomes permanent or shows up in profiles, deduplicate these as first course of action. + let ast = parse_spirv(SHADER_STAGE, raw_data)?; + + let mut map = auxil::FastHashMap::default(); + + let inputs = ast + .get_shader_resources() + .map_err(|err| gen_query_error(SHADER_STAGE, err))? + .stage_inputs; + for input in inputs { + let idx = ast + .get_decoration(input.id, spirv::Decoration::Location) + .map_err(|err| gen_query_error(SHADER_STAGE, err))?; + + let ty = ast + .get_type(input.type_id) + .map_err(|err| gen_query_error(SHADER_STAGE, err))?; + + match ty { + spirv::Type::Boolean { columns, .. } + | spirv::Type::Int { columns, .. } + | spirv::Type::UInt { columns, .. } + | spirv::Type::Half { columns, .. } + | spirv::Type::Float { columns, .. } + | spirv::Type::Double { columns, .. } + if columns > 1 => + { + for col in 0..columns { + if let Some(_) = map.insert(idx + col, Some((idx, col))) { + let error = format!( + "Shader has overlapping input attachments at location {}", + idx + ); + return Err(pso::CreationError::ShaderCreationError( + SHADER_STAGE.to_flag(), + error, + )); + } + } + } + _ => { + if let Some(_) = map.insert(idx, None) { + let error = format!( + "Shader has overlapping input attachments at location {}", + idx + ); + return Err(pso::CreationError::ShaderCreationError( + SHADER_STAGE.to_flag(), + error, + )); + } + } + } + } + + Ok(map) +} + +pub(crate) fn compile_spirv_entrypoint( + raw_data: &[u32], + stage: ShaderStage, + source: &pso::EntryPoint, + layout: &PipelineLayout, + features: &hal::Features, + device_feature_level: u32, +) -> Result>, pso::CreationError> { + let mut ast = parse_spirv(stage, raw_data)?; + spirv_cross_specialize_ast(&mut ast, &source.specialization) + .map_err(pso::CreationError::InvalidSpecialization)?; + + patch_spirv_resources(&mut ast, stage, layout)?; + let shader_model = conv::map_feature_level_to_shader_model(device_feature_level); + let shader_code = translate_spirv( + &mut ast, + shader_model, + layout, + stage, + features, + source.entry, + )?; + log::debug!("Generated {:?} shader:\n{}", stage, shader_code,); + + let real_name = ast + .get_cleansed_entry_point_name(source.entry, conv::map_stage(stage)) + .map_err(|err| gen_query_error(stage, err))?; + + let shader = compile_hlsl_shader(stage, shader_model, &real_name, shader_code.as_bytes())?; + Ok(Some(unsafe { ComPtr::from_raw(shader) })) +} + +pub(crate) fn compile_hlsl_shader( + stage: ShaderStage, + shader_model: hlsl::ShaderModel, + entry: &str, + code: &[u8], +) -> Result<*mut d3dcommon::ID3DBlob, pso::CreationError> { + let stage_str = { + let stage = match stage { + ShaderStage::Vertex => "vs", + ShaderStage::Fragment => "ps", + ShaderStage::Compute => "cs", + _ => unimplemented!(), + }; + + let model = match shader_model { + hlsl::ShaderModel::V4_0L9_1 => "4_0_level_9_1", + hlsl::ShaderModel::V4_0L9_3 => "4_0_level_9_3", + hlsl::ShaderModel::V4_0 => "4_0", + hlsl::ShaderModel::V4_1 => "4_1", + hlsl::ShaderModel::V5_0 => "5_0", + // TODO: >= 11.3 + hlsl::ShaderModel::V5_1 => "5_1", + // TODO: >= 12?, no mention of 11 on msdn + hlsl::ShaderModel::V6_0 => "6_0", + _ => unimplemented!(), + }; + + format!("{}_{}\0", stage, model) + }; + + let mut blob = ptr::null_mut(); + let mut error = ptr::null_mut(); + let entry = ffi::CString::new(entry).unwrap(); + let hr = unsafe { + d3dcompiler::D3DCompile( + code.as_ptr() as *const _, + code.len(), + ptr::null(), + ptr::null(), + ptr::null_mut(), + entry.as_ptr() as *const _, + stage_str.as_ptr() as *const i8, + 1, + 0, + &mut blob as *mut *mut _, + &mut error as *mut *mut _, + ) + }; + + if !winerror::SUCCEEDED(hr) { + let error = unsafe { ComPtr::::from_raw(error) }; + let message = unsafe { + let pointer = error.GetBufferPointer(); + let size = error.GetBufferSize(); + let slice = slice::from_raw_parts(pointer as *const u8, size as usize); + String::from_utf8_lossy(slice).into_owned() + }; + error!("D3DCompile error {:x}: {}", hr, message); + Err(pso::CreationError::ShaderCreationError( + stage.to_flag(), + message, + )) + } else { + Ok(blob) + } +} + +fn parse_spirv( + stage: ShaderStage, + raw_data: &[u32], +) -> Result, pso::CreationError> { + let module = spirv::Module::from_words(raw_data); + + match spirv::Ast::parse(&module) { + Ok(ast) => Ok(ast), + Err(err) => { + let msg = match err { + SpirvErrorCode::CompilationError(msg) => msg, + SpirvErrorCode::Unhandled => "Unknown parsing error".into(), + }; + let error = format!("SPIR-V parsing failed: {:?}", msg); + Err(pso::CreationError::ShaderCreationError( + stage.to_flag(), + error, + )) + } + } +} + +fn patch_spirv_resources( + ast: &mut spirv::Ast, + stage: ShaderStage, + layout: &PipelineLayout, +) -> Result<(), pso::CreationError> { + // we remap all `layout(binding = n, set = n)` to a flat space which we get from our + // `PipelineLayout` which knows of all descriptor set layouts + + // With multi-entry-point shaders, we will end up with resources in the SPIRV that aren't part of the provided + // pipeline layout. We need to be tolerant of getting out of bounds set and binding indices in the spirv which we can safely ignore. + + let shader_resources = ast + .get_shader_resources() + .map_err(|err| gen_query_error(stage, err))?; + for image in &shader_resources.separate_images { + let set = ast + .get_decoration(image.id, spirv::Decoration::DescriptorSet) + .map_err(|err| gen_query_error(stage, err))? as usize; + let binding = ast + .get_decoration(image.id, spirv::Decoration::Binding) + .map_err(|err| gen_query_error(stage, err))?; + + let set_ref = match layout.sets.get(set) { + Some(set) => set, + None => continue, + }; + + if let Some((_content, res_index)) = set_ref.find_register(stage, binding) { + ast.set_decoration(image.id, spirv::Decoration::Binding, res_index.t as u32) + .map_err(|err| gen_unexpected_error(stage, err))?; + } + } + + for uniform_buffer in &shader_resources.uniform_buffers { + let set = ast + .get_decoration(uniform_buffer.id, spirv::Decoration::DescriptorSet) + .map_err(|err| gen_query_error(stage, err))? as usize; + let binding = ast + .get_decoration(uniform_buffer.id, spirv::Decoration::Binding) + .map_err(|err| gen_query_error(stage, err))?; + + let set_ref = match layout.sets.get(set) { + Some(set) => set, + None => continue, + }; + + if let Some((_content, res_index)) = set_ref.find_register(stage, binding) { + ast.set_decoration( + uniform_buffer.id, + spirv::Decoration::Binding, + res_index.c as u32, + ) + .map_err(|err| gen_unexpected_error(stage, err))?; + } + } + + for storage_buffer in &shader_resources.storage_buffers { + let set = ast + .get_decoration(storage_buffer.id, spirv::Decoration::DescriptorSet) + .map_err(|err| gen_query_error(stage, err))? as usize; + let binding = ast + .get_decoration(storage_buffer.id, spirv::Decoration::Binding) + .map_err(|err| gen_query_error(stage, err))?; + + let set_ref = match layout.sets.get(set) { + Some(set) => set, + None => continue, + }; + + let binding_ref = match set_ref.bindings.get(binding as usize) { + Some(binding) => binding, + None => continue, + }; + + let read_only = match binding_ref.ty { + pso::DescriptorType::Buffer { + ty: pso::BufferDescriptorType::Storage { read_only }, + .. + } => read_only, + _ => unreachable!(), + }; + + let (_content, res_index) = if read_only { + if let Some((_content, res_index)) = set_ref.find_register(stage, binding) { + (_content, res_index) + } else { + continue; + } + } else { + set_ref.find_uav_register(stage, binding) + }; + + // If the binding is read/write, we need to generate a UAV here. + if !read_only { + ast.set_member_decoration(storage_buffer.type_id, 0, spirv::Decoration::NonWritable, 0) + .map_err(|err| gen_unexpected_error(stage, err))?; + } + + let index = if read_only { + res_index.t as u32 + } else if stage == ShaderStage::Compute { + res_index.u as u32 + } else { + d3d11::D3D11_PS_CS_UAV_REGISTER_COUNT - 1 - res_index.u as u32 + }; + + ast.set_decoration(storage_buffer.id, spirv::Decoration::Binding, index) + .map_err(|err| gen_unexpected_error(stage, err))?; + } + + for image in &shader_resources.storage_images { + let set = ast + .get_decoration(image.id, spirv::Decoration::DescriptorSet) + .map_err(|err| gen_query_error(stage, err))? as usize; + let binding = ast + .get_decoration(image.id, spirv::Decoration::Binding) + .map_err(|err| gen_query_error(stage, err))?; + + let set_ref = match layout.sets.get(set) { + Some(set) => set, + None => continue, + }; + + let (_content, res_index) = set_ref.find_uav_register(stage, binding); + + // Read only storage images are generated as UAVs by spirv-cross. + // + // Compute uses bottom up stack, all other stages use top down. + let index = if stage == ShaderStage::Compute { + res_index.u as u32 + } else { + d3d11::D3D11_PS_CS_UAV_REGISTER_COUNT - 1 - res_index.u as u32 + }; + + ast.set_decoration(image.id, spirv::Decoration::Binding, index) + .map_err(|err| gen_unexpected_error(stage, err))?; + } + + for sampler in &shader_resources.separate_samplers { + let set = ast + .get_decoration(sampler.id, spirv::Decoration::DescriptorSet) + .map_err(|err| gen_query_error(stage, err))? as usize; + let binding = ast + .get_decoration(sampler.id, spirv::Decoration::Binding) + .map_err(|err| gen_query_error(stage, err))?; + + let set_ref = match layout.sets.get(set) { + Some(set) => set, + None => continue, + }; + + if let Some((_content, res_index)) = set_ref.find_register(stage, binding) { + ast.set_decoration(sampler.id, spirv::Decoration::Binding, res_index.s as u32) + .map_err(|err| gen_unexpected_error(stage, err))?; + } + } + + for image in &shader_resources.sampled_images { + let set = ast + .get_decoration(image.id, spirv::Decoration::DescriptorSet) + .map_err(|err| gen_query_error(stage, err))? as usize; + let binding = ast + .get_decoration(image.id, spirv::Decoration::Binding) + .map_err(|err| gen_query_error(stage, err))?; + + let set_ref = match layout.sets.get(set) { + Some(set) => set, + None => continue, + }; + + if let Some((_content, res_index)) = set_ref.find_register(stage, binding) { + ast.set_decoration(image.id, spirv::Decoration::Binding, res_index.t as u32) + .map_err(|err| gen_unexpected_error(stage, err))?; + } + } + + assert!( + shader_resources.push_constant_buffers.len() <= 1, + "Only 1 push constant buffer is supported" + ); + for push_constant_buffer in &shader_resources.push_constant_buffers { + ast.set_decoration( + push_constant_buffer.id, + spirv::Decoration::DescriptorSet, + 0, // value doesn't matter, just needs a value + ) + .map_err(|err| gen_unexpected_error(stage, err))?; + ast.set_decoration( + push_constant_buffer.id, + spirv::Decoration::Binding, + d3d11::D3D11_COMMONSHADER_CONSTANT_BUFFER_API_SLOT_COUNT - 1, + ) + .map_err(|err| gen_unexpected_error(stage, err))?; + } + + Ok(()) +} + +fn translate_spirv( + ast: &mut spirv::Ast, + shader_model: hlsl::ShaderModel, + _layout: &PipelineLayout, + stage: ShaderStage, + features: &hal::Features, + entry_point: &str, +) -> Result { + let mut compile_options = hlsl::CompilerOptions::default(); + compile_options.shader_model = shader_model; + compile_options.vertex.invert_y = !features.contains(hal::Features::NDC_Y_UP); + compile_options.force_zero_initialized_variables = true; + compile_options.entry_point = Some((entry_point.to_string(), conv::map_stage(stage))); + + //let stage_flag = stage.into(); + + // TODO: + /*let root_constant_layout = layout + .root_constants + .iter() + .filter_map(|constant| if constant.stages.contains(stage_flag) { + Some(hlsl::RootConstant { + start: constant.range.start * 4, + end: constant.range.end * 4, + binding: constant.range.start, + space: 0, + }) + } else { + None + }) + .collect();*/ + ast.set_compiler_options(&compile_options) + .map_err(|err| gen_unexpected_error(stage, err))?; + //ast.set_root_constant_layout(root_constant_layout) + // .map_err(gen_unexpected_error)?; + ast.compile().map_err(|err| { + let msg = match err { + SpirvErrorCode::CompilationError(msg) => msg, + SpirvErrorCode::Unhandled => "Unknown compile error".into(), + }; + let error = format!("SPIR-V compile failed: {:?}", msg); + pso::CreationError::ShaderCreationError(stage.to_flag(), error) + }) +} diff --git a/third_party/rust/gfx-backend-dx12/.cargo-checksum.json b/third_party/rust/gfx-backend-dx12/.cargo-checksum.json index f9d352e6a38d..778d01e53621 100644 --- a/third_party/rust/gfx-backend-dx12/.cargo-checksum.json +++ b/third_party/rust/gfx-backend-dx12/.cargo-checksum.json @@ -1 +1 @@ -{"files":{"Cargo.toml":"a735bb8cd1bb9ac621a7db1964931bf2c7b17f111f34d98381a4e5ff8c527f81","README.md":"a92efd814ab7708898fd26c7378ac3bacbfa4e670c670d316aaa6c1402a0046c","shaders/blit.hlsl":"1f8819f3a91acf71a69bfd14ccd245180a92a9efb0ab76bf6b2e89aae10f3952","src/command.rs":"7b79e68e4086341ac53170af8081045762109bf8a5447056a895bdc5000a6c1a","src/conv.rs":"b367d52fb4775afd34e266a81a9d0af4a3707b95a6ddf5fa4a9db6cf0e1b1eda","src/descriptors_cpu.rs":"36bb020dac945f817813beecbfb44bda9d0c68ca51a1aa8aa35f661c083692d9","src/device.rs":"c16c6cd1790882e1af1e2ed6fb9abeb25a37b28cd0f21d5d773061282bde48c1","src/internal.rs":"c91b9fcbaa681bda3894f13d459e8c4152afd84accac75b98bf635767e85fe50","src/lib.rs":"2e3171989ffba5fe511000aea5b0334b4b57dfc7ce03385997396256454074e3","src/pool.rs":"f3c55543190e18353eb37088fc6169ea3d9af93cfc79f726c1a619d4649beaa4","src/resource.rs":"3b9cbe723cc72e4e10456d9715969f6a0cbb1b50cfb446701c97f4c517e2eac0","src/root_constants.rs":"9bf4b8cc24ee7ceec0de5f3c293a995ee8e9cf4526ef2b80c95514bf6f76cb2f","src/window.rs":"55015d626f4a2094b10753c169c1bbdece5e8e30869d0f18dae8286168ea4d5c"},"package":"21506399f64a3c4d389182a89a30073856ae33eb712315456b4fd8f39ee7682a"} \ No newline at end of file +{"files":{"Cargo.toml":"a3cb76b75cbb58c8617f333e70ac724177cd093040ade7c76900d885b6deb9b5","README.md":"de987fd4e01bc95d01094ba75140d92b0c34041768616b69e615c0a3b7dd4968","shaders/blit.hlsl":"0c95317cd14b0ae71459d74c3e5658acac0baf8476525d2063c143e300180fe6","src/command.rs":"d22111d9e6ba43e1e9701701645792307d8f31ef89e5d43ada3f08ec209f625b","src/conv.rs":"89b47062783a91ccb5be00453d1a0ece9d0fee521cfd784570922899d22f6ebc","src/descriptors_cpu.rs":"3aea4aa606ddf2240aa96ca8df7d9c531cb41e04f3e9e67690e124d1571b33b8","src/device.rs":"9417c5d388194d8cdb6d373ed335f3d7c9fd403e2fc72ca6a3ff01b3b077856f","src/internal.rs":"6f45623b7c6ca293310d295c87810fe4f2b7343189a88ad92b8d882ffedbe76b","src/lib.rs":"6ecd14730d9d61aeadf133becf182eb15d501725c0f0c36003034a6500f09d7c","src/pool.rs":"8f611e9b1c61ade025d7c787df375c6c927b196ebcffc34b429c978132b71022","src/resource.rs":"461c3ffbc6c0f8f8b78ffc1f8b66358b26b09538bb5cdf215283d25a47af32a2","src/root_constants.rs":"1f74217772e9702a5493e6e6b43c17eaf0462e3f4cee4fa9a5f93318397953f4","src/window.rs":"52e3567332c7cd48d4cf0e0c9877d843ff6dede068334289612871a45c5255b2"},"package":null} \ No newline at end of file diff --git a/third_party/rust/gfx-backend-dx12/Cargo.toml b/third_party/rust/gfx-backend-dx12/Cargo.toml index e05ecf0da5c0..36a8104e6de8 100644 --- a/third_party/rust/gfx-backend-dx12/Cargo.toml +++ b/third_party/rust/gfx-backend-dx12/Cargo.toml @@ -1,83 +1,40 @@ -# THIS FILE IS AUTOMATICALLY GENERATED BY CARGO -# -# When uploading crates to the registry Cargo will automatically -# "normalize" Cargo.toml files for maximal compatibility -# with all versions of Cargo and also rewrite `path` dependencies -# to registry (e.g., crates.io) dependencies -# -# If you believe there's an error in this file please file an -# issue against the rust-lang/cargo repository. If you're -# editing this file be aware that the upstream Cargo.toml -# will likely look very different (and much more reasonable) - [package] -edition = "2018" name = "gfx-backend-dx12" -version = "0.9.1" -authors = ["The Gfx-rs Developers"] +version = "0.8.0" description = "DirectX-12 API backend for gfx-rs" homepage = "https://github.com/gfx-rs/gfx" -documentation = "https://docs.rs/gfx-backend-dx12" -readme = "README.md" +repository = "https://github.com/gfx-rs/gfx" keywords = ["graphics", "gamedev"] license = "MIT OR Apache-2.0" -repository = "https://github.com/gfx-rs/gfx" -[package.metadata.docs.rs] -default-target = "x86_64-pc-windows-msvc" - -[lib] -name = "gfx_backend_dx12" -[dependencies.arrayvec] -version = "0.5" - -[dependencies.auxil] -version = "0.10" -features = ["spirv_cross"] -package = "gfx-auxil" - -[dependencies.bit-set] -version = "0.5" - -[dependencies.bitflags] -version = "1" - -[dependencies.gfx-renderdoc] -version = "0.1.0" - -[dependencies.hal] -version = "0.9" -package = "gfx-hal" - -[dependencies.log] -version = "0.4" - -[dependencies.native] -version = "0.4" -features = ["libloading"] -package = "d3d12" - -[dependencies.parking_lot] -version = "0.11" - -[dependencies.range-alloc] -version = "0.1.1" - -[dependencies.raw-window-handle] -version = "0.3" - -[dependencies.smallvec] -version = "1" - -[dependencies.spirv_cross] -version = "0.23" -features = ["hlsl"] - -[dependencies.thunderdome] -version = "0.4" - -[dependencies.winapi] -version = "0.3" -features = ["basetsd", "d3d12", "d3d12sdklayers", "d3d12shader", "d3dcommon", "d3dcompiler", "dxgi1_2", "dxgi1_3", "dxgi1_4", "dxgi1_5", "dxgi1_6", "dxgidebug", "dxgiformat", "dxgitype", "handleapi", "minwindef", "synchapi", "unknwnbase", "winbase", "windef", "winerror", "winnt", "winuser"] +authors = ["The Gfx-rs Developers"] +readme = "README.md" +documentation = "https://docs.rs/gfx-backend-dx12" +workspace = "../../.." +edition = "2018" [features] default = [] + +[lib] +name = "gfx_backend_dx12" + +[dependencies] +arrayvec = "0.5" +auxil = { path = "../../auxil/auxil", version = "0.9", package = "gfx-auxil", features = ["spirv_cross"] } +hal = { path = "../../hal", version = "0.8", package = "gfx-hal" } +range-alloc = { path = "../../auxil/range-alloc", version = "0.1.1" } +bitflags = "1" +bit-set = "0.5" +native = { package = "d3d12", version = "0.4", features = ["libloading"] } +log = "0.4" +parking_lot = "0.11" +smallvec = "1" +spirv_cross = { version = "0.23", features = ["hlsl"] } +thunderdome = "0.4" +winapi = { version = "0.3", features = ["basetsd","d3d12","d3d12sdklayers","d3d12shader","d3dcommon","d3dcompiler","dxgi1_2","dxgi1_3","dxgi1_4","dxgi1_5","dxgi1_6","dxgidebug","dxgiformat","dxgitype","handleapi","minwindef","synchapi","unknwnbase","winbase","windef","winerror","winnt","winuser"] } +raw-window-handle = "0.3" + +# This forces docs.rs to build the crate on windows, otherwise the build fails +# and we get no docs at all. +[package.metadata.docs.rs] +default-target = "x86_64-pc-windows-msvc" diff --git a/third_party/rust/gfx-backend-dx12/README.md b/third_party/rust/gfx-backend-dx12/README.md index c415c062ad87..c5a931476a0b 100644 --- a/third_party/rust/gfx-backend-dx12/README.md +++ b/third_party/rust/gfx-backend-dx12/README.md @@ -1,19 +1,19 @@ -# gfx-backend-dx12 - -DX12 backend for gfx. - -## Normalized Coordinates - -Render | Depth | Texture --------|-------|-------- -![render_coordinates](../../../info/gl_render_coordinates.png) | ![depth_coordinates](../../../info/dx_depth_coordinates.png) | ![texture_coordinates](../../../info/dx_texture_coordinates.png) - -## Binding Model - -Dimensions of the model: - 1. Space: 0..8 - 2. Binding (tight): constant buffers (CBV), shader resources (SRV), unordered access (UAV), samplers - -## Mirroring - -TODO +# gfx-backend-dx12 + +DX12 backend for gfx. + +## Normalized Coordinates + +Render | Depth | Texture +-------|-------|-------- +![render_coordinates](../../../info/gl_render_coordinates.png) | ![depth_coordinates](../../../info/dx_depth_coordinates.png) | ![texture_coordinates](../../../info/dx_texture_coordinates.png) + +## Binding Model + +Dimensions of the model: + 1. Space: 0..8 + 2. Binding (tight): constant buffers (CBV), shader resources (SRV), unordered access (UAV), samplers + +## Mirroring + +TODO diff --git a/third_party/rust/gfx-backend-dx12/shaders/blit.hlsl b/third_party/rust/gfx-backend-dx12/shaders/blit.hlsl index 40810558a93a..bd152009803c 100644 --- a/third_party/rust/gfx-backend-dx12/shaders/blit.hlsl +++ b/third_party/rust/gfx-backend-dx12/shaders/blit.hlsl @@ -1,29 +1,29 @@ - -Texture2DArray BlitSource : register(t0); -SamplerState BlitSampler : register(s0); - -cbuffer Region : register(b0) { - float2 offset; - float2 extent; - float z; - float level; -}; - -struct VsOutput { - float4 pos: SV_POSITION; - float4 uv: TEXCOORD0; -}; - -// Create a screen filling triangle -VsOutput vs_blit_2d(uint id: SV_VertexID) { - float2 coord = float2((id << 1) & 2, id & 2); - VsOutput output = { - float4(float2(-1.0, 1.0) + coord * float2(2.0, -2.0), 0.0, 1.0), - float4(offset + coord * extent, z, level) - }; - return output; -} - -float4 ps_blit_2d(VsOutput input) : SV_TARGET { - return BlitSource.SampleLevel(BlitSampler, input.uv.xyz, input.uv.w); -} + +Texture2DArray BlitSource : register(t0); +SamplerState BlitSampler : register(s0); + +cbuffer Region : register(b0) { + float2 offset; + float2 extent; + float z; + float level; +}; + +struct VsOutput { + float4 pos: SV_POSITION; + float4 uv: TEXCOORD0; +}; + +// Create a screen filling triangle +VsOutput vs_blit_2d(uint id: SV_VertexID) { + float2 coord = float2((id << 1) & 2, id & 2); + VsOutput output = { + float4(float2(-1.0, 1.0) + coord * float2(2.0, -2.0), 0.0, 1.0), + float4(offset + coord * extent, z, level) + }; + return output; +} + +float4 ps_blit_2d(VsOutput input) : SV_TARGET { + return BlitSource.SampleLevel(BlitSampler, input.uv.xyz, input.uv.w); +} diff --git a/third_party/rust/gfx-backend-dx12/src/command.rs b/third_party/rust/gfx-backend-dx12/src/command.rs index 717a87b5cd50..3c86af38f588 100644 --- a/third_party/rust/gfx-backend-dx12/src/command.rs +++ b/third_party/rust/gfx-backend-dx12/src/command.rs @@ -1,2841 +1,2803 @@ -use auxil::FastHashMap; -use hal::{ - buffer, command as com, format, format::Aspects, image, memory, pass, pso, query, DrawCount, - IndexCount, IndexType, InstanceCount, TaskCount, VertexCount, VertexOffset, WorkGroupCount, -}; - -use arrayvec::ArrayVec; -use smallvec::SmallVec; -use winapi::{ - ctypes, - shared::{dxgiformat, minwindef, winerror}, - um::{d3d12, d3dcommon}, - Interface, -}; - -use std::{cmp, fmt, iter, mem, ops::Range, ptr, sync::Arc}; - -use crate::{ - conv, descriptors_cpu, device, internal, - pool::{CommandAllocatorIndex, PoolShared}, - resource as r, validate_line_width, Backend, Device, Shared, MAX_DESCRIPTOR_SETS, - MAX_VERTEX_BUFFERS, -}; - -// Fixed size of the root signature. -// Limited by D3D12. -const ROOT_SIGNATURE_SIZE: usize = 64; - -const NULL_VERTEX_BUFFER_VIEW: d3d12::D3D12_VERTEX_BUFFER_VIEW = d3d12::D3D12_VERTEX_BUFFER_VIEW { - BufferLocation: 0, - SizeInBytes: 0, - StrideInBytes: 0, -}; - -fn get_rect(rect: &pso::Rect) -> d3d12::D3D12_RECT { - d3d12::D3D12_RECT { - left: rect.x as i32, - top: rect.y as i32, - right: (rect.x + rect.w) as i32, - bottom: (rect.y + rect.h) as i32, - } -} - -fn div(a: u32, b: u32) -> u32 { - (a + b - 1) / b -} - -fn up_align(x: u32, alignment: u32) -> u32 { - (x + alignment - 1) & !(alignment - 1) -} - -#[derive(Clone, Debug)] -struct AttachmentInfo { - subpass_id: Option, - view: r::ImageView, - clear_value: Option, - stencil_value: Option, -} - -pub struct RenderPassCache { - render_pass: r::RenderPass, - target_rect: d3d12::D3D12_RECT, - attachments: Vec, - num_layers: image::Layer, - has_name: bool, -} - -impl fmt::Debug for RenderPassCache { - fn fmt(&self, fmt: &mut fmt::Formatter) -> fmt::Result { - fmt.write_str("RenderPassCache") - } -} - -#[derive(Debug, Copy, Clone, PartialEq, Eq)] -enum OcclusionQuery { - Binary(minwindef::UINT), - Precise(minwindef::UINT), -} - -#[derive(Debug, Copy, Clone, PartialEq, Eq)] -enum DynamicBuffer { - Cbv, - Srv, - Uav, -} - -/// Strongly-typed root signature element -/// -/// Could be removed for an unsafer variant to occupy less memory -#[derive(Debug, Copy, Clone, PartialEq, Eq)] -enum RootElement { - /// Root constant in the signature - Constant(u32), - /// Descriptor table, storing table offset for the current descriptor heap - TableSrvCbvUav(u32), - /// Descriptor table, storing table offset for the current descriptor heap - TableSampler(u32), - /// Root descriptors take 2 DWORDs in the root signature. - DescriptorDynamic { - buffer: u64, - kind: DynamicBuffer, - }, - DescriptorPlaceholder, - /// Undefined value, implementation specific - Undefined, -} - -/// Virtual data storage for the current root signature memory. -struct UserData { - data: [RootElement; ROOT_SIGNATURE_SIZE], - dirty_mask: u64, -} - -impl Default for UserData { - fn default() -> Self { - UserData { - data: [RootElement::Undefined; ROOT_SIGNATURE_SIZE], - dirty_mask: 0, - } - } -} - -impl fmt::Debug for UserData { - fn fmt(&self, fmt: &mut fmt::Formatter<'_>) -> fmt::Result { - fmt.debug_struct("UserData") - .field("data", &&self.data[..]) - .field("dirty_mask", &self.dirty_mask) - .finish() - } -} - -impl UserData { - /// Write root constant values into the user data, overwriting virtual memory - /// range [offset..offset + data.len()]. Changes are marked as dirty. - fn set_constants(&mut self, offset: usize, data: &[u32]) { - assert!(offset + data.len() <= ROOT_SIGNATURE_SIZE); - // Each root constant occupies one DWORD - for (i, val) in data.iter().enumerate() { - self.data[offset + i] = RootElement::Constant(*val); - self.dirty_mask |= 1u64 << (offset + i); - } - } - - /// Write a SRV/CBV/UAV descriptor table into the user data, overwriting virtual - /// memory range [offset..offset + 1]. Changes are marked as dirty. - fn set_srv_cbv_uav_table(&mut self, offset: usize, table_start: u32) { - assert!(offset < ROOT_SIGNATURE_SIZE); - // A descriptor table occupies one DWORD - self.data[offset] = RootElement::TableSrvCbvUav(table_start); - self.dirty_mask |= 1u64 << offset; - } - - /// Write a sampler descriptor table into the user data, overwriting virtual - /// memory range [offset..offset + 1]. Changes are marked as dirty. - fn set_sampler_table(&mut self, offset: usize, table_start: u32) { - assert!(offset < ROOT_SIGNATURE_SIZE); - // A descriptor table occupies one DWORD - self.data[offset] = RootElement::TableSampler(table_start); - self.dirty_mask |= 1u64 << offset; - } - - /// Write a dynamic buffer root descriptor into the user data, overwriting virtual - /// memory range [offset..offset + 2]. Changes are marked as dirty. - fn set_descriptor_dynamic(&mut self, offset: usize, buffer: u64, kind: DynamicBuffer) { - assert!(offset + 1 < ROOT_SIGNATURE_SIZE); - // A root descriptor occupies two DWORDs - self.data[offset] = RootElement::DescriptorDynamic { buffer, kind }; - self.data[offset + 1] = RootElement::DescriptorPlaceholder; - self.dirty_mask |= 1u64 << offset; - self.dirty_mask |= 1u64 << offset + 1; - } - - fn is_dirty(&self) -> bool { - self.dirty_mask != 0 - } - - fn is_index_dirty(&self, i: u32) -> bool { - ((self.dirty_mask >> i) & 1) == 1 - } - - /// Mark all entries as dirty. - fn dirty_all(&mut self) { - self.dirty_mask = !0; - } - - /// Mark all entries as clear up to the given i. - fn clear_up_to(&mut self, i: u32) { - self.dirty_mask &= !((1 << i) - 1); - } -} - -#[derive(Debug, Default)] -struct PipelineCache { - // Bound pipeline and layout info. - // Changed on bind pipeline calls. - pipeline: Option<(native::PipelineState, Arc)>, - - // Virtualized root signature user data of the shaders - user_data: UserData, - - temp_constants: Vec, - - // Descriptor heap gpu handle offsets - srv_cbv_uav_start: u64, - sampler_start: u64, -} - -impl PipelineCache { - fn bind_descriptor_sets<'a, J>( - &mut self, - layout: &r::PipelineLayout, - first_set: usize, - sets: &[&r::DescriptorSet], - offsets: J, - ) -> [native::DescriptorHeap; 2] - where - J: Iterator, - { - let mut offsets = offsets.map(|offset| offset as u64); - - // 'Global' GPU descriptor heaps. - // All descriptors live in the same heaps. - let (srv_cbv_uav_start, sampler_start, heap_srv_cbv_uav, heap_sampler) = - if let Some(set) = sets.first() { - ( - set.srv_cbv_uav_gpu_start().ptr, - set.sampler_gpu_start().ptr, - set.heap_srv_cbv_uav, - set.heap_samplers, - ) - } else { - return [native::DescriptorHeap::null(); 2]; - }; - - self.srv_cbv_uav_start = srv_cbv_uav_start; - self.sampler_start = sampler_start; - - for (&set, element) in sets.iter().zip(layout.elements[first_set..].iter()) { - let mut root_offset = element.table.offset; - - // Bind CBV/SRC/UAV descriptor tables - if let Some(gpu) = set.first_gpu_view { - assert!(element.table.ty.contains(r::SRV_CBV_UAV)); - // Cast is safe as offset **must** be in u32 range. Unable to - // create heaps with more descriptors. - let table_gpu_offset = (gpu.ptr - srv_cbv_uav_start) as u32; - self.user_data - .set_srv_cbv_uav_table(root_offset, table_gpu_offset); - root_offset += 1; - } - - // Bind Sampler descriptor tables. - if let Some(gpu) = set.first_gpu_sampler { - assert!(element.table.ty.contains(r::SAMPLERS)); - - // Cast is safe as offset **must** be in u32 range. Unable to - // create heaps with more descriptors. - let table_gpu_offset = (gpu.ptr - sampler_start) as u32; - self.user_data - .set_sampler_table(root_offset, table_gpu_offset); - root_offset += 1; - } - - // Bind root descriptors - // TODO: slow, can we move the dynamic descriptors into toplevel somehow during initialization? - // Requires changes then in the descriptor update process. - for binding in &set.binding_infos { - // It's not valid to modify the descriptor sets during recording -> access if safe. - for descriptor in binding.dynamic_descriptors.iter() { - let gpu_offset = descriptor.gpu_buffer_location + offsets.next().unwrap(); - let kind = if descriptor.content.contains(r::DescriptorContent::CBV) { - DynamicBuffer::Cbv - } else if descriptor.content.contains(r::DescriptorContent::SRV) { - DynamicBuffer::Srv - } else if descriptor.content.contains(r::DescriptorContent::UAV) { - DynamicBuffer::Uav - } else { - unreachable!() - }; - self.user_data - .set_descriptor_dynamic(root_offset, gpu_offset, kind); - root_offset += 2; - } - } - } - - [heap_srv_cbv_uav, heap_sampler] - } - - fn flush_user_data( - &mut self, - mut constants_update: F, - mut table_update: G, - mut descriptor_dynamic_update: H, - ) where - F: FnMut(u32, &[u32]), - G: FnMut(u32, d3d12::D3D12_GPU_DESCRIPTOR_HANDLE), - H: FnMut(u32, d3d12::D3D12_GPU_VIRTUAL_ADDRESS, DynamicBuffer), - { - let user_data = &mut self.user_data; - if !user_data.is_dirty() { - return; - } - - let shared = match self.pipeline { - Some((_, ref shared)) => shared, - None => return, - }; - - for (root_index, &root_offset) in shared.parameter_offsets.iter().enumerate() { - if !user_data.is_index_dirty(root_offset) { - continue; - } - match user_data.data[root_offset as usize] { - RootElement::Constant(_) => { - let c = &shared.constants[root_index]; - debug_assert_eq!(root_offset, c.range.start); - self.temp_constants.clear(); - self.temp_constants.extend( - user_data.data[c.range.start as usize..c.range.end as usize] - .iter() - .map(|ud| match *ud { - RootElement::Constant(v) => v, - _ => { - warn!( - "Unset or mismatching root constant at index {:?} ({:?})", - c, ud - ); - 0 - } - }), - ); - constants_update(root_index as u32, &self.temp_constants); - } - RootElement::TableSrvCbvUav(offset) => { - let gpu = d3d12::D3D12_GPU_DESCRIPTOR_HANDLE { - ptr: self.srv_cbv_uav_start + offset as u64, - }; - table_update(root_index as u32, gpu); - } - RootElement::TableSampler(offset) => { - let gpu = d3d12::D3D12_GPU_DESCRIPTOR_HANDLE { - ptr: self.sampler_start + offset as u64, - }; - table_update(root_index as u32, gpu); - } - RootElement::DescriptorDynamic { buffer, kind } => { - debug_assert!(user_data.is_index_dirty(root_offset + 1)); - debug_assert_eq!( - user_data.data[root_offset as usize + 1], - RootElement::DescriptorPlaceholder - ); - - descriptor_dynamic_update(root_index as u32, buffer, kind); - } - RootElement::DescriptorPlaceholder | RootElement::Undefined => { - error!( - "Undefined user data element in the root signature at {}", - root_offset - ); - } - } - } - - user_data.clear_up_to(shared.total_slots); - } -} - -#[derive(Debug, Copy, Clone, PartialEq, Eq)] -enum BindPoint { - Compute, - Graphics { - /// Internal pipelines used for blitting, copying, etc. - internal: bool, - }, -} - -#[derive(Clone, Debug)] -struct Copy { - footprint_offset: u64, - footprint: image::Extent, - row_pitch: u32, - img_subresource: u32, - img_offset: image::Offset, - buf_offset: image::Offset, - copy_extent: image::Extent, -} - -#[derive(Clone, Copy, Debug, PartialEq)] -enum Phase { - Initial, - Recording, - Executable, - // Pending, // not useful, and we don't have mutable access - // to self in `submit()`, so we can't set it anyway. -} - -pub struct CommandBuffer { - //Note: this is going to be NULL instead of `Option` to avoid - // `unwrap()` on every operation. This is not idiomatic. - pub(crate) raw: native::GraphicsCommandList, - //Note: this is NULL when the command buffer is reset, and no - // allocation is used for the `raw` list. - allocator_index: Option, - phase: Phase, - shared: Arc, - pool_shared: Arc, - begin_flags: com::CommandBufferFlags, - - /// Cache renderpasses for graphics operations - pass_cache: Option, - cur_subpass: pass::SubpassId, - - /// Cache current graphics root signature and pipeline to minimize rebinding and support two - /// bindpoints. - gr_pipeline: PipelineCache, - /// Primitive topology of the currently bound graphics pipeline. - /// Caching required for internal graphics pipelines. - primitive_topology: d3d12::D3D12_PRIMITIVE_TOPOLOGY, - /// Cache current compute root signature and pipeline. - comp_pipeline: PipelineCache, - /// D3D12 only has one slot for both bindpoints. Need to rebind everything if we want to switch - /// between different bind points (ie. calling draw or dispatch). - active_bindpoint: BindPoint, - /// Current descriptor heaps heaps (CBV/SRV/UAV and Sampler). - /// Required for resetting due to internal descriptor heaps. - active_descriptor_heaps: [native::DescriptorHeap; 2], - - /// Active queries in the command buffer. - /// Queries must begin and end in the same command buffer, which allows us to track them. - /// The query pool type on `begin_query` must differ from all currently active queries. - /// Therefore, only one query per query type can be active at the same time. Binary and precise - /// occlusion queries share one queue type in Vulkan. - occlusion_query: Option, - pipeline_stats_query: Option, - - /// Cached vertex buffer views to bind. - /// `Stride` values are not known at `bind_vertex_buffers` time because they are only stored - /// inside the pipeline state. - vertex_bindings_remap: [Option; MAX_VERTEX_BUFFERS], - - vertex_buffer_views: [d3d12::D3D12_VERTEX_BUFFER_VIEW; MAX_VERTEX_BUFFERS], - - /// Re-using allocation for the image-buffer copies. - copies: Vec, - - /// D3D12 only allows setting all viewports or all scissors at once, not partial updates. - /// So we must cache the implied state for these partial updates. - viewport_cache: ArrayVec< - [d3d12::D3D12_VIEWPORT; - d3d12::D3D12_VIEWPORT_AND_SCISSORRECT_OBJECT_COUNT_PER_PIPELINE as usize], - >, - scissor_cache: ArrayVec< - [d3d12::D3D12_RECT; - d3d12::D3D12_VIEWPORT_AND_SCISSORRECT_OBJECT_COUNT_PER_PIPELINE as usize], - >, - - /// HACK: renderdoc workaround for temporary RTVs - rtv_pools: Vec, - /// Temporary gpu descriptor heaps (internal). - temporary_gpu_heaps: Vec, - /// Resources that need to be alive till the end of the GPU execution. - retained_resources: Vec, - - /// Temporary wide string for the marker. - temp_marker: Vec, - - /// Temporary transition barriers. - barriers: Vec, - /// Name of the underlying raw `GraphicsCommandList` object. - pub(crate) raw_name: Vec, -} - -impl fmt::Debug for CommandBuffer { - fn fmt(&self, fmt: &mut fmt::Formatter) -> fmt::Result { - fmt.write_str("CommandBuffer") - } -} - -unsafe impl Send for CommandBuffer {} -unsafe impl Sync for CommandBuffer {} - -// Insetion point for subpasses. -enum BarrierPoint { - // Pre barriers are inserted of the beginning when switching into a new subpass. - Pre, - // Post barriers are applied after exectuing the user defined commands. - Post, -} - -impl CommandBuffer { - pub(crate) fn new(shared: &Arc, pool_shared: &Arc) -> Self { - CommandBuffer { - raw: native::GraphicsCommandList::null(), - allocator_index: None, - shared: Arc::clone(shared), - pool_shared: Arc::clone(pool_shared), - phase: Phase::Initial, - begin_flags: com::CommandBufferFlags::empty(), - pass_cache: None, - cur_subpass: !0, - gr_pipeline: PipelineCache::default(), - primitive_topology: d3dcommon::D3D_PRIMITIVE_TOPOLOGY_UNDEFINED, - comp_pipeline: PipelineCache::default(), - active_bindpoint: BindPoint::Graphics { internal: false }, - active_descriptor_heaps: [native::DescriptorHeap::null(); 2], - occlusion_query: None, - pipeline_stats_query: None, - vertex_bindings_remap: [None; MAX_VERTEX_BUFFERS], - vertex_buffer_views: [NULL_VERTEX_BUFFER_VIEW; MAX_VERTEX_BUFFERS], - copies: Vec::new(), - viewport_cache: ArrayVec::new(), - scissor_cache: ArrayVec::new(), - rtv_pools: Vec::new(), - temporary_gpu_heaps: Vec::new(), - retained_resources: Vec::new(), - temp_marker: Vec::new(), - barriers: Vec::new(), - raw_name: Vec::new(), - } - } - - pub(crate) unsafe fn destroy( - self, - ) -> Option<(CommandAllocatorIndex, Option)> { - let list = match self.phase { - Phase::Initial => None, - Phase::Recording => { - self.raw.close(); - Some(self.raw) - } - Phase::Executable => Some(self.raw), - }; - for heap in &self.rtv_pools { - heap.destroy(); - } - for heap in &self.temporary_gpu_heaps { - heap.destroy(); - } - for resource in &self.retained_resources { - resource.destroy(); - } - self.allocator_index.map(|index| (index, list)) - } - - pub(crate) unsafe fn as_raw_list(&self) -> *mut d3d12::ID3D12CommandList { - match self.phase { - Phase::Executable => (), - other => error!("Submitting a command buffer in {:?} state", other), - } - self.raw.as_mut_ptr() as *mut _ - } - - // Indicates that the pipeline slot has been overriden with an internal pipeline. - // - // This only invalidates the slot and the user data! - fn set_internal_graphics_pipeline(&mut self) { - self.active_bindpoint = BindPoint::Graphics { internal: true }; - self.gr_pipeline.user_data.dirty_all(); - } - - fn bind_descriptor_heaps(&mut self) { - self.raw.set_descriptor_heaps(&self.active_descriptor_heaps); - } - - fn mark_bound_descriptor(&mut self, index: usize, set: &r::DescriptorSet) { - if !set.raw_name.is_empty() { - self.temp_marker.clear(); - self.temp_marker.push(0x20); // ' ' - self.temp_marker.push(0x30 + index as u16); // '1..9' - self.temp_marker.push(0x3A); // ':' - self.temp_marker.push(0x20); // ' ' - self.temp_marker.extend_from_slice(&set.raw_name); - unsafe { - self.raw.SetMarker( - 0, - self.temp_marker.as_ptr() as *const _, - self.temp_marker.len() as u32 * 2, - ) - }; - } - } - - unsafe fn insert_subpass_barriers(&mut self, insertion: BarrierPoint) { - let state = self.pass_cache.as_ref().unwrap(); - let proto_barriers = match state.render_pass.subpasses.get(self.cur_subpass as usize) { - Some(subpass) => match insertion { - BarrierPoint::Pre => &subpass.pre_barriers, - BarrierPoint::Post => &subpass.post_barriers, - }, - None => &state.render_pass.post_barriers, - }; - - self.barriers.clear(); - for barrier in proto_barriers { - let mut resource_barrier = d3d12::D3D12_RESOURCE_BARRIER { - Type: d3d12::D3D12_RESOURCE_BARRIER_TYPE_TRANSITION, - Flags: barrier.flags, - u: mem::zeroed(), - }; - - *resource_barrier.u.Transition_mut() = d3d12::D3D12_RESOURCE_TRANSITION_BARRIER { - pResource: state.attachments[barrier.attachment_id] - .view - .resource - .as_mut_ptr(), - Subresource: d3d12::D3D12_RESOURCE_BARRIER_ALL_SUBRESOURCES, - StateBefore: barrier.states.start, - StateAfter: barrier.states.end, - }; - - self.barriers.push(resource_barrier); - } - - self.flush_barriers(); - } - - fn bind_targets(&mut self) { - let state = self.pass_cache.as_ref().unwrap(); - let subpass = &state.render_pass.subpasses[self.cur_subpass as usize]; - - // collect render targets - let color_views = subpass - .color_attachments - .iter() - .map(|&(id, _)| state.attachments[id].view.handle_rtv.raw().unwrap()) - .collect::>(); - let ds_view = match subpass.depth_stencil_attachment { - Some((id, _)) => state.attachments[id] - .view - .handle_dsv - .as_ref() - .map(|handle| &handle.raw) - .unwrap() as *const _, - None => ptr::null(), - }; - // set render targets - unsafe { - self.raw.OMSetRenderTargets( - color_views.len() as _, - color_views.as_ptr(), - minwindef::FALSE, - ds_view, - ); - } - - // performs clears for all the attachments first used in this subpass - for at in state.attachments.iter() { - if at.subpass_id != Some(self.cur_subpass) { - continue; - } - - if let (Some(rtv), Some(cv)) = (at.view.handle_rtv.raw(), at.clear_value) { - self.clear_render_target_view(rtv, unsafe { cv.color }, &[state.target_rect]); - } - - if let Some(handle) = at.view.handle_dsv { - let depth = at.clear_value.map(|cv| unsafe { cv.depth_stencil.depth }); - let stencil = at.stencil_value; - - if depth.is_some() || stencil.is_some() { - self.clear_depth_stencil_view(handle.raw, depth, stencil, &[state.target_rect]); - } - } - } - } - - fn resolve_attachments(&self) { - let state = self.pass_cache.as_ref().unwrap(); - let subpass = &state.render_pass.subpasses[self.cur_subpass as usize]; - - for (&(src_attachment, _), &(dst_attachment, _)) in subpass - .color_attachments - .iter() - .zip(subpass.resolve_attachments.iter()) - { - if dst_attachment == pass::ATTACHMENT_UNUSED { - continue; - } - - let resolve_src = &state.attachments[src_attachment].view; - let resolve_dst = &state.attachments[dst_attachment].view; - - // The number of layers of the render area are given on framebuffer creation. - for l in 0..state.num_layers { - // Attachtments only have a single mip level by specification. - let subresource_src = resolve_src.calc_subresource( - resolve_src.mip_levels.0 as _, - (resolve_src.layers.0 + l) as _, - ); - let subresource_dst = resolve_dst.calc_subresource( - resolve_dst.mip_levels.0 as _, - (resolve_dst.layers.0 + l) as _, - ); - - // TODO: take width and height of render area into account. - unsafe { - self.raw.ResolveSubresource( - resolve_dst.resource.as_mut_ptr(), - subresource_dst, - resolve_src.resource.as_mut_ptr(), - subresource_src, - resolve_dst.dxgi_format, - ); - } - } - } - } - - fn clear_render_target_view( - &self, - rtv: d3d12::D3D12_CPU_DESCRIPTOR_HANDLE, - color: com::ClearColor, - rects: &[d3d12::D3D12_RECT], - ) { - let num_rects = rects.len() as _; - let rects = if num_rects > 0 { - rects.as_ptr() - } else { - ptr::null() - }; - - unsafe { - self.raw - .clone() - .ClearRenderTargetView(rtv, &color.float32, num_rects, rects); - } - } - - fn clear_depth_stencil_view( - &self, - dsv: d3d12::D3D12_CPU_DESCRIPTOR_HANDLE, - depth: Option, - stencil: Option, - rects: &[d3d12::D3D12_RECT], - ) { - let mut flags = native::ClearFlags::empty(); - if depth.is_some() { - flags |= native::ClearFlags::DEPTH; - } - if stencil.is_some() { - flags |= native::ClearFlags::STENCIL; - } - - self.raw.clear_depth_stencil_view( - dsv, - flags, - depth.unwrap_or_default(), - stencil.unwrap_or_default() as _, - rects, - ); - } - - fn set_graphics_bind_point(&mut self) { - match self.active_bindpoint { - BindPoint::Compute => { - // Switch to graphics bind point - let &(pipeline, _) = self - .gr_pipeline - .pipeline - .as_ref() - .expect("No graphics pipeline bound"); - self.raw.set_pipeline_state(pipeline); - } - BindPoint::Graphics { internal: true } => { - // Switch to graphics bind point - let &(pipeline, ref shared) = self - .gr_pipeline - .pipeline - .as_ref() - .expect("No graphics pipeline bound"); - self.raw.set_pipeline_state(pipeline); - self.raw.set_graphics_root_signature(shared.signature); - self.bind_descriptor_heaps(); - } - BindPoint::Graphics { internal: false } => {} - } - - self.active_bindpoint = BindPoint::Graphics { internal: false }; - let cmd_buffer = &mut self.raw; - - // Flush root signature data - self.gr_pipeline.flush_user_data( - |slot, data| unsafe { - cmd_buffer.clone().SetGraphicsRoot32BitConstants( - slot, - data.len() as _, - data.as_ptr() as *const _, - 0, - ) - }, - |slot, gpu| cmd_buffer.set_graphics_root_descriptor_table(slot, gpu), - |slot, buffer, kind| match kind { - DynamicBuffer::Cbv => { - cmd_buffer.set_graphics_root_constant_buffer_view(slot, buffer) - } - DynamicBuffer::Srv => { - cmd_buffer.set_graphics_root_shader_resource_view(slot, buffer) - } - DynamicBuffer::Uav => { - cmd_buffer.set_graphics_root_unordered_access_view(slot, buffer) - } - }, - ); - } - - fn set_compute_bind_point(&mut self) { - match self.active_bindpoint { - BindPoint::Graphics { internal } => { - // Switch to compute bind point - let &(pipeline, _) = self - .comp_pipeline - .pipeline - .as_ref() - .expect("No compute pipeline bound"); - - self.raw.set_pipeline_state(pipeline); - - self.active_bindpoint = BindPoint::Compute; - - if internal { - self.bind_descriptor_heaps(); - // Rebind the graphics root signature as we come from an internal graphics. - // Issuing a draw call afterwards would hide the information that we internally - // changed the graphics root signature. - if let Some((_, ref shared)) = self.gr_pipeline.pipeline { - self.raw.set_graphics_root_signature(shared.signature); - } - } - } - BindPoint::Compute => {} // Nothing to do - } - - let cmd_buffer = &mut self.raw; - self.comp_pipeline.flush_user_data( - |slot, data| unsafe { - cmd_buffer.clone().SetComputeRoot32BitConstants( - slot, - data.len() as _, - data.as_ptr() as *const _, - 0, - ) - }, - |slot, gpu| cmd_buffer.set_compute_root_descriptor_table(slot, gpu), - |slot, buffer, kind| match kind { - DynamicBuffer::Cbv => { - cmd_buffer.set_compute_root_constant_buffer_view(slot, buffer) - } - DynamicBuffer::Srv => { - cmd_buffer.set_compute_root_shader_resource_view(slot, buffer) - } - DynamicBuffer::Uav => { - cmd_buffer.set_compute_root_unordered_access_view(slot, buffer) - } - }, - ); - } - - fn transition_barrier( - transition: d3d12::D3D12_RESOURCE_TRANSITION_BARRIER, - ) -> d3d12::D3D12_RESOURCE_BARRIER { - let mut barrier = d3d12::D3D12_RESOURCE_BARRIER { - Type: d3d12::D3D12_RESOURCE_BARRIER_TYPE_TRANSITION, - Flags: d3d12::D3D12_RESOURCE_BARRIER_FLAG_NONE, - u: unsafe { mem::zeroed() }, - }; - - *unsafe { barrier.u.Transition_mut() } = transition; - barrier - } - - fn dual_transition_barriers( - resource: native::Resource, - sub: u32, - states: Range, - ) -> (d3d12::D3D12_RESOURCE_BARRIER, d3d12::D3D12_RESOURCE_BARRIER) { - ( - Self::transition_barrier(d3d12::D3D12_RESOURCE_TRANSITION_BARRIER { - pResource: resource.as_mut_ptr(), - Subresource: sub, - StateBefore: states.start, - StateAfter: states.end, - }), - Self::transition_barrier(d3d12::D3D12_RESOURCE_TRANSITION_BARRIER { - pResource: resource.as_mut_ptr(), - Subresource: sub, - StateBefore: states.end, - StateAfter: states.start, - }), - ) - } - - fn split_buffer_copy(copies: &mut Vec, r: com::BufferImageCopy, image: &r::ImageBound) { - let buffer_width = if r.buffer_width == 0 { - r.image_extent.width - } else { - r.buffer_width - }; - let buffer_height = if r.buffer_height == 0 { - r.image_extent.height - } else { - r.buffer_height - }; - let desc = image.surface_type.desc(); - let bytes_per_block = desc.bits as u32 / 8; - let image_extent_aligned = image::Extent { - width: up_align(r.image_extent.width, desc.dim.0 as _), - height: up_align(r.image_extent.height, desc.dim.1 as _), - depth: r.image_extent.depth, - }; - let row_pitch = div(buffer_width, desc.dim.0 as _) * bytes_per_block; - let slice_pitch = div(buffer_height, desc.dim.1 as _) * row_pitch; - let is_pitch_aligned = row_pitch % d3d12::D3D12_TEXTURE_DATA_PITCH_ALIGNMENT == 0; - - for layer in r.image_layers.layers.clone() { - let img_subresource = image.calc_subresource(r.image_layers.level as _, layer as _, 0); - let layer_relative = (layer - r.image_layers.layers.start) as u32; - let layer_offset = r.buffer_offset as u64 - + (layer_relative * slice_pitch * r.image_extent.depth) as u64; - let aligned_offset = - layer_offset & !(d3d12::D3D12_TEXTURE_DATA_PLACEMENT_ALIGNMENT as u64 - 1); - if layer_offset == aligned_offset && is_pitch_aligned { - // trivial case: everything is aligned, ready for copying - copies.push(Copy { - footprint_offset: aligned_offset, - footprint: image_extent_aligned, - row_pitch, - img_subresource, - img_offset: r.image_offset, - buf_offset: image::Offset::ZERO, - copy_extent: image_extent_aligned, - }); - } else if is_pitch_aligned { - // buffer offset is not aligned - let row_pitch_texels = row_pitch / bytes_per_block * desc.dim.0 as u32; - let gap = (layer_offset - aligned_offset) as i32; - let buf_offset = image::Offset { - x: (gap % row_pitch as i32) / bytes_per_block as i32 * desc.dim.0 as i32, - y: (gap % slice_pitch as i32) / row_pitch as i32 * desc.dim.1 as i32, - z: gap / slice_pitch as i32, - }; - let footprint = image::Extent { - width: buf_offset.x as u32 + image_extent_aligned.width, - height: buf_offset.y as u32 + image_extent_aligned.height, - depth: buf_offset.z as u32 + image_extent_aligned.depth, - }; - if r.image_extent.width + buf_offset.x as u32 <= row_pitch_texels { - // we can map it to the aligned one and adjust the offsets accordingly - copies.push(Copy { - footprint_offset: aligned_offset, - footprint, - row_pitch, - img_subresource, - img_offset: r.image_offset, - buf_offset, - copy_extent: image_extent_aligned, - }); - } else { - // split the copy region into 2 that suffice the previous condition - assert!(buf_offset.x as u32 <= row_pitch_texels); - let half = row_pitch_texels - buf_offset.x as u32; - assert!(half <= r.image_extent.width); - - copies.push(Copy { - footprint_offset: aligned_offset, - footprint: image::Extent { - width: row_pitch_texels, - ..footprint - }, - row_pitch, - img_subresource, - img_offset: r.image_offset, - buf_offset, - copy_extent: image::Extent { - width: half, - ..r.image_extent - }, - }); - copies.push(Copy { - footprint_offset: aligned_offset, - footprint: image::Extent { - width: image_extent_aligned.width - half, - height: footprint.height + desc.dim.1 as u32, - depth: footprint.depth, - }, - row_pitch, - img_subresource, - img_offset: image::Offset { - x: r.image_offset.x + half as i32, - ..r.image_offset - }, - buf_offset: image::Offset { - x: 0, - y: buf_offset.y + desc.dim.1 as i32, - z: buf_offset.z, - }, - copy_extent: image::Extent { - width: image_extent_aligned.width - half, - ..image_extent_aligned - }, - }); - } - } else { - // worst case: row by row copy - for z in 0..r.image_extent.depth { - for y in 0..image_extent_aligned.height / desc.dim.1 as u32 { - // an image row starts non-aligned - let row_offset = layer_offset - + z as u64 * slice_pitch as u64 - + y as u64 * row_pitch as u64; - let aligned_offset = row_offset - & !(d3d12::D3D12_TEXTURE_DATA_PLACEMENT_ALIGNMENT as u64 - 1); - let next_aligned_offset = - aligned_offset + d3d12::D3D12_TEXTURE_DATA_PLACEMENT_ALIGNMENT as u64; - let cut_row_texels = (next_aligned_offset - row_offset) - / bytes_per_block as u64 - * desc.dim.0 as u64; - let cut_width = - cmp::min(image_extent_aligned.width, cut_row_texels as image::Size); - let gap_texels = (row_offset - aligned_offset) as image::Size - / bytes_per_block as image::Size - * desc.dim.0 as image::Size; - // this is a conservative row pitch that should be compatible with both copies - let max_unaligned_pitch = - (r.image_extent.width + gap_texels) * bytes_per_block; - let row_pitch = (max_unaligned_pitch - | (d3d12::D3D12_TEXTURE_DATA_PITCH_ALIGNMENT - 1)) - + 1; - - copies.push(Copy { - footprint_offset: aligned_offset, - footprint: image::Extent { - width: cut_width + gap_texels, - height: desc.dim.1 as _, - depth: 1, - }, - row_pitch, - img_subresource, - img_offset: image::Offset { - x: r.image_offset.x, - y: r.image_offset.y + desc.dim.1 as i32 * y as i32, - z: r.image_offset.z + z as i32, - }, - buf_offset: image::Offset { - x: gap_texels as i32, - y: 0, - z: 0, - }, - copy_extent: image::Extent { - width: cut_width, - height: desc.dim.1 as _, - depth: 1, - }, - }); - - // and if it crosses a pitch alignment - we copy the rest separately - if cut_width >= image_extent_aligned.width { - continue; - } - let leftover = image_extent_aligned.width - cut_width; - - copies.push(Copy { - footprint_offset: next_aligned_offset, - footprint: image::Extent { - width: leftover, - height: desc.dim.1 as _, - depth: 1, - }, - row_pitch, - img_subresource, - img_offset: image::Offset { - x: r.image_offset.x + cut_width as i32, - y: r.image_offset.y + y as i32 * desc.dim.1 as i32, - z: r.image_offset.z + z as i32, - }, - buf_offset: image::Offset::ZERO, - copy_extent: image::Extent { - width: leftover, - height: desc.dim.1 as _, - depth: 1, - }, - }); - } - } - } - } - } - - fn set_vertex_buffers(&mut self) { - let cmd_buffer = &mut self.raw; - let vbs_remap = &self.vertex_bindings_remap; - let vbs = &self.vertex_buffer_views; - let mut last_end_slot = 0; - loop { - let start_offset = match vbs_remap[last_end_slot..] - .iter() - .position(|remap| remap.is_some()) - { - Some(offset) => offset, - None => break, - }; - - let start_slot = last_end_slot + start_offset; - let buffers = vbs_remap[start_slot..] - .iter() - .take_while(|x| x.is_some()) - .filter_map(|mapping| { - let mapping = mapping.unwrap(); - let view = vbs[mapping.mapped_binding]; - // Skip bindings that don't make sense. Since this function is called eagerly, - // we expect it to be called with all the valid inputs prior to drawing. - view.SizeInBytes.checked_sub(mapping.offset).map(|size| { - d3d12::D3D12_VERTEX_BUFFER_VIEW { - BufferLocation: view.BufferLocation + mapping.offset as u64, - SizeInBytes: size, - StrideInBytes: mapping.stride, - } - }) - }) - .collect::>(); - - if buffers.is_empty() { - last_end_slot = start_slot + 1; - } else { - let num_views = buffers.len(); - unsafe { - cmd_buffer.IASetVertexBuffers( - start_slot as _, - num_views as _, - buffers.as_ptr(), - ); - } - last_end_slot = start_slot + num_views; - } - } - } - - fn flip_barriers(&mut self) { - for barrier in self.barriers.iter_mut() { - if barrier.Type == d3d12::D3D12_RESOURCE_BARRIER_TYPE_TRANSITION { - let transition = unsafe { barrier.u.Transition_mut() }; - mem::swap(&mut transition.StateBefore, &mut transition.StateAfter); - } - } - } - - fn _set_buffer_barrier( - &mut self, - target: &r::BufferBound, - state: d3d12::D3D12_RESOURCE_STATES, - ) { - let barrier = Self::transition_barrier(d3d12::D3D12_RESOURCE_TRANSITION_BARRIER { - pResource: target.resource.as_mut_ptr(), - Subresource: d3d12::D3D12_RESOURCE_BARRIER_ALL_SUBRESOURCES, - StateBefore: d3d12::D3D12_RESOURCE_STATE_COMMON, - StateAfter: state, - }); - self.barriers.clear(); - self.barriers.push(barrier); - } - - fn fill_texture_barries( - &mut self, - target: &r::ImageBound, - states: Range, - range: &image::SubresourceRange, - ) { - if states.start == states.end { - return; - } - - let mut bar = Self::transition_barrier(d3d12::D3D12_RESOURCE_TRANSITION_BARRIER { - pResource: target.resource.as_mut_ptr(), - Subresource: d3d12::D3D12_RESOURCE_BARRIER_ALL_SUBRESOURCES, - StateBefore: states.start, - StateAfter: states.end, - }); - let full_range = image::SubresourceRange { - aspects: range.aspects, - ..Default::default() - }; - let num_levels = range.resolve_level_count(target.mip_levels); - let num_layers = range.resolve_layer_count(target.kind.num_layers()); - - if *range == full_range { - // Only one barrier if it affects the whole image. - self.barriers.push(bar); - } else { - // Generate barrier for each layer/level combination. - for rel_level in 0..num_levels { - for rel_layer in 0..num_layers { - unsafe { - let transition_barrier = &mut *bar.u.Transition_mut(); - transition_barrier.Subresource = target.calc_subresource( - (range.level_start + rel_level) as _, - (range.layer_start + rel_layer) as _, - 0, - ); - } - self.barriers.push(bar); - } - } - } - } - - fn fill_marker(&mut self, name: &str) -> (*const ctypes::c_void, u32) { - self.temp_marker.clear(); - self.temp_marker.extend(name.encode_utf16()); - self.temp_marker.push(0); - ( - self.temp_marker.as_ptr() as *const _, - self.temp_marker.len() as u32 * 2, - ) - } - - unsafe fn flush_barriers(&self) { - if !self.barriers.is_empty() { - self.raw - .ResourceBarrier(self.barriers.len() as _, self.barriers.as_ptr()); - } - } -} - -impl com::CommandBuffer for CommandBuffer { - unsafe fn begin( - &mut self, - flags: com::CommandBufferFlags, - _info: com::CommandBufferInheritanceInfo, - ) { - // TODO: Implement flags and secondary command buffers (bundles). - // Note: we need to be ready for a situation where the whole - // command pool was reset. - self.reset(true); - self.phase = Phase::Recording; - self.begin_flags = flags; - let (allocator_index, list) = self.pool_shared.acquire(); - - assert!(self.allocator_index.is_none()); - assert_eq!(self.raw, native::GraphicsCommandList::null()); - self.allocator_index = Some(allocator_index); - self.raw = list; - - if !self.raw_name.is_empty() { - self.raw.SetName(self.raw_name.as_ptr()); - } - } - - unsafe fn finish(&mut self) { - self.raw.close(); - assert_eq!(self.phase, Phase::Recording); - self.phase = Phase::Executable; - self.pool_shared - .release_allocator(self.allocator_index.unwrap()); - } - - unsafe fn reset(&mut self, _release_resources: bool) { - if self.phase == Phase::Recording { - self.raw.close(); - self.pool_shared - .release_allocator(self.allocator_index.unwrap()); - } - if self.phase != Phase::Initial { - // Reset the name so it won't get used later for an unnamed `CommandBuffer`. - const EMPTY_NAME: u16 = 0; - self.raw.SetName(&EMPTY_NAME); - - let allocator_index = self.allocator_index.take().unwrap(); - self.pool_shared.release_list(self.raw, allocator_index); - self.raw = native::GraphicsCommandList::null(); - } - self.phase = Phase::Initial; - - self.pass_cache = None; - self.cur_subpass = !0; - self.gr_pipeline = PipelineCache::default(); - self.primitive_topology = d3dcommon::D3D_PRIMITIVE_TOPOLOGY_UNDEFINED; - self.comp_pipeline = PipelineCache::default(); - self.active_bindpoint = BindPoint::Graphics { internal: false }; - self.active_descriptor_heaps = [native::DescriptorHeap::null(); 2]; - self.occlusion_query = None; - self.pipeline_stats_query = None; - self.vertex_bindings_remap = [None; MAX_VERTEX_BUFFERS]; - self.vertex_buffer_views = [NULL_VERTEX_BUFFER_VIEW; MAX_VERTEX_BUFFERS]; - for heap in self.rtv_pools.drain(..) { - heap.destroy(); - } - for heap in self.temporary_gpu_heaps.drain(..) { - heap.destroy(); - } - for resource in self.retained_resources.drain(..) { - resource.destroy(); - } - } - - unsafe fn begin_render_pass<'a, T>( - &mut self, - render_pass: &r::RenderPass, - framebuffer: &r::Framebuffer, - target_rect: pso::Rect, - attachment_infos: T, - _first_subpass: com::SubpassContents, - ) where - T: Iterator>, - { - // Make sure that no subpass works with Present as intermediate layout. - // This wouldn't make much sense, and proceeding with this constraint - // allows the state transitions generated from subpass dependencies - // to ignore the layouts completely. - assert!(!render_pass.subpasses.iter().any(|sp| sp - .color_attachments - .iter() - .chain(sp.depth_stencil_attachment.iter()) - .chain(sp.input_attachments.iter()) - .any(|aref| aref.1 == image::Layout::Present))); - - if !render_pass.raw_name.is_empty() { - let n = &render_pass.raw_name; - self.raw - .BeginEvent(0, n.as_ptr() as *const _, n.len() as u32 * 2); - } - - self.barriers.clear(); - let mut attachments = Vec::new(); - for (i, (info, attachment)) in attachment_infos - .zip(render_pass.attachments.iter()) - .enumerate() - { - let view = info.image_view.clone(); - // for swapchain views, we consider the initial layout to always be `General` - let pass_start_state = - conv::map_image_resource_state(image::Access::empty(), attachment.layouts.start); - if view.is_swapchain() && pass_start_state != d3d12::D3D12_RESOURCE_STATE_COMMON { - let barrier = d3d12::D3D12_RESOURCE_TRANSITION_BARRIER { - pResource: view.resource.as_mut_ptr(), - Subresource: d3d12::D3D12_RESOURCE_BARRIER_ALL_SUBRESOURCES, - StateBefore: d3d12::D3D12_RESOURCE_STATE_COMMON, - StateAfter: pass_start_state, - }; - self.barriers.push(Self::transition_barrier(barrier)); - } - - attachments.push(AttachmentInfo { - subpass_id: render_pass - .subpasses - .iter() - .position(|sp| sp.is_using(i)) - .map(|i| i as pass::SubpassId), - view, - clear_value: if attachment.ops.load == pass::AttachmentLoadOp::Clear { - Some(info.clear_value) - } else { - None - }, - stencil_value: if attachment.stencil_ops.load == pass::AttachmentLoadOp::Clear { - Some(info.clear_value.depth_stencil.stencil) - } else { - None - }, - }); - } - self.flush_barriers(); - - self.pass_cache = Some(RenderPassCache { - render_pass: render_pass.clone(), - target_rect: get_rect(&target_rect), - attachments, - num_layers: framebuffer.layers, - has_name: !render_pass.raw_name.is_empty(), - }); - self.cur_subpass = 0; - self.insert_subpass_barriers(BarrierPoint::Pre); - self.bind_targets(); - } - - unsafe fn next_subpass(&mut self, _contents: com::SubpassContents) { - self.insert_subpass_barriers(BarrierPoint::Post); - self.resolve_attachments(); - - self.cur_subpass += 1; - self.insert_subpass_barriers(BarrierPoint::Pre); - self.bind_targets(); - } - - unsafe fn end_render_pass(&mut self) { - self.insert_subpass_barriers(BarrierPoint::Post); - self.resolve_attachments(); - - self.cur_subpass = !0; - self.insert_subpass_barriers(BarrierPoint::Pre); - - let pc = self.pass_cache.take().unwrap(); - self.barriers.clear(); - for (at, attachment) in pc.attachments.iter().zip(pc.render_pass.attachments.iter()) { - // for swapchain views, we consider the initial layout to always be `General` - let pass_end_state = - conv::map_image_resource_state(image::Access::empty(), attachment.layouts.end); - if at.view.is_swapchain() && pass_end_state != d3d12::D3D12_RESOURCE_STATE_COMMON { - let barrier = d3d12::D3D12_RESOURCE_TRANSITION_BARRIER { - pResource: at.view.resource.as_mut_ptr(), - Subresource: d3d12::D3D12_RESOURCE_BARRIER_ALL_SUBRESOURCES, - StateBefore: pass_end_state, - StateAfter: d3d12::D3D12_RESOURCE_STATE_COMMON, - }; - self.barriers.push(Self::transition_barrier(barrier)); - } - } - self.flush_barriers(); - - if pc.has_name { - self.raw.EndEvent(); - } - } - - unsafe fn pipeline_barrier<'a, T>( - &mut self, - stages: Range, - _dependencies: memory::Dependencies, - barriers: T, - ) where - T: Iterator>, - { - self.barriers.clear(); - - // transition barriers - for barrier in barriers { - match barrier { - memory::Barrier::AllBuffers(_) | memory::Barrier::AllImages(_) => { - // Aliasing barrier with NULL resource is the closest we can get to - // a global memory barrier in Vulkan. - // Was suggested by a Microsoft representative as well as some of the IHVs. - let mut bar = d3d12::D3D12_RESOURCE_BARRIER { - Type: d3d12::D3D12_RESOURCE_BARRIER_TYPE_UAV, - Flags: d3d12::D3D12_RESOURCE_BARRIER_FLAG_NONE, - u: mem::zeroed(), - }; - *bar.u.UAV_mut() = d3d12::D3D12_RESOURCE_UAV_BARRIER { - pResource: ptr::null_mut(), - }; - self.barriers.push(bar); - } - memory::Barrier::Buffer { - ref states, - target, - ref families, - range: _, - } => { - // TODO: Implement queue family ownership transitions for dx12 - if let Some(f) = families { - if f.start.0 != f.end.0 { - unimplemented!("Queue family resource ownership transitions are not implemented for DX12 (attempted transition from queue family {} to {}", f.start.0, f.end.0); - } - } - - let state_src = conv::map_buffer_resource_state(states.start); - let state_dst = conv::map_buffer_resource_state(states.end); - - if state_src == state_dst { - continue; - } - - let target = target.expect_bound(); - let bar = Self::transition_barrier(d3d12::D3D12_RESOURCE_TRANSITION_BARRIER { - pResource: target.resource.as_mut_ptr(), - Subresource: d3d12::D3D12_RESOURCE_BARRIER_ALL_SUBRESOURCES, - StateBefore: state_src, - StateAfter: state_dst, - }); - - self.barriers.push(bar); - } - memory::Barrier::Image { - ref states, - target, - ref families, - ref range, - } => { - // TODO: Implement queue family ownership transitions for dx12 - if let Some(f) = families { - if f.start.0 != f.end.0 { - unimplemented!("Queue family resource ownership transitions are not implemented for DX12 (attempted transition from queue family {} to {}", f.start.0, f.end.0); - } - } - - let state_src = conv::map_image_resource_state(states.start.0, states.start.1); - let state_dst = conv::map_image_resource_state(states.end.0, states.end.1); - - let target = target.expect_bound(); - - match target.place { - r::Place::Heap { .. } => { - self.fill_texture_barries(target, state_src..state_dst, range); - } - r::Place::Swapchain { .. } => {} //ignore - } - } - } - } - - let all_shader_stages = pso::PipelineStage::VERTEX_SHADER - | pso::PipelineStage::FRAGMENT_SHADER - | pso::PipelineStage::COMPUTE_SHADER - | pso::PipelineStage::GEOMETRY_SHADER - | pso::PipelineStage::HULL_SHADER - | pso::PipelineStage::DOMAIN_SHADER; - - // UAV barriers - // - // TODO: Currently always add a global UAV barrier. - // WAR only requires an execution barrier but D3D12 seems to need - // a UAV barrier for this according to docs. Can we make this better? - if (stages.start & stages.end).intersects(all_shader_stages) { - let mut barrier = d3d12::D3D12_RESOURCE_BARRIER { - Type: d3d12::D3D12_RESOURCE_BARRIER_TYPE_UAV, - Flags: d3d12::D3D12_RESOURCE_BARRIER_FLAG_NONE, - u: mem::zeroed(), - }; - *barrier.u.UAV_mut() = d3d12::D3D12_RESOURCE_UAV_BARRIER { - pResource: ptr::null_mut(), - }; - self.barriers.push(barrier); - } - - // Alias barriers - // - // TODO: Optimize, don't always add an alias barrier - if false { - let mut barrier = d3d12::D3D12_RESOURCE_BARRIER { - Type: d3d12::D3D12_RESOURCE_BARRIER_TYPE_ALIASING, - Flags: d3d12::D3D12_RESOURCE_BARRIER_FLAG_NONE, - u: mem::zeroed(), - }; - *barrier.u.Aliasing_mut() = d3d12::D3D12_RESOURCE_ALIASING_BARRIER { - pResourceBefore: ptr::null_mut(), - pResourceAfter: ptr::null_mut(), - }; - self.barriers.push(barrier); - } - - self.flush_barriers(); - } - - unsafe fn clear_image( - &mut self, - image: &r::Image, - layout: image::Layout, - value: com::ClearValue, - subresource_ranges: T, - ) where - T: Iterator, - { - let image = image.expect_bound(); - let base_state = conv::map_image_resource_state(image::Access::TRANSFER_WRITE, layout); - - for sub in subresource_ranges { - if sub.level_start != 0 || image.mip_levels != 1 { - warn!("Clearing non-zero mipmap levels is not supported yet"); - } - let target_state = if sub.aspects.contains(Aspects::COLOR) { - d3d12::D3D12_RESOURCE_STATE_RENDER_TARGET - } else { - d3d12::D3D12_RESOURCE_STATE_DEPTH_WRITE - }; - - self.barriers.clear(); - self.fill_texture_barries(image, base_state..target_state, &sub); - self.flush_barriers(); - - for rel_layer in 0..sub.resolve_layer_count(image.kind.num_layers()) { - let layer = (sub.layer_start + rel_layer) as usize; - if sub.aspects.contains(Aspects::COLOR) { - let rtv = image.clear_cv[layer].raw; - self.clear_render_target_view(rtv, value.color, &[]); - } - if sub.aspects.contains(Aspects::DEPTH) { - let dsv = image.clear_dv[layer].raw; - self.clear_depth_stencil_view(dsv, Some(value.depth_stencil.depth), None, &[]); - } - if sub.aspects.contains(Aspects::STENCIL) { - let dsv = image.clear_sv[layer].raw; - self.clear_depth_stencil_view( - dsv, - None, - Some(value.depth_stencil.stencil as _), - &[], - ); - } - } - - self.flip_barriers(); - self.flush_barriers(); - } - } - - unsafe fn clear_attachments(&mut self, clears: T, rects: U) - where - T: Iterator, - U: Iterator, - { - let pass_cache = match self.pass_cache { - Some(ref cache) => cache, - None => panic!("`clear_attachments` can only be called inside a renderpass"), - }; - let sub_pass = &pass_cache.render_pass.subpasses[self.cur_subpass as usize]; - - let clear_rects: SmallVec<[pso::ClearRect; 4]> = rects.collect(); - - let device = self.shared.service_pipes.device; - - for clear in clears { - match clear { - com::AttachmentClear::Color { index, value } => { - let attachment = { - let rtv_id = sub_pass.color_attachments[index]; - &pass_cache.attachments[rtv_id.0].view - }; - - let mut rtv_pool = descriptors_cpu::HeapLinear::new( - device, - native::DescriptorHeapType::Rtv, - clear_rects.len(), - ); - - for clear_rect in &clear_rects { - assert!(attachment.layers.0 + clear_rect.layers.end <= attachment.layers.1); - let rect = [get_rect(&clear_rect.rect)]; - - let view_info = device::ViewInfo { - resource: attachment.resource, - kind: attachment.kind, - caps: image::ViewCapabilities::empty(), - view_kind: image::ViewKind::D2Array, - format: attachment.dxgi_format, - component_mapping: device::IDENTITY_MAPPING, - levels: attachment.mip_levels.0..attachment.mip_levels.1, - layers: attachment.layers.0 + clear_rect.layers.start - ..attachment.layers.0 + clear_rect.layers.end, - }; - let rtv = rtv_pool.alloc_handle(); - Device::view_image_as_render_target_impl(device, rtv, &view_info).unwrap(); - self.clear_render_target_view(rtv, value.into(), &rect); - } - - rtv_pool.destroy(); - } - com::AttachmentClear::DepthStencil { depth, stencil } => { - let attachment = { - let dsv_id = sub_pass.depth_stencil_attachment.unwrap(); - &pass_cache.attachments[dsv_id.0].view - }; - - let mut dsv_pool = descriptors_cpu::HeapLinear::new( - device, - native::DescriptorHeapType::Dsv, - clear_rects.len(), - ); - - for clear_rect in &clear_rects { - assert!(attachment.layers.0 + clear_rect.layers.end <= attachment.layers.1); - let rect = [get_rect(&clear_rect.rect)]; - - let view_info = device::ViewInfo { - resource: attachment.resource, - kind: attachment.kind, - caps: image::ViewCapabilities::empty(), - view_kind: image::ViewKind::D2Array, - format: attachment.dxgi_format, - component_mapping: device::IDENTITY_MAPPING, - levels: attachment.mip_levels.0..attachment.mip_levels.1, - layers: attachment.layers.0 + clear_rect.layers.start - ..attachment.layers.0 + clear_rect.layers.end, - }; - let dsv = dsv_pool.alloc_handle(); - Device::view_image_as_depth_stencil_impl(device, dsv, &view_info).unwrap(); - self.clear_depth_stencil_view(dsv, depth, stencil, &rect); - } - - dsv_pool.destroy(); - } - } - } - } - - unsafe fn resolve_image( - &mut self, - src: &r::Image, - _src_layout: image::Layout, - dst: &r::Image, - _dst_layout: image::Layout, - regions: T, - ) where - T: Iterator, - { - let src = src.expect_bound(); - let dst = dst.expect_bound(); - assert_eq!(src.descriptor.Format, dst.descriptor.Format); - - { - // Insert barrier for `COPY_DEST` to `RESOLVE_DEST` as we only expose - // `TRANSFER_WRITE` which is used for all copy commands. - let transition_barrier = - Self::transition_barrier(d3d12::D3D12_RESOURCE_TRANSITION_BARRIER { - pResource: dst.resource.as_mut_ptr(), - Subresource: d3d12::D3D12_RESOURCE_BARRIER_ALL_SUBRESOURCES, // TODO: only affected ranges - StateBefore: d3d12::D3D12_RESOURCE_STATE_COPY_DEST, - StateAfter: d3d12::D3D12_RESOURCE_STATE_RESOLVE_DEST, - }); - self.raw.ResourceBarrier(1, &transition_barrier); - } - - for r in regions { - for layer in 0..r.extent.depth as u32 { - self.raw.ResolveSubresource( - src.resource.as_mut_ptr(), - src.calc_subresource( - r.src_subresource.level as _, - r.src_subresource.layers.start as u32 + layer, - 0, - ), - dst.resource.as_mut_ptr(), - dst.calc_subresource( - r.dst_subresource.level as _, - r.dst_subresource.layers.start as u32 + layer, - 0, - ), - src.descriptor.Format, - ); - } - } - - { - // Insert barrier for back transition from `RESOLVE_DEST` to `COPY_DEST`. - let transition_barrier = - Self::transition_barrier(d3d12::D3D12_RESOURCE_TRANSITION_BARRIER { - pResource: dst.resource.as_mut_ptr(), - Subresource: d3d12::D3D12_RESOURCE_BARRIER_ALL_SUBRESOURCES, // TODO: only affected ranges - StateBefore: d3d12::D3D12_RESOURCE_STATE_RESOLVE_DEST, - StateAfter: d3d12::D3D12_RESOURCE_STATE_COPY_DEST, - }); - self.raw.ResourceBarrier(1, &transition_barrier); - } - } - - unsafe fn blit_image( - &mut self, - src: &r::Image, - _src_layout: image::Layout, - dst: &r::Image, - _dst_layout: image::Layout, - filter: image::Filter, - regions: T, - ) where - T: Iterator, - { - let device = self.shared.service_pipes.device.clone(); - let src = src.expect_bound(); - let dst = dst.expect_bound(); - - // TODO: Resource barriers for src. - // TODO: depth or stencil images not supported so far - - // TODO: only supporting 2D images - match (src.kind, dst.kind) { - (image::Kind::D2(..), image::Kind::D2(..)) => {} - _ => unimplemented!(), - } - - // Descriptor heap for the current blit, only storing the src image - let (srv_heap, _) = device.create_descriptor_heap( - 1, - native::DescriptorHeapType::CbvSrvUav, - native::DescriptorHeapFlags::SHADER_VISIBLE, - 0, - ); - self.raw.set_descriptor_heaps(&[srv_heap]); - self.temporary_gpu_heaps.push(srv_heap); - - let srv_desc = Device::build_image_as_shader_resource_desc(&device::ViewInfo { - resource: src.resource, - kind: src.kind, - caps: src.view_caps, - view_kind: image::ViewKind::D2Array, // TODO - format: src.default_view_format.unwrap(), - component_mapping: device::IDENTITY_MAPPING, - levels: 0..src.descriptor.MipLevels as _, - layers: 0..src.kind.num_layers(), - }) - .unwrap(); - device.CreateShaderResourceView( - src.resource.as_mut_ptr(), - &srv_desc, - srv_heap.start_cpu_descriptor(), - ); - - let filter = match filter { - image::Filter::Nearest => d3d12::D3D12_FILTER_MIN_MAG_MIP_POINT, - image::Filter::Linear => d3d12::D3D12_FILTER_MIN_MAG_LINEAR_MIP_POINT, - }; - - struct Instance { - rtv: d3d12::D3D12_CPU_DESCRIPTOR_HANDLE, - viewport: d3d12::D3D12_VIEWPORT, - data: internal::BlitData, - } - let mut instances = FastHashMap::>::default(); - let mut barriers = Vec::new(); - - for r in regions { - let first_layer = r.dst_subresource.layers.start; - let num_layers = r.dst_subresource.layers.end - first_layer; - - // WORKAROUND: renderdoc crashes if we destroy the pool too early - let rtv_pool = Device::create_descriptor_heap_impl( - device, - native::DescriptorHeapType::Rtv, - false, - num_layers as _, - ); - self.rtv_pools.push(rtv_pool.raw.clone()); - - let key = match r.dst_subresource.aspects { - format::Aspects::COLOR => { - let format = dst.default_view_format.unwrap(); - // Create RTVs of the dst image for the miplevel of the current region - for i in 0..num_layers { - let mut desc = d3d12::D3D12_RENDER_TARGET_VIEW_DESC { - Format: format, - ViewDimension: d3d12::D3D12_RTV_DIMENSION_TEXTURE2DARRAY, - u: mem::zeroed(), - }; - - *desc.u.Texture2DArray_mut() = d3d12::D3D12_TEX2D_ARRAY_RTV { - MipSlice: r.dst_subresource.level as _, - FirstArraySlice: (i + first_layer) as u32, - ArraySize: 1, - PlaneSlice: 0, // TODO - }; - - let view = rtv_pool.at(i as _, 0).cpu; - device.CreateRenderTargetView(dst.resource.as_mut_ptr(), &desc, view); - } - - (format, filter) - } - _ => unimplemented!(), - }; - - // Take flipping into account - let viewport = d3d12::D3D12_VIEWPORT { - TopLeftX: cmp::min(r.dst_bounds.start.x, r.dst_bounds.end.x) as _, - TopLeftY: cmp::min(r.dst_bounds.start.y, r.dst_bounds.end.y) as _, - Width: (r.dst_bounds.end.x - r.dst_bounds.start.x).abs() as _, - Height: (r.dst_bounds.end.y - r.dst_bounds.start.y).abs() as _, - MinDepth: 0.0, - MaxDepth: 1.0, - }; - - let list = instances.entry(key).or_insert(Vec::new()); - - for i in 0..num_layers { - let src_layer = r.src_subresource.layers.start + i; - // Screen space triangle blitting - let data = { - // Image extents, layers are treated as depth - let (sx, dx) = if r.dst_bounds.start.x > r.dst_bounds.end.x { - ( - r.src_bounds.end.x, - r.src_bounds.start.x - r.src_bounds.end.x, - ) - } else { - ( - r.src_bounds.start.x, - r.src_bounds.end.x - r.src_bounds.start.x, - ) - }; - let (sy, dy) = if r.dst_bounds.start.y > r.dst_bounds.end.y { - ( - r.src_bounds.end.y, - r.src_bounds.start.y - r.src_bounds.end.y, - ) - } else { - ( - r.src_bounds.start.y, - r.src_bounds.end.y - r.src_bounds.start.y, - ) - }; - let image::Extent { width, height, .. } = - src.kind.level_extent(r.src_subresource.level); - - internal::BlitData { - src_offset: [sx as f32 / width as f32, sy as f32 / height as f32], - src_extent: [dx as f32 / width as f32, dy as f32 / height as f32], - layer: src_layer as f32, - level: r.src_subresource.level as _, - } - }; - - list.push(Instance { - rtv: rtv_pool.at(i as _, 0).cpu, - viewport, - data, - }); - - barriers.push(Self::transition_barrier( - d3d12::D3D12_RESOURCE_TRANSITION_BARRIER { - pResource: dst.resource.as_mut_ptr(), - Subresource: dst.calc_subresource( - r.dst_subresource.level as _, - (first_layer + i) as _, - 0, - ), - StateBefore: d3d12::D3D12_RESOURCE_STATE_COPY_DEST, - StateAfter: d3d12::D3D12_RESOURCE_STATE_RENDER_TARGET, - }, - )); - } - } - - // pre barriers - self.raw - .ResourceBarrier(barriers.len() as _, barriers.as_ptr()); - // execute blits - self.set_internal_graphics_pipeline(); - for (key, list) in instances { - let blit = self.shared.service_pipes.get_blit_2d_color(key); - self.raw - .IASetPrimitiveTopology(d3dcommon::D3D_PRIMITIVE_TOPOLOGY_TRIANGLELIST); - self.raw.set_pipeline_state(blit.pipeline); - self.raw.set_graphics_root_signature(blit.signature); - self.raw - .set_graphics_root_descriptor_table(0, srv_heap.start_gpu_descriptor()); - for inst in list { - let scissor = d3d12::D3D12_RECT { - left: inst.viewport.TopLeftX as _, - top: inst.viewport.TopLeftY as _, - right: (inst.viewport.TopLeftX + inst.viewport.Width) as _, - bottom: (inst.viewport.TopLeftY + inst.viewport.Height) as _, - }; - self.raw.RSSetViewports(1, &inst.viewport); - self.raw.RSSetScissorRects(1, &scissor); - self.raw.SetGraphicsRoot32BitConstants( - 1, - (mem::size_of::() / 4) as _, - &inst.data as *const _ as *const _, - 0, - ); - self.raw - .OMSetRenderTargets(1, &inst.rtv, minwindef::TRUE, ptr::null()); - self.raw.draw(3, 1, 0, 0); - } - } - // post barriers - for bar in &mut barriers { - let transition = bar.u.Transition_mut(); - mem::swap(&mut transition.StateBefore, &mut transition.StateAfter); - } - self.raw - .ResourceBarrier(barriers.len() as _, barriers.as_ptr()); - - // Reset states - self.raw - .RSSetViewports(self.viewport_cache.len() as _, self.viewport_cache.as_ptr()); - self.raw - .RSSetScissorRects(self.scissor_cache.len() as _, self.scissor_cache.as_ptr()); - if self.primitive_topology != d3dcommon::D3D_PRIMITIVE_TOPOLOGY_UNDEFINED { - self.raw.IASetPrimitiveTopology(self.primitive_topology); - } - } - - unsafe fn bind_index_buffer( - &mut self, - buffer: &r::Buffer, - sub: buffer::SubRange, - ty: IndexType, - ) { - let buffer = buffer.expect_bound(); - let format = match ty { - IndexType::U16 => dxgiformat::DXGI_FORMAT_R16_UINT, - IndexType::U32 => dxgiformat::DXGI_FORMAT_R32_UINT, - }; - let location = buffer.resource.gpu_virtual_address(); - self.raw.set_index_buffer( - location + sub.offset, - sub.size_to(buffer.requirements.size) as u32, - format, - ); - } - - unsafe fn bind_vertex_buffers<'a, T>(&mut self, first_binding: pso::BufferIndex, buffers: T) - where - T: Iterator, - { - assert!(first_binding as usize <= MAX_VERTEX_BUFFERS); - - for (view, (buffer, sub)) in self.vertex_buffer_views[first_binding as _..] - .iter_mut() - .zip(buffers) - { - let b = buffer.expect_bound(); - let base = (*b.resource).GetGPUVirtualAddress(); - view.BufferLocation = base + sub.offset; - view.SizeInBytes = sub.size_to(b.requirements.size) as u32; - } - self.set_vertex_buffers(); - } - - unsafe fn set_viewports(&mut self, first_viewport: u32, viewports: T) - where - T: Iterator, - { - for (i, vp) in viewports.enumerate() { - let viewport = d3d12::D3D12_VIEWPORT { - TopLeftX: vp.rect.x as _, - TopLeftY: vp.rect.y as _, - Width: vp.rect.w as _, - Height: vp.rect.h as _, - MinDepth: vp.depth.start, - MaxDepth: vp.depth.end, - }; - if i + first_viewport as usize >= self.viewport_cache.len() { - self.viewport_cache.push(viewport); - } else { - self.viewport_cache[i + first_viewport as usize] = viewport; - } - } - - self.raw - .RSSetViewports(self.viewport_cache.len() as _, self.viewport_cache.as_ptr()); - } - - unsafe fn set_scissors(&mut self, first_scissor: u32, scissors: T) - where - T: Iterator, - { - for (i, r) in scissors.enumerate() { - let rect = get_rect(&r); - if i + first_scissor as usize >= self.scissor_cache.len() { - self.scissor_cache.push(rect); - } else { - self.scissor_cache[i + first_scissor as usize] = rect; - } - } - - self.raw - .RSSetScissorRects(self.scissor_cache.len() as _, self.scissor_cache.as_ptr()) - } - - unsafe fn set_blend_constants(&mut self, color: pso::ColorValue) { - self.raw.set_blend_factor(color); - } - - unsafe fn set_stencil_reference(&mut self, faces: pso::Face, value: pso::StencilValue) { - assert!(!faces.is_empty()); - - if !faces.is_all() { - warn!( - "Stencil ref values set for both faces but only one was requested ({})", - faces.bits(), - ); - } - - self.raw.set_stencil_reference(value as _); - } - - unsafe fn set_stencil_read_mask(&mut self, _faces: pso::Face, _value: pso::StencilValue) { - //TODO: - // unimplemented!(); - } - - unsafe fn set_stencil_write_mask(&mut self, _faces: pso::Face, _value: pso::StencilValue) { - //TODO: - //unimplemented!(); - } - - unsafe fn set_depth_bounds(&mut self, bounds: Range) { - let (cmd_list1, hr) = self.raw.cast::(); - if winerror::SUCCEEDED(hr) { - cmd_list1.OMSetDepthBounds(bounds.start, bounds.end); - cmd_list1.destroy(); - } else { - warn!("Depth bounds test is not supported"); - } - } - - unsafe fn set_line_width(&mut self, width: f32) { - validate_line_width(width); - } - - unsafe fn set_depth_bias(&mut self, _depth_bias: pso::DepthBias) { - //TODO: - // unimplemented!() - } - - unsafe fn bind_graphics_pipeline(&mut self, pipeline: &r::GraphicsPipeline) { - match self.gr_pipeline.pipeline { - Some((_, ref shared)) if Arc::ptr_eq(shared, &pipeline.shared) => { - // Same root signature, nothing to do - } - _ => { - self.raw - .set_graphics_root_signature(pipeline.shared.signature); - // All slots need to be rebound internally on signature change. - self.gr_pipeline.user_data.dirty_all(); - } - } - self.raw.set_pipeline_state(pipeline.raw); - self.raw.IASetPrimitiveTopology(pipeline.topology); - self.primitive_topology = pipeline.topology; - - self.active_bindpoint = BindPoint::Graphics { internal: false }; - self.gr_pipeline.pipeline = Some((pipeline.raw, Arc::clone(&pipeline.shared))); - self.vertex_bindings_remap = pipeline.vertex_bindings; - - self.set_vertex_buffers(); - - if let Some(ref vp) = pipeline.baked_states.viewport { - self.set_viewports(0, iter::once(vp.clone())); - } - if let Some(ref rect) = pipeline.baked_states.scissor { - self.set_scissors(0, iter::once(rect.clone())); - } - if let Some(color) = pipeline.baked_states.blend_constants { - self.set_blend_constants(color); - } - if let Some(ref bounds) = pipeline.baked_states.depth_bounds { - self.set_depth_bounds(bounds.clone()); - } - } - - unsafe fn bind_graphics_descriptor_sets<'a, I, J>( - &mut self, - layout: &r::PipelineLayout, - first_set: usize, - sets: I, - offsets: J, - ) where - I: Iterator, - J: Iterator, - { - let set_array = sets.collect::>(); - self.active_descriptor_heaps = self - .gr_pipeline - .bind_descriptor_sets(layout, first_set, &set_array, offsets); - self.bind_descriptor_heaps(); - - for (i, set) in set_array.into_iter().enumerate() { - self.mark_bound_descriptor(first_set + i, set); - } - } - - unsafe fn bind_compute_pipeline(&mut self, pipeline: &r::ComputePipeline) { - match self.comp_pipeline.pipeline { - Some((_, ref shared)) if Arc::ptr_eq(shared, &pipeline.shared) => { - // Same root signature, nothing to do - } - _ => { - self.raw - .set_compute_root_signature(pipeline.shared.signature); - // All slots need to be rebound internally on signature change. - self.comp_pipeline.user_data.dirty_all(); - } - } - self.raw.set_pipeline_state(pipeline.raw); - - self.active_bindpoint = BindPoint::Compute; - self.comp_pipeline.pipeline = Some((pipeline.raw, Arc::clone(&pipeline.shared))); - } - - unsafe fn bind_compute_descriptor_sets<'a, I, J>( - &mut self, - layout: &r::PipelineLayout, - first_set: usize, - sets: I, - offsets: J, - ) where - I: Iterator, - J: Iterator, - { - let set_array = sets.collect::>(); - self.active_descriptor_heaps = self - .comp_pipeline - .bind_descriptor_sets(layout, first_set, &set_array, offsets); - self.bind_descriptor_heaps(); - - for (i, set) in set_array.into_iter().enumerate() { - self.mark_bound_descriptor(first_set + i, set); - } - } - - unsafe fn dispatch(&mut self, count: WorkGroupCount) { - self.set_compute_bind_point(); - self.raw.dispatch(count); - } - - unsafe fn dispatch_indirect(&mut self, buffer: &r::Buffer, offset: buffer::Offset) { - let buffer = buffer.expect_bound(); - self.set_compute_bind_point(); - self.raw.ExecuteIndirect( - self.shared.signatures.dispatch.as_mut_ptr(), - 1, - buffer.resource.as_mut_ptr(), - offset, - ptr::null_mut(), - 0, - ); - } - - unsafe fn fill_buffer(&mut self, buffer: &r::Buffer, range: buffer::SubRange, data: u32) { - let buffer = buffer.expect_bound(); - let bytes_per_unit = 4; - let start = range.offset as i32; - let end = range - .size - .map_or(buffer.requirements.size, |s| range.offset + s) as i32; - if start % 4 != 0 || end % 4 != 0 { - warn!("Fill buffer bounds have to be multiples of 4"); - } - let rect = d3d12::D3D12_RECT { - left: start / bytes_per_unit, - top: 0, - right: end / bytes_per_unit, - bottom: 1, - }; - - let (pre_barrier, post_barrier) = Self::dual_transition_barriers( - buffer.resource, - d3d12::D3D12_RESOURCE_BARRIER_ALL_SUBRESOURCES, - d3d12::D3D12_RESOURCE_STATE_COPY_DEST..d3d12::D3D12_RESOURCE_STATE_UNORDERED_ACCESS, - ); - self.raw.ResourceBarrier(1, &pre_barrier); - - // Descriptor heap for the current blit, only storing the src image - let device = self.shared.service_pipes.device.clone(); - let (uav_heap, _) = device.create_descriptor_heap( - 1, - native::DescriptorHeapType::CbvSrvUav, - native::DescriptorHeapFlags::SHADER_VISIBLE, - 0, - ); - let mut uav_desc = d3d12::D3D12_UNORDERED_ACCESS_VIEW_DESC { - Format: dxgiformat::DXGI_FORMAT_R32_TYPELESS, - ViewDimension: d3d12::D3D12_UAV_DIMENSION_BUFFER, - u: mem::zeroed(), - }; - *uav_desc.u.Buffer_mut() = d3d12::D3D12_BUFFER_UAV { - FirstElement: 0, - NumElements: (buffer.requirements.size / bytes_per_unit as u64) as u32, - StructureByteStride: 0, - CounterOffsetInBytes: 0, - Flags: d3d12::D3D12_BUFFER_UAV_FLAG_RAW, - }; - device.CreateUnorderedAccessView( - buffer.resource.as_mut_ptr(), - ptr::null_mut(), - &uav_desc, - uav_heap.start_cpu_descriptor(), - ); - self.raw.set_descriptor_heaps(&[uav_heap]); - self.temporary_gpu_heaps.push(uav_heap); - - let cpu_descriptor = buffer - .clear_uav - .expect("Buffer needs to be created with usage `TRANSFER_DST`"); - - self.raw.ClearUnorderedAccessViewUint( - uav_heap.start_gpu_descriptor(), - cpu_descriptor.raw, - buffer.resource.as_mut_ptr(), - &[data; 4], - 1, - &rect as *const _, - ); - - self.raw.ResourceBarrier(1, &post_barrier); - } - - unsafe fn update_buffer(&mut self, _buffer: &r::Buffer, _offset: buffer::Offset, _data: &[u8]) { - unimplemented!() - } - - unsafe fn copy_buffer(&mut self, src: &r::Buffer, dst: &r::Buffer, regions: T) - where - T: Iterator, - { - let src = src.expect_bound(); - let dst = dst.expect_bound(); - - /* - let (pre_barrier, post_barrier) = Self::dual_transition_barriers( - dst.resource, - d3d12::D3D12_RESOURCE_BARRIER_ALL_SUBRESOURCES, - d3d12::D3D12_RESOURCE_STATE_COPY_DEST, - ); - self.raw.ResourceBarrier(1, &pre_barrier); - */ - - // TODO: Optimization: Copy whole resource if possible - // copy each region - for region in regions { - self.raw.CopyBufferRegion( - dst.resource.as_mut_ptr(), - region.dst as _, - src.resource.as_mut_ptr(), - region.src as _, - region.size as _, - ); - } - - //self.raw.ResourceBarrier(1, &post_barrier); - } - - unsafe fn copy_image( - &mut self, - src: &r::Image, - _: image::Layout, - dst: &r::Image, - _: image::Layout, - regions: T, - ) where - T: Iterator, - { - let src = src.expect_bound(); - let dst = dst.expect_bound(); - let mut src_image = d3d12::D3D12_TEXTURE_COPY_LOCATION { - pResource: src.resource.as_mut_ptr(), - Type: d3d12::D3D12_TEXTURE_COPY_TYPE_SUBRESOURCE_INDEX, - u: mem::zeroed(), - }; - let mut dst_image = d3d12::D3D12_TEXTURE_COPY_LOCATION { - pResource: dst.resource.as_mut_ptr(), - Type: d3d12::D3D12_TEXTURE_COPY_TYPE_SUBRESOURCE_INDEX, - u: mem::zeroed(), - }; - - let device = self.shared.service_pipes.device.clone(); - let src_desc = src.surface_type.desc(); - let dst_desc = dst.surface_type.desc(); - assert_eq!(src_desc.bits, dst_desc.bits); - //Note: Direct3D 10.1 enables copies between prestructured-typed textures - // and block-compressed textures of the same bit widths. - let do_alias = src.surface_type != dst.surface_type - && src_desc.is_compressed() == dst_desc.is_compressed(); - - if do_alias { - // D3D12 only permits changing the channel type for copies, - // similarly to how it allows the views to be created. - - // create an aliased resource to the source - let mut alias = native::Resource::null(); - let desc = d3d12::D3D12_RESOURCE_DESC { - Format: dst.descriptor.Format, - ..src.descriptor.clone() - }; - let (heap_ptr, heap_offset) = match src.place { - r::Place::Heap { raw, offset } => (raw.as_mut_ptr(), offset), - r::Place::Swapchain {} => { - error!("Unable to copy swapchain image, skipping"); - return; - } - }; - assert_eq!( - winerror::S_OK, - device.CreatePlacedResource( - heap_ptr, - heap_offset, - &desc, - d3d12::D3D12_RESOURCE_STATE_COMMON, - ptr::null(), - &d3d12::ID3D12Resource::uuidof(), - alias.mut_void(), - ) - ); - src_image.pResource = alias.as_mut_ptr(); - self.retained_resources.push(alias); - - // signal the aliasing transition - let sub_barrier = d3d12::D3D12_RESOURCE_ALIASING_BARRIER { - pResourceBefore: src.resource.as_mut_ptr(), - pResourceAfter: src_image.pResource, - }; - let mut barrier = d3d12::D3D12_RESOURCE_BARRIER { - Type: d3d12::D3D12_RESOURCE_BARRIER_TYPE_ALIASING, - Flags: d3d12::D3D12_RESOURCE_BARRIER_FLAG_NONE, - u: mem::zeroed(), - }; - *barrier.u.Aliasing_mut() = sub_barrier; - self.raw.ResourceBarrier(1, &barrier as *const _); - } - - for r in regions { - debug_assert_eq!( - r.src_subresource.layers.len(), - r.dst_subresource.layers.len() - ); - let src_box = d3d12::D3D12_BOX { - left: r.src_offset.x as _, - top: r.src_offset.y as _, - right: (r.src_offset.x + r.extent.width as i32) as _, - bottom: (r.src_offset.y + r.extent.height as i32) as _, - front: r.src_offset.z as _, - back: (r.src_offset.z + r.extent.depth as i32) as _, - }; - - for (src_layer, dst_layer) in r - .src_subresource - .layers - .clone() - .zip(r.dst_subresource.layers.clone()) - { - *src_image.u.SubresourceIndex_mut() = - src.calc_subresource(r.src_subresource.level as _, src_layer as _, 0); - *dst_image.u.SubresourceIndex_mut() = - dst.calc_subresource(r.dst_subresource.level as _, dst_layer as _, 0); - self.raw.CopyTextureRegion( - &dst_image, - r.dst_offset.x as _, - r.dst_offset.y as _, - r.dst_offset.z as _, - &src_image, - &src_box, - ); - } - } - - if do_alias { - // signal the aliasing transition - back to the original - let sub_barrier = d3d12::D3D12_RESOURCE_ALIASING_BARRIER { - pResourceBefore: src_image.pResource, - pResourceAfter: src.resource.as_mut_ptr(), - }; - let mut barrier = d3d12::D3D12_RESOURCE_BARRIER { - Type: d3d12::D3D12_RESOURCE_BARRIER_TYPE_ALIASING, - Flags: d3d12::D3D12_RESOURCE_BARRIER_FLAG_NONE, - u: mem::zeroed(), - }; - *barrier.u.Aliasing_mut() = sub_barrier; - self.raw.ResourceBarrier(1, &barrier as *const _); - } - } - - unsafe fn copy_buffer_to_image( - &mut self, - buffer: &r::Buffer, - image: &r::Image, - _: image::Layout, - regions: T, - ) where - T: Iterator, - { - let buffer = buffer.expect_bound(); - let image = image.expect_bound(); - assert!(self.copies.is_empty()); - - for r in regions { - Self::split_buffer_copy(&mut self.copies, r, image); - } - - if self.copies.is_empty() { - return; - } - - let mut src = d3d12::D3D12_TEXTURE_COPY_LOCATION { - pResource: buffer.resource.as_mut_ptr(), - Type: d3d12::D3D12_TEXTURE_COPY_TYPE_PLACED_FOOTPRINT, - u: mem::zeroed(), - }; - let mut dst = d3d12::D3D12_TEXTURE_COPY_LOCATION { - pResource: image.resource.as_mut_ptr(), - Type: d3d12::D3D12_TEXTURE_COPY_TYPE_SUBRESOURCE_INDEX, - u: mem::zeroed(), - }; - - for c in self.copies.drain(..) { - let src_box = d3d12::D3D12_BOX { - left: c.buf_offset.x as u32, - top: c.buf_offset.y as u32, - right: c.buf_offset.x as u32 + c.copy_extent.width, - bottom: c.buf_offset.y as u32 + c.copy_extent.height, - front: c.buf_offset.z as u32, - back: c.buf_offset.z as u32 + c.copy_extent.depth, - }; - let footprint = d3d12::D3D12_PLACED_SUBRESOURCE_FOOTPRINT { - Offset: c.footprint_offset, - Footprint: d3d12::D3D12_SUBRESOURCE_FOOTPRINT { - Format: image.descriptor.Format, - Width: c.footprint.width, - Height: c.footprint.height, - Depth: c.footprint.depth, - RowPitch: c.row_pitch, - }, - }; - *src.u.PlacedFootprint_mut() = footprint; - *dst.u.SubresourceIndex_mut() = c.img_subresource; - self.raw.CopyTextureRegion( - &dst, - c.img_offset.x as _, - c.img_offset.y as _, - c.img_offset.z as _, - &src, - &src_box, - ); - } - } - - unsafe fn copy_image_to_buffer( - &mut self, - image: &r::Image, - _: image::Layout, - buffer: &r::Buffer, - regions: T, - ) where - T: Iterator, - { - let image = image.expect_bound(); - let buffer = buffer.expect_bound(); - assert!(self.copies.is_empty()); - - for r in regions { - Self::split_buffer_copy(&mut self.copies, r, image); - } - - if self.copies.is_empty() { - return; - } - - let mut src = d3d12::D3D12_TEXTURE_COPY_LOCATION { - pResource: image.resource.as_mut_ptr(), - Type: d3d12::D3D12_TEXTURE_COPY_TYPE_SUBRESOURCE_INDEX, - u: mem::zeroed(), - }; - let mut dst = d3d12::D3D12_TEXTURE_COPY_LOCATION { - pResource: buffer.resource.as_mut_ptr(), - Type: d3d12::D3D12_TEXTURE_COPY_TYPE_PLACED_FOOTPRINT, - u: mem::zeroed(), - }; - - for c in self.copies.drain(..) { - let src_box = d3d12::D3D12_BOX { - left: c.img_offset.x as u32, - top: c.img_offset.y as u32, - right: c.img_offset.x as u32 + c.copy_extent.width, - bottom: c.img_offset.y as u32 + c.copy_extent.height, - front: c.img_offset.z as u32, - back: c.img_offset.z as u32 + c.copy_extent.depth, - }; - let footprint = d3d12::D3D12_PLACED_SUBRESOURCE_FOOTPRINT { - Offset: c.footprint_offset, - Footprint: d3d12::D3D12_SUBRESOURCE_FOOTPRINT { - Format: image.descriptor.Format, - Width: c.footprint.width, - Height: c.footprint.height, - Depth: c.footprint.depth, - RowPitch: c.row_pitch, - }, - }; - *dst.u.PlacedFootprint_mut() = footprint; - *src.u.SubresourceIndex_mut() = c.img_subresource; - self.raw.CopyTextureRegion( - &dst, - c.buf_offset.x as _, - c.buf_offset.y as _, - c.buf_offset.z as _, - &src, - &src_box, - ); - } - } - - unsafe fn draw(&mut self, vertices: Range, instances: Range) { - self.set_graphics_bind_point(); - self.raw.draw( - vertices.end - vertices.start, - instances.end - instances.start, - vertices.start, - instances.start, - ); - } - - unsafe fn draw_indexed( - &mut self, - indices: Range, - base_vertex: VertexOffset, - instances: Range, - ) { - self.set_graphics_bind_point(); - self.raw.draw_indexed( - indices.end - indices.start, - instances.end - instances.start, - indices.start, - base_vertex, - instances.start, - ); - } - - unsafe fn draw_indirect( - &mut self, - buffer: &r::Buffer, - offset: buffer::Offset, - draw_count: DrawCount, - stride: buffer::Stride, - ) { - assert_eq!(stride, 16); - let buffer = buffer.expect_bound(); - self.set_graphics_bind_point(); - self.raw.ExecuteIndirect( - self.shared.signatures.draw.as_mut_ptr(), - draw_count, - buffer.resource.as_mut_ptr(), - offset, - ptr::null_mut(), - 0, - ); - } - - unsafe fn draw_indexed_indirect( - &mut self, - buffer: &r::Buffer, - offset: buffer::Offset, - draw_count: DrawCount, - stride: buffer::Stride, - ) { - assert_eq!(stride, 20); - let buffer = buffer.expect_bound(); - self.set_graphics_bind_point(); - self.raw.ExecuteIndirect( - self.shared.signatures.draw_indexed.as_mut_ptr(), - draw_count, - buffer.resource.as_mut_ptr(), - offset, - ptr::null_mut(), - 0, - ); - } - - unsafe fn draw_mesh_tasks(&mut self, _: TaskCount, _: TaskCount) { - unimplemented!() - } - - unsafe fn draw_mesh_tasks_indirect( - &mut self, - _: &r::Buffer, - _: buffer::Offset, - _: hal::DrawCount, - _: buffer::Stride, - ) { - unimplemented!() - } - - unsafe fn draw_mesh_tasks_indirect_count( - &mut self, - _: &r::Buffer, - _: buffer::Offset, - _: &r::Buffer, - _: buffer::Offset, - _: DrawCount, - _: buffer::Stride, - ) { - unimplemented!() - } - - unsafe fn draw_indirect_count( - &mut self, - buffer: &r::Buffer, - offset: buffer::Offset, - count_buffer: &r::Buffer, - count_buffer_offset: buffer::Offset, - max_draw_count: DrawCount, - stride: buffer::Stride, - ) { - assert_eq!(stride, 16); - let buffer = buffer.expect_bound(); - let count_buffer = count_buffer.expect_bound(); - self.set_graphics_bind_point(); - self.raw.ExecuteIndirect( - self.shared.signatures.draw.as_mut_ptr(), - max_draw_count, - buffer.resource.as_mut_ptr(), - offset, - count_buffer.resource.as_mut_ptr(), - count_buffer_offset, - ); - } - - unsafe fn draw_indexed_indirect_count( - &mut self, - buffer: &r::Buffer, - offset: buffer::Offset, - count_buffer: &r::Buffer, - count_buffer_offset: buffer::Offset, - max_draw_count: DrawCount, - stride: buffer::Stride, - ) { - assert_eq!(stride, 20); - let buffer = buffer.expect_bound(); - let count_buffer = count_buffer.expect_bound(); - self.set_graphics_bind_point(); - self.raw.ExecuteIndirect( - self.shared.signatures.draw_indexed.as_mut_ptr(), - max_draw_count, - buffer.resource.as_mut_ptr(), - offset, - count_buffer.resource.as_mut_ptr(), - count_buffer_offset, - ); - } - - unsafe fn set_event(&mut self, _: &(), _: pso::PipelineStage) { - unimplemented!() - } - - unsafe fn reset_event(&mut self, _: &(), _: pso::PipelineStage) { - unimplemented!() - } - - unsafe fn wait_events<'a, I, J>(&mut self, _: I, _: Range, _: J) - where - I: Iterator, - J: Iterator>, - { - unimplemented!() - } - - unsafe fn begin_query(&mut self, query: query::Query, flags: query::ControlFlags) { - let query_ty = match query.pool.ty { - query::Type::Occlusion => { - if flags.contains(query::ControlFlags::PRECISE) { - self.occlusion_query = Some(OcclusionQuery::Precise(query.id)); - d3d12::D3D12_QUERY_TYPE_OCCLUSION - } else { - // Default to binary occlusion as it might be faster due to early depth/stencil - // tests. - self.occlusion_query = Some(OcclusionQuery::Binary(query.id)); - d3d12::D3D12_QUERY_TYPE_BINARY_OCCLUSION - } - } - query::Type::Timestamp => panic!("Timestap queries are issued via "), - query::Type::PipelineStatistics(_) => { - self.pipeline_stats_query = Some(query.id); - d3d12::D3D12_QUERY_TYPE_PIPELINE_STATISTICS - } - }; - - self.raw - .BeginQuery(query.pool.raw.as_mut_ptr(), query_ty, query.id); - } - - unsafe fn end_query(&mut self, query: query::Query) { - let id = query.id; - let query_ty = match query.pool.ty { - query::Type::Occlusion if self.occlusion_query == Some(OcclusionQuery::Precise(id)) => { - self.occlusion_query = None; - d3d12::D3D12_QUERY_TYPE_OCCLUSION - } - query::Type::Occlusion if self.occlusion_query == Some(OcclusionQuery::Binary(id)) => { - self.occlusion_query = None; - d3d12::D3D12_QUERY_TYPE_BINARY_OCCLUSION - } - query::Type::PipelineStatistics(_) if self.pipeline_stats_query == Some(id) => { - self.pipeline_stats_query = None; - d3d12::D3D12_QUERY_TYPE_PIPELINE_STATISTICS - } - _ => panic!("Missing `begin_query` call for query: {:?}", query), - }; - - self.raw.EndQuery(query.pool.raw.as_mut_ptr(), query_ty, id); - } - - unsafe fn reset_query_pool(&mut self, _pool: &r::QueryPool, _queries: Range) { - // Nothing to do here - // vkCmdResetQueryPool sets the queries to `unavailable` but the specification - // doesn't state an affect on the `active` state. Every queries at the end of the command - // buffer must be made inactive, which can only be done with EndQuery. - // Therefore, every `begin_query` must follow a `end_query` state, the resulting values - // after calling are undefined. - } - - unsafe fn copy_query_pool_results( - &mut self, - _pool: &r::QueryPool, - _queries: Range, - _buffer: &r::Buffer, - _offset: buffer::Offset, - _stride: buffer::Stride, - _flags: query::ResultFlags, - ) { - unimplemented!() - } - - unsafe fn write_timestamp(&mut self, _: pso::PipelineStage, query: query::Query) { - self.raw.EndQuery( - query.pool.raw.as_mut_ptr(), - d3d12::D3D12_QUERY_TYPE_TIMESTAMP, - query.id, - ); - } - - unsafe fn push_graphics_constants( - &mut self, - _layout: &r::PipelineLayout, - _stages: pso::ShaderStageFlags, - offset: u32, - constants: &[u32], - ) { - assert!(offset % 4 == 0); - self.gr_pipeline - .user_data - .set_constants(offset as usize / 4, constants); - } - - unsafe fn push_compute_constants( - &mut self, - _layout: &r::PipelineLayout, - offset: u32, - constants: &[u32], - ) { - assert!(offset % 4 == 0); - self.comp_pipeline - .user_data - .set_constants(offset as usize / 4, constants); - } - - unsafe fn execute_commands<'a, T>(&mut self, cmd_buffers: T) - where - T: Iterator, - { - for _cmd_buf in cmd_buffers { - error!("TODO: execute_commands"); - } - } - - unsafe fn insert_debug_marker(&mut self, name: &str, _color: u32) { - let (ptr, size) = self.fill_marker(name); - self.raw.SetMarker(0, ptr, size); - } - unsafe fn begin_debug_marker(&mut self, name: &str, _color: u32) { - let (ptr, size) = self.fill_marker(name); - self.raw.BeginEvent(0, ptr, size); - } - unsafe fn end_debug_marker(&mut self) { - self.raw.EndEvent(); - } -} +use auxil::FastHashMap; +use hal::{ + buffer, command as com, format, format::Aspects, image, memory, pass, pso, query, DrawCount, + IndexCount, IndexType, InstanceCount, TaskCount, VertexCount, VertexOffset, WorkGroupCount, +}; + +use arrayvec::ArrayVec; +use smallvec::SmallVec; +use winapi::{ + ctypes, + shared::{dxgiformat, minwindef, winerror}, + um::{d3d12, d3dcommon}, + Interface, +}; + +use std::{cmp, fmt, iter, mem, ops::Range, ptr, sync::Arc}; + +use crate::{ + conv, descriptors_cpu, device, internal, + pool::{CommandAllocatorIndex, PoolShared}, + resource as r, validate_line_width, Backend, Device, Shared, MAX_DESCRIPTOR_SETS, + MAX_VERTEX_BUFFERS, +}; + +// Fixed size of the root signature. +// Limited by D3D12. +const ROOT_SIGNATURE_SIZE: usize = 64; + +const NULL_VERTEX_BUFFER_VIEW: d3d12::D3D12_VERTEX_BUFFER_VIEW = d3d12::D3D12_VERTEX_BUFFER_VIEW { + BufferLocation: 0, + SizeInBytes: 0, + StrideInBytes: 0, +}; + +fn get_rect(rect: &pso::Rect) -> d3d12::D3D12_RECT { + d3d12::D3D12_RECT { + left: rect.x as i32, + top: rect.y as i32, + right: (rect.x + rect.w) as i32, + bottom: (rect.y + rect.h) as i32, + } +} + +fn div(a: u32, b: u32) -> u32 { + (a + b - 1) / b +} + +fn up_align(x: u32, alignment: u32) -> u32 { + (x + alignment - 1) & !(alignment - 1) +} + +#[derive(Clone, Debug)] +struct AttachmentInfo { + subpass_id: Option, + view: r::ImageView, + clear_value: Option, + stencil_value: Option, +} + +pub struct RenderPassCache { + render_pass: r::RenderPass, + target_rect: d3d12::D3D12_RECT, + attachments: Vec, + num_layers: image::Layer, + has_name: bool, +} + +impl fmt::Debug for RenderPassCache { + fn fmt(&self, fmt: &mut fmt::Formatter) -> fmt::Result { + fmt.write_str("RenderPassCache") + } +} + +#[derive(Debug, Copy, Clone, PartialEq, Eq)] +enum OcclusionQuery { + Binary(minwindef::UINT), + Precise(minwindef::UINT), +} + +/// Strongly-typed root signature element +/// +/// Could be removed for an unsafer variant to occupy less memory +#[derive(Debug, Copy, Clone, PartialEq, Eq)] +enum RootElement { + /// Root constant in the signature + Constant(u32), + /// Descriptor table, storing table offset for the current descriptor heap + TableSrvCbvUav(u32), + /// Descriptor table, storing table offset for the current descriptor heap + TableSampler(u32), + /// Root descriptors take 2 DWORDs in the root signature. + DescriptorCbv { + buffer: u64, + }, + DescriptorPlaceholder, + /// Undefined value, implementation specific + Undefined, +} + +/// Virtual data storage for the current root signature memory. +struct UserData { + data: [RootElement; ROOT_SIGNATURE_SIZE], + dirty_mask: u64, +} + +impl Default for UserData { + fn default() -> Self { + UserData { + data: [RootElement::Undefined; ROOT_SIGNATURE_SIZE], + dirty_mask: 0, + } + } +} + +impl fmt::Debug for UserData { + fn fmt(&self, fmt: &mut fmt::Formatter<'_>) -> fmt::Result { + fmt.debug_struct("UserData") + .field("data", &&self.data[..]) + .field("dirty_mask", &self.dirty_mask) + .finish() + } +} + +impl UserData { + /// Write root constant values into the user data, overwriting virtual memory + /// range [offset..offset + data.len()]. Changes are marked as dirty. + fn set_constants(&mut self, offset: usize, data: &[u32]) { + assert!(offset + data.len() <= ROOT_SIGNATURE_SIZE); + // Each root constant occupies one DWORD + for (i, val) in data.iter().enumerate() { + self.data[offset + i] = RootElement::Constant(*val); + self.dirty_mask |= 1u64 << (offset + i); + } + } + + /// Write a SRV/CBV/UAV descriptor table into the user data, overwriting virtual + /// memory range [offset..offset + 1]. Changes are marked as dirty. + fn set_srv_cbv_uav_table(&mut self, offset: usize, table_start: u32) { + assert!(offset < ROOT_SIGNATURE_SIZE); + // A descriptor table occupies one DWORD + self.data[offset] = RootElement::TableSrvCbvUav(table_start); + self.dirty_mask |= 1u64 << offset; + } + + /// Write a sampler descriptor table into the user data, overwriting virtual + /// memory range [offset..offset + 1]. Changes are marked as dirty. + fn set_sampler_table(&mut self, offset: usize, table_start: u32) { + assert!(offset < ROOT_SIGNATURE_SIZE); + // A descriptor table occupies one DWORD + self.data[offset] = RootElement::TableSampler(table_start); + self.dirty_mask |= 1u64 << offset; + } + + /// Write a CBV root descriptor into the user data, overwriting virtual + /// memory range [offset..offset + 2]. Changes are marked as dirty. + fn set_descriptor_cbv(&mut self, offset: usize, buffer: u64) { + assert!(offset + 1 < ROOT_SIGNATURE_SIZE); + // A root descriptor occupies two DWORDs + self.data[offset] = RootElement::DescriptorCbv { buffer }; + self.data[offset + 1] = RootElement::DescriptorPlaceholder; + self.dirty_mask |= 1u64 << offset; + self.dirty_mask |= 1u64 << offset + 1; + } + + fn is_dirty(&self) -> bool { + self.dirty_mask != 0 + } + + fn is_index_dirty(&self, i: u32) -> bool { + ((self.dirty_mask >> i) & 1) == 1 + } + + /// Mark all entries as dirty. + fn dirty_all(&mut self) { + self.dirty_mask = !0; + } + + /// Mark all entries as clear up to the given i. + fn clear_up_to(&mut self, i: u32) { + self.dirty_mask &= !((1 << i) - 1); + } +} + +#[derive(Debug, Default)] +struct PipelineCache { + // Bound pipeline and layout info. + // Changed on bind pipeline calls. + pipeline: Option<(native::PipelineState, Arc)>, + + // Virtualized root signature user data of the shaders + user_data: UserData, + + temp_constants: Vec, + + // Descriptor heap gpu handle offsets + srv_cbv_uav_start: u64, + sampler_start: u64, +} + +impl PipelineCache { + fn bind_descriptor_sets<'a, J>( + &mut self, + layout: &r::PipelineLayout, + first_set: usize, + sets: &[&r::DescriptorSet], + offsets: J, + ) -> [native::DescriptorHeap; 2] + where + J: Iterator, + { + let mut offsets = offsets.map(|offset| offset as u64); + + // 'Global' GPU descriptor heaps. + // All descriptors live in the same heaps. + let (srv_cbv_uav_start, sampler_start, heap_srv_cbv_uav, heap_sampler) = + if let Some(set) = sets.first() { + ( + set.srv_cbv_uav_gpu_start().ptr, + set.sampler_gpu_start().ptr, + set.heap_srv_cbv_uav, + set.heap_samplers, + ) + } else { + return [native::DescriptorHeap::null(); 2]; + }; + + self.srv_cbv_uav_start = srv_cbv_uav_start; + self.sampler_start = sampler_start; + + for (&set, element) in sets.iter().zip(layout.elements[first_set..].iter()) { + let mut root_offset = element.table.offset; + + // Bind CBV/SRC/UAV descriptor tables + if let Some(gpu) = set.first_gpu_view { + assert!(element.table.ty.contains(r::SRV_CBV_UAV)); + // Cast is safe as offset **must** be in u32 range. Unable to + // create heaps with more descriptors. + let table_gpu_offset = (gpu.ptr - srv_cbv_uav_start) as u32; + self.user_data + .set_srv_cbv_uav_table(root_offset, table_gpu_offset); + root_offset += 1; + } + + // Bind Sampler descriptor tables. + if let Some(gpu) = set.first_gpu_sampler { + assert!(element.table.ty.contains(r::SAMPLERS)); + + // Cast is safe as offset **must** be in u32 range. Unable to + // create heaps with more descriptors. + let table_gpu_offset = (gpu.ptr - sampler_start) as u32; + self.user_data + .set_sampler_table(root_offset, table_gpu_offset); + root_offset += 1; + } + + // Bind root descriptors + // TODO: slow, can we move the dynamic descriptors into toplevel somehow during initialization? + // Requires changes then in the descriptor update process. + for binding in &set.binding_infos { + // It's not valid to modify the descriptor sets during recording -> access if safe. + for descriptor in binding.dynamic_descriptors.iter() { + let gpu_offset = descriptor.gpu_buffer_location + offsets.next().unwrap(); + self.user_data.set_descriptor_cbv(root_offset, gpu_offset); + root_offset += 2; + } + } + } + + [heap_srv_cbv_uav, heap_sampler] + } + + fn flush_user_data( + &mut self, + mut constants_update: F, + mut table_update: G, + mut descriptor_cbv_update: H, + ) where + F: FnMut(u32, &[u32]), + G: FnMut(u32, d3d12::D3D12_GPU_DESCRIPTOR_HANDLE), + H: FnMut(u32, d3d12::D3D12_GPU_VIRTUAL_ADDRESS), + { + let user_data = &mut self.user_data; + if !user_data.is_dirty() { + return; + } + + let shared = match self.pipeline { + Some((_, ref shared)) => shared, + None => return, + }; + + for (root_index, &root_offset) in shared.parameter_offsets.iter().enumerate() { + if !user_data.is_index_dirty(root_offset) { + continue; + } + match user_data.data[root_offset as usize] { + RootElement::Constant(_) => { + let c = &shared.constants[root_index]; + debug_assert_eq!(root_offset, c.range.start); + self.temp_constants.clear(); + self.temp_constants.extend( + user_data.data[c.range.start as usize..c.range.end as usize] + .iter() + .map(|ud| match *ud { + RootElement::Constant(v) => v, + _ => { + warn!( + "Unset or mismatching root constant at index {:?} ({:?})", + c, ud + ); + 0 + } + }), + ); + constants_update(root_index as u32, &self.temp_constants); + } + RootElement::TableSrvCbvUav(offset) => { + let gpu = d3d12::D3D12_GPU_DESCRIPTOR_HANDLE { + ptr: self.srv_cbv_uav_start + offset as u64, + }; + table_update(root_index as u32, gpu); + } + RootElement::TableSampler(offset) => { + let gpu = d3d12::D3D12_GPU_DESCRIPTOR_HANDLE { + ptr: self.sampler_start + offset as u64, + }; + table_update(root_index as u32, gpu); + } + RootElement::DescriptorCbv { buffer } => { + debug_assert!(user_data.is_index_dirty(root_offset + 1)); + debug_assert_eq!( + user_data.data[root_offset as usize + 1], + RootElement::DescriptorPlaceholder + ); + + descriptor_cbv_update(root_index as u32, buffer); + } + RootElement::DescriptorPlaceholder | RootElement::Undefined => { + error!( + "Undefined user data element in the root signature at {}", + root_offset + ); + } + } + } + + user_data.clear_up_to(shared.total_slots); + } +} + +#[derive(Debug, Copy, Clone, PartialEq, Eq)] +enum BindPoint { + Compute, + Graphics { + /// Internal pipelines used for blitting, copying, etc. + internal: bool, + }, +} + +#[derive(Clone, Debug)] +struct Copy { + footprint_offset: u64, + footprint: image::Extent, + row_pitch: u32, + img_subresource: u32, + img_offset: image::Offset, + buf_offset: image::Offset, + copy_extent: image::Extent, +} + +#[derive(Clone, Copy, Debug, PartialEq)] +enum Phase { + Initial, + Recording, + Executable, + // Pending, // not useful, and we don't have mutable access + // to self in `submit()`, so we can't set it anyway. +} + +pub struct CommandBuffer { + //Note: this is going to be NULL instead of `Option` to avoid + // `unwrap()` on every operation. This is not idiomatic. + pub(crate) raw: native::GraphicsCommandList, + //Note: this is NULL when the command buffer is reset, and no + // allocation is used for the `raw` list. + allocator_index: Option, + phase: Phase, + shared: Arc, + pool_shared: Arc, + begin_flags: com::CommandBufferFlags, + + /// Cache renderpasses for graphics operations + pass_cache: Option, + cur_subpass: pass::SubpassId, + + /// Cache current graphics root signature and pipeline to minimize rebinding and support two + /// bindpoints. + gr_pipeline: PipelineCache, + /// Primitive topology of the currently bound graphics pipeline. + /// Caching required for internal graphics pipelines. + primitive_topology: d3d12::D3D12_PRIMITIVE_TOPOLOGY, + /// Cache current compute root signature and pipeline. + comp_pipeline: PipelineCache, + /// D3D12 only has one slot for both bindpoints. Need to rebind everything if we want to switch + /// between different bind points (ie. calling draw or dispatch). + active_bindpoint: BindPoint, + /// Current descriptor heaps heaps (CBV/SRV/UAV and Sampler). + /// Required for resetting due to internal descriptor heaps. + active_descriptor_heaps: [native::DescriptorHeap; 2], + + /// Active queries in the command buffer. + /// Queries must begin and end in the same command buffer, which allows us to track them. + /// The query pool type on `begin_query` must differ from all currently active queries. + /// Therefore, only one query per query type can be active at the same time. Binary and precise + /// occlusion queries share one queue type in Vulkan. + occlusion_query: Option, + pipeline_stats_query: Option, + + /// Cached vertex buffer views to bind. + /// `Stride` values are not known at `bind_vertex_buffers` time because they are only stored + /// inside the pipeline state. + vertex_bindings_remap: [Option; MAX_VERTEX_BUFFERS], + + vertex_buffer_views: [d3d12::D3D12_VERTEX_BUFFER_VIEW; MAX_VERTEX_BUFFERS], + + /// Re-using allocation for the image-buffer copies. + copies: Vec, + + /// D3D12 only allows setting all viewports or all scissors at once, not partial updates. + /// So we must cache the implied state for these partial updates. + viewport_cache: ArrayVec< + [d3d12::D3D12_VIEWPORT; + d3d12::D3D12_VIEWPORT_AND_SCISSORRECT_OBJECT_COUNT_PER_PIPELINE as usize], + >, + scissor_cache: ArrayVec< + [d3d12::D3D12_RECT; + d3d12::D3D12_VIEWPORT_AND_SCISSORRECT_OBJECT_COUNT_PER_PIPELINE as usize], + >, + + /// HACK: renderdoc workaround for temporary RTVs + rtv_pools: Vec, + /// Temporary gpu descriptor heaps (internal). + temporary_gpu_heaps: Vec, + /// Resources that need to be alive till the end of the GPU execution. + retained_resources: Vec, + + /// Temporary wide string for the marker. + temp_marker: Vec, + + /// Temporary transition barriers. + barriers: Vec, + /// Name of the underlying raw `GraphicsCommandList` object. + pub(crate) raw_name: Vec, +} + +impl fmt::Debug for CommandBuffer { + fn fmt(&self, fmt: &mut fmt::Formatter) -> fmt::Result { + fmt.write_str("CommandBuffer") + } +} + +unsafe impl Send for CommandBuffer {} +unsafe impl Sync for CommandBuffer {} + +// Insetion point for subpasses. +enum BarrierPoint { + // Pre barriers are inserted of the beginning when switching into a new subpass. + Pre, + // Post barriers are applied after exectuing the user defined commands. + Post, +} + +impl CommandBuffer { + pub(crate) fn new(shared: &Arc, pool_shared: &Arc) -> Self { + CommandBuffer { + raw: native::GraphicsCommandList::null(), + allocator_index: None, + shared: Arc::clone(shared), + pool_shared: Arc::clone(pool_shared), + phase: Phase::Initial, + begin_flags: com::CommandBufferFlags::empty(), + pass_cache: None, + cur_subpass: !0, + gr_pipeline: PipelineCache::default(), + primitive_topology: d3dcommon::D3D_PRIMITIVE_TOPOLOGY_UNDEFINED, + comp_pipeline: PipelineCache::default(), + active_bindpoint: BindPoint::Graphics { internal: false }, + active_descriptor_heaps: [native::DescriptorHeap::null(); 2], + occlusion_query: None, + pipeline_stats_query: None, + vertex_bindings_remap: [None; MAX_VERTEX_BUFFERS], + vertex_buffer_views: [NULL_VERTEX_BUFFER_VIEW; MAX_VERTEX_BUFFERS], + copies: Vec::new(), + viewport_cache: ArrayVec::new(), + scissor_cache: ArrayVec::new(), + rtv_pools: Vec::new(), + temporary_gpu_heaps: Vec::new(), + retained_resources: Vec::new(), + temp_marker: Vec::new(), + barriers: Vec::new(), + raw_name: Vec::new(), + } + } + + pub(crate) unsafe fn destroy( + self, + ) -> Option<(CommandAllocatorIndex, Option)> { + let list = match self.phase { + Phase::Initial => None, + Phase::Recording => { + self.raw.close(); + Some(self.raw) + } + Phase::Executable => Some(self.raw), + }; + for heap in &self.rtv_pools { + heap.destroy(); + } + for heap in &self.temporary_gpu_heaps { + heap.destroy(); + } + for resource in &self.retained_resources { + resource.destroy(); + } + self.allocator_index.map(|index| (index, list)) + } + + pub(crate) unsafe fn as_raw_list(&self) -> *mut d3d12::ID3D12CommandList { + match self.phase { + Phase::Executable => (), + other => error!("Submitting a command buffer in {:?} state", other), + } + self.raw.as_mut_ptr() as *mut _ + } + + // Indicates that the pipeline slot has been overriden with an internal pipeline. + // + // This only invalidates the slot and the user data! + fn set_internal_graphics_pipeline(&mut self) { + self.active_bindpoint = BindPoint::Graphics { internal: true }; + self.gr_pipeline.user_data.dirty_all(); + } + + fn bind_descriptor_heaps(&mut self) { + self.raw.set_descriptor_heaps(&self.active_descriptor_heaps); + } + + fn mark_bound_descriptor(&mut self, index: usize, set: &r::DescriptorSet) { + if !set.raw_name.is_empty() { + self.temp_marker.clear(); + self.temp_marker.push(0x20); // ' ' + self.temp_marker.push(0x30 + index as u16); // '1..9' + self.temp_marker.push(0x3A); // ':' + self.temp_marker.push(0x20); // ' ' + self.temp_marker.extend_from_slice(&set.raw_name); + unsafe { + self.raw.SetMarker( + 0, + self.temp_marker.as_ptr() as *const _, + self.temp_marker.len() as u32 * 2, + ) + }; + } + } + + unsafe fn insert_subpass_barriers(&mut self, insertion: BarrierPoint) { + let state = self.pass_cache.as_ref().unwrap(); + let proto_barriers = match state.render_pass.subpasses.get(self.cur_subpass as usize) { + Some(subpass) => match insertion { + BarrierPoint::Pre => &subpass.pre_barriers, + BarrierPoint::Post => &subpass.post_barriers, + }, + None => &state.render_pass.post_barriers, + }; + + self.barriers.clear(); + for barrier in proto_barriers { + let mut resource_barrier = d3d12::D3D12_RESOURCE_BARRIER { + Type: d3d12::D3D12_RESOURCE_BARRIER_TYPE_TRANSITION, + Flags: barrier.flags, + u: mem::zeroed(), + }; + + *resource_barrier.u.Transition_mut() = d3d12::D3D12_RESOURCE_TRANSITION_BARRIER { + pResource: state.attachments[barrier.attachment_id] + .view + .resource + .as_mut_ptr(), + Subresource: d3d12::D3D12_RESOURCE_BARRIER_ALL_SUBRESOURCES, + StateBefore: barrier.states.start, + StateAfter: barrier.states.end, + }; + + self.barriers.push(resource_barrier); + } + + self.flush_barriers(); + } + + fn bind_targets(&mut self) { + let state = self.pass_cache.as_ref().unwrap(); + let subpass = &state.render_pass.subpasses[self.cur_subpass as usize]; + + // collect render targets + let color_views = subpass + .color_attachments + .iter() + .map(|&(id, _)| state.attachments[id].view.handle_rtv.raw().unwrap()) + .collect::>(); + let ds_view = match subpass.depth_stencil_attachment { + Some((id, _)) => state.attachments[id] + .view + .handle_dsv + .as_ref() + .map(|handle| &handle.raw) + .unwrap() as *const _, + None => ptr::null(), + }; + // set render targets + unsafe { + self.raw.OMSetRenderTargets( + color_views.len() as _, + color_views.as_ptr(), + minwindef::FALSE, + ds_view, + ); + } + + // performs clears for all the attachments first used in this subpass + for at in state.attachments.iter() { + if at.subpass_id != Some(self.cur_subpass) { + continue; + } + + if let (Some(rtv), Some(cv)) = (at.view.handle_rtv.raw(), at.clear_value) { + self.clear_render_target_view(rtv, unsafe { cv.color }, &[state.target_rect]); + } + + if let Some(handle) = at.view.handle_dsv { + let depth = at.clear_value.map(|cv| unsafe { cv.depth_stencil.depth }); + let stencil = at.stencil_value; + + if depth.is_some() || stencil.is_some() { + self.clear_depth_stencil_view(handle.raw, depth, stencil, &[state.target_rect]); + } + } + } + } + + fn resolve_attachments(&self) { + let state = self.pass_cache.as_ref().unwrap(); + let subpass = &state.render_pass.subpasses[self.cur_subpass as usize]; + + for (&(src_attachment, _), &(dst_attachment, _)) in subpass + .color_attachments + .iter() + .zip(subpass.resolve_attachments.iter()) + { + if dst_attachment == pass::ATTACHMENT_UNUSED { + continue; + } + + let resolve_src = &state.attachments[src_attachment].view; + let resolve_dst = &state.attachments[dst_attachment].view; + + // The number of layers of the render area are given on framebuffer creation. + for l in 0..state.num_layers { + // Attachtments only have a single mip level by specification. + let subresource_src = resolve_src.calc_subresource( + resolve_src.mip_levels.0 as _, + (resolve_src.layers.0 + l) as _, + ); + let subresource_dst = resolve_dst.calc_subresource( + resolve_dst.mip_levels.0 as _, + (resolve_dst.layers.0 + l) as _, + ); + + // TODO: take width and height of render area into account. + unsafe { + self.raw.ResolveSubresource( + resolve_dst.resource.as_mut_ptr(), + subresource_dst, + resolve_src.resource.as_mut_ptr(), + subresource_src, + resolve_dst.dxgi_format, + ); + } + } + } + } + + fn clear_render_target_view( + &self, + rtv: d3d12::D3D12_CPU_DESCRIPTOR_HANDLE, + color: com::ClearColor, + rects: &[d3d12::D3D12_RECT], + ) { + let num_rects = rects.len() as _; + let rects = if num_rects > 0 { + rects.as_ptr() + } else { + ptr::null() + }; + + unsafe { + self.raw + .clone() + .ClearRenderTargetView(rtv, &color.float32, num_rects, rects); + } + } + + fn clear_depth_stencil_view( + &self, + dsv: d3d12::D3D12_CPU_DESCRIPTOR_HANDLE, + depth: Option, + stencil: Option, + rects: &[d3d12::D3D12_RECT], + ) { + let mut flags = native::ClearFlags::empty(); + if depth.is_some() { + flags |= native::ClearFlags::DEPTH; + } + if stencil.is_some() { + flags |= native::ClearFlags::STENCIL; + } + + self.raw.clear_depth_stencil_view( + dsv, + flags, + depth.unwrap_or_default(), + stencil.unwrap_or_default() as _, + rects, + ); + } + + fn set_graphics_bind_point(&mut self) { + match self.active_bindpoint { + BindPoint::Compute => { + // Switch to graphics bind point + let &(pipeline, _) = self + .gr_pipeline + .pipeline + .as_ref() + .expect("No graphics pipeline bound"); + self.raw.set_pipeline_state(pipeline); + } + BindPoint::Graphics { internal: true } => { + // Switch to graphics bind point + let &(pipeline, ref shared) = self + .gr_pipeline + .pipeline + .as_ref() + .expect("No graphics pipeline bound"); + self.raw.set_pipeline_state(pipeline); + self.raw.set_graphics_root_signature(shared.signature); + self.bind_descriptor_heaps(); + } + BindPoint::Graphics { internal: false } => {} + } + + self.active_bindpoint = BindPoint::Graphics { internal: false }; + let cmd_buffer = &mut self.raw; + + // Flush root signature data + self.gr_pipeline.flush_user_data( + |slot, data| unsafe { + cmd_buffer.clone().SetGraphicsRoot32BitConstants( + slot, + data.len() as _, + data.as_ptr() as *const _, + 0, + ) + }, + |slot, gpu| cmd_buffer.set_graphics_root_descriptor_table(slot, gpu), + |slot, buffer| cmd_buffer.set_graphics_root_constant_buffer_view(slot, buffer), + ); + } + + fn set_compute_bind_point(&mut self) { + match self.active_bindpoint { + BindPoint::Graphics { internal } => { + // Switch to compute bind point + let &(pipeline, _) = self + .comp_pipeline + .pipeline + .as_ref() + .expect("No compute pipeline bound"); + + self.raw.set_pipeline_state(pipeline); + + self.active_bindpoint = BindPoint::Compute; + + if internal { + self.bind_descriptor_heaps(); + // Rebind the graphics root signature as we come from an internal graphics. + // Issuing a draw call afterwards would hide the information that we internally + // changed the graphics root signature. + if let Some((_, ref shared)) = self.gr_pipeline.pipeline { + self.raw.set_graphics_root_signature(shared.signature); + } + } + } + BindPoint::Compute => {} // Nothing to do + } + + let cmd_buffer = &mut self.raw; + self.comp_pipeline.flush_user_data( + |slot, data| unsafe { + cmd_buffer.clone().SetComputeRoot32BitConstants( + slot, + data.len() as _, + data.as_ptr() as *const _, + 0, + ) + }, + |slot, gpu| cmd_buffer.set_compute_root_descriptor_table(slot, gpu), + |slot, buffer| cmd_buffer.set_compute_root_constant_buffer_view(slot, buffer), + ); + } + + fn transition_barrier( + transition: d3d12::D3D12_RESOURCE_TRANSITION_BARRIER, + ) -> d3d12::D3D12_RESOURCE_BARRIER { + let mut barrier = d3d12::D3D12_RESOURCE_BARRIER { + Type: d3d12::D3D12_RESOURCE_BARRIER_TYPE_TRANSITION, + Flags: d3d12::D3D12_RESOURCE_BARRIER_FLAG_NONE, + u: unsafe { mem::zeroed() }, + }; + + *unsafe { barrier.u.Transition_mut() } = transition; + barrier + } + + fn dual_transition_barriers( + resource: native::Resource, + sub: u32, + states: Range, + ) -> (d3d12::D3D12_RESOURCE_BARRIER, d3d12::D3D12_RESOURCE_BARRIER) { + ( + Self::transition_barrier(d3d12::D3D12_RESOURCE_TRANSITION_BARRIER { + pResource: resource.as_mut_ptr(), + Subresource: sub, + StateBefore: states.start, + StateAfter: states.end, + }), + Self::transition_barrier(d3d12::D3D12_RESOURCE_TRANSITION_BARRIER { + pResource: resource.as_mut_ptr(), + Subresource: sub, + StateBefore: states.end, + StateAfter: states.start, + }), + ) + } + + fn split_buffer_copy(copies: &mut Vec, r: com::BufferImageCopy, image: &r::ImageBound) { + let buffer_width = if r.buffer_width == 0 { + r.image_extent.width + } else { + r.buffer_width + }; + let buffer_height = if r.buffer_height == 0 { + r.image_extent.height + } else { + r.buffer_height + }; + let desc = image.surface_type.desc(); + let bytes_per_block = desc.bits as u32 / 8; + let image_extent_aligned = image::Extent { + width: up_align(r.image_extent.width, desc.dim.0 as _), + height: up_align(r.image_extent.height, desc.dim.1 as _), + depth: r.image_extent.depth, + }; + let row_pitch = div(buffer_width, desc.dim.0 as _) * bytes_per_block; + let slice_pitch = div(buffer_height, desc.dim.1 as _) * row_pitch; + let is_pitch_aligned = row_pitch % d3d12::D3D12_TEXTURE_DATA_PITCH_ALIGNMENT == 0; + + for layer in r.image_layers.layers.clone() { + let img_subresource = image.calc_subresource(r.image_layers.level as _, layer as _, 0); + let layer_relative = (layer - r.image_layers.layers.start) as u32; + let layer_offset = r.buffer_offset as u64 + + (layer_relative * slice_pitch * r.image_extent.depth) as u64; + let aligned_offset = + layer_offset & !(d3d12::D3D12_TEXTURE_DATA_PLACEMENT_ALIGNMENT as u64 - 1); + if layer_offset == aligned_offset && is_pitch_aligned { + // trivial case: everything is aligned, ready for copying + copies.push(Copy { + footprint_offset: aligned_offset, + footprint: image_extent_aligned, + row_pitch, + img_subresource, + img_offset: r.image_offset, + buf_offset: image::Offset::ZERO, + copy_extent: image_extent_aligned, + }); + } else if is_pitch_aligned { + // buffer offset is not aligned + let row_pitch_texels = row_pitch / bytes_per_block * desc.dim.0 as u32; + let gap = (layer_offset - aligned_offset) as i32; + let buf_offset = image::Offset { + x: (gap % row_pitch as i32) / bytes_per_block as i32 * desc.dim.0 as i32, + y: (gap % slice_pitch as i32) / row_pitch as i32 * desc.dim.1 as i32, + z: gap / slice_pitch as i32, + }; + let footprint = image::Extent { + width: buf_offset.x as u32 + image_extent_aligned.width, + height: buf_offset.y as u32 + image_extent_aligned.height, + depth: buf_offset.z as u32 + image_extent_aligned.depth, + }; + if r.image_extent.width + buf_offset.x as u32 <= row_pitch_texels { + // we can map it to the aligned one and adjust the offsets accordingly + copies.push(Copy { + footprint_offset: aligned_offset, + footprint, + row_pitch, + img_subresource, + img_offset: r.image_offset, + buf_offset, + copy_extent: image_extent_aligned, + }); + } else { + // split the copy region into 2 that suffice the previous condition + assert!(buf_offset.x as u32 <= row_pitch_texels); + let half = row_pitch_texels - buf_offset.x as u32; + assert!(half <= r.image_extent.width); + + copies.push(Copy { + footprint_offset: aligned_offset, + footprint: image::Extent { + width: row_pitch_texels, + ..footprint + }, + row_pitch, + img_subresource, + img_offset: r.image_offset, + buf_offset, + copy_extent: image::Extent { + width: half, + ..r.image_extent + }, + }); + copies.push(Copy { + footprint_offset: aligned_offset, + footprint: image::Extent { + width: image_extent_aligned.width - half, + height: footprint.height + desc.dim.1 as u32, + depth: footprint.depth, + }, + row_pitch, + img_subresource, + img_offset: image::Offset { + x: r.image_offset.x + half as i32, + ..r.image_offset + }, + buf_offset: image::Offset { + x: 0, + y: buf_offset.y + desc.dim.1 as i32, + z: buf_offset.z, + }, + copy_extent: image::Extent { + width: image_extent_aligned.width - half, + ..image_extent_aligned + }, + }); + } + } else { + // worst case: row by row copy + for z in 0..r.image_extent.depth { + for y in 0..image_extent_aligned.height / desc.dim.1 as u32 { + // an image row starts non-aligned + let row_offset = layer_offset + + z as u64 * slice_pitch as u64 + + y as u64 * row_pitch as u64; + let aligned_offset = row_offset + & !(d3d12::D3D12_TEXTURE_DATA_PLACEMENT_ALIGNMENT as u64 - 1); + let next_aligned_offset = + aligned_offset + d3d12::D3D12_TEXTURE_DATA_PLACEMENT_ALIGNMENT as u64; + let cut_row_texels = (next_aligned_offset - row_offset) + / bytes_per_block as u64 + * desc.dim.0 as u64; + let cut_width = + cmp::min(image_extent_aligned.width, cut_row_texels as image::Size); + let gap_texels = (row_offset - aligned_offset) as image::Size + / bytes_per_block as image::Size + * desc.dim.0 as image::Size; + // this is a conservative row pitch that should be compatible with both copies + let max_unaligned_pitch = + (r.image_extent.width + gap_texels) * bytes_per_block; + let row_pitch = (max_unaligned_pitch + | (d3d12::D3D12_TEXTURE_DATA_PITCH_ALIGNMENT - 1)) + + 1; + + copies.push(Copy { + footprint_offset: aligned_offset, + footprint: image::Extent { + width: cut_width + gap_texels, + height: desc.dim.1 as _, + depth: 1, + }, + row_pitch, + img_subresource, + img_offset: image::Offset { + x: r.image_offset.x, + y: r.image_offset.y + desc.dim.1 as i32 * y as i32, + z: r.image_offset.z + z as i32, + }, + buf_offset: image::Offset { + x: gap_texels as i32, + y: 0, + z: 0, + }, + copy_extent: image::Extent { + width: cut_width, + height: desc.dim.1 as _, + depth: 1, + }, + }); + + // and if it crosses a pitch alignment - we copy the rest separately + if cut_width >= image_extent_aligned.width { + continue; + } + let leftover = image_extent_aligned.width - cut_width; + + copies.push(Copy { + footprint_offset: next_aligned_offset, + footprint: image::Extent { + width: leftover, + height: desc.dim.1 as _, + depth: 1, + }, + row_pitch, + img_subresource, + img_offset: image::Offset { + x: r.image_offset.x + cut_width as i32, + y: r.image_offset.y + y as i32 * desc.dim.1 as i32, + z: r.image_offset.z + z as i32, + }, + buf_offset: image::Offset::ZERO, + copy_extent: image::Extent { + width: leftover, + height: desc.dim.1 as _, + depth: 1, + }, + }); + } + } + } + } + } + + fn set_vertex_buffers(&mut self) { + let cmd_buffer = &mut self.raw; + let vbs_remap = &self.vertex_bindings_remap; + let vbs = &self.vertex_buffer_views; + let mut last_end_slot = 0; + loop { + let start_offset = match vbs_remap[last_end_slot..] + .iter() + .position(|remap| remap.is_some()) + { + Some(offset) => offset, + None => break, + }; + + let start_slot = last_end_slot + start_offset; + let buffers = vbs_remap[start_slot..] + .iter() + .take_while(|x| x.is_some()) + .filter_map(|mapping| { + let mapping = mapping.unwrap(); + let view = vbs[mapping.mapped_binding]; + // Skip bindings that don't make sense. Since this function is called eagerly, + // we expect it to be called with all the valid inputs prior to drawing. + view.SizeInBytes.checked_sub(mapping.offset).map(|size| { + d3d12::D3D12_VERTEX_BUFFER_VIEW { + BufferLocation: view.BufferLocation + mapping.offset as u64, + SizeInBytes: size, + StrideInBytes: mapping.stride, + } + }) + }) + .collect::>(); + + if buffers.is_empty() { + last_end_slot = start_slot + 1; + } else { + let num_views = buffers.len(); + unsafe { + cmd_buffer.IASetVertexBuffers( + start_slot as _, + num_views as _, + buffers.as_ptr(), + ); + } + last_end_slot = start_slot + num_views; + } + } + } + + fn flip_barriers(&mut self) { + for barrier in self.barriers.iter_mut() { + if barrier.Type == d3d12::D3D12_RESOURCE_BARRIER_TYPE_TRANSITION { + let transition = unsafe { barrier.u.Transition_mut() }; + mem::swap(&mut transition.StateBefore, &mut transition.StateAfter); + } + } + } + + fn _set_buffer_barrier( + &mut self, + target: &r::BufferBound, + state: d3d12::D3D12_RESOURCE_STATES, + ) { + let barrier = Self::transition_barrier(d3d12::D3D12_RESOURCE_TRANSITION_BARRIER { + pResource: target.resource.as_mut_ptr(), + Subresource: d3d12::D3D12_RESOURCE_BARRIER_ALL_SUBRESOURCES, + StateBefore: d3d12::D3D12_RESOURCE_STATE_COMMON, + StateAfter: state, + }); + self.barriers.clear(); + self.barriers.push(barrier); + } + + fn fill_texture_barries( + &mut self, + target: &r::ImageBound, + states: Range, + range: &image::SubresourceRange, + ) { + if states.start == states.end { + return; + } + + let mut bar = Self::transition_barrier(d3d12::D3D12_RESOURCE_TRANSITION_BARRIER { + pResource: target.resource.as_mut_ptr(), + Subresource: d3d12::D3D12_RESOURCE_BARRIER_ALL_SUBRESOURCES, + StateBefore: states.start, + StateAfter: states.end, + }); + let full_range = image::SubresourceRange { + aspects: range.aspects, + ..Default::default() + }; + let num_levels = range.resolve_level_count(target.mip_levels); + let num_layers = range.resolve_layer_count(target.kind.num_layers()); + + if *range == full_range { + // Only one barrier if it affects the whole image. + self.barriers.push(bar); + } else { + // Generate barrier for each layer/level combination. + for rel_level in 0..num_levels { + for rel_layer in 0..num_layers { + unsafe { + let transition_barrier = &mut *bar.u.Transition_mut(); + transition_barrier.Subresource = target.calc_subresource( + (range.level_start + rel_level) as _, + (range.layer_start + rel_layer) as _, + 0, + ); + } + self.barriers.push(bar); + } + } + } + } + + fn fill_marker(&mut self, name: &str) -> (*const ctypes::c_void, u32) { + self.temp_marker.clear(); + self.temp_marker.extend(name.encode_utf16()); + self.temp_marker.push(0); + ( + self.temp_marker.as_ptr() as *const _, + self.temp_marker.len() as u32 * 2, + ) + } + + unsafe fn flush_barriers(&self) { + if !self.barriers.is_empty() { + self.raw + .ResourceBarrier(self.barriers.len() as _, self.barriers.as_ptr()); + } + } +} + +impl com::CommandBuffer for CommandBuffer { + unsafe fn begin( + &mut self, + flags: com::CommandBufferFlags, + _info: com::CommandBufferInheritanceInfo, + ) { + // TODO: Implement flags and secondary command buffers (bundles). + // Note: we need to be ready for a situation where the whole + // command pool was reset. + self.reset(true); + self.phase = Phase::Recording; + self.begin_flags = flags; + let (allocator_index, list) = self.pool_shared.acquire(); + + assert!(self.allocator_index.is_none()); + assert_eq!(self.raw, native::GraphicsCommandList::null()); + self.allocator_index = Some(allocator_index); + self.raw = list; + + if !self.raw_name.is_empty() { + self.raw.SetName(self.raw_name.as_ptr()); + } + } + + unsafe fn finish(&mut self) { + self.raw.close(); + assert_eq!(self.phase, Phase::Recording); + self.phase = Phase::Executable; + self.pool_shared + .release_allocator(self.allocator_index.unwrap()); + } + + unsafe fn reset(&mut self, _release_resources: bool) { + if self.phase == Phase::Recording { + self.raw.close(); + self.pool_shared + .release_allocator(self.allocator_index.unwrap()); + } + if self.phase != Phase::Initial { + // Reset the name so it won't get used later for an unnamed `CommandBuffer`. + const EMPTY_NAME: u16 = 0; + self.raw.SetName(&EMPTY_NAME); + + let allocator_index = self.allocator_index.take().unwrap(); + self.pool_shared.release_list(self.raw, allocator_index); + self.raw = native::GraphicsCommandList::null(); + } + self.phase = Phase::Initial; + + self.pass_cache = None; + self.cur_subpass = !0; + self.gr_pipeline = PipelineCache::default(); + self.primitive_topology = d3dcommon::D3D_PRIMITIVE_TOPOLOGY_UNDEFINED; + self.comp_pipeline = PipelineCache::default(); + self.active_bindpoint = BindPoint::Graphics { internal: false }; + self.active_descriptor_heaps = [native::DescriptorHeap::null(); 2]; + self.occlusion_query = None; + self.pipeline_stats_query = None; + self.vertex_bindings_remap = [None; MAX_VERTEX_BUFFERS]; + self.vertex_buffer_views = [NULL_VERTEX_BUFFER_VIEW; MAX_VERTEX_BUFFERS]; + for heap in self.rtv_pools.drain(..) { + heap.destroy(); + } + for heap in self.temporary_gpu_heaps.drain(..) { + heap.destroy(); + } + for resource in self.retained_resources.drain(..) { + resource.destroy(); + } + } + + unsafe fn begin_render_pass<'a, T>( + &mut self, + render_pass: &r::RenderPass, + framebuffer: &r::Framebuffer, + target_rect: pso::Rect, + attachment_infos: T, + _first_subpass: com::SubpassContents, + ) where + T: Iterator>, + { + // Make sure that no subpass works with Present as intermediate layout. + // This wouldn't make much sense, and proceeding with this constraint + // allows the state transitions generated from subpass dependencies + // to ignore the layouts completely. + assert!(!render_pass.subpasses.iter().any(|sp| sp + .color_attachments + .iter() + .chain(sp.depth_stencil_attachment.iter()) + .chain(sp.input_attachments.iter()) + .any(|aref| aref.1 == image::Layout::Present))); + + if !render_pass.raw_name.is_empty() { + let n = &render_pass.raw_name; + self.raw + .BeginEvent(0, n.as_ptr() as *const _, n.len() as u32 * 2); + } + + self.barriers.clear(); + let mut attachments = Vec::new(); + for (i, (info, attachment)) in attachment_infos + .zip(render_pass.attachments.iter()) + .enumerate() + { + let view = info.image_view.clone(); + // for swapchain views, we consider the initial layout to always be `General` + let pass_start_state = + conv::map_image_resource_state(image::Access::empty(), attachment.layouts.start); + if view.is_swapchain() && pass_start_state != d3d12::D3D12_RESOURCE_STATE_COMMON { + let barrier = d3d12::D3D12_RESOURCE_TRANSITION_BARRIER { + pResource: view.resource.as_mut_ptr(), + Subresource: d3d12::D3D12_RESOURCE_BARRIER_ALL_SUBRESOURCES, + StateBefore: d3d12::D3D12_RESOURCE_STATE_COMMON, + StateAfter: pass_start_state, + }; + self.barriers.push(Self::transition_barrier(barrier)); + } + + attachments.push(AttachmentInfo { + subpass_id: render_pass + .subpasses + .iter() + .position(|sp| sp.is_using(i)) + .map(|i| i as pass::SubpassId), + view, + clear_value: if attachment.ops.load == pass::AttachmentLoadOp::Clear { + Some(info.clear_value) + } else { + None + }, + stencil_value: if attachment.stencil_ops.load == pass::AttachmentLoadOp::Clear { + Some(info.clear_value.depth_stencil.stencil) + } else { + None + }, + }); + } + self.flush_barriers(); + + self.pass_cache = Some(RenderPassCache { + render_pass: render_pass.clone(), + target_rect: get_rect(&target_rect), + attachments, + num_layers: framebuffer.layers, + has_name: !render_pass.raw_name.is_empty(), + }); + self.cur_subpass = 0; + self.insert_subpass_barriers(BarrierPoint::Pre); + self.bind_targets(); + } + + unsafe fn next_subpass(&mut self, _contents: com::SubpassContents) { + self.insert_subpass_barriers(BarrierPoint::Post); + self.resolve_attachments(); + + self.cur_subpass += 1; + self.insert_subpass_barriers(BarrierPoint::Pre); + self.bind_targets(); + } + + unsafe fn end_render_pass(&mut self) { + self.insert_subpass_barriers(BarrierPoint::Post); + self.resolve_attachments(); + + self.cur_subpass = !0; + self.insert_subpass_barriers(BarrierPoint::Pre); + + let pc = self.pass_cache.take().unwrap(); + self.barriers.clear(); + for (at, attachment) in pc.attachments.iter().zip(pc.render_pass.attachments.iter()) { + // for swapchain views, we consider the initial layout to always be `General` + let pass_end_state = + conv::map_image_resource_state(image::Access::empty(), attachment.layouts.end); + if at.view.is_swapchain() && pass_end_state != d3d12::D3D12_RESOURCE_STATE_COMMON { + let barrier = d3d12::D3D12_RESOURCE_TRANSITION_BARRIER { + pResource: at.view.resource.as_mut_ptr(), + Subresource: d3d12::D3D12_RESOURCE_BARRIER_ALL_SUBRESOURCES, + StateBefore: pass_end_state, + StateAfter: d3d12::D3D12_RESOURCE_STATE_COMMON, + }; + self.barriers.push(Self::transition_barrier(barrier)); + } + } + self.flush_barriers(); + + if pc.has_name { + self.raw.EndEvent(); + } + } + + unsafe fn pipeline_barrier<'a, T>( + &mut self, + stages: Range, + _dependencies: memory::Dependencies, + barriers: T, + ) where + T: Iterator>, + { + self.barriers.clear(); + + // transition barriers + for barrier in barriers { + match barrier { + memory::Barrier::AllBuffers(_) | memory::Barrier::AllImages(_) => { + // Aliasing barrier with NULL resource is the closest we can get to + // a global memory barrier in Vulkan. + // Was suggested by a Microsoft representative as well as some of the IHVs. + let mut bar = d3d12::D3D12_RESOURCE_BARRIER { + Type: d3d12::D3D12_RESOURCE_BARRIER_TYPE_UAV, + Flags: d3d12::D3D12_RESOURCE_BARRIER_FLAG_NONE, + u: mem::zeroed(), + }; + *bar.u.UAV_mut() = d3d12::D3D12_RESOURCE_UAV_BARRIER { + pResource: ptr::null_mut(), + }; + self.barriers.push(bar); + } + memory::Barrier::Buffer { + ref states, + target, + ref families, + range: _, + } => { + // TODO: Implement queue family ownership transitions for dx12 + if let Some(f) = families { + if f.start.0 != f.end.0 { + unimplemented!("Queue family resource ownership transitions are not implemented for DX12 (attempted transition from queue family {} to {}", f.start.0, f.end.0); + } + } + + let state_src = conv::map_buffer_resource_state(states.start); + let state_dst = conv::map_buffer_resource_state(states.end); + + if state_src == state_dst { + continue; + } + + let target = target.expect_bound(); + let bar = Self::transition_barrier(d3d12::D3D12_RESOURCE_TRANSITION_BARRIER { + pResource: target.resource.as_mut_ptr(), + Subresource: d3d12::D3D12_RESOURCE_BARRIER_ALL_SUBRESOURCES, + StateBefore: state_src, + StateAfter: state_dst, + }); + + self.barriers.push(bar); + } + memory::Barrier::Image { + ref states, + target, + ref families, + ref range, + } => { + // TODO: Implement queue family ownership transitions for dx12 + if let Some(f) = families { + if f.start.0 != f.end.0 { + unimplemented!("Queue family resource ownership transitions are not implemented for DX12 (attempted transition from queue family {} to {}", f.start.0, f.end.0); + } + } + + let state_src = conv::map_image_resource_state(states.start.0, states.start.1); + let state_dst = conv::map_image_resource_state(states.end.0, states.end.1); + + let target = target.expect_bound(); + + match target.place { + r::Place::Heap { .. } => { + self.fill_texture_barries(target, state_src..state_dst, range); + } + r::Place::Swapchain { .. } => {} //ignore + } + } + } + } + + let all_shader_stages = pso::PipelineStage::VERTEX_SHADER + | pso::PipelineStage::FRAGMENT_SHADER + | pso::PipelineStage::COMPUTE_SHADER + | pso::PipelineStage::GEOMETRY_SHADER + | pso::PipelineStage::HULL_SHADER + | pso::PipelineStage::DOMAIN_SHADER; + + // UAV barriers + // + // TODO: Currently always add a global UAV barrier. + // WAR only requires an execution barrier but D3D12 seems to need + // a UAV barrier for this according to docs. Can we make this better? + if (stages.start & stages.end).intersects(all_shader_stages) { + let mut barrier = d3d12::D3D12_RESOURCE_BARRIER { + Type: d3d12::D3D12_RESOURCE_BARRIER_TYPE_UAV, + Flags: d3d12::D3D12_RESOURCE_BARRIER_FLAG_NONE, + u: mem::zeroed(), + }; + *barrier.u.UAV_mut() = d3d12::D3D12_RESOURCE_UAV_BARRIER { + pResource: ptr::null_mut(), + }; + self.barriers.push(barrier); + } + + // Alias barriers + // + // TODO: Optimize, don't always add an alias barrier + if false { + let mut barrier = d3d12::D3D12_RESOURCE_BARRIER { + Type: d3d12::D3D12_RESOURCE_BARRIER_TYPE_ALIASING, + Flags: d3d12::D3D12_RESOURCE_BARRIER_FLAG_NONE, + u: mem::zeroed(), + }; + *barrier.u.Aliasing_mut() = d3d12::D3D12_RESOURCE_ALIASING_BARRIER { + pResourceBefore: ptr::null_mut(), + pResourceAfter: ptr::null_mut(), + }; + self.barriers.push(barrier); + } + + self.flush_barriers(); + } + + unsafe fn clear_image( + &mut self, + image: &r::Image, + layout: image::Layout, + value: com::ClearValue, + subresource_ranges: T, + ) where + T: Iterator, + { + let image = image.expect_bound(); + let base_state = conv::map_image_resource_state(image::Access::TRANSFER_WRITE, layout); + + for sub in subresource_ranges { + if sub.level_start != 0 || image.mip_levels != 1 { + warn!("Clearing non-zero mipmap levels is not supported yet"); + } + let target_state = if sub.aspects.contains(Aspects::COLOR) { + d3d12::D3D12_RESOURCE_STATE_RENDER_TARGET + } else { + d3d12::D3D12_RESOURCE_STATE_DEPTH_WRITE + }; + + self.barriers.clear(); + self.fill_texture_barries(image, base_state..target_state, &sub); + self.flush_barriers(); + + for rel_layer in 0..sub.resolve_layer_count(image.kind.num_layers()) { + let layer = (sub.layer_start + rel_layer) as usize; + if sub.aspects.contains(Aspects::COLOR) { + let rtv = image.clear_cv[layer].raw; + self.clear_render_target_view(rtv, value.color, &[]); + } + if sub.aspects.contains(Aspects::DEPTH) { + let dsv = image.clear_dv[layer].raw; + self.clear_depth_stencil_view(dsv, Some(value.depth_stencil.depth), None, &[]); + } + if sub.aspects.contains(Aspects::STENCIL) { + let dsv = image.clear_sv[layer].raw; + self.clear_depth_stencil_view( + dsv, + None, + Some(value.depth_stencil.stencil as _), + &[], + ); + } + } + + self.flip_barriers(); + self.flush_barriers(); + } + } + + unsafe fn clear_attachments(&mut self, clears: T, rects: U) + where + T: Iterator, + U: Iterator, + { + let pass_cache = match self.pass_cache { + Some(ref cache) => cache, + None => panic!("`clear_attachments` can only be called inside a renderpass"), + }; + let sub_pass = &pass_cache.render_pass.subpasses[self.cur_subpass as usize]; + + let clear_rects: SmallVec<[pso::ClearRect; 4]> = rects.collect(); + + let device = self.shared.service_pipes.device; + + for clear in clears { + match clear { + com::AttachmentClear::Color { index, value } => { + let attachment = { + let rtv_id = sub_pass.color_attachments[index]; + &pass_cache.attachments[rtv_id.0].view + }; + + let mut rtv_pool = descriptors_cpu::HeapLinear::new( + device, + native::DescriptorHeapType::Rtv, + clear_rects.len(), + ); + + for clear_rect in &clear_rects { + assert!(attachment.layers.0 + clear_rect.layers.end <= attachment.layers.1); + let rect = [get_rect(&clear_rect.rect)]; + + let view_info = device::ViewInfo { + resource: attachment.resource, + kind: attachment.kind, + caps: image::ViewCapabilities::empty(), + view_kind: image::ViewKind::D2Array, + format: attachment.dxgi_format, + component_mapping: device::IDENTITY_MAPPING, + levels: attachment.mip_levels.0..attachment.mip_levels.1, + layers: attachment.layers.0 + clear_rect.layers.start + ..attachment.layers.0 + clear_rect.layers.end, + }; + let rtv = rtv_pool.alloc_handle(); + Device::view_image_as_render_target_impl(device, rtv, &view_info).unwrap(); + self.clear_render_target_view(rtv, value.into(), &rect); + } + + rtv_pool.destroy(); + } + com::AttachmentClear::DepthStencil { depth, stencil } => { + let attachment = { + let dsv_id = sub_pass.depth_stencil_attachment.unwrap(); + &pass_cache.attachments[dsv_id.0].view + }; + + let mut dsv_pool = descriptors_cpu::HeapLinear::new( + device, + native::DescriptorHeapType::Dsv, + clear_rects.len(), + ); + + for clear_rect in &clear_rects { + assert!(attachment.layers.0 + clear_rect.layers.end <= attachment.layers.1); + let rect = [get_rect(&clear_rect.rect)]; + + let view_info = device::ViewInfo { + resource: attachment.resource, + kind: attachment.kind, + caps: image::ViewCapabilities::empty(), + view_kind: image::ViewKind::D2Array, + format: attachment.dxgi_format, + component_mapping: device::IDENTITY_MAPPING, + levels: attachment.mip_levels.0..attachment.mip_levels.1, + layers: attachment.layers.0 + clear_rect.layers.start + ..attachment.layers.0 + clear_rect.layers.end, + }; + let dsv = dsv_pool.alloc_handle(); + Device::view_image_as_depth_stencil_impl(device, dsv, &view_info).unwrap(); + self.clear_depth_stencil_view(dsv, depth, stencil, &rect); + } + + dsv_pool.destroy(); + } + } + } + } + + unsafe fn resolve_image( + &mut self, + src: &r::Image, + _src_layout: image::Layout, + dst: &r::Image, + _dst_layout: image::Layout, + regions: T, + ) where + T: Iterator, + { + let src = src.expect_bound(); + let dst = dst.expect_bound(); + assert_eq!(src.descriptor.Format, dst.descriptor.Format); + + { + // Insert barrier for `COPY_DEST` to `RESOLVE_DEST` as we only expose + // `TRANSFER_WRITE` which is used for all copy commands. + let transition_barrier = + Self::transition_barrier(d3d12::D3D12_RESOURCE_TRANSITION_BARRIER { + pResource: dst.resource.as_mut_ptr(), + Subresource: d3d12::D3D12_RESOURCE_BARRIER_ALL_SUBRESOURCES, // TODO: only affected ranges + StateBefore: d3d12::D3D12_RESOURCE_STATE_COPY_DEST, + StateAfter: d3d12::D3D12_RESOURCE_STATE_RESOLVE_DEST, + }); + self.raw.ResourceBarrier(1, &transition_barrier); + } + + for r in regions { + for layer in 0..r.extent.depth as u32 { + self.raw.ResolveSubresource( + src.resource.as_mut_ptr(), + src.calc_subresource( + r.src_subresource.level as _, + r.src_subresource.layers.start as u32 + layer, + 0, + ), + dst.resource.as_mut_ptr(), + dst.calc_subresource( + r.dst_subresource.level as _, + r.dst_subresource.layers.start as u32 + layer, + 0, + ), + src.descriptor.Format, + ); + } + } + + { + // Insert barrier for back transition from `RESOLVE_DEST` to `COPY_DEST`. + let transition_barrier = + Self::transition_barrier(d3d12::D3D12_RESOURCE_TRANSITION_BARRIER { + pResource: dst.resource.as_mut_ptr(), + Subresource: d3d12::D3D12_RESOURCE_BARRIER_ALL_SUBRESOURCES, // TODO: only affected ranges + StateBefore: d3d12::D3D12_RESOURCE_STATE_RESOLVE_DEST, + StateAfter: d3d12::D3D12_RESOURCE_STATE_COPY_DEST, + }); + self.raw.ResourceBarrier(1, &transition_barrier); + } + } + + unsafe fn blit_image( + &mut self, + src: &r::Image, + _src_layout: image::Layout, + dst: &r::Image, + _dst_layout: image::Layout, + filter: image::Filter, + regions: T, + ) where + T: Iterator, + { + let device = self.shared.service_pipes.device.clone(); + let src = src.expect_bound(); + let dst = dst.expect_bound(); + + // TODO: Resource barriers for src. + // TODO: depth or stencil images not supported so far + + // TODO: only supporting 2D images + match (src.kind, dst.kind) { + (image::Kind::D2(..), image::Kind::D2(..)) => {} + _ => unimplemented!(), + } + + // Descriptor heap for the current blit, only storing the src image + let (srv_heap, _) = device.create_descriptor_heap( + 1, + native::DescriptorHeapType::CbvSrvUav, + native::DescriptorHeapFlags::SHADER_VISIBLE, + 0, + ); + self.raw.set_descriptor_heaps(&[srv_heap]); + self.temporary_gpu_heaps.push(srv_heap); + + let srv_desc = Device::build_image_as_shader_resource_desc(&device::ViewInfo { + resource: src.resource, + kind: src.kind, + caps: src.view_caps, + view_kind: image::ViewKind::D2Array, // TODO + format: src.default_view_format.unwrap(), + component_mapping: device::IDENTITY_MAPPING, + levels: 0..src.descriptor.MipLevels as _, + layers: 0..src.kind.num_layers(), + }) + .unwrap(); + device.CreateShaderResourceView( + src.resource.as_mut_ptr(), + &srv_desc, + srv_heap.start_cpu_descriptor(), + ); + + let filter = match filter { + image::Filter::Nearest => d3d12::D3D12_FILTER_MIN_MAG_MIP_POINT, + image::Filter::Linear => d3d12::D3D12_FILTER_MIN_MAG_LINEAR_MIP_POINT, + }; + + struct Instance { + rtv: d3d12::D3D12_CPU_DESCRIPTOR_HANDLE, + viewport: d3d12::D3D12_VIEWPORT, + data: internal::BlitData, + } + let mut instances = FastHashMap::>::default(); + let mut barriers = Vec::new(); + + for r in regions { + let first_layer = r.dst_subresource.layers.start; + let num_layers = r.dst_subresource.layers.end - first_layer; + + // WORKAROUND: renderdoc crashes if we destroy the pool too early + let rtv_pool = Device::create_descriptor_heap_impl( + device, + native::DescriptorHeapType::Rtv, + false, + num_layers as _, + ); + self.rtv_pools.push(rtv_pool.raw.clone()); + + let key = match r.dst_subresource.aspects { + format::Aspects::COLOR => { + let format = dst.default_view_format.unwrap(); + // Create RTVs of the dst image for the miplevel of the current region + for i in 0..num_layers { + let mut desc = d3d12::D3D12_RENDER_TARGET_VIEW_DESC { + Format: format, + ViewDimension: d3d12::D3D12_RTV_DIMENSION_TEXTURE2DARRAY, + u: mem::zeroed(), + }; + + *desc.u.Texture2DArray_mut() = d3d12::D3D12_TEX2D_ARRAY_RTV { + MipSlice: r.dst_subresource.level as _, + FirstArraySlice: (i + first_layer) as u32, + ArraySize: 1, + PlaneSlice: 0, // TODO + }; + + let view = rtv_pool.at(i as _, 0).cpu; + device.CreateRenderTargetView(dst.resource.as_mut_ptr(), &desc, view); + } + + (format, filter) + } + _ => unimplemented!(), + }; + + // Take flipping into account + let viewport = d3d12::D3D12_VIEWPORT { + TopLeftX: cmp::min(r.dst_bounds.start.x, r.dst_bounds.end.x) as _, + TopLeftY: cmp::min(r.dst_bounds.start.y, r.dst_bounds.end.y) as _, + Width: (r.dst_bounds.end.x - r.dst_bounds.start.x).abs() as _, + Height: (r.dst_bounds.end.y - r.dst_bounds.start.y).abs() as _, + MinDepth: 0.0, + MaxDepth: 1.0, + }; + + let list = instances.entry(key).or_insert(Vec::new()); + + for i in 0..num_layers { + let src_layer = r.src_subresource.layers.start + i; + // Screen space triangle blitting + let data = { + // Image extents, layers are treated as depth + let (sx, dx) = if r.dst_bounds.start.x > r.dst_bounds.end.x { + ( + r.src_bounds.end.x, + r.src_bounds.start.x - r.src_bounds.end.x, + ) + } else { + ( + r.src_bounds.start.x, + r.src_bounds.end.x - r.src_bounds.start.x, + ) + }; + let (sy, dy) = if r.dst_bounds.start.y > r.dst_bounds.end.y { + ( + r.src_bounds.end.y, + r.src_bounds.start.y - r.src_bounds.end.y, + ) + } else { + ( + r.src_bounds.start.y, + r.src_bounds.end.y - r.src_bounds.start.y, + ) + }; + let image::Extent { width, height, .. } = + src.kind.level_extent(r.src_subresource.level); + + internal::BlitData { + src_offset: [sx as f32 / width as f32, sy as f32 / height as f32], + src_extent: [dx as f32 / width as f32, dy as f32 / height as f32], + layer: src_layer as f32, + level: r.src_subresource.level as _, + } + }; + + list.push(Instance { + rtv: rtv_pool.at(i as _, 0).cpu, + viewport, + data, + }); + + barriers.push(Self::transition_barrier( + d3d12::D3D12_RESOURCE_TRANSITION_BARRIER { + pResource: dst.resource.as_mut_ptr(), + Subresource: dst.calc_subresource( + r.dst_subresource.level as _, + (first_layer + i) as _, + 0, + ), + StateBefore: d3d12::D3D12_RESOURCE_STATE_COPY_DEST, + StateAfter: d3d12::D3D12_RESOURCE_STATE_RENDER_TARGET, + }, + )); + } + } + + // pre barriers + self.raw + .ResourceBarrier(barriers.len() as _, barriers.as_ptr()); + // execute blits + self.set_internal_graphics_pipeline(); + for (key, list) in instances { + let blit = self.shared.service_pipes.get_blit_2d_color(key); + self.raw + .IASetPrimitiveTopology(d3dcommon::D3D_PRIMITIVE_TOPOLOGY_TRIANGLELIST); + self.raw.set_pipeline_state(blit.pipeline); + self.raw.set_graphics_root_signature(blit.signature); + self.raw + .set_graphics_root_descriptor_table(0, srv_heap.start_gpu_descriptor()); + for inst in list { + let scissor = d3d12::D3D12_RECT { + left: inst.viewport.TopLeftX as _, + top: inst.viewport.TopLeftY as _, + right: (inst.viewport.TopLeftX + inst.viewport.Width) as _, + bottom: (inst.viewport.TopLeftY + inst.viewport.Height) as _, + }; + self.raw.RSSetViewports(1, &inst.viewport); + self.raw.RSSetScissorRects(1, &scissor); + self.raw.SetGraphicsRoot32BitConstants( + 1, + (mem::size_of::() / 4) as _, + &inst.data as *const _ as *const _, + 0, + ); + self.raw + .OMSetRenderTargets(1, &inst.rtv, minwindef::TRUE, ptr::null()); + self.raw.draw(3, 1, 0, 0); + } + } + // post barriers + for bar in &mut barriers { + let transition = bar.u.Transition_mut(); + mem::swap(&mut transition.StateBefore, &mut transition.StateAfter); + } + self.raw + .ResourceBarrier(barriers.len() as _, barriers.as_ptr()); + + // Reset states + self.raw + .RSSetViewports(self.viewport_cache.len() as _, self.viewport_cache.as_ptr()); + self.raw + .RSSetScissorRects(self.scissor_cache.len() as _, self.scissor_cache.as_ptr()); + if self.primitive_topology != d3dcommon::D3D_PRIMITIVE_TOPOLOGY_UNDEFINED { + self.raw.IASetPrimitiveTopology(self.primitive_topology); + } + } + + unsafe fn bind_index_buffer( + &mut self, + buffer: &r::Buffer, + sub: buffer::SubRange, + ty: IndexType, + ) { + let buffer = buffer.expect_bound(); + let format = match ty { + IndexType::U16 => dxgiformat::DXGI_FORMAT_R16_UINT, + IndexType::U32 => dxgiformat::DXGI_FORMAT_R32_UINT, + }; + let location = buffer.resource.gpu_virtual_address(); + self.raw.set_index_buffer( + location + sub.offset, + sub.size_to(buffer.requirements.size) as u32, + format, + ); + } + + unsafe fn bind_vertex_buffers<'a, T>(&mut self, first_binding: pso::BufferIndex, buffers: T) + where + T: Iterator, + { + assert!(first_binding as usize <= MAX_VERTEX_BUFFERS); + + for (view, (buffer, sub)) in self.vertex_buffer_views[first_binding as _..] + .iter_mut() + .zip(buffers) + { + let b = buffer.expect_bound(); + let base = (*b.resource).GetGPUVirtualAddress(); + view.BufferLocation = base + sub.offset; + view.SizeInBytes = sub.size_to(b.requirements.size) as u32; + } + self.set_vertex_buffers(); + } + + unsafe fn set_viewports(&mut self, first_viewport: u32, viewports: T) + where + T: Iterator, + { + for (i, vp) in viewports.enumerate() { + let viewport = d3d12::D3D12_VIEWPORT { + TopLeftX: vp.rect.x as _, + TopLeftY: vp.rect.y as _, + Width: vp.rect.w as _, + Height: vp.rect.h as _, + MinDepth: vp.depth.start, + MaxDepth: vp.depth.end, + }; + if i + first_viewport as usize >= self.viewport_cache.len() { + self.viewport_cache.push(viewport); + } else { + self.viewport_cache[i + first_viewport as usize] = viewport; + } + } + + self.raw + .RSSetViewports(self.viewport_cache.len() as _, self.viewport_cache.as_ptr()); + } + + unsafe fn set_scissors(&mut self, first_scissor: u32, scissors: T) + where + T: Iterator, + { + for (i, r) in scissors.enumerate() { + let rect = get_rect(&r); + if i + first_scissor as usize >= self.scissor_cache.len() { + self.scissor_cache.push(rect); + } else { + self.scissor_cache[i + first_scissor as usize] = rect; + } + } + + self.raw + .RSSetScissorRects(self.scissor_cache.len() as _, self.scissor_cache.as_ptr()) + } + + unsafe fn set_blend_constants(&mut self, color: pso::ColorValue) { + self.raw.set_blend_factor(color); + } + + unsafe fn set_stencil_reference(&mut self, faces: pso::Face, value: pso::StencilValue) { + assert!(!faces.is_empty()); + + if !faces.is_all() { + warn!( + "Stencil ref values set for both faces but only one was requested ({})", + faces.bits(), + ); + } + + self.raw.set_stencil_reference(value as _); + } + + unsafe fn set_stencil_read_mask(&mut self, _faces: pso::Face, _value: pso::StencilValue) { + //TODO: + // unimplemented!(); + } + + unsafe fn set_stencil_write_mask(&mut self, _faces: pso::Face, _value: pso::StencilValue) { + //TODO: + //unimplemented!(); + } + + unsafe fn set_depth_bounds(&mut self, bounds: Range) { + let (cmd_list1, hr) = self.raw.cast::(); + if winerror::SUCCEEDED(hr) { + cmd_list1.OMSetDepthBounds(bounds.start, bounds.end); + cmd_list1.destroy(); + } else { + warn!("Depth bounds test is not supported"); + } + } + + unsafe fn set_line_width(&mut self, width: f32) { + validate_line_width(width); + } + + unsafe fn set_depth_bias(&mut self, _depth_bias: pso::DepthBias) { + //TODO: + // unimplemented!() + } + + unsafe fn bind_graphics_pipeline(&mut self, pipeline: &r::GraphicsPipeline) { + match self.gr_pipeline.pipeline { + Some((_, ref shared)) if Arc::ptr_eq(shared, &pipeline.shared) => { + // Same root signature, nothing to do + } + _ => { + self.raw + .set_graphics_root_signature(pipeline.shared.signature); + // All slots need to be rebound internally on signature change. + self.gr_pipeline.user_data.dirty_all(); + } + } + self.raw.set_pipeline_state(pipeline.raw); + self.raw.IASetPrimitiveTopology(pipeline.topology); + self.primitive_topology = pipeline.topology; + + self.active_bindpoint = BindPoint::Graphics { internal: false }; + self.gr_pipeline.pipeline = Some((pipeline.raw, Arc::clone(&pipeline.shared))); + self.vertex_bindings_remap = pipeline.vertex_bindings; + + self.set_vertex_buffers(); + + if let Some(ref vp) = pipeline.baked_states.viewport { + self.set_viewports(0, iter::once(vp.clone())); + } + if let Some(ref rect) = pipeline.baked_states.scissor { + self.set_scissors(0, iter::once(rect.clone())); + } + if let Some(color) = pipeline.baked_states.blend_constants { + self.set_blend_constants(color); + } + if let Some(ref bounds) = pipeline.baked_states.depth_bounds { + self.set_depth_bounds(bounds.clone()); + } + } + + unsafe fn bind_graphics_descriptor_sets<'a, I, J>( + &mut self, + layout: &r::PipelineLayout, + first_set: usize, + sets: I, + offsets: J, + ) where + I: Iterator, + J: Iterator, + { + let set_array = sets.collect::>(); + self.active_descriptor_heaps = self + .gr_pipeline + .bind_descriptor_sets(layout, first_set, &set_array, offsets); + self.bind_descriptor_heaps(); + + for (i, set) in set_array.into_iter().enumerate() { + self.mark_bound_descriptor(first_set + i, set); + } + } + + unsafe fn bind_compute_pipeline(&mut self, pipeline: &r::ComputePipeline) { + match self.comp_pipeline.pipeline { + Some((_, ref shared)) if Arc::ptr_eq(shared, &pipeline.shared) => { + // Same root signature, nothing to do + } + _ => { + self.raw + .set_compute_root_signature(pipeline.shared.signature); + // All slots need to be rebound internally on signature change. + self.comp_pipeline.user_data.dirty_all(); + } + } + self.raw.set_pipeline_state(pipeline.raw); + + self.active_bindpoint = BindPoint::Compute; + self.comp_pipeline.pipeline = Some((pipeline.raw, Arc::clone(&pipeline.shared))); + } + + unsafe fn bind_compute_descriptor_sets<'a, I, J>( + &mut self, + layout: &r::PipelineLayout, + first_set: usize, + sets: I, + offsets: J, + ) where + I: Iterator, + J: Iterator, + { + let set_array = sets.collect::>(); + self.active_descriptor_heaps = self + .comp_pipeline + .bind_descriptor_sets(layout, first_set, &set_array, offsets); + self.bind_descriptor_heaps(); + + for (i, set) in set_array.into_iter().enumerate() { + self.mark_bound_descriptor(first_set + i, set); + } + } + + unsafe fn dispatch(&mut self, count: WorkGroupCount) { + self.set_compute_bind_point(); + self.raw.dispatch(count); + } + + unsafe fn dispatch_indirect(&mut self, buffer: &r::Buffer, offset: buffer::Offset) { + let buffer = buffer.expect_bound(); + self.set_compute_bind_point(); + self.raw.ExecuteIndirect( + self.shared.signatures.dispatch.as_mut_ptr(), + 1, + buffer.resource.as_mut_ptr(), + offset, + ptr::null_mut(), + 0, + ); + } + + unsafe fn fill_buffer(&mut self, buffer: &r::Buffer, range: buffer::SubRange, data: u32) { + let buffer = buffer.expect_bound(); + let bytes_per_unit = 4; + let start = range.offset as i32; + let end = range + .size + .map_or(buffer.requirements.size, |s| range.offset + s) as i32; + if start % 4 != 0 || end % 4 != 0 { + warn!("Fill buffer bounds have to be multiples of 4"); + } + let rect = d3d12::D3D12_RECT { + left: start / bytes_per_unit, + top: 0, + right: end / bytes_per_unit, + bottom: 1, + }; + + let (pre_barrier, post_barrier) = Self::dual_transition_barriers( + buffer.resource, + d3d12::D3D12_RESOURCE_BARRIER_ALL_SUBRESOURCES, + d3d12::D3D12_RESOURCE_STATE_COPY_DEST..d3d12::D3D12_RESOURCE_STATE_UNORDERED_ACCESS, + ); + self.raw.ResourceBarrier(1, &pre_barrier); + + // Descriptor heap for the current blit, only storing the src image + let device = self.shared.service_pipes.device.clone(); + let (uav_heap, _) = device.create_descriptor_heap( + 1, + native::DescriptorHeapType::CbvSrvUav, + native::DescriptorHeapFlags::SHADER_VISIBLE, + 0, + ); + let mut uav_desc = d3d12::D3D12_UNORDERED_ACCESS_VIEW_DESC { + Format: dxgiformat::DXGI_FORMAT_R32_TYPELESS, + ViewDimension: d3d12::D3D12_UAV_DIMENSION_BUFFER, + u: mem::zeroed(), + }; + *uav_desc.u.Buffer_mut() = d3d12::D3D12_BUFFER_UAV { + FirstElement: 0, + NumElements: (buffer.requirements.size / bytes_per_unit as u64) as u32, + StructureByteStride: 0, + CounterOffsetInBytes: 0, + Flags: d3d12::D3D12_BUFFER_UAV_FLAG_RAW, + }; + device.CreateUnorderedAccessView( + buffer.resource.as_mut_ptr(), + ptr::null_mut(), + &uav_desc, + uav_heap.start_cpu_descriptor(), + ); + self.raw.set_descriptor_heaps(&[uav_heap]); + self.temporary_gpu_heaps.push(uav_heap); + + let cpu_descriptor = buffer + .clear_uav + .expect("Buffer needs to be created with usage `TRANSFER_DST`"); + + self.raw.ClearUnorderedAccessViewUint( + uav_heap.start_gpu_descriptor(), + cpu_descriptor.raw, + buffer.resource.as_mut_ptr(), + &[data; 4], + 1, + &rect as *const _, + ); + + self.raw.ResourceBarrier(1, &post_barrier); + } + + unsafe fn update_buffer(&mut self, _buffer: &r::Buffer, _offset: buffer::Offset, _data: &[u8]) { + unimplemented!() + } + + unsafe fn copy_buffer(&mut self, src: &r::Buffer, dst: &r::Buffer, regions: T) + where + T: Iterator, + { + let src = src.expect_bound(); + let dst = dst.expect_bound(); + + /* + let (pre_barrier, post_barrier) = Self::dual_transition_barriers( + dst.resource, + d3d12::D3D12_RESOURCE_BARRIER_ALL_SUBRESOURCES, + d3d12::D3D12_RESOURCE_STATE_COPY_DEST, + ); + self.raw.ResourceBarrier(1, &pre_barrier); + */ + + // TODO: Optimization: Copy whole resource if possible + // copy each region + for region in regions { + self.raw.CopyBufferRegion( + dst.resource.as_mut_ptr(), + region.dst as _, + src.resource.as_mut_ptr(), + region.src as _, + region.size as _, + ); + } + + //self.raw.ResourceBarrier(1, &post_barrier); + } + + unsafe fn copy_image( + &mut self, + src: &r::Image, + _: image::Layout, + dst: &r::Image, + _: image::Layout, + regions: T, + ) where + T: Iterator, + { + let src = src.expect_bound(); + let dst = dst.expect_bound(); + let mut src_image = d3d12::D3D12_TEXTURE_COPY_LOCATION { + pResource: src.resource.as_mut_ptr(), + Type: d3d12::D3D12_TEXTURE_COPY_TYPE_SUBRESOURCE_INDEX, + u: mem::zeroed(), + }; + let mut dst_image = d3d12::D3D12_TEXTURE_COPY_LOCATION { + pResource: dst.resource.as_mut_ptr(), + Type: d3d12::D3D12_TEXTURE_COPY_TYPE_SUBRESOURCE_INDEX, + u: mem::zeroed(), + }; + + let device = self.shared.service_pipes.device.clone(); + let src_desc = src.surface_type.desc(); + let dst_desc = dst.surface_type.desc(); + assert_eq!(src_desc.bits, dst_desc.bits); + //Note: Direct3D 10.1 enables copies between prestructured-typed textures + // and block-compressed textures of the same bit widths. + let do_alias = src.surface_type != dst.surface_type + && src_desc.is_compressed() == dst_desc.is_compressed(); + + if do_alias { + // D3D12 only permits changing the channel type for copies, + // similarly to how it allows the views to be created. + + // create an aliased resource to the source + let mut alias = native::Resource::null(); + let desc = d3d12::D3D12_RESOURCE_DESC { + Format: dst.descriptor.Format, + ..src.descriptor.clone() + }; + let (heap_ptr, heap_offset) = match src.place { + r::Place::Heap { raw, offset } => (raw.as_mut_ptr(), offset), + r::Place::Swapchain {} => { + error!("Unable to copy swapchain image, skipping"); + return; + } + }; + assert_eq!( + winerror::S_OK, + device.CreatePlacedResource( + heap_ptr, + heap_offset, + &desc, + d3d12::D3D12_RESOURCE_STATE_COMMON, + ptr::null(), + &d3d12::ID3D12Resource::uuidof(), + alias.mut_void(), + ) + ); + src_image.pResource = alias.as_mut_ptr(); + self.retained_resources.push(alias); + + // signal the aliasing transition + let sub_barrier = d3d12::D3D12_RESOURCE_ALIASING_BARRIER { + pResourceBefore: src.resource.as_mut_ptr(), + pResourceAfter: src_image.pResource, + }; + let mut barrier = d3d12::D3D12_RESOURCE_BARRIER { + Type: d3d12::D3D12_RESOURCE_BARRIER_TYPE_ALIASING, + Flags: d3d12::D3D12_RESOURCE_BARRIER_FLAG_NONE, + u: mem::zeroed(), + }; + *barrier.u.Aliasing_mut() = sub_barrier; + self.raw.ResourceBarrier(1, &barrier as *const _); + } + + for r in regions { + debug_assert_eq!( + r.src_subresource.layers.len(), + r.dst_subresource.layers.len() + ); + let src_box = d3d12::D3D12_BOX { + left: r.src_offset.x as _, + top: r.src_offset.y as _, + right: (r.src_offset.x + r.extent.width as i32) as _, + bottom: (r.src_offset.y + r.extent.height as i32) as _, + front: r.src_offset.z as _, + back: (r.src_offset.z + r.extent.depth as i32) as _, + }; + + for (src_layer, dst_layer) in r + .src_subresource + .layers + .clone() + .zip(r.dst_subresource.layers.clone()) + { + *src_image.u.SubresourceIndex_mut() = + src.calc_subresource(r.src_subresource.level as _, src_layer as _, 0); + *dst_image.u.SubresourceIndex_mut() = + dst.calc_subresource(r.dst_subresource.level as _, dst_layer as _, 0); + self.raw.CopyTextureRegion( + &dst_image, + r.dst_offset.x as _, + r.dst_offset.y as _, + r.dst_offset.z as _, + &src_image, + &src_box, + ); + } + } + + if do_alias { + // signal the aliasing transition - back to the original + let sub_barrier = d3d12::D3D12_RESOURCE_ALIASING_BARRIER { + pResourceBefore: src_image.pResource, + pResourceAfter: src.resource.as_mut_ptr(), + }; + let mut barrier = d3d12::D3D12_RESOURCE_BARRIER { + Type: d3d12::D3D12_RESOURCE_BARRIER_TYPE_ALIASING, + Flags: d3d12::D3D12_RESOURCE_BARRIER_FLAG_NONE, + u: mem::zeroed(), + }; + *barrier.u.Aliasing_mut() = sub_barrier; + self.raw.ResourceBarrier(1, &barrier as *const _); + } + } + + unsafe fn copy_buffer_to_image( + &mut self, + buffer: &r::Buffer, + image: &r::Image, + _: image::Layout, + regions: T, + ) where + T: Iterator, + { + let buffer = buffer.expect_bound(); + let image = image.expect_bound(); + assert!(self.copies.is_empty()); + + for r in regions { + Self::split_buffer_copy(&mut self.copies, r, image); + } + + if self.copies.is_empty() { + return; + } + + let mut src = d3d12::D3D12_TEXTURE_COPY_LOCATION { + pResource: buffer.resource.as_mut_ptr(), + Type: d3d12::D3D12_TEXTURE_COPY_TYPE_PLACED_FOOTPRINT, + u: mem::zeroed(), + }; + let mut dst = d3d12::D3D12_TEXTURE_COPY_LOCATION { + pResource: image.resource.as_mut_ptr(), + Type: d3d12::D3D12_TEXTURE_COPY_TYPE_SUBRESOURCE_INDEX, + u: mem::zeroed(), + }; + + for c in self.copies.drain(..) { + let src_box = d3d12::D3D12_BOX { + left: c.buf_offset.x as u32, + top: c.buf_offset.y as u32, + right: c.buf_offset.x as u32 + c.copy_extent.width, + bottom: c.buf_offset.y as u32 + c.copy_extent.height, + front: c.buf_offset.z as u32, + back: c.buf_offset.z as u32 + c.copy_extent.depth, + }; + let footprint = d3d12::D3D12_PLACED_SUBRESOURCE_FOOTPRINT { + Offset: c.footprint_offset, + Footprint: d3d12::D3D12_SUBRESOURCE_FOOTPRINT { + Format: image.descriptor.Format, + Width: c.footprint.width, + Height: c.footprint.height, + Depth: c.footprint.depth, + RowPitch: c.row_pitch, + }, + }; + *src.u.PlacedFootprint_mut() = footprint; + *dst.u.SubresourceIndex_mut() = c.img_subresource; + self.raw.CopyTextureRegion( + &dst, + c.img_offset.x as _, + c.img_offset.y as _, + c.img_offset.z as _, + &src, + &src_box, + ); + } + } + + unsafe fn copy_image_to_buffer( + &mut self, + image: &r::Image, + _: image::Layout, + buffer: &r::Buffer, + regions: T, + ) where + T: Iterator, + { + let image = image.expect_bound(); + let buffer = buffer.expect_bound(); + assert!(self.copies.is_empty()); + + for r in regions { + Self::split_buffer_copy(&mut self.copies, r, image); + } + + if self.copies.is_empty() { + return; + } + + let mut src = d3d12::D3D12_TEXTURE_COPY_LOCATION { + pResource: image.resource.as_mut_ptr(), + Type: d3d12::D3D12_TEXTURE_COPY_TYPE_SUBRESOURCE_INDEX, + u: mem::zeroed(), + }; + let mut dst = d3d12::D3D12_TEXTURE_COPY_LOCATION { + pResource: buffer.resource.as_mut_ptr(), + Type: d3d12::D3D12_TEXTURE_COPY_TYPE_PLACED_FOOTPRINT, + u: mem::zeroed(), + }; + + for c in self.copies.drain(..) { + let src_box = d3d12::D3D12_BOX { + left: c.img_offset.x as u32, + top: c.img_offset.y as u32, + right: c.img_offset.x as u32 + c.copy_extent.width, + bottom: c.img_offset.y as u32 + c.copy_extent.height, + front: c.img_offset.z as u32, + back: c.img_offset.z as u32 + c.copy_extent.depth, + }; + let footprint = d3d12::D3D12_PLACED_SUBRESOURCE_FOOTPRINT { + Offset: c.footprint_offset, + Footprint: d3d12::D3D12_SUBRESOURCE_FOOTPRINT { + Format: image.descriptor.Format, + Width: c.footprint.width, + Height: c.footprint.height, + Depth: c.footprint.depth, + RowPitch: c.row_pitch, + }, + }; + *dst.u.PlacedFootprint_mut() = footprint; + *src.u.SubresourceIndex_mut() = c.img_subresource; + self.raw.CopyTextureRegion( + &dst, + c.buf_offset.x as _, + c.buf_offset.y as _, + c.buf_offset.z as _, + &src, + &src_box, + ); + } + } + + unsafe fn draw(&mut self, vertices: Range, instances: Range) { + self.set_graphics_bind_point(); + self.raw.draw( + vertices.end - vertices.start, + instances.end - instances.start, + vertices.start, + instances.start, + ); + } + + unsafe fn draw_indexed( + &mut self, + indices: Range, + base_vertex: VertexOffset, + instances: Range, + ) { + self.set_graphics_bind_point(); + self.raw.draw_indexed( + indices.end - indices.start, + instances.end - instances.start, + indices.start, + base_vertex, + instances.start, + ); + } + + unsafe fn draw_indirect( + &mut self, + buffer: &r::Buffer, + offset: buffer::Offset, + draw_count: DrawCount, + stride: buffer::Stride, + ) { + assert_eq!(stride, 16); + let buffer = buffer.expect_bound(); + self.set_graphics_bind_point(); + self.raw.ExecuteIndirect( + self.shared.signatures.draw.as_mut_ptr(), + draw_count, + buffer.resource.as_mut_ptr(), + offset, + ptr::null_mut(), + 0, + ); + } + + unsafe fn draw_indexed_indirect( + &mut self, + buffer: &r::Buffer, + offset: buffer::Offset, + draw_count: DrawCount, + stride: buffer::Stride, + ) { + assert_eq!(stride, 20); + let buffer = buffer.expect_bound(); + self.set_graphics_bind_point(); + self.raw.ExecuteIndirect( + self.shared.signatures.draw_indexed.as_mut_ptr(), + draw_count, + buffer.resource.as_mut_ptr(), + offset, + ptr::null_mut(), + 0, + ); + } + + unsafe fn draw_mesh_tasks(&mut self, _: TaskCount, _: TaskCount) { + unimplemented!() + } + + unsafe fn draw_mesh_tasks_indirect( + &mut self, + _: &r::Buffer, + _: buffer::Offset, + _: hal::DrawCount, + _: buffer::Stride, + ) { + unimplemented!() + } + + unsafe fn draw_mesh_tasks_indirect_count( + &mut self, + _: &r::Buffer, + _: buffer::Offset, + _: &r::Buffer, + _: buffer::Offset, + _: DrawCount, + _: buffer::Stride, + ) { + unimplemented!() + } + + unsafe fn draw_indirect_count( + &mut self, + buffer: &r::Buffer, + offset: buffer::Offset, + count_buffer: &r::Buffer, + count_buffer_offset: buffer::Offset, + max_draw_count: DrawCount, + stride: buffer::Stride, + ) { + assert_eq!(stride, 16); + let buffer = buffer.expect_bound(); + let count_buffer = count_buffer.expect_bound(); + self.set_graphics_bind_point(); + self.raw.ExecuteIndirect( + self.shared.signatures.draw.as_mut_ptr(), + max_draw_count, + buffer.resource.as_mut_ptr(), + offset, + count_buffer.resource.as_mut_ptr(), + count_buffer_offset, + ); + } + + unsafe fn draw_indexed_indirect_count( + &mut self, + buffer: &r::Buffer, + offset: buffer::Offset, + count_buffer: &r::Buffer, + count_buffer_offset: buffer::Offset, + max_draw_count: DrawCount, + stride: buffer::Stride, + ) { + assert_eq!(stride, 20); + let buffer = buffer.expect_bound(); + let count_buffer = count_buffer.expect_bound(); + self.set_graphics_bind_point(); + self.raw.ExecuteIndirect( + self.shared.signatures.draw_indexed.as_mut_ptr(), + max_draw_count, + buffer.resource.as_mut_ptr(), + offset, + count_buffer.resource.as_mut_ptr(), + count_buffer_offset, + ); + } + + unsafe fn set_event(&mut self, _: &(), _: pso::PipelineStage) { + unimplemented!() + } + + unsafe fn reset_event(&mut self, _: &(), _: pso::PipelineStage) { + unimplemented!() + } + + unsafe fn wait_events<'a, I, J>(&mut self, _: I, _: Range, _: J) + where + I: Iterator, + J: Iterator>, + { + unimplemented!() + } + + unsafe fn begin_query(&mut self, query: query::Query, flags: query::ControlFlags) { + let query_ty = match query.pool.ty { + query::Type::Occlusion => { + if flags.contains(query::ControlFlags::PRECISE) { + self.occlusion_query = Some(OcclusionQuery::Precise(query.id)); + d3d12::D3D12_QUERY_TYPE_OCCLUSION + } else { + // Default to binary occlusion as it might be faster due to early depth/stencil + // tests. + self.occlusion_query = Some(OcclusionQuery::Binary(query.id)); + d3d12::D3D12_QUERY_TYPE_BINARY_OCCLUSION + } + } + query::Type::Timestamp => panic!("Timestap queries are issued via "), + query::Type::PipelineStatistics(_) => { + self.pipeline_stats_query = Some(query.id); + d3d12::D3D12_QUERY_TYPE_PIPELINE_STATISTICS + } + }; + + self.raw + .BeginQuery(query.pool.raw.as_mut_ptr(), query_ty, query.id); + } + + unsafe fn end_query(&mut self, query: query::Query) { + let id = query.id; + let query_ty = match query.pool.ty { + query::Type::Occlusion if self.occlusion_query == Some(OcclusionQuery::Precise(id)) => { + self.occlusion_query = None; + d3d12::D3D12_QUERY_TYPE_OCCLUSION + } + query::Type::Occlusion if self.occlusion_query == Some(OcclusionQuery::Binary(id)) => { + self.occlusion_query = None; + d3d12::D3D12_QUERY_TYPE_BINARY_OCCLUSION + } + query::Type::PipelineStatistics(_) if self.pipeline_stats_query == Some(id) => { + self.pipeline_stats_query = None; + d3d12::D3D12_QUERY_TYPE_PIPELINE_STATISTICS + } + _ => panic!("Missing `begin_query` call for query: {:?}", query), + }; + + self.raw.EndQuery(query.pool.raw.as_mut_ptr(), query_ty, id); + } + + unsafe fn reset_query_pool(&mut self, _pool: &r::QueryPool, _queries: Range) { + // Nothing to do here + // vkCmdResetQueryPool sets the queries to `unavailable` but the specification + // doesn't state an affect on the `active` state. Every queries at the end of the command + // buffer must be made inactive, which can only be done with EndQuery. + // Therefore, every `begin_query` must follow a `end_query` state, the resulting values + // after calling are undefined. + } + + unsafe fn copy_query_pool_results( + &mut self, + _pool: &r::QueryPool, + _queries: Range, + _buffer: &r::Buffer, + _offset: buffer::Offset, + _stride: buffer::Stride, + _flags: query::ResultFlags, + ) { + unimplemented!() + } + + unsafe fn write_timestamp(&mut self, _: pso::PipelineStage, query: query::Query) { + self.raw.EndQuery( + query.pool.raw.as_mut_ptr(), + d3d12::D3D12_QUERY_TYPE_TIMESTAMP, + query.id, + ); + } + + unsafe fn push_graphics_constants( + &mut self, + _layout: &r::PipelineLayout, + _stages: pso::ShaderStageFlags, + offset: u32, + constants: &[u32], + ) { + assert!(offset % 4 == 0); + self.gr_pipeline + .user_data + .set_constants(offset as usize / 4, constants); + } + + unsafe fn push_compute_constants( + &mut self, + _layout: &r::PipelineLayout, + offset: u32, + constants: &[u32], + ) { + assert!(offset % 4 == 0); + self.comp_pipeline + .user_data + .set_constants(offset as usize / 4, constants); + } + + unsafe fn execute_commands<'a, T>(&mut self, cmd_buffers: T) + where + T: Iterator, + { + for _cmd_buf in cmd_buffers { + error!("TODO: execute_commands"); + } + } + + unsafe fn insert_debug_marker(&mut self, name: &str, _color: u32) { + let (ptr, size) = self.fill_marker(name); + self.raw.SetMarker(0, ptr, size); + } + unsafe fn begin_debug_marker(&mut self, name: &str, _color: u32) { + let (ptr, size) = self.fill_marker(name); + self.raw.BeginEvent(0, ptr, size); + } + unsafe fn end_debug_marker(&mut self) { + self.raw.EndEvent(); + } +} diff --git a/third_party/rust/gfx-backend-dx12/src/conv.rs b/third_party/rust/gfx-backend-dx12/src/conv.rs index 43c6389a76d2..2dff58b4eab0 100644 --- a/third_party/rust/gfx-backend-dx12/src/conv.rs +++ b/third_party/rust/gfx-backend-dx12/src/conv.rs @@ -1,677 +1,677 @@ -use crate::validate_line_width; - -use spirv_cross::spirv; -use std::mem; - -use winapi::{ - shared::{ - basetsd::UINT8, - dxgiformat::*, - minwindef::{FALSE, INT, TRUE, UINT}, - }, - um::{d3d12::*, d3dcommon::*}, -}; - -use auxil::ShaderStage; -use hal::{ - buffer, - format::{Format, ImageFeature, SurfaceType, Swizzle}, - image, pso, -}; - -use native::ShaderVisibility; - -fn is_little_endinan() -> bool { - unsafe { 1 == *(&1u32 as *const _ as *const u8) } -} - -pub fn map_format(format: Format) -> Option { - use hal::format::Format::*; - - // Handling packed formats according to the platform endianness. - let reverse = is_little_endinan(); - let format = match format { - Bgra4Unorm if !reverse => DXGI_FORMAT_B4G4R4A4_UNORM, - R5g6b5Unorm if reverse => DXGI_FORMAT_B5G6R5_UNORM, - B5g6r5Unorm if !reverse => DXGI_FORMAT_B5G6R5_UNORM, - B5g5r5a1Unorm if !reverse => DXGI_FORMAT_B5G5R5A1_UNORM, - A1r5g5b5Unorm if reverse => DXGI_FORMAT_B5G5R5A1_UNORM, - R8Unorm => DXGI_FORMAT_R8_UNORM, - R8Snorm => DXGI_FORMAT_R8_SNORM, - R8Uint => DXGI_FORMAT_R8_UINT, - R8Sint => DXGI_FORMAT_R8_SINT, - Rg8Unorm => DXGI_FORMAT_R8G8_UNORM, - Rg8Snorm => DXGI_FORMAT_R8G8_SNORM, - Rg8Uint => DXGI_FORMAT_R8G8_UINT, - Rg8Sint => DXGI_FORMAT_R8G8_SINT, - Rgba8Unorm => DXGI_FORMAT_R8G8B8A8_UNORM, - Rgba8Snorm => DXGI_FORMAT_R8G8B8A8_SNORM, - Rgba8Uint => DXGI_FORMAT_R8G8B8A8_UINT, - Rgba8Sint => DXGI_FORMAT_R8G8B8A8_SINT, - Rgba8Srgb => DXGI_FORMAT_R8G8B8A8_UNORM_SRGB, - Bgra8Unorm => DXGI_FORMAT_B8G8R8A8_UNORM, - Bgra8Srgb => DXGI_FORMAT_B8G8R8A8_UNORM_SRGB, - Abgr8Unorm if reverse => DXGI_FORMAT_R8G8B8A8_UNORM, - Abgr8Snorm if reverse => DXGI_FORMAT_R8G8B8A8_SNORM, - Abgr8Uint if reverse => DXGI_FORMAT_R8G8B8A8_UINT, - Abgr8Sint if reverse => DXGI_FORMAT_R8G8B8A8_SINT, - Abgr8Srgb if reverse => DXGI_FORMAT_R8G8B8A8_UNORM_SRGB, - A2b10g10r10Unorm if reverse => DXGI_FORMAT_R10G10B10A2_UNORM, - A2b10g10r10Uint if reverse => DXGI_FORMAT_R10G10B10A2_UINT, - R16Unorm => DXGI_FORMAT_R16_UNORM, - R16Snorm => DXGI_FORMAT_R16_SNORM, - R16Uint => DXGI_FORMAT_R16_UINT, - R16Sint => DXGI_FORMAT_R16_SINT, - R16Sfloat => DXGI_FORMAT_R16_FLOAT, - Rg16Unorm => DXGI_FORMAT_R16G16_UNORM, - Rg16Snorm => DXGI_FORMAT_R16G16_SNORM, - Rg16Uint => DXGI_FORMAT_R16G16_UINT, - Rg16Sint => DXGI_FORMAT_R16G16_SINT, - Rg16Sfloat => DXGI_FORMAT_R16G16_FLOAT, - Rgba16Unorm => DXGI_FORMAT_R16G16B16A16_UNORM, - Rgba16Snorm => DXGI_FORMAT_R16G16B16A16_SNORM, - Rgba16Uint => DXGI_FORMAT_R16G16B16A16_UINT, - Rgba16Sint => DXGI_FORMAT_R16G16B16A16_SINT, - Rgba16Sfloat => DXGI_FORMAT_R16G16B16A16_FLOAT, - R32Uint => DXGI_FORMAT_R32_UINT, - R32Sint => DXGI_FORMAT_R32_SINT, - R32Sfloat => DXGI_FORMAT_R32_FLOAT, - Rg32Uint => DXGI_FORMAT_R32G32_UINT, - Rg32Sint => DXGI_FORMAT_R32G32_SINT, - Rg32Sfloat => DXGI_FORMAT_R32G32_FLOAT, - Rgb32Uint => DXGI_FORMAT_R32G32B32_UINT, - Rgb32Sint => DXGI_FORMAT_R32G32B32_SINT, - Rgb32Sfloat => DXGI_FORMAT_R32G32B32_FLOAT, - Rgba32Uint => DXGI_FORMAT_R32G32B32A32_UINT, - Rgba32Sint => DXGI_FORMAT_R32G32B32A32_SINT, - Rgba32Sfloat => DXGI_FORMAT_R32G32B32A32_FLOAT, - B10g11r11Ufloat if reverse => DXGI_FORMAT_R11G11B10_FLOAT, - E5b9g9r9Ufloat if reverse => DXGI_FORMAT_R9G9B9E5_SHAREDEXP, - D16Unorm => DXGI_FORMAT_D16_UNORM, - D24UnormS8Uint => DXGI_FORMAT_D24_UNORM_S8_UINT, - X8D24Unorm if reverse => DXGI_FORMAT_D24_UNORM_S8_UINT, - D32Sfloat => DXGI_FORMAT_D32_FLOAT, - D32SfloatS8Uint => DXGI_FORMAT_D32_FLOAT_S8X24_UINT, - Bc1RgbUnorm | Bc1RgbaUnorm => DXGI_FORMAT_BC1_UNORM, - Bc1RgbSrgb | Bc1RgbaSrgb => DXGI_FORMAT_BC1_UNORM_SRGB, - Bc2Unorm => DXGI_FORMAT_BC2_UNORM, - Bc2Srgb => DXGI_FORMAT_BC2_UNORM_SRGB, - Bc3Unorm => DXGI_FORMAT_BC3_UNORM, - Bc3Srgb => DXGI_FORMAT_BC3_UNORM_SRGB, - Bc4Unorm => DXGI_FORMAT_BC4_UNORM, - Bc4Snorm => DXGI_FORMAT_BC4_SNORM, - Bc5Unorm => DXGI_FORMAT_BC5_UNORM, - Bc5Snorm => DXGI_FORMAT_BC5_SNORM, - Bc6hUfloat => DXGI_FORMAT_BC6H_UF16, - Bc6hSfloat => DXGI_FORMAT_BC6H_SF16, - Bc7Unorm => DXGI_FORMAT_BC7_UNORM, - Bc7Srgb => DXGI_FORMAT_BC7_UNORM_SRGB, - - _ => return None, - }; - - Some(format) -} - -pub fn map_format_shader_depth(surface: SurfaceType) -> Option { - match surface { - SurfaceType::D16 => Some(DXGI_FORMAT_R16_UNORM), - SurfaceType::X8D24 | SurfaceType::D24_S8 => Some(DXGI_FORMAT_R24_UNORM_X8_TYPELESS), - SurfaceType::D32 => Some(DXGI_FORMAT_R32_FLOAT), - SurfaceType::D32_S8 => Some(DXGI_FORMAT_R32_FLOAT_X8X24_TYPELESS), - _ => return None, - } -} - -pub fn map_format_shader_stencil(surface: SurfaceType) -> Option { - match surface { - SurfaceType::D24_S8 => Some(DXGI_FORMAT_X24_TYPELESS_G8_UINT), - SurfaceType::D32_S8 => Some(DXGI_FORMAT_X32_TYPELESS_G8X24_UINT), - _ => None, - } -} - -pub fn map_format_dsv(surface: SurfaceType) -> Option { - match surface { - SurfaceType::D16 => Some(DXGI_FORMAT_D16_UNORM), - SurfaceType::X8D24 | SurfaceType::D24_S8 => Some(DXGI_FORMAT_D24_UNORM_S8_UINT), - SurfaceType::D32 => Some(DXGI_FORMAT_D32_FLOAT), - SurfaceType::D32_S8 => Some(DXGI_FORMAT_D32_FLOAT_S8X24_UINT), - _ => None, - } -} - -pub fn map_format_nosrgb(format: Format) -> Option { - // NOTE: DXGI doesn't allow sRGB format on the swapchain, but - // creating RTV of swapchain buffers with sRGB works - match format { - Format::Bgra8Srgb => Some(DXGI_FORMAT_B8G8R8A8_UNORM), - Format::Rgba8Srgb => Some(DXGI_FORMAT_R8G8B8A8_UNORM), - _ => map_format(format), - } -} - -pub fn map_swizzle(swizzle: Swizzle) -> UINT { - use hal::format::Component::*; - - [swizzle.0, swizzle.1, swizzle.2, swizzle.3] - .iter() - .enumerate() - .fold( - D3D12_SHADER_COMPONENT_MAPPING_ALWAYS_SET_BIT_AVOIDING_ZEROMEM_MISTAKES, - |mapping, (i, &component)| { - let value = match component { - R => D3D12_SHADER_COMPONENT_MAPPING_FROM_MEMORY_COMPONENT_0, - G => D3D12_SHADER_COMPONENT_MAPPING_FROM_MEMORY_COMPONENT_1, - B => D3D12_SHADER_COMPONENT_MAPPING_FROM_MEMORY_COMPONENT_2, - A => D3D12_SHADER_COMPONENT_MAPPING_FROM_MEMORY_COMPONENT_3, - Zero => D3D12_SHADER_COMPONENT_MAPPING_FORCE_VALUE_0, - One => D3D12_SHADER_COMPONENT_MAPPING_FORCE_VALUE_1, - }; - mapping | (value << D3D12_SHADER_COMPONENT_MAPPING_SHIFT as usize * i) - }, - ) -} - -pub fn swizzle_rg(swizzle: Swizzle) -> Swizzle { - use hal::format::Component as C; - fn map_component(c: C) -> C { - match c { - C::R => C::G, - C::G => C::R, - x => x, - } - } - Swizzle( - map_component(swizzle.0), - map_component(swizzle.1), - map_component(swizzle.2), - map_component(swizzle.3), - ) -} - -pub fn map_surface_type(st: SurfaceType) -> Option { - use hal::format::SurfaceType::*; - - assert_eq!(1, unsafe { *(&1u32 as *const _ as *const u8) }); - Some(match st { - R5_G6_B5 => DXGI_FORMAT_B5G6R5_UNORM, - A1_R5_G5_B5 => DXGI_FORMAT_B5G5R5A1_UNORM, - R8 => DXGI_FORMAT_R8_TYPELESS, - R8_G8 => DXGI_FORMAT_R8G8_TYPELESS, - R8_G8_B8_A8 => DXGI_FORMAT_R8G8B8A8_TYPELESS, - B8_G8_R8_A8 => DXGI_FORMAT_B8G8R8A8_TYPELESS, - A8_B8_G8_R8 => DXGI_FORMAT_R8G8B8A8_TYPELESS, - A2_B10_G10_R10 => DXGI_FORMAT_R10G10B10A2_TYPELESS, - R16 => DXGI_FORMAT_R16_TYPELESS, - R16_G16 => DXGI_FORMAT_R16G16_TYPELESS, - R16_G16_B16_A16 => DXGI_FORMAT_R16G16B16A16_TYPELESS, - R32 => DXGI_FORMAT_R32_TYPELESS, - R32_G32 => DXGI_FORMAT_R32G32_TYPELESS, - R32_G32_B32 => DXGI_FORMAT_R32G32B32_TYPELESS, - R32_G32_B32_A32 => DXGI_FORMAT_R32G32B32A32_TYPELESS, - B10_G11_R11 => DXGI_FORMAT_R11G11B10_FLOAT, - E5_B9_G9_R9 => DXGI_FORMAT_R9G9B9E5_SHAREDEXP, - D16 => DXGI_FORMAT_R16_TYPELESS, - X8D24 => DXGI_FORMAT_R24G8_TYPELESS, - D32 => DXGI_FORMAT_R32_TYPELESS, - D24_S8 => DXGI_FORMAT_R24G8_TYPELESS, - D32_S8 => DXGI_FORMAT_R32_FLOAT_X8X24_TYPELESS, - _ => return None, - }) -} - -pub fn map_topology_type(primitive: pso::Primitive) -> D3D12_PRIMITIVE_TOPOLOGY_TYPE { - use hal::pso::Primitive::*; - match primitive { - PointList => D3D12_PRIMITIVE_TOPOLOGY_TYPE_POINT, - LineList | LineStrip => D3D12_PRIMITIVE_TOPOLOGY_TYPE_LINE, - TriangleList | TriangleStrip => D3D12_PRIMITIVE_TOPOLOGY_TYPE_TRIANGLE, - PatchList(_) => D3D12_PRIMITIVE_TOPOLOGY_TYPE_PATCH, - } -} - -pub fn map_topology(ia: &pso::InputAssemblerDesc) -> D3D12_PRIMITIVE_TOPOLOGY { - use hal::pso::Primitive::*; - match (ia.primitive, ia.with_adjacency) { - (PointList, false) => D3D_PRIMITIVE_TOPOLOGY_POINTLIST, - (PointList, true) => panic!("Points can't have adjacency info"), - (LineList, false) => D3D_PRIMITIVE_TOPOLOGY_LINELIST, - (LineList, true) => D3D_PRIMITIVE_TOPOLOGY_LINELIST_ADJ, - (LineStrip, false) => D3D_PRIMITIVE_TOPOLOGY_LINESTRIP, - (LineStrip, true) => D3D_PRIMITIVE_TOPOLOGY_LINESTRIP_ADJ, - (TriangleList, false) => D3D_PRIMITIVE_TOPOLOGY_TRIANGLELIST, - (TriangleList, true) => D3D_PRIMITIVE_TOPOLOGY_TRIANGLELIST_ADJ, - (TriangleStrip, false) => D3D_PRIMITIVE_TOPOLOGY_TRIANGLESTRIP, - (TriangleStrip, true) => D3D_PRIMITIVE_TOPOLOGY_TRIANGLESTRIP_ADJ, - (PatchList(num), false) => { - assert!(num != 0); - D3D_PRIMITIVE_TOPOLOGY_1_CONTROL_POINT_PATCHLIST + (num as u32) - 1 - } - (_, true) => panic!("Patches can't have adjacency info"), - } -} - -pub fn map_rasterizer(rasterizer: &pso::Rasterizer, multisample: bool) -> D3D12_RASTERIZER_DESC { - use hal::pso::FrontFace::*; - use hal::pso::PolygonMode::*; - - let bias = match rasterizer.depth_bias { - //TODO: support dynamic depth bias - Some(pso::State::Static(db)) => db, - Some(_) | None => pso::DepthBias::default(), - }; - - if let pso::State::Static(w) = rasterizer.line_width { - validate_line_width(w); - } - - D3D12_RASTERIZER_DESC { - FillMode: match rasterizer.polygon_mode { - Point => { - error!("Point rasterization is not supported"); - D3D12_FILL_MODE_WIREFRAME - } - Line => D3D12_FILL_MODE_WIREFRAME, - Fill => D3D12_FILL_MODE_SOLID, - }, - CullMode: match rasterizer.cull_face { - pso::Face::NONE => D3D12_CULL_MODE_NONE, - pso::Face::FRONT => D3D12_CULL_MODE_FRONT, - pso::Face::BACK => D3D12_CULL_MODE_BACK, - _ => panic!("Culling both front and back faces is not supported"), - }, - FrontCounterClockwise: match rasterizer.front_face { - Clockwise => FALSE, - CounterClockwise => TRUE, - }, - DepthBias: bias.const_factor as INT, - DepthBiasClamp: bias.clamp, - SlopeScaledDepthBias: bias.slope_factor, - DepthClipEnable: !rasterizer.depth_clamping as _, - MultisampleEnable: if multisample { TRUE } else { FALSE }, - ForcedSampleCount: 0, // TODO: currently not supported - AntialiasedLineEnable: FALSE, // TODO: currently not supported - ConservativeRaster: if rasterizer.conservative { - D3D12_CONSERVATIVE_RASTERIZATION_MODE_ON - } else { - D3D12_CONSERVATIVE_RASTERIZATION_MODE_OFF - }, - } -} - -fn map_factor(factor: pso::Factor, is_alpha: bool) -> D3D12_BLEND { - use hal::pso::Factor::*; - match factor { - Zero => D3D12_BLEND_ZERO, - One => D3D12_BLEND_ONE, - SrcColor if is_alpha => D3D12_BLEND_SRC_ALPHA, - SrcColor => D3D12_BLEND_SRC_COLOR, - OneMinusSrcColor if is_alpha => D3D12_BLEND_INV_SRC_ALPHA, - OneMinusSrcColor => D3D12_BLEND_INV_SRC_COLOR, - DstColor if is_alpha => D3D12_BLEND_DEST_ALPHA, - DstColor => D3D12_BLEND_DEST_COLOR, - OneMinusDstColor if is_alpha => D3D12_BLEND_INV_DEST_ALPHA, - OneMinusDstColor => D3D12_BLEND_INV_DEST_COLOR, - SrcAlpha => D3D12_BLEND_SRC_ALPHA, - OneMinusSrcAlpha => D3D12_BLEND_INV_SRC_ALPHA, - DstAlpha => D3D12_BLEND_DEST_ALPHA, - OneMinusDstAlpha => D3D12_BLEND_INV_DEST_ALPHA, - ConstColor | ConstAlpha => D3D12_BLEND_BLEND_FACTOR, - OneMinusConstColor | OneMinusConstAlpha => D3D12_BLEND_INV_BLEND_FACTOR, - SrcAlphaSaturate => D3D12_BLEND_SRC_ALPHA_SAT, - Src1Color if is_alpha => D3D12_BLEND_SRC1_ALPHA, - Src1Color => D3D12_BLEND_SRC1_COLOR, - OneMinusSrc1Color if is_alpha => D3D12_BLEND_INV_SRC1_ALPHA, - OneMinusSrc1Color => D3D12_BLEND_INV_SRC1_COLOR, - Src1Alpha => D3D12_BLEND_SRC1_ALPHA, - OneMinusSrc1Alpha => D3D12_BLEND_INV_SRC1_ALPHA, - } -} - -fn map_blend_op( - operation: pso::BlendOp, - is_alpha: bool, -) -> (D3D12_BLEND_OP, D3D12_BLEND, D3D12_BLEND) { - use hal::pso::BlendOp::*; - match operation { - Add { src, dst } => ( - D3D12_BLEND_OP_ADD, - map_factor(src, is_alpha), - map_factor(dst, is_alpha), - ), - Sub { src, dst } => ( - D3D12_BLEND_OP_SUBTRACT, - map_factor(src, is_alpha), - map_factor(dst, is_alpha), - ), - RevSub { src, dst } => ( - D3D12_BLEND_OP_REV_SUBTRACT, - map_factor(src, is_alpha), - map_factor(dst, is_alpha), - ), - Min => (D3D12_BLEND_OP_MIN, D3D12_BLEND_ZERO, D3D12_BLEND_ZERO), - Max => (D3D12_BLEND_OP_MAX, D3D12_BLEND_ZERO, D3D12_BLEND_ZERO), - } -} - -pub fn map_render_targets( - color_targets: &[pso::ColorBlendDesc], -) -> [D3D12_RENDER_TARGET_BLEND_DESC; D3D12_SIMULTANEOUS_RENDER_TARGET_COUNT as usize] { - let dummy_target = D3D12_RENDER_TARGET_BLEND_DESC { - BlendEnable: FALSE, - LogicOpEnable: FALSE, - SrcBlend: D3D12_BLEND_ZERO, - DestBlend: D3D12_BLEND_ZERO, - BlendOp: D3D12_BLEND_OP_ADD, - SrcBlendAlpha: D3D12_BLEND_ZERO, - DestBlendAlpha: D3D12_BLEND_ZERO, - BlendOpAlpha: D3D12_BLEND_OP_ADD, - LogicOp: D3D12_LOGIC_OP_CLEAR, - RenderTargetWriteMask: 0, - }; - let mut targets = [dummy_target; D3D12_SIMULTANEOUS_RENDER_TARGET_COUNT as usize]; - - for (target, color_desc) in targets.iter_mut().zip(color_targets.iter()) { - target.RenderTargetWriteMask = color_desc.mask.bits() as UINT8; - if let Some(ref blend) = color_desc.blend { - let (color_op, color_src, color_dst) = map_blend_op(blend.color, false); - let (alpha_op, alpha_src, alpha_dst) = map_blend_op(blend.alpha, true); - target.BlendEnable = TRUE; - target.BlendOp = color_op; - target.SrcBlend = color_src; - target.DestBlend = color_dst; - target.BlendOpAlpha = alpha_op; - target.SrcBlendAlpha = alpha_src; - target.DestBlendAlpha = alpha_dst; - } - } - - targets -} - -pub fn map_depth_stencil(dsi: &pso::DepthStencilDesc) -> D3D12_DEPTH_STENCIL_DESC { - let (depth_on, depth_write, depth_func) = match dsi.depth { - Some(ref depth) => (TRUE, depth.write, map_comparison(depth.fun)), - None => unsafe { mem::zeroed() }, - }; - - let (stencil_on, front, back, read_mask, write_mask) = match dsi.stencil { - Some(ref stencil) => { - let read_masks = stencil.read_masks.static_or(pso::Sided::new(!0)); - let write_masks = stencil.write_masks.static_or(pso::Sided::new(!0)); - if read_masks.front != read_masks.back || write_masks.front != write_masks.back { - error!( - "Different sides are specified for read ({:?} and write ({:?}) stencil masks", - read_masks, write_masks - ); - } - ( - TRUE, - map_stencil_side(&stencil.faces.front), - map_stencil_side(&stencil.faces.back), - read_masks.front, - write_masks.front, - ) - } - None => unsafe { mem::zeroed() }, - }; - - D3D12_DEPTH_STENCIL_DESC { - DepthEnable: depth_on, - DepthWriteMask: if depth_write { - D3D12_DEPTH_WRITE_MASK_ALL - } else { - D3D12_DEPTH_WRITE_MASK_ZERO - }, - DepthFunc: depth_func, - StencilEnable: stencil_on, - StencilReadMask: read_mask as _, - StencilWriteMask: write_mask as _, - FrontFace: front, - BackFace: back, - } -} - -pub fn map_comparison(func: pso::Comparison) -> D3D12_COMPARISON_FUNC { - use hal::pso::Comparison::*; - match func { - Never => D3D12_COMPARISON_FUNC_NEVER, - Less => D3D12_COMPARISON_FUNC_LESS, - LessEqual => D3D12_COMPARISON_FUNC_LESS_EQUAL, - Equal => D3D12_COMPARISON_FUNC_EQUAL, - GreaterEqual => D3D12_COMPARISON_FUNC_GREATER_EQUAL, - Greater => D3D12_COMPARISON_FUNC_GREATER, - NotEqual => D3D12_COMPARISON_FUNC_NOT_EQUAL, - Always => D3D12_COMPARISON_FUNC_ALWAYS, - } -} - -fn map_stencil_op(op: pso::StencilOp) -> D3D12_STENCIL_OP { - use hal::pso::StencilOp::*; - match op { - Keep => D3D12_STENCIL_OP_KEEP, - Zero => D3D12_STENCIL_OP_ZERO, - Replace => D3D12_STENCIL_OP_REPLACE, - IncrementClamp => D3D12_STENCIL_OP_INCR_SAT, - IncrementWrap => D3D12_STENCIL_OP_INCR, - DecrementClamp => D3D12_STENCIL_OP_DECR_SAT, - DecrementWrap => D3D12_STENCIL_OP_DECR, - Invert => D3D12_STENCIL_OP_INVERT, - } -} - -fn map_stencil_side(side: &pso::StencilFace) -> D3D12_DEPTH_STENCILOP_DESC { - D3D12_DEPTH_STENCILOP_DESC { - StencilFailOp: map_stencil_op(side.op_fail), - StencilDepthFailOp: map_stencil_op(side.op_depth_fail), - StencilPassOp: map_stencil_op(side.op_pass), - StencilFunc: map_comparison(side.fun), - } -} - -pub fn map_wrap(wrap: image::WrapMode) -> D3D12_TEXTURE_ADDRESS_MODE { - use hal::image::WrapMode::*; - match wrap { - Tile => D3D12_TEXTURE_ADDRESS_MODE_WRAP, - Mirror => D3D12_TEXTURE_ADDRESS_MODE_MIRROR, - Clamp => D3D12_TEXTURE_ADDRESS_MODE_CLAMP, - Border => D3D12_TEXTURE_ADDRESS_MODE_BORDER, - MirrorClamp => D3D12_TEXTURE_ADDRESS_MODE_MIRROR_ONCE, - } -} - -fn map_filter_type(filter: image::Filter) -> D3D12_FILTER_TYPE { - match filter { - image::Filter::Nearest => D3D12_FILTER_TYPE_POINT, - image::Filter::Linear => D3D12_FILTER_TYPE_LINEAR, - } -} - -pub fn map_filter( - mag_filter: image::Filter, - min_filter: image::Filter, - mip_filter: image::Filter, - reduction: D3D12_FILTER_REDUCTION_TYPE, - anisotropy_clamp: Option, -) -> D3D12_FILTER { - let mag = map_filter_type(mag_filter); - let min = map_filter_type(min_filter); - let mip = map_filter_type(mip_filter); - - (min & D3D12_FILTER_TYPE_MASK) << D3D12_MIN_FILTER_SHIFT - | (mag & D3D12_FILTER_TYPE_MASK) << D3D12_MAG_FILTER_SHIFT - | (mip & D3D12_FILTER_TYPE_MASK) << D3D12_MIP_FILTER_SHIFT - | (reduction & D3D12_FILTER_REDUCTION_TYPE_MASK) << D3D12_FILTER_REDUCTION_TYPE_SHIFT - | anisotropy_clamp - .map(|_| D3D12_FILTER_ANISOTROPIC) - .unwrap_or(0) -} - -pub fn map_buffer_resource_state(access: buffer::Access) -> D3D12_RESOURCE_STATES { - use self::buffer::Access; - // Mutable states - if access.contains(Access::SHADER_WRITE) { - return D3D12_RESOURCE_STATE_UNORDERED_ACCESS; - } - if access.contains(Access::TRANSFER_WRITE) { - // Resolve not relevant for buffers. - return D3D12_RESOURCE_STATE_COPY_DEST; - } - - // Read-only states - let mut state = D3D12_RESOURCE_STATE_COMMON; - - if access.contains(Access::TRANSFER_READ) { - state |= D3D12_RESOURCE_STATE_COPY_SOURCE; - } - if access.contains(Access::INDEX_BUFFER_READ) { - state |= D3D12_RESOURCE_STATE_INDEX_BUFFER; - } - if access.contains(Access::VERTEX_BUFFER_READ) || access.contains(Access::UNIFORM_READ) { - state |= D3D12_RESOURCE_STATE_VERTEX_AND_CONSTANT_BUFFER; - } - if access.contains(Access::INDIRECT_COMMAND_READ) { - state |= D3D12_RESOURCE_STATE_INDIRECT_ARGUMENT; - } - if access.contains(Access::SHADER_READ) { - // SHADER_READ only allows SRV access - state |= D3D12_RESOURCE_STATE_PIXEL_SHADER_RESOURCE - | D3D12_RESOURCE_STATE_NON_PIXEL_SHADER_RESOURCE; - } - - state -} - -/// Derive the combination of read-only states from the access flags. -fn derive_immutable_image_states(access: image::Access) -> D3D12_RESOURCE_STATES { - let mut state = D3D12_RESOURCE_STATE_COMMON; - - if access.contains(image::Access::TRANSFER_READ) { - state |= D3D12_RESOURCE_STATE_COPY_SOURCE; - } - if access.contains(image::Access::INPUT_ATTACHMENT_READ) { - state |= D3D12_RESOURCE_STATE_PIXEL_SHADER_RESOURCE; - } - if access.contains(image::Access::DEPTH_STENCIL_ATTACHMENT_READ) { - state |= D3D12_RESOURCE_STATE_DEPTH_READ; - } - if access.contains(image::Access::SHADER_READ) { - state |= D3D12_RESOURCE_STATE_PIXEL_SHADER_RESOURCE - | D3D12_RESOURCE_STATE_NON_PIXEL_SHADER_RESOURCE; - } - - state -} - -/// Derive the mutable or read-only states from the access flags. -fn derive_mutable_image_states(access: image::Access) -> D3D12_RESOURCE_STATES { - if access.contains(image::Access::SHADER_WRITE) { - return D3D12_RESOURCE_STATE_UNORDERED_ACCESS; - } - if access.contains(image::Access::COLOR_ATTACHMENT_WRITE) { - return D3D12_RESOURCE_STATE_RENDER_TARGET; - } - if access.contains(image::Access::DEPTH_STENCIL_ATTACHMENT_WRITE) { - return D3D12_RESOURCE_STATE_DEPTH_WRITE; - } - if access.contains(image::Access::TRANSFER_WRITE) { - return D3D12_RESOURCE_STATE_COPY_DEST; - } - - derive_immutable_image_states(access) -} - -pub fn map_image_resource_state( - access: image::Access, - layout: image::Layout, -) -> D3D12_RESOURCE_STATES { - match layout { - // the same as COMMON (general state) - image::Layout::Present => D3D12_RESOURCE_STATE_PRESENT, - image::Layout::ColorAttachmentOptimal => D3D12_RESOURCE_STATE_RENDER_TARGET, - image::Layout::DepthStencilAttachmentOptimal => D3D12_RESOURCE_STATE_DEPTH_WRITE, - // `TRANSFER_WRITE` requires special handling as it requires RESOLVE_DEST | COPY_DEST - // but only 1 write-only allowed. We do the required translation before the commands. - // We currently assume that `COPY_DEST` is more common state than out of renderpass resolves. - // Resolve operations need to insert a barrier before and after the command to transition - // from and into `COPY_DEST` to have a consistent state for srcAccess. - image::Layout::TransferDstOptimal => D3D12_RESOURCE_STATE_COPY_DEST, - image::Layout::TransferSrcOptimal => D3D12_RESOURCE_STATE_COPY_SOURCE, - image::Layout::General => derive_mutable_image_states(access), - image::Layout::ShaderReadOnlyOptimal | image::Layout::DepthStencilReadOnlyOptimal => { - derive_immutable_image_states(access) - } - image::Layout::Undefined | image::Layout::Preinitialized => D3D12_RESOURCE_STATE_COMMON, - } -} - -pub fn map_shader_visibility(flags: pso::ShaderStageFlags) -> ShaderVisibility { - use hal::pso::ShaderStageFlags as Ssf; - - match flags { - Ssf::VERTEX => ShaderVisibility::VS, - Ssf::GEOMETRY => ShaderVisibility::GS, - Ssf::HULL => ShaderVisibility::HS, - Ssf::DOMAIN => ShaderVisibility::DS, - Ssf::FRAGMENT => ShaderVisibility::PS, - _ => ShaderVisibility::All, - } -} - -pub fn map_buffer_flags(usage: buffer::Usage) -> D3D12_RESOURCE_FLAGS { - let mut flags = D3D12_RESOURCE_FLAG_NONE; - - // TRANSFER_DST also used for clearing buffers, which is implemented via UAV clears. - if usage.contains(buffer::Usage::STORAGE) || usage.contains(buffer::Usage::TRANSFER_DST) { - flags |= D3D12_RESOURCE_FLAG_ALLOW_UNORDERED_ACCESS; - } - - flags -} - -pub fn map_image_flags(usage: image::Usage, features: ImageFeature) -> D3D12_RESOURCE_FLAGS { - use self::image::Usage; - let mut flags = D3D12_RESOURCE_FLAG_NONE; - - // Blit operations implemented via a graphics pipeline - if usage.contains(Usage::COLOR_ATTACHMENT) { - debug_assert!(features.contains(ImageFeature::COLOR_ATTACHMENT)); - flags |= D3D12_RESOURCE_FLAG_ALLOW_RENDER_TARGET; - } - if usage.contains(Usage::DEPTH_STENCIL_ATTACHMENT) { - debug_assert!(features.contains(ImageFeature::DEPTH_STENCIL_ATTACHMENT)); - flags |= D3D12_RESOURCE_FLAG_ALLOW_DEPTH_STENCIL; - } - if usage.contains(Usage::TRANSFER_DST) { - if features.contains(ImageFeature::COLOR_ATTACHMENT) { - flags |= D3D12_RESOURCE_FLAG_ALLOW_RENDER_TARGET - }; - if features.contains(ImageFeature::DEPTH_STENCIL_ATTACHMENT) { - flags |= D3D12_RESOURCE_FLAG_ALLOW_DEPTH_STENCIL - }; - } - if usage.contains(Usage::STORAGE) { - debug_assert!(features.contains(ImageFeature::STORAGE)); - flags |= D3D12_RESOURCE_FLAG_ALLOW_UNORDERED_ACCESS; - } - if !features.contains(ImageFeature::SAMPLED) { - flags |= D3D12_RESOURCE_FLAG_DENY_SHADER_RESOURCE; - } - - flags -} - -pub fn map_stage(stage: ShaderStage) -> spirv::ExecutionModel { - match stage { - ShaderStage::Vertex => spirv::ExecutionModel::Vertex, - ShaderStage::Fragment => spirv::ExecutionModel::Fragment, - ShaderStage::Geometry => spirv::ExecutionModel::Geometry, - ShaderStage::Compute => spirv::ExecutionModel::GlCompute, - ShaderStage::Hull => spirv::ExecutionModel::TessellationControl, - ShaderStage::Domain => spirv::ExecutionModel::TessellationEvaluation, - ShaderStage::Task | ShaderStage::Mesh => { - panic!("{:?} shader is not yet implemented in SPIRV-Cross", stage) - } - } -} +use crate::validate_line_width; + +use spirv_cross::spirv; +use std::mem; + +use winapi::{ + shared::{ + basetsd::UINT8, + dxgiformat::*, + minwindef::{FALSE, INT, TRUE, UINT}, + }, + um::{d3d12::*, d3dcommon::*}, +}; + +use auxil::ShaderStage; +use hal::{ + buffer, + format::{Format, ImageFeature, SurfaceType, Swizzle}, + image, pso, +}; + +use native::ShaderVisibility; + +fn is_little_endinan() -> bool { + unsafe { 1 == *(&1u32 as *const _ as *const u8) } +} + +pub fn map_format(format: Format) -> Option { + use hal::format::Format::*; + + // Handling packed formats according to the platform endianness. + let reverse = is_little_endinan(); + let format = match format { + Bgra4Unorm if !reverse => DXGI_FORMAT_B4G4R4A4_UNORM, + R5g6b5Unorm if reverse => DXGI_FORMAT_B5G6R5_UNORM, + B5g6r5Unorm if !reverse => DXGI_FORMAT_B5G6R5_UNORM, + B5g5r5a1Unorm if !reverse => DXGI_FORMAT_B5G5R5A1_UNORM, + A1r5g5b5Unorm if reverse => DXGI_FORMAT_B5G5R5A1_UNORM, + R8Unorm => DXGI_FORMAT_R8_UNORM, + R8Snorm => DXGI_FORMAT_R8_SNORM, + R8Uint => DXGI_FORMAT_R8_UINT, + R8Sint => DXGI_FORMAT_R8_SINT, + Rg8Unorm => DXGI_FORMAT_R8G8_UNORM, + Rg8Snorm => DXGI_FORMAT_R8G8_SNORM, + Rg8Uint => DXGI_FORMAT_R8G8_UINT, + Rg8Sint => DXGI_FORMAT_R8G8_SINT, + Rgba8Unorm => DXGI_FORMAT_R8G8B8A8_UNORM, + Rgba8Snorm => DXGI_FORMAT_R8G8B8A8_SNORM, + Rgba8Uint => DXGI_FORMAT_R8G8B8A8_UINT, + Rgba8Sint => DXGI_FORMAT_R8G8B8A8_SINT, + Rgba8Srgb => DXGI_FORMAT_R8G8B8A8_UNORM_SRGB, + Bgra8Unorm => DXGI_FORMAT_B8G8R8A8_UNORM, + Bgra8Srgb => DXGI_FORMAT_B8G8R8A8_UNORM_SRGB, + Abgr8Unorm if reverse => DXGI_FORMAT_R8G8B8A8_UNORM, + Abgr8Snorm if reverse => DXGI_FORMAT_R8G8B8A8_SNORM, + Abgr8Uint if reverse => DXGI_FORMAT_R8G8B8A8_UINT, + Abgr8Sint if reverse => DXGI_FORMAT_R8G8B8A8_SINT, + Abgr8Srgb if reverse => DXGI_FORMAT_R8G8B8A8_UNORM_SRGB, + A2b10g10r10Unorm if reverse => DXGI_FORMAT_R10G10B10A2_UNORM, + A2b10g10r10Uint if reverse => DXGI_FORMAT_R10G10B10A2_UINT, + R16Unorm => DXGI_FORMAT_R16_UNORM, + R16Snorm => DXGI_FORMAT_R16_SNORM, + R16Uint => DXGI_FORMAT_R16_UINT, + R16Sint => DXGI_FORMAT_R16_SINT, + R16Sfloat => DXGI_FORMAT_R16_FLOAT, + Rg16Unorm => DXGI_FORMAT_R16G16_UNORM, + Rg16Snorm => DXGI_FORMAT_R16G16_SNORM, + Rg16Uint => DXGI_FORMAT_R16G16_UINT, + Rg16Sint => DXGI_FORMAT_R16G16_SINT, + Rg16Sfloat => DXGI_FORMAT_R16G16_FLOAT, + Rgba16Unorm => DXGI_FORMAT_R16G16B16A16_UNORM, + Rgba16Snorm => DXGI_FORMAT_R16G16B16A16_SNORM, + Rgba16Uint => DXGI_FORMAT_R16G16B16A16_UINT, + Rgba16Sint => DXGI_FORMAT_R16G16B16A16_SINT, + Rgba16Sfloat => DXGI_FORMAT_R16G16B16A16_FLOAT, + R32Uint => DXGI_FORMAT_R32_UINT, + R32Sint => DXGI_FORMAT_R32_SINT, + R32Sfloat => DXGI_FORMAT_R32_FLOAT, + Rg32Uint => DXGI_FORMAT_R32G32_UINT, + Rg32Sint => DXGI_FORMAT_R32G32_SINT, + Rg32Sfloat => DXGI_FORMAT_R32G32_FLOAT, + Rgb32Uint => DXGI_FORMAT_R32G32B32_UINT, + Rgb32Sint => DXGI_FORMAT_R32G32B32_SINT, + Rgb32Sfloat => DXGI_FORMAT_R32G32B32_FLOAT, + Rgba32Uint => DXGI_FORMAT_R32G32B32A32_UINT, + Rgba32Sint => DXGI_FORMAT_R32G32B32A32_SINT, + Rgba32Sfloat => DXGI_FORMAT_R32G32B32A32_FLOAT, + B10g11r11Ufloat if reverse => DXGI_FORMAT_R11G11B10_FLOAT, + E5b9g9r9Ufloat if reverse => DXGI_FORMAT_R9G9B9E5_SHAREDEXP, + D16Unorm => DXGI_FORMAT_D16_UNORM, + D24UnormS8Uint => DXGI_FORMAT_D24_UNORM_S8_UINT, + X8D24Unorm if reverse => DXGI_FORMAT_D24_UNORM_S8_UINT, + D32Sfloat => DXGI_FORMAT_D32_FLOAT, + D32SfloatS8Uint => DXGI_FORMAT_D32_FLOAT_S8X24_UINT, + Bc1RgbUnorm | Bc1RgbaUnorm => DXGI_FORMAT_BC1_UNORM, + Bc1RgbSrgb | Bc1RgbaSrgb => DXGI_FORMAT_BC1_UNORM_SRGB, + Bc2Unorm => DXGI_FORMAT_BC2_UNORM, + Bc2Srgb => DXGI_FORMAT_BC2_UNORM_SRGB, + Bc3Unorm => DXGI_FORMAT_BC3_UNORM, + Bc3Srgb => DXGI_FORMAT_BC3_UNORM_SRGB, + Bc4Unorm => DXGI_FORMAT_BC4_UNORM, + Bc4Snorm => DXGI_FORMAT_BC4_SNORM, + Bc5Unorm => DXGI_FORMAT_BC5_UNORM, + Bc5Snorm => DXGI_FORMAT_BC5_SNORM, + Bc6hUfloat => DXGI_FORMAT_BC6H_UF16, + Bc6hSfloat => DXGI_FORMAT_BC6H_SF16, + Bc7Unorm => DXGI_FORMAT_BC7_UNORM, + Bc7Srgb => DXGI_FORMAT_BC7_UNORM_SRGB, + + _ => return None, + }; + + Some(format) +} + +pub fn map_format_shader_depth(surface: SurfaceType) -> Option { + match surface { + SurfaceType::D16 => Some(DXGI_FORMAT_R16_UNORM), + SurfaceType::X8D24 | SurfaceType::D24_S8 => Some(DXGI_FORMAT_R24_UNORM_X8_TYPELESS), + SurfaceType::D32 => Some(DXGI_FORMAT_R32_FLOAT), + SurfaceType::D32_S8 => Some(DXGI_FORMAT_R32_FLOAT_X8X24_TYPELESS), + _ => return None, + } +} + +pub fn map_format_shader_stencil(surface: SurfaceType) -> Option { + match surface { + SurfaceType::D24_S8 => Some(DXGI_FORMAT_X24_TYPELESS_G8_UINT), + SurfaceType::D32_S8 => Some(DXGI_FORMAT_X32_TYPELESS_G8X24_UINT), + _ => None, + } +} + +pub fn map_format_dsv(surface: SurfaceType) -> Option { + match surface { + SurfaceType::D16 => Some(DXGI_FORMAT_D16_UNORM), + SurfaceType::X8D24 | SurfaceType::D24_S8 => Some(DXGI_FORMAT_D24_UNORM_S8_UINT), + SurfaceType::D32 => Some(DXGI_FORMAT_D32_FLOAT), + SurfaceType::D32_S8 => Some(DXGI_FORMAT_D32_FLOAT_S8X24_UINT), + _ => None, + } +} + +pub fn map_format_nosrgb(format: Format) -> Option { + // NOTE: DXGI doesn't allow sRGB format on the swapchain, but + // creating RTV of swapchain buffers with sRGB works + match format { + Format::Bgra8Srgb => Some(DXGI_FORMAT_B8G8R8A8_UNORM), + Format::Rgba8Srgb => Some(DXGI_FORMAT_R8G8B8A8_UNORM), + _ => map_format(format), + } +} + +pub fn map_swizzle(swizzle: Swizzle) -> UINT { + use hal::format::Component::*; + + [swizzle.0, swizzle.1, swizzle.2, swizzle.3] + .iter() + .enumerate() + .fold( + D3D12_SHADER_COMPONENT_MAPPING_ALWAYS_SET_BIT_AVOIDING_ZEROMEM_MISTAKES, + |mapping, (i, &component)| { + let value = match component { + R => D3D12_SHADER_COMPONENT_MAPPING_FROM_MEMORY_COMPONENT_0, + G => D3D12_SHADER_COMPONENT_MAPPING_FROM_MEMORY_COMPONENT_1, + B => D3D12_SHADER_COMPONENT_MAPPING_FROM_MEMORY_COMPONENT_2, + A => D3D12_SHADER_COMPONENT_MAPPING_FROM_MEMORY_COMPONENT_3, + Zero => D3D12_SHADER_COMPONENT_MAPPING_FORCE_VALUE_0, + One => D3D12_SHADER_COMPONENT_MAPPING_FORCE_VALUE_1, + }; + mapping | (value << D3D12_SHADER_COMPONENT_MAPPING_SHIFT as usize * i) + }, + ) +} + +pub fn swizzle_rg(swizzle: Swizzle) -> Swizzle { + use hal::format::Component as C; + fn map_component(c: C) -> C { + match c { + C::R => C::G, + C::G => C::R, + x => x, + } + } + Swizzle( + map_component(swizzle.0), + map_component(swizzle.1), + map_component(swizzle.2), + map_component(swizzle.3), + ) +} + +pub fn map_surface_type(st: SurfaceType) -> Option { + use hal::format::SurfaceType::*; + + assert_eq!(1, unsafe { *(&1u32 as *const _ as *const u8) }); + Some(match st { + R5_G6_B5 => DXGI_FORMAT_B5G6R5_UNORM, + A1_R5_G5_B5 => DXGI_FORMAT_B5G5R5A1_UNORM, + R8 => DXGI_FORMAT_R8_TYPELESS, + R8_G8 => DXGI_FORMAT_R8G8_TYPELESS, + R8_G8_B8_A8 => DXGI_FORMAT_R8G8B8A8_TYPELESS, + B8_G8_R8_A8 => DXGI_FORMAT_B8G8R8A8_TYPELESS, + A8_B8_G8_R8 => DXGI_FORMAT_R8G8B8A8_TYPELESS, + A2_B10_G10_R10 => DXGI_FORMAT_R10G10B10A2_TYPELESS, + R16 => DXGI_FORMAT_R16_TYPELESS, + R16_G16 => DXGI_FORMAT_R16G16_TYPELESS, + R16_G16_B16_A16 => DXGI_FORMAT_R16G16B16A16_TYPELESS, + R32 => DXGI_FORMAT_R32_TYPELESS, + R32_G32 => DXGI_FORMAT_R32G32_TYPELESS, + R32_G32_B32 => DXGI_FORMAT_R32G32B32_TYPELESS, + R32_G32_B32_A32 => DXGI_FORMAT_R32G32B32A32_TYPELESS, + B10_G11_R11 => DXGI_FORMAT_R11G11B10_FLOAT, + E5_B9_G9_R9 => DXGI_FORMAT_R9G9B9E5_SHAREDEXP, + D16 => DXGI_FORMAT_R16_TYPELESS, + X8D24 => DXGI_FORMAT_R24G8_TYPELESS, + D32 => DXGI_FORMAT_R32_TYPELESS, + D24_S8 => DXGI_FORMAT_R24G8_TYPELESS, + D32_S8 => DXGI_FORMAT_R32_FLOAT_X8X24_TYPELESS, + _ => return None, + }) +} + +pub fn map_topology_type(primitive: pso::Primitive) -> D3D12_PRIMITIVE_TOPOLOGY_TYPE { + use hal::pso::Primitive::*; + match primitive { + PointList => D3D12_PRIMITIVE_TOPOLOGY_TYPE_POINT, + LineList | LineStrip => D3D12_PRIMITIVE_TOPOLOGY_TYPE_LINE, + TriangleList | TriangleStrip => D3D12_PRIMITIVE_TOPOLOGY_TYPE_TRIANGLE, + PatchList(_) => D3D12_PRIMITIVE_TOPOLOGY_TYPE_PATCH, + } +} + +pub fn map_topology(ia: &pso::InputAssemblerDesc) -> D3D12_PRIMITIVE_TOPOLOGY { + use hal::pso::Primitive::*; + match (ia.primitive, ia.with_adjacency) { + (PointList, false) => D3D_PRIMITIVE_TOPOLOGY_POINTLIST, + (PointList, true) => panic!("Points can't have adjacency info"), + (LineList, false) => D3D_PRIMITIVE_TOPOLOGY_LINELIST, + (LineList, true) => D3D_PRIMITIVE_TOPOLOGY_LINELIST_ADJ, + (LineStrip, false) => D3D_PRIMITIVE_TOPOLOGY_LINESTRIP, + (LineStrip, true) => D3D_PRIMITIVE_TOPOLOGY_LINESTRIP_ADJ, + (TriangleList, false) => D3D_PRIMITIVE_TOPOLOGY_TRIANGLELIST, + (TriangleList, true) => D3D_PRIMITIVE_TOPOLOGY_TRIANGLELIST_ADJ, + (TriangleStrip, false) => D3D_PRIMITIVE_TOPOLOGY_TRIANGLESTRIP, + (TriangleStrip, true) => D3D_PRIMITIVE_TOPOLOGY_TRIANGLESTRIP_ADJ, + (PatchList(num), false) => { + assert!(num != 0); + D3D_PRIMITIVE_TOPOLOGY_1_CONTROL_POINT_PATCHLIST + (num as u32) - 1 + } + (_, true) => panic!("Patches can't have adjacency info"), + } +} + +pub fn map_rasterizer(rasterizer: &pso::Rasterizer, multisample: bool) -> D3D12_RASTERIZER_DESC { + use hal::pso::FrontFace::*; + use hal::pso::PolygonMode::*; + + let bias = match rasterizer.depth_bias { + //TODO: support dynamic depth bias + Some(pso::State::Static(db)) => db, + Some(_) | None => pso::DepthBias::default(), + }; + + if let pso::State::Static(w) = rasterizer.line_width { + validate_line_width(w); + } + + D3D12_RASTERIZER_DESC { + FillMode: match rasterizer.polygon_mode { + Point => { + error!("Point rasterization is not supported"); + D3D12_FILL_MODE_WIREFRAME + } + Line => D3D12_FILL_MODE_WIREFRAME, + Fill => D3D12_FILL_MODE_SOLID, + }, + CullMode: match rasterizer.cull_face { + pso::Face::NONE => D3D12_CULL_MODE_NONE, + pso::Face::FRONT => D3D12_CULL_MODE_FRONT, + pso::Face::BACK => D3D12_CULL_MODE_BACK, + _ => panic!("Culling both front and back faces is not supported"), + }, + FrontCounterClockwise: match rasterizer.front_face { + Clockwise => FALSE, + CounterClockwise => TRUE, + }, + DepthBias: bias.const_factor as INT, + DepthBiasClamp: bias.clamp, + SlopeScaledDepthBias: bias.slope_factor, + DepthClipEnable: !rasterizer.depth_clamping as _, + MultisampleEnable: if multisample { TRUE } else { FALSE }, + ForcedSampleCount: 0, // TODO: currently not supported + AntialiasedLineEnable: FALSE, // TODO: currently not supported + ConservativeRaster: if rasterizer.conservative { + D3D12_CONSERVATIVE_RASTERIZATION_MODE_ON + } else { + D3D12_CONSERVATIVE_RASTERIZATION_MODE_OFF + }, + } +} + +fn map_factor(factor: pso::Factor, is_alpha: bool) -> D3D12_BLEND { + use hal::pso::Factor::*; + match factor { + Zero => D3D12_BLEND_ZERO, + One => D3D12_BLEND_ONE, + SrcColor if is_alpha => D3D12_BLEND_SRC_ALPHA, + SrcColor => D3D12_BLEND_SRC_COLOR, + OneMinusSrcColor if is_alpha => D3D12_BLEND_INV_SRC_ALPHA, + OneMinusSrcColor => D3D12_BLEND_INV_SRC_COLOR, + DstColor if is_alpha => D3D12_BLEND_DEST_ALPHA, + DstColor => D3D12_BLEND_DEST_COLOR, + OneMinusDstColor if is_alpha => D3D12_BLEND_INV_DEST_ALPHA, + OneMinusDstColor => D3D12_BLEND_INV_DEST_COLOR, + SrcAlpha => D3D12_BLEND_SRC_ALPHA, + OneMinusSrcAlpha => D3D12_BLEND_INV_SRC_ALPHA, + DstAlpha => D3D12_BLEND_DEST_ALPHA, + OneMinusDstAlpha => D3D12_BLEND_INV_DEST_ALPHA, + ConstColor | ConstAlpha => D3D12_BLEND_BLEND_FACTOR, + OneMinusConstColor | OneMinusConstAlpha => D3D12_BLEND_INV_BLEND_FACTOR, + SrcAlphaSaturate => D3D12_BLEND_SRC_ALPHA_SAT, + Src1Color if is_alpha => D3D12_BLEND_SRC1_ALPHA, + Src1Color => D3D12_BLEND_SRC1_COLOR, + OneMinusSrc1Color if is_alpha => D3D12_BLEND_INV_SRC1_ALPHA, + OneMinusSrc1Color => D3D12_BLEND_INV_SRC1_COLOR, + Src1Alpha => D3D12_BLEND_SRC1_ALPHA, + OneMinusSrc1Alpha => D3D12_BLEND_INV_SRC1_ALPHA, + } +} + +fn map_blend_op( + operation: pso::BlendOp, + is_alpha: bool, +) -> (D3D12_BLEND_OP, D3D12_BLEND, D3D12_BLEND) { + use hal::pso::BlendOp::*; + match operation { + Add { src, dst } => ( + D3D12_BLEND_OP_ADD, + map_factor(src, is_alpha), + map_factor(dst, is_alpha), + ), + Sub { src, dst } => ( + D3D12_BLEND_OP_SUBTRACT, + map_factor(src, is_alpha), + map_factor(dst, is_alpha), + ), + RevSub { src, dst } => ( + D3D12_BLEND_OP_REV_SUBTRACT, + map_factor(src, is_alpha), + map_factor(dst, is_alpha), + ), + Min => (D3D12_BLEND_OP_MIN, D3D12_BLEND_ZERO, D3D12_BLEND_ZERO), + Max => (D3D12_BLEND_OP_MAX, D3D12_BLEND_ZERO, D3D12_BLEND_ZERO), + } +} + +pub fn map_render_targets( + color_targets: &[pso::ColorBlendDesc], +) -> [D3D12_RENDER_TARGET_BLEND_DESC; D3D12_SIMULTANEOUS_RENDER_TARGET_COUNT as usize] { + let dummy_target = D3D12_RENDER_TARGET_BLEND_DESC { + BlendEnable: FALSE, + LogicOpEnable: FALSE, + SrcBlend: D3D12_BLEND_ZERO, + DestBlend: D3D12_BLEND_ZERO, + BlendOp: D3D12_BLEND_OP_ADD, + SrcBlendAlpha: D3D12_BLEND_ZERO, + DestBlendAlpha: D3D12_BLEND_ZERO, + BlendOpAlpha: D3D12_BLEND_OP_ADD, + LogicOp: D3D12_LOGIC_OP_CLEAR, + RenderTargetWriteMask: 0, + }; + let mut targets = [dummy_target; D3D12_SIMULTANEOUS_RENDER_TARGET_COUNT as usize]; + + for (target, color_desc) in targets.iter_mut().zip(color_targets.iter()) { + target.RenderTargetWriteMask = color_desc.mask.bits() as UINT8; + if let Some(ref blend) = color_desc.blend { + let (color_op, color_src, color_dst) = map_blend_op(blend.color, false); + let (alpha_op, alpha_src, alpha_dst) = map_blend_op(blend.alpha, true); + target.BlendEnable = TRUE; + target.BlendOp = color_op; + target.SrcBlend = color_src; + target.DestBlend = color_dst; + target.BlendOpAlpha = alpha_op; + target.SrcBlendAlpha = alpha_src; + target.DestBlendAlpha = alpha_dst; + } + } + + targets +} + +pub fn map_depth_stencil(dsi: &pso::DepthStencilDesc) -> D3D12_DEPTH_STENCIL_DESC { + let (depth_on, depth_write, depth_func) = match dsi.depth { + Some(ref depth) => (TRUE, depth.write, map_comparison(depth.fun)), + None => unsafe { mem::zeroed() }, + }; + + let (stencil_on, front, back, read_mask, write_mask) = match dsi.stencil { + Some(ref stencil) => { + let read_masks = stencil.read_masks.static_or(pso::Sided::new(!0)); + let write_masks = stencil.write_masks.static_or(pso::Sided::new(!0)); + if read_masks.front != read_masks.back || write_masks.front != write_masks.back { + error!( + "Different sides are specified for read ({:?} and write ({:?}) stencil masks", + read_masks, write_masks + ); + } + ( + TRUE, + map_stencil_side(&stencil.faces.front), + map_stencil_side(&stencil.faces.back), + read_masks.front, + write_masks.front, + ) + } + None => unsafe { mem::zeroed() }, + }; + + D3D12_DEPTH_STENCIL_DESC { + DepthEnable: depth_on, + DepthWriteMask: if depth_write { + D3D12_DEPTH_WRITE_MASK_ALL + } else { + D3D12_DEPTH_WRITE_MASK_ZERO + }, + DepthFunc: depth_func, + StencilEnable: stencil_on, + StencilReadMask: read_mask as _, + StencilWriteMask: write_mask as _, + FrontFace: front, + BackFace: back, + } +} + +pub fn map_comparison(func: pso::Comparison) -> D3D12_COMPARISON_FUNC { + use hal::pso::Comparison::*; + match func { + Never => D3D12_COMPARISON_FUNC_NEVER, + Less => D3D12_COMPARISON_FUNC_LESS, + LessEqual => D3D12_COMPARISON_FUNC_LESS_EQUAL, + Equal => D3D12_COMPARISON_FUNC_EQUAL, + GreaterEqual => D3D12_COMPARISON_FUNC_GREATER_EQUAL, + Greater => D3D12_COMPARISON_FUNC_GREATER, + NotEqual => D3D12_COMPARISON_FUNC_NOT_EQUAL, + Always => D3D12_COMPARISON_FUNC_ALWAYS, + } +} + +fn map_stencil_op(op: pso::StencilOp) -> D3D12_STENCIL_OP { + use hal::pso::StencilOp::*; + match op { + Keep => D3D12_STENCIL_OP_KEEP, + Zero => D3D12_STENCIL_OP_ZERO, + Replace => D3D12_STENCIL_OP_REPLACE, + IncrementClamp => D3D12_STENCIL_OP_INCR_SAT, + IncrementWrap => D3D12_STENCIL_OP_INCR, + DecrementClamp => D3D12_STENCIL_OP_DECR_SAT, + DecrementWrap => D3D12_STENCIL_OP_DECR, + Invert => D3D12_STENCIL_OP_INVERT, + } +} + +fn map_stencil_side(side: &pso::StencilFace) -> D3D12_DEPTH_STENCILOP_DESC { + D3D12_DEPTH_STENCILOP_DESC { + StencilFailOp: map_stencil_op(side.op_fail), + StencilDepthFailOp: map_stencil_op(side.op_depth_fail), + StencilPassOp: map_stencil_op(side.op_pass), + StencilFunc: map_comparison(side.fun), + } +} + +pub fn map_wrap(wrap: image::WrapMode) -> D3D12_TEXTURE_ADDRESS_MODE { + use hal::image::WrapMode::*; + match wrap { + Tile => D3D12_TEXTURE_ADDRESS_MODE_WRAP, + Mirror => D3D12_TEXTURE_ADDRESS_MODE_MIRROR, + Clamp => D3D12_TEXTURE_ADDRESS_MODE_CLAMP, + Border => D3D12_TEXTURE_ADDRESS_MODE_BORDER, + MirrorClamp => D3D12_TEXTURE_ADDRESS_MODE_MIRROR_ONCE, + } +} + +fn map_filter_type(filter: image::Filter) -> D3D12_FILTER_TYPE { + match filter { + image::Filter::Nearest => D3D12_FILTER_TYPE_POINT, + image::Filter::Linear => D3D12_FILTER_TYPE_LINEAR, + } +} + +pub fn map_filter( + mag_filter: image::Filter, + min_filter: image::Filter, + mip_filter: image::Filter, + reduction: D3D12_FILTER_REDUCTION_TYPE, + anisotropy_clamp: Option, +) -> D3D12_FILTER { + let mag = map_filter_type(mag_filter); + let min = map_filter_type(min_filter); + let mip = map_filter_type(mip_filter); + + (min & D3D12_FILTER_TYPE_MASK) << D3D12_MIN_FILTER_SHIFT + | (mag & D3D12_FILTER_TYPE_MASK) << D3D12_MAG_FILTER_SHIFT + | (mip & D3D12_FILTER_TYPE_MASK) << D3D12_MIP_FILTER_SHIFT + | (reduction & D3D12_FILTER_REDUCTION_TYPE_MASK) << D3D12_FILTER_REDUCTION_TYPE_SHIFT + | anisotropy_clamp + .map(|_| D3D12_FILTER_ANISOTROPIC) + .unwrap_or(0) +} + +pub fn map_buffer_resource_state(access: buffer::Access) -> D3D12_RESOURCE_STATES { + use self::buffer::Access; + // Mutable states + if access.contains(Access::SHADER_WRITE) { + return D3D12_RESOURCE_STATE_UNORDERED_ACCESS; + } + if access.contains(Access::TRANSFER_WRITE) { + // Resolve not relevant for buffers. + return D3D12_RESOURCE_STATE_COPY_DEST; + } + + // Read-only states + let mut state = D3D12_RESOURCE_STATE_COMMON; + + if access.contains(Access::TRANSFER_READ) { + state |= D3D12_RESOURCE_STATE_COPY_SOURCE; + } + if access.contains(Access::INDEX_BUFFER_READ) { + state |= D3D12_RESOURCE_STATE_INDEX_BUFFER; + } + if access.contains(Access::VERTEX_BUFFER_READ) || access.contains(Access::UNIFORM_READ) { + state |= D3D12_RESOURCE_STATE_VERTEX_AND_CONSTANT_BUFFER; + } + if access.contains(Access::INDIRECT_COMMAND_READ) { + state |= D3D12_RESOURCE_STATE_INDIRECT_ARGUMENT; + } + if access.contains(Access::SHADER_READ) { + // SHADER_READ only allows SRV access + state |= D3D12_RESOURCE_STATE_PIXEL_SHADER_RESOURCE + | D3D12_RESOURCE_STATE_NON_PIXEL_SHADER_RESOURCE; + } + + state +} + +/// Derive the combination of read-only states from the access flags. +fn derive_immutable_image_states(access: image::Access) -> D3D12_RESOURCE_STATES { + let mut state = D3D12_RESOURCE_STATE_COMMON; + + if access.contains(image::Access::TRANSFER_READ) { + state |= D3D12_RESOURCE_STATE_COPY_SOURCE; + } + if access.contains(image::Access::INPUT_ATTACHMENT_READ) { + state |= D3D12_RESOURCE_STATE_PIXEL_SHADER_RESOURCE; + } + if access.contains(image::Access::DEPTH_STENCIL_ATTACHMENT_READ) { + state |= D3D12_RESOURCE_STATE_DEPTH_READ; + } + if access.contains(image::Access::SHADER_READ) { + state |= D3D12_RESOURCE_STATE_PIXEL_SHADER_RESOURCE + | D3D12_RESOURCE_STATE_NON_PIXEL_SHADER_RESOURCE; + } + + state +} + +/// Derive the mutable or read-only states from the access flags. +fn derive_mutable_image_states(access: image::Access) -> D3D12_RESOURCE_STATES { + if access.contains(image::Access::SHADER_WRITE) { + return D3D12_RESOURCE_STATE_UNORDERED_ACCESS; + } + if access.contains(image::Access::COLOR_ATTACHMENT_WRITE) { + return D3D12_RESOURCE_STATE_RENDER_TARGET; + } + if access.contains(image::Access::DEPTH_STENCIL_ATTACHMENT_WRITE) { + return D3D12_RESOURCE_STATE_DEPTH_WRITE; + } + if access.contains(image::Access::TRANSFER_WRITE) { + return D3D12_RESOURCE_STATE_COPY_DEST; + } + + derive_immutable_image_states(access) +} + +pub fn map_image_resource_state( + access: image::Access, + layout: image::Layout, +) -> D3D12_RESOURCE_STATES { + match layout { + // the same as COMMON (general state) + image::Layout::Present => D3D12_RESOURCE_STATE_PRESENT, + image::Layout::ColorAttachmentOptimal => D3D12_RESOURCE_STATE_RENDER_TARGET, + image::Layout::DepthStencilAttachmentOptimal => D3D12_RESOURCE_STATE_DEPTH_WRITE, + // `TRANSFER_WRITE` requires special handling as it requires RESOLVE_DEST | COPY_DEST + // but only 1 write-only allowed. We do the required translation before the commands. + // We currently assume that `COPY_DEST` is more common state than out of renderpass resolves. + // Resolve operations need to insert a barrier before and after the command to transition + // from and into `COPY_DEST` to have a consistent state for srcAccess. + image::Layout::TransferDstOptimal => D3D12_RESOURCE_STATE_COPY_DEST, + image::Layout::TransferSrcOptimal => D3D12_RESOURCE_STATE_COPY_SOURCE, + image::Layout::General => derive_mutable_image_states(access), + image::Layout::ShaderReadOnlyOptimal | image::Layout::DepthStencilReadOnlyOptimal => { + derive_immutable_image_states(access) + } + image::Layout::Undefined | image::Layout::Preinitialized => D3D12_RESOURCE_STATE_COMMON, + } +} + +pub fn map_shader_visibility(flags: pso::ShaderStageFlags) -> ShaderVisibility { + use hal::pso::ShaderStageFlags as Ssf; + + match flags { + Ssf::VERTEX => ShaderVisibility::VS, + Ssf::GEOMETRY => ShaderVisibility::GS, + Ssf::HULL => ShaderVisibility::HS, + Ssf::DOMAIN => ShaderVisibility::DS, + Ssf::FRAGMENT => ShaderVisibility::PS, + _ => ShaderVisibility::All, + } +} + +pub fn map_buffer_flags(usage: buffer::Usage) -> D3D12_RESOURCE_FLAGS { + let mut flags = D3D12_RESOURCE_FLAG_NONE; + + // TRANSFER_DST also used for clearing buffers, which is implemented via UAV clears. + if usage.contains(buffer::Usage::STORAGE) || usage.contains(buffer::Usage::TRANSFER_DST) { + flags |= D3D12_RESOURCE_FLAG_ALLOW_UNORDERED_ACCESS; + } + + flags +} + +pub fn map_image_flags(usage: image::Usage, features: ImageFeature) -> D3D12_RESOURCE_FLAGS { + use self::image::Usage; + let mut flags = D3D12_RESOURCE_FLAG_NONE; + + // Blit operations implemented via a graphics pipeline + if usage.contains(Usage::COLOR_ATTACHMENT) { + debug_assert!(features.contains(ImageFeature::COLOR_ATTACHMENT)); + flags |= D3D12_RESOURCE_FLAG_ALLOW_RENDER_TARGET; + } + if usage.contains(Usage::DEPTH_STENCIL_ATTACHMENT) { + debug_assert!(features.contains(ImageFeature::DEPTH_STENCIL_ATTACHMENT)); + flags |= D3D12_RESOURCE_FLAG_ALLOW_DEPTH_STENCIL; + } + if usage.contains(Usage::TRANSFER_DST) { + if features.contains(ImageFeature::COLOR_ATTACHMENT) { + flags |= D3D12_RESOURCE_FLAG_ALLOW_RENDER_TARGET + }; + if features.contains(ImageFeature::DEPTH_STENCIL_ATTACHMENT) { + flags |= D3D12_RESOURCE_FLAG_ALLOW_DEPTH_STENCIL + }; + } + if usage.contains(Usage::STORAGE) { + debug_assert!(features.contains(ImageFeature::STORAGE)); + flags |= D3D12_RESOURCE_FLAG_ALLOW_UNORDERED_ACCESS; + } + if !features.contains(ImageFeature::SAMPLED) { + flags |= D3D12_RESOURCE_FLAG_DENY_SHADER_RESOURCE; + } + + flags +} + +pub fn map_stage(stage: ShaderStage) -> spirv::ExecutionModel { + match stage { + ShaderStage::Vertex => spirv::ExecutionModel::Vertex, + ShaderStage::Fragment => spirv::ExecutionModel::Fragment, + ShaderStage::Geometry => spirv::ExecutionModel::Geometry, + ShaderStage::Compute => spirv::ExecutionModel::GlCompute, + ShaderStage::Hull => spirv::ExecutionModel::TessellationControl, + ShaderStage::Domain => spirv::ExecutionModel::TessellationEvaluation, + ShaderStage::Task | ShaderStage::Mesh => { + panic!("{:?} shader is not yet implemented in SPIRV-Cross", stage) + } + } +} diff --git a/third_party/rust/gfx-backend-dx12/src/descriptors_cpu.rs b/third_party/rust/gfx-backend-dx12/src/descriptors_cpu.rs index 8365b752ff1d..818b82f28c5b 100644 --- a/third_party/rust/gfx-backend-dx12/src/descriptors_cpu.rs +++ b/third_party/rust/gfx-backend-dx12/src/descriptors_cpu.rs @@ -1,305 +1,305 @@ -use bit_set::BitSet; -use native::{CpuDescriptor, DescriptorHeapFlags, DescriptorHeapType}; -use std::fmt; - -// Linear stack allocator for CPU descriptor heaps. -pub struct HeapLinear { - handle_size: usize, - num: usize, - size: usize, - start: CpuDescriptor, - raw: native::DescriptorHeap, -} - -impl fmt::Debug for HeapLinear { - fn fmt(&self, fmt: &mut fmt::Formatter) -> fmt::Result { - fmt.write_str("HeapLinear") - } -} - -impl HeapLinear { - pub fn new(device: native::Device, ty: DescriptorHeapType, size: usize) -> Self { - let (heap, _hr) = - device.create_descriptor_heap(size as _, ty, DescriptorHeapFlags::empty(), 0); - - HeapLinear { - handle_size: device.get_descriptor_increment_size(ty) as _, - num: 0, - size, - start: heap.start_cpu_descriptor(), - raw: heap, - } - } - - pub fn alloc_handle(&mut self) -> CpuDescriptor { - assert!(!self.is_full()); - - let slot = self.num; - self.num += 1; - - CpuDescriptor { - ptr: self.start.ptr + self.handle_size * slot, - } - } - - pub fn is_full(&self) -> bool { - self.num >= self.size - } - - pub fn clear(&mut self) { - self.num = 0; - } - - pub unsafe fn destroy(&self) { - self.raw.destroy(); - } -} - -const HEAP_SIZE_FIXED: usize = 64; - -// Fixed-size free-list allocator for CPU descriptors. -struct Heap { - // Bit flag representation of available handles in the heap. - // - // 0 - Occupied - // 1 - free - availability: u64, - handle_size: usize, - start: CpuDescriptor, - raw: native::DescriptorHeap, -} - -impl fmt::Debug for Heap { - fn fmt(&self, fmt: &mut fmt::Formatter) -> fmt::Result { - fmt.write_str("Heap") - } -} - -impl Heap { - fn new(device: native::Device, ty: DescriptorHeapType) -> Self { - let (heap, _hr) = device.create_descriptor_heap( - HEAP_SIZE_FIXED as _, - ty, - DescriptorHeapFlags::empty(), - 0, - ); - - Heap { - handle_size: device.get_descriptor_increment_size(ty) as _, - availability: !0, // all free! - start: heap.start_cpu_descriptor(), - raw: heap, - } - } - - pub fn alloc_handle(&mut self) -> CpuDescriptor { - // Find first free slot. - let slot = self.availability.trailing_zeros() as usize; - assert!(slot < HEAP_SIZE_FIXED); - // Set the slot as occupied. - self.availability ^= 1 << slot; - - CpuDescriptor { - ptr: self.start.ptr + self.handle_size * slot, - } - } - - pub fn free_handle(&mut self, handle: CpuDescriptor) { - let slot = (handle.ptr - self.start.ptr) / self.handle_size; - assert!(slot < HEAP_SIZE_FIXED); - assert_eq!(self.availability & (1 << slot), 0); - self.availability ^= 1 << slot; - } - - pub fn is_full(&self) -> bool { - self.availability == 0 - } - - pub unsafe fn destroy(&self) { - self.raw.destroy(); - } -} - -#[derive(Clone, Copy)] -pub struct Handle { - pub raw: CpuDescriptor, - heap_index: usize, -} - -pub struct DescriptorCpuPool { - device: native::Device, - ty: DescriptorHeapType, - heaps: Vec, - avaliable_heap_indices: BitSet, -} - -impl fmt::Debug for DescriptorCpuPool { - fn fmt(&self, fmt: &mut fmt::Formatter) -> fmt::Result { - fmt.write_str("DescriptorCpuPool") - } -} - -impl DescriptorCpuPool { - pub fn new(device: native::Device, ty: DescriptorHeapType) -> Self { - DescriptorCpuPool { - device, - ty, - heaps: Vec::new(), - avaliable_heap_indices: BitSet::new(), - } - } - - pub fn alloc_handle(&mut self) -> Handle { - let heap_index = self - .avaliable_heap_indices - .iter() - .next() - .unwrap_or_else(|| { - // Allocate a new heap - let id = self.heaps.len(); - self.heaps.push(Heap::new(self.device, self.ty)); - self.avaliable_heap_indices.insert(id); - id - }); - - let heap = &mut self.heaps[heap_index]; - let handle = Handle { - raw: heap.alloc_handle(), - heap_index, - }; - if heap.is_full() { - self.avaliable_heap_indices.remove(heap_index); - } - - handle - } - - pub fn free_handle(&mut self, handle: Handle) { - self.heaps[handle.heap_index].free_handle(handle.raw); - self.avaliable_heap_indices.insert(handle.heap_index); - } - - pub unsafe fn destroy(&self) { - for heap in &self.heaps { - heap.destroy(); - } - } -} - -#[derive(Default)] -pub struct CopyAccumulator { - starts: Vec, - counts: Vec, -} - -impl CopyAccumulator { - pub fn add(&mut self, start: CpuDescriptor, count: u32) { - self.starts.push(start); - self.counts.push(count); - } - - pub fn clear(&mut self) { - self.starts.clear(); - self.counts.clear(); - } - - fn total(&self) -> u32 { - self.counts.iter().sum() - } -} - -#[derive(Default)] -pub struct MultiCopyAccumulator { - pub src_views: CopyAccumulator, - pub src_samplers: CopyAccumulator, - pub dst_views: CopyAccumulator, - pub dst_samplers: CopyAccumulator, -} - -impl MultiCopyAccumulator { - pub unsafe fn flush(&self, device: native::Device) { - use winapi::um::d3d12; - assert_eq!(self.src_views.total(), self.dst_views.total()); - assert_eq!(self.src_samplers.total(), self.dst_samplers.total()); - - if !self.src_views.starts.is_empty() { - device.CopyDescriptors( - self.dst_views.starts.len() as u32, - self.dst_views.starts.as_ptr(), - self.dst_views.counts.as_ptr(), - self.src_views.starts.len() as u32, - self.src_views.starts.as_ptr(), - self.src_views.counts.as_ptr(), - d3d12::D3D12_DESCRIPTOR_HEAP_TYPE_CBV_SRV_UAV, - ); - } - if !self.src_samplers.starts.is_empty() { - device.CopyDescriptors( - self.dst_samplers.starts.len() as u32, - self.dst_samplers.starts.as_ptr(), - self.dst_samplers.counts.as_ptr(), - self.src_samplers.starts.len() as u32, - self.src_samplers.starts.as_ptr(), - self.src_samplers.counts.as_ptr(), - d3d12::D3D12_DESCRIPTOR_HEAP_TYPE_SAMPLER, - ); - } - } -} - -pub struct DescriptorUpdater { - heaps: Vec, - heap_index: usize, - reset_heap_index: usize, - avoid_overwrite: bool, -} - -impl DescriptorUpdater { - pub fn new(device: native::Device, avoid_overwrite: bool) -> Self { - DescriptorUpdater { - heaps: vec![Self::create_heap(device)], - heap_index: 0, - reset_heap_index: 0, - avoid_overwrite, - } - } - - pub unsafe fn destroy(&mut self) { - for heap in self.heaps.drain(..) { - heap.destroy(); - } - } - - pub fn reset(&mut self) { - if self.avoid_overwrite { - self.reset_heap_index = self.heap_index; - } else { - self.heap_index = 0; - for heap in self.heaps.iter_mut() { - heap.clear(); - } - } - } - - fn create_heap(device: native::Device) -> HeapLinear { - let size = 1 << 12; //arbitrary - HeapLinear::new(device, native::DescriptorHeapType::CbvSrvUav, size) - } - - pub fn alloc_handle(&mut self, device: native::Device) -> CpuDescriptor { - if self.heaps[self.heap_index].is_full() { - self.heap_index += 1; - if self.heap_index == self.heaps.len() { - self.heap_index = 0; - } - if self.heap_index == self.reset_heap_index { - let heap = Self::create_heap(device); - self.heaps.insert(self.heap_index, heap); - self.reset_heap_index += 1; - } else { - self.heaps[self.heap_index].clear(); - } - } - self.heaps[self.heap_index].alloc_handle() - } -} +use bit_set::BitSet; +use native::{CpuDescriptor, DescriptorHeapFlags, DescriptorHeapType}; +use std::fmt; + +// Linear stack allocator for CPU descriptor heaps. +pub struct HeapLinear { + handle_size: usize, + num: usize, + size: usize, + start: CpuDescriptor, + raw: native::DescriptorHeap, +} + +impl fmt::Debug for HeapLinear { + fn fmt(&self, fmt: &mut fmt::Formatter) -> fmt::Result { + fmt.write_str("HeapLinear") + } +} + +impl HeapLinear { + pub fn new(device: native::Device, ty: DescriptorHeapType, size: usize) -> Self { + let (heap, _hr) = + device.create_descriptor_heap(size as _, ty, DescriptorHeapFlags::empty(), 0); + + HeapLinear { + handle_size: device.get_descriptor_increment_size(ty) as _, + num: 0, + size, + start: heap.start_cpu_descriptor(), + raw: heap, + } + } + + pub fn alloc_handle(&mut self) -> CpuDescriptor { + assert!(!self.is_full()); + + let slot = self.num; + self.num += 1; + + CpuDescriptor { + ptr: self.start.ptr + self.handle_size * slot, + } + } + + pub fn is_full(&self) -> bool { + self.num >= self.size + } + + pub fn clear(&mut self) { + self.num = 0; + } + + pub unsafe fn destroy(&self) { + self.raw.destroy(); + } +} + +const HEAP_SIZE_FIXED: usize = 64; + +// Fixed-size free-list allocator for CPU descriptors. +struct Heap { + // Bit flag representation of available handles in the heap. + // + // 0 - Occupied + // 1 - free + availability: u64, + handle_size: usize, + start: CpuDescriptor, + raw: native::DescriptorHeap, +} + +impl fmt::Debug for Heap { + fn fmt(&self, fmt: &mut fmt::Formatter) -> fmt::Result { + fmt.write_str("Heap") + } +} + +impl Heap { + fn new(device: native::Device, ty: DescriptorHeapType) -> Self { + let (heap, _hr) = device.create_descriptor_heap( + HEAP_SIZE_FIXED as _, + ty, + DescriptorHeapFlags::empty(), + 0, + ); + + Heap { + handle_size: device.get_descriptor_increment_size(ty) as _, + availability: !0, // all free! + start: heap.start_cpu_descriptor(), + raw: heap, + } + } + + pub fn alloc_handle(&mut self) -> CpuDescriptor { + // Find first free slot. + let slot = self.availability.trailing_zeros() as usize; + assert!(slot < HEAP_SIZE_FIXED); + // Set the slot as occupied. + self.availability ^= 1 << slot; + + CpuDescriptor { + ptr: self.start.ptr + self.handle_size * slot, + } + } + + pub fn free_handle(&mut self, handle: CpuDescriptor) { + let slot = (handle.ptr - self.start.ptr) / self.handle_size; + assert!(slot < HEAP_SIZE_FIXED); + assert_eq!(self.availability & (1 << slot), 0); + self.availability ^= 1 << slot; + } + + pub fn is_full(&self) -> bool { + self.availability == 0 + } + + pub unsafe fn destroy(&self) { + self.raw.destroy(); + } +} + +#[derive(Clone, Copy)] +pub struct Handle { + pub raw: CpuDescriptor, + heap_index: usize, +} + +pub struct DescriptorCpuPool { + device: native::Device, + ty: DescriptorHeapType, + heaps: Vec, + avaliable_heap_indices: BitSet, +} + +impl fmt::Debug for DescriptorCpuPool { + fn fmt(&self, fmt: &mut fmt::Formatter) -> fmt::Result { + fmt.write_str("DescriptorCpuPool") + } +} + +impl DescriptorCpuPool { + pub fn new(device: native::Device, ty: DescriptorHeapType) -> Self { + DescriptorCpuPool { + device, + ty, + heaps: Vec::new(), + avaliable_heap_indices: BitSet::new(), + } + } + + pub fn alloc_handle(&mut self) -> Handle { + let heap_index = self + .avaliable_heap_indices + .iter() + .next() + .unwrap_or_else(|| { + // Allocate a new heap + let id = self.heaps.len(); + self.heaps.push(Heap::new(self.device, self.ty)); + self.avaliable_heap_indices.insert(id); + id + }); + + let heap = &mut self.heaps[heap_index]; + let handle = Handle { + raw: heap.alloc_handle(), + heap_index, + }; + if heap.is_full() { + self.avaliable_heap_indices.remove(heap_index); + } + + handle + } + + pub fn free_handle(&mut self, handle: Handle) { + self.heaps[handle.heap_index].free_handle(handle.raw); + self.avaliable_heap_indices.insert(handle.heap_index); + } + + pub unsafe fn destroy(&self) { + for heap in &self.heaps { + heap.destroy(); + } + } +} + +#[derive(Default)] +pub struct CopyAccumulator { + starts: Vec, + counts: Vec, +} + +impl CopyAccumulator { + pub fn add(&mut self, start: CpuDescriptor, count: u32) { + self.starts.push(start); + self.counts.push(count); + } + + pub fn clear(&mut self) { + self.starts.clear(); + self.counts.clear(); + } + + fn total(&self) -> u32 { + self.counts.iter().sum() + } +} + +#[derive(Default)] +pub struct MultiCopyAccumulator { + pub src_views: CopyAccumulator, + pub src_samplers: CopyAccumulator, + pub dst_views: CopyAccumulator, + pub dst_samplers: CopyAccumulator, +} + +impl MultiCopyAccumulator { + pub unsafe fn flush(&self, device: native::Device) { + use winapi::um::d3d12; + assert_eq!(self.src_views.total(), self.dst_views.total()); + assert_eq!(self.src_samplers.total(), self.dst_samplers.total()); + + if !self.src_views.starts.is_empty() { + device.CopyDescriptors( + self.dst_views.starts.len() as u32, + self.dst_views.starts.as_ptr(), + self.dst_views.counts.as_ptr(), + self.src_views.starts.len() as u32, + self.src_views.starts.as_ptr(), + self.src_views.counts.as_ptr(), + d3d12::D3D12_DESCRIPTOR_HEAP_TYPE_CBV_SRV_UAV, + ); + } + if !self.src_samplers.starts.is_empty() { + device.CopyDescriptors( + self.dst_samplers.starts.len() as u32, + self.dst_samplers.starts.as_ptr(), + self.dst_samplers.counts.as_ptr(), + self.src_samplers.starts.len() as u32, + self.src_samplers.starts.as_ptr(), + self.src_samplers.counts.as_ptr(), + d3d12::D3D12_DESCRIPTOR_HEAP_TYPE_SAMPLER, + ); + } + } +} + +pub struct DescriptorUpdater { + heaps: Vec, + heap_index: usize, + reset_heap_index: usize, + avoid_overwrite: bool, +} + +impl DescriptorUpdater { + pub fn new(device: native::Device, avoid_overwrite: bool) -> Self { + DescriptorUpdater { + heaps: vec![Self::create_heap(device)], + heap_index: 0, + reset_heap_index: 0, + avoid_overwrite, + } + } + + pub unsafe fn destroy(&mut self) { + for heap in self.heaps.drain(..) { + heap.destroy(); + } + } + + pub fn reset(&mut self) { + if self.avoid_overwrite { + self.reset_heap_index = self.heap_index; + } else { + self.heap_index = 0; + for heap in self.heaps.iter_mut() { + heap.clear(); + } + } + } + + fn create_heap(device: native::Device) -> HeapLinear { + let size = 1 << 12; //arbitrary + HeapLinear::new(device, native::DescriptorHeapType::CbvSrvUav, size) + } + + pub fn alloc_handle(&mut self, device: native::Device) -> CpuDescriptor { + if self.heaps[self.heap_index].is_full() { + self.heap_index += 1; + if self.heap_index == self.heaps.len() { + self.heap_index = 0; + } + if self.heap_index == self.reset_heap_index { + let heap = Self::create_heap(device); + self.heaps.insert(self.heap_index, heap); + self.reset_heap_index += 1; + } else { + self.heaps[self.heap_index].clear(); + } + } + self.heaps[self.heap_index].alloc_handle() + } +} diff --git a/third_party/rust/gfx-backend-dx12/src/device.rs b/third_party/rust/gfx-backend-dx12/src/device.rs index ede09ab5d706..a6fb66ff3c80 100644 --- a/third_party/rust/gfx-backend-dx12/src/device.rs +++ b/third_party/rust/gfx-backend-dx12/src/device.rs @@ -1,3898 +1,3791 @@ -use std::{collections::hash_map::Entry, ffi, iter, mem, ops::Range, ptr, slice, sync::Arc}; - -use range_alloc::RangeAllocator; -use smallvec::SmallVec; -use spirv_cross::{hlsl, spirv, ErrorCode as SpirvErrorCode}; - -use winapi::{ - shared::{ - dxgi, dxgi1_2, dxgi1_4, dxgiformat, dxgitype, - minwindef::{FALSE, TRUE, UINT}, - windef, winerror, - }, - um::{d3d12, d3dcompiler, synchapi, winbase, winnt}, - Interface, -}; - -use auxil::{spirv_cross_specialize_ast, ShaderStage}; -use hal::{ - buffer, device as d, display, format, format::Aspects, image, memory, memory::Requirements, - pass, pool::CommandPoolCreateFlags, pso, pso::VertexInputRate, query, queue::QueueFamilyId, - window as w, -}; - -use crate::{ - command as cmd, conv, descriptors_cpu, pool::CommandPool, resource as r, root_constants, - root_constants::RootConstant, window::Swapchain, Backend as B, Device, MemoryGroup, - MAX_VERTEX_BUFFERS, NUM_HEAP_PROPERTIES, QUEUE_FAMILIES, -}; -use native::{PipelineStateSubobject, Subobject}; - -// Register space used for root constants. -const ROOT_CONSTANT_SPACE: u32 = 0; - -const MEM_TYPE_MASK: u32 = 0x7; -const MEM_TYPE_SHIFT: u32 = 3; - -const MEM_TYPE_UNIVERSAL_SHIFT: u32 = MEM_TYPE_SHIFT * MemoryGroup::Universal as u32; -const MEM_TYPE_BUFFER_SHIFT: u32 = MEM_TYPE_SHIFT * MemoryGroup::BufferOnly as u32; -const MEM_TYPE_IMAGE_SHIFT: u32 = MEM_TYPE_SHIFT * MemoryGroup::ImageOnly as u32; -const MEM_TYPE_TARGET_SHIFT: u32 = MEM_TYPE_SHIFT * MemoryGroup::TargetOnly as u32; - -pub const IDENTITY_MAPPING: UINT = 0x1688; // D3D12_DEFAULT_SHADER_4_COMPONENT_MAPPING - -fn wide_cstr(name: &str) -> Vec { - name.encode_utf16().chain(iter::once(0)).collect() -} - -/// Emit error during shader module creation. Used if we don't expect an error -/// but might panic due to an exception in SPIRV-Cross. -fn gen_unexpected_error(stage: ShaderStage, err: SpirvErrorCode) -> pso::CreationError { - let msg = match err { - SpirvErrorCode::CompilationError(msg) => msg, - SpirvErrorCode::Unhandled => "Unexpected error".into(), - }; - let error = format!("SPIR-V unexpected error {:?}", msg); - pso::CreationError::ShaderCreationError(stage.to_flag(), error) -} - -/// Emit error during shader module creation. Used if we execute an query command. -fn gen_query_error(stage: ShaderStage, err: SpirvErrorCode) -> pso::CreationError { - let msg = match err { - SpirvErrorCode::CompilationError(msg) => msg, - SpirvErrorCode::Unhandled => "Unknown query error".into(), - }; - let error = format!("SPIR-V query error {:?}", msg); - pso::CreationError::ShaderCreationError(stage.to_flag(), error) -} - -#[derive(Clone, Debug)] -pub(crate) struct ViewInfo { - pub(crate) resource: native::Resource, - pub(crate) kind: image::Kind, - pub(crate) caps: image::ViewCapabilities, - pub(crate) view_kind: image::ViewKind, - pub(crate) format: dxgiformat::DXGI_FORMAT, - pub(crate) component_mapping: UINT, - pub(crate) levels: Range, - pub(crate) layers: Range, -} - -pub(crate) enum CommandSignature { - Draw, - DrawIndexed, - Dispatch, -} - -/// Compile a single shader entry point from a HLSL text shader -pub(crate) fn compile_shader( - stage: ShaderStage, - shader_model: hlsl::ShaderModel, - features: &hal::Features, - entry: &str, - code: &[u8], -) -> Result { - let stage_str = match stage { - ShaderStage::Vertex => "vs", - ShaderStage::Fragment => "ps", - ShaderStage::Compute => "cs", - _ => unimplemented!(), - }; - let model_str = match shader_model { - hlsl::ShaderModel::V5_0 => "5_0", - hlsl::ShaderModel::V5_1 => "5_1", - hlsl::ShaderModel::V6_0 => "6_0", - _ => unimplemented!(), - }; - let full_stage = format!("{}_{}\0", stage_str, model_str); - - let mut shader_data = native::Blob::null(); - let mut error = native::Blob::null(); - let entry = ffi::CString::new(entry).unwrap(); - let mut compile_flags = d3dcompiler::D3DCOMPILE_ENABLE_STRICTNESS; - if cfg!(debug_assertions) { - compile_flags |= d3dcompiler::D3DCOMPILE_DEBUG; - } - if features.contains(hal::Features::UNSIZED_DESCRIPTOR_ARRAY) { - compile_flags |= d3dcompiler::D3DCOMPILE_ENABLE_UNBOUNDED_DESCRIPTOR_TABLES; - } - let hr = unsafe { - d3dcompiler::D3DCompile( - code.as_ptr() as *const _, - code.len(), - ptr::null(), - ptr::null(), - ptr::null_mut(), - entry.as_ptr() as *const _, - full_stage.as_ptr() as *const i8, - compile_flags, - 0, - shader_data.mut_void() as *mut *mut _, - error.mut_void() as *mut *mut _, - ) - }; - if !winerror::SUCCEEDED(hr) { - let message = unsafe { - let pointer = error.GetBufferPointer(); - let size = error.GetBufferSize(); - let slice = slice::from_raw_parts(pointer as *const u8, size as usize); - String::from_utf8_lossy(slice).into_owned() - }; - unsafe { - error.destroy(); - } - let error = format!("D3DCompile error {:x}: {}", hr, message); - Err(pso::CreationError::ShaderCreationError( - stage.to_flag(), - error, - )) - } else { - Ok(shader_data) - } -} - -#[repr(C)] -struct GraphicsPipelineStateSubobjectStream { - root_signature: PipelineStateSubobject<*mut d3d12::ID3D12RootSignature>, - vs: PipelineStateSubobject, - ps: PipelineStateSubobject, - ds: PipelineStateSubobject, - hs: PipelineStateSubobject, - gs: PipelineStateSubobject, - stream_output: PipelineStateSubobject, - blend: PipelineStateSubobject, - sample_mask: PipelineStateSubobject, - rasterizer: PipelineStateSubobject, - depth_stencil: PipelineStateSubobject, - input_layout: PipelineStateSubobject, - ib_strip_cut_value: PipelineStateSubobject, - primitive_topology: PipelineStateSubobject, - render_target_formats: PipelineStateSubobject, - depth_stencil_format: PipelineStateSubobject, - sample_desc: PipelineStateSubobject, - node_mask: PipelineStateSubobject, - cached_pso: PipelineStateSubobject, - flags: PipelineStateSubobject, -} - -impl GraphicsPipelineStateSubobjectStream { - fn new( - pso_desc: &d3d12::D3D12_GRAPHICS_PIPELINE_STATE_DESC, - depth_bounds_test_enable: bool, - ) -> Self { - GraphicsPipelineStateSubobjectStream { - root_signature: PipelineStateSubobject::new( - Subobject::RootSignature, - pso_desc.pRootSignature, - ), - vs: PipelineStateSubobject::new(Subobject::VS, pso_desc.VS), - ps: PipelineStateSubobject::new(Subobject::PS, pso_desc.PS), - ds: PipelineStateSubobject::new(Subobject::DS, pso_desc.DS), - hs: PipelineStateSubobject::new(Subobject::HS, pso_desc.HS), - gs: PipelineStateSubobject::new(Subobject::GS, pso_desc.GS), - stream_output: PipelineStateSubobject::new( - Subobject::StreamOutput, - pso_desc.StreamOutput, - ), - blend: PipelineStateSubobject::new(Subobject::Blend, pso_desc.BlendState), - sample_mask: PipelineStateSubobject::new(Subobject::SampleMask, pso_desc.SampleMask), - rasterizer: PipelineStateSubobject::new( - Subobject::Rasterizer, - pso_desc.RasterizerState, - ), - depth_stencil: PipelineStateSubobject::new( - Subobject::DepthStencil1, - d3d12::D3D12_DEPTH_STENCIL_DESC1 { - DepthEnable: pso_desc.DepthStencilState.DepthEnable, - DepthWriteMask: pso_desc.DepthStencilState.DepthWriteMask, - DepthFunc: pso_desc.DepthStencilState.DepthFunc, - StencilEnable: pso_desc.DepthStencilState.StencilEnable, - StencilReadMask: pso_desc.DepthStencilState.StencilReadMask, - StencilWriteMask: pso_desc.DepthStencilState.StencilWriteMask, - FrontFace: pso_desc.DepthStencilState.FrontFace, - BackFace: pso_desc.DepthStencilState.BackFace, - DepthBoundsTestEnable: depth_bounds_test_enable as _, - }, - ), - input_layout: PipelineStateSubobject::new(Subobject::InputLayout, pso_desc.InputLayout), - ib_strip_cut_value: PipelineStateSubobject::new( - Subobject::IBStripCut, - pso_desc.IBStripCutValue, - ), - primitive_topology: PipelineStateSubobject::new( - Subobject::PrimitiveTopology, - pso_desc.PrimitiveTopologyType, - ), - render_target_formats: PipelineStateSubobject::new( - Subobject::RTFormats, - d3d12::D3D12_RT_FORMAT_ARRAY { - RTFormats: pso_desc.RTVFormats, - NumRenderTargets: pso_desc.NumRenderTargets, - }, - ), - depth_stencil_format: PipelineStateSubobject::new( - Subobject::DSFormat, - pso_desc.DSVFormat, - ), - sample_desc: PipelineStateSubobject::new(Subobject::SampleDesc, pso_desc.SampleDesc), - node_mask: PipelineStateSubobject::new(Subobject::NodeMask, pso_desc.NodeMask), - cached_pso: PipelineStateSubobject::new(Subobject::CachedPSO, pso_desc.CachedPSO), - flags: PipelineStateSubobject::new(Subobject::Flags, pso_desc.Flags), - } - } -} - -impl Device { - fn parse_spirv( - stage: ShaderStage, - raw_data: &[u32], - ) -> Result, pso::CreationError> { - let module = spirv::Module::from_words(raw_data); - - spirv::Ast::parse(&module).map_err(|err| { - let msg = match err { - SpirvErrorCode::CompilationError(msg) => msg, - SpirvErrorCode::Unhandled => "Unknown parsing error".into(), - }; - let error = format!("SPIR-V parsing failed: {:?}", msg); - pso::CreationError::ShaderCreationError(stage.to_flag(), error) - }) - } - - /// Introspects the input attributes of given SPIR-V shader and returns an optional vertex semantic remapping. - /// - /// The returned hashmap has attribute location as a key and an Optional remapping to a two part semantic. - /// - /// eg. - /// `2 -> None` means use default semantic `TEXCOORD2` - /// `2 -> Some((0, 2))` means use two part semantic `TEXCOORD0_2`. This is how matrices are represented by spirv-cross. - /// - /// This is a temporary workaround for https://github.com/KhronosGroup/SPIRV-Cross/issues/1512. - /// - /// This workaround also exists under the same name in the DX11 backend. - pub(crate) fn introspect_spirv_vertex_semantic_remapping( - raw_data: &[u32], - ) -> Result>, pso::CreationError> { - const SHADER_STAGE: ShaderStage = ShaderStage::Vertex; - // This is inefficient as we already parse it once before. This is a temporary workaround only called - // on vertex shaders. If this becomes permanent or shows up in profiles, deduplicate these as first course of action. - let ast = Self::parse_spirv(SHADER_STAGE, raw_data)?; - - let mut map = auxil::FastHashMap::default(); - - let inputs = ast - .get_shader_resources() - .map_err(|err| gen_query_error(SHADER_STAGE, err))? - .stage_inputs; - for input in inputs { - let idx = ast - .get_decoration(input.id, spirv::Decoration::Location) - .map_err(|err| gen_query_error(SHADER_STAGE, err))?; - - let ty = ast - .get_type(input.type_id) - .map_err(|err| gen_query_error(SHADER_STAGE, err))?; - - match ty { - spirv::Type::Boolean { columns, .. } - | spirv::Type::Int { columns, .. } - | spirv::Type::UInt { columns, .. } - | spirv::Type::Half { columns, .. } - | spirv::Type::Float { columns, .. } - | spirv::Type::Double { columns, .. } - if columns > 1 => - { - for col in 0..columns { - if let Some(_) = map.insert(idx + col, Some((idx, col))) { - let error = format!( - "Shader has overlapping input attachments at location {}", - idx - ); - return Err(pso::CreationError::ShaderCreationError( - SHADER_STAGE.to_flag(), - error, - )); - } - } - } - _ => { - if let Some(_) = map.insert(idx, None) { - let error = format!( - "Shader has overlapping input attachments at location {}", - idx - ); - return Err(pso::CreationError::ShaderCreationError( - SHADER_STAGE.to_flag(), - error, - )); - } - } - } - } - - Ok(map) - } - - fn patch_spirv_resources( - stage: ShaderStage, - ast: &mut spirv::Ast, - layout: &r::PipelineLayout, - ) -> Result<(), pso::CreationError> { - // Move the descriptor sets away to yield for the root constants at "space0". - let space_offset = if layout.shared.constants.is_empty() { - 0 - } else { - 1 - }; - let shader_resources = ast - .get_shader_resources() - .map_err(|err| gen_query_error(stage, err))?; - - if space_offset != 0 { - for image in &shader_resources.separate_images { - let set = ast - .get_decoration(image.id, spirv::Decoration::DescriptorSet) - .map_err(|err| gen_query_error(stage, err))?; - ast.set_decoration( - image.id, - spirv::Decoration::DescriptorSet, - space_offset + set, - ) - .map_err(|err| gen_unexpected_error(stage, err))?; - } - } - - if space_offset != 0 { - for uniform_buffer in &shader_resources.uniform_buffers { - let set = ast - .get_decoration(uniform_buffer.id, spirv::Decoration::DescriptorSet) - .map_err(|err| gen_query_error(stage, err))?; - ast.set_decoration( - uniform_buffer.id, - spirv::Decoration::DescriptorSet, - space_offset + set, - ) - .map_err(|err| gen_unexpected_error(stage, err))?; - } - } - - for storage_buffer in &shader_resources.storage_buffers { - let set = ast - .get_decoration(storage_buffer.id, spirv::Decoration::DescriptorSet) - .map_err(|err| gen_query_error(stage, err))?; - let binding = ast - .get_decoration(storage_buffer.id, spirv::Decoration::Binding) - .map_err(|err| gen_query_error(stage, err))?; - if space_offset != 0 { - ast.set_decoration( - storage_buffer.id, - spirv::Decoration::DescriptorSet, - space_offset + set, - ) - .map_err(|err| gen_unexpected_error(stage, err))?; - } - if !layout.elements[set as usize] - .mutable_bindings - .contains(&binding) - { - ast.set_decoration(storage_buffer.id, spirv::Decoration::NonWritable, 1) - .map_err(|err| gen_unexpected_error(stage, err))? - } - } - - for image in &shader_resources.storage_images { - let set = ast - .get_decoration(image.id, spirv::Decoration::DescriptorSet) - .map_err(|err| gen_query_error(stage, err))?; - let binding = ast - .get_decoration(image.id, spirv::Decoration::Binding) - .map_err(|err| gen_query_error(stage, err))?; - if space_offset != 0 { - ast.set_decoration( - image.id, - spirv::Decoration::DescriptorSet, - space_offset + set, - ) - .map_err(|err| gen_unexpected_error(stage, err))?; - } - if !layout.elements[set as usize] - .mutable_bindings - .contains(&binding) - { - ast.set_decoration(image.id, spirv::Decoration::NonWritable, 1) - .map_err(|err| gen_unexpected_error(stage, err))? - } - } - - if space_offset != 0 { - for sampler in &shader_resources.separate_samplers { - let set = ast - .get_decoration(sampler.id, spirv::Decoration::DescriptorSet) - .map_err(|err| gen_query_error(stage, err))?; - ast.set_decoration( - sampler.id, - spirv::Decoration::DescriptorSet, - space_offset + set, - ) - .map_err(|err| gen_unexpected_error(stage, err))?; - } - } - - if space_offset != 0 { - for image in &shader_resources.sampled_images { - let set = ast - .get_decoration(image.id, spirv::Decoration::DescriptorSet) - .map_err(|err| gen_query_error(stage, err))?; - ast.set_decoration( - image.id, - spirv::Decoration::DescriptorSet, - space_offset + set, - ) - .map_err(|err| gen_unexpected_error(stage, err))?; - } - } - - if space_offset != 0 { - for input in &shader_resources.subpass_inputs { - let set = ast - .get_decoration(input.id, spirv::Decoration::DescriptorSet) - .map_err(|err| gen_query_error(stage, err))?; - ast.set_decoration( - input.id, - spirv::Decoration::DescriptorSet, - space_offset + set, - ) - .map_err(|err| gen_unexpected_error(stage, err))?; - } - } - - Ok(()) - } - - fn translate_spirv( - ast: &mut spirv::Ast, - shader_model: hlsl::ShaderModel, - layout: &r::PipelineLayout, - stage: ShaderStage, - features: &hal::Features, - entry_point: &str, - ) -> Result { - let mut compile_options = hlsl::CompilerOptions::default(); - compile_options.shader_model = shader_model; - compile_options.vertex.invert_y = !features.contains(hal::Features::NDC_Y_UP); - compile_options.force_zero_initialized_variables = true; - compile_options.nonwritable_uav_texture_as_srv = true; - // these are technically incorrect, but better than panicking - compile_options.point_size_compat = true; - compile_options.point_coord_compat = true; - compile_options.entry_point = Some((entry_point.to_string(), conv::map_stage(stage))); - - let stage_flag = stage.to_flag(); - let root_constant_layout = layout - .shared - .constants - .iter() - .filter_map(|constant| { - if constant.stages.contains(stage_flag) { - Some(hlsl::RootConstant { - start: constant.range.start * 4, - end: constant.range.end * 4, - binding: constant.range.start, - space: 0, - }) - } else { - None - } - }) - .collect(); - ast.set_compiler_options(&compile_options) - .map_err(|err| gen_unexpected_error(stage, err))?; - ast.set_root_constant_layout(root_constant_layout) - .map_err(|err| gen_unexpected_error(stage, err))?; - ast.compile().map_err(|err| { - let msg = match err { - SpirvErrorCode::CompilationError(msg) => msg, - SpirvErrorCode::Unhandled => "Unknown compile error".into(), - }; - let error = format!("SPIR-V compile failed: {}", msg); - pso::CreationError::ShaderCreationError(stage.to_flag(), error) - }) - } - - // Extract entry point from shader module on pipeline creation. - // Returns compiled shader blob and bool to indicate if the shader should be - // destroyed after pipeline creation - fn extract_entry_point( - stage: ShaderStage, - source: &pso::EntryPoint, - layout: &r::PipelineLayout, - features: &hal::Features, - ) -> Result<(native::Blob, bool), pso::CreationError> { - match *source.module { - r::ShaderModule::Compiled(ref shaders) => { - // TODO: do we need to check for specialization constants? - // Use precompiled shader, ignore specialization or layout. - shaders - .get(source.entry) - .map(|src| (*src, false)) - .ok_or(pso::CreationError::MissingEntryPoint(source.entry.into())) - } - r::ShaderModule::Spirv(ref raw_data) => { - let mut ast = Self::parse_spirv(stage, raw_data)?; - spirv_cross_specialize_ast(&mut ast, &source.specialization) - .map_err(pso::CreationError::InvalidSpecialization)?; - Self::patch_spirv_resources(stage, &mut ast, layout)?; - - let execution_model = conv::map_stage(stage); - let shader_model = hlsl::ShaderModel::V5_1; - let shader_code = Self::translate_spirv( - &mut ast, - shader_model, - layout, - stage, - features, - source.entry, - )?; - debug!("SPIRV-Cross generated shader:\n{}", shader_code); - - let real_name = ast - .get_cleansed_entry_point_name(source.entry, execution_model) - .map_err(|err| gen_query_error(stage, err))?; - - let shader = compile_shader( - stage, - shader_model, - features, - &real_name, - shader_code.as_bytes(), - )?; - Ok((shader, true)) - } - } - } - - pub(crate) fn create_command_signature( - device: native::Device, - ty: CommandSignature, - ) -> native::CommandSignature { - let (arg, stride) = match ty { - CommandSignature::Draw => (native::IndirectArgument::draw(), 16), - CommandSignature::DrawIndexed => (native::IndirectArgument::draw_indexed(), 20), - CommandSignature::Dispatch => (native::IndirectArgument::dispatch(), 12), - }; - - let (signature, hr) = - device.create_command_signature(native::RootSignature::null(), &[arg], stride, 0); - - if !winerror::SUCCEEDED(hr) { - error!("error on command signature creation: {:x}", hr); - } - signature - } - - pub(crate) fn create_descriptor_heap_impl( - device: native::Device, - heap_type: native::DescriptorHeapType, - shader_visible: bool, - capacity: usize, - ) -> r::DescriptorHeap { - assert_ne!(capacity, 0); - - let (heap, _hr) = device.create_descriptor_heap( - capacity as _, - heap_type, - if shader_visible { - native::DescriptorHeapFlags::SHADER_VISIBLE - } else { - native::DescriptorHeapFlags::empty() - }, - 0, - ); - - let descriptor_size = device.get_descriptor_increment_size(heap_type); - let cpu_handle = heap.start_cpu_descriptor(); - let gpu_handle = heap.start_gpu_descriptor(); - - r::DescriptorHeap { - raw: heap, - handle_size: descriptor_size as _, - total_handles: capacity as _, - start: r::DualHandle { - cpu: cpu_handle, - gpu: gpu_handle, - size: 0, - }, - } - } - - pub(crate) fn view_image_as_render_target_impl( - device: native::Device, - handle: d3d12::D3D12_CPU_DESCRIPTOR_HANDLE, - info: &ViewInfo, - ) -> Result<(), image::ViewCreationError> { - #![allow(non_snake_case)] - - let mut desc = d3d12::D3D12_RENDER_TARGET_VIEW_DESC { - Format: info.format, - ViewDimension: 0, - u: unsafe { mem::zeroed() }, - }; - - let MipSlice = info.levels.start as _; - let FirstArraySlice = info.layers.start as _; - let ArraySize = (info.layers.end - info.layers.start) as _; - let is_msaa = info.kind.num_samples() > 1; - if info.levels.start + 1 != info.levels.end { - return Err(image::ViewCreationError::Level(info.levels.start)); - } - if info.layers.end > info.kind.num_layers() { - return Err(image::ViewCreationError::Layer( - image::LayerError::OutOfBounds, - )); - } - - match info.view_kind { - image::ViewKind::D1 => { - assert_eq!(info.layers, 0..1); - desc.ViewDimension = d3d12::D3D12_RTV_DIMENSION_TEXTURE1D; - *unsafe { desc.u.Texture1D_mut() } = d3d12::D3D12_TEX1D_RTV { MipSlice } - } - image::ViewKind::D1Array => { - desc.ViewDimension = d3d12::D3D12_RTV_DIMENSION_TEXTURE1DARRAY; - *unsafe { desc.u.Texture1DArray_mut() } = d3d12::D3D12_TEX1D_ARRAY_RTV { - MipSlice, - FirstArraySlice, - ArraySize, - } - } - image::ViewKind::D2 if is_msaa => { - assert_eq!(info.layers, 0..1); - desc.ViewDimension = d3d12::D3D12_RTV_DIMENSION_TEXTURE2DMS; - *unsafe { desc.u.Texture2DMS_mut() } = d3d12::D3D12_TEX2DMS_RTV { - UnusedField_NothingToDefine: 0, - } - } - image::ViewKind::D2 => { - assert_eq!(info.layers, 0..1); - desc.ViewDimension = d3d12::D3D12_RTV_DIMENSION_TEXTURE2D; - *unsafe { desc.u.Texture2D_mut() } = d3d12::D3D12_TEX2D_RTV { - MipSlice, - PlaneSlice: 0, //TODO - } - } - image::ViewKind::D2Array if is_msaa => { - desc.ViewDimension = d3d12::D3D12_RTV_DIMENSION_TEXTURE2DMSARRAY; - *unsafe { desc.u.Texture2DMSArray_mut() } = d3d12::D3D12_TEX2DMS_ARRAY_RTV { - FirstArraySlice, - ArraySize, - } - } - image::ViewKind::D2Array => { - desc.ViewDimension = d3d12::D3D12_RTV_DIMENSION_TEXTURE2DARRAY; - *unsafe { desc.u.Texture2DArray_mut() } = d3d12::D3D12_TEX2D_ARRAY_RTV { - MipSlice, - FirstArraySlice, - ArraySize, - PlaneSlice: 0, //TODO - } - } - image::ViewKind::D3 => { - assert_eq!(info.layers, 0..1); - desc.ViewDimension = d3d12::D3D12_RTV_DIMENSION_TEXTURE3D; - *unsafe { desc.u.Texture3D_mut() } = d3d12::D3D12_TEX3D_RTV { - MipSlice, - FirstWSlice: 0, - WSize: info.kind.extent().depth as _, - } - } - image::ViewKind::Cube | image::ViewKind::CubeArray => { - desc.ViewDimension = d3d12::D3D12_RTV_DIMENSION_TEXTURE2DARRAY; - //TODO: double-check if any *6 are needed - *unsafe { desc.u.Texture2DArray_mut() } = d3d12::D3D12_TEX2D_ARRAY_RTV { - MipSlice, - FirstArraySlice, - ArraySize, - PlaneSlice: 0, //TODO - } - } - }; - - unsafe { - device.CreateRenderTargetView(info.resource.as_mut_ptr(), &desc, handle); - } - - Ok(()) - } - - pub(crate) fn view_image_as_render_target( - &self, - info: &ViewInfo, - ) -> Result { - let handle = self.rtv_pool.lock().alloc_handle(); - Self::view_image_as_render_target_impl(self.raw, handle.raw, info).map(|_| handle) - } - - pub(crate) fn view_image_as_depth_stencil_impl( - device: native::Device, - handle: d3d12::D3D12_CPU_DESCRIPTOR_HANDLE, - info: &ViewInfo, - ) -> Result<(), image::ViewCreationError> { - #![allow(non_snake_case)] - - let mut desc = d3d12::D3D12_DEPTH_STENCIL_VIEW_DESC { - Format: info.format, - ViewDimension: 0, - Flags: 0, - u: unsafe { mem::zeroed() }, - }; - - let MipSlice = info.levels.start as _; - let FirstArraySlice = info.layers.start as _; - let ArraySize = (info.layers.end - info.layers.start) as _; - let is_msaa = info.kind.num_samples() > 1; - if info.levels.start + 1 != info.levels.end { - return Err(image::ViewCreationError::Level(info.levels.start)); - } - if info.layers.end > info.kind.num_layers() { - return Err(image::ViewCreationError::Layer( - image::LayerError::OutOfBounds, - )); - } - - match info.view_kind { - image::ViewKind::D1 => { - assert_eq!(info.layers, 0..1); - desc.ViewDimension = d3d12::D3D12_DSV_DIMENSION_TEXTURE1D; - *unsafe { desc.u.Texture1D_mut() } = d3d12::D3D12_TEX1D_DSV { MipSlice } - } - image::ViewKind::D1Array => { - desc.ViewDimension = d3d12::D3D12_DSV_DIMENSION_TEXTURE1DARRAY; - *unsafe { desc.u.Texture1DArray_mut() } = d3d12::D3D12_TEX1D_ARRAY_DSV { - MipSlice, - FirstArraySlice, - ArraySize, - } - } - image::ViewKind::D2 if is_msaa => { - assert_eq!(info.layers, 0..1); - desc.ViewDimension = d3d12::D3D12_DSV_DIMENSION_TEXTURE2DMS; - *unsafe { desc.u.Texture2DMS_mut() } = d3d12::D3D12_TEX2DMS_DSV { - UnusedField_NothingToDefine: 0, - } - } - image::ViewKind::D2 => { - assert_eq!(info.layers, 0..1); - desc.ViewDimension = d3d12::D3D12_DSV_DIMENSION_TEXTURE2D; - *unsafe { desc.u.Texture2D_mut() } = d3d12::D3D12_TEX2D_DSV { MipSlice } - } - image::ViewKind::D2Array if is_msaa => { - desc.ViewDimension = d3d12::D3D12_DSV_DIMENSION_TEXTURE2DMSARRAY; - *unsafe { desc.u.Texture2DMSArray_mut() } = d3d12::D3D12_TEX2DMS_ARRAY_DSV { - FirstArraySlice, - ArraySize, - } - } - image::ViewKind::D2Array => { - desc.ViewDimension = d3d12::D3D12_DSV_DIMENSION_TEXTURE2DARRAY; - *unsafe { desc.u.Texture2DArray_mut() } = d3d12::D3D12_TEX2D_ARRAY_DSV { - MipSlice, - FirstArraySlice, - ArraySize, - } - } - image::ViewKind::D3 | image::ViewKind::Cube | image::ViewKind::CubeArray => { - warn!( - "3D and cube views are not supported for the image, kind: {:?}", - info.kind - ); - return Err(image::ViewCreationError::BadKind(info.view_kind)); - } - }; - - unsafe { - device.CreateDepthStencilView(info.resource.as_mut_ptr(), &desc, handle); - } - - Ok(()) - } - - pub(crate) fn view_image_as_depth_stencil( - &self, - info: &ViewInfo, - ) -> Result { - let handle = self.dsv_pool.lock().alloc_handle(); - Self::view_image_as_depth_stencil_impl(self.raw, handle.raw, info).map(|_| handle) - } - - pub(crate) fn build_image_as_shader_resource_desc( - info: &ViewInfo, - ) -> Result { - #![allow(non_snake_case)] - - let mut desc = d3d12::D3D12_SHADER_RESOURCE_VIEW_DESC { - Format: info.format, - ViewDimension: 0, - Shader4ComponentMapping: info.component_mapping, - u: unsafe { mem::zeroed() }, - }; - - let MostDetailedMip = info.levels.start as _; - let MipLevels = (info.levels.end - info.levels.start) as _; - let FirstArraySlice = info.layers.start as _; - let ArraySize = (info.layers.end - info.layers.start) as _; - - if info.layers.end > info.kind.num_layers() { - return Err(image::ViewCreationError::Layer( - image::LayerError::OutOfBounds, - )); - } - let is_msaa = info.kind.num_samples() > 1; - let is_cube = info.caps.contains(image::ViewCapabilities::KIND_CUBE); - - match info.view_kind { - image::ViewKind::D1 => { - assert_eq!(info.layers, 0..1); - desc.ViewDimension = d3d12::D3D12_SRV_DIMENSION_TEXTURE1D; - *unsafe { desc.u.Texture1D_mut() } = d3d12::D3D12_TEX1D_SRV { - MostDetailedMip, - MipLevels, - ResourceMinLODClamp: 0.0, - } - } - image::ViewKind::D1Array => { - desc.ViewDimension = d3d12::D3D12_SRV_DIMENSION_TEXTURE1DARRAY; - *unsafe { desc.u.Texture1DArray_mut() } = d3d12::D3D12_TEX1D_ARRAY_SRV { - MostDetailedMip, - MipLevels, - FirstArraySlice, - ArraySize, - ResourceMinLODClamp: 0.0, - } - } - image::ViewKind::D2 if is_msaa => { - assert_eq!(info.layers, 0..1); - desc.ViewDimension = d3d12::D3D12_SRV_DIMENSION_TEXTURE2DMS; - *unsafe { desc.u.Texture2DMS_mut() } = d3d12::D3D12_TEX2DMS_SRV { - UnusedField_NothingToDefine: 0, - } - } - image::ViewKind::D2 => { - assert_eq!(info.layers, 0..1); - desc.ViewDimension = d3d12::D3D12_SRV_DIMENSION_TEXTURE2D; - *unsafe { desc.u.Texture2D_mut() } = d3d12::D3D12_TEX2D_SRV { - MostDetailedMip, - MipLevels, - PlaneSlice: 0, //TODO - ResourceMinLODClamp: 0.0, - } - } - image::ViewKind::D2Array if is_msaa => { - desc.ViewDimension = d3d12::D3D12_SRV_DIMENSION_TEXTURE2DMSARRAY; - *unsafe { desc.u.Texture2DMSArray_mut() } = d3d12::D3D12_TEX2DMS_ARRAY_SRV { - FirstArraySlice, - ArraySize, - } - } - image::ViewKind::D2Array => { - desc.ViewDimension = d3d12::D3D12_SRV_DIMENSION_TEXTURE2DARRAY; - *unsafe { desc.u.Texture2DArray_mut() } = d3d12::D3D12_TEX2D_ARRAY_SRV { - MostDetailedMip, - MipLevels, - FirstArraySlice, - ArraySize, - PlaneSlice: 0, //TODO - ResourceMinLODClamp: 0.0, - } - } - image::ViewKind::D3 => { - assert_eq!(info.layers, 0..1); - desc.ViewDimension = d3d12::D3D12_SRV_DIMENSION_TEXTURE3D; - *unsafe { desc.u.Texture3D_mut() } = d3d12::D3D12_TEX3D_SRV { - MostDetailedMip, - MipLevels, - ResourceMinLODClamp: 0.0, - } - } - image::ViewKind::Cube if is_cube => { - desc.ViewDimension = d3d12::D3D12_SRV_DIMENSION_TEXTURECUBE; - *unsafe { desc.u.TextureCube_mut() } = d3d12::D3D12_TEXCUBE_SRV { - MostDetailedMip, - MipLevels, - ResourceMinLODClamp: 0.0, - } - } - image::ViewKind::CubeArray if is_cube => { - assert_eq!(0, ArraySize % 6); - desc.ViewDimension = d3d12::D3D12_SRV_DIMENSION_TEXTURECUBEARRAY; - *unsafe { desc.u.TextureCubeArray_mut() } = d3d12::D3D12_TEXCUBE_ARRAY_SRV { - MostDetailedMip, - MipLevels, - First2DArrayFace: FirstArraySlice, - NumCubes: ArraySize / 6, - ResourceMinLODClamp: 0.0, - } - } - image::ViewKind::Cube | image::ViewKind::CubeArray => { - warn!( - "Cube views are not supported for the image, kind: {:?}", - info.kind - ); - return Err(image::ViewCreationError::BadKind(info.view_kind)); - } - } - - Ok(desc) - } - - fn view_image_as_shader_resource( - &self, - info: &ViewInfo, - ) -> Result { - let desc = Self::build_image_as_shader_resource_desc(&info)?; - let handle = self.srv_uav_pool.lock().alloc_handle(); - unsafe { - self.raw - .CreateShaderResourceView(info.resource.as_mut_ptr(), &desc, handle.raw); - } - - Ok(handle) - } - - fn view_image_as_storage( - &self, - info: &ViewInfo, - ) -> Result { - #![allow(non_snake_case)] - - // Cannot make a storage image over multiple mips - if info.levels.start + 1 != info.levels.end { - return Err(image::ViewCreationError::Unsupported); - } - - let mut desc = d3d12::D3D12_UNORDERED_ACCESS_VIEW_DESC { - Format: info.format, - ViewDimension: 0, - u: unsafe { mem::zeroed() }, - }; - - let MipSlice = info.levels.start as _; - let FirstArraySlice = info.layers.start as _; - let ArraySize = (info.layers.end - info.layers.start) as _; - - if info.layers.end > info.kind.num_layers() { - return Err(image::ViewCreationError::Layer( - image::LayerError::OutOfBounds, - )); - } - if info.kind.num_samples() > 1 { - error!("MSAA images can't be viewed as UAV"); - return Err(image::ViewCreationError::Unsupported); - } - - match info.view_kind { - image::ViewKind::D1 => { - assert_eq!(info.layers, 0..1); - desc.ViewDimension = d3d12::D3D12_UAV_DIMENSION_TEXTURE1D; - *unsafe { desc.u.Texture1D_mut() } = d3d12::D3D12_TEX1D_UAV { MipSlice } - } - image::ViewKind::D1Array => { - desc.ViewDimension = d3d12::D3D12_UAV_DIMENSION_TEXTURE1DARRAY; - *unsafe { desc.u.Texture1DArray_mut() } = d3d12::D3D12_TEX1D_ARRAY_UAV { - MipSlice, - FirstArraySlice, - ArraySize, - } - } - image::ViewKind::D2 => { - assert_eq!(info.layers, 0..1); - desc.ViewDimension = d3d12::D3D12_UAV_DIMENSION_TEXTURE2D; - *unsafe { desc.u.Texture2D_mut() } = d3d12::D3D12_TEX2D_UAV { - MipSlice, - PlaneSlice: 0, //TODO - } - } - image::ViewKind::D2Array => { - desc.ViewDimension = d3d12::D3D12_UAV_DIMENSION_TEXTURE2DARRAY; - *unsafe { desc.u.Texture2DArray_mut() } = d3d12::D3D12_TEX2D_ARRAY_UAV { - MipSlice, - FirstArraySlice, - ArraySize, - PlaneSlice: 0, //TODO - } - } - image::ViewKind::D3 => { - assert_eq!(info.layers, 0..1); - desc.ViewDimension = d3d12::D3D12_UAV_DIMENSION_TEXTURE3D; - *unsafe { desc.u.Texture3D_mut() } = d3d12::D3D12_TEX3D_UAV { - MipSlice, - FirstWSlice: 0, - WSize: info.kind.extent().depth as _, - } - } - image::ViewKind::Cube | image::ViewKind::CubeArray => { - error!("Cubic images can't be viewed as UAV"); - return Err(image::ViewCreationError::Unsupported); - } - } - - let handle = self.srv_uav_pool.lock().alloc_handle(); - unsafe { - self.raw.CreateUnorderedAccessView( - info.resource.as_mut_ptr(), - ptr::null_mut(), - &desc, - handle.raw, - ); - } - - Ok(handle) - } - - pub(crate) fn create_raw_fence(&self, signalled: bool) -> native::Fence { - let mut handle = native::Fence::null(); - assert_eq!(winerror::S_OK, unsafe { - self.raw.CreateFence( - if signalled { 1 } else { 0 }, - d3d12::D3D12_FENCE_FLAG_NONE, - &d3d12::ID3D12Fence::uuidof(), - handle.mut_void(), - ) - }); - handle - } - - pub(crate) fn create_swapchain_impl( - &self, - config: &w::SwapchainConfig, - window_handle: windef::HWND, - factory: native::WeakPtr, - ) -> Result< - ( - native::WeakPtr, - dxgiformat::DXGI_FORMAT, - ), - w::SwapchainError, - > { - let mut swap_chain1 = native::WeakPtr::::null(); - - //TODO: proper error type? - let non_srgb_format = conv::map_format_nosrgb(config.format).unwrap(); - - let mut flags = dxgi::DXGI_SWAP_CHAIN_FLAG_FRAME_LATENCY_WAITABLE_OBJECT; - if config.present_mode.contains(w::PresentMode::IMMEDIATE) { - flags |= dxgi::DXGI_SWAP_CHAIN_FLAG_ALLOW_TEARING; - } - - // TODO: double-check values - let desc = dxgi1_2::DXGI_SWAP_CHAIN_DESC1 { - AlphaMode: dxgi1_2::DXGI_ALPHA_MODE_IGNORE, - BufferCount: config.image_count, - Width: config.extent.width, - Height: config.extent.height, - Format: non_srgb_format, - Flags: flags, - BufferUsage: dxgitype::DXGI_USAGE_RENDER_TARGET_OUTPUT, - SampleDesc: dxgitype::DXGI_SAMPLE_DESC { - Count: 1, - Quality: 0, - }, - Scaling: dxgi1_2::DXGI_SCALING_STRETCH, - Stereo: FALSE, - SwapEffect: dxgi::DXGI_SWAP_EFFECT_FLIP_DISCARD, - }; - - unsafe { - let hr = factory.CreateSwapChainForHwnd( - self.present_queue.as_mut_ptr() as *mut _, - window_handle, - &desc, - ptr::null(), - ptr::null_mut(), - swap_chain1.mut_void() as *mut *mut _, - ); - - if !winerror::SUCCEEDED(hr) { - error!("error on swapchain creation 0x{:x}", hr); - return Err(w::SwapchainError::Unknown); - } - - let (swap_chain3, hr3) = swap_chain1.cast::(); - if !winerror::SUCCEEDED(hr3) { - error!("error on swapchain cast 0x{:x}", hr3); - } - - swap_chain1.destroy(); - Ok((swap_chain3, non_srgb_format)) - } - } - - pub(crate) fn wrap_swapchain( - &self, - inner: native::WeakPtr, - config: &w::SwapchainConfig, - ) -> Swapchain { - let waitable = unsafe { - inner.SetMaximumFrameLatency(config.image_count); - inner.GetFrameLatencyWaitableObject() - }; - - let rtv_desc = d3d12::D3D12_RENDER_TARGET_VIEW_DESC { - Format: conv::map_format(config.format).unwrap(), - ViewDimension: d3d12::D3D12_RTV_DIMENSION_TEXTURE2D, - ..unsafe { mem::zeroed() } - }; - let rtv_heap = Device::create_descriptor_heap_impl( - self.raw, - native::DescriptorHeapType::Rtv, - false, - config.image_count as _, - ); - - let mut resources = vec![native::Resource::null(); config.image_count as usize]; - for (i, res) in resources.iter_mut().enumerate() { - let rtv_handle = rtv_heap.at(i as _, 0).cpu; - unsafe { - inner.GetBuffer(i as _, &d3d12::ID3D12Resource::uuidof(), res.mut_void()); - self.raw - .CreateRenderTargetView(res.as_mut_ptr(), &rtv_desc, rtv_handle); - } - } - - Swapchain { - inner, - rtv_heap, - resources, - waitable, - usage: config.image_usage, - acquired_count: 0, - } - } - - pub(crate) fn bind_image_resource( - &self, - resource: native::WeakPtr, - image: &mut r::Image, - place: r::Place, - ) { - let image_unbound = image.expect_unbound(); - let num_layers = image_unbound.kind.num_layers(); - - //TODO: the clear_Xv is incomplete. We should support clearing images created without XXX_ATTACHMENT usage. - // for this, we need to check the format and force the `RENDER_TARGET` flag behind the user's back - // if the format supports being rendered into, allowing us to create clear_Xv - let info = ViewInfo { - resource, - kind: image_unbound.kind, - caps: image::ViewCapabilities::empty(), - view_kind: match image_unbound.kind { - image::Kind::D1(..) => image::ViewKind::D1Array, - image::Kind::D2(..) => image::ViewKind::D2Array, - image::Kind::D3(..) => image::ViewKind::D3, - }, - format: image_unbound.desc.Format, - component_mapping: IDENTITY_MAPPING, - levels: 0..1, - layers: 0..0, - }; - let format_properties = self - .format_properties - .resolve(image_unbound.format as usize) - .properties; - let props = match image_unbound.tiling { - image::Tiling::Optimal => format_properties.optimal_tiling, - image::Tiling::Linear => format_properties.linear_tiling, - }; - let can_clear_color = image_unbound - .usage - .intersects(image::Usage::TRANSFER_DST | image::Usage::COLOR_ATTACHMENT) - && props.contains(format::ImageFeature::COLOR_ATTACHMENT); - let can_clear_depth = image_unbound - .usage - .intersects(image::Usage::TRANSFER_DST | image::Usage::DEPTH_STENCIL_ATTACHMENT) - && props.contains(format::ImageFeature::DEPTH_STENCIL_ATTACHMENT); - let aspects = image_unbound.format.surface_desc().aspects; - - *image = r::Image::Bound(r::ImageBound { - resource, - place, - surface_type: image_unbound.format.base_format().0, - kind: image_unbound.kind, - mip_levels: image_unbound.mip_levels, - default_view_format: image_unbound.view_format, - view_caps: image_unbound.view_caps, - descriptor: image_unbound.desc, - clear_cv: if aspects.contains(Aspects::COLOR) && can_clear_color { - let format = image_unbound.view_format.unwrap(); - (0..num_layers) - .map(|layer| { - self.view_image_as_render_target(&ViewInfo { - format, - layers: layer..layer + 1, - ..info.clone() - }) - .unwrap() - }) - .collect() - } else { - Vec::new() - }, - clear_dv: if aspects.contains(Aspects::DEPTH) && can_clear_depth { - let format = image_unbound.dsv_format.unwrap(); - (0..num_layers) - .map(|layer| { - self.view_image_as_depth_stencil(&ViewInfo { - format, - layers: layer..layer + 1, - ..info.clone() - }) - .unwrap() - }) - .collect() - } else { - Vec::new() - }, - clear_sv: if aspects.contains(Aspects::STENCIL) && can_clear_depth { - let format = image_unbound.dsv_format.unwrap(); - (0..num_layers) - .map(|layer| { - self.view_image_as_depth_stencil(&ViewInfo { - format, - layers: layer..layer + 1, - ..info.clone() - }) - .unwrap() - }) - .collect() - } else { - Vec::new() - }, - requirements: image_unbound.requirements, - }); - } -} - -impl d::Device for Device { - unsafe fn allocate_memory( - &self, - mem_type: hal::MemoryTypeId, - size: u64, - ) -> Result { - let mem_type = mem_type.0; - let mem_base_id = mem_type % NUM_HEAP_PROPERTIES; - let heap_property = &self.heap_properties[mem_base_id]; - - let properties = d3d12::D3D12_HEAP_PROPERTIES { - Type: d3d12::D3D12_HEAP_TYPE_CUSTOM, - CPUPageProperty: heap_property.page_property, - MemoryPoolPreference: heap_property.memory_pool, - CreationNodeMask: 0, - VisibleNodeMask: 0, - }; - - // Exposed memory types are grouped according to their capabilities. - // See `MemoryGroup` for more details. - let mem_group = mem_type / NUM_HEAP_PROPERTIES; - - let desc = d3d12::D3D12_HEAP_DESC { - SizeInBytes: size, - Properties: properties, - Alignment: d3d12::D3D12_DEFAULT_MSAA_RESOURCE_PLACEMENT_ALIGNMENT as _, // TODO: not always..? - Flags: match mem_group { - 0 => d3d12::D3D12_HEAP_FLAG_ALLOW_ALL_BUFFERS_AND_TEXTURES, - 1 => d3d12::D3D12_HEAP_FLAG_ALLOW_ONLY_BUFFERS, - 2 => d3d12::D3D12_HEAP_FLAG_ALLOW_ONLY_NON_RT_DS_TEXTURES, - 3 => d3d12::D3D12_HEAP_FLAG_ALLOW_ONLY_RT_DS_TEXTURES, - _ => unreachable!(), - }, - }; - - let mut heap = native::Heap::null(); - let hr = self - .raw - .clone() - .CreateHeap(&desc, &d3d12::ID3D12Heap::uuidof(), heap.mut_void()); - if hr != winerror::S_OK { - if hr != winerror::E_OUTOFMEMORY { - error!("Error in CreateHeap: 0x{:X}", hr); - } - return Err(d::OutOfMemory::Device.into()); - } - - // The first memory heap of each group corresponds to the default heap, which is can never - // be mapped. - // Devices supporting heap tier 1 can only created buffers on mem group 1 (ALLOW_ONLY_BUFFERS). - // Devices supporting heap tier 2 always expose only mem group 0 and don't have any further restrictions. - let is_mapable = mem_base_id != 0 - && (mem_group == MemoryGroup::Universal as _ - || mem_group == MemoryGroup::BufferOnly as _); - - // Create a buffer resource covering the whole memory slice to be able to map the whole memory. - let resource = if is_mapable { - let mut resource = native::Resource::null(); - let desc = d3d12::D3D12_RESOURCE_DESC { - Dimension: d3d12::D3D12_RESOURCE_DIMENSION_BUFFER, - Alignment: 0, - Width: size, - Height: 1, - DepthOrArraySize: 1, - MipLevels: 1, - Format: dxgiformat::DXGI_FORMAT_UNKNOWN, - SampleDesc: dxgitype::DXGI_SAMPLE_DESC { - Count: 1, - Quality: 0, - }, - Layout: d3d12::D3D12_TEXTURE_LAYOUT_ROW_MAJOR, - Flags: d3d12::D3D12_RESOURCE_FLAG_NONE, - }; - - assert_eq!( - winerror::S_OK, - self.raw.clone().CreatePlacedResource( - heap.as_mut_ptr(), - 0, - &desc, - d3d12::D3D12_RESOURCE_STATE_COMMON, - ptr::null(), - &d3d12::ID3D12Resource::uuidof(), - resource.mut_void(), - ) - ); - - Some(resource) - } else { - None - }; - - Ok(r::Memory { - heap, - type_id: mem_type, - size, - resource, - }) - } - - unsafe fn create_command_pool( - &self, - family: QueueFamilyId, - create_flags: CommandPoolCreateFlags, - ) -> Result { - let list_type = QUEUE_FAMILIES[family.0].native_type(); - Ok(CommandPool::new( - self.raw, - list_type, - &self.shared, - create_flags, - )) - } - - unsafe fn destroy_command_pool(&self, _pool: CommandPool) {} - - unsafe fn create_render_pass<'a, Ia, Is, Id>( - &self, - attachments: Ia, - subpasses: Is, - dependencies: Id, - ) -> Result - where - Ia: Iterator, - Is: Iterator>, - Id: Iterator, - { - #[derive(Copy, Clone, Debug, PartialEq)] - enum SubState { - New(d3d12::D3D12_RESOURCE_STATES), - // Color attachment which will be resolved at the end of the subpass - Resolve(d3d12::D3D12_RESOURCE_STATES), - Preserve, - Undefined, - } - /// Temporary information about every sub-pass - struct SubInfo<'a> { - desc: pass::SubpassDesc<'a>, - /// States before the render-pass (in self.start) - /// and after the render-pass (in self.end). - external_dependencies: Range, - /// Counts the number of dependencies that need to be resolved - /// before starting this subpass. - unresolved_dependencies: u16, - } - struct AttachmentInfo { - sub_states: Vec, - last_state: d3d12::D3D12_RESOURCE_STATES, - barrier_start_index: usize, - } - - let attachments = attachments.collect::>(); - let mut sub_infos = subpasses - .map(|desc| SubInfo { - desc: desc.clone(), - external_dependencies: image::Access::empty()..image::Access::empty(), - unresolved_dependencies: 0, - }) - .collect::>(); - let dependencies = dependencies.collect::>(); - - let mut att_infos = (0..attachments.len()) - .map(|_| AttachmentInfo { - sub_states: vec![SubState::Undefined; sub_infos.len()], - last_state: d3d12::D3D12_RESOURCE_STATE_COMMON, // is to be overwritten - barrier_start_index: 0, - }) - .collect::>(); - - for dep in &dependencies { - match dep.passes { - Range { - start: None, - end: None, - } => { - error!("Unexpected external-external dependency!"); - } - Range { - start: None, - end: Some(sid), - } => { - sub_infos[sid as usize].external_dependencies.start |= dep.accesses.start; - } - Range { - start: Some(sid), - end: None, - } => { - sub_infos[sid as usize].external_dependencies.end |= dep.accesses.end; - } - Range { - start: Some(from_sid), - end: Some(sid), - } => { - //Note: self-dependencies are ignored - if from_sid != sid { - sub_infos[sid as usize].unresolved_dependencies += 1; - } - } - } - } - - // Fill out subpass known layouts - for (sid, sub_info) in sub_infos.iter().enumerate() { - let sub = &sub_info.desc; - for (i, &(id, _layout)) in sub.colors.iter().enumerate() { - let target_state = d3d12::D3D12_RESOURCE_STATE_RENDER_TARGET; - let state = match sub.resolves.get(i) { - Some(_) => SubState::Resolve(target_state), - None => SubState::New(target_state), - }; - let old = mem::replace(&mut att_infos[id].sub_states[sid], state); - debug_assert_eq!(SubState::Undefined, old); - } - for &(id, layout) in sub.depth_stencil { - let state = SubState::New(match layout { - image::Layout::DepthStencilAttachmentOptimal => { - d3d12::D3D12_RESOURCE_STATE_DEPTH_WRITE - } - image::Layout::DepthStencilReadOnlyOptimal => { - d3d12::D3D12_RESOURCE_STATE_DEPTH_READ - } - image::Layout::General => d3d12::D3D12_RESOURCE_STATE_DEPTH_WRITE, - _ => { - error!("Unexpected depth/stencil layout: {:?}", layout); - d3d12::D3D12_RESOURCE_STATE_COMMON - } - }); - let old = mem::replace(&mut att_infos[id].sub_states[sid], state); - debug_assert_eq!(SubState::Undefined, old); - } - for &(id, _layout) in sub.inputs { - let state = SubState::New(d3d12::D3D12_RESOURCE_STATE_PIXEL_SHADER_RESOURCE); - let old = mem::replace(&mut att_infos[id].sub_states[sid], state); - debug_assert_eq!(SubState::Undefined, old); - } - for &(id, _layout) in sub.resolves { - let state = SubState::New(d3d12::D3D12_RESOURCE_STATE_RESOLVE_DEST); - let old = mem::replace(&mut att_infos[id].sub_states[sid], state); - debug_assert_eq!(SubState::Undefined, old); - } - for &id in sub.preserves { - let old = mem::replace(&mut att_infos[id].sub_states[sid], SubState::Preserve); - debug_assert_eq!(SubState::Undefined, old); - } - } - - let mut rp = r::RenderPass { - attachments: attachments.iter().cloned().collect(), - subpasses: Vec::new(), - post_barriers: Vec::new(), - raw_name: Vec::new(), - }; - - while let Some(sid) = sub_infos - .iter() - .position(|si| si.unresolved_dependencies == 0) - { - for dep in &dependencies { - if dep.passes.start != dep.passes.end - && dep.passes.start == Some(sid as pass::SubpassId) - { - if let Some(other) = dep.passes.end { - sub_infos[other as usize].unresolved_dependencies -= 1; - } - } - } - - let si = &mut sub_infos[sid]; - si.unresolved_dependencies = !0; // mark as done - - // Subpass barriers - let mut pre_barriers = Vec::new(); - let mut post_barriers = Vec::new(); - for (att_id, (ai, att)) in att_infos.iter_mut().zip(attachments.iter()).enumerate() { - // Attachment wasn't used before, figure out the initial state - if ai.barrier_start_index == 0 { - //Note: the external dependencies are provided for all attachments that are - // first used in this sub-pass, so they may contain more states than we expect - // for this particular attachment. - ai.last_state = conv::map_image_resource_state( - si.external_dependencies.start, - att.layouts.start, - ); - } - // Barrier from previous subpass to current or following subpasses. - match ai.sub_states[sid] { - SubState::Preserve => { - ai.barrier_start_index = rp.subpasses.len() + 1; - } - SubState::New(state) if state != ai.last_state => { - let barrier = r::BarrierDesc::new(att_id, ai.last_state..state); - match rp.subpasses.get_mut(ai.barrier_start_index) { - Some(past_subpass) => { - let split = barrier.split(); - past_subpass.pre_barriers.push(split.start); - pre_barriers.push(split.end); - } - None => pre_barriers.push(barrier), - } - ai.last_state = state; - ai.barrier_start_index = rp.subpasses.len() + 1; - } - SubState::Resolve(state) => { - // 1. Standard pre barrier to update state from previous pass into desired substate. - if state != ai.last_state { - let barrier = r::BarrierDesc::new(att_id, ai.last_state..state); - match rp.subpasses.get_mut(ai.barrier_start_index) { - Some(past_subpass) => { - let split = barrier.split(); - past_subpass.pre_barriers.push(split.start); - pre_barriers.push(split.end); - } - None => pre_barriers.push(barrier), - } - } - - // 2. Post Barrier at the end of the subpass into RESOLVE_SOURCE. - let resolve_state = d3d12::D3D12_RESOURCE_STATE_RESOLVE_SOURCE; - let barrier = r::BarrierDesc::new(att_id, state..resolve_state); - post_barriers.push(barrier); - - ai.last_state = resolve_state; - ai.barrier_start_index = rp.subpasses.len() + 1; - } - SubState::Undefined | SubState::New(_) => {} - }; - } - - rp.subpasses.push(r::SubpassDesc { - color_attachments: si.desc.colors.iter().cloned().collect(), - depth_stencil_attachment: si.desc.depth_stencil.cloned(), - input_attachments: si.desc.inputs.iter().cloned().collect(), - resolve_attachments: si.desc.resolves.iter().cloned().collect(), - pre_barriers, - post_barriers, - }); - } - // if this fails, our graph has cycles - assert_eq!(rp.subpasses.len(), sub_infos.len()); - assert!(sub_infos.iter().all(|si| si.unresolved_dependencies == !0)); - - // take care of the post-pass transitions at the end of the renderpass. - for (att_id, (ai, att)) in att_infos.iter().zip(attachments.iter()).enumerate() { - let state_dst = if ai.barrier_start_index == 0 { - // attachment wasn't used in any sub-pass? - continue; - } else { - let si = &sub_infos[ai.barrier_start_index - 1]; - conv::map_image_resource_state(si.external_dependencies.end, att.layouts.end) - }; - if state_dst == ai.last_state { - continue; - } - let barrier = r::BarrierDesc::new(att_id, ai.last_state..state_dst); - match rp.subpasses.get_mut(ai.barrier_start_index) { - Some(past_subpass) => { - let split = barrier.split(); - past_subpass.pre_barriers.push(split.start); - rp.post_barriers.push(split.end); - } - None => rp.post_barriers.push(barrier), - } - } - - Ok(rp) - } - - unsafe fn create_pipeline_layout<'a, Is, Ic>( - &self, - sets: Is, - push_constant_ranges: Ic, - ) -> Result - where - Is: Iterator, - Ic: Iterator)>, - { - // Pipeline layouts are implemented as RootSignature for D3D12. - // - // Push Constants are implemented as root constants. - // - // Each descriptor set layout will be one table entry of the root signature. - // We have the additional restriction that SRV/CBV/UAV and samplers need to be - // separated, so each set layout will actually occupy up to 2 entries! - // SRV/CBV/UAV tables are added to the signature first, then Sampler tables, - // and finally dynamic uniform descriptors. - // - // Dynamic uniform buffers are implemented as root descriptors. - // This allows to handle the dynamic offsets properly, which would not be feasible - // with a combination of root constant and descriptor table. - // - // Root signature layout: - // Root Constants: Register: Offest/4, Space: 0 - // ... - // DescriptorTable0: Space: 1 (SrvCbvUav) - // DescriptorTable0: Space: 1 (Sampler) - // Root Descriptors 0 - // DescriptorTable1: Space: 2 (SrvCbvUav) - // Root Descriptors 1 - // ... - - let sets = sets.collect::>(); - - let mut root_offset = 0u32; - let root_constants = root_constants::split(push_constant_ranges) - .iter() - .map(|constant| { - assert!(constant.range.start <= constant.range.end); - root_offset += constant.range.end - constant.range.start; - - RootConstant { - stages: constant.stages, - range: constant.range.start..constant.range.end, - } - }) - .collect::>(); - - info!( - "Creating a pipeline layout with {} sets and {} root constants", - sets.len(), - root_constants.len() - ); - - // Number of elements in the root signature. - // Guarantees that no re-allocation is done, and our pointers are valid - let mut parameters = Vec::with_capacity(root_constants.len() + sets.len() * 2); - let mut parameter_offsets = Vec::with_capacity(parameters.capacity()); - - // Convert root signature descriptions into root signature parameters. - for root_constant in root_constants.iter() { - debug!( - "\tRoot constant set={} range {:?}", - ROOT_CONSTANT_SPACE, root_constant.range - ); - parameter_offsets.push(root_constant.range.start); - parameters.push(native::RootParameter::constants( - conv::map_shader_visibility(root_constant.stages), - native::Binding { - register: root_constant.range.start as _, - space: ROOT_CONSTANT_SPACE, - }, - (root_constant.range.end - root_constant.range.start) as _, - )); - } - - // Offest of `spaceN` for descriptor tables. Root constants will be in - // `space0`. - // This has to match `patch_spirv_resources` logic. - let root_space_offset = if !root_constants.is_empty() { 1 } else { 0 }; - - // Collect the whole number of bindings we will create upfront. - // It allows us to preallocate enough storage to avoid reallocation, - // which could cause invalid pointers. - let total = sets - .iter() - .map(|desc_set| { - let mut sum = 0; - for binding in desc_set.bindings.iter() { - let content = r::DescriptorContent::from(binding.ty); - if !content.is_dynamic() { - sum += content.bits().count_ones() as usize; - } - } - sum - }) - .sum(); - let mut ranges = Vec::with_capacity(total); - - let elements = sets - .iter() - .enumerate() - .map(|(i, set)| { - let space = (root_space_offset + i) as u32; - let mut table_type = r::SetTableTypes::empty(); - let root_table_offset = root_offset; - log::debug!("\tSet {} space={}, root offset={}", i, space, root_offset); - - //TODO: split between sampler and non-sampler tables - let visibility = conv::map_shader_visibility( - set.bindings - .iter() - .fold(pso::ShaderStageFlags::empty(), |u, bind| { - u | bind.stage_flags - }), - ); - - for bind in set.bindings.iter() { - debug!("\tRange {:?} at space={}", bind, space); - } - - let describe = |bind: &pso::DescriptorSetLayoutBinding, ty| { - native::DescriptorRange::new( - ty, - bind.count as _, - native::Binding { - register: bind.binding as _, - space, - }, - d3d12::D3D12_DESCRIPTOR_RANGE_OFFSET_APPEND, - ) - }; - - let mut mutable_bindings = auxil::FastHashSet::default(); - - // SRV/CBV/UAV descriptor tables - let mut range_base = ranges.len(); - for bind in set.bindings.iter() { - let content = r::DescriptorContent::from(bind.ty); - if !content.is_dynamic() { - // Descriptor table ranges - if content.contains(r::DescriptorContent::CBV) { - ranges.push(describe(bind, native::DescriptorRangeType::CBV)); - } - if content.contains(r::DescriptorContent::SRV) { - ranges.push(describe(bind, native::DescriptorRangeType::SRV)); - } - if content.contains(r::DescriptorContent::UAV) { - ranges.push(describe(bind, native::DescriptorRangeType::UAV)); - mutable_bindings.insert(bind.binding); - } - } - } - if ranges.len() > range_base { - parameter_offsets.push(root_offset); - log::debug!( - "\tView table with {} views at root offset {}", - ranges.len() - range_base, - root_offset - ); - parameters.push(native::RootParameter::descriptor_table( - visibility, - &ranges[range_base..], - )); - table_type |= r::SRV_CBV_UAV; - root_offset += 1; - } - - // Sampler descriptor tables - range_base = ranges.len(); - for bind in set.bindings.iter() { - let content = r::DescriptorContent::from(bind.ty); - if content.contains(r::DescriptorContent::SAMPLER) { - ranges.push(describe(bind, native::DescriptorRangeType::Sampler)); - } - } - if ranges.len() > range_base { - log::debug!( - "\tSampler table with {} samplers at root offset {}", - ranges.len() - range_base, - root_offset - ); - parameter_offsets.push(root_offset); - parameters.push(native::RootParameter::descriptor_table( - visibility, - &ranges[range_base..], - )); - table_type |= r::SAMPLERS; - root_offset += 1; - } - - // Root (dynamic) descriptor tables - for bind in set.bindings.iter() { - let content = r::DescriptorContent::from(bind.ty); - if content.is_dynamic() { - let binding = native::Binding { - register: bind.binding as _, - space, - }; - - if content.contains(r::DescriptorContent::CBV) { - parameter_offsets.push(root_offset); - parameters - .push(native::RootParameter::cbv_descriptor(visibility, binding)); - root_offset += 2; // root CBV costs 2 words - } - if content.contains(r::DescriptorContent::SRV) { - parameter_offsets.push(root_offset); - parameters - .push(native::RootParameter::srv_descriptor(visibility, binding)); - root_offset += 2; // root SRV costs 2 words - } - if content.contains(r::DescriptorContent::UAV) { - parameter_offsets.push(root_offset); - parameters - .push(native::RootParameter::uav_descriptor(visibility, binding)); - root_offset += 2; // root UAV costs 2 words - } - } - } - - r::RootElement { - table: r::RootTable { - ty: table_type, - offset: root_table_offset as _, - }, - mutable_bindings, - } - }) - .collect(); - - // Ensure that we didn't reallocate! - debug_assert_eq!(ranges.len(), total); - assert_eq!(parameters.len(), parameter_offsets.len()); - - // TODO: error handling - let (signature_raw, error) = match self.library.serialize_root_signature( - native::RootSignatureVersion::V1_0, - ¶meters, - &[], - native::RootSignatureFlags::ALLOW_IA_INPUT_LAYOUT, - ) { - Ok((pair, hr)) if winerror::SUCCEEDED(hr) => pair, - Ok((_, hr)) => panic!("Can't serialize root signature: {:?}", hr), - Err(e) => panic!("Can't find serialization function: {:?}", e), - }; - - if !error.is_null() { - error!( - "Root signature serialization error: {:?}", - error.as_c_str().to_str().unwrap() - ); - error.destroy(); - } - - // TODO: error handling - let (signature, _hr) = self.raw.create_root_signature(signature_raw, 0); - signature_raw.destroy(); - - Ok(r::PipelineLayout { - shared: Arc::new(r::PipelineShared { - signature, - constants: root_constants, - parameter_offsets, - total_slots: root_offset, - }), - elements, - }) - } - - unsafe fn create_pipeline_cache(&self, _data: Option<&[u8]>) -> Result<(), d::OutOfMemory> { - Ok(()) - } - - unsafe fn get_pipeline_cache_data(&self, _cache: &()) -> Result, d::OutOfMemory> { - //empty - Ok(Vec::new()) - } - - unsafe fn destroy_pipeline_cache(&self, _: ()) { - //empty - } - - unsafe fn merge_pipeline_caches<'a, I>(&self, _: &mut (), _: I) -> Result<(), d::OutOfMemory> - where - I: Iterator, - { - //empty - Ok(()) - } - - unsafe fn create_graphics_pipeline<'a>( - &self, - desc: &pso::GraphicsPipelineDesc<'a, B>, - _cache: Option<&()>, - ) -> Result { - enum ShaderBc { - Owned(native::Blob), - Borrowed(native::Blob), - None, - } - let features = &self.features; - impl ShaderBc { - pub fn shader(&self) -> native::Shader { - match *self { - ShaderBc::Owned(ref bc) | ShaderBc::Borrowed(ref bc) => { - native::Shader::from_blob(*bc) - } - ShaderBc::None => native::Shader::null(), - } - } - } - - let build_shader = |stage: ShaderStage, - source: Option<&pso::EntryPoint<'a, B>>| - -> Result { - let source = match source { - Some(src) => src, - None => return Ok(ShaderBc::None), - }; - - let (shader, owned) = Self::extract_entry_point(stage, source, desc.layout, features)?; - Ok(if owned { - ShaderBc::Owned(shader) - } else { - ShaderBc::Borrowed(shader) - }) - }; - - let vertex_buffers: Vec = Vec::new(); - let attributes: Vec = Vec::new(); - let mesh_input_assembler = pso::InputAssemblerDesc::new(pso::Primitive::TriangleList); - let (vertex_buffers, attributes, input_assembler, vs, gs, hs, ds, _, _) = - match desc.primitive_assembler { - pso::PrimitiveAssemblerDesc::Vertex { - buffers, - attributes, - ref input_assembler, - ref vertex, - ref tessellation, - ref geometry, - } => { - let (hs, ds) = if let Some(ts) = tessellation { - (Some(&ts.0), Some(&ts.1)) - } else { - (None, None) - }; - - ( - buffers, - attributes, - input_assembler, - Some(vertex), - geometry.as_ref(), - hs, - ds, - None, - None, - ) - } - pso::PrimitiveAssemblerDesc::Mesh { ref task, ref mesh } => ( - &vertex_buffers[..], - &attributes[..], - &mesh_input_assembler, - None, - None, - None, - None, - task.as_ref(), - Some(mesh), - ), - }; - - let vertex_semantic_remapping = if let Some(ref vs) = vs { - // If we have a pre-compiled shader, we've lost the information we need to recover - // this information, so just pretend like this workaround never existed and hope - // for the best. - if let crate::resource::ShaderModule::Spirv(ref spv) = vs.module { - Some(Self::introspect_spirv_vertex_semantic_remapping(spv)?) - } else { - None - } - } else { - None - }; - - let vs = build_shader(ShaderStage::Vertex, vs)?; - let gs = build_shader(ShaderStage::Geometry, gs)?; - let hs = build_shader(ShaderStage::Domain, hs)?; - let ds = build_shader(ShaderStage::Hull, ds)?; - let ps = build_shader(ShaderStage::Fragment, desc.fragment.as_ref())?; - - // Rebind vertex buffers, see native.rs for more details. - let mut vertex_bindings = [None; MAX_VERTEX_BUFFERS]; - let mut vertex_strides = [0; MAX_VERTEX_BUFFERS]; - - for buffer in vertex_buffers { - vertex_strides[buffer.binding as usize] = buffer.stride; - } - // Fill in identity mapping where we don't need to adjust anything. - for attrib in attributes { - let binding = attrib.binding as usize; - let stride = vertex_strides[attrib.binding as usize]; - if attrib.element.offset < stride { - vertex_bindings[binding] = Some(r::VertexBinding { - stride: vertex_strides[attrib.binding as usize], - offset: 0, - mapped_binding: binding, - }); - } - } - - // See [`introspect_spirv_vertex_semantic_remapping`] for details of why this is needed. - let semantics: Vec<_> = attributes - .iter() - .map(|attrib| { - let semantics = vertex_semantic_remapping - .as_ref() - .and_then(|map| map.get(&attrib.location)); - match semantics { - Some(Some((major, minor))) => { - let name = std::borrow::Cow::Owned(format!("TEXCOORD{}_\0", major)); - let location = *minor; - (name, location) - } - _ => { - let name = std::borrow::Cow::Borrowed("TEXCOORD\0"); - let location = attrib.location; - (name, location) - } - } - }) - .collect(); - - // Define input element descriptions - let input_element_descs = attributes - .iter() - .zip(semantics.iter()) - .filter_map(|(attrib, (semantic_name, semantic_index))| { - let buffer_desc = match vertex_buffers - .iter() - .find(|buffer_desc| buffer_desc.binding == attrib.binding) - { - Some(buffer_desc) => buffer_desc, - None => { - error!( - "Couldn't find associated vertex buffer description {:?}", - attrib.binding - ); - return Some(Err(pso::CreationError::Other)); - } - }; - - let (slot_class, step_rate) = match buffer_desc.rate { - VertexInputRate::Vertex => { - (d3d12::D3D12_INPUT_CLASSIFICATION_PER_VERTEX_DATA, 0) - } - VertexInputRate::Instance(divisor) => { - (d3d12::D3D12_INPUT_CLASSIFICATION_PER_INSTANCE_DATA, divisor) - } - }; - let format = attrib.element.format; - - // Check if we need to add a new remapping in-case the offset is - // higher than the vertex stride. - // In this case we rebase the attribute to zero offset. - let binding = attrib.binding as usize; - let stride = vertex_strides[binding]; - let offset = attrib.element.offset; - let (input_slot, offset) = if stride <= offset { - // Number of input attributes may not exceed bindings, see limits. - // We will always find at least one free binding. - let mapping = vertex_bindings.iter().position(Option::is_none).unwrap(); - vertex_bindings[mapping] = Some(r::VertexBinding { - stride: vertex_strides[binding], - offset: offset, - mapped_binding: binding, - }); - - (mapping, 0) - } else { - (binding, offset) - }; - - Some(Ok(d3d12::D3D12_INPUT_ELEMENT_DESC { - SemanticName: semantic_name.as_ptr() as *const _, // Semantic name used by SPIRV-Cross - SemanticIndex: *semantic_index, - Format: match conv::map_format(format) { - Some(fm) => fm, - None => { - error!("Unable to find DXGI format for {:?}", format); - return Some(Err(pso::CreationError::Other)); - } - }, - InputSlot: input_slot as _, - AlignedByteOffset: offset, - InputSlotClass: slot_class, - InstanceDataStepRate: step_rate as _, - })) - }) - .collect::, _>>()?; - - // TODO: check maximum number of rtvs - // Get associated subpass information - let pass = { - let subpass = &desc.subpass; - match subpass.main_pass.subpasses.get(subpass.index as usize) { - Some(subpass) => subpass, - None => return Err(pso::CreationError::InvalidSubpass(subpass.index)), - } - }; - - // Get color attachment formats from subpass - let (rtvs, num_rtvs) = { - let mut rtvs = [dxgiformat::DXGI_FORMAT_UNKNOWN; - d3d12::D3D12_SIMULTANEOUS_RENDER_TARGET_COUNT as usize]; - let mut num_rtvs = 0; - for (rtv, target) in rtvs.iter_mut().zip(pass.color_attachments.iter()) { - let format = desc.subpass.main_pass.attachments[target.0].format; - *rtv = format - .and_then(conv::map_format) - .unwrap_or(dxgiformat::DXGI_FORMAT_UNKNOWN); - num_rtvs += 1; - } - (rtvs, num_rtvs) - }; - - let sample_desc = dxgitype::DXGI_SAMPLE_DESC { - Count: match desc.multisampling { - Some(ref ms) => ms.rasterization_samples as _, - None => 1, - }, - Quality: 0, - }; - - // Setup pipeline description - let pso_desc = d3d12::D3D12_GRAPHICS_PIPELINE_STATE_DESC { - pRootSignature: desc.layout.shared.signature.as_mut_ptr(), - VS: *vs.shader(), - PS: *ps.shader(), - GS: *gs.shader(), - DS: *ds.shader(), - HS: *hs.shader(), - StreamOutput: d3d12::D3D12_STREAM_OUTPUT_DESC { - pSODeclaration: ptr::null(), - NumEntries: 0, - pBufferStrides: ptr::null(), - NumStrides: 0, - RasterizedStream: 0, - }, - BlendState: d3d12::D3D12_BLEND_DESC { - AlphaToCoverageEnable: desc.multisampling.as_ref().map_or(FALSE, |ms| { - if ms.alpha_coverage { - TRUE - } else { - FALSE - } - }), - IndependentBlendEnable: TRUE, - RenderTarget: conv::map_render_targets(&desc.blender.targets), - }, - SampleMask: match desc.multisampling { - Some(ref ms) => ms.sample_mask as u32, - None => UINT::MAX, - }, - RasterizerState: conv::map_rasterizer(&desc.rasterizer, desc.multisampling.is_some()), - DepthStencilState: conv::map_depth_stencil(&desc.depth_stencil), - InputLayout: d3d12::D3D12_INPUT_LAYOUT_DESC { - pInputElementDescs: if input_element_descs.is_empty() { - ptr::null() - } else { - input_element_descs.as_ptr() - }, - NumElements: input_element_descs.len() as u32, - }, - IBStripCutValue: match input_assembler.restart_index { - Some(hal::IndexType::U16) => d3d12::D3D12_INDEX_BUFFER_STRIP_CUT_VALUE_0xFFFF, - Some(hal::IndexType::U32) => d3d12::D3D12_INDEX_BUFFER_STRIP_CUT_VALUE_0xFFFFFFFF, - None => d3d12::D3D12_INDEX_BUFFER_STRIP_CUT_VALUE_DISABLED, - }, - PrimitiveTopologyType: conv::map_topology_type(input_assembler.primitive), - NumRenderTargets: num_rtvs, - RTVFormats: rtvs, - DSVFormat: pass - .depth_stencil_attachment - .and_then(|att_ref| { - desc.subpass.main_pass.attachments[att_ref.0] - .format - .and_then(|f| conv::map_format_dsv(f.base_format().0)) - }) - .unwrap_or(dxgiformat::DXGI_FORMAT_UNKNOWN), - SampleDesc: sample_desc, - NodeMask: 0, - CachedPSO: d3d12::D3D12_CACHED_PIPELINE_STATE { - pCachedBlob: ptr::null(), - CachedBlobSizeInBytes: 0, - }, - Flags: d3d12::D3D12_PIPELINE_STATE_FLAG_NONE, - }; - let topology = conv::map_topology(input_assembler); - - // Create PSO - let mut pipeline = native::PipelineState::null(); - let hr = if desc.depth_stencil.depth_bounds { - // The DepthBoundsTestEnable option isn't available in the original D3D12_GRAPHICS_PIPELINE_STATE_DESC struct. - // Instead, we must use the newer subobject stream method. - let (device2, hr) = self.raw.cast::(); - if winerror::SUCCEEDED(hr) { - let mut pss_stream = GraphicsPipelineStateSubobjectStream::new(&pso_desc, true); - let pss_desc = d3d12::D3D12_PIPELINE_STATE_STREAM_DESC { - SizeInBytes: mem::size_of_val(&pss_stream), - pPipelineStateSubobjectStream: &mut pss_stream as *mut _ as _, - }; - device2.CreatePipelineState( - &pss_desc, - &d3d12::ID3D12PipelineState::uuidof(), - pipeline.mut_void(), - ) - } else { - hr - } - } else { - self.raw.clone().CreateGraphicsPipelineState( - &pso_desc, - &d3d12::ID3D12PipelineState::uuidof(), - pipeline.mut_void(), - ) - }; - - let destroy_shader = |shader: ShaderBc| { - if let ShaderBc::Owned(bc) = shader { - bc.destroy(); - } - }; - - destroy_shader(vs); - destroy_shader(ps); - destroy_shader(gs); - destroy_shader(hs); - destroy_shader(ds); - - if winerror::SUCCEEDED(hr) { - let mut baked_states = desc.baked_states.clone(); - if !desc.depth_stencil.depth_bounds { - baked_states.depth_bounds = None; - } - if let Some(name) = desc.label { - let cwstr = wide_cstr(name); - pipeline.SetName(cwstr.as_ptr()); - } - - Ok(r::GraphicsPipeline { - raw: pipeline, - shared: Arc::clone(&desc.layout.shared), - topology, - vertex_bindings, - baked_states, - }) - } else { - let error = format!("Failed to build shader: {:x}", hr); - Err(pso::CreationError::ShaderCreationError( - pso::ShaderStageFlags::GRAPHICS, - error, - )) - } - } - - unsafe fn create_compute_pipeline<'a>( - &self, - desc: &pso::ComputePipelineDesc<'a, B>, - _cache: Option<&()>, - ) -> Result { - let (cs, cs_destroy) = Self::extract_entry_point( - ShaderStage::Compute, - &desc.shader, - desc.layout, - &self.features, - )?; - - let (pipeline, hr) = self.raw.create_compute_pipeline_state( - desc.layout.shared.signature, - native::Shader::from_blob(cs), - 0, - native::CachedPSO::null(), - native::PipelineStateFlags::empty(), - ); - - if cs_destroy { - cs.destroy(); - } - - if winerror::SUCCEEDED(hr) { - if let Some(name) = desc.label { - let cwstr = wide_cstr(name); - pipeline.SetName(cwstr.as_ptr()); - } - - Ok(r::ComputePipeline { - raw: pipeline, - shared: Arc::clone(&desc.layout.shared), - }) - } else { - let error = format!("Failed to build shader: {:x}", hr); - Err(pso::CreationError::ShaderCreationError( - pso::ShaderStageFlags::COMPUTE, - error, - )) - } - } - - unsafe fn create_framebuffer( - &self, - _renderpass: &r::RenderPass, - _attachments: I, - extent: image::Extent, - ) -> Result { - Ok(r::Framebuffer { - layers: extent.depth as _, - }) - } - - unsafe fn create_shader_module( - &self, - raw_data: &[u32], - ) -> Result { - Ok(r::ShaderModule::Spirv(raw_data.into())) - } - - unsafe fn create_buffer( - &self, - mut size: u64, - usage: buffer::Usage, - _sparse: memory::SparseFlags, - ) -> Result { - if usage.contains(buffer::Usage::UNIFORM) { - // Constant buffer view sizes need to be aligned. - // Coupled with the offset alignment we can enforce an aligned CBV size - // on descriptor updates. - let mask = d3d12::D3D12_CONSTANT_BUFFER_DATA_PLACEMENT_ALIGNMENT as u64 - 1; - size = (size + mask) & !mask; - } - if usage.contains(buffer::Usage::TRANSFER_DST) { - // minimum of 1 word for the clear UAV - size = size.max(4); - } - - let type_mask_shift = if self.private_caps.heterogeneous_resource_heaps { - MEM_TYPE_UNIVERSAL_SHIFT - } else { - MEM_TYPE_BUFFER_SHIFT - }; - - let requirements = memory::Requirements { - size, - alignment: d3d12::D3D12_DEFAULT_RESOURCE_PLACEMENT_ALIGNMENT as u64, - type_mask: MEM_TYPE_MASK << type_mask_shift, - }; - - Ok(r::Buffer::Unbound(r::BufferUnbound { - requirements, - usage, - name: None, - })) - } - - unsafe fn get_buffer_requirements(&self, buffer: &r::Buffer) -> Requirements { - match buffer { - r::Buffer::Unbound(b) => b.requirements, - r::Buffer::Bound(b) => b.requirements, - } - } - - unsafe fn bind_buffer_memory( - &self, - memory: &r::Memory, - offset: u64, - buffer: &mut r::Buffer, - ) -> Result<(), d::BindError> { - let buffer_unbound = buffer.expect_unbound(); - if buffer_unbound.requirements.type_mask & (1 << memory.type_id) == 0 { - error!( - "Bind memory failure: supported mask 0x{:x}, given id {}", - buffer_unbound.requirements.type_mask, memory.type_id - ); - return Err(d::BindError::WrongMemory); - } - if offset + buffer_unbound.requirements.size > memory.size { - return Err(d::BindError::OutOfBounds); - } - - let mut resource = native::Resource::null(); - let desc = d3d12::D3D12_RESOURCE_DESC { - Dimension: d3d12::D3D12_RESOURCE_DIMENSION_BUFFER, - Alignment: 0, - Width: buffer_unbound.requirements.size, - Height: 1, - DepthOrArraySize: 1, - MipLevels: 1, - Format: dxgiformat::DXGI_FORMAT_UNKNOWN, - SampleDesc: dxgitype::DXGI_SAMPLE_DESC { - Count: 1, - Quality: 0, - }, - Layout: d3d12::D3D12_TEXTURE_LAYOUT_ROW_MAJOR, - Flags: conv::map_buffer_flags(buffer_unbound.usage), - }; - - assert_eq!( - winerror::S_OK, - self.raw.clone().CreatePlacedResource( - memory.heap.as_mut_ptr(), - offset, - &desc, - d3d12::D3D12_RESOURCE_STATE_COMMON, - ptr::null(), - &d3d12::ID3D12Resource::uuidof(), - resource.mut_void(), - ) - ); - - if let Some(ref name) = buffer_unbound.name { - resource.SetName(name.as_ptr()); - } - - let clear_uav = if buffer_unbound.usage.contains(buffer::Usage::TRANSFER_DST) { - let handle = self.srv_uav_pool.lock().alloc_handle(); - let mut view_desc = d3d12::D3D12_UNORDERED_ACCESS_VIEW_DESC { - Format: dxgiformat::DXGI_FORMAT_R32_TYPELESS, - ViewDimension: d3d12::D3D12_UAV_DIMENSION_BUFFER, - u: mem::zeroed(), - }; - - *view_desc.u.Buffer_mut() = d3d12::D3D12_BUFFER_UAV { - FirstElement: 0, - NumElements: (buffer_unbound.requirements.size / 4) as _, - StructureByteStride: 0, - CounterOffsetInBytes: 0, - Flags: d3d12::D3D12_BUFFER_UAV_FLAG_RAW, - }; - - self.raw.CreateUnorderedAccessView( - resource.as_mut_ptr(), - ptr::null_mut(), - &view_desc, - handle.raw, - ); - Some(handle) - } else { - None - }; - - *buffer = r::Buffer::Bound(r::BufferBound { - resource, - requirements: buffer_unbound.requirements, - clear_uav, - }); - - Ok(()) - } - - unsafe fn create_buffer_view( - &self, - buffer: &r::Buffer, - format: Option, - sub: buffer::SubRange, - ) -> Result { - let buffer = buffer.expect_bound(); - let buffer_features = { - let idx = format.map(|fmt| fmt as usize).unwrap_or(0); - self.format_properties - .resolve(idx) - .properties - .buffer_features - }; - let (format, format_desc) = match format.and_then(conv::map_format) { - Some(fmt) => (fmt, format.unwrap().surface_desc()), - None => return Err(buffer::ViewCreationError::UnsupportedFormat(format)), - }; - - let start = sub.offset; - let size = sub.size.unwrap_or(buffer.requirements.size - start); - - let bytes_per_texel = (format_desc.bits / 8) as u64; - // Check if it adheres to the texel buffer offset limit - assert_eq!(start % bytes_per_texel, 0); - let first_element = start / bytes_per_texel; - let num_elements = size / bytes_per_texel; // rounds down to next smaller size - - let handle_srv = if buffer_features.contains(format::BufferFeature::UNIFORM_TEXEL) { - let mut desc = d3d12::D3D12_SHADER_RESOURCE_VIEW_DESC { - Format: format, - ViewDimension: d3d12::D3D12_SRV_DIMENSION_BUFFER, - Shader4ComponentMapping: IDENTITY_MAPPING, - u: mem::zeroed(), - }; - - *desc.u.Buffer_mut() = d3d12::D3D12_BUFFER_SRV { - FirstElement: first_element, - NumElements: num_elements as _, - StructureByteStride: bytes_per_texel as _, - Flags: d3d12::D3D12_BUFFER_SRV_FLAG_NONE, - }; - - let handle = self.srv_uav_pool.lock().alloc_handle(); - self.raw.clone().CreateShaderResourceView( - buffer.resource.as_mut_ptr(), - &desc, - handle.raw, - ); - Some(handle) - } else { - None - }; - - let handle_uav = if buffer_features.intersects( - format::BufferFeature::STORAGE_TEXEL | format::BufferFeature::STORAGE_TEXEL_ATOMIC, - ) { - let mut desc = d3d12::D3D12_UNORDERED_ACCESS_VIEW_DESC { - Format: format, - ViewDimension: d3d12::D3D12_UAV_DIMENSION_BUFFER, - u: mem::zeroed(), - }; - - *desc.u.Buffer_mut() = d3d12::D3D12_BUFFER_UAV { - FirstElement: first_element, - NumElements: num_elements as _, - StructureByteStride: bytes_per_texel as _, - Flags: d3d12::D3D12_BUFFER_UAV_FLAG_NONE, - CounterOffsetInBytes: 0, - }; - - let handle = self.srv_uav_pool.lock().alloc_handle(); - self.raw.clone().CreateUnorderedAccessView( - buffer.resource.as_mut_ptr(), - ptr::null_mut(), - &desc, - handle.raw, - ); - Some(handle) - } else { - None - }; - - return Ok(r::BufferView { - handle_srv, - handle_uav, - }); - } - - unsafe fn create_image( - &self, - kind: image::Kind, - mip_levels: image::Level, - format: format::Format, - tiling: image::Tiling, - usage: image::Usage, - sparse: memory::SparseFlags, - view_caps: image::ViewCapabilities, - ) -> Result { - assert!(mip_levels <= kind.compute_num_levels()); - - let base_format = format.base_format(); - let format_desc = base_format.0.desc(); - let bytes_per_block = (format_desc.bits / 8) as _; - let block_dim = format_desc.dim; - let view_format = conv::map_format(format); - let extent = kind.extent(); - - let format_info = self.format_properties.resolve(format as usize); - let (layout, features) = if sparse.contains(memory::SparseFlags::SPARSE_BINDING) { - ( - d3d12::D3D12_TEXTURE_LAYOUT_64KB_UNDEFINED_SWIZZLE, - format_info.properties.optimal_tiling, - ) - } else { - match tiling { - image::Tiling::Optimal => ( - d3d12::D3D12_TEXTURE_LAYOUT_UNKNOWN, - format_info.properties.optimal_tiling, - ), - image::Tiling::Linear => ( - d3d12::D3D12_TEXTURE_LAYOUT_ROW_MAJOR, - format_info.properties.linear_tiling, - ), - } - }; - if format_info.sample_count_mask & kind.num_samples() == 0 { - return Err(image::CreationError::Samples(kind.num_samples())); - } - - let desc = d3d12::D3D12_RESOURCE_DESC { - Dimension: match kind { - image::Kind::D1(..) => d3d12::D3D12_RESOURCE_DIMENSION_TEXTURE1D, - image::Kind::D2(..) => d3d12::D3D12_RESOURCE_DIMENSION_TEXTURE2D, - image::Kind::D3(..) => d3d12::D3D12_RESOURCE_DIMENSION_TEXTURE3D, - }, - Alignment: 0, - Width: extent.width as _, - Height: extent.height as _, - DepthOrArraySize: if extent.depth > 1 { - extent.depth as _ - } else { - kind.num_layers() as _ - }, - MipLevels: mip_levels as _, - Format: if format_desc.is_compressed() { - view_format.unwrap() - } else { - match conv::map_surface_type(base_format.0) { - Some(format) => format, - None => return Err(image::CreationError::Format(format)), - } - }, - SampleDesc: dxgitype::DXGI_SAMPLE_DESC { - Count: kind.num_samples() as _, - Quality: 0, - }, - Layout: layout, - Flags: conv::map_image_flags(usage, features), - }; - - let alloc_info = self.raw.clone().GetResourceAllocationInfo(0, 1, &desc); - - // Image flags which require RT/DS heap due to internal implementation. - let target_flags = d3d12::D3D12_RESOURCE_FLAG_ALLOW_RENDER_TARGET - | d3d12::D3D12_RESOURCE_FLAG_ALLOW_DEPTH_STENCIL; - let type_mask_shift = if self.private_caps.heterogeneous_resource_heaps { - MEM_TYPE_UNIVERSAL_SHIFT - } else if desc.Flags & target_flags != 0 { - MEM_TYPE_TARGET_SHIFT - } else { - MEM_TYPE_IMAGE_SHIFT - }; - - Ok(r::Image::Unbound(r::ImageUnbound { - view_format, - dsv_format: conv::map_format_dsv(base_format.0), - desc, - requirements: memory::Requirements { - size: alloc_info.SizeInBytes, - alignment: alloc_info.Alignment, - type_mask: MEM_TYPE_MASK << type_mask_shift, - }, - format, - kind, - mip_levels, - usage, - tiling, - view_caps, - bytes_per_block, - block_dim, - name: None, - })) - } - - unsafe fn get_image_requirements(&self, image: &r::Image) -> Requirements { - match image { - r::Image::Bound(i) => i.requirements, - r::Image::Unbound(i) => i.requirements, - } - } - - unsafe fn get_image_subresource_footprint( - &self, - image: &r::Image, - sub: image::Subresource, - ) -> image::SubresourceFootprint { - let mut num_rows = 0; - let mut total_bytes = 0; - let _desc = match image { - r::Image::Bound(i) => i.descriptor, - r::Image::Unbound(i) => i.desc, - }; - let footprint = { - let mut footprint = mem::zeroed(); - self.raw.GetCopyableFootprints( - image.get_desc(), - image.calc_subresource(sub.level as _, sub.layer as _, 0), - 1, - 0, - &mut footprint, - &mut num_rows, - ptr::null_mut(), // row size in bytes - &mut total_bytes, - ); - footprint - }; - - let depth_pitch = (footprint.Footprint.RowPitch * num_rows) as buffer::Offset; - let array_pitch = footprint.Footprint.Depth as buffer::Offset * depth_pitch; - image::SubresourceFootprint { - slice: footprint.Offset..footprint.Offset + total_bytes, - row_pitch: footprint.Footprint.RowPitch as _, - depth_pitch, - array_pitch, - } - } - - unsafe fn bind_image_memory( - &self, - memory: &r::Memory, - offset: u64, - image: &mut r::Image, - ) -> Result<(), d::BindError> { - let image_unbound = image.expect_unbound(); - if image_unbound.requirements.type_mask & (1 << memory.type_id) == 0 { - error!( - "Bind memory failure: supported mask 0x{:x}, given id {}", - image_unbound.requirements.type_mask, memory.type_id - ); - return Err(d::BindError::WrongMemory); - } - if offset + image_unbound.requirements.size > memory.size { - return Err(d::BindError::OutOfBounds); - } - - let mut resource = native::Resource::null(); - - assert_eq!( - winerror::S_OK, - self.raw.clone().CreatePlacedResource( - memory.heap.as_mut_ptr(), - offset, - &image_unbound.desc, - d3d12::D3D12_RESOURCE_STATE_COMMON, - ptr::null(), - &d3d12::ID3D12Resource::uuidof(), - resource.mut_void(), - ) - ); - - if let Some(ref name) = image_unbound.name { - resource.SetName(name.as_ptr()); - } - - self.bind_image_resource( - resource, - image, - r::Place::Heap { - raw: memory.heap.clone(), - offset, - }, - ); - - Ok(()) - } - - unsafe fn create_image_view( - &self, - image: &r::Image, - view_kind: image::ViewKind, - format: format::Format, - swizzle: format::Swizzle, - usage: image::Usage, - range: image::SubresourceRange, - ) -> Result { - let image = image.expect_bound(); - let is_array = image.kind.num_layers() > 1; - let mip_levels = ( - range.level_start, - range.level_start + range.resolve_level_count(image.mip_levels), - ); - let layers = ( - range.layer_start, - range.layer_start + range.resolve_layer_count(image.kind.num_layers()), - ); - let surface_format = format.base_format().0; - - let info = ViewInfo { - resource: image.resource, - kind: image.kind, - caps: image.view_caps, - // D3D12 doesn't allow looking at a single slice of an array as a non-array - view_kind: if is_array && view_kind == image::ViewKind::D2 { - image::ViewKind::D2Array - } else if is_array && view_kind == image::ViewKind::D1 { - image::ViewKind::D1Array - } else { - view_kind - }, - format: conv::map_format(format).ok_or(image::ViewCreationError::BadFormat(format))?, - component_mapping: conv::map_swizzle(swizzle), - levels: mip_levels.0..mip_levels.1, - layers: layers.0..layers.1, - }; - - //Note: we allow RTV/DSV/SRV/UAV views to fail to be created here, - // because we don't know if the user will even need to use them. - //Update: now we have `usage`, but some of the users (like `wgpu`) - // still don't know ahead of time what it needs to be. - - Ok(r::ImageView { - resource: image.resource, - handle_srv: if usage.intersects(image::Usage::SAMPLED | image::Usage::INPUT_ATTACHMENT) - { - let info = if range.aspects.contains(format::Aspects::DEPTH) { - conv::map_format_shader_depth(surface_format).map(|format| ViewInfo { - format, - ..info.clone() - }) - } else if range.aspects.contains(format::Aspects::STENCIL) { - // Vulkan/gfx expects stencil to be read from the R channel, - // while DX12 exposes it in "G" always. - let new_swizzle = conv::swizzle_rg(swizzle); - conv::map_format_shader_stencil(surface_format).map(|format| ViewInfo { - format, - component_mapping: conv::map_swizzle(new_swizzle), - ..info.clone() - }) - } else { - Some(info.clone()) - }; - if let Some(ref info) = info { - self.view_image_as_shader_resource(&info).ok() - } else { - None - } - } else { - None - }, - handle_rtv: if usage.contains(image::Usage::COLOR_ATTACHMENT) { - // This view is not necessarily going to be rendered to, even - // if the image supports that in general. - match self.view_image_as_render_target(&info) { - Ok(handle) => r::RenderTargetHandle::Pool(handle), - Err(_) => r::RenderTargetHandle::None, - } - } else { - r::RenderTargetHandle::None - }, - handle_uav: if usage.contains(image::Usage::STORAGE) { - self.view_image_as_storage(&info).ok() - } else { - None - }, - handle_dsv: if usage.contains(image::Usage::DEPTH_STENCIL_ATTACHMENT) { - match conv::map_format_dsv(surface_format) { - Some(dsv_format) => self - .view_image_as_depth_stencil(&ViewInfo { - format: dsv_format, - ..info - }) - .ok(), - None => None, - } - } else { - None - }, - dxgi_format: image.default_view_format.unwrap(), - num_levels: image.descriptor.MipLevels as image::Level, - mip_levels, - layers, - kind: info.kind, - }) - } - - unsafe fn create_sampler( - &self, - info: &image::SamplerDesc, - ) -> Result { - if !info.normalized { - warn!("Sampler with unnormalized coordinates is not supported!"); - } - let handle = match self.samplers.map.lock().entry(info.clone()) { - Entry::Occupied(e) => *e.get(), - Entry::Vacant(e) => { - let handle = self.samplers.pool.lock().alloc_handle(); - let info = e.key(); - let op = match info.comparison { - Some(_) => d3d12::D3D12_FILTER_REDUCTION_TYPE_COMPARISON, - None => d3d12::D3D12_FILTER_REDUCTION_TYPE_STANDARD, - }; - self.raw.create_sampler( - handle.raw, - conv::map_filter( - info.mag_filter, - info.min_filter, - info.mip_filter, - op, - info.anisotropy_clamp, - ), - [ - conv::map_wrap(info.wrap_mode.0), - conv::map_wrap(info.wrap_mode.1), - conv::map_wrap(info.wrap_mode.2), - ], - info.lod_bias.0, - info.anisotropy_clamp.map_or(0, |aniso| aniso as u32), - conv::map_comparison(info.comparison.unwrap_or(pso::Comparison::Always)), - info.border.into(), - info.lod_range.start.0..info.lod_range.end.0, - ); - *e.insert(handle) - } - }; - Ok(r::Sampler { handle }) - } - - unsafe fn create_descriptor_pool( - &self, - max_sets: usize, - ranges: I, - _flags: pso::DescriptorPoolCreateFlags, - ) -> Result - where - I: Iterator, - { - // Descriptor pools are implemented as slices of the global descriptor heaps. - // A descriptor pool will occupy a contiguous space in each heap (CBV/SRV/UAV and Sampler) depending - // on the total requested amount of descriptors. - - let mut num_srv_cbv_uav = 0; - let mut num_samplers = 0; - - let ranges = ranges.collect::>(); - - info!("create_descriptor_pool with {} max sets", max_sets); - for desc in &ranges { - let content = r::DescriptorContent::from(desc.ty); - debug!("\tcontent {:?}", content); - if content.contains(r::DescriptorContent::CBV) { - num_srv_cbv_uav += desc.count; - } - if content.contains(r::DescriptorContent::SRV) { - num_srv_cbv_uav += desc.count; - } - if content.contains(r::DescriptorContent::UAV) { - num_srv_cbv_uav += desc.count; - } - if content.contains(r::DescriptorContent::SAMPLER) { - num_samplers += desc.count; - } - } - - info!( - "total {} views and {} samplers", - num_srv_cbv_uav, num_samplers - ); - - // Allocate slices of the global GPU descriptor heaps. - let heap_srv_cbv_uav = { - let view_heap = &self.heap_srv_cbv_uav.0; - - let range = match num_srv_cbv_uav { - 0 => 0..0, - _ => self - .heap_srv_cbv_uav - .1 - .lock() - .allocate_range(num_srv_cbv_uav as _) - .map_err(|e| { - warn!("View pool allocation error: {:?}", e); - d::OutOfMemory::Host - })?, - }; - - r::DescriptorHeapSlice { - heap: view_heap.raw.clone(), - handle_size: view_heap.handle_size as _, - range_allocator: RangeAllocator::new(range), - start: view_heap.start, - } - }; - - Ok(r::DescriptorPool { - heap_srv_cbv_uav, - heap_raw_sampler: self.samplers.heap.raw, - pools: ranges, - max_size: max_sets as _, - }) - } - - unsafe fn create_descriptor_set_layout<'a, I, J>( - &self, - bindings: I, - _immutable_samplers: J, - ) -> Result - where - I: Iterator, - J: Iterator, - { - Ok(r::DescriptorSetLayout { - bindings: bindings.collect(), - }) - } - - unsafe fn write_descriptor_set<'a, I>(&self, op: pso::DescriptorSetWrite<'a, B, I>) - where - I: Iterator>, - { - let mut descriptor_updater = self.descriptor_updater.lock(); - descriptor_updater.reset(); - - let mut accum = descriptors_cpu::MultiCopyAccumulator::default(); - debug!("write_descriptor_set"); - - let mut offset = op.array_offset as u64; - let mut target_binding = op.binding as usize; - let base_sampler_offset = op.set.sampler_offset(op.binding, op.array_offset); - trace!("\tsampler offset {}", base_sampler_offset); - let mut sampler_offset = base_sampler_offset; - debug!("\tbinding {} array offset {}", target_binding, offset); - - for descriptor in op.descriptors { - // spill over the writes onto the next binding - while offset >= op.set.binding_infos[target_binding].count { - target_binding += 1; - offset = 0; - } - let bind_info = &mut op.set.binding_infos[target_binding]; - let mut src_cbv = None; - let mut src_srv = None; - let mut src_uav = None; - - match descriptor { - pso::Descriptor::Buffer(buffer, ref sub) => { - let buffer = buffer.expect_bound(); - - if bind_info.content.is_dynamic() { - // Root Descriptor - let buffer_address = (*buffer.resource).GetGPUVirtualAddress(); - // Descriptor sets need to be externally synchronized according to specification - bind_info.dynamic_descriptors[offset as usize].gpu_buffer_location = - buffer_address + sub.offset; - } else { - // Descriptor table - let size = sub.size_to(buffer.requirements.size); - - if bind_info.content.contains(r::DescriptorContent::CBV) { - // Making the size field of buffer requirements for uniform - // buffers a multiple of 256 and setting the required offset - // alignment to 256 allows us to patch the size here. - // We can always enforce the size to be aligned to 256 for - // CBVs without going out-of-bounds. - let mask = d3d12::D3D12_CONSTANT_BUFFER_DATA_PLACEMENT_ALIGNMENT - 1; - let desc = d3d12::D3D12_CONSTANT_BUFFER_VIEW_DESC { - BufferLocation: (*buffer.resource).GetGPUVirtualAddress() - + sub.offset, - SizeInBytes: (size as u32 + mask) as u32 & !mask, - }; - let handle = descriptor_updater.alloc_handle(self.raw); - self.raw.CreateConstantBufferView(&desc, handle); - src_cbv = Some(handle); - } - if bind_info.content.contains(r::DescriptorContent::SRV) { - assert_eq!(size % 4, 0); - let mut desc = d3d12::D3D12_SHADER_RESOURCE_VIEW_DESC { - Format: dxgiformat::DXGI_FORMAT_R32_TYPELESS, - Shader4ComponentMapping: IDENTITY_MAPPING, - ViewDimension: d3d12::D3D12_SRV_DIMENSION_BUFFER, - u: mem::zeroed(), - }; - *desc.u.Buffer_mut() = d3d12::D3D12_BUFFER_SRV { - FirstElement: sub.offset as _, - NumElements: (size / 4) as _, - StructureByteStride: 0, - Flags: d3d12::D3D12_BUFFER_SRV_FLAG_RAW, - }; - let handle = descriptor_updater.alloc_handle(self.raw); - self.raw.CreateShaderResourceView( - buffer.resource.as_mut_ptr(), - &desc, - handle, - ); - src_srv = Some(handle); - } - if bind_info.content.contains(r::DescriptorContent::UAV) { - assert_eq!(size % 4, 0); - let mut desc = d3d12::D3D12_UNORDERED_ACCESS_VIEW_DESC { - Format: dxgiformat::DXGI_FORMAT_R32_TYPELESS, - ViewDimension: d3d12::D3D12_UAV_DIMENSION_BUFFER, - u: mem::zeroed(), - }; - *desc.u.Buffer_mut() = d3d12::D3D12_BUFFER_UAV { - FirstElement: sub.offset as _, - NumElements: (size / 4) as _, - StructureByteStride: 0, - CounterOffsetInBytes: 0, - Flags: d3d12::D3D12_BUFFER_UAV_FLAG_RAW, - }; - let handle = descriptor_updater.alloc_handle(self.raw); - self.raw.CreateUnorderedAccessView( - buffer.resource.as_mut_ptr(), - ptr::null_mut(), - &desc, - handle, - ); - src_uav = Some(handle); - } - } - } - pso::Descriptor::Image(image, _layout) => { - if bind_info.content.contains(r::DescriptorContent::SRV) { - src_srv = image.handle_srv.map(|h| h.raw); - } - if bind_info.content.contains(r::DescriptorContent::UAV) { - src_uav = image.handle_uav.map(|h| h.raw); - } - } - pso::Descriptor::CombinedImageSampler(image, _layout, sampler) => { - src_srv = image.handle_srv.map(|h| h.raw); - op.set.sampler_origins[sampler_offset] = sampler.handle.raw; - sampler_offset += 1; - } - pso::Descriptor::Sampler(sampler) => { - op.set.sampler_origins[sampler_offset] = sampler.handle.raw; - sampler_offset += 1; - } - pso::Descriptor::TexelBuffer(buffer_view) => { - if bind_info.content.contains(r::DescriptorContent::SRV) { - let handle = buffer_view.handle_srv - .expect("SRV handle of the storage texel buffer is zero (not supported by specified format)"); - src_srv = Some(handle.raw); - } - if bind_info.content.contains(r::DescriptorContent::UAV) { - let handle = buffer_view.handle_uav - .expect("UAV handle of the storage texel buffer is zero (not supported by specified format)"); - src_uav = Some(handle.raw); - } - } - } - - if let Some(handle) = src_cbv { - trace!("\tcbv offset {}", offset); - accum.src_views.add(handle, 1); - accum - .dst_views - .add(bind_info.view_range.as_ref().unwrap().at(offset), 1); - } - if let Some(handle) = src_srv { - trace!("\tsrv offset {}", offset); - accum.src_views.add(handle, 1); - accum - .dst_views - .add(bind_info.view_range.as_ref().unwrap().at(offset), 1); - } - if let Some(handle) = src_uav { - let uav_offset = if bind_info.content.contains(r::DescriptorContent::SRV) { - bind_info.count + offset - } else { - offset - }; - trace!("\tuav offset {}", uav_offset); - accum.src_views.add(handle, 1); - accum - .dst_views - .add(bind_info.view_range.as_ref().unwrap().at(uav_offset), 1); - } - - offset += 1; - } - - if sampler_offset != base_sampler_offset { - op.set - .update_samplers(&self.samplers.heap, &self.samplers.origins, &mut accum); - } - - accum.flush(self.raw); - } - - unsafe fn copy_descriptor_set<'a>(&self, op: pso::DescriptorSetCopy<'a, B>) { - let mut accum = descriptors_cpu::MultiCopyAccumulator::default(); - - let src_info = &op.src_set.binding_infos[op.src_binding as usize]; - let dst_info = &op.dst_set.binding_infos[op.dst_binding as usize]; - - if let (Some(src_range), Some(dst_range)) = - (src_info.view_range.as_ref(), dst_info.view_range.as_ref()) - { - assert!(op.src_array_offset + op.count <= src_range.handle.size as usize); - assert!(op.dst_array_offset + op.count <= dst_range.handle.size as usize); - let count = op.count as u32; - accum - .src_views - .add(src_range.at(op.src_array_offset as _), count); - accum - .dst_views - .add(dst_range.at(op.dst_array_offset as _), count); - - if (src_info.content & dst_info.content) - .contains(r::DescriptorContent::SRV | r::DescriptorContent::UAV) - { - assert!( - src_info.count as usize + op.src_array_offset + op.count - <= src_range.handle.size as usize - ); - assert!( - dst_info.count as usize + op.dst_array_offset + op.count - <= dst_range.handle.size as usize - ); - accum.src_views.add( - src_range.at(src_info.count + op.src_array_offset as u64), - count, - ); - accum.dst_views.add( - dst_range.at(dst_info.count + op.dst_array_offset as u64), - count, - ); - } - } - - if dst_info.content.contains(r::DescriptorContent::SAMPLER) { - let src_offset = op - .src_set - .sampler_offset(op.src_binding, op.src_array_offset); - let dst_offset = op - .dst_set - .sampler_offset(op.dst_binding, op.dst_array_offset); - op.dst_set.sampler_origins[dst_offset..dst_offset + op.count] - .copy_from_slice(&op.src_set.sampler_origins[src_offset..src_offset + op.count]); - - op.dst_set - .update_samplers(&self.samplers.heap, &self.samplers.origins, &mut accum); - } - - accum.flush(self.raw.clone()); - } - - unsafe fn map_memory( - &self, - memory: &mut r::Memory, - segment: memory::Segment, - ) -> Result<*mut u8, d::MapError> { - let mem = memory - .resource - .expect("Memory not created with a memory type exposing `CPU_VISIBLE`"); - let mut ptr = ptr::null_mut(); - assert_eq!( - winerror::S_OK, - (*mem).Map(0, &d3d12::D3D12_RANGE { Begin: 0, End: 0 }, &mut ptr) - ); - ptr = ptr.offset(segment.offset as isize); - Ok(ptr as *mut _) - } - - unsafe fn unmap_memory(&self, memory: &mut r::Memory) { - if let Some(mem) = memory.resource { - (*mem).Unmap(0, &d3d12::D3D12_RANGE { Begin: 0, End: 0 }); - } - } - - unsafe fn flush_mapped_memory_ranges<'a, I>(&self, ranges: I) -> Result<(), d::OutOfMemory> - where - I: Iterator, - { - for (memory, ref segment) in ranges { - if let Some(mem) = memory.resource { - // map and immediately unmap, hoping that dx12 drivers internally cache - // currently mapped buffers. - assert_eq!( - winerror::S_OK, - (*mem).Map(0, &d3d12::D3D12_RANGE { Begin: 0, End: 0 }, ptr::null_mut()) - ); - - let start = segment.offset; - let end = segment.size.map_or(memory.size, |s| start + s); // TODO: only need to be end of current mapping - - (*mem).Unmap( - 0, - &d3d12::D3D12_RANGE { - Begin: start as _, - End: end as _, - }, - ); - } - } - - Ok(()) - } - - unsafe fn invalidate_mapped_memory_ranges<'a, I>(&self, ranges: I) -> Result<(), d::OutOfMemory> - where - I: Iterator, - { - for (memory, ref segment) in ranges { - if let Some(mem) = memory.resource { - let start = segment.offset; - let end = segment.size.map_or(memory.size, |s| start + s); // TODO: only need to be end of current mapping - - // map and immediately unmap, hoping that dx12 drivers internally cache - // currently mapped buffers. - assert_eq!( - winerror::S_OK, - (*mem).Map( - 0, - &d3d12::D3D12_RANGE { - Begin: start as _, - End: end as _, - }, - ptr::null_mut(), - ) - ); - - (*mem).Unmap(0, &d3d12::D3D12_RANGE { Begin: 0, End: 0 }); - } - } - - Ok(()) - } - - fn create_semaphore(&self) -> Result { - let fence = self.create_fence(false)?; - Ok(r::Semaphore { raw: fence.raw }) - } - - fn create_fence(&self, signalled: bool) -> Result { - Ok(r::Fence { - raw: self.create_raw_fence(signalled), - }) - } - - unsafe fn reset_fence(&self, fence: &mut r::Fence) -> Result<(), d::OutOfMemory> { - assert_eq!(winerror::S_OK, fence.raw.signal(0)); - Ok(()) - } - - unsafe fn wait_for_fences<'a, I>( - &self, - fences: I, - wait: d::WaitFor, - timeout_ns: u64, - ) -> Result - where - I: Iterator, - { - let mut count = 0; - let mut events = self.events.lock(); - - for fence in fences { - if count == events.len() { - events.push(native::Event::create(false, false)); - } - let event = events[count]; - synchapi::ResetEvent(event.0); - assert_eq!(winerror::S_OK, fence.raw.set_event_on_completion(event, 1)); - count += 1; - } - - let all = match wait { - d::WaitFor::Any => FALSE, - d::WaitFor::All => TRUE, - }; - - let hr = { - // This block handles overflow when converting to u32 and always rounds up - // The Vulkan specification allows to wait more than specified - let timeout_ms = { - if timeout_ns > (::MAX as u64) * 1_000_000 { - ::MAX - } else { - ((timeout_ns + 999_999) / 1_000_000) as u32 - } - }; - - synchapi::WaitForMultipleObjects( - count as u32, - events.as_ptr() as *const _, - all, - timeout_ms, - ) - }; - - const WAIT_OBJECT_LAST: u32 = winbase::WAIT_OBJECT_0 + winnt::MAXIMUM_WAIT_OBJECTS; - const WAIT_ABANDONED_LAST: u32 = winbase::WAIT_ABANDONED_0 + winnt::MAXIMUM_WAIT_OBJECTS; - match hr { - winbase::WAIT_OBJECT_0..=WAIT_OBJECT_LAST => Ok(true), - winbase::WAIT_ABANDONED_0..=WAIT_ABANDONED_LAST => Ok(true), //TODO? - winbase::WAIT_FAILED => Err(d::WaitError::DeviceLost(d::DeviceLost)), - winerror::WAIT_TIMEOUT => Ok(false), - _ => panic!("Unexpected wait status 0x{:X}", hr), - } - } - - unsafe fn get_fence_status(&self, fence: &r::Fence) -> Result { - match fence.raw.GetCompletedValue() { - 0 => Ok(false), - 1 => Ok(true), - _ => Err(d::DeviceLost), - } - } - - fn create_event(&self) -> Result<(), d::OutOfMemory> { - unimplemented!() - } - - unsafe fn get_event_status(&self, _event: &()) -> Result { - unimplemented!() - } - - unsafe fn set_event(&self, _event: &mut ()) -> Result<(), d::OutOfMemory> { - unimplemented!() - } - - unsafe fn reset_event(&self, _event: &mut ()) -> Result<(), d::OutOfMemory> { - unimplemented!() - } - - unsafe fn free_memory(&self, memory: r::Memory) { - memory.heap.destroy(); - if let Some(buffer) = memory.resource { - buffer.destroy(); - } - } - - unsafe fn create_query_pool( - &self, - query_ty: query::Type, - count: query::Id, - ) -> Result { - let heap_ty = match query_ty { - query::Type::Occlusion => native::QueryHeapType::Occlusion, - query::Type::PipelineStatistics(_) => native::QueryHeapType::PipelineStatistics, - query::Type::Timestamp => native::QueryHeapType::Timestamp, - }; - - let (query_heap, hr) = self.raw.create_query_heap(heap_ty, count, 0); - assert_eq!(winerror::S_OK, hr); - - Ok(r::QueryPool { - raw: query_heap, - ty: query_ty, - }) - } - - unsafe fn destroy_query_pool(&self, pool: r::QueryPool) { - pool.raw.destroy(); - } - - unsafe fn get_query_pool_results( - &self, - pool: &r::QueryPool, - queries: Range, - data: &mut [u8], - stride: buffer::Stride, - flags: query::ResultFlags, - ) -> Result { - let num_queries = queries.end - queries.start; - let size = 8 * num_queries as u64; - let buffer_desc = d3d12::D3D12_RESOURCE_DESC { - Dimension: d3d12::D3D12_RESOURCE_DIMENSION_BUFFER, - Alignment: 0, - Width: size, - Height: 1, - DepthOrArraySize: 1, - MipLevels: 1, - Format: dxgiformat::DXGI_FORMAT_UNKNOWN, - SampleDesc: dxgitype::DXGI_SAMPLE_DESC { - Count: 1, - Quality: 0, - }, - Layout: d3d12::D3D12_TEXTURE_LAYOUT_ROW_MAJOR, - Flags: d3d12::D3D12_RESOURCE_FLAG_NONE, - }; - - let properties = d3d12::D3D12_HEAP_PROPERTIES { - Type: d3d12::D3D12_HEAP_TYPE_READBACK, - CPUPageProperty: d3d12::D3D12_CPU_PAGE_PROPERTY_UNKNOWN, - MemoryPoolPreference: d3d12::D3D12_MEMORY_POOL_UNKNOWN, - CreationNodeMask: 0, - VisibleNodeMask: 0, - }; - - let heap_desc = d3d12::D3D12_HEAP_DESC { - SizeInBytes: size, - Properties: properties, - Alignment: 0, - Flags: d3d12::D3D12_HEAP_FLAG_ALLOW_ONLY_BUFFERS, - }; - - let mut heap = native::Heap::null(); - assert_eq!( - self.raw - .clone() - .CreateHeap(&heap_desc, &d3d12::ID3D12Heap::uuidof(), heap.mut_void()), - winerror::S_OK - ); - - let mut temp_buffer = native::Resource::null(); - assert_eq!( - winerror::S_OK, - self.raw.clone().CreatePlacedResource( - heap.as_mut_ptr(), - 0, - &buffer_desc, - d3d12::D3D12_RESOURCE_STATE_COPY_DEST, - ptr::null(), - &d3d12::ID3D12Resource::uuidof(), - temp_buffer.mut_void(), - ) - ); - - let list_type = native::CmdListType::Direct; - let (com_allocator, hr_alloc) = self.raw.create_command_allocator(list_type); - assert_eq!( - winerror::S_OK, - hr_alloc, - "error on command allocator creation: {:x}", - hr_alloc - ); - let (com_list, hr_list) = self.raw.create_graphics_command_list( - list_type, - com_allocator, - native::PipelineState::null(), - 0, - ); - assert_eq!( - winerror::S_OK, - hr_list, - "error on command list creation: {:x}", - hr_list - ); - - let query_ty = match pool.ty { - query::Type::Occlusion => d3d12::D3D12_QUERY_TYPE_OCCLUSION, - query::Type::PipelineStatistics(_) => d3d12::D3D12_QUERY_TYPE_PIPELINE_STATISTICS, - query::Type::Timestamp => d3d12::D3D12_QUERY_TYPE_TIMESTAMP, - }; - com_list.ResolveQueryData( - pool.raw.as_mut_ptr(), - query_ty, - queries.start, - num_queries, - temp_buffer.as_mut_ptr(), - 0, - ); - com_list.close(); - - self.queues[0] - .raw - .ExecuteCommandLists(1, &(com_list.as_mut_ptr() as *mut _)); - - if !flags.contains(query::ResultFlags::WAIT) { - warn!("Can't really not wait here") - } - let result = self.queues[0].wait_idle_impl(); - if result.is_ok() { - let mut ptr = ptr::null_mut(); - assert_eq!( - winerror::S_OK, - temp_buffer.Map(0, &d3d12::D3D12_RANGE { Begin: 0, End: 0 }, &mut ptr) - ); - let src_data = slice::from_raw_parts(ptr as *const u64, num_queries as usize); - for (i, &value) in src_data.iter().enumerate() { - let dst = data.as_mut_ptr().add(i * stride as usize); - if flags.contains(query::ResultFlags::BITS_64) { - *(dst as *mut u64) = value; - } else { - *(dst as *mut u32) = value as u32; - } - } - temp_buffer.Unmap(0, &d3d12::D3D12_RANGE { Begin: 0, End: 0 }); - } - - temp_buffer.destroy(); - heap.destroy(); - com_list.destroy(); - com_allocator.destroy(); - - match result { - Ok(()) => Ok(true), - Err(e) => Err(e.into()), - } - } - - unsafe fn destroy_shader_module(&self, shader_lib: r::ShaderModule) { - if let r::ShaderModule::Compiled(shaders) = shader_lib { - for (_, blob) in shaders { - blob.destroy(); - } - } - } - - unsafe fn destroy_render_pass(&self, _rp: r::RenderPass) { - // Just drop - } - - unsafe fn destroy_pipeline_layout(&self, layout: r::PipelineLayout) { - layout.shared.signature.destroy(); - } - - unsafe fn destroy_graphics_pipeline(&self, pipeline: r::GraphicsPipeline) { - pipeline.raw.destroy(); - } - - unsafe fn destroy_compute_pipeline(&self, pipeline: r::ComputePipeline) { - pipeline.raw.destroy(); - } - - unsafe fn destroy_framebuffer(&self, _fb: r::Framebuffer) { - // Just drop - } - - unsafe fn destroy_buffer(&self, buffer: r::Buffer) { - match buffer { - r::Buffer::Bound(buffer) => { - if let Some(handle) = buffer.clear_uav { - self.srv_uav_pool.lock().free_handle(handle); - } - buffer.resource.destroy(); - } - r::Buffer::Unbound(_) => {} - } - } - - unsafe fn destroy_buffer_view(&self, view: r::BufferView) { - let mut pool = self.srv_uav_pool.lock(); - if let Some(handle) = view.handle_srv { - pool.free_handle(handle); - } - if let Some(handle) = view.handle_uav { - pool.free_handle(handle); - } - } - - unsafe fn destroy_image(&self, image: r::Image) { - match image { - r::Image::Bound(image) => { - let mut dsv_pool = self.dsv_pool.lock(); - for handle in image.clear_cv { - self.rtv_pool.lock().free_handle(handle); - } - for handle in image.clear_dv { - dsv_pool.free_handle(handle); - } - for handle in image.clear_sv { - dsv_pool.free_handle(handle); - } - image.resource.destroy(); - } - r::Image::Unbound(_) => {} - } - } - - unsafe fn destroy_image_view(&self, view: r::ImageView) { - if let Some(handle) = view.handle_srv { - self.srv_uav_pool.lock().free_handle(handle); - } - if let Some(handle) = view.handle_uav { - self.srv_uav_pool.lock().free_handle(handle); - } - if let r::RenderTargetHandle::Pool(handle) = view.handle_rtv { - self.rtv_pool.lock().free_handle(handle); - } - if let Some(handle) = view.handle_dsv { - self.dsv_pool.lock().free_handle(handle); - } - } - - unsafe fn destroy_sampler(&self, _sampler: r::Sampler) { - // We don't destroy samplers, they are permanently cached - } - - unsafe fn destroy_descriptor_pool(&self, pool: r::DescriptorPool) { - let view_range = pool.heap_srv_cbv_uav.range_allocator.initial_range(); - if view_range.start < view_range.end { - self.heap_srv_cbv_uav - .1 - .lock() - .free_range(view_range.clone()); - } - } - - unsafe fn destroy_descriptor_set_layout(&self, _layout: r::DescriptorSetLayout) { - // Just drop - } - - unsafe fn destroy_fence(&self, fence: r::Fence) { - fence.raw.destroy(); - } - - unsafe fn destroy_semaphore(&self, semaphore: r::Semaphore) { - semaphore.raw.destroy(); - } - - unsafe fn destroy_event(&self, _event: ()) { - unimplemented!() - } - - fn wait_idle(&self) -> Result<(), d::OutOfMemory> { - for queue in &self.queues { - queue.wait_idle_impl()?; - } - Ok(()) - } - - unsafe fn set_image_name(&self, image: &mut r::Image, name: &str) { - let cwstr = wide_cstr(name); - match *image { - r::Image::Unbound(ref mut image) => image.name = Some(cwstr), - r::Image::Bound(ref image) => { - image.resource.SetName(cwstr.as_ptr()); - } - } - } - - unsafe fn set_buffer_name(&self, buffer: &mut r::Buffer, name: &str) { - let cwstr = wide_cstr(name); - match *buffer { - r::Buffer::Unbound(ref mut buffer) => buffer.name = Some(cwstr), - r::Buffer::Bound(ref buffer) => { - buffer.resource.SetName(cwstr.as_ptr()); - } - } - } - - unsafe fn set_command_buffer_name(&self, command_buffer: &mut cmd::CommandBuffer, name: &str) { - let cwstr = wide_cstr(name); - if !command_buffer.raw.is_null() { - command_buffer.raw.SetName(cwstr.as_ptr()); - } - command_buffer.raw_name = cwstr; - } - - unsafe fn set_semaphore_name(&self, semaphore: &mut r::Semaphore, name: &str) { - let cwstr = wide_cstr(name); - semaphore.raw.SetName(cwstr.as_ptr()); - } - - unsafe fn set_fence_name(&self, fence: &mut r::Fence, name: &str) { - let cwstr = wide_cstr(name); - fence.raw.SetName(cwstr.as_ptr()); - } - - unsafe fn set_framebuffer_name(&self, _framebuffer: &mut r::Framebuffer, _name: &str) { - // ignored - } - - unsafe fn set_render_pass_name(&self, render_pass: &mut r::RenderPass, name: &str) { - render_pass.raw_name = wide_cstr(name); - } - - unsafe fn set_descriptor_set_name(&self, descriptor_set: &mut r::DescriptorSet, name: &str) { - descriptor_set.raw_name = wide_cstr(name); - } - - unsafe fn set_descriptor_set_layout_name( - &self, - _descriptor_set_layout: &mut r::DescriptorSetLayout, - _name: &str, - ) { - // ignored - } - - unsafe fn set_pipeline_layout_name(&self, pipeline_layout: &mut r::PipelineLayout, name: &str) { - let cwstr = wide_cstr(name); - pipeline_layout.shared.signature.SetName(cwstr.as_ptr()); - } - - unsafe fn set_display_power_state( - &self, - _display: &display::Display, - _power_state: &display::control::PowerState, - ) -> Result<(), display::control::DisplayControlError> { - unimplemented!() - } - - unsafe fn register_device_event( - &self, - _device_event: &display::control::DeviceEvent, - _fence: &mut ::Fence, - ) -> Result<(), display::control::DisplayControlError> { - unimplemented!() - } - - unsafe fn register_display_event( - &self, - _display: &display::Display, - _display_event: &display::control::DisplayEvent, - _fence: &mut ::Fence, - ) -> Result<(), display::control::DisplayControlError> { - unimplemented!() - } - - unsafe fn create_allocate_external_buffer( - &self, - _external_memory_type: hal::external_memory::ExternalBufferMemoryType, - _usage: hal::buffer::Usage, - _sparse: hal::memory::SparseFlags, - _type_mask: u32, - _size: u64, - ) -> Result<(r::Buffer, r::Memory), hal::external_memory::ExternalResourceError> { - unimplemented!() - } - - unsafe fn import_external_buffer( - &self, - _external_memory: hal::external_memory::ExternalBufferMemory, - _usage: hal::buffer::Usage, - _sparse: hal::memory::SparseFlags, - _type_mask: u32, - _size: u64, - ) -> Result<(r::Buffer, r::Memory), hal::external_memory::ExternalResourceError> { - unimplemented!() - } - - unsafe fn create_allocate_external_image( - &self, - _external_memory_type: hal::external_memory::ExternalImageMemoryType, - _kind: hal::image::Kind, - _mip_levels: hal::image::Level, - _format: hal::format::Format, - _tiling: hal::image::Tiling, - _usage: hal::image::Usage, - _sparse: hal::memory::SparseFlags, - _view_caps: hal::image::ViewCapabilities, - _type_mask: u32, - ) -> Result<(r::Image, r::Memory), hal::external_memory::ExternalResourceError> { - unimplemented!() - } - - unsafe fn import_external_image( - &self, - _external_memory: hal::external_memory::ExternalImageMemory, - _kind: hal::image::Kind, - _mip_levels: hal::image::Level, - _format: hal::format::Format, - _tiling: hal::image::Tiling, - _usage: hal::image::Usage, - _sparse: hal::memory::SparseFlags, - _view_caps: hal::image::ViewCapabilities, - _type_mask: u32, - ) -> Result<(r::Image, r::Memory), hal::external_memory::ExternalResourceError> { - unimplemented!() - } - - unsafe fn export_memory( - &self, - _external_memory_type: hal::external_memory::ExternalMemoryType, - _memory: &r::Memory, - ) -> Result - { - unimplemented!() - } - - unsafe fn drm_format_modifier(&self, _image: &r::Image) -> Option { - None - } - - fn start_capture(&self) { - unsafe { - self.render_doc - .start_frame_capture(self.raw.as_mut_ptr() as *mut _, ptr::null_mut()) - } - } - - fn stop_capture(&self) { - unsafe { - self.render_doc - .end_frame_capture(self.raw.as_mut_ptr() as *mut _, ptr::null_mut()) - } - } -} - -#[test] -fn test_identity_mapping() { - assert_eq!(conv::map_swizzle(format::Swizzle::NO), IDENTITY_MAPPING); -} +use std::{collections::hash_map::Entry, ffi, iter, mem, ops::Range, ptr, slice, sync::Arc}; + +use range_alloc::RangeAllocator; +use smallvec::SmallVec; +use spirv_cross::{hlsl, spirv, ErrorCode as SpirvErrorCode}; + +use winapi::{ + shared::{ + dxgi, dxgi1_2, dxgi1_4, dxgiformat, dxgitype, + minwindef::{FALSE, TRUE, UINT}, + windef, winerror, + }, + um::{d3d12, d3dcompiler, synchapi, winbase, winnt}, + Interface, +}; + +use auxil::{spirv_cross_specialize_ast, ShaderStage}; +use hal::{ + buffer, device as d, format, format::Aspects, image, memory, memory::Requirements, pass, + pool::CommandPoolCreateFlags, pso, pso::VertexInputRate, query, queue::QueueFamilyId, + window as w, +}; + +use crate::{ + command as cmd, conv, descriptors_cpu, pool::CommandPool, resource as r, root_constants, + root_constants::RootConstant, window::Swapchain, Backend as B, Device, MemoryGroup, + MAX_VERTEX_BUFFERS, NUM_HEAP_PROPERTIES, QUEUE_FAMILIES, +}; +use native::{PipelineStateSubobject, Subobject}; + +// Register space used for root constants. +const ROOT_CONSTANT_SPACE: u32 = 0; + +const MEM_TYPE_MASK: u32 = 0x7; +const MEM_TYPE_SHIFT: u32 = 3; + +const MEM_TYPE_UNIVERSAL_SHIFT: u32 = MEM_TYPE_SHIFT * MemoryGroup::Universal as u32; +const MEM_TYPE_BUFFER_SHIFT: u32 = MEM_TYPE_SHIFT * MemoryGroup::BufferOnly as u32; +const MEM_TYPE_IMAGE_SHIFT: u32 = MEM_TYPE_SHIFT * MemoryGroup::ImageOnly as u32; +const MEM_TYPE_TARGET_SHIFT: u32 = MEM_TYPE_SHIFT * MemoryGroup::TargetOnly as u32; + +pub const IDENTITY_MAPPING: UINT = 0x1688; // D3D12_DEFAULT_SHADER_4_COMPONENT_MAPPING + +fn wide_cstr(name: &str) -> Vec { + name.encode_utf16().chain(iter::once(0)).collect() +} + +/// Emit error during shader module creation. Used if we don't expect an error +/// but might panic due to an exception in SPIRV-Cross. +fn gen_unexpected_error(stage: ShaderStage, err: SpirvErrorCode) -> pso::CreationError { + let msg = match err { + SpirvErrorCode::CompilationError(msg) => msg, + SpirvErrorCode::Unhandled => "Unexpected error".into(), + }; + let error = format!("SPIR-V unexpected error {:?}", msg); + pso::CreationError::ShaderCreationError(stage.to_flag(), error) +} + +/// Emit error during shader module creation. Used if we execute an query command. +fn gen_query_error(stage: ShaderStage, err: SpirvErrorCode) -> pso::CreationError { + let msg = match err { + SpirvErrorCode::CompilationError(msg) => msg, + SpirvErrorCode::Unhandled => "Unknown query error".into(), + }; + let error = format!("SPIR-V query error {:?}", msg); + pso::CreationError::ShaderCreationError(stage.to_flag(), error) +} + +#[derive(Clone, Debug)] +pub(crate) struct ViewInfo { + pub(crate) resource: native::Resource, + pub(crate) kind: image::Kind, + pub(crate) caps: image::ViewCapabilities, + pub(crate) view_kind: image::ViewKind, + pub(crate) format: dxgiformat::DXGI_FORMAT, + pub(crate) component_mapping: UINT, + pub(crate) levels: Range, + pub(crate) layers: Range, +} + +pub(crate) enum CommandSignature { + Draw, + DrawIndexed, + Dispatch, +} + +/// Compile a single shader entry point from a HLSL text shader +pub(crate) fn compile_shader( + stage: ShaderStage, + shader_model: hlsl::ShaderModel, + features: &hal::Features, + entry: &str, + code: &[u8], +) -> Result { + let stage_str = match stage { + ShaderStage::Vertex => "vs", + ShaderStage::Fragment => "ps", + ShaderStage::Compute => "cs", + _ => unimplemented!(), + }; + let model_str = match shader_model { + hlsl::ShaderModel::V5_0 => "5_0", + hlsl::ShaderModel::V5_1 => "5_1", + hlsl::ShaderModel::V6_0 => "6_0", + _ => unimplemented!(), + }; + let full_stage = format!("{}_{}\0", stage_str, model_str); + + let mut shader_data = native::Blob::null(); + let mut error = native::Blob::null(); + let entry = ffi::CString::new(entry).unwrap(); + let mut compile_flags = d3dcompiler::D3DCOMPILE_ENABLE_STRICTNESS; + if cfg!(debug_assertions) { + compile_flags |= d3dcompiler::D3DCOMPILE_DEBUG; + } + if features.contains(hal::Features::UNSIZED_DESCRIPTOR_ARRAY) { + compile_flags |= d3dcompiler::D3DCOMPILE_ENABLE_UNBOUNDED_DESCRIPTOR_TABLES; + } + let hr = unsafe { + d3dcompiler::D3DCompile( + code.as_ptr() as *const _, + code.len(), + ptr::null(), + ptr::null(), + ptr::null_mut(), + entry.as_ptr() as *const _, + full_stage.as_ptr() as *const i8, + compile_flags, + 0, + shader_data.mut_void() as *mut *mut _, + error.mut_void() as *mut *mut _, + ) + }; + if !winerror::SUCCEEDED(hr) { + let message = unsafe { + let pointer = error.GetBufferPointer(); + let size = error.GetBufferSize(); + let slice = slice::from_raw_parts(pointer as *const u8, size as usize); + String::from_utf8_lossy(slice).into_owned() + }; + unsafe { + error.destroy(); + } + let error = format!("D3DCompile error {:x}: {}", hr, message); + Err(pso::CreationError::ShaderCreationError( + stage.to_flag(), + error, + )) + } else { + Ok(shader_data) + } +} + +#[repr(C)] +struct GraphicsPipelineStateSubobjectStream { + root_signature: PipelineStateSubobject<*mut d3d12::ID3D12RootSignature>, + vs: PipelineStateSubobject, + ps: PipelineStateSubobject, + ds: PipelineStateSubobject, + hs: PipelineStateSubobject, + gs: PipelineStateSubobject, + stream_output: PipelineStateSubobject, + blend: PipelineStateSubobject, + sample_mask: PipelineStateSubobject, + rasterizer: PipelineStateSubobject, + depth_stencil: PipelineStateSubobject, + input_layout: PipelineStateSubobject, + ib_strip_cut_value: PipelineStateSubobject, + primitive_topology: PipelineStateSubobject, + render_target_formats: PipelineStateSubobject, + depth_stencil_format: PipelineStateSubobject, + sample_desc: PipelineStateSubobject, + node_mask: PipelineStateSubobject, + cached_pso: PipelineStateSubobject, + flags: PipelineStateSubobject, +} + +impl GraphicsPipelineStateSubobjectStream { + fn new( + pso_desc: &d3d12::D3D12_GRAPHICS_PIPELINE_STATE_DESC, + depth_bounds_test_enable: bool, + ) -> Self { + GraphicsPipelineStateSubobjectStream { + root_signature: PipelineStateSubobject::new( + Subobject::RootSignature, + pso_desc.pRootSignature, + ), + vs: PipelineStateSubobject::new(Subobject::VS, pso_desc.VS), + ps: PipelineStateSubobject::new(Subobject::PS, pso_desc.PS), + ds: PipelineStateSubobject::new(Subobject::DS, pso_desc.DS), + hs: PipelineStateSubobject::new(Subobject::HS, pso_desc.HS), + gs: PipelineStateSubobject::new(Subobject::GS, pso_desc.GS), + stream_output: PipelineStateSubobject::new( + Subobject::StreamOutput, + pso_desc.StreamOutput, + ), + blend: PipelineStateSubobject::new(Subobject::Blend, pso_desc.BlendState), + sample_mask: PipelineStateSubobject::new(Subobject::SampleMask, pso_desc.SampleMask), + rasterizer: PipelineStateSubobject::new( + Subobject::Rasterizer, + pso_desc.RasterizerState, + ), + depth_stencil: PipelineStateSubobject::new( + Subobject::DepthStencil1, + d3d12::D3D12_DEPTH_STENCIL_DESC1 { + DepthEnable: pso_desc.DepthStencilState.DepthEnable, + DepthWriteMask: pso_desc.DepthStencilState.DepthWriteMask, + DepthFunc: pso_desc.DepthStencilState.DepthFunc, + StencilEnable: pso_desc.DepthStencilState.StencilEnable, + StencilReadMask: pso_desc.DepthStencilState.StencilReadMask, + StencilWriteMask: pso_desc.DepthStencilState.StencilWriteMask, + FrontFace: pso_desc.DepthStencilState.FrontFace, + BackFace: pso_desc.DepthStencilState.BackFace, + DepthBoundsTestEnable: depth_bounds_test_enable as _, + }, + ), + input_layout: PipelineStateSubobject::new(Subobject::InputLayout, pso_desc.InputLayout), + ib_strip_cut_value: PipelineStateSubobject::new( + Subobject::IBStripCut, + pso_desc.IBStripCutValue, + ), + primitive_topology: PipelineStateSubobject::new( + Subobject::PrimitiveTopology, + pso_desc.PrimitiveTopologyType, + ), + render_target_formats: PipelineStateSubobject::new( + Subobject::RTFormats, + d3d12::D3D12_RT_FORMAT_ARRAY { + RTFormats: pso_desc.RTVFormats, + NumRenderTargets: pso_desc.NumRenderTargets, + }, + ), + depth_stencil_format: PipelineStateSubobject::new( + Subobject::DSFormat, + pso_desc.DSVFormat, + ), + sample_desc: PipelineStateSubobject::new(Subobject::SampleDesc, pso_desc.SampleDesc), + node_mask: PipelineStateSubobject::new(Subobject::NodeMask, pso_desc.NodeMask), + cached_pso: PipelineStateSubobject::new(Subobject::CachedPSO, pso_desc.CachedPSO), + flags: PipelineStateSubobject::new(Subobject::Flags, pso_desc.Flags), + } + } +} + +impl Device { + fn parse_spirv( + stage: ShaderStage, + raw_data: &[u32], + ) -> Result, pso::CreationError> { + let module = spirv::Module::from_words(raw_data); + + spirv::Ast::parse(&module).map_err(|err| { + let msg = match err { + SpirvErrorCode::CompilationError(msg) => msg, + SpirvErrorCode::Unhandled => "Unknown parsing error".into(), + }; + let error = format!("SPIR-V parsing failed: {:?}", msg); + pso::CreationError::ShaderCreationError(stage.to_flag(), error) + }) + } + + /// Introspects the input attributes of given SPIR-V shader and returns an optional vertex semantic remapping. + /// + /// The returned hashmap has attribute location as a key and an Optional remapping to a two part semantic. + /// + /// eg. + /// `2 -> None` means use default semantic `TEXCOORD2` + /// `2 -> Some((0, 2))` means use two part semantic `TEXCOORD0_2`. This is how matrices are represented by spirv-cross. + /// + /// This is a temporary workaround for https://github.com/KhronosGroup/SPIRV-Cross/issues/1512. + /// + /// This workaround also exists under the same name in the DX11 backend. + pub(crate) fn introspect_spirv_vertex_semantic_remapping( + raw_data: &[u32], + ) -> Result>, pso::CreationError> { + const SHADER_STAGE: ShaderStage = ShaderStage::Vertex; + // This is inefficient as we already parse it once before. This is a temporary workaround only called + // on vertex shaders. If this becomes permanent or shows up in profiles, deduplicate these as first course of action. + let ast = Self::parse_spirv(SHADER_STAGE, raw_data)?; + + let mut map = auxil::FastHashMap::default(); + + let inputs = ast + .get_shader_resources() + .map_err(|err| gen_query_error(SHADER_STAGE, err))? + .stage_inputs; + for input in inputs { + let idx = ast + .get_decoration(input.id, spirv::Decoration::Location) + .map_err(|err| gen_query_error(SHADER_STAGE, err))?; + + let ty = ast + .get_type(input.type_id) + .map_err(|err| gen_query_error(SHADER_STAGE, err))?; + + match ty { + spirv::Type::Boolean { columns, .. } + | spirv::Type::Int { columns, .. } + | spirv::Type::UInt { columns, .. } + | spirv::Type::Half { columns, .. } + | spirv::Type::Float { columns, .. } + | spirv::Type::Double { columns, .. } + if columns > 1 => + { + for col in 0..columns { + if let Some(_) = map.insert(idx + col, Some((idx, col))) { + let error = format!( + "Shader has overlapping input attachments at location {}", + idx + ); + return Err(pso::CreationError::ShaderCreationError( + SHADER_STAGE.to_flag(), + error, + )); + } + } + } + _ => { + if let Some(_) = map.insert(idx, None) { + let error = format!( + "Shader has overlapping input attachments at location {}", + idx + ); + return Err(pso::CreationError::ShaderCreationError( + SHADER_STAGE.to_flag(), + error, + )); + } + } + } + } + + Ok(map) + } + + fn patch_spirv_resources( + stage: ShaderStage, + ast: &mut spirv::Ast, + layout: &r::PipelineLayout, + ) -> Result<(), pso::CreationError> { + // Move the descriptor sets away to yield for the root constants at "space0". + let space_offset = if layout.shared.constants.is_empty() { + 0 + } else { + 1 + }; + let shader_resources = ast + .get_shader_resources() + .map_err(|err| gen_query_error(stage, err))?; + + if space_offset != 0 { + for image in &shader_resources.separate_images { + let set = ast + .get_decoration(image.id, spirv::Decoration::DescriptorSet) + .map_err(|err| gen_query_error(stage, err))?; + ast.set_decoration( + image.id, + spirv::Decoration::DescriptorSet, + space_offset + set, + ) + .map_err(|err| gen_unexpected_error(stage, err))?; + } + } + + if space_offset != 0 { + for uniform_buffer in &shader_resources.uniform_buffers { + let set = ast + .get_decoration(uniform_buffer.id, spirv::Decoration::DescriptorSet) + .map_err(|err| gen_query_error(stage, err))?; + ast.set_decoration( + uniform_buffer.id, + spirv::Decoration::DescriptorSet, + space_offset + set, + ) + .map_err(|err| gen_unexpected_error(stage, err))?; + } + } + + for storage_buffer in &shader_resources.storage_buffers { + let set = ast + .get_decoration(storage_buffer.id, spirv::Decoration::DescriptorSet) + .map_err(|err| gen_query_error(stage, err))?; + let binding = ast + .get_decoration(storage_buffer.id, spirv::Decoration::Binding) + .map_err(|err| gen_query_error(stage, err))?; + if space_offset != 0 { + ast.set_decoration( + storage_buffer.id, + spirv::Decoration::DescriptorSet, + space_offset + set, + ) + .map_err(|err| gen_unexpected_error(stage, err))?; + } + if !layout.elements[set as usize] + .mutable_bindings + .contains(&binding) + { + ast.set_decoration(storage_buffer.id, spirv::Decoration::NonWritable, 1) + .map_err(|err| gen_unexpected_error(stage, err))? + } + } + + for image in &shader_resources.storage_images { + let set = ast + .get_decoration(image.id, spirv::Decoration::DescriptorSet) + .map_err(|err| gen_query_error(stage, err))?; + let binding = ast + .get_decoration(image.id, spirv::Decoration::Binding) + .map_err(|err| gen_query_error(stage, err))?; + if space_offset != 0 { + ast.set_decoration( + image.id, + spirv::Decoration::DescriptorSet, + space_offset + set, + ) + .map_err(|err| gen_unexpected_error(stage, err))?; + } + if !layout.elements[set as usize] + .mutable_bindings + .contains(&binding) + { + ast.set_decoration(image.id, spirv::Decoration::NonWritable, 1) + .map_err(|err| gen_unexpected_error(stage, err))? + } + } + + if space_offset != 0 { + for sampler in &shader_resources.separate_samplers { + let set = ast + .get_decoration(sampler.id, spirv::Decoration::DescriptorSet) + .map_err(|err| gen_query_error(stage, err))?; + ast.set_decoration( + sampler.id, + spirv::Decoration::DescriptorSet, + space_offset + set, + ) + .map_err(|err| gen_unexpected_error(stage, err))?; + } + } + + if space_offset != 0 { + for image in &shader_resources.sampled_images { + let set = ast + .get_decoration(image.id, spirv::Decoration::DescriptorSet) + .map_err(|err| gen_query_error(stage, err))?; + ast.set_decoration( + image.id, + spirv::Decoration::DescriptorSet, + space_offset + set, + ) + .map_err(|err| gen_unexpected_error(stage, err))?; + } + } + + if space_offset != 0 { + for input in &shader_resources.subpass_inputs { + let set = ast + .get_decoration(input.id, spirv::Decoration::DescriptorSet) + .map_err(|err| gen_query_error(stage, err))?; + ast.set_decoration( + input.id, + spirv::Decoration::DescriptorSet, + space_offset + set, + ) + .map_err(|err| gen_unexpected_error(stage, err))?; + } + } + + Ok(()) + } + + fn translate_spirv( + ast: &mut spirv::Ast, + shader_model: hlsl::ShaderModel, + layout: &r::PipelineLayout, + stage: ShaderStage, + features: &hal::Features, + entry_point: &str, + ) -> Result { + let mut compile_options = hlsl::CompilerOptions::default(); + compile_options.shader_model = shader_model; + compile_options.vertex.invert_y = !features.contains(hal::Features::NDC_Y_UP); + compile_options.force_zero_initialized_variables = true; + compile_options.nonwritable_uav_texture_as_srv = true; + // these are technically incorrect, but better than panicking + compile_options.point_size_compat = true; + compile_options.point_coord_compat = true; + compile_options.entry_point = Some((entry_point.to_string(), conv::map_stage(stage))); + + let stage_flag = stage.to_flag(); + let root_constant_layout = layout + .shared + .constants + .iter() + .filter_map(|constant| { + if constant.stages.contains(stage_flag) { + Some(hlsl::RootConstant { + start: constant.range.start * 4, + end: constant.range.end * 4, + binding: constant.range.start, + space: 0, + }) + } else { + None + } + }) + .collect(); + ast.set_compiler_options(&compile_options) + .map_err(|err| gen_unexpected_error(stage, err))?; + ast.set_root_constant_layout(root_constant_layout) + .map_err(|err| gen_unexpected_error(stage, err))?; + ast.compile().map_err(|err| { + let msg = match err { + SpirvErrorCode::CompilationError(msg) => msg, + SpirvErrorCode::Unhandled => "Unknown compile error".into(), + }; + let error = format!("SPIR-V compile failed: {}", msg); + pso::CreationError::ShaderCreationError(stage.to_flag(), error) + }) + } + + // Extract entry point from shader module on pipeline creation. + // Returns compiled shader blob and bool to indicate if the shader should be + // destroyed after pipeline creation + fn extract_entry_point( + stage: ShaderStage, + source: &pso::EntryPoint, + layout: &r::PipelineLayout, + features: &hal::Features, + ) -> Result<(native::Blob, bool), pso::CreationError> { + match *source.module { + r::ShaderModule::Compiled(ref shaders) => { + // TODO: do we need to check for specialization constants? + // Use precompiled shader, ignore specialization or layout. + shaders + .get(source.entry) + .map(|src| (*src, false)) + .ok_or(pso::CreationError::MissingEntryPoint(source.entry.into())) + } + r::ShaderModule::Spirv(ref raw_data) => { + let mut ast = Self::parse_spirv(stage, raw_data)?; + spirv_cross_specialize_ast(&mut ast, &source.specialization) + .map_err(pso::CreationError::InvalidSpecialization)?; + Self::patch_spirv_resources(stage, &mut ast, layout)?; + + let execution_model = conv::map_stage(stage); + let shader_model = hlsl::ShaderModel::V5_1; + let shader_code = Self::translate_spirv( + &mut ast, + shader_model, + layout, + stage, + features, + source.entry, + )?; + debug!("SPIRV-Cross generated shader:\n{}", shader_code); + + let real_name = ast + .get_cleansed_entry_point_name(source.entry, execution_model) + .map_err(|err| gen_query_error(stage, err))?; + + let shader = compile_shader( + stage, + shader_model, + features, + &real_name, + shader_code.as_bytes(), + )?; + Ok((shader, true)) + } + } + } + + pub(crate) fn create_command_signature( + device: native::Device, + ty: CommandSignature, + ) -> native::CommandSignature { + let (arg, stride) = match ty { + CommandSignature::Draw => (native::IndirectArgument::draw(), 16), + CommandSignature::DrawIndexed => (native::IndirectArgument::draw_indexed(), 20), + CommandSignature::Dispatch => (native::IndirectArgument::dispatch(), 12), + }; + + let (signature, hr) = + device.create_command_signature(native::RootSignature::null(), &[arg], stride, 0); + + if !winerror::SUCCEEDED(hr) { + error!("error on command signature creation: {:x}", hr); + } + signature + } + + pub(crate) fn create_descriptor_heap_impl( + device: native::Device, + heap_type: native::DescriptorHeapType, + shader_visible: bool, + capacity: usize, + ) -> r::DescriptorHeap { + assert_ne!(capacity, 0); + + let (heap, _hr) = device.create_descriptor_heap( + capacity as _, + heap_type, + if shader_visible { + native::DescriptorHeapFlags::SHADER_VISIBLE + } else { + native::DescriptorHeapFlags::empty() + }, + 0, + ); + + let descriptor_size = device.get_descriptor_increment_size(heap_type); + let cpu_handle = heap.start_cpu_descriptor(); + let gpu_handle = heap.start_gpu_descriptor(); + + r::DescriptorHeap { + raw: heap, + handle_size: descriptor_size as _, + total_handles: capacity as _, + start: r::DualHandle { + cpu: cpu_handle, + gpu: gpu_handle, + size: 0, + }, + } + } + + pub(crate) fn view_image_as_render_target_impl( + device: native::Device, + handle: d3d12::D3D12_CPU_DESCRIPTOR_HANDLE, + info: &ViewInfo, + ) -> Result<(), image::ViewCreationError> { + #![allow(non_snake_case)] + + let mut desc = d3d12::D3D12_RENDER_TARGET_VIEW_DESC { + Format: info.format, + ViewDimension: 0, + u: unsafe { mem::zeroed() }, + }; + + let MipSlice = info.levels.start as _; + let FirstArraySlice = info.layers.start as _; + let ArraySize = (info.layers.end - info.layers.start) as _; + let is_msaa = info.kind.num_samples() > 1; + if info.levels.start + 1 != info.levels.end { + return Err(image::ViewCreationError::Level(info.levels.start)); + } + if info.layers.end > info.kind.num_layers() { + return Err(image::ViewCreationError::Layer( + image::LayerError::OutOfBounds, + )); + } + + match info.view_kind { + image::ViewKind::D1 => { + assert_eq!(info.layers, 0..1); + desc.ViewDimension = d3d12::D3D12_RTV_DIMENSION_TEXTURE1D; + *unsafe { desc.u.Texture1D_mut() } = d3d12::D3D12_TEX1D_RTV { MipSlice } + } + image::ViewKind::D1Array => { + desc.ViewDimension = d3d12::D3D12_RTV_DIMENSION_TEXTURE1DARRAY; + *unsafe { desc.u.Texture1DArray_mut() } = d3d12::D3D12_TEX1D_ARRAY_RTV { + MipSlice, + FirstArraySlice, + ArraySize, + } + } + image::ViewKind::D2 if is_msaa => { + assert_eq!(info.layers, 0..1); + desc.ViewDimension = d3d12::D3D12_RTV_DIMENSION_TEXTURE2DMS; + *unsafe { desc.u.Texture2DMS_mut() } = d3d12::D3D12_TEX2DMS_RTV { + UnusedField_NothingToDefine: 0, + } + } + image::ViewKind::D2 => { + assert_eq!(info.layers, 0..1); + desc.ViewDimension = d3d12::D3D12_RTV_DIMENSION_TEXTURE2D; + *unsafe { desc.u.Texture2D_mut() } = d3d12::D3D12_TEX2D_RTV { + MipSlice, + PlaneSlice: 0, //TODO + } + } + image::ViewKind::D2Array if is_msaa => { + desc.ViewDimension = d3d12::D3D12_RTV_DIMENSION_TEXTURE2DMSARRAY; + *unsafe { desc.u.Texture2DMSArray_mut() } = d3d12::D3D12_TEX2DMS_ARRAY_RTV { + FirstArraySlice, + ArraySize, + } + } + image::ViewKind::D2Array => { + desc.ViewDimension = d3d12::D3D12_RTV_DIMENSION_TEXTURE2DARRAY; + *unsafe { desc.u.Texture2DArray_mut() } = d3d12::D3D12_TEX2D_ARRAY_RTV { + MipSlice, + FirstArraySlice, + ArraySize, + PlaneSlice: 0, //TODO + } + } + image::ViewKind::D3 => { + assert_eq!(info.layers, 0..1); + desc.ViewDimension = d3d12::D3D12_RTV_DIMENSION_TEXTURE3D; + *unsafe { desc.u.Texture3D_mut() } = d3d12::D3D12_TEX3D_RTV { + MipSlice, + FirstWSlice: 0, + WSize: info.kind.extent().depth as _, + } + } + image::ViewKind::Cube | image::ViewKind::CubeArray => { + desc.ViewDimension = d3d12::D3D12_RTV_DIMENSION_TEXTURE2DARRAY; + //TODO: double-check if any *6 are needed + *unsafe { desc.u.Texture2DArray_mut() } = d3d12::D3D12_TEX2D_ARRAY_RTV { + MipSlice, + FirstArraySlice, + ArraySize, + PlaneSlice: 0, //TODO + } + } + }; + + unsafe { + device.CreateRenderTargetView(info.resource.as_mut_ptr(), &desc, handle); + } + + Ok(()) + } + + pub(crate) fn view_image_as_render_target( + &self, + info: &ViewInfo, + ) -> Result { + let handle = self.rtv_pool.lock().alloc_handle(); + Self::view_image_as_render_target_impl(self.raw, handle.raw, info).map(|_| handle) + } + + pub(crate) fn view_image_as_depth_stencil_impl( + device: native::Device, + handle: d3d12::D3D12_CPU_DESCRIPTOR_HANDLE, + info: &ViewInfo, + ) -> Result<(), image::ViewCreationError> { + #![allow(non_snake_case)] + + let mut desc = d3d12::D3D12_DEPTH_STENCIL_VIEW_DESC { + Format: info.format, + ViewDimension: 0, + Flags: 0, + u: unsafe { mem::zeroed() }, + }; + + let MipSlice = info.levels.start as _; + let FirstArraySlice = info.layers.start as _; + let ArraySize = (info.layers.end - info.layers.start) as _; + let is_msaa = info.kind.num_samples() > 1; + if info.levels.start + 1 != info.levels.end { + return Err(image::ViewCreationError::Level(info.levels.start)); + } + if info.layers.end > info.kind.num_layers() { + return Err(image::ViewCreationError::Layer( + image::LayerError::OutOfBounds, + )); + } + + match info.view_kind { + image::ViewKind::D1 => { + assert_eq!(info.layers, 0..1); + desc.ViewDimension = d3d12::D3D12_DSV_DIMENSION_TEXTURE1D; + *unsafe { desc.u.Texture1D_mut() } = d3d12::D3D12_TEX1D_DSV { MipSlice } + } + image::ViewKind::D1Array => { + desc.ViewDimension = d3d12::D3D12_DSV_DIMENSION_TEXTURE1DARRAY; + *unsafe { desc.u.Texture1DArray_mut() } = d3d12::D3D12_TEX1D_ARRAY_DSV { + MipSlice, + FirstArraySlice, + ArraySize, + } + } + image::ViewKind::D2 if is_msaa => { + assert_eq!(info.layers, 0..1); + desc.ViewDimension = d3d12::D3D12_DSV_DIMENSION_TEXTURE2DMS; + *unsafe { desc.u.Texture2DMS_mut() } = d3d12::D3D12_TEX2DMS_DSV { + UnusedField_NothingToDefine: 0, + } + } + image::ViewKind::D2 => { + assert_eq!(info.layers, 0..1); + desc.ViewDimension = d3d12::D3D12_DSV_DIMENSION_TEXTURE2D; + *unsafe { desc.u.Texture2D_mut() } = d3d12::D3D12_TEX2D_DSV { MipSlice } + } + image::ViewKind::D2Array if is_msaa => { + desc.ViewDimension = d3d12::D3D12_DSV_DIMENSION_TEXTURE2DMSARRAY; + *unsafe { desc.u.Texture2DMSArray_mut() } = d3d12::D3D12_TEX2DMS_ARRAY_DSV { + FirstArraySlice, + ArraySize, + } + } + image::ViewKind::D2Array => { + desc.ViewDimension = d3d12::D3D12_DSV_DIMENSION_TEXTURE2DARRAY; + *unsafe { desc.u.Texture2DArray_mut() } = d3d12::D3D12_TEX2D_ARRAY_DSV { + MipSlice, + FirstArraySlice, + ArraySize, + } + } + image::ViewKind::D3 | image::ViewKind::Cube | image::ViewKind::CubeArray => { + warn!( + "3D and cube views are not supported for the image, kind: {:?}", + info.kind + ); + return Err(image::ViewCreationError::BadKind(info.view_kind)); + } + }; + + unsafe { + device.CreateDepthStencilView(info.resource.as_mut_ptr(), &desc, handle); + } + + Ok(()) + } + + pub(crate) fn view_image_as_depth_stencil( + &self, + info: &ViewInfo, + ) -> Result { + let handle = self.dsv_pool.lock().alloc_handle(); + Self::view_image_as_depth_stencil_impl(self.raw, handle.raw, info).map(|_| handle) + } + + pub(crate) fn build_image_as_shader_resource_desc( + info: &ViewInfo, + ) -> Result { + #![allow(non_snake_case)] + + let mut desc = d3d12::D3D12_SHADER_RESOURCE_VIEW_DESC { + Format: info.format, + ViewDimension: 0, + Shader4ComponentMapping: info.component_mapping, + u: unsafe { mem::zeroed() }, + }; + + let MostDetailedMip = info.levels.start as _; + let MipLevels = (info.levels.end - info.levels.start) as _; + let FirstArraySlice = info.layers.start as _; + let ArraySize = (info.layers.end - info.layers.start) as _; + + if info.layers.end > info.kind.num_layers() { + return Err(image::ViewCreationError::Layer( + image::LayerError::OutOfBounds, + )); + } + let is_msaa = info.kind.num_samples() > 1; + let is_cube = info.caps.contains(image::ViewCapabilities::KIND_CUBE); + + match info.view_kind { + image::ViewKind::D1 => { + assert_eq!(info.layers, 0..1); + desc.ViewDimension = d3d12::D3D12_SRV_DIMENSION_TEXTURE1D; + *unsafe { desc.u.Texture1D_mut() } = d3d12::D3D12_TEX1D_SRV { + MostDetailedMip, + MipLevels, + ResourceMinLODClamp: 0.0, + } + } + image::ViewKind::D1Array => { + desc.ViewDimension = d3d12::D3D12_SRV_DIMENSION_TEXTURE1DARRAY; + *unsafe { desc.u.Texture1DArray_mut() } = d3d12::D3D12_TEX1D_ARRAY_SRV { + MostDetailedMip, + MipLevels, + FirstArraySlice, + ArraySize, + ResourceMinLODClamp: 0.0, + } + } + image::ViewKind::D2 if is_msaa => { + assert_eq!(info.layers, 0..1); + desc.ViewDimension = d3d12::D3D12_SRV_DIMENSION_TEXTURE2DMS; + *unsafe { desc.u.Texture2DMS_mut() } = d3d12::D3D12_TEX2DMS_SRV { + UnusedField_NothingToDefine: 0, + } + } + image::ViewKind::D2 => { + assert_eq!(info.layers, 0..1); + desc.ViewDimension = d3d12::D3D12_SRV_DIMENSION_TEXTURE2D; + *unsafe { desc.u.Texture2D_mut() } = d3d12::D3D12_TEX2D_SRV { + MostDetailedMip, + MipLevels, + PlaneSlice: 0, //TODO + ResourceMinLODClamp: 0.0, + } + } + image::ViewKind::D2Array if is_msaa => { + desc.ViewDimension = d3d12::D3D12_SRV_DIMENSION_TEXTURE2DMSARRAY; + *unsafe { desc.u.Texture2DMSArray_mut() } = d3d12::D3D12_TEX2DMS_ARRAY_SRV { + FirstArraySlice, + ArraySize, + } + } + image::ViewKind::D2Array => { + desc.ViewDimension = d3d12::D3D12_SRV_DIMENSION_TEXTURE2DARRAY; + *unsafe { desc.u.Texture2DArray_mut() } = d3d12::D3D12_TEX2D_ARRAY_SRV { + MostDetailedMip, + MipLevels, + FirstArraySlice, + ArraySize, + PlaneSlice: 0, //TODO + ResourceMinLODClamp: 0.0, + } + } + image::ViewKind::D3 => { + assert_eq!(info.layers, 0..1); + desc.ViewDimension = d3d12::D3D12_SRV_DIMENSION_TEXTURE3D; + *unsafe { desc.u.Texture3D_mut() } = d3d12::D3D12_TEX3D_SRV { + MostDetailedMip, + MipLevels, + ResourceMinLODClamp: 0.0, + } + } + image::ViewKind::Cube if is_cube => { + desc.ViewDimension = d3d12::D3D12_SRV_DIMENSION_TEXTURECUBE; + *unsafe { desc.u.TextureCube_mut() } = d3d12::D3D12_TEXCUBE_SRV { + MostDetailedMip, + MipLevels, + ResourceMinLODClamp: 0.0, + } + } + image::ViewKind::CubeArray if is_cube => { + assert_eq!(0, ArraySize % 6); + desc.ViewDimension = d3d12::D3D12_SRV_DIMENSION_TEXTURECUBEARRAY; + *unsafe { desc.u.TextureCubeArray_mut() } = d3d12::D3D12_TEXCUBE_ARRAY_SRV { + MostDetailedMip, + MipLevels, + First2DArrayFace: FirstArraySlice, + NumCubes: ArraySize / 6, + ResourceMinLODClamp: 0.0, + } + } + image::ViewKind::Cube | image::ViewKind::CubeArray => { + warn!( + "Cube views are not supported for the image, kind: {:?}", + info.kind + ); + return Err(image::ViewCreationError::BadKind(info.view_kind)); + } + } + + Ok(desc) + } + + fn view_image_as_shader_resource( + &self, + info: &ViewInfo, + ) -> Result { + let desc = Self::build_image_as_shader_resource_desc(&info)?; + let handle = self.srv_uav_pool.lock().alloc_handle(); + unsafe { + self.raw + .CreateShaderResourceView(info.resource.as_mut_ptr(), &desc, handle.raw); + } + + Ok(handle) + } + + fn view_image_as_storage( + &self, + info: &ViewInfo, + ) -> Result { + #![allow(non_snake_case)] + + // Cannot make a storage image over multiple mips + if info.levels.start + 1 != info.levels.end { + return Err(image::ViewCreationError::Unsupported); + } + + let mut desc = d3d12::D3D12_UNORDERED_ACCESS_VIEW_DESC { + Format: info.format, + ViewDimension: 0, + u: unsafe { mem::zeroed() }, + }; + + let MipSlice = info.levels.start as _; + let FirstArraySlice = info.layers.start as _; + let ArraySize = (info.layers.end - info.layers.start) as _; + + if info.layers.end > info.kind.num_layers() { + return Err(image::ViewCreationError::Layer( + image::LayerError::OutOfBounds, + )); + } + if info.kind.num_samples() > 1 { + error!("MSAA images can't be viewed as UAV"); + return Err(image::ViewCreationError::Unsupported); + } + + match info.view_kind { + image::ViewKind::D1 => { + assert_eq!(info.layers, 0..1); + desc.ViewDimension = d3d12::D3D12_UAV_DIMENSION_TEXTURE1D; + *unsafe { desc.u.Texture1D_mut() } = d3d12::D3D12_TEX1D_UAV { MipSlice } + } + image::ViewKind::D1Array => { + desc.ViewDimension = d3d12::D3D12_UAV_DIMENSION_TEXTURE1DARRAY; + *unsafe { desc.u.Texture1DArray_mut() } = d3d12::D3D12_TEX1D_ARRAY_UAV { + MipSlice, + FirstArraySlice, + ArraySize, + } + } + image::ViewKind::D2 => { + assert_eq!(info.layers, 0..1); + desc.ViewDimension = d3d12::D3D12_UAV_DIMENSION_TEXTURE2D; + *unsafe { desc.u.Texture2D_mut() } = d3d12::D3D12_TEX2D_UAV { + MipSlice, + PlaneSlice: 0, //TODO + } + } + image::ViewKind::D2Array => { + desc.ViewDimension = d3d12::D3D12_UAV_DIMENSION_TEXTURE2DARRAY; + *unsafe { desc.u.Texture2DArray_mut() } = d3d12::D3D12_TEX2D_ARRAY_UAV { + MipSlice, + FirstArraySlice, + ArraySize, + PlaneSlice: 0, //TODO + } + } + image::ViewKind::D3 => { + assert_eq!(info.layers, 0..1); + desc.ViewDimension = d3d12::D3D12_UAV_DIMENSION_TEXTURE3D; + *unsafe { desc.u.Texture3D_mut() } = d3d12::D3D12_TEX3D_UAV { + MipSlice, + FirstWSlice: 0, + WSize: info.kind.extent().depth as _, + } + } + image::ViewKind::Cube | image::ViewKind::CubeArray => { + error!("Cubic images can't be viewed as UAV"); + return Err(image::ViewCreationError::Unsupported); + } + } + + let handle = self.srv_uav_pool.lock().alloc_handle(); + unsafe { + self.raw.CreateUnorderedAccessView( + info.resource.as_mut_ptr(), + ptr::null_mut(), + &desc, + handle.raw, + ); + } + + Ok(handle) + } + + pub(crate) fn create_raw_fence(&self, signalled: bool) -> native::Fence { + let mut handle = native::Fence::null(); + assert_eq!(winerror::S_OK, unsafe { + self.raw.CreateFence( + if signalled { 1 } else { 0 }, + d3d12::D3D12_FENCE_FLAG_NONE, + &d3d12::ID3D12Fence::uuidof(), + handle.mut_void(), + ) + }); + handle + } + + pub(crate) fn create_swapchain_impl( + &self, + config: &w::SwapchainConfig, + window_handle: windef::HWND, + factory: native::WeakPtr, + ) -> Result< + ( + native::WeakPtr, + dxgiformat::DXGI_FORMAT, + ), + w::SwapchainError, + > { + let mut swap_chain1 = native::WeakPtr::::null(); + + //TODO: proper error type? + let non_srgb_format = conv::map_format_nosrgb(config.format).unwrap(); + + let mut flags = dxgi::DXGI_SWAP_CHAIN_FLAG_FRAME_LATENCY_WAITABLE_OBJECT; + if config.present_mode.contains(w::PresentMode::IMMEDIATE) { + flags |= dxgi::DXGI_SWAP_CHAIN_FLAG_ALLOW_TEARING; + } + + // TODO: double-check values + let desc = dxgi1_2::DXGI_SWAP_CHAIN_DESC1 { + AlphaMode: dxgi1_2::DXGI_ALPHA_MODE_IGNORE, + BufferCount: config.image_count, + Width: config.extent.width, + Height: config.extent.height, + Format: non_srgb_format, + Flags: flags, + BufferUsage: dxgitype::DXGI_USAGE_RENDER_TARGET_OUTPUT, + SampleDesc: dxgitype::DXGI_SAMPLE_DESC { + Count: 1, + Quality: 0, + }, + Scaling: dxgi1_2::DXGI_SCALING_STRETCH, + Stereo: FALSE, + SwapEffect: dxgi::DXGI_SWAP_EFFECT_FLIP_DISCARD, + }; + + unsafe { + let hr = factory.CreateSwapChainForHwnd( + self.present_queue.as_mut_ptr() as *mut _, + window_handle, + &desc, + ptr::null(), + ptr::null_mut(), + swap_chain1.mut_void() as *mut *mut _, + ); + + if !winerror::SUCCEEDED(hr) { + error!("error on swapchain creation 0x{:x}", hr); + return Err(w::SwapchainError::Unknown); + } + + let (swap_chain3, hr3) = swap_chain1.cast::(); + if !winerror::SUCCEEDED(hr3) { + error!("error on swapchain cast 0x{:x}", hr3); + } + + swap_chain1.destroy(); + Ok((swap_chain3, non_srgb_format)) + } + } + + pub(crate) fn wrap_swapchain( + &self, + inner: native::WeakPtr, + config: &w::SwapchainConfig, + ) -> Swapchain { + let waitable = unsafe { + inner.SetMaximumFrameLatency(config.image_count); + inner.GetFrameLatencyWaitableObject() + }; + + let rtv_desc = d3d12::D3D12_RENDER_TARGET_VIEW_DESC { + Format: conv::map_format(config.format).unwrap(), + ViewDimension: d3d12::D3D12_RTV_DIMENSION_TEXTURE2D, + ..unsafe { mem::zeroed() } + }; + let rtv_heap = Device::create_descriptor_heap_impl( + self.raw, + native::DescriptorHeapType::Rtv, + false, + config.image_count as _, + ); + + let mut resources = vec![native::Resource::null(); config.image_count as usize]; + for (i, res) in resources.iter_mut().enumerate() { + let rtv_handle = rtv_heap.at(i as _, 0).cpu; + unsafe { + inner.GetBuffer(i as _, &d3d12::ID3D12Resource::uuidof(), res.mut_void()); + self.raw + .CreateRenderTargetView(res.as_mut_ptr(), &rtv_desc, rtv_handle); + } + } + + Swapchain { + inner, + rtv_heap, + resources, + waitable, + usage: config.image_usage, + acquired_count: 0, + } + } + + pub(crate) fn bind_image_resource( + &self, + resource: native::WeakPtr, + image: &mut r::Image, + place: r::Place, + ) { + let image_unbound = image.expect_unbound(); + let num_layers = image_unbound.kind.num_layers(); + + //TODO: the clear_Xv is incomplete. We should support clearing images created without XXX_ATTACHMENT usage. + // for this, we need to check the format and force the `RENDER_TARGET` flag behind the user's back + // if the format supports being rendered into, allowing us to create clear_Xv + let info = ViewInfo { + resource, + kind: image_unbound.kind, + caps: image::ViewCapabilities::empty(), + view_kind: match image_unbound.kind { + image::Kind::D1(..) => image::ViewKind::D1Array, + image::Kind::D2(..) => image::ViewKind::D2Array, + image::Kind::D3(..) => image::ViewKind::D3, + }, + format: image_unbound.desc.Format, + component_mapping: IDENTITY_MAPPING, + levels: 0..1, + layers: 0..0, + }; + let format_properties = self + .format_properties + .resolve(image_unbound.format as usize) + .properties; + let props = match image_unbound.tiling { + image::Tiling::Optimal => format_properties.optimal_tiling, + image::Tiling::Linear => format_properties.linear_tiling, + }; + let can_clear_color = image_unbound + .usage + .intersects(image::Usage::TRANSFER_DST | image::Usage::COLOR_ATTACHMENT) + && props.contains(format::ImageFeature::COLOR_ATTACHMENT); + let can_clear_depth = image_unbound + .usage + .intersects(image::Usage::TRANSFER_DST | image::Usage::DEPTH_STENCIL_ATTACHMENT) + && props.contains(format::ImageFeature::DEPTH_STENCIL_ATTACHMENT); + let aspects = image_unbound.format.surface_desc().aspects; + + *image = r::Image::Bound(r::ImageBound { + resource, + place, + surface_type: image_unbound.format.base_format().0, + kind: image_unbound.kind, + mip_levels: image_unbound.mip_levels, + default_view_format: image_unbound.view_format, + view_caps: image_unbound.view_caps, + descriptor: image_unbound.desc, + clear_cv: if aspects.contains(Aspects::COLOR) && can_clear_color { + let format = image_unbound.view_format.unwrap(); + (0..num_layers) + .map(|layer| { + self.view_image_as_render_target(&ViewInfo { + format, + layers: layer..layer + 1, + ..info.clone() + }) + .unwrap() + }) + .collect() + } else { + Vec::new() + }, + clear_dv: if aspects.contains(Aspects::DEPTH) && can_clear_depth { + let format = image_unbound.dsv_format.unwrap(); + (0..num_layers) + .map(|layer| { + self.view_image_as_depth_stencil(&ViewInfo { + format, + layers: layer..layer + 1, + ..info.clone() + }) + .unwrap() + }) + .collect() + } else { + Vec::new() + }, + clear_sv: if aspects.contains(Aspects::STENCIL) && can_clear_depth { + let format = image_unbound.dsv_format.unwrap(); + (0..num_layers) + .map(|layer| { + self.view_image_as_depth_stencil(&ViewInfo { + format, + layers: layer..layer + 1, + ..info.clone() + }) + .unwrap() + }) + .collect() + } else { + Vec::new() + }, + requirements: image_unbound.requirements, + }); + } +} + +impl d::Device for Device { + unsafe fn allocate_memory( + &self, + mem_type: hal::MemoryTypeId, + size: u64, + ) -> Result { + let mem_type = mem_type.0; + let mem_base_id = mem_type % NUM_HEAP_PROPERTIES; + let heap_property = &self.heap_properties[mem_base_id]; + + let properties = d3d12::D3D12_HEAP_PROPERTIES { + Type: d3d12::D3D12_HEAP_TYPE_CUSTOM, + CPUPageProperty: heap_property.page_property, + MemoryPoolPreference: heap_property.memory_pool, + CreationNodeMask: 0, + VisibleNodeMask: 0, + }; + + // Exposed memory types are grouped according to their capabilities. + // See `MemoryGroup` for more details. + let mem_group = mem_type / NUM_HEAP_PROPERTIES; + + let desc = d3d12::D3D12_HEAP_DESC { + SizeInBytes: size, + Properties: properties, + Alignment: d3d12::D3D12_DEFAULT_MSAA_RESOURCE_PLACEMENT_ALIGNMENT as _, // TODO: not always..? + Flags: match mem_group { + 0 => d3d12::D3D12_HEAP_FLAG_ALLOW_ALL_BUFFERS_AND_TEXTURES, + 1 => d3d12::D3D12_HEAP_FLAG_ALLOW_ONLY_BUFFERS, + 2 => d3d12::D3D12_HEAP_FLAG_ALLOW_ONLY_NON_RT_DS_TEXTURES, + 3 => d3d12::D3D12_HEAP_FLAG_ALLOW_ONLY_RT_DS_TEXTURES, + _ => unreachable!(), + }, + }; + + let mut heap = native::Heap::null(); + let hr = self + .raw + .clone() + .CreateHeap(&desc, &d3d12::ID3D12Heap::uuidof(), heap.mut_void()); + if hr != winerror::S_OK { + if hr != winerror::E_OUTOFMEMORY { + error!("Error in CreateHeap: 0x{:X}", hr); + } + return Err(d::OutOfMemory::Device.into()); + } + + // The first memory heap of each group corresponds to the default heap, which is can never + // be mapped. + // Devices supporting heap tier 1 can only created buffers on mem group 1 (ALLOW_ONLY_BUFFERS). + // Devices supporting heap tier 2 always expose only mem group 0 and don't have any further restrictions. + let is_mapable = mem_base_id != 0 + && (mem_group == MemoryGroup::Universal as _ + || mem_group == MemoryGroup::BufferOnly as _); + + // Create a buffer resource covering the whole memory slice to be able to map the whole memory. + let resource = if is_mapable { + let mut resource = native::Resource::null(); + let desc = d3d12::D3D12_RESOURCE_DESC { + Dimension: d3d12::D3D12_RESOURCE_DIMENSION_BUFFER, + Alignment: 0, + Width: size, + Height: 1, + DepthOrArraySize: 1, + MipLevels: 1, + Format: dxgiformat::DXGI_FORMAT_UNKNOWN, + SampleDesc: dxgitype::DXGI_SAMPLE_DESC { + Count: 1, + Quality: 0, + }, + Layout: d3d12::D3D12_TEXTURE_LAYOUT_ROW_MAJOR, + Flags: d3d12::D3D12_RESOURCE_FLAG_NONE, + }; + + assert_eq!( + winerror::S_OK, + self.raw.clone().CreatePlacedResource( + heap.as_mut_ptr(), + 0, + &desc, + d3d12::D3D12_RESOURCE_STATE_COMMON, + ptr::null(), + &d3d12::ID3D12Resource::uuidof(), + resource.mut_void(), + ) + ); + + Some(resource) + } else { + None + }; + + Ok(r::Memory { + heap, + type_id: mem_type, + size, + resource, + }) + } + + unsafe fn create_command_pool( + &self, + family: QueueFamilyId, + create_flags: CommandPoolCreateFlags, + ) -> Result { + let list_type = QUEUE_FAMILIES[family.0].native_type(); + Ok(CommandPool::new( + self.raw, + list_type, + &self.shared, + create_flags, + )) + } + + unsafe fn destroy_command_pool(&self, _pool: CommandPool) {} + + unsafe fn create_render_pass<'a, Ia, Is, Id>( + &self, + attachments: Ia, + subpasses: Is, + dependencies: Id, + ) -> Result + where + Ia: Iterator, + Is: Iterator>, + Id: Iterator, + { + #[derive(Copy, Clone, Debug, PartialEq)] + enum SubState { + New(d3d12::D3D12_RESOURCE_STATES), + // Color attachment which will be resolved at the end of the subpass + Resolve(d3d12::D3D12_RESOURCE_STATES), + Preserve, + Undefined, + } + /// Temporary information about every sub-pass + struct SubInfo<'a> { + desc: pass::SubpassDesc<'a>, + /// States before the render-pass (in self.start) + /// and after the render-pass (in self.end). + external_dependencies: Range, + /// Counts the number of dependencies that need to be resolved + /// before starting this subpass. + unresolved_dependencies: u16, + } + struct AttachmentInfo { + sub_states: Vec, + last_state: d3d12::D3D12_RESOURCE_STATES, + barrier_start_index: usize, + } + + let attachments = attachments.collect::>(); + let mut sub_infos = subpasses + .map(|desc| SubInfo { + desc: desc.clone(), + external_dependencies: image::Access::empty()..image::Access::empty(), + unresolved_dependencies: 0, + }) + .collect::>(); + let dependencies = dependencies.collect::>(); + + let mut att_infos = (0..attachments.len()) + .map(|_| AttachmentInfo { + sub_states: vec![SubState::Undefined; sub_infos.len()], + last_state: d3d12::D3D12_RESOURCE_STATE_COMMON, // is to be overwritten + barrier_start_index: 0, + }) + .collect::>(); + + for dep in &dependencies { + match dep.passes { + Range { + start: None, + end: None, + } => { + error!("Unexpected external-external dependency!"); + } + Range { + start: None, + end: Some(sid), + } => { + sub_infos[sid as usize].external_dependencies.start |= dep.accesses.start; + } + Range { + start: Some(sid), + end: None, + } => { + sub_infos[sid as usize].external_dependencies.end |= dep.accesses.end; + } + Range { + start: Some(from_sid), + end: Some(sid), + } => { + //Note: self-dependencies are ignored + if from_sid != sid { + sub_infos[sid as usize].unresolved_dependencies += 1; + } + } + } + } + + // Fill out subpass known layouts + for (sid, sub_info) in sub_infos.iter().enumerate() { + let sub = &sub_info.desc; + for (i, &(id, _layout)) in sub.colors.iter().enumerate() { + let target_state = d3d12::D3D12_RESOURCE_STATE_RENDER_TARGET; + let state = match sub.resolves.get(i) { + Some(_) => SubState::Resolve(target_state), + None => SubState::New(target_state), + }; + let old = mem::replace(&mut att_infos[id].sub_states[sid], state); + debug_assert_eq!(SubState::Undefined, old); + } + for &(id, layout) in sub.depth_stencil { + let state = SubState::New(match layout { + image::Layout::DepthStencilAttachmentOptimal => { + d3d12::D3D12_RESOURCE_STATE_DEPTH_WRITE + } + image::Layout::DepthStencilReadOnlyOptimal => { + d3d12::D3D12_RESOURCE_STATE_DEPTH_READ + } + image::Layout::General => d3d12::D3D12_RESOURCE_STATE_DEPTH_WRITE, + _ => { + error!("Unexpected depth/stencil layout: {:?}", layout); + d3d12::D3D12_RESOURCE_STATE_COMMON + } + }); + let old = mem::replace(&mut att_infos[id].sub_states[sid], state); + debug_assert_eq!(SubState::Undefined, old); + } + for &(id, _layout) in sub.inputs { + let state = SubState::New(d3d12::D3D12_RESOURCE_STATE_PIXEL_SHADER_RESOURCE); + let old = mem::replace(&mut att_infos[id].sub_states[sid], state); + debug_assert_eq!(SubState::Undefined, old); + } + for &(id, _layout) in sub.resolves { + let state = SubState::New(d3d12::D3D12_RESOURCE_STATE_RESOLVE_DEST); + let old = mem::replace(&mut att_infos[id].sub_states[sid], state); + debug_assert_eq!(SubState::Undefined, old); + } + for &id in sub.preserves { + let old = mem::replace(&mut att_infos[id].sub_states[sid], SubState::Preserve); + debug_assert_eq!(SubState::Undefined, old); + } + } + + let mut rp = r::RenderPass { + attachments: attachments.iter().cloned().collect(), + subpasses: Vec::new(), + post_barriers: Vec::new(), + raw_name: Vec::new(), + }; + + while let Some(sid) = sub_infos + .iter() + .position(|si| si.unresolved_dependencies == 0) + { + for dep in &dependencies { + if dep.passes.start != dep.passes.end + && dep.passes.start == Some(sid as pass::SubpassId) + { + if let Some(other) = dep.passes.end { + sub_infos[other as usize].unresolved_dependencies -= 1; + } + } + } + + let si = &mut sub_infos[sid]; + si.unresolved_dependencies = !0; // mark as done + + // Subpass barriers + let mut pre_barriers = Vec::new(); + let mut post_barriers = Vec::new(); + for (att_id, (ai, att)) in att_infos.iter_mut().zip(attachments.iter()).enumerate() { + // Attachment wasn't used before, figure out the initial state + if ai.barrier_start_index == 0 { + //Note: the external dependencies are provided for all attachments that are + // first used in this sub-pass, so they may contain more states than we expect + // for this particular attachment. + ai.last_state = conv::map_image_resource_state( + si.external_dependencies.start, + att.layouts.start, + ); + } + // Barrier from previous subpass to current or following subpasses. + match ai.sub_states[sid] { + SubState::Preserve => { + ai.barrier_start_index = rp.subpasses.len() + 1; + } + SubState::New(state) if state != ai.last_state => { + let barrier = r::BarrierDesc::new(att_id, ai.last_state..state); + match rp.subpasses.get_mut(ai.barrier_start_index) { + Some(past_subpass) => { + let split = barrier.split(); + past_subpass.pre_barriers.push(split.start); + pre_barriers.push(split.end); + } + None => pre_barriers.push(barrier), + } + ai.last_state = state; + ai.barrier_start_index = rp.subpasses.len() + 1; + } + SubState::Resolve(state) => { + // 1. Standard pre barrier to update state from previous pass into desired substate. + if state != ai.last_state { + let barrier = r::BarrierDesc::new(att_id, ai.last_state..state); + match rp.subpasses.get_mut(ai.barrier_start_index) { + Some(past_subpass) => { + let split = barrier.split(); + past_subpass.pre_barriers.push(split.start); + pre_barriers.push(split.end); + } + None => pre_barriers.push(barrier), + } + } + + // 2. Post Barrier at the end of the subpass into RESOLVE_SOURCE. + let resolve_state = d3d12::D3D12_RESOURCE_STATE_RESOLVE_SOURCE; + let barrier = r::BarrierDesc::new(att_id, state..resolve_state); + post_barriers.push(barrier); + + ai.last_state = resolve_state; + ai.barrier_start_index = rp.subpasses.len() + 1; + } + SubState::Undefined | SubState::New(_) => {} + }; + } + + rp.subpasses.push(r::SubpassDesc { + color_attachments: si.desc.colors.iter().cloned().collect(), + depth_stencil_attachment: si.desc.depth_stencil.cloned(), + input_attachments: si.desc.inputs.iter().cloned().collect(), + resolve_attachments: si.desc.resolves.iter().cloned().collect(), + pre_barriers, + post_barriers, + }); + } + // if this fails, our graph has cycles + assert_eq!(rp.subpasses.len(), sub_infos.len()); + assert!(sub_infos.iter().all(|si| si.unresolved_dependencies == !0)); + + // take care of the post-pass transitions at the end of the renderpass. + for (att_id, (ai, att)) in att_infos.iter().zip(attachments.iter()).enumerate() { + let state_dst = if ai.barrier_start_index == 0 { + // attachment wasn't used in any sub-pass? + continue; + } else { + let si = &sub_infos[ai.barrier_start_index - 1]; + conv::map_image_resource_state(si.external_dependencies.end, att.layouts.end) + }; + if state_dst == ai.last_state { + continue; + } + let barrier = r::BarrierDesc::new(att_id, ai.last_state..state_dst); + match rp.subpasses.get_mut(ai.barrier_start_index) { + Some(past_subpass) => { + let split = barrier.split(); + past_subpass.pre_barriers.push(split.start); + rp.post_barriers.push(split.end); + } + None => rp.post_barriers.push(barrier), + } + } + + Ok(rp) + } + + unsafe fn create_pipeline_layout<'a, Is, Ic>( + &self, + sets: Is, + push_constant_ranges: Ic, + ) -> Result + where + Is: Iterator, + Ic: Iterator)>, + { + // Pipeline layouts are implemented as RootSignature for D3D12. + // + // Push Constants are implemented as root constants. + // + // Each descriptor set layout will be one table entry of the root signature. + // We have the additional restriction that SRV/CBV/UAV and samplers need to be + // separated, so each set layout will actually occupy up to 2 entries! + // SRV/CBV/UAV tables are added to the signature first, then Sampler tables, + // and finally dynamic uniform descriptors. + // + // Dynamic uniform buffers are implemented as root descriptors. + // This allows to handle the dynamic offsets properly, which would not be feasible + // with a combination of root constant and descriptor table. + // + // Root signature layout: + // Root Constants: Register: Offest/4, Space: 0 + // ... + // DescriptorTable0: Space: 1 (SrvCbvUav) + // DescriptorTable0: Space: 1 (Sampler) + // Root Descriptors 0 + // DescriptorTable1: Space: 2 (SrvCbvUav) + // Root Descriptors 1 + // ... + + let sets = sets.collect::>(); + + let mut root_offset = 0u32; + let root_constants = root_constants::split(push_constant_ranges) + .iter() + .map(|constant| { + assert!(constant.range.start <= constant.range.end); + root_offset += constant.range.end - constant.range.start; + + RootConstant { + stages: constant.stages, + range: constant.range.start..constant.range.end, + } + }) + .collect::>(); + + info!( + "Creating a pipeline layout with {} sets and {} root constants", + sets.len(), + root_constants.len() + ); + + // Number of elements in the root signature. + // Guarantees that no re-allocation is done, and our pointers are valid + let mut parameters = Vec::with_capacity(root_constants.len() + sets.len() * 2); + let mut parameter_offsets = Vec::with_capacity(parameters.capacity()); + + // Convert root signature descriptions into root signature parameters. + for root_constant in root_constants.iter() { + debug!( + "\tRoot constant set={} range {:?}", + ROOT_CONSTANT_SPACE, root_constant.range + ); + parameter_offsets.push(root_constant.range.start); + parameters.push(native::RootParameter::constants( + conv::map_shader_visibility(root_constant.stages), + native::Binding { + register: root_constant.range.start as _, + space: ROOT_CONSTANT_SPACE, + }, + (root_constant.range.end - root_constant.range.start) as _, + )); + } + + // Offest of `spaceN` for descriptor tables. Root constants will be in + // `space0`. + // This has to match `patch_spirv_resources` logic. + let root_space_offset = if !root_constants.is_empty() { 1 } else { 0 }; + + // Collect the whole number of bindings we will create upfront. + // It allows us to preallocate enough storage to avoid reallocation, + // which could cause invalid pointers. + let total = sets + .iter() + .map(|desc_set| { + let mut sum = 0; + for binding in desc_set.bindings.iter() { + let content = r::DescriptorContent::from(binding.ty); + if !content.is_dynamic() { + sum += content.bits().count_ones() as usize; + } + } + sum + }) + .sum(); + let mut ranges = Vec::with_capacity(total); + + let elements = sets + .iter() + .enumerate() + .map(|(i, set)| { + let space = (root_space_offset + i) as u32; + let mut table_type = r::SetTableTypes::empty(); + let root_table_offset = root_offset; + + //TODO: split between sampler and non-sampler tables + let visibility = conv::map_shader_visibility( + set.bindings + .iter() + .fold(pso::ShaderStageFlags::empty(), |u, bind| { + u | bind.stage_flags + }), + ); + + for bind in set.bindings.iter() { + debug!("\tRange {:?} at space={}", bind, space); + } + + let describe = |bind: &pso::DescriptorSetLayoutBinding, ty| { + native::DescriptorRange::new( + ty, + bind.count as _, + native::Binding { + register: bind.binding as _, + space, + }, + d3d12::D3D12_DESCRIPTOR_RANGE_OFFSET_APPEND, + ) + }; + + let mut mutable_bindings = auxil::FastHashSet::default(); + + // SRV/CBV/UAV descriptor tables + let mut range_base = ranges.len(); + for bind in set.bindings.iter() { + let content = r::DescriptorContent::from(bind.ty); + if !content.is_dynamic() { + // Descriptor table ranges + if content.contains(r::DescriptorContent::CBV) { + ranges.push(describe(bind, native::DescriptorRangeType::CBV)); + } + if content.contains(r::DescriptorContent::SRV) { + ranges.push(describe(bind, native::DescriptorRangeType::SRV)); + } + if content.contains(r::DescriptorContent::UAV) { + ranges.push(describe(bind, native::DescriptorRangeType::UAV)); + mutable_bindings.insert(bind.binding); + } + } + } + if ranges.len() > range_base { + parameter_offsets.push(root_offset); + parameters.push(native::RootParameter::descriptor_table( + visibility, + &ranges[range_base..], + )); + table_type |= r::SRV_CBV_UAV; + root_offset += 1; + } + + // Sampler descriptor tables + range_base = ranges.len(); + for bind in set.bindings.iter() { + let content = r::DescriptorContent::from(bind.ty); + if content.contains(r::DescriptorContent::SAMPLER) { + ranges.push(describe(bind, native::DescriptorRangeType::Sampler)); + } + } + if ranges.len() > range_base { + parameter_offsets.push(root_offset); + parameters.push(native::RootParameter::descriptor_table( + visibility, + &ranges[range_base..], + )); + table_type |= r::SAMPLERS; + root_offset += 1; + } + + // Root (dynamic) descriptor tables + for bind in set.bindings.iter() { + let content = r::DescriptorContent::from(bind.ty); + if content.is_dynamic() { + let binding = native::Binding { + register: bind.binding as _, + space, + }; + + if content.contains(r::DescriptorContent::CBV) { + parameter_offsets.push(root_offset); + parameters + .push(native::RootParameter::cbv_descriptor(visibility, binding)); + root_offset += 2; // root CBV costs 2 words + } + if content.contains(r::DescriptorContent::SRV) { + parameter_offsets.push(root_offset); + parameters + .push(native::RootParameter::srv_descriptor(visibility, binding)); + root_offset += 2; // root SRV costs 2 words + } + if content.contains(r::DescriptorContent::UAV) { + parameter_offsets.push(root_offset); + parameters + .push(native::RootParameter::uav_descriptor(visibility, binding)); + root_offset += 2; // root UAV costs 2 words + } + } + } + + r::RootElement { + table: r::RootTable { + ty: table_type, + offset: root_table_offset as _, + }, + mutable_bindings, + } + }) + .collect(); + + // Ensure that we didn't reallocate! + debug_assert_eq!(ranges.len(), total); + assert_eq!(parameters.len(), parameter_offsets.len()); + + // TODO: error handling + let (signature_raw, error) = match self.library.serialize_root_signature( + native::RootSignatureVersion::V1_0, + ¶meters, + &[], + native::RootSignatureFlags::ALLOW_IA_INPUT_LAYOUT, + ) { + Ok((pair, hr)) if winerror::SUCCEEDED(hr) => pair, + Ok((_, hr)) => panic!("Can't serialize root signature: {:?}", hr), + Err(e) => panic!("Can't find serialization function: {:?}", e), + }; + + if !error.is_null() { + error!( + "Root signature serialization error: {:?}", + error.as_c_str().to_str().unwrap() + ); + error.destroy(); + } + + // TODO: error handling + let (signature, _hr) = self.raw.create_root_signature(signature_raw, 0); + signature_raw.destroy(); + + Ok(r::PipelineLayout { + shared: Arc::new(r::PipelineShared { + signature, + constants: root_constants, + parameter_offsets, + total_slots: root_offset, + }), + elements, + }) + } + + unsafe fn create_pipeline_cache(&self, _data: Option<&[u8]>) -> Result<(), d::OutOfMemory> { + Ok(()) + } + + unsafe fn get_pipeline_cache_data(&self, _cache: &()) -> Result, d::OutOfMemory> { + //empty + Ok(Vec::new()) + } + + unsafe fn destroy_pipeline_cache(&self, _: ()) { + //empty + } + + unsafe fn merge_pipeline_caches<'a, I>(&self, _: &mut (), _: I) -> Result<(), d::OutOfMemory> + where + I: Iterator, + { + //empty + Ok(()) + } + + unsafe fn create_graphics_pipeline<'a>( + &self, + desc: &pso::GraphicsPipelineDesc<'a, B>, + _cache: Option<&()>, + ) -> Result { + enum ShaderBc { + Owned(native::Blob), + Borrowed(native::Blob), + None, + } + let features = &self.features; + impl ShaderBc { + pub fn shader(&self) -> native::Shader { + match *self { + ShaderBc::Owned(ref bc) | ShaderBc::Borrowed(ref bc) => { + native::Shader::from_blob(*bc) + } + ShaderBc::None => native::Shader::null(), + } + } + } + + let build_shader = |stage: ShaderStage, + source: Option<&pso::EntryPoint<'a, B>>| + -> Result { + let source = match source { + Some(src) => src, + None => return Ok(ShaderBc::None), + }; + + let (shader, owned) = Self::extract_entry_point(stage, source, desc.layout, features)?; + Ok(if owned { + ShaderBc::Owned(shader) + } else { + ShaderBc::Borrowed(shader) + }) + }; + + let vertex_buffers: Vec = Vec::new(); + let attributes: Vec = Vec::new(); + let mesh_input_assembler = pso::InputAssemblerDesc::new(pso::Primitive::TriangleList); + let (vertex_buffers, attributes, input_assembler, vs, gs, hs, ds, _, _) = + match desc.primitive_assembler { + pso::PrimitiveAssemblerDesc::Vertex { + buffers, + attributes, + ref input_assembler, + ref vertex, + ref tessellation, + ref geometry, + } => { + let (hs, ds) = if let Some(ts) = tessellation { + (Some(&ts.0), Some(&ts.1)) + } else { + (None, None) + }; + + ( + buffers, + attributes, + input_assembler, + Some(vertex), + geometry.as_ref(), + hs, + ds, + None, + None, + ) + } + pso::PrimitiveAssemblerDesc::Mesh { ref task, ref mesh } => ( + &vertex_buffers[..], + &attributes[..], + &mesh_input_assembler, + None, + None, + None, + None, + task.as_ref(), + Some(mesh), + ), + }; + + let vertex_semantic_remapping = if let Some(ref vs) = vs { + // If we have a pre-compiled shader, we've lost the information we need to recover + // this information, so just pretend like this workaround never existed and hope + // for the best. + if let crate::resource::ShaderModule::Spirv(ref spv) = vs.module { + Some(Self::introspect_spirv_vertex_semantic_remapping(spv)?) + } else { + None + } + } else { + None + }; + + let vs = build_shader(ShaderStage::Vertex, vs)?; + let gs = build_shader(ShaderStage::Geometry, gs)?; + let hs = build_shader(ShaderStage::Domain, hs)?; + let ds = build_shader(ShaderStage::Hull, ds)?; + let ps = build_shader(ShaderStage::Fragment, desc.fragment.as_ref())?; + + // Rebind vertex buffers, see native.rs for more details. + let mut vertex_bindings = [None; MAX_VERTEX_BUFFERS]; + let mut vertex_strides = [0; MAX_VERTEX_BUFFERS]; + + for buffer in vertex_buffers { + vertex_strides[buffer.binding as usize] = buffer.stride; + } + // Fill in identity mapping where we don't need to adjust anything. + for attrib in attributes { + let binding = attrib.binding as usize; + let stride = vertex_strides[attrib.binding as usize]; + if attrib.element.offset < stride { + vertex_bindings[binding] = Some(r::VertexBinding { + stride: vertex_strides[attrib.binding as usize], + offset: 0, + mapped_binding: binding, + }); + } + } + + // See [`introspect_spirv_vertex_semantic_remapping`] for details of why this is needed. + let semantics: Vec<_> = attributes + .iter() + .map(|attrib| { + let semantics = vertex_semantic_remapping + .as_ref() + .and_then(|map| map.get(&attrib.location)); + match semantics { + Some(Some((major, minor))) => { + let name = std::borrow::Cow::Owned(format!("TEXCOORD{}_\0", major)); + let location = *minor; + (name, location) + } + _ => { + let name = std::borrow::Cow::Borrowed("TEXCOORD\0"); + let location = attrib.location; + (name, location) + } + } + }) + .collect(); + + // Define input element descriptions + let input_element_descs = attributes + .iter() + .zip(semantics.iter()) + .filter_map(|(attrib, (semantic_name, semantic_index))| { + let buffer_desc = match vertex_buffers + .iter() + .find(|buffer_desc| buffer_desc.binding == attrib.binding) + { + Some(buffer_desc) => buffer_desc, + None => { + error!( + "Couldn't find associated vertex buffer description {:?}", + attrib.binding + ); + return Some(Err(pso::CreationError::Other)); + } + }; + + let (slot_class, step_rate) = match buffer_desc.rate { + VertexInputRate::Vertex => { + (d3d12::D3D12_INPUT_CLASSIFICATION_PER_VERTEX_DATA, 0) + } + VertexInputRate::Instance(divisor) => { + (d3d12::D3D12_INPUT_CLASSIFICATION_PER_INSTANCE_DATA, divisor) + } + }; + let format = attrib.element.format; + + // Check if we need to add a new remapping in-case the offset is + // higher than the vertex stride. + // In this case we rebase the attribute to zero offset. + let binding = attrib.binding as usize; + let stride = vertex_strides[binding]; + let offset = attrib.element.offset; + let (input_slot, offset) = if stride <= offset { + // Number of input attributes may not exceed bindings, see limits. + // We will always find at least one free binding. + let mapping = vertex_bindings.iter().position(Option::is_none).unwrap(); + vertex_bindings[mapping] = Some(r::VertexBinding { + stride: vertex_strides[binding], + offset: offset, + mapped_binding: binding, + }); + + (mapping, 0) + } else { + (binding, offset) + }; + + Some(Ok(d3d12::D3D12_INPUT_ELEMENT_DESC { + SemanticName: semantic_name.as_ptr() as *const _, // Semantic name used by SPIRV-Cross + SemanticIndex: *semantic_index, + Format: match conv::map_format(format) { + Some(fm) => fm, + None => { + error!("Unable to find DXGI format for {:?}", format); + return Some(Err(pso::CreationError::Other)); + } + }, + InputSlot: input_slot as _, + AlignedByteOffset: offset, + InputSlotClass: slot_class, + InstanceDataStepRate: step_rate as _, + })) + }) + .collect::, _>>()?; + + // TODO: check maximum number of rtvs + // Get associated subpass information + let pass = { + let subpass = &desc.subpass; + match subpass.main_pass.subpasses.get(subpass.index as usize) { + Some(subpass) => subpass, + None => return Err(pso::CreationError::InvalidSubpass(subpass.index)), + } + }; + + // Get color attachment formats from subpass + let (rtvs, num_rtvs) = { + let mut rtvs = [dxgiformat::DXGI_FORMAT_UNKNOWN; + d3d12::D3D12_SIMULTANEOUS_RENDER_TARGET_COUNT as usize]; + let mut num_rtvs = 0; + for (rtv, target) in rtvs.iter_mut().zip(pass.color_attachments.iter()) { + let format = desc.subpass.main_pass.attachments[target.0].format; + *rtv = format + .and_then(conv::map_format) + .unwrap_or(dxgiformat::DXGI_FORMAT_UNKNOWN); + num_rtvs += 1; + } + (rtvs, num_rtvs) + }; + + let sample_desc = dxgitype::DXGI_SAMPLE_DESC { + Count: match desc.multisampling { + Some(ref ms) => ms.rasterization_samples as _, + None => 1, + }, + Quality: 0, + }; + + // Setup pipeline description + let pso_desc = d3d12::D3D12_GRAPHICS_PIPELINE_STATE_DESC { + pRootSignature: desc.layout.shared.signature.as_mut_ptr(), + VS: *vs.shader(), + PS: *ps.shader(), + GS: *gs.shader(), + DS: *ds.shader(), + HS: *hs.shader(), + StreamOutput: d3d12::D3D12_STREAM_OUTPUT_DESC { + pSODeclaration: ptr::null(), + NumEntries: 0, + pBufferStrides: ptr::null(), + NumStrides: 0, + RasterizedStream: 0, + }, + BlendState: d3d12::D3D12_BLEND_DESC { + AlphaToCoverageEnable: desc.multisampling.as_ref().map_or(FALSE, |ms| { + if ms.alpha_coverage { + TRUE + } else { + FALSE + } + }), + IndependentBlendEnable: TRUE, + RenderTarget: conv::map_render_targets(&desc.blender.targets), + }, + SampleMask: match desc.multisampling { + Some(ref ms) => ms.sample_mask as u32, + None => UINT::MAX, + }, + RasterizerState: conv::map_rasterizer(&desc.rasterizer, desc.multisampling.is_some()), + DepthStencilState: conv::map_depth_stencil(&desc.depth_stencil), + InputLayout: d3d12::D3D12_INPUT_LAYOUT_DESC { + pInputElementDescs: if input_element_descs.is_empty() { + ptr::null() + } else { + input_element_descs.as_ptr() + }, + NumElements: input_element_descs.len() as u32, + }, + IBStripCutValue: match input_assembler.restart_index { + Some(hal::IndexType::U16) => d3d12::D3D12_INDEX_BUFFER_STRIP_CUT_VALUE_0xFFFF, + Some(hal::IndexType::U32) => d3d12::D3D12_INDEX_BUFFER_STRIP_CUT_VALUE_0xFFFFFFFF, + None => d3d12::D3D12_INDEX_BUFFER_STRIP_CUT_VALUE_DISABLED, + }, + PrimitiveTopologyType: conv::map_topology_type(input_assembler.primitive), + NumRenderTargets: num_rtvs, + RTVFormats: rtvs, + DSVFormat: pass + .depth_stencil_attachment + .and_then(|att_ref| { + desc.subpass.main_pass.attachments[att_ref.0] + .format + .and_then(|f| conv::map_format_dsv(f.base_format().0)) + }) + .unwrap_or(dxgiformat::DXGI_FORMAT_UNKNOWN), + SampleDesc: sample_desc, + NodeMask: 0, + CachedPSO: d3d12::D3D12_CACHED_PIPELINE_STATE { + pCachedBlob: ptr::null(), + CachedBlobSizeInBytes: 0, + }, + Flags: d3d12::D3D12_PIPELINE_STATE_FLAG_NONE, + }; + let topology = conv::map_topology(input_assembler); + + // Create PSO + let mut pipeline = native::PipelineState::null(); + let hr = if desc.depth_stencil.depth_bounds { + // The DepthBoundsTestEnable option isn't available in the original D3D12_GRAPHICS_PIPELINE_STATE_DESC struct. + // Instead, we must use the newer subobject stream method. + let (device2, hr) = self.raw.cast::(); + if winerror::SUCCEEDED(hr) { + let mut pss_stream = GraphicsPipelineStateSubobjectStream::new(&pso_desc, true); + let pss_desc = d3d12::D3D12_PIPELINE_STATE_STREAM_DESC { + SizeInBytes: mem::size_of_val(&pss_stream), + pPipelineStateSubobjectStream: &mut pss_stream as *mut _ as _, + }; + device2.CreatePipelineState( + &pss_desc, + &d3d12::ID3D12PipelineState::uuidof(), + pipeline.mut_void(), + ) + } else { + hr + } + } else { + self.raw.clone().CreateGraphicsPipelineState( + &pso_desc, + &d3d12::ID3D12PipelineState::uuidof(), + pipeline.mut_void(), + ) + }; + + let destroy_shader = |shader: ShaderBc| { + if let ShaderBc::Owned(bc) = shader { + bc.destroy(); + } + }; + + destroy_shader(vs); + destroy_shader(ps); + destroy_shader(gs); + destroy_shader(hs); + destroy_shader(ds); + + if winerror::SUCCEEDED(hr) { + let mut baked_states = desc.baked_states.clone(); + if !desc.depth_stencil.depth_bounds { + baked_states.depth_bounds = None; + } + if let Some(name) = desc.label { + let cwstr = wide_cstr(name); + pipeline.SetName(cwstr.as_ptr()); + } + + Ok(r::GraphicsPipeline { + raw: pipeline, + shared: Arc::clone(&desc.layout.shared), + topology, + vertex_bindings, + baked_states, + }) + } else { + let error = format!("Failed to build shader: {:x}", hr); + Err(pso::CreationError::ShaderCreationError( + pso::ShaderStageFlags::GRAPHICS, + error, + )) + } + } + + unsafe fn create_compute_pipeline<'a>( + &self, + desc: &pso::ComputePipelineDesc<'a, B>, + _cache: Option<&()>, + ) -> Result { + let (cs, cs_destroy) = Self::extract_entry_point( + ShaderStage::Compute, + &desc.shader, + desc.layout, + &self.features, + )?; + + let (pipeline, hr) = self.raw.create_compute_pipeline_state( + desc.layout.shared.signature, + native::Shader::from_blob(cs), + 0, + native::CachedPSO::null(), + native::PipelineStateFlags::empty(), + ); + + if cs_destroy { + cs.destroy(); + } + + if winerror::SUCCEEDED(hr) { + if let Some(name) = desc.label { + let cwstr = wide_cstr(name); + pipeline.SetName(cwstr.as_ptr()); + } + + Ok(r::ComputePipeline { + raw: pipeline, + shared: Arc::clone(&desc.layout.shared), + }) + } else { + let error = format!("Failed to build shader: {:x}", hr); + Err(pso::CreationError::ShaderCreationError( + pso::ShaderStageFlags::COMPUTE, + error, + )) + } + } + + unsafe fn create_framebuffer( + &self, + _renderpass: &r::RenderPass, + _attachments: I, + extent: image::Extent, + ) -> Result { + Ok(r::Framebuffer { + layers: extent.depth as _, + }) + } + + unsafe fn create_shader_module( + &self, + raw_data: &[u32], + ) -> Result { + Ok(r::ShaderModule::Spirv(raw_data.into())) + } + + unsafe fn create_buffer( + &self, + mut size: u64, + usage: buffer::Usage, + _sparse: memory::SparseFlags, + ) -> Result { + if usage.contains(buffer::Usage::UNIFORM) { + // Constant buffer view sizes need to be aligned. + // Coupled with the offset alignment we can enforce an aligned CBV size + // on descriptor updates. + let mask = d3d12::D3D12_CONSTANT_BUFFER_DATA_PLACEMENT_ALIGNMENT as u64 - 1; + size = (size + mask) & !mask; + } + if usage.contains(buffer::Usage::TRANSFER_DST) { + // minimum of 1 word for the clear UAV + size = size.max(4); + } + + let type_mask_shift = if self.private_caps.heterogeneous_resource_heaps { + MEM_TYPE_UNIVERSAL_SHIFT + } else { + MEM_TYPE_BUFFER_SHIFT + }; + + let requirements = memory::Requirements { + size, + alignment: d3d12::D3D12_DEFAULT_RESOURCE_PLACEMENT_ALIGNMENT as u64, + type_mask: MEM_TYPE_MASK << type_mask_shift, + }; + + Ok(r::Buffer::Unbound(r::BufferUnbound { + requirements, + usage, + name: None, + })) + } + + unsafe fn get_buffer_requirements(&self, buffer: &r::Buffer) -> Requirements { + match buffer { + r::Buffer::Unbound(b) => b.requirements, + r::Buffer::Bound(b) => b.requirements, + } + } + + unsafe fn bind_buffer_memory( + &self, + memory: &r::Memory, + offset: u64, + buffer: &mut r::Buffer, + ) -> Result<(), d::BindError> { + let buffer_unbound = buffer.expect_unbound(); + if buffer_unbound.requirements.type_mask & (1 << memory.type_id) == 0 { + error!( + "Bind memory failure: supported mask 0x{:x}, given id {}", + buffer_unbound.requirements.type_mask, memory.type_id + ); + return Err(d::BindError::WrongMemory); + } + if offset + buffer_unbound.requirements.size > memory.size { + return Err(d::BindError::OutOfBounds); + } + + let mut resource = native::Resource::null(); + let desc = d3d12::D3D12_RESOURCE_DESC { + Dimension: d3d12::D3D12_RESOURCE_DIMENSION_BUFFER, + Alignment: 0, + Width: buffer_unbound.requirements.size, + Height: 1, + DepthOrArraySize: 1, + MipLevels: 1, + Format: dxgiformat::DXGI_FORMAT_UNKNOWN, + SampleDesc: dxgitype::DXGI_SAMPLE_DESC { + Count: 1, + Quality: 0, + }, + Layout: d3d12::D3D12_TEXTURE_LAYOUT_ROW_MAJOR, + Flags: conv::map_buffer_flags(buffer_unbound.usage), + }; + + assert_eq!( + winerror::S_OK, + self.raw.clone().CreatePlacedResource( + memory.heap.as_mut_ptr(), + offset, + &desc, + d3d12::D3D12_RESOURCE_STATE_COMMON, + ptr::null(), + &d3d12::ID3D12Resource::uuidof(), + resource.mut_void(), + ) + ); + + if let Some(ref name) = buffer_unbound.name { + resource.SetName(name.as_ptr()); + } + + let clear_uav = if buffer_unbound.usage.contains(buffer::Usage::TRANSFER_DST) { + let handle = self.srv_uav_pool.lock().alloc_handle(); + let mut view_desc = d3d12::D3D12_UNORDERED_ACCESS_VIEW_DESC { + Format: dxgiformat::DXGI_FORMAT_R32_TYPELESS, + ViewDimension: d3d12::D3D12_UAV_DIMENSION_BUFFER, + u: mem::zeroed(), + }; + + *view_desc.u.Buffer_mut() = d3d12::D3D12_BUFFER_UAV { + FirstElement: 0, + NumElements: (buffer_unbound.requirements.size / 4) as _, + StructureByteStride: 0, + CounterOffsetInBytes: 0, + Flags: d3d12::D3D12_BUFFER_UAV_FLAG_RAW, + }; + + self.raw.CreateUnorderedAccessView( + resource.as_mut_ptr(), + ptr::null_mut(), + &view_desc, + handle.raw, + ); + Some(handle) + } else { + None + }; + + *buffer = r::Buffer::Bound(r::BufferBound { + resource, + requirements: buffer_unbound.requirements, + clear_uav, + }); + + Ok(()) + } + + unsafe fn create_buffer_view( + &self, + buffer: &r::Buffer, + format: Option, + sub: buffer::SubRange, + ) -> Result { + let buffer = buffer.expect_bound(); + let buffer_features = { + let idx = format.map(|fmt| fmt as usize).unwrap_or(0); + self.format_properties + .resolve(idx) + .properties + .buffer_features + }; + let (format, format_desc) = match format.and_then(conv::map_format) { + Some(fmt) => (fmt, format.unwrap().surface_desc()), + None => return Err(buffer::ViewCreationError::UnsupportedFormat(format)), + }; + + let start = sub.offset; + let size = sub.size.unwrap_or(buffer.requirements.size - start); + + let bytes_per_texel = (format_desc.bits / 8) as u64; + // Check if it adheres to the texel buffer offset limit + assert_eq!(start % bytes_per_texel, 0); + let first_element = start / bytes_per_texel; + let num_elements = size / bytes_per_texel; // rounds down to next smaller size + + let handle_srv = if buffer_features.contains(format::BufferFeature::UNIFORM_TEXEL) { + let mut desc = d3d12::D3D12_SHADER_RESOURCE_VIEW_DESC { + Format: format, + ViewDimension: d3d12::D3D12_SRV_DIMENSION_BUFFER, + Shader4ComponentMapping: IDENTITY_MAPPING, + u: mem::zeroed(), + }; + + *desc.u.Buffer_mut() = d3d12::D3D12_BUFFER_SRV { + FirstElement: first_element, + NumElements: num_elements as _, + StructureByteStride: bytes_per_texel as _, + Flags: d3d12::D3D12_BUFFER_SRV_FLAG_NONE, + }; + + let handle = self.srv_uav_pool.lock().alloc_handle(); + self.raw.clone().CreateShaderResourceView( + buffer.resource.as_mut_ptr(), + &desc, + handle.raw, + ); + Some(handle) + } else { + None + }; + + let handle_uav = if buffer_features.intersects( + format::BufferFeature::STORAGE_TEXEL | format::BufferFeature::STORAGE_TEXEL_ATOMIC, + ) { + let mut desc = d3d12::D3D12_UNORDERED_ACCESS_VIEW_DESC { + Format: format, + ViewDimension: d3d12::D3D12_UAV_DIMENSION_BUFFER, + u: mem::zeroed(), + }; + + *desc.u.Buffer_mut() = d3d12::D3D12_BUFFER_UAV { + FirstElement: first_element, + NumElements: num_elements as _, + StructureByteStride: bytes_per_texel as _, + Flags: d3d12::D3D12_BUFFER_UAV_FLAG_NONE, + CounterOffsetInBytes: 0, + }; + + let handle = self.srv_uav_pool.lock().alloc_handle(); + self.raw.clone().CreateUnorderedAccessView( + buffer.resource.as_mut_ptr(), + ptr::null_mut(), + &desc, + handle.raw, + ); + Some(handle) + } else { + None + }; + + return Ok(r::BufferView { + handle_srv, + handle_uav, + }); + } + + unsafe fn create_image( + &self, + kind: image::Kind, + mip_levels: image::Level, + format: format::Format, + tiling: image::Tiling, + usage: image::Usage, + sparse: memory::SparseFlags, + view_caps: image::ViewCapabilities, + ) -> Result { + assert!(mip_levels <= kind.compute_num_levels()); + + let base_format = format.base_format(); + let format_desc = base_format.0.desc(); + let bytes_per_block = (format_desc.bits / 8) as _; + let block_dim = format_desc.dim; + let view_format = conv::map_format(format); + let extent = kind.extent(); + + let format_info = self.format_properties.resolve(format as usize); + let (layout, features) = if sparse.contains(memory::SparseFlags::SPARSE_BINDING) { + ( + d3d12::D3D12_TEXTURE_LAYOUT_64KB_UNDEFINED_SWIZZLE, + format_info.properties.optimal_tiling, + ) + } else { + match tiling { + image::Tiling::Optimal => ( + d3d12::D3D12_TEXTURE_LAYOUT_UNKNOWN, + format_info.properties.optimal_tiling, + ), + image::Tiling::Linear => ( + d3d12::D3D12_TEXTURE_LAYOUT_ROW_MAJOR, + format_info.properties.linear_tiling, + ), + } + }; + if format_info.sample_count_mask & kind.num_samples() == 0 { + return Err(image::CreationError::Samples(kind.num_samples())); + } + + let desc = d3d12::D3D12_RESOURCE_DESC { + Dimension: match kind { + image::Kind::D1(..) => d3d12::D3D12_RESOURCE_DIMENSION_TEXTURE1D, + image::Kind::D2(..) => d3d12::D3D12_RESOURCE_DIMENSION_TEXTURE2D, + image::Kind::D3(..) => d3d12::D3D12_RESOURCE_DIMENSION_TEXTURE3D, + }, + Alignment: 0, + Width: extent.width as _, + Height: extent.height as _, + DepthOrArraySize: if extent.depth > 1 { + extent.depth as _ + } else { + kind.num_layers() as _ + }, + MipLevels: mip_levels as _, + Format: if format_desc.is_compressed() { + view_format.unwrap() + } else { + match conv::map_surface_type(base_format.0) { + Some(format) => format, + None => return Err(image::CreationError::Format(format)), + } + }, + SampleDesc: dxgitype::DXGI_SAMPLE_DESC { + Count: kind.num_samples() as _, + Quality: 0, + }, + Layout: layout, + Flags: conv::map_image_flags(usage, features), + }; + + let alloc_info = self.raw.clone().GetResourceAllocationInfo(0, 1, &desc); + + // Image flags which require RT/DS heap due to internal implementation. + let target_flags = d3d12::D3D12_RESOURCE_FLAG_ALLOW_RENDER_TARGET + | d3d12::D3D12_RESOURCE_FLAG_ALLOW_DEPTH_STENCIL; + let type_mask_shift = if self.private_caps.heterogeneous_resource_heaps { + MEM_TYPE_UNIVERSAL_SHIFT + } else if desc.Flags & target_flags != 0 { + MEM_TYPE_TARGET_SHIFT + } else { + MEM_TYPE_IMAGE_SHIFT + }; + + Ok(r::Image::Unbound(r::ImageUnbound { + view_format, + dsv_format: conv::map_format_dsv(base_format.0), + desc, + requirements: memory::Requirements { + size: alloc_info.SizeInBytes, + alignment: alloc_info.Alignment, + type_mask: MEM_TYPE_MASK << type_mask_shift, + }, + format, + kind, + mip_levels, + usage, + tiling, + view_caps, + bytes_per_block, + block_dim, + name: None, + })) + } + + unsafe fn get_image_requirements(&self, image: &r::Image) -> Requirements { + match image { + r::Image::Bound(i) => i.requirements, + r::Image::Unbound(i) => i.requirements, + } + } + + unsafe fn get_image_subresource_footprint( + &self, + image: &r::Image, + sub: image::Subresource, + ) -> image::SubresourceFootprint { + let mut num_rows = 0; + let mut total_bytes = 0; + let _desc = match image { + r::Image::Bound(i) => i.descriptor, + r::Image::Unbound(i) => i.desc, + }; + let footprint = { + let mut footprint = mem::zeroed(); + self.raw.GetCopyableFootprints( + image.get_desc(), + image.calc_subresource(sub.level as _, sub.layer as _, 0), + 1, + 0, + &mut footprint, + &mut num_rows, + ptr::null_mut(), // row size in bytes + &mut total_bytes, + ); + footprint + }; + + let depth_pitch = (footprint.Footprint.RowPitch * num_rows) as buffer::Offset; + let array_pitch = footprint.Footprint.Depth as buffer::Offset * depth_pitch; + image::SubresourceFootprint { + slice: footprint.Offset..footprint.Offset + total_bytes, + row_pitch: footprint.Footprint.RowPitch as _, + depth_pitch, + array_pitch, + } + } + + unsafe fn bind_image_memory( + &self, + memory: &r::Memory, + offset: u64, + image: &mut r::Image, + ) -> Result<(), d::BindError> { + let image_unbound = image.expect_unbound(); + if image_unbound.requirements.type_mask & (1 << memory.type_id) == 0 { + error!( + "Bind memory failure: supported mask 0x{:x}, given id {}", + image_unbound.requirements.type_mask, memory.type_id + ); + return Err(d::BindError::WrongMemory); + } + if offset + image_unbound.requirements.size > memory.size { + return Err(d::BindError::OutOfBounds); + } + + let mut resource = native::Resource::null(); + + assert_eq!( + winerror::S_OK, + self.raw.clone().CreatePlacedResource( + memory.heap.as_mut_ptr(), + offset, + &image_unbound.desc, + d3d12::D3D12_RESOURCE_STATE_COMMON, + ptr::null(), + &d3d12::ID3D12Resource::uuidof(), + resource.mut_void(), + ) + ); + + if let Some(ref name) = image_unbound.name { + resource.SetName(name.as_ptr()); + } + + self.bind_image_resource( + resource, + image, + r::Place::Heap { + raw: memory.heap.clone(), + offset, + }, + ); + + Ok(()) + } + + unsafe fn create_image_view( + &self, + image: &r::Image, + view_kind: image::ViewKind, + format: format::Format, + swizzle: format::Swizzle, + usage: image::Usage, + range: image::SubresourceRange, + ) -> Result { + let image = image.expect_bound(); + let is_array = image.kind.num_layers() > 1; + let mip_levels = ( + range.level_start, + range.level_start + range.resolve_level_count(image.mip_levels), + ); + let layers = ( + range.layer_start, + range.layer_start + range.resolve_layer_count(image.kind.num_layers()), + ); + let surface_format = format.base_format().0; + + let info = ViewInfo { + resource: image.resource, + kind: image.kind, + caps: image.view_caps, + // D3D12 doesn't allow looking at a single slice of an array as a non-array + view_kind: if is_array && view_kind == image::ViewKind::D2 { + image::ViewKind::D2Array + } else if is_array && view_kind == image::ViewKind::D1 { + image::ViewKind::D1Array + } else { + view_kind + }, + format: conv::map_format(format).ok_or(image::ViewCreationError::BadFormat(format))?, + component_mapping: conv::map_swizzle(swizzle), + levels: mip_levels.0..mip_levels.1, + layers: layers.0..layers.1, + }; + + //Note: we allow RTV/DSV/SRV/UAV views to fail to be created here, + // because we don't know if the user will even need to use them. + //Update: now we have `usage`, but some of the users (like `wgpu`) + // still don't know ahead of time what it needs to be. + + Ok(r::ImageView { + resource: image.resource, + handle_srv: if usage.intersects(image::Usage::SAMPLED | image::Usage::INPUT_ATTACHMENT) + { + let info = if range.aspects.contains(format::Aspects::DEPTH) { + conv::map_format_shader_depth(surface_format).map(|format| ViewInfo { + format, + ..info.clone() + }) + } else if range.aspects.contains(format::Aspects::STENCIL) { + // Vulkan/gfx expects stencil to be read from the R channel, + // while DX12 exposes it in "G" always. + let new_swizzle = conv::swizzle_rg(swizzle); + conv::map_format_shader_stencil(surface_format).map(|format| ViewInfo { + format, + component_mapping: conv::map_swizzle(new_swizzle), + ..info.clone() + }) + } else { + Some(info.clone()) + }; + if let Some(ref info) = info { + self.view_image_as_shader_resource(&info).ok() + } else { + None + } + } else { + None + }, + handle_rtv: if usage.contains(image::Usage::COLOR_ATTACHMENT) { + // This view is not necessarily going to be rendered to, even + // if the image supports that in general. + match self.view_image_as_render_target(&info) { + Ok(handle) => r::RenderTargetHandle::Pool(handle), + Err(_) => r::RenderTargetHandle::None, + } + } else { + r::RenderTargetHandle::None + }, + handle_uav: if usage.contains(image::Usage::STORAGE) { + self.view_image_as_storage(&info).ok() + } else { + None + }, + handle_dsv: if usage.contains(image::Usage::DEPTH_STENCIL_ATTACHMENT) { + match conv::map_format_dsv(surface_format) { + Some(dsv_format) => self + .view_image_as_depth_stencil(&ViewInfo { + format: dsv_format, + ..info + }) + .ok(), + None => None, + } + } else { + None + }, + dxgi_format: image.default_view_format.unwrap(), + num_levels: image.descriptor.MipLevels as image::Level, + mip_levels, + layers, + kind: info.kind, + }) + } + + unsafe fn create_sampler( + &self, + info: &image::SamplerDesc, + ) -> Result { + if !info.normalized { + warn!("Sampler with unnormalized coordinates is not supported!"); + } + let handle = match self.samplers.map.lock().entry(info.clone()) { + Entry::Occupied(e) => *e.get(), + Entry::Vacant(e) => { + let handle = self.samplers.pool.lock().alloc_handle(); + let info = e.key(); + let op = match info.comparison { + Some(_) => d3d12::D3D12_FILTER_REDUCTION_TYPE_COMPARISON, + None => d3d12::D3D12_FILTER_REDUCTION_TYPE_STANDARD, + }; + self.raw.create_sampler( + handle.raw, + conv::map_filter( + info.mag_filter, + info.min_filter, + info.mip_filter, + op, + info.anisotropy_clamp, + ), + [ + conv::map_wrap(info.wrap_mode.0), + conv::map_wrap(info.wrap_mode.1), + conv::map_wrap(info.wrap_mode.2), + ], + info.lod_bias.0, + info.anisotropy_clamp.map_or(0, |aniso| aniso as u32), + conv::map_comparison(info.comparison.unwrap_or(pso::Comparison::Always)), + info.border.into(), + info.lod_range.start.0..info.lod_range.end.0, + ); + *e.insert(handle) + } + }; + Ok(r::Sampler { handle }) + } + + unsafe fn create_descriptor_pool( + &self, + max_sets: usize, + ranges: I, + _flags: pso::DescriptorPoolCreateFlags, + ) -> Result + where + I: Iterator, + { + // Descriptor pools are implemented as slices of the global descriptor heaps. + // A descriptor pool will occupy a contiguous space in each heap (CBV/SRV/UAV and Sampler) depending + // on the total requested amount of descriptors. + + let mut num_srv_cbv_uav = 0; + let mut num_samplers = 0; + + let ranges = ranges.collect::>(); + + info!("create_descriptor_pool with {} max sets", max_sets); + for desc in &ranges { + let content = r::DescriptorContent::from(desc.ty); + debug!("\tcontent {:?}", content); + if content.contains(r::DescriptorContent::CBV) { + num_srv_cbv_uav += desc.count; + } + if content.contains(r::DescriptorContent::SRV) { + num_srv_cbv_uav += desc.count; + } + if content.contains(r::DescriptorContent::UAV) { + num_srv_cbv_uav += desc.count; + } + if content.contains(r::DescriptorContent::SAMPLER) { + num_samplers += desc.count; + } + } + + info!( + "total {} views and {} samplers", + num_srv_cbv_uav, num_samplers + ); + + // Allocate slices of the global GPU descriptor heaps. + let heap_srv_cbv_uav = { + let view_heap = &self.heap_srv_cbv_uav.0; + + let range = match num_srv_cbv_uav { + 0 => 0..0, + _ => self + .heap_srv_cbv_uav + .1 + .lock() + .allocate_range(num_srv_cbv_uav as _) + .map_err(|e| { + warn!("View pool allocation error: {:?}", e); + d::OutOfMemory::Host + })?, + }; + + r::DescriptorHeapSlice { + heap: view_heap.raw.clone(), + handle_size: view_heap.handle_size as _, + range_allocator: RangeAllocator::new(range), + start: view_heap.start, + } + }; + + Ok(r::DescriptorPool { + heap_srv_cbv_uav, + heap_raw_sampler: self.samplers.heap.raw, + pools: ranges, + max_size: max_sets as _, + }) + } + + unsafe fn create_descriptor_set_layout<'a, I, J>( + &self, + bindings: I, + _immutable_samplers: J, + ) -> Result + where + I: Iterator, + J: Iterator, + { + Ok(r::DescriptorSetLayout { + bindings: bindings.collect(), + }) + } + + unsafe fn write_descriptor_set<'a, I>(&self, op: pso::DescriptorSetWrite<'a, B, I>) + where + I: Iterator>, + { + let mut descriptor_updater = self.descriptor_updater.lock(); + descriptor_updater.reset(); + + let mut accum = descriptors_cpu::MultiCopyAccumulator::default(); + debug!("write_descriptor_set"); + + let mut offset = op.array_offset as u64; + let mut target_binding = op.binding as usize; + let base_sampler_offset = op.set.sampler_offset(op.binding, op.array_offset); + trace!("\tsampler offset {}", base_sampler_offset); + let mut sampler_offset = base_sampler_offset; + debug!("\tbinding {} array offset {}", target_binding, offset); + + for descriptor in op.descriptors { + // spill over the writes onto the next binding + while offset >= op.set.binding_infos[target_binding].count { + target_binding += 1; + offset = 0; + } + let bind_info = &mut op.set.binding_infos[target_binding]; + let mut src_cbv = None; + let mut src_srv = None; + let mut src_uav = None; + + match descriptor { + pso::Descriptor::Buffer(buffer, ref sub) => { + let buffer = buffer.expect_bound(); + + if bind_info.content.is_dynamic() { + // Root Descriptor + let buffer_address = (*buffer.resource).GetGPUVirtualAddress(); + // Descriptor sets need to be externally synchronized according to specification + bind_info.dynamic_descriptors[offset as usize].gpu_buffer_location = + buffer_address + sub.offset; + } else { + // Descriptor table + let size = sub.size_to(buffer.requirements.size); + + if bind_info.content.contains(r::DescriptorContent::CBV) { + // Making the size field of buffer requirements for uniform + // buffers a multiple of 256 and setting the required offset + // alignment to 256 allows us to patch the size here. + // We can always enforce the size to be aligned to 256 for + // CBVs without going out-of-bounds. + let mask = d3d12::D3D12_CONSTANT_BUFFER_DATA_PLACEMENT_ALIGNMENT - 1; + let desc = d3d12::D3D12_CONSTANT_BUFFER_VIEW_DESC { + BufferLocation: (*buffer.resource).GetGPUVirtualAddress() + + sub.offset, + SizeInBytes: (size as u32 + mask) as u32 & !mask, + }; + let handle = descriptor_updater.alloc_handle(self.raw); + self.raw.CreateConstantBufferView(&desc, handle); + src_cbv = Some(handle); + } + if bind_info.content.contains(r::DescriptorContent::SRV) { + assert_eq!(size % 4, 0); + let mut desc = d3d12::D3D12_SHADER_RESOURCE_VIEW_DESC { + Format: dxgiformat::DXGI_FORMAT_R32_TYPELESS, + Shader4ComponentMapping: IDENTITY_MAPPING, + ViewDimension: d3d12::D3D12_SRV_DIMENSION_BUFFER, + u: mem::zeroed(), + }; + *desc.u.Buffer_mut() = d3d12::D3D12_BUFFER_SRV { + FirstElement: sub.offset as _, + NumElements: (size / 4) as _, + StructureByteStride: 0, + Flags: d3d12::D3D12_BUFFER_SRV_FLAG_RAW, + }; + let handle = descriptor_updater.alloc_handle(self.raw); + self.raw.CreateShaderResourceView( + buffer.resource.as_mut_ptr(), + &desc, + handle, + ); + src_srv = Some(handle); + } + if bind_info.content.contains(r::DescriptorContent::UAV) { + assert_eq!(size % 4, 0); + let mut desc = d3d12::D3D12_UNORDERED_ACCESS_VIEW_DESC { + Format: dxgiformat::DXGI_FORMAT_R32_TYPELESS, + ViewDimension: d3d12::D3D12_UAV_DIMENSION_BUFFER, + u: mem::zeroed(), + }; + *desc.u.Buffer_mut() = d3d12::D3D12_BUFFER_UAV { + FirstElement: sub.offset as _, + NumElements: (size / 4) as _, + StructureByteStride: 0, + CounterOffsetInBytes: 0, + Flags: d3d12::D3D12_BUFFER_UAV_FLAG_RAW, + }; + let handle = descriptor_updater.alloc_handle(self.raw); + self.raw.CreateUnorderedAccessView( + buffer.resource.as_mut_ptr(), + ptr::null_mut(), + &desc, + handle, + ); + src_uav = Some(handle); + } + } + } + pso::Descriptor::Image(image, _layout) => { + if bind_info.content.contains(r::DescriptorContent::SRV) { + src_srv = image.handle_srv.map(|h| h.raw); + } + if bind_info.content.contains(r::DescriptorContent::UAV) { + src_uav = image.handle_uav.map(|h| h.raw); + } + } + pso::Descriptor::CombinedImageSampler(image, _layout, sampler) => { + src_srv = image.handle_srv.map(|h| h.raw); + op.set.sampler_origins[sampler_offset] = sampler.handle.raw; + sampler_offset += 1; + } + pso::Descriptor::Sampler(sampler) => { + op.set.sampler_origins[sampler_offset] = sampler.handle.raw; + sampler_offset += 1; + } + pso::Descriptor::TexelBuffer(buffer_view) => { + if bind_info.content.contains(r::DescriptorContent::SRV) { + let handle = buffer_view.handle_srv + .expect("SRV handle of the storage texel buffer is zero (not supported by specified format)"); + src_srv = Some(handle.raw); + } + if bind_info.content.contains(r::DescriptorContent::UAV) { + let handle = buffer_view.handle_uav + .expect("UAV handle of the storage texel buffer is zero (not supported by specified format)"); + src_uav = Some(handle.raw); + } + } + } + + if let Some(handle) = src_cbv { + trace!("\tcbv offset {}", offset); + accum.src_views.add(handle, 1); + accum + .dst_views + .add(bind_info.view_range.as_ref().unwrap().at(offset), 1); + } + if let Some(handle) = src_srv { + trace!("\tsrv offset {}", offset); + accum.src_views.add(handle, 1); + accum + .dst_views + .add(bind_info.view_range.as_ref().unwrap().at(offset), 1); + } + if let Some(handle) = src_uav { + let uav_offset = if bind_info.content.contains(r::DescriptorContent::SRV) { + bind_info.count + offset + } else { + offset + }; + trace!("\tuav offset {}", uav_offset); + accum.src_views.add(handle, 1); + accum + .dst_views + .add(bind_info.view_range.as_ref().unwrap().at(uav_offset), 1); + } + + offset += 1; + } + + if sampler_offset != base_sampler_offset { + op.set + .update_samplers(&self.samplers.heap, &self.samplers.origins, &mut accum); + } + + accum.flush(self.raw); + } + + unsafe fn copy_descriptor_set<'a>(&self, op: pso::DescriptorSetCopy<'a, B>) { + let mut accum = descriptors_cpu::MultiCopyAccumulator::default(); + + let src_info = &op.src_set.binding_infos[op.src_binding as usize]; + let dst_info = &op.dst_set.binding_infos[op.dst_binding as usize]; + + if let (Some(src_range), Some(dst_range)) = + (src_info.view_range.as_ref(), dst_info.view_range.as_ref()) + { + assert!(op.src_array_offset + op.count <= src_range.handle.size as usize); + assert!(op.dst_array_offset + op.count <= dst_range.handle.size as usize); + let count = op.count as u32; + accum + .src_views + .add(src_range.at(op.src_array_offset as _), count); + accum + .dst_views + .add(dst_range.at(op.dst_array_offset as _), count); + + if (src_info.content & dst_info.content) + .contains(r::DescriptorContent::SRV | r::DescriptorContent::UAV) + { + assert!( + src_info.count as usize + op.src_array_offset + op.count + <= src_range.handle.size as usize + ); + assert!( + dst_info.count as usize + op.dst_array_offset + op.count + <= dst_range.handle.size as usize + ); + accum.src_views.add( + src_range.at(src_info.count + op.src_array_offset as u64), + count, + ); + accum.dst_views.add( + dst_range.at(dst_info.count + op.dst_array_offset as u64), + count, + ); + } + } + + if dst_info.content.contains(r::DescriptorContent::SAMPLER) { + let src_offset = op + .src_set + .sampler_offset(op.src_binding, op.src_array_offset); + let dst_offset = op + .dst_set + .sampler_offset(op.dst_binding, op.dst_array_offset); + op.dst_set.sampler_origins[dst_offset..dst_offset + op.count] + .copy_from_slice(&op.src_set.sampler_origins[src_offset..src_offset + op.count]); + + op.dst_set + .update_samplers(&self.samplers.heap, &self.samplers.origins, &mut accum); + } + + accum.flush(self.raw.clone()); + } + + unsafe fn map_memory( + &self, + memory: &mut r::Memory, + segment: memory::Segment, + ) -> Result<*mut u8, d::MapError> { + let mem = memory + .resource + .expect("Memory not created with a memory type exposing `CPU_VISIBLE`"); + let mut ptr = ptr::null_mut(); + assert_eq!( + winerror::S_OK, + (*mem).Map(0, &d3d12::D3D12_RANGE { Begin: 0, End: 0 }, &mut ptr) + ); + ptr = ptr.offset(segment.offset as isize); + Ok(ptr as *mut _) + } + + unsafe fn unmap_memory(&self, memory: &mut r::Memory) { + if let Some(mem) = memory.resource { + (*mem).Unmap(0, &d3d12::D3D12_RANGE { Begin: 0, End: 0 }); + } + } + + unsafe fn flush_mapped_memory_ranges<'a, I>(&self, ranges: I) -> Result<(), d::OutOfMemory> + where + I: Iterator, + { + for (memory, ref segment) in ranges { + if let Some(mem) = memory.resource { + // map and immediately unmap, hoping that dx12 drivers internally cache + // currently mapped buffers. + assert_eq!( + winerror::S_OK, + (*mem).Map(0, &d3d12::D3D12_RANGE { Begin: 0, End: 0 }, ptr::null_mut()) + ); + + let start = segment.offset; + let end = segment.size.map_or(memory.size, |s| start + s); // TODO: only need to be end of current mapping + + (*mem).Unmap( + 0, + &d3d12::D3D12_RANGE { + Begin: start as _, + End: end as _, + }, + ); + } + } + + Ok(()) + } + + unsafe fn invalidate_mapped_memory_ranges<'a, I>(&self, ranges: I) -> Result<(), d::OutOfMemory> + where + I: Iterator, + { + for (memory, ref segment) in ranges { + if let Some(mem) = memory.resource { + let start = segment.offset; + let end = segment.size.map_or(memory.size, |s| start + s); // TODO: only need to be end of current mapping + + // map and immediately unmap, hoping that dx12 drivers internally cache + // currently mapped buffers. + assert_eq!( + winerror::S_OK, + (*mem).Map( + 0, + &d3d12::D3D12_RANGE { + Begin: start as _, + End: end as _, + }, + ptr::null_mut(), + ) + ); + + (*mem).Unmap(0, &d3d12::D3D12_RANGE { Begin: 0, End: 0 }); + } + } + + Ok(()) + } + + fn create_semaphore(&self) -> Result { + let fence = self.create_fence(false)?; + Ok(r::Semaphore { raw: fence.raw }) + } + + fn create_fence(&self, signalled: bool) -> Result { + Ok(r::Fence { + raw: self.create_raw_fence(signalled), + }) + } + + unsafe fn reset_fence(&self, fence: &mut r::Fence) -> Result<(), d::OutOfMemory> { + assert_eq!(winerror::S_OK, fence.raw.signal(0)); + Ok(()) + } + + unsafe fn wait_for_fences<'a, I>( + &self, + fences: I, + wait: d::WaitFor, + timeout_ns: u64, + ) -> Result + where + I: Iterator, + { + let mut count = 0; + let mut events = self.events.lock(); + + for fence in fences { + if count == events.len() { + events.push(native::Event::create(false, false)); + } + let event = events[count]; + synchapi::ResetEvent(event.0); + assert_eq!(winerror::S_OK, fence.raw.set_event_on_completion(event, 1)); + count += 1; + } + + let all = match wait { + d::WaitFor::Any => FALSE, + d::WaitFor::All => TRUE, + }; + + let hr = { + // This block handles overflow when converting to u32 and always rounds up + // The Vulkan specification allows to wait more than specified + let timeout_ms = { + if timeout_ns > (::MAX as u64) * 1_000_000 { + ::MAX + } else { + ((timeout_ns + 999_999) / 1_000_000) as u32 + } + }; + + synchapi::WaitForMultipleObjects( + count as u32, + events.as_ptr() as *const _, + all, + timeout_ms, + ) + }; + + const WAIT_OBJECT_LAST: u32 = winbase::WAIT_OBJECT_0 + winnt::MAXIMUM_WAIT_OBJECTS; + const WAIT_ABANDONED_LAST: u32 = winbase::WAIT_ABANDONED_0 + winnt::MAXIMUM_WAIT_OBJECTS; + match hr { + winbase::WAIT_OBJECT_0..=WAIT_OBJECT_LAST => Ok(true), + winbase::WAIT_ABANDONED_0..=WAIT_ABANDONED_LAST => Ok(true), //TODO? + winbase::WAIT_FAILED => Err(d::WaitError::DeviceLost(d::DeviceLost)), + winerror::WAIT_TIMEOUT => Ok(false), + _ => panic!("Unexpected wait status 0x{:X}", hr), + } + } + + unsafe fn get_fence_status(&self, fence: &r::Fence) -> Result { + match fence.raw.GetCompletedValue() { + 0 => Ok(false), + 1 => Ok(true), + _ => Err(d::DeviceLost), + } + } + + fn create_event(&self) -> Result<(), d::OutOfMemory> { + unimplemented!() + } + + unsafe fn get_event_status(&self, _event: &()) -> Result { + unimplemented!() + } + + unsafe fn set_event(&self, _event: &mut ()) -> Result<(), d::OutOfMemory> { + unimplemented!() + } + + unsafe fn reset_event(&self, _event: &mut ()) -> Result<(), d::OutOfMemory> { + unimplemented!() + } + + unsafe fn free_memory(&self, memory: r::Memory) { + memory.heap.destroy(); + if let Some(buffer) = memory.resource { + buffer.destroy(); + } + } + + unsafe fn create_query_pool( + &self, + query_ty: query::Type, + count: query::Id, + ) -> Result { + let heap_ty = match query_ty { + query::Type::Occlusion => native::QueryHeapType::Occlusion, + query::Type::PipelineStatistics(_) => native::QueryHeapType::PipelineStatistics, + query::Type::Timestamp => native::QueryHeapType::Timestamp, + }; + + let (query_heap, hr) = self.raw.create_query_heap(heap_ty, count, 0); + assert_eq!(winerror::S_OK, hr); + + Ok(r::QueryPool { + raw: query_heap, + ty: query_ty, + }) + } + + unsafe fn destroy_query_pool(&self, pool: r::QueryPool) { + pool.raw.destroy(); + } + + unsafe fn get_query_pool_results( + &self, + pool: &r::QueryPool, + queries: Range, + data: &mut [u8], + stride: buffer::Stride, + flags: query::ResultFlags, + ) -> Result { + let num_queries = queries.end - queries.start; + let size = 8 * num_queries as u64; + let buffer_desc = d3d12::D3D12_RESOURCE_DESC { + Dimension: d3d12::D3D12_RESOURCE_DIMENSION_BUFFER, + Alignment: 0, + Width: size, + Height: 1, + DepthOrArraySize: 1, + MipLevels: 1, + Format: dxgiformat::DXGI_FORMAT_UNKNOWN, + SampleDesc: dxgitype::DXGI_SAMPLE_DESC { + Count: 1, + Quality: 0, + }, + Layout: d3d12::D3D12_TEXTURE_LAYOUT_ROW_MAJOR, + Flags: d3d12::D3D12_RESOURCE_FLAG_NONE, + }; + + let properties = d3d12::D3D12_HEAP_PROPERTIES { + Type: d3d12::D3D12_HEAP_TYPE_READBACK, + CPUPageProperty: d3d12::D3D12_CPU_PAGE_PROPERTY_UNKNOWN, + MemoryPoolPreference: d3d12::D3D12_MEMORY_POOL_UNKNOWN, + CreationNodeMask: 0, + VisibleNodeMask: 0, + }; + + let heap_desc = d3d12::D3D12_HEAP_DESC { + SizeInBytes: size, + Properties: properties, + Alignment: 0, + Flags: d3d12::D3D12_HEAP_FLAG_ALLOW_ONLY_BUFFERS, + }; + + let mut heap = native::Heap::null(); + assert_eq!( + self.raw + .clone() + .CreateHeap(&heap_desc, &d3d12::ID3D12Heap::uuidof(), heap.mut_void()), + winerror::S_OK + ); + + let mut temp_buffer = native::Resource::null(); + assert_eq!( + winerror::S_OK, + self.raw.clone().CreatePlacedResource( + heap.as_mut_ptr(), + 0, + &buffer_desc, + d3d12::D3D12_RESOURCE_STATE_COPY_DEST, + ptr::null(), + &d3d12::ID3D12Resource::uuidof(), + temp_buffer.mut_void(), + ) + ); + + let list_type = native::CmdListType::Direct; + let (com_allocator, hr_alloc) = self.raw.create_command_allocator(list_type); + assert_eq!( + winerror::S_OK, + hr_alloc, + "error on command allocator creation: {:x}", + hr_alloc + ); + let (com_list, hr_list) = self.raw.create_graphics_command_list( + list_type, + com_allocator, + native::PipelineState::null(), + 0, + ); + assert_eq!( + winerror::S_OK, + hr_list, + "error on command list creation: {:x}", + hr_list + ); + + let query_ty = match pool.ty { + query::Type::Occlusion => d3d12::D3D12_QUERY_TYPE_OCCLUSION, + query::Type::PipelineStatistics(_) => d3d12::D3D12_QUERY_TYPE_PIPELINE_STATISTICS, + query::Type::Timestamp => d3d12::D3D12_QUERY_TYPE_TIMESTAMP, + }; + com_list.ResolveQueryData( + pool.raw.as_mut_ptr(), + query_ty, + queries.start, + num_queries, + temp_buffer.as_mut_ptr(), + 0, + ); + com_list.close(); + + self.queues[0] + .raw + .ExecuteCommandLists(1, &(com_list.as_mut_ptr() as *mut _)); + + if !flags.contains(query::ResultFlags::WAIT) { + warn!("Can't really not wait here") + } + let result = self.queues[0].wait_idle_impl(); + if result.is_ok() { + let mut ptr = ptr::null_mut(); + assert_eq!( + winerror::S_OK, + temp_buffer.Map(0, &d3d12::D3D12_RANGE { Begin: 0, End: 0 }, &mut ptr) + ); + let src_data = slice::from_raw_parts(ptr as *const u64, num_queries as usize); + for (i, &value) in src_data.iter().enumerate() { + let dst = data.as_mut_ptr().add(i * stride as usize); + if flags.contains(query::ResultFlags::BITS_64) { + *(dst as *mut u64) = value; + } else { + *(dst as *mut u32) = value as u32; + } + } + temp_buffer.Unmap(0, &d3d12::D3D12_RANGE { Begin: 0, End: 0 }); + } + + temp_buffer.destroy(); + heap.destroy(); + com_list.destroy(); + com_allocator.destroy(); + + match result { + Ok(()) => Ok(true), + Err(e) => Err(e.into()), + } + } + + unsafe fn destroy_shader_module(&self, shader_lib: r::ShaderModule) { + if let r::ShaderModule::Compiled(shaders) = shader_lib { + for (_, blob) in shaders { + blob.destroy(); + } + } + } + + unsafe fn destroy_render_pass(&self, _rp: r::RenderPass) { + // Just drop + } + + unsafe fn destroy_pipeline_layout(&self, layout: r::PipelineLayout) { + layout.shared.signature.destroy(); + } + + unsafe fn destroy_graphics_pipeline(&self, pipeline: r::GraphicsPipeline) { + pipeline.raw.destroy(); + } + + unsafe fn destroy_compute_pipeline(&self, pipeline: r::ComputePipeline) { + pipeline.raw.destroy(); + } + + unsafe fn destroy_framebuffer(&self, _fb: r::Framebuffer) { + // Just drop + } + + unsafe fn destroy_buffer(&self, buffer: r::Buffer) { + match buffer { + r::Buffer::Bound(buffer) => { + if let Some(handle) = buffer.clear_uav { + self.srv_uav_pool.lock().free_handle(handle); + } + buffer.resource.destroy(); + } + r::Buffer::Unbound(_) => {} + } + } + + unsafe fn destroy_buffer_view(&self, view: r::BufferView) { + let mut pool = self.srv_uav_pool.lock(); + if let Some(handle) = view.handle_srv { + pool.free_handle(handle); + } + if let Some(handle) = view.handle_uav { + pool.free_handle(handle); + } + } + + unsafe fn destroy_image(&self, image: r::Image) { + match image { + r::Image::Bound(image) => { + let mut dsv_pool = self.dsv_pool.lock(); + for handle in image.clear_cv { + self.rtv_pool.lock().free_handle(handle); + } + for handle in image.clear_dv { + dsv_pool.free_handle(handle); + } + for handle in image.clear_sv { + dsv_pool.free_handle(handle); + } + image.resource.destroy(); + } + r::Image::Unbound(_) => {} + } + } + + unsafe fn destroy_image_view(&self, view: r::ImageView) { + if let Some(handle) = view.handle_srv { + self.srv_uav_pool.lock().free_handle(handle); + } + if let Some(handle) = view.handle_uav { + self.srv_uav_pool.lock().free_handle(handle); + } + if let r::RenderTargetHandle::Pool(handle) = view.handle_rtv { + self.rtv_pool.lock().free_handle(handle); + } + if let Some(handle) = view.handle_dsv { + self.dsv_pool.lock().free_handle(handle); + } + } + + unsafe fn destroy_sampler(&self, _sampler: r::Sampler) { + // We don't destroy samplers, they are permanently cached + } + + unsafe fn destroy_descriptor_pool(&self, pool: r::DescriptorPool) { + let view_range = pool.heap_srv_cbv_uav.range_allocator.initial_range(); + if view_range.start < view_range.end { + self.heap_srv_cbv_uav + .1 + .lock() + .free_range(view_range.clone()); + } + } + + unsafe fn destroy_descriptor_set_layout(&self, _layout: r::DescriptorSetLayout) { + // Just drop + } + + unsafe fn destroy_fence(&self, fence: r::Fence) { + fence.raw.destroy(); + } + + unsafe fn destroy_semaphore(&self, semaphore: r::Semaphore) { + semaphore.raw.destroy(); + } + + unsafe fn destroy_event(&self, _event: ()) { + unimplemented!() + } + + fn wait_idle(&self) -> Result<(), d::OutOfMemory> { + for queue in &self.queues { + queue.wait_idle_impl()?; + } + Ok(()) + } + + unsafe fn set_image_name(&self, image: &mut r::Image, name: &str) { + let cwstr = wide_cstr(name); + match *image { + r::Image::Unbound(ref mut image) => image.name = Some(cwstr), + r::Image::Bound(ref image) => { + image.resource.SetName(cwstr.as_ptr()); + } + } + } + + unsafe fn set_buffer_name(&self, buffer: &mut r::Buffer, name: &str) { + let cwstr = wide_cstr(name); + match *buffer { + r::Buffer::Unbound(ref mut buffer) => buffer.name = Some(cwstr), + r::Buffer::Bound(ref buffer) => { + buffer.resource.SetName(cwstr.as_ptr()); + } + } + } + + unsafe fn set_command_buffer_name(&self, command_buffer: &mut cmd::CommandBuffer, name: &str) { + let cwstr = wide_cstr(name); + if !command_buffer.raw.is_null() { + command_buffer.raw.SetName(cwstr.as_ptr()); + } + command_buffer.raw_name = cwstr; + } + + unsafe fn set_semaphore_name(&self, semaphore: &mut r::Semaphore, name: &str) { + let cwstr = wide_cstr(name); + semaphore.raw.SetName(cwstr.as_ptr()); + } + + unsafe fn set_fence_name(&self, fence: &mut r::Fence, name: &str) { + let cwstr = wide_cstr(name); + fence.raw.SetName(cwstr.as_ptr()); + } + + unsafe fn set_framebuffer_name(&self, _framebuffer: &mut r::Framebuffer, _name: &str) { + // ignored + } + + unsafe fn set_render_pass_name(&self, render_pass: &mut r::RenderPass, name: &str) { + render_pass.raw_name = wide_cstr(name); + } + + unsafe fn set_descriptor_set_name(&self, descriptor_set: &mut r::DescriptorSet, name: &str) { + descriptor_set.raw_name = wide_cstr(name); + } + + unsafe fn set_descriptor_set_layout_name( + &self, + _descriptor_set_layout: &mut r::DescriptorSetLayout, + _name: &str, + ) { + // ignored + } + + unsafe fn set_pipeline_layout_name(&self, pipeline_layout: &mut r::PipelineLayout, name: &str) { + let cwstr = wide_cstr(name); + pipeline_layout.shared.signature.SetName(cwstr.as_ptr()); + } + + fn start_capture(&self) { + //TODO + } + + fn stop_capture(&self) { + //TODO + } +} + +#[test] +fn test_identity_mapping() { + assert_eq!(conv::map_swizzle(format::Swizzle::NO), IDENTITY_MAPPING); +} diff --git a/third_party/rust/gfx-backend-dx12/src/internal.rs b/third_party/rust/gfx-backend-dx12/src/internal.rs index c00546f965ea..9b563e007a65 100644 --- a/third_party/rust/gfx-backend-dx12/src/internal.rs +++ b/third_party/rust/gfx-backend-dx12/src/internal.rs @@ -1,244 +1,244 @@ -use auxil::FastHashMap; -use std::{ffi::CStr, mem, ptr, sync::Arc}; - -use parking_lot::Mutex; -use winapi::{ - shared::{ - dxgiformat, dxgitype, - minwindef::{FALSE, TRUE}, - winerror, - }, - um::d3d12::{self, *}, - Interface, -}; - -use native; - -#[derive(Clone, Debug)] -pub struct BlitPipe { - pub pipeline: native::PipelineState, - pub signature: native::RootSignature, -} - -impl BlitPipe { - pub unsafe fn destroy(&self) { - self.pipeline.destroy(); - self.signature.destroy(); - } -} - -// Information to pass to the shader -#[repr(C)] -#[derive(Debug)] -pub struct BlitData { - pub src_offset: [f32; 2], - pub src_extent: [f32; 2], - pub layer: f32, - pub level: f32, -} - -pub type BlitKey = (dxgiformat::DXGI_FORMAT, d3d12::D3D12_FILTER); -type BlitMap = FastHashMap; - -#[derive(Debug)] -pub(crate) struct ServicePipes { - pub(crate) device: native::Device, - library: Arc, - blits_2d_color: Mutex, -} - -impl ServicePipes { - pub fn new(device: native::Device, library: Arc) -> Self { - ServicePipes { - device, - library, - blits_2d_color: Mutex::new(FastHashMap::default()), - } - } - - pub unsafe fn destroy(&self) { - let blits = self.blits_2d_color.lock(); - for (_, pipe) in &*blits { - pipe.destroy(); - } - } - - pub fn get_blit_2d_color(&self, key: BlitKey) -> BlitPipe { - let mut blits = self.blits_2d_color.lock(); - blits - .entry(key) - .or_insert_with(|| self.create_blit_2d_color(key)) - .clone() - } - - fn create_blit_2d_color(&self, (dst_format, filter): BlitKey) -> BlitPipe { - let descriptor_range = [native::DescriptorRange::new( - native::DescriptorRangeType::SRV, - 1, - native::Binding { - register: 0, - space: 0, - }, - 0, - )]; - - let root_parameters = [ - native::RootParameter::descriptor_table( - native::ShaderVisibility::All, - &descriptor_range, - ), - native::RootParameter::constants( - native::ShaderVisibility::All, - native::Binding { - register: 0, - space: 0, - }, - (mem::size_of::() / 4) as _, - ), - ]; - - let static_samplers = [native::StaticSampler::new( - native::ShaderVisibility::PS, - native::Binding { - register: 0, - space: 0, - }, - filter, - [ - d3d12::D3D12_TEXTURE_ADDRESS_MODE_CLAMP, - d3d12::D3D12_TEXTURE_ADDRESS_MODE_CLAMP, - d3d12::D3D12_TEXTURE_ADDRESS_MODE_CLAMP, - ], - 0.0, - 0, - d3d12::D3D12_COMPARISON_FUNC_ALWAYS, - native::StaticBorderColor::TransparentBlack, - 0.0..d3d12::D3D12_FLOAT32_MAX, - )]; - - let (signature_raw, error) = match self.library.serialize_root_signature( - native::RootSignatureVersion::V1_0, - &root_parameters, - &static_samplers, - native::RootSignatureFlags::empty(), - ) { - Ok((pair, hr)) if winerror::SUCCEEDED(hr) => pair, - Ok((_, hr)) => panic!("Can't serialize internal root signature: {:?}", hr), - Err(e) => panic!("Can't find serialization function: {:?}", e), - }; - - if !error.is_null() { - error!("D3D12SerializeRootSignature error: {:?}", unsafe { - error.as_c_str().to_str().unwrap() - }); - unsafe { error.destroy() }; - } - - let (signature, _hr) = self.device.create_root_signature(signature_raw, 0); - unsafe { - signature_raw.destroy(); - } - - let shader_src = include_bytes!("../shaders/blit.hlsl"); - // TODO: check results - let ((vs, _), _hr_vs) = native::Shader::compile( - shader_src, - unsafe { CStr::from_bytes_with_nul_unchecked(b"vs_5_0\0") }, - unsafe { CStr::from_bytes_with_nul_unchecked(b"vs_blit_2d\0") }, - native::ShaderCompileFlags::empty(), - ); - let ((ps, _), _hr_ps) = native::Shader::compile( - shader_src, - unsafe { CStr::from_bytes_with_nul_unchecked(b"ps_5_0\0") }, - unsafe { CStr::from_bytes_with_nul_unchecked(b"ps_blit_2d\0") }, - native::ShaderCompileFlags::empty(), - ); - - let mut rtvs = [dxgiformat::DXGI_FORMAT_UNKNOWN; 8]; - rtvs[0] = dst_format; - - let dummy_target = D3D12_RENDER_TARGET_BLEND_DESC { - BlendEnable: FALSE, - LogicOpEnable: FALSE, - SrcBlend: D3D12_BLEND_ZERO, - DestBlend: D3D12_BLEND_ZERO, - BlendOp: D3D12_BLEND_OP_ADD, - SrcBlendAlpha: D3D12_BLEND_ZERO, - DestBlendAlpha: D3D12_BLEND_ZERO, - BlendOpAlpha: D3D12_BLEND_OP_ADD, - LogicOp: D3D12_LOGIC_OP_CLEAR, - RenderTargetWriteMask: D3D12_COLOR_WRITE_ENABLE_ALL as _, - }; - let render_targets = [dummy_target; 8]; - - let pso_desc = d3d12::D3D12_GRAPHICS_PIPELINE_STATE_DESC { - pRootSignature: signature.as_mut_ptr(), - VS: *native::Shader::from_blob(vs), - PS: *native::Shader::from_blob(ps), - GS: *native::Shader::null(), - DS: *native::Shader::null(), - HS: *native::Shader::null(), - StreamOutput: d3d12::D3D12_STREAM_OUTPUT_DESC { - pSODeclaration: ptr::null(), - NumEntries: 0, - pBufferStrides: ptr::null(), - NumStrides: 0, - RasterizedStream: 0, - }, - BlendState: d3d12::D3D12_BLEND_DESC { - AlphaToCoverageEnable: FALSE, - IndependentBlendEnable: FALSE, - RenderTarget: render_targets, - }, - SampleMask: !0, - RasterizerState: D3D12_RASTERIZER_DESC { - FillMode: D3D12_FILL_MODE_SOLID, - CullMode: D3D12_CULL_MODE_NONE, - FrontCounterClockwise: TRUE, - DepthBias: 0, - DepthBiasClamp: 0.0, - SlopeScaledDepthBias: 0.0, - DepthClipEnable: FALSE, - MultisampleEnable: FALSE, - ForcedSampleCount: 0, - AntialiasedLineEnable: FALSE, - ConservativeRaster: D3D12_CONSERVATIVE_RASTERIZATION_MODE_OFF, - }, - DepthStencilState: unsafe { mem::zeroed() }, - InputLayout: d3d12::D3D12_INPUT_LAYOUT_DESC { - pInputElementDescs: ptr::null(), - NumElements: 0, - }, - IBStripCutValue: d3d12::D3D12_INDEX_BUFFER_STRIP_CUT_VALUE_DISABLED, - PrimitiveTopologyType: D3D12_PRIMITIVE_TOPOLOGY_TYPE_TRIANGLE, - NumRenderTargets: 1, - RTVFormats: rtvs, - DSVFormat: dxgiformat::DXGI_FORMAT_UNKNOWN, - SampleDesc: dxgitype::DXGI_SAMPLE_DESC { - Count: 1, - Quality: 0, - }, - NodeMask: 0, - CachedPSO: d3d12::D3D12_CACHED_PIPELINE_STATE { - pCachedBlob: ptr::null(), - CachedBlobSizeInBytes: 0, - }, - Flags: d3d12::D3D12_PIPELINE_STATE_FLAG_NONE, - }; - - let mut pipeline = native::PipelineState::null(); - let hr = unsafe { - self.device.CreateGraphicsPipelineState( - &pso_desc, - &d3d12::ID3D12PipelineState::uuidof(), - pipeline.mut_void(), - ) - }; - assert_eq!(hr, winerror::S_OK); - - BlitPipe { - pipeline, - signature, - } - } -} +use auxil::FastHashMap; +use std::{ffi::CStr, mem, ptr, sync::Arc}; + +use parking_lot::Mutex; +use winapi::{ + shared::{ + dxgiformat, dxgitype, + minwindef::{FALSE, TRUE}, + winerror, + }, + um::d3d12::{self, *}, + Interface, +}; + +use native; + +#[derive(Clone, Debug)] +pub struct BlitPipe { + pub pipeline: native::PipelineState, + pub signature: native::RootSignature, +} + +impl BlitPipe { + pub unsafe fn destroy(&self) { + self.pipeline.destroy(); + self.signature.destroy(); + } +} + +// Information to pass to the shader +#[repr(C)] +#[derive(Debug)] +pub struct BlitData { + pub src_offset: [f32; 2], + pub src_extent: [f32; 2], + pub layer: f32, + pub level: f32, +} + +pub type BlitKey = (dxgiformat::DXGI_FORMAT, d3d12::D3D12_FILTER); +type BlitMap = FastHashMap; + +#[derive(Debug)] +pub(crate) struct ServicePipes { + pub(crate) device: native::Device, + library: Arc, + blits_2d_color: Mutex, +} + +impl ServicePipes { + pub fn new(device: native::Device, library: Arc) -> Self { + ServicePipes { + device, + library, + blits_2d_color: Mutex::new(FastHashMap::default()), + } + } + + pub unsafe fn destroy(&self) { + let blits = self.blits_2d_color.lock(); + for (_, pipe) in &*blits { + pipe.destroy(); + } + } + + pub fn get_blit_2d_color(&self, key: BlitKey) -> BlitPipe { + let mut blits = self.blits_2d_color.lock(); + blits + .entry(key) + .or_insert_with(|| self.create_blit_2d_color(key)) + .clone() + } + + fn create_blit_2d_color(&self, (dst_format, filter): BlitKey) -> BlitPipe { + let descriptor_range = [native::DescriptorRange::new( + native::DescriptorRangeType::SRV, + 1, + native::Binding { + register: 0, + space: 0, + }, + 0, + )]; + + let root_parameters = [ + native::RootParameter::descriptor_table( + native::ShaderVisibility::All, + &descriptor_range, + ), + native::RootParameter::constants( + native::ShaderVisibility::All, + native::Binding { + register: 0, + space: 0, + }, + (mem::size_of::() / 4) as _, + ), + ]; + + let static_samplers = [native::StaticSampler::new( + native::ShaderVisibility::PS, + native::Binding { + register: 0, + space: 0, + }, + filter, + [ + d3d12::D3D12_TEXTURE_ADDRESS_MODE_CLAMP, + d3d12::D3D12_TEXTURE_ADDRESS_MODE_CLAMP, + d3d12::D3D12_TEXTURE_ADDRESS_MODE_CLAMP, + ], + 0.0, + 0, + d3d12::D3D12_COMPARISON_FUNC_ALWAYS, + native::StaticBorderColor::TransparentBlack, + 0.0..d3d12::D3D12_FLOAT32_MAX, + )]; + + let (signature_raw, error) = match self.library.serialize_root_signature( + native::RootSignatureVersion::V1_0, + &root_parameters, + &static_samplers, + native::RootSignatureFlags::empty(), + ) { + Ok((pair, hr)) if winerror::SUCCEEDED(hr) => pair, + Ok((_, hr)) => panic!("Can't serialize internal root signature: {:?}", hr), + Err(e) => panic!("Can't find serialization function: {:?}", e), + }; + + if !error.is_null() { + error!("D3D12SerializeRootSignature error: {:?}", unsafe { + error.as_c_str().to_str().unwrap() + }); + unsafe { error.destroy() }; + } + + let (signature, _hr) = self.device.create_root_signature(signature_raw, 0); + unsafe { + signature_raw.destroy(); + } + + let shader_src = include_bytes!("../shaders/blit.hlsl"); + // TODO: check results + let ((vs, _), _hr_vs) = native::Shader::compile( + shader_src, + unsafe { CStr::from_bytes_with_nul_unchecked(b"vs_5_0\0") }, + unsafe { CStr::from_bytes_with_nul_unchecked(b"vs_blit_2d\0") }, + native::ShaderCompileFlags::empty(), + ); + let ((ps, _), _hr_ps) = native::Shader::compile( + shader_src, + unsafe { CStr::from_bytes_with_nul_unchecked(b"ps_5_0\0") }, + unsafe { CStr::from_bytes_with_nul_unchecked(b"ps_blit_2d\0") }, + native::ShaderCompileFlags::empty(), + ); + + let mut rtvs = [dxgiformat::DXGI_FORMAT_UNKNOWN; 8]; + rtvs[0] = dst_format; + + let dummy_target = D3D12_RENDER_TARGET_BLEND_DESC { + BlendEnable: FALSE, + LogicOpEnable: FALSE, + SrcBlend: D3D12_BLEND_ZERO, + DestBlend: D3D12_BLEND_ZERO, + BlendOp: D3D12_BLEND_OP_ADD, + SrcBlendAlpha: D3D12_BLEND_ZERO, + DestBlendAlpha: D3D12_BLEND_ZERO, + BlendOpAlpha: D3D12_BLEND_OP_ADD, + LogicOp: D3D12_LOGIC_OP_CLEAR, + RenderTargetWriteMask: D3D12_COLOR_WRITE_ENABLE_ALL as _, + }; + let render_targets = [dummy_target; 8]; + + let pso_desc = d3d12::D3D12_GRAPHICS_PIPELINE_STATE_DESC { + pRootSignature: signature.as_mut_ptr(), + VS: *native::Shader::from_blob(vs), + PS: *native::Shader::from_blob(ps), + GS: *native::Shader::null(), + DS: *native::Shader::null(), + HS: *native::Shader::null(), + StreamOutput: d3d12::D3D12_STREAM_OUTPUT_DESC { + pSODeclaration: ptr::null(), + NumEntries: 0, + pBufferStrides: ptr::null(), + NumStrides: 0, + RasterizedStream: 0, + }, + BlendState: d3d12::D3D12_BLEND_DESC { + AlphaToCoverageEnable: FALSE, + IndependentBlendEnable: FALSE, + RenderTarget: render_targets, + }, + SampleMask: !0, + RasterizerState: D3D12_RASTERIZER_DESC { + FillMode: D3D12_FILL_MODE_SOLID, + CullMode: D3D12_CULL_MODE_NONE, + FrontCounterClockwise: TRUE, + DepthBias: 0, + DepthBiasClamp: 0.0, + SlopeScaledDepthBias: 0.0, + DepthClipEnable: FALSE, + MultisampleEnable: FALSE, + ForcedSampleCount: 0, + AntialiasedLineEnable: FALSE, + ConservativeRaster: D3D12_CONSERVATIVE_RASTERIZATION_MODE_OFF, + }, + DepthStencilState: unsafe { mem::zeroed() }, + InputLayout: d3d12::D3D12_INPUT_LAYOUT_DESC { + pInputElementDescs: ptr::null(), + NumElements: 0, + }, + IBStripCutValue: d3d12::D3D12_INDEX_BUFFER_STRIP_CUT_VALUE_DISABLED, + PrimitiveTopologyType: D3D12_PRIMITIVE_TOPOLOGY_TYPE_TRIANGLE, + NumRenderTargets: 1, + RTVFormats: rtvs, + DSVFormat: dxgiformat::DXGI_FORMAT_UNKNOWN, + SampleDesc: dxgitype::DXGI_SAMPLE_DESC { + Count: 1, + Quality: 0, + }, + NodeMask: 0, + CachedPSO: d3d12::D3D12_CACHED_PIPELINE_STATE { + pCachedBlob: ptr::null(), + CachedBlobSizeInBytes: 0, + }, + Flags: d3d12::D3D12_PIPELINE_STATE_FLAG_NONE, + }; + + let mut pipeline = native::PipelineState::null(); + let hr = unsafe { + self.device.CreateGraphicsPipelineState( + &pso_desc, + &d3d12::ID3D12PipelineState::uuidof(), + pipeline.mut_void(), + ) + }; + assert_eq!(hr, winerror::S_OK); + + BlitPipe { + pipeline, + signature, + } + } +} diff --git a/third_party/rust/gfx-backend-dx12/src/lib.rs b/third_party/rust/gfx-backend-dx12/src/lib.rs index a321a3cd98ef..c63f59acd3ba 100644 --- a/third_party/rust/gfx-backend-dx12/src/lib.rs +++ b/third_party/rust/gfx-backend-dx12/src/lib.rs @@ -1,1780 +1,1708 @@ -/*! -# D3D12 backend internals. - -## Resource transitions - -Vulkan semantics for resource states doesn't exactly match D3D12. - -For regular images, whenever there is a specific layout used, -we map it to a corresponding D3D12 resource state. - -For the swapchain images, we consider them to be in COMMON state -everywhere except for render passes, where it's forcefully -transitioned into the render state. When transfers to/from are -requested, we transition them into and from the COPY_ states. - -For buffers and images in General layout, we the best effort of guessing -the single mutable state based on the access flags. We can't reliably -handle a case where multiple mutable access flags are used. -*/ - -#[macro_use] -extern crate bitflags; -#[macro_use] -extern crate log; - -mod command; -mod conv; -mod descriptors_cpu; -mod device; -mod internal; -mod pool; -mod resource; -mod root_constants; -mod window; - -use auxil::FastHashMap; -use hal::{ - adapter, display, format as f, image, memory, pso::PipelineStage, queue as q, Features, Limits, - PhysicalDeviceProperties, -}; -use range_alloc::RangeAllocator; - -use parking_lot::{Mutex, RwLock}; -use smallvec::SmallVec; -use winapi::{ - shared::{dxgi, dxgi1_2, dxgi1_4, dxgi1_6, minwindef::TRUE, winerror}, - um::{d3d12, d3d12sdklayers, handleapi, synchapi, winbase}, - Interface, -}; - -use std::{ - borrow::{Borrow, BorrowMut}, - ffi::OsString, - fmt, - mem, - os::windows::ffi::OsStringExt, - //TODO: use parking_lot - sync::Arc, -}; - -use self::descriptors_cpu::DescriptorCpuPool; -use crate::resource::Image; - -#[derive(Debug)] -pub(crate) struct HeapProperties { - pub page_property: d3d12::D3D12_CPU_PAGE_PROPERTY, - pub memory_pool: d3d12::D3D12_MEMORY_POOL, -} - -// https://msdn.microsoft.com/de-de/library/windows/desktop/dn770377(v=vs.85).aspx -// Only 16 input slots allowed. -const MAX_VERTEX_BUFFERS: usize = 16; -const MAX_DESCRIPTOR_SETS: usize = 8; - -const NUM_HEAP_PROPERTIES: usize = 3; - -pub type DescriptorIndex = u64; - -// Memory types are grouped according to the supported resources. -// Grouping is done to circumvent the limitations of heap tier 1 devices. -// Devices with Tier 1 will expose `BuffersOnly`, `ImageOnly` and `TargetOnly`. -// Devices with Tier 2 or higher will only expose `Universal`. -enum MemoryGroup { - Universal = 0, - BufferOnly, - ImageOnly, - TargetOnly, - - NumGroups, -} - -// https://msdn.microsoft.com/de-de/library/windows/desktop/dn788678(v=vs.85).aspx -static HEAPS_NUMA: [HeapProperties; NUM_HEAP_PROPERTIES] = [ - // DEFAULT - HeapProperties { - page_property: d3d12::D3D12_CPU_PAGE_PROPERTY_NOT_AVAILABLE, - memory_pool: d3d12::D3D12_MEMORY_POOL_L1, - }, - // UPLOAD - HeapProperties { - page_property: d3d12::D3D12_CPU_PAGE_PROPERTY_WRITE_COMBINE, - memory_pool: d3d12::D3D12_MEMORY_POOL_L0, - }, - // READBACK - HeapProperties { - page_property: d3d12::D3D12_CPU_PAGE_PROPERTY_WRITE_BACK, - memory_pool: d3d12::D3D12_MEMORY_POOL_L0, - }, -]; - -static HEAPS_UMA: [HeapProperties; NUM_HEAP_PROPERTIES] = [ - // DEFAULT - HeapProperties { - page_property: d3d12::D3D12_CPU_PAGE_PROPERTY_NOT_AVAILABLE, - memory_pool: d3d12::D3D12_MEMORY_POOL_L0, - }, - // UPLOAD - HeapProperties { - page_property: d3d12::D3D12_CPU_PAGE_PROPERTY_WRITE_COMBINE, - memory_pool: d3d12::D3D12_MEMORY_POOL_L0, - }, - // READBACK - HeapProperties { - page_property: d3d12::D3D12_CPU_PAGE_PROPERTY_WRITE_BACK, - memory_pool: d3d12::D3D12_MEMORY_POOL_L0, - }, -]; - -static HEAPS_CCUMA: [HeapProperties; NUM_HEAP_PROPERTIES] = [ - // DEFAULT - HeapProperties { - page_property: d3d12::D3D12_CPU_PAGE_PROPERTY_NOT_AVAILABLE, - memory_pool: d3d12::D3D12_MEMORY_POOL_L0, - }, - // UPLOAD - HeapProperties { - page_property: d3d12::D3D12_CPU_PAGE_PROPERTY_WRITE_BACK, - memory_pool: d3d12::D3D12_MEMORY_POOL_L0, - }, - //READBACK - HeapProperties { - page_property: d3d12::D3D12_CPU_PAGE_PROPERTY_WRITE_BACK, - memory_pool: d3d12::D3D12_MEMORY_POOL_L0, - }, -]; - -#[derive(Debug, Copy, Clone)] -pub enum QueueFamily { - // Specially marked present queue. - // It's basically a normal 3D queue but D3D12 swapchain creation requires an - // associated queue, which we don't know on `create_swapchain`. - Present, - Normal(q::QueueType), -} - -const MAX_QUEUES: usize = 16; // infinite, to be fair - -impl q::QueueFamily for QueueFamily { - fn queue_type(&self) -> q::QueueType { - match *self { - QueueFamily::Present => q::QueueType::General, - QueueFamily::Normal(ty) => ty, - } - } - fn max_queues(&self) -> usize { - match *self { - QueueFamily::Present => 1, - QueueFamily::Normal(_) => MAX_QUEUES, - } - } - fn id(&self) -> q::QueueFamilyId { - // This must match the order exposed by `QUEUE_FAMILIES` - q::QueueFamilyId(match *self { - QueueFamily::Present => 0, - QueueFamily::Normal(q::QueueType::General) => 1, - QueueFamily::Normal(q::QueueType::Compute) => 2, - QueueFamily::Normal(q::QueueType::Transfer) => 3, - _ => unreachable!(), - }) - } - fn supports_sparse_binding(&self) -> bool { - true - } -} - -impl QueueFamily { - fn native_type(&self) -> native::CmdListType { - use hal::queue::QueueFamily as _; - use native::CmdListType as Clt; - - let queue_type = self.queue_type(); - match queue_type { - q::QueueType::General | q::QueueType::Graphics => Clt::Direct, - q::QueueType::Compute => Clt::Compute, - q::QueueType::Transfer => Clt::Copy, - } - } -} - -static QUEUE_FAMILIES: [QueueFamily; 4] = [ - QueueFamily::Present, - QueueFamily::Normal(q::QueueType::General), - QueueFamily::Normal(q::QueueType::Compute), - QueueFamily::Normal(q::QueueType::Transfer), -]; - -#[derive(Default)] -struct Workarounds { - // On WARP, temporary CPU descriptors are still used by the runtime - // after we call `CopyDescriptors`. - avoid_cpu_descriptor_overwrites: bool, -} - -//Note: fields are dropped in the order of declaration, so we put the -// most owning fields last. -pub struct PhysicalDevice { - features: Features, - properties: PhysicalDeviceProperties, - format_properties: Arc, - private_caps: PrivateCapabilities, - workarounds: Workarounds, - heap_properties: &'static [HeapProperties; NUM_HEAP_PROPERTIES], - memory_properties: adapter::MemoryProperties, - // Indicates that there is currently an active logical device. - // Opening the same adapter multiple times will return the same D3D12Device again. - is_open: Arc>, - adapter: native::WeakPtr, - library: Arc, -} - -impl fmt::Debug for PhysicalDevice { - fn fmt(&self, fmt: &mut fmt::Formatter) -> fmt::Result { - fmt.write_str("PhysicalDevice") - } -} - -unsafe impl Send for PhysicalDevice {} -unsafe impl Sync for PhysicalDevice {} - -impl adapter::PhysicalDevice for PhysicalDevice { - unsafe fn open( - &self, - families: &[(&QueueFamily, &[q::QueuePriority])], - requested_features: Features, - ) -> Result, hal::device::CreationError> { - let mut open_guard = match self.is_open.try_lock() { - Some(inner) => inner, - None => return Err(hal::device::CreationError::TooManyObjects), - }; - - if !self.features().contains(requested_features) { - return Err(hal::device::CreationError::MissingFeature); - } - - let device_raw = match self - .library - .create_device(self.adapter, native::FeatureLevel::L11_0) - { - Ok((device, hr)) if winerror::SUCCEEDED(hr) => device, - Ok((_, hr)) => { - error!("error on device creation: {:x}", hr); - return Err(hal::device::CreationError::InitializationFailed); - } - Err(e) => panic!("device creation failed with {:?}", e), - }; - - // Always create the presentation queue in case we want to build a swapchain. - let (present_queue, hr_queue) = device_raw.create_command_queue( - QueueFamily::Present.native_type(), - native::Priority::Normal, - native::CommandQueueFlags::empty(), - 0, - ); - if !winerror::SUCCEEDED(hr_queue) { - error!("error on queue creation: {:x}", hr_queue); - } - - let mut device = Device::new(device_raw, &self, present_queue); - device.features = requested_features; - - let queue_groups = families - .iter() - .map(|&(&family, priorities)| { - use hal::queue::QueueFamily as _; - let mut group = q::QueueGroup::new(family.id()); - - let create_idle_event = || native::Event::create(true, false); - - match family { - QueueFamily::Present => { - // Exactly **one** present queue! - // Number of queues need to be larger than 0 else it - // violates the specification. - let queue = Queue { - raw: device.present_queue.clone(), - idle_fence: device.create_raw_fence(false), - idle_event: create_idle_event(), - }; - device.append_queue(queue.clone()); - group.add_queue(queue); - } - QueueFamily::Normal(_) => { - let list_type = family.native_type(); - for _ in 0..priorities.len() { - let (queue, hr_queue) = device_raw.create_command_queue( - list_type, - native::Priority::Normal, - native::CommandQueueFlags::empty(), - 0, - ); - - if winerror::SUCCEEDED(hr_queue) { - let queue = Queue { - raw: queue, - idle_fence: device.create_raw_fence(false), - idle_event: create_idle_event(), - }; - device.append_queue(queue.clone()); - group.add_queue(queue); - } else { - error!("error on queue creation: {:x}", hr_queue); - } - } - } - } - - group - }) - .collect(); - - *open_guard = true; - - Ok(adapter::Gpu { - device, - queue_groups, - }) - } - - fn format_properties(&self, fmt: Option) -> f::Properties { - let idx = fmt.map(|fmt| fmt as usize).unwrap_or(0); - self.format_properties.resolve(idx).properties - } - - fn image_format_properties( - &self, - format: f::Format, - dimensions: u8, - tiling: image::Tiling, - usage: image::Usage, - view_caps: image::ViewCapabilities, - ) -> Option { - conv::map_format(format)?; //filter out unknown formats - let format_info = self.format_properties.resolve(format as usize); - - let supported_usage = { - use hal::image::Usage as U; - let props = match tiling { - image::Tiling::Optimal => format_info.properties.optimal_tiling, - image::Tiling::Linear => format_info.properties.linear_tiling, - }; - let mut flags = U::empty(); - if props.contains(f::ImageFeature::TRANSFER_SRC) { - flags |= U::TRANSFER_SRC; - } - if props.contains(f::ImageFeature::TRANSFER_DST) { - flags |= U::TRANSFER_DST; - } - if props.contains(f::ImageFeature::SAMPLED) { - flags |= U::SAMPLED; - } - if props.contains(f::ImageFeature::STORAGE) { - flags |= U::STORAGE; - } - if props.contains(f::ImageFeature::COLOR_ATTACHMENT) { - flags |= U::COLOR_ATTACHMENT; - } - if props.contains(f::ImageFeature::DEPTH_STENCIL_ATTACHMENT) { - flags |= U::DEPTH_STENCIL_ATTACHMENT; - } - flags - }; - if !supported_usage.contains(usage) { - return None; - } - - let max_resource_size = - (d3d12::D3D12_REQ_RESOURCE_SIZE_IN_MEGABYTES_EXPRESSION_A_TERM as usize) << 20; - Some(match tiling { - image::Tiling::Optimal => image::FormatProperties { - max_extent: match dimensions { - 1 => image::Extent { - width: d3d12::D3D12_REQ_TEXTURE1D_U_DIMENSION, - height: 1, - depth: 1, - }, - 2 => image::Extent { - width: d3d12::D3D12_REQ_TEXTURE2D_U_OR_V_DIMENSION, - height: d3d12::D3D12_REQ_TEXTURE2D_U_OR_V_DIMENSION, - depth: 1, - }, - 3 => image::Extent { - width: d3d12::D3D12_REQ_TEXTURE3D_U_V_OR_W_DIMENSION, - height: d3d12::D3D12_REQ_TEXTURE3D_U_V_OR_W_DIMENSION, - depth: d3d12::D3D12_REQ_TEXTURE3D_U_V_OR_W_DIMENSION, - }, - _ => return None, - }, - max_levels: d3d12::D3D12_REQ_MIP_LEVELS as _, - max_layers: match dimensions { - 1 => d3d12::D3D12_REQ_TEXTURE1D_ARRAY_AXIS_DIMENSION as _, - 2 => d3d12::D3D12_REQ_TEXTURE2D_ARRAY_AXIS_DIMENSION as _, - _ => return None, - }, - sample_count_mask: if dimensions == 2 - && !view_caps.contains(image::ViewCapabilities::KIND_CUBE) - && !usage.contains(image::Usage::STORAGE) - { - format_info.sample_count_mask - } else { - 0x1 - }, - max_resource_size, - }, - image::Tiling::Linear => image::FormatProperties { - max_extent: match dimensions { - 2 => image::Extent { - width: d3d12::D3D12_REQ_TEXTURE2D_U_OR_V_DIMENSION, - height: d3d12::D3D12_REQ_TEXTURE2D_U_OR_V_DIMENSION, - depth: 1, - }, - _ => return None, - }, - max_levels: 1, - max_layers: 1, - sample_count_mask: 0x1, - max_resource_size, - }, - }) - } - - fn memory_properties(&self) -> adapter::MemoryProperties { - self.memory_properties.clone() - } - - fn external_buffer_properties( - &self, - _usage: hal::buffer::Usage, - _sparse: hal::memory::SparseFlags, - _memory_type: hal::external_memory::ExternalMemoryType, - ) -> hal::external_memory::ExternalMemoryProperties { - unimplemented!() - } - - fn external_image_properties( - &self, - _format: hal::format::Format, - _dimensions: u8, - _tiling: hal::image::Tiling, - _usage: hal::image::Usage, - _view_caps: hal::image::ViewCapabilities, - _memory_type: hal::external_memory::ExternalMemoryType, - ) -> Result< - hal::external_memory::ExternalMemoryProperties, - hal::external_memory::ExternalImagePropertiesError, - > { - unimplemented!() - } - - fn features(&self) -> Features { - self.features - } - - fn properties(&self) -> PhysicalDeviceProperties { - self.properties - } - - unsafe fn enumerate_displays(&self) -> Vec> { - unimplemented!(); - } - - unsafe fn enumerate_compatible_planes( - &self, - _display: &display::Display, - ) -> Vec { - unimplemented!(); - } - - unsafe fn create_display_mode( - &self, - _display: &display::Display, - _resolution: (u32, u32), - _refresh_rate: u32, - ) -> Result, display::DisplayModeError> { - unimplemented!(); - } - - unsafe fn create_display_plane<'a>( - &self, - _display: &'a display::DisplayMode, - _plane: &'a display::Plane, - ) -> Result, hal::device::OutOfMemory> { - unimplemented!(); - } -} - -#[derive(Clone)] -pub struct Queue { - pub(crate) raw: native::CommandQueue, - idle_fence: native::Fence, - idle_event: native::Event, -} - -impl fmt::Debug for Queue { - fn fmt(&self, fmt: &mut fmt::Formatter) -> fmt::Result { - fmt.write_str("Queue") - } -} - -impl Queue { - unsafe fn destroy(&self) { - handleapi::CloseHandle(self.idle_event.0); - self.idle_fence.destroy(); - self.raw.destroy(); - } - - fn wait_idle_impl(&self) -> Result<(), hal::device::OutOfMemory> { - self.raw.signal(self.idle_fence, 1); - assert_eq!( - winerror::S_OK, - self.idle_fence.set_event_on_completion(self.idle_event, 1) - ); - - unsafe { - synchapi::WaitForSingleObject(self.idle_event.0, winbase::INFINITE); - } - - Ok(()) - } -} - -unsafe impl Send for Queue {} -unsafe impl Sync for Queue {} - -impl q::Queue for Queue { - unsafe fn submit<'a, Ic, Iw, Is>( - &mut self, - command_buffers: Ic, - _wait_semaphores: Iw, - _signal_semaphores: Is, - fence: Option<&mut resource::Fence>, - ) where - Ic: Iterator, - Iw: Iterator, - Is: Iterator, - { - // Reset idle fence and event - // That's safe here due to exclusive access to the queue - self.idle_fence.signal(0); - synchapi::ResetEvent(self.idle_event.0); - - // TODO: semaphores - let lists = command_buffers - .map(|cmd_buf| cmd_buf.as_raw_list()) - .collect::>(); - self.raw - .ExecuteCommandLists(lists.len() as _, lists.as_ptr()); - - if let Some(fence) = fence { - assert_eq!(winerror::S_OK, self.raw.Signal(fence.raw.as_mut_ptr(), 1)); - } - } - - unsafe fn bind_sparse<'a, Iw, Is, Ibi, Ib, Iii, Io, Ii>( - &mut self, - _wait_semaphores: Iw, - _signal_semaphores: Is, - _buffer_memory_binds: Ib, - _image_opaque_memory_binds: Io, - image_memory_binds: Ii, - device: &Device, - fence: Option<&resource::Fence>, - ) where - Ibi: Iterator>, - Ib: Iterator, - Iii: Iterator>, - Io: Iterator, - Ii: Iterator, - Iw: Iterator, - Is: Iterator, - { - // Reset idle fence and event - // That's safe here due to exclusive access to the queue - self.idle_fence.signal(0); - synchapi::ResetEvent(self.idle_event.0); - - // TODO: semaphores - - for (image, binds) in image_memory_binds { - let image = image.borrow_mut(); - - let (bits, image_kind) = match image { - Image::Unbound(unbound) => (unbound.format.surface_desc().bits, unbound.kind), - Image::Bound(bound) => (bound.surface_type.desc().bits, bound.kind), - }; - let block_size = match image_kind { - image::Kind::D1(_, _) => unimplemented!(), - image::Kind::D2(_, _, _, samples) => { - image::get_tile_size(image::TileKind::Flat(samples), bits) - } - image::Kind::D3(_, _, _) => image::get_tile_size(image::TileKind::Volume, bits), - }; - - // TODO avoid allocations - let mut resource_coords = Vec::new(); - let mut region_sizes = Vec::new(); - let mut range_flags = Vec::new(); - let mut heap_range_start_offsets = Vec::new(); - let mut range_tile_counts = Vec::new(); - - let mut heap: *mut d3d12::ID3D12Heap = std::ptr::null_mut(); - for bind in binds { - resource_coords.push(d3d12::D3D12_TILED_RESOURCE_COORDINATE { - X: bind.offset.x as u32, - Y: bind.offset.y as u32, - Z: bind.offset.z as u32, - Subresource: image.calc_subresource( - bind.subresource.level as _, - bind.subresource.layer as _, - 0, - ), - }); - - // Increment one tile if the extent is not a multiple of the block size - // Accessing these IS unsafe, but that is also true of Vulkan as the documentation - // requires an extent multiple of the block size. - let tile_extents = ( - (bind.extent.width / block_size.0 as u32) - + ((bind.extent.width % block_size.0 as u32) != 0) as u32, - (bind.extent.height / block_size.1 as u32) - + ((bind.extent.height % block_size.1 as u32) != 0) as u32, - (bind.extent.depth / block_size.2 as u32) - + ((bind.extent.depth % block_size.2 as u32) != 0) as u32, - ); - let number_tiles = tile_extents.0 * tile_extents.1 * tile_extents.2; - region_sizes.push(d3d12::D3D12_TILE_REGION_SIZE { - NumTiles: number_tiles, - UseBox: 1, - Width: tile_extents.0, - Height: tile_extents.1 as u16, - Depth: tile_extents.2 as u16, - }); - - if let Some((memory, memory_offset)) = bind.memory { - // TODO multiple heap support - // would involve multiple update tile mapping calls - if heap.is_null() { - heap = memory.borrow().heap.as_mut_ptr(); - } else if cfg!(debug_assertions) { - debug_assert_eq!(heap, memory.borrow().heap.as_mut_ptr()); - } - range_flags.push(d3d12::D3D12_TILE_RANGE_FLAG_NONE); - heap_range_start_offsets.push(memory_offset as u32); - } else { - range_flags.push(d3d12::D3D12_TILE_RANGE_FLAG_NULL); - heap_range_start_offsets.push(0); - } - range_tile_counts.push(number_tiles); - } - - match image { - Image::Bound(bound) => { - self.raw.UpdateTileMappings( - bound.resource.as_mut_ptr(), - resource_coords.len() as u32, - resource_coords.as_ptr(), - region_sizes.as_ptr(), - heap, - range_flags.len() as u32, - range_flags.as_ptr(), - heap_range_start_offsets.as_ptr(), - range_tile_counts.as_ptr(), - d3d12::D3D12_TILE_MAPPING_FLAG_NONE, - ); - } - Image::Unbound(image_unbound) => { - let mut resource = native::Resource::null(); - assert_eq!( - winerror::S_OK, - device.raw.clone().CreateReservedResource( - &image_unbound.desc, - d3d12::D3D12_RESOURCE_STATE_COMMON, - std::ptr::null(), - &d3d12::ID3D12Resource::uuidof(), - resource.mut_void(), - ) - ); - - self.raw.UpdateTileMappings( - resource.as_mut_ptr(), - resource_coords.len() as u32, - resource_coords.as_ptr(), - region_sizes.as_ptr(), - heap, - range_flags.len() as u32, - range_flags.as_ptr(), - heap_range_start_offsets.as_ptr(), - range_tile_counts.as_ptr(), - d3d12::D3D12_TILE_MAPPING_FLAG_NONE, - ); - - device.bind_image_resource(resource, image, resource::Place::Swapchain {}); - } - } - } - // TODO sparse buffers and opaque images iterated here - - if let Some(fence) = fence { - assert_eq!(winerror::S_OK, self.raw.Signal(fence.raw.as_mut_ptr(), 1)); - } - } - - unsafe fn present( - &mut self, - surface: &mut window::Surface, - image: window::SwapchainImage, - _wait_semaphore: Option<&mut resource::Semaphore>, - ) -> Result, hal::window::PresentError> { - surface.present(image).map(|()| None) - } - - fn wait_idle(&mut self) -> Result<(), hal::device::OutOfMemory> { - self.wait_idle_impl() - } - - fn timestamp_period(&self) -> f32 { - let mut frequency = 0u64; - unsafe { - self.raw.GetTimestampFrequency(&mut frequency); - } - (1_000_000_000.0 / frequency as f64) as f32 - } -} - -#[derive(Debug, Clone, Copy)] -enum MemoryArchitecture { - NUMA, - UMA, - CacheCoherentUMA, -} - -#[derive(Debug, Clone, Copy)] -pub struct PrivateCapabilities { - heterogeneous_resource_heaps: bool, - memory_architecture: MemoryArchitecture, -} - -#[derive(Clone, Debug)] -struct CmdSignatures { - draw: native::CommandSignature, - draw_indexed: native::CommandSignature, - dispatch: native::CommandSignature, -} - -impl CmdSignatures { - unsafe fn destroy(&self) { - self.draw.destroy(); - self.draw_indexed.destroy(); - self.dispatch.destroy(); - } -} - -// Shared objects between command buffers, owned by the device. -#[derive(Debug)] -struct Shared { - pub signatures: CmdSignatures, - pub service_pipes: internal::ServicePipes, -} - -impl Shared { - unsafe fn destroy(&self) { - self.signatures.destroy(); - self.service_pipes.destroy(); - } -} - -pub struct SamplerStorage { - map: Mutex>, - //TODO: respect the D3D12_REQ_SAMPLER_OBJECT_COUNT_PER_DEVICE limit - pool: Mutex, - heap: resource::DescriptorHeap, - origins: RwLock, -} - -impl SamplerStorage { - unsafe fn destroy(&mut self) { - self.pool.lock().destroy(); - self.heap.destroy(); - } -} - -pub struct Device { - raw: native::Device, - private_caps: PrivateCapabilities, - features: Features, - format_properties: Arc, - heap_properties: &'static [HeapProperties], - // CPU only pools - rtv_pool: Mutex, - dsv_pool: Mutex, - srv_uav_pool: Mutex, - descriptor_updater: Mutex, - // CPU/GPU descriptor heaps - heap_srv_cbv_uav: ( - resource::DescriptorHeap, - Mutex>, - ), - samplers: SamplerStorage, - events: Mutex>, - shared: Arc, - // Present queue exposed by the `Present` queue family. - // Required for swapchain creation. Only a single queue supports presentation. - present_queue: native::CommandQueue, - // List of all queues created from this device, including present queue. - // Needed for `wait_idle`. - queues: Vec, - // Indicates that there is currently an active device. - open: Arc>, - library: Arc, - render_doc: gfx_renderdoc::RenderDoc, -} - -impl fmt::Debug for Device { - fn fmt(&self, fmt: &mut fmt::Formatter) -> fmt::Result { - fmt.write_str("Device") - } -} - -unsafe impl Send for Device {} //blocked by ComPtr -unsafe impl Sync for Device {} //blocked by ComPtr - -impl Device { - fn new( - device: native::Device, - physical_device: &PhysicalDevice, - present_queue: native::CommandQueue, - ) -> Self { - // Allocate descriptor heaps - let rtv_pool = DescriptorCpuPool::new(device, native::DescriptorHeapType::Rtv); - let dsv_pool = DescriptorCpuPool::new(device, native::DescriptorHeapType::Dsv); - let srv_uav_pool = DescriptorCpuPool::new(device, native::DescriptorHeapType::CbvSrvUav); - - // maximum number of CBV/SRV/UAV descriptors in heap for Tier 1 - let view_capacity = 1_000_000; - let heap_srv_cbv_uav = Self::create_descriptor_heap_impl( - device, - native::DescriptorHeapType::CbvSrvUav, - true, - view_capacity, - ); - let view_range_allocator = RangeAllocator::new(0..(view_capacity as u64)); - - let sampler_pool = DescriptorCpuPool::new(device, native::DescriptorHeapType::Sampler); - let heap_sampler = Self::create_descriptor_heap_impl( - device, - native::DescriptorHeapType::Sampler, - true, - 2_048, - ); - - let descriptor_updater = descriptors_cpu::DescriptorUpdater::new( - device, - physical_device.workarounds.avoid_cpu_descriptor_overwrites, - ); - - let draw_signature = Self::create_command_signature(device, device::CommandSignature::Draw); - let draw_indexed_signature = - Self::create_command_signature(device, device::CommandSignature::DrawIndexed); - let dispatch_signature = - Self::create_command_signature(device, device::CommandSignature::Dispatch); - - let signatures = CmdSignatures { - draw: draw_signature, - draw_indexed: draw_indexed_signature, - dispatch: dispatch_signature, - }; - let service_pipes = - internal::ServicePipes::new(device, Arc::clone(&physical_device.library)); - let shared = Shared { - signatures, - service_pipes, - }; - - Device { - raw: device, - library: Arc::clone(&physical_device.library), - private_caps: physical_device.private_caps, - features: Features::empty(), - format_properties: physical_device.format_properties.clone(), - heap_properties: physical_device.heap_properties, - rtv_pool: Mutex::new(rtv_pool), - dsv_pool: Mutex::new(dsv_pool), - srv_uav_pool: Mutex::new(srv_uav_pool), - descriptor_updater: Mutex::new(descriptor_updater), - heap_srv_cbv_uav: (heap_srv_cbv_uav, Mutex::new(view_range_allocator)), - samplers: SamplerStorage { - map: Mutex::default(), - pool: Mutex::new(sampler_pool), - heap: heap_sampler, - origins: RwLock::default(), - }, - events: Mutex::new(Vec::new()), - shared: Arc::new(shared), - present_queue, - queues: Vec::new(), - open: Arc::clone(&physical_device.is_open), - render_doc: Default::default(), - } - } - - fn append_queue(&mut self, queue: Queue) { - self.queues.push(queue); - } - - /// Get the native d3d12 device. - /// - /// Required for FFI with libraries like RenderDoc. - pub unsafe fn as_raw(&self) -> *mut d3d12::ID3D12Device { - self.raw.as_mut_ptr() - } -} - -impl Drop for Device { - fn drop(&mut self) { - *self.open.lock() = false; - - unsafe { - for queue in &mut self.queues { - let _ = q::Queue::wait_idle(queue); - queue.destroy(); - } - - self.shared.destroy(); - self.heap_srv_cbv_uav.0.destroy(); - self.samplers.destroy(); - self.rtv_pool.lock().destroy(); - self.dsv_pool.lock().destroy(); - self.srv_uav_pool.lock().destroy(); - - self.descriptor_updater.lock().destroy(); - - // Debug tracking alive objects - let (debug_device, hr_debug) = self.raw.cast::(); - if winerror::SUCCEEDED(hr_debug) { - debug_device.ReportLiveDeviceObjects(d3d12sdklayers::D3D12_RLDO_DETAIL); - debug_device.destroy(); - } - - self.raw.destroy(); - } - } -} - -#[derive(Debug)] -pub struct Instance { - pub(crate) factory: native::Factory4, - library: Arc, - lib_dxgi: native::DxgiLib, -} - -impl Drop for Instance { - fn drop(&mut self) { - unsafe { - self.factory.destroy(); - } - } -} - -unsafe impl Send for Instance {} -unsafe impl Sync for Instance {} - -impl hal::Instance for Instance { - fn create(_: &str, _: u32) -> Result { - let lib_main = match native::D3D12Lib::new() { - Ok(lib) => lib, - Err(_) => return Err(hal::UnsupportedBackend), - }; - - #[cfg(debug_assertions)] - { - // Enable debug layer - match lib_main.get_debug_interface() { - Ok((debug_controller, hr)) if winerror::SUCCEEDED(hr) => { - debug_controller.enable_layer(); - unsafe { - debug_controller.Release(); - } - } - _ => { - warn!("Unable to get D3D12 debug interface"); - } - } - } - - let lib_dxgi = native::DxgiLib::new().unwrap(); - - // The `DXGI_CREATE_FACTORY_DEBUG` flag is only allowed to be passed to - // `CreateDXGIFactory2` if the debug interface is actually available. So - // we check for whether it exists first. - let factory_flags = match lib_dxgi.get_debug_interface1() { - Ok((queue, hr)) if winerror::SUCCEEDED(hr) => { - unsafe { queue.destroy() }; - native::FactoryCreationFlags::DEBUG - } - _ => native::FactoryCreationFlags::empty(), - }; - - // Create DXGI factory - let factory = match lib_dxgi.create_factory2(factory_flags) { - Ok((factory, hr)) if winerror::SUCCEEDED(hr) => factory, - Ok((_, hr)) => { - info!("Failed on dxgi factory creation: {:?}", hr); - return Err(hal::UnsupportedBackend); - } - Err(_) => return Err(hal::UnsupportedBackend), - }; - - Ok(Instance { - factory, - library: Arc::new(lib_main), - lib_dxgi, - }) - } - - fn enumerate_adapters(&self) -> Vec> { - use self::memory::Properties; - - // Try to use high performance order by default (returns None on Windows < 1803) - let (use_f6, factory6) = unsafe { - let (f6, hr) = self.factory.cast::(); - if winerror::SUCCEEDED(hr) { - // It's okay to decrement the refcount here because we - // have another reference to the factory already owned by `self`. - f6.destroy(); - (true, f6) - } else { - (false, native::WeakPtr::null()) - } - }; - - // Enumerate adapters - let mut cur_index = 0; - let mut adapters = Vec::new(); - loop { - let adapter = if use_f6 { - let mut adapter2 = native::WeakPtr::::null(); - let hr = unsafe { - factory6.EnumAdapterByGpuPreference( - cur_index, - 2, // HIGH_PERFORMANCE - &dxgi1_2::IDXGIAdapter2::uuidof(), - adapter2.mut_void() as *mut *mut _, - ) - }; - - if hr == winerror::DXGI_ERROR_NOT_FOUND { - break; - } - if !winerror::SUCCEEDED(hr) { - error!("Failed enumerating adapters: 0x{:x}", hr); - break; - } - - adapter2 - } else { - let mut adapter1 = native::WeakPtr::::null(); - let hr1 = unsafe { - self.factory - .EnumAdapters1(cur_index, adapter1.mut_void() as *mut *mut _) - }; - - if hr1 == winerror::DXGI_ERROR_NOT_FOUND { - break; - } - - let (adapter2, hr2) = unsafe { adapter1.cast::() }; - if !winerror::SUCCEEDED(hr2) { - error!("Failed casting to Adapter2: 0x{:x}", hr2); - break; - } - - unsafe { - adapter1.destroy(); - } - adapter2 - }; - - cur_index += 1; - - // Check for D3D12 support - // Create temporary device to get physical device information - let device = match self - .library - .create_device(adapter, native::FeatureLevel::L11_0) - { - Ok((device, hr)) if winerror::SUCCEEDED(hr) => device, - _ => continue, - }; - - // We have found a possible adapter - // acquire the device information - let mut desc: dxgi1_2::DXGI_ADAPTER_DESC2 = unsafe { mem::zeroed() }; - unsafe { - adapter.GetDesc2(&mut desc); - } - - let device_name = { - let len = desc.Description.iter().take_while(|&&c| c != 0).count(); - let name = ::from_wide(&desc.Description[..len]); - name.to_string_lossy().into_owned() - }; - - let mut features_architecture: d3d12::D3D12_FEATURE_DATA_ARCHITECTURE = - unsafe { mem::zeroed() }; - assert_eq!(winerror::S_OK, unsafe { - device.CheckFeatureSupport( - d3d12::D3D12_FEATURE_ARCHITECTURE, - &mut features_architecture as *mut _ as *mut _, - mem::size_of::() as _, - ) - }); - - let mut workarounds = Workarounds::default(); - - let info = adapter::AdapterInfo { - name: device_name, - vendor: desc.VendorId as usize, - device: desc.DeviceId as usize, - device_type: if (desc.Flags & dxgi::DXGI_ADAPTER_FLAG_SOFTWARE) != 0 { - workarounds.avoid_cpu_descriptor_overwrites = true; - adapter::DeviceType::VirtualGpu - } else if features_architecture.CacheCoherentUMA == TRUE { - adapter::DeviceType::IntegratedGpu - } else { - adapter::DeviceType::DiscreteGpu - }, - }; - - let mut features: d3d12::D3D12_FEATURE_DATA_D3D12_OPTIONS = unsafe { mem::zeroed() }; - assert_eq!(winerror::S_OK, unsafe { - device.CheckFeatureSupport( - d3d12::D3D12_FEATURE_D3D12_OPTIONS, - &mut features as *mut _ as *mut _, - mem::size_of::() as _, - ) - }); - - let depth_bounds_test_supported = { - let mut features2: d3d12::D3D12_FEATURE_DATA_D3D12_OPTIONS2 = - unsafe { mem::zeroed() }; - let hr = unsafe { - device.CheckFeatureSupport( - d3d12::D3D12_FEATURE_D3D12_OPTIONS2, - &mut features2 as *mut _ as *mut _, - mem::size_of::() as _, - ) - }; - if hr == winerror::S_OK { - features2.DepthBoundsTestSupported != 0 - } else { - false - } - }; - - let heterogeneous_resource_heaps = - features.ResourceHeapTier != d3d12::D3D12_RESOURCE_HEAP_TIER_1; - - let uma = features_architecture.UMA == TRUE; - let cc_uma = features_architecture.CacheCoherentUMA == TRUE; - - let (memory_architecture, heap_properties) = match (uma, cc_uma) { - (true, true) => (MemoryArchitecture::CacheCoherentUMA, &HEAPS_CCUMA), - (true, false) => (MemoryArchitecture::UMA, &HEAPS_UMA), - (false, _) => (MemoryArchitecture::NUMA, &HEAPS_NUMA), - }; - - // https://msdn.microsoft.com/en-us/library/windows/desktop/dn788678(v=vs.85).aspx - let base_memory_types: [adapter::MemoryType; NUM_HEAP_PROPERTIES] = - match memory_architecture { - MemoryArchitecture::NUMA => [ - // DEFAULT - adapter::MemoryType { - properties: Properties::DEVICE_LOCAL, - heap_index: 0, - }, - // UPLOAD - adapter::MemoryType { - properties: Properties::CPU_VISIBLE | Properties::COHERENT, - heap_index: 1, - }, - // READBACK - adapter::MemoryType { - properties: Properties::CPU_VISIBLE - | Properties::COHERENT - | Properties::CPU_CACHED, - heap_index: 1, - }, - ], - MemoryArchitecture::UMA => [ - // DEFAULT - adapter::MemoryType { - properties: Properties::DEVICE_LOCAL, - heap_index: 0, - }, - // UPLOAD - adapter::MemoryType { - properties: Properties::DEVICE_LOCAL - | Properties::CPU_VISIBLE - | Properties::COHERENT, - heap_index: 0, - }, - // READBACK - adapter::MemoryType { - properties: Properties::DEVICE_LOCAL - | Properties::CPU_VISIBLE - | Properties::COHERENT - | Properties::CPU_CACHED, - heap_index: 0, - }, - ], - MemoryArchitecture::CacheCoherentUMA => [ - // DEFAULT - adapter::MemoryType { - properties: Properties::DEVICE_LOCAL, - heap_index: 0, - }, - // UPLOAD - adapter::MemoryType { - properties: Properties::DEVICE_LOCAL - | Properties::CPU_VISIBLE - | Properties::COHERENT - | Properties::CPU_CACHED, - heap_index: 0, - }, - // READBACK - adapter::MemoryType { - properties: Properties::DEVICE_LOCAL - | Properties::CPU_VISIBLE - | Properties::COHERENT - | Properties::CPU_CACHED, - heap_index: 0, - }, - ], - }; - - let memory_types = if heterogeneous_resource_heaps { - base_memory_types.to_vec() - } else { - // We multiplicate the base memory types depending on the resource usage: - // 0.. 3: Reserved for futures use - // 4.. 6: Buffers - // 7.. 9: Images - // 10..12: Targets - // - // The supported memory types for a resource can be requested by asking for - // the memory requirements. Memory type indices are encoded as bitflags. - // `device::MEM_TYPE_MASK` (0b111) defines the bitmask for one base memory type group. - // The corresponding shift masks (`device::MEM_TYPE_BUFFER_SHIFT`, - // `device::MEM_TYPE_IMAGE_SHIFT`, `device::MEM_TYPE_TARGET_SHIFT`) - // denote the usage group. - let mut types = Vec::new(); - for i in 0..MemoryGroup::NumGroups as _ { - types.extend(base_memory_types.iter().map(|mem_type| { - let mut ty = mem_type.clone(); - - // Images and Targets are not host visible as we can't create - // a corresponding buffer for mapping. - if i == MemoryGroup::ImageOnly as _ || i == MemoryGroup::TargetOnly as _ { - ty.properties.remove(Properties::CPU_VISIBLE); - // Coherent and cached can only be on memory types that are cpu visible - ty.properties.remove(Properties::COHERENT); - ty.properties.remove(Properties::CPU_CACHED); - } - ty - })); - } - types - }; - - let memory_heaps = { - // Get the IDXGIAdapter3 from the created device to query video memory information. - let adapter_id = unsafe { device.GetAdapterLuid() }; - let adapter = { - let mut adapter = native::WeakPtr::::null(); - unsafe { - assert_eq!( - winerror::S_OK, - self.factory.EnumAdapterByLuid( - adapter_id, - &dxgi1_4::IDXGIAdapter3::uuidof(), - adapter.mut_void(), - ) - ); - } - adapter - }; - - let query_memory = |segment: dxgi1_4::DXGI_MEMORY_SEGMENT_GROUP| unsafe { - let mut mem_info: dxgi1_4::DXGI_QUERY_VIDEO_MEMORY_INFO = mem::zeroed(); - assert_eq!( - winerror::S_OK, - adapter.QueryVideoMemoryInfo(0, segment, &mut mem_info) - ); - mem_info.Budget - }; - - let mut heaps = vec![adapter::MemoryHeap { - size: query_memory(dxgi1_4::DXGI_MEMORY_SEGMENT_GROUP_LOCAL), - flags: memory::HeapFlags::DEVICE_LOCAL, - }]; - if let MemoryArchitecture::NUMA = memory_architecture { - heaps.push(adapter::MemoryHeap { - size: query_memory(dxgi1_4::DXGI_MEMORY_SEGMENT_GROUP_NON_LOCAL), - flags: memory::HeapFlags::empty(), - }); - } - heaps - }; - //TODO: find a way to get a tighter bound? - let sample_count_mask = 0x3F; - - // Theoretically vram limited, but in practice 2^20 is the limit - let tier3_practical_descriptor_limit = 1 << 20; - - let full_heap_count = match features.ResourceBindingTier { - d3d12::D3D12_RESOURCE_BINDING_TIER_1 => { - d3d12::D3D12_MAX_SHADER_VISIBLE_DESCRIPTOR_HEAP_SIZE_TIER_1 - } - d3d12::D3D12_RESOURCE_BINDING_TIER_2 => { - d3d12::D3D12_MAX_SHADER_VISIBLE_DESCRIPTOR_HEAP_SIZE_TIER_2 - } - d3d12::D3D12_RESOURCE_BINDING_TIER_3 => tier3_practical_descriptor_limit, - _ => unreachable!(), - } as _; - - let uav_limit = match features.ResourceBindingTier { - d3d12::D3D12_RESOURCE_BINDING_TIER_1 => 8, // conservative, is 64 on feature level 11.1 - d3d12::D3D12_RESOURCE_BINDING_TIER_2 => 64, - d3d12::D3D12_RESOURCE_BINDING_TIER_3 => tier3_practical_descriptor_limit, - _ => unreachable!(), - } as _; - - let mut tiled_resource_features = Features::empty(); - if features.TiledResourcesTier >= d3d12::D3D12_TILED_RESOURCES_TIER_1 { - tiled_resource_features |= Features::SPARSE_BINDING; - tiled_resource_features |= Features::SPARSE_RESIDENCY_IMAGE_2D; - tiled_resource_features |= Features::SPARSE_RESIDENCY_BUFFER; - tiled_resource_features |= Features::SPARSE_RESIDENCY_ALIASED; - tiled_resource_features |= Features::SPARSE_RESIDENCY_2_SAMPLES; - tiled_resource_features |= Features::SPARSE_RESIDENCY_4_SAMPLES; - tiled_resource_features |= Features::SPARSE_RESIDENCY_8_SAMPLES; - tiled_resource_features |= Features::SPARSE_RESIDENCY_16_SAMPLES; - } - if features.TiledResourcesTier >= d3d12::D3D12_TILED_RESOURCES_TIER_3 { - tiled_resource_features |= Features::SPARSE_RESIDENCY_IMAGE_3D; - } - - let conservative_faster_features = if features.ConservativeRasterizationTier - == d3d12::D3D12_CONSERVATIVE_RASTERIZATION_TIER_NOT_SUPPORTED - { - Features::empty() - } else { - Features::CONSERVATIVE_RASTERIZATION - }; - - let physical_device = PhysicalDevice { - library: Arc::clone(&self.library), - adapter, - features: - // TODO: add more features, based on - // https://msdn.microsoft.com/de-de/library/windows/desktop/mt186615(v=vs.85).aspx - Features::ROBUST_BUFFER_ACCESS | - Features::IMAGE_CUBE_ARRAY | - Features::GEOMETRY_SHADER | - Features::TESSELLATION_SHADER | - Features::NON_FILL_POLYGON_MODE | - if depth_bounds_test_supported { Features::DEPTH_BOUNDS } else { Features::empty() } | - //logic_op: false, // Optional on feature level 11_0 - Features::MULTI_DRAW_INDIRECT | - Features::FORMAT_BC | - Features::INSTANCE_RATE | - Features::DEPTH_CLAMP | - Features::SAMPLER_MIP_LOD_BIAS | - Features::SAMPLER_BORDER_COLOR | - Features::MUTABLE_COMPARISON_SAMPLER | - Features::SAMPLER_ANISOTROPY | - Features::TEXTURE_DESCRIPTOR_ARRAY | - Features::BUFFER_DESCRIPTOR_ARRAY | - Features::SAMPLER_MIRROR_CLAMP_EDGE | - Features::NDC_Y_UP | - Features::SHADER_SAMPLED_IMAGE_ARRAY_DYNAMIC_INDEXING | - Features::SHADER_STORAGE_IMAGE_ARRAY_DYNAMIC_INDEXING | - Features::SHADER_STORAGE_BUFFER_ARRAY_DYNAMIC_INDEXING | - Features::SHADER_UNIFORM_BUFFER_ARRAY_DYNAMIC_INDEXING | - Features::SAMPLED_TEXTURE_DESCRIPTOR_INDEXING | - Features::STORAGE_TEXTURE_DESCRIPTOR_INDEXING | - Features::STORAGE_BUFFER_DESCRIPTOR_INDEXING | - Features::UNIFORM_BUFFER_DESCRIPTOR_INDEXING | - Features::UNSIZED_DESCRIPTOR_ARRAY | - Features::DRAW_INDIRECT_COUNT | - Features::INDEPENDENT_BLENDING | - Features::SAMPLE_RATE_SHADING | - Features::FRAGMENT_STORES_AND_ATOMICS | - tiled_resource_features | - conservative_faster_features, - properties: PhysicalDeviceProperties { - limits: Limits { - //TODO: verify all of these not linked to constants - max_memory_allocation_count: !0, - max_bound_descriptor_sets: MAX_DESCRIPTOR_SETS as u16, - descriptor_limits: hal::DescriptorLimits { - max_per_stage_descriptor_samplers: match features.ResourceBindingTier { - d3d12::D3D12_RESOURCE_BINDING_TIER_1 => 16, - d3d12::D3D12_RESOURCE_BINDING_TIER_2 | d3d12::D3D12_RESOURCE_BINDING_TIER_3 | _ => d3d12::D3D12_MAX_SHADER_VISIBLE_SAMPLER_HEAP_SIZE, - } as _, - max_per_stage_descriptor_uniform_buffers: match features.ResourceBindingTier - { - d3d12::D3D12_RESOURCE_BINDING_TIER_1 | d3d12::D3D12_RESOURCE_BINDING_TIER_2 => { - d3d12::D3D12_COMMONSHADER_CONSTANT_BUFFER_API_SLOT_COUNT - } - d3d12::D3D12_RESOURCE_BINDING_TIER_3 | _ => full_heap_count as _, - } as _, - max_per_stage_descriptor_storage_buffers: uav_limit, - max_per_stage_descriptor_sampled_images: match features.ResourceBindingTier - { - d3d12::D3D12_RESOURCE_BINDING_TIER_1 => 128, - d3d12::D3D12_RESOURCE_BINDING_TIER_2 - | d3d12::D3D12_RESOURCE_BINDING_TIER_3 - | _ => full_heap_count, - } as _, - max_per_stage_descriptor_storage_images: uav_limit, - max_per_stage_resources: !0, - max_descriptor_set_samplers: d3d12::D3D12_MAX_SHADER_VISIBLE_SAMPLER_HEAP_SIZE as _, - max_descriptor_set_uniform_buffers: full_heap_count, - max_descriptor_set_uniform_buffers_dynamic: 8, - max_descriptor_set_storage_buffers: uav_limit, - max_descriptor_set_storage_buffers_dynamic: 4, - max_descriptor_set_sampled_images: full_heap_count, - max_descriptor_set_storage_images: uav_limit, - ..hal::DescriptorLimits::default() // TODO - }, - max_uniform_buffer_range: (d3d12::D3D12_REQ_CONSTANT_BUFFER_ELEMENT_COUNT * 16) - as _, - max_storage_buffer_range: !0, - // Is actually 256, but need space for the descriptors in there, so leave at 128 to discourage explosions - max_push_constants_size: 128, - max_image_1d_size: d3d12::D3D12_REQ_TEXTURE1D_U_DIMENSION as _, - max_image_2d_size: d3d12::D3D12_REQ_TEXTURE2D_U_OR_V_DIMENSION as _, - max_image_3d_size: d3d12::D3D12_REQ_TEXTURE3D_U_V_OR_W_DIMENSION as _, - max_image_cube_size: d3d12::D3D12_REQ_TEXTURECUBE_DIMENSION as _, - max_image_array_layers: d3d12::D3D12_REQ_TEXTURE2D_ARRAY_AXIS_DIMENSION as _, - max_texel_elements: 0, - max_patch_size: 0, - max_viewports: d3d12::D3D12_VIEWPORT_AND_SCISSORRECT_OBJECT_COUNT_PER_PIPELINE as _, - max_viewport_dimensions: [d3d12::D3D12_VIEWPORT_BOUNDS_MAX as _; 2], - max_framebuffer_extent: hal::image::Extent { - width: d3d12::D3D12_REQ_TEXTURE2D_U_OR_V_DIMENSION, - height: d3d12::D3D12_REQ_TEXTURE2D_U_OR_V_DIMENSION, - depth: 1, - }, - max_framebuffer_layers: 1, - max_compute_work_group_count: [ - d3d12::D3D12_CS_DISPATCH_MAX_THREAD_GROUPS_PER_DIMENSION, - d3d12::D3D12_CS_DISPATCH_MAX_THREAD_GROUPS_PER_DIMENSION, - d3d12::D3D12_CS_DISPATCH_MAX_THREAD_GROUPS_PER_DIMENSION, - ], - max_compute_work_group_invocations: d3d12::D3D12_CS_THREAD_GROUP_MAX_THREADS_PER_GROUP as _, - max_compute_work_group_size: [ - d3d12::D3D12_CS_THREAD_GROUP_MAX_X, - d3d12::D3D12_CS_THREAD_GROUP_MAX_Y, - d3d12::D3D12_CS_THREAD_GROUP_MAX_Z, - ], - max_vertex_input_attributes: d3d12::D3D12_IA_VERTEX_INPUT_RESOURCE_SLOT_COUNT as _, - max_vertex_input_bindings: d3d12::D3D12_VS_INPUT_REGISTER_COUNT as _, - max_vertex_input_attribute_offset: 255, // TODO - max_vertex_input_binding_stride: d3d12::D3D12_REQ_MULTI_ELEMENT_STRUCTURE_SIZE_IN_BYTES as _, - max_vertex_output_components: d3d12::D3D12_VS_OUTPUT_REGISTER_COUNT as _, - max_fragment_input_components: d3d12::D3D12_PS_INPUT_REGISTER_COUNT as _, - max_fragment_output_attachments: d3d12::D3D12_PS_OUTPUT_REGISTER_COUNT as _, - max_fragment_dual_source_attachments: 1, - max_fragment_combined_output_resources: (d3d12::D3D12_PS_OUTPUT_REGISTER_COUNT + d3d12::D3D12_PS_CS_UAV_REGISTER_COUNT) as _, - min_texel_buffer_offset_alignment: 1, // TODO - min_uniform_buffer_offset_alignment: d3d12::D3D12_CONSTANT_BUFFER_DATA_PLACEMENT_ALIGNMENT as _, - min_storage_buffer_offset_alignment: 4, // TODO - framebuffer_color_sample_counts: sample_count_mask, - framebuffer_depth_sample_counts: sample_count_mask, - framebuffer_stencil_sample_counts: sample_count_mask, - max_color_attachments: d3d12::D3D12_SIMULTANEOUS_RENDER_TARGET_COUNT as _, - buffer_image_granularity: 1, - non_coherent_atom_size: 1, //TODO: confirm - max_sampler_anisotropy: 16., - optimal_buffer_copy_offset_alignment: d3d12::D3D12_TEXTURE_DATA_PLACEMENT_ALIGNMENT as _, - optimal_buffer_copy_pitch_alignment: d3d12::D3D12_TEXTURE_DATA_PITCH_ALIGNMENT as _, - min_vertex_input_binding_stride_alignment: 1, - ..Limits::default() //TODO - }, - dynamic_pipeline_states: hal::DynamicStates::VIEWPORT - | hal::DynamicStates::SCISSOR - | hal::DynamicStates::BLEND_CONSTANTS - | hal::DynamicStates::STENCIL_REFERENCE, - downlevel: hal::DownlevelProperties::all_enabled(), - ..PhysicalDeviceProperties::default() - }, - format_properties: Arc::new(FormatProperties::new(device)), - private_caps: PrivateCapabilities { - heterogeneous_resource_heaps, - memory_architecture, - }, - workarounds, - heap_properties, - memory_properties: adapter::MemoryProperties { - memory_types, - memory_heaps, - }, - is_open: Arc::new(Mutex::new(false)), - }; - - let queue_families = QUEUE_FAMILIES.to_vec(); - - adapters.push(adapter::Adapter { - info, - physical_device, - queue_families, - }); - } - adapters - } - - unsafe fn create_surface( - &self, - has_handle: &impl raw_window_handle::HasRawWindowHandle, - ) -> Result { - match has_handle.raw_window_handle() { - raw_window_handle::RawWindowHandle::Windows(handle) => { - Ok(self.create_surface_from_hwnd(handle.hwnd)) - } - _ => Err(hal::window::InitError::UnsupportedWindowHandle), - } - } - - unsafe fn destroy_surface(&self, _surface: window::Surface) { - // TODO: Implement Surface cleanup - } - - unsafe fn create_display_plane_surface( - &self, - _display_plane: &display::DisplayPlane, - _plane_stack_index: u32, - _transformation: display::SurfaceTransform, - _alpha: display::DisplayPlaneAlpha, - _image_extent: hal::window::Extent2D, - ) -> Result { - unimplemented!(); - } -} - -#[derive(Copy, Clone, Debug, Eq, Hash, PartialEq)] -pub enum Backend {} -impl hal::Backend for Backend { - type Instance = Instance; - type PhysicalDevice = PhysicalDevice; - type Device = Device; - type Surface = window::Surface; - - type QueueFamily = QueueFamily; - type Queue = Queue; - type CommandBuffer = command::CommandBuffer; - - type Memory = resource::Memory; - type CommandPool = pool::CommandPool; - - type ShaderModule = resource::ShaderModule; - type RenderPass = resource::RenderPass; - type Framebuffer = resource::Framebuffer; - - type Buffer = resource::Buffer; - type BufferView = resource::BufferView; - type Image = resource::Image; - type ImageView = resource::ImageView; - type Sampler = resource::Sampler; - - type ComputePipeline = resource::ComputePipeline; - type GraphicsPipeline = resource::GraphicsPipeline; - type PipelineLayout = resource::PipelineLayout; - type PipelineCache = (); - type DescriptorSetLayout = resource::DescriptorSetLayout; - type DescriptorPool = resource::DescriptorPool; - type DescriptorSet = resource::DescriptorSet; - - type Fence = resource::Fence; - type Semaphore = resource::Semaphore; - type Event = (); - type QueryPool = resource::QueryPool; - - type Display = (); - type DisplayMode = (); -} - -fn validate_line_width(width: f32) { - // Note from the Vulkan spec: - // > If the wide lines feature is not enabled, lineWidth must be 1.0 - // Simply assert and no-op because DX12 never exposes `Features::LINE_WIDTH` - assert_eq!(width, 1.0); -} - -#[derive(Clone, Debug, Default)] -struct FormatInfo { - properties: f::Properties, - sample_count_mask: u8, -} - -#[derive(Debug)] -pub struct FormatProperties { - info: Box<[Mutex>]>, - device: native::Device, -} - -impl Drop for FormatProperties { - fn drop(&mut self) { - unsafe { - self.device.destroy(); - } - } -} - -impl FormatProperties { - fn new(device: native::Device) -> Self { - let mut buf = Vec::with_capacity(f::NUM_FORMATS); - buf.push(Mutex::new(Some(FormatInfo::default()))); - for _ in 1..f::NUM_FORMATS { - buf.push(Mutex::new(None)) - } - FormatProperties { - info: buf.into_boxed_slice(), - device, - } - } - - fn resolve(&self, idx: usize) -> FormatInfo { - let mut guard = self.info[idx].lock(); - use std::ops::Deref; - if let Some(info) = guard.deref().clone() { - return info; - } - let format: f::Format = unsafe { mem::transmute(idx as u32) }; - let is_compressed = format.surface_desc().is_compressed(); - let dxgi_format = match conv::map_format(format) { - Some(format) => format, - None => { - let info = FormatInfo::default(); - *guard = Some(info.clone()); - return info; - } - }; - - let properties = { - let mut props = f::Properties::default(); - let mut data = d3d12::D3D12_FEATURE_DATA_FORMAT_SUPPORT { - Format: dxgi_format, - Support1: unsafe { mem::zeroed() }, - Support2: unsafe { mem::zeroed() }, - }; - assert_eq!(winerror::S_OK, unsafe { - self.device.CheckFeatureSupport( - d3d12::D3D12_FEATURE_FORMAT_SUPPORT, - &mut data as *mut _ as *mut _, - mem::size_of::() as _, - ) - }); - let can_buffer = 0 != data.Support1 & d3d12::D3D12_FORMAT_SUPPORT1_BUFFER; - let can_image = 0 - != data.Support1 - & (d3d12::D3D12_FORMAT_SUPPORT1_TEXTURE1D - | d3d12::D3D12_FORMAT_SUPPORT1_TEXTURE2D - | d3d12::D3D12_FORMAT_SUPPORT1_TEXTURE3D - | d3d12::D3D12_FORMAT_SUPPORT1_TEXTURECUBE); - let can_linear = can_image && !is_compressed; - if can_image { - props.optimal_tiling |= f::ImageFeature::TRANSFER_SRC - | f::ImageFeature::TRANSFER_DST - | f::ImageFeature::SAMPLED; - } - if can_linear { - props.linear_tiling |= f::ImageFeature::TRANSFER_SRC - | f::ImageFeature::TRANSFER_DST - | f::ImageFeature::SAMPLED - | f::ImageFeature::BLIT_SRC; - } - if data.Support1 & d3d12::D3D12_FORMAT_SUPPORT1_IA_VERTEX_BUFFER != 0 { - props.buffer_features |= f::BufferFeature::VERTEX; - } - if data.Support1 & d3d12::D3D12_FORMAT_SUPPORT1_SHADER_SAMPLE != 0 { - props.optimal_tiling |= f::ImageFeature::SAMPLED_LINEAR; - } - if data.Support1 & d3d12::D3D12_FORMAT_SUPPORT1_RENDER_TARGET != 0 { - props.optimal_tiling |= - f::ImageFeature::COLOR_ATTACHMENT | f::ImageFeature::BLIT_DST; - } - if data.Support1 & d3d12::D3D12_FORMAT_SUPPORT1_BLENDABLE != 0 { - props.optimal_tiling |= f::ImageFeature::COLOR_ATTACHMENT_BLEND; - } - if data.Support1 & d3d12::D3D12_FORMAT_SUPPORT1_DEPTH_STENCIL != 0 { - props.optimal_tiling |= f::ImageFeature::DEPTH_STENCIL_ATTACHMENT; - } - if data.Support1 & d3d12::D3D12_FORMAT_SUPPORT1_SHADER_LOAD != 0 { - //TODO: check d3d12::D3D12_FORMAT_SUPPORT2_UAV_TYPED_LOAD ? - if can_buffer { - props.buffer_features |= f::BufferFeature::UNIFORM_TEXEL; - } - } - if data.Support2 & d3d12::D3D12_FORMAT_SUPPORT2_UAV_ATOMIC_ADD != 0 { - //TODO: other atomic flags? - if can_buffer { - props.buffer_features |= f::BufferFeature::STORAGE_TEXEL_ATOMIC; - } - if can_image { - props.optimal_tiling |= f::ImageFeature::STORAGE_ATOMIC; - } - } - if data.Support2 & d3d12::D3D12_FORMAT_SUPPORT2_UAV_TYPED_STORE != 0 { - if can_buffer { - props.buffer_features |= f::BufferFeature::STORAGE_TEXEL; - } - if can_image { - // Since read-only storage is exposed as SRV, we can guarantee read-only storage - // without checking D3D12_FORMAT_SUPPORT2_UAV_TYPED_LOAD first. - props.optimal_tiling |= f::ImageFeature::STORAGE; - - if data.Support2 & d3d12::D3D12_FORMAT_SUPPORT2_UAV_TYPED_LOAD != 0 { - props.optimal_tiling |= f::ImageFeature::STORAGE_READ_WRITE; - } - } - } - //TODO: blits, linear tiling - props - }; - - let sample_count_mask = if is_compressed { - // just an optimization to avoid the queries - 1 - } else { - let mut mask = 0; - for i in 0..6 { - let mut data = d3d12::D3D12_FEATURE_DATA_MULTISAMPLE_QUALITY_LEVELS { - Format: dxgi_format, - SampleCount: 1 << i, - Flags: 0, - NumQualityLevels: 0, - }; - assert_eq!(winerror::S_OK, unsafe { - self.device.CheckFeatureSupport( - d3d12::D3D12_FEATURE_MULTISAMPLE_QUALITY_LEVELS, - &mut data as *mut _ as *mut _, - mem::size_of::() as _, - ) - }); - if data.NumQualityLevels != 0 { - mask |= 1 << i; - } - } - mask - }; - - let info = FormatInfo { - properties, - sample_count_mask, - }; - *guard = Some(info.clone()); - info - } -} +/*! +# D3D12 backend internals. + +## Resource transitions + +Vulkan semantics for resource states doesn't exactly match D3D12. + +For regular images, whenever there is a specific layout used, +we map it to a corresponding D3D12 resource state. + +For the swapchain images, we consider them to be in COMMON state +everywhere except for render passes, where it's forcefully +transitioned into the render state. When transfers to/from are +requested, we transition them into and from the COPY_ states. + +For buffers and images in General layout, we the best effort of guessing +the single mutable state based on the access flags. We can't reliably +handle a case where multiple mutable access flags are used. +*/ + +#[macro_use] +extern crate bitflags; +#[macro_use] +extern crate log; + +mod command; +mod conv; +mod descriptors_cpu; +mod device; +mod internal; +mod pool; +mod resource; +mod root_constants; +mod window; + +use auxil::FastHashMap; +use hal::{ + adapter, format as f, image, memory, pso::PipelineStage, queue as q, Features, Limits, + PhysicalDeviceProperties, +}; +use range_alloc::RangeAllocator; + +use parking_lot::{Mutex, RwLock}; +use smallvec::SmallVec; +use winapi::{ + shared::{dxgi, dxgi1_2, dxgi1_4, dxgi1_6, minwindef::TRUE, winerror}, + um::{d3d12, d3d12sdklayers, handleapi, synchapi, winbase}, + Interface, +}; + +use std::{ + borrow::{Borrow, BorrowMut}, + ffi::OsString, + fmt, + mem, + os::windows::ffi::OsStringExt, + //TODO: use parking_lot + sync::Arc, +}; + +use self::descriptors_cpu::DescriptorCpuPool; +use crate::resource::Image; + +#[derive(Debug)] +pub(crate) struct HeapProperties { + pub page_property: d3d12::D3D12_CPU_PAGE_PROPERTY, + pub memory_pool: d3d12::D3D12_MEMORY_POOL, +} + +// https://msdn.microsoft.com/de-de/library/windows/desktop/dn770377(v=vs.85).aspx +// Only 16 input slots allowed. +const MAX_VERTEX_BUFFERS: usize = 16; +const MAX_DESCRIPTOR_SETS: usize = 8; + +const NUM_HEAP_PROPERTIES: usize = 3; + +pub type DescriptorIndex = u64; + +// Memory types are grouped according to the supported resources. +// Grouping is done to circumvent the limitations of heap tier 1 devices. +// Devices with Tier 1 will expose `BuffersOnly`, `ImageOnly` and `TargetOnly`. +// Devices with Tier 2 or higher will only expose `Universal`. +enum MemoryGroup { + Universal = 0, + BufferOnly, + ImageOnly, + TargetOnly, + + NumGroups, +} + +// https://msdn.microsoft.com/de-de/library/windows/desktop/dn788678(v=vs.85).aspx +static HEAPS_NUMA: [HeapProperties; NUM_HEAP_PROPERTIES] = [ + // DEFAULT + HeapProperties { + page_property: d3d12::D3D12_CPU_PAGE_PROPERTY_NOT_AVAILABLE, + memory_pool: d3d12::D3D12_MEMORY_POOL_L1, + }, + // UPLOAD + HeapProperties { + page_property: d3d12::D3D12_CPU_PAGE_PROPERTY_WRITE_COMBINE, + memory_pool: d3d12::D3D12_MEMORY_POOL_L0, + }, + // READBACK + HeapProperties { + page_property: d3d12::D3D12_CPU_PAGE_PROPERTY_WRITE_BACK, + memory_pool: d3d12::D3D12_MEMORY_POOL_L0, + }, +]; + +static HEAPS_UMA: [HeapProperties; NUM_HEAP_PROPERTIES] = [ + // DEFAULT + HeapProperties { + page_property: d3d12::D3D12_CPU_PAGE_PROPERTY_NOT_AVAILABLE, + memory_pool: d3d12::D3D12_MEMORY_POOL_L0, + }, + // UPLOAD + HeapProperties { + page_property: d3d12::D3D12_CPU_PAGE_PROPERTY_WRITE_COMBINE, + memory_pool: d3d12::D3D12_MEMORY_POOL_L0, + }, + // READBACK + HeapProperties { + page_property: d3d12::D3D12_CPU_PAGE_PROPERTY_WRITE_BACK, + memory_pool: d3d12::D3D12_MEMORY_POOL_L0, + }, +]; + +static HEAPS_CCUMA: [HeapProperties; NUM_HEAP_PROPERTIES] = [ + // DEFAULT + HeapProperties { + page_property: d3d12::D3D12_CPU_PAGE_PROPERTY_NOT_AVAILABLE, + memory_pool: d3d12::D3D12_MEMORY_POOL_L0, + }, + // UPLOAD + HeapProperties { + page_property: d3d12::D3D12_CPU_PAGE_PROPERTY_WRITE_BACK, + memory_pool: d3d12::D3D12_MEMORY_POOL_L0, + }, + //READBACK + HeapProperties { + page_property: d3d12::D3D12_CPU_PAGE_PROPERTY_WRITE_BACK, + memory_pool: d3d12::D3D12_MEMORY_POOL_L0, + }, +]; + +#[derive(Debug, Copy, Clone)] +pub enum QueueFamily { + // Specially marked present queue. + // It's basically a normal 3D queue but D3D12 swapchain creation requires an + // associated queue, which we don't know on `create_swapchain`. + Present, + Normal(q::QueueType), +} + +const MAX_QUEUES: usize = 16; // infinite, to be fair + +impl q::QueueFamily for QueueFamily { + fn queue_type(&self) -> q::QueueType { + match *self { + QueueFamily::Present => q::QueueType::General, + QueueFamily::Normal(ty) => ty, + } + } + fn max_queues(&self) -> usize { + match *self { + QueueFamily::Present => 1, + QueueFamily::Normal(_) => MAX_QUEUES, + } + } + fn id(&self) -> q::QueueFamilyId { + // This must match the order exposed by `QUEUE_FAMILIES` + q::QueueFamilyId(match *self { + QueueFamily::Present => 0, + QueueFamily::Normal(q::QueueType::General) => 1, + QueueFamily::Normal(q::QueueType::Compute) => 2, + QueueFamily::Normal(q::QueueType::Transfer) => 3, + _ => unreachable!(), + }) + } + fn supports_sparse_binding(&self) -> bool { + true + } +} + +impl QueueFamily { + fn native_type(&self) -> native::CmdListType { + use hal::queue::QueueFamily as _; + use native::CmdListType as Clt; + + let queue_type = self.queue_type(); + match queue_type { + q::QueueType::General | q::QueueType::Graphics => Clt::Direct, + q::QueueType::Compute => Clt::Compute, + q::QueueType::Transfer => Clt::Copy, + } + } +} + +static QUEUE_FAMILIES: [QueueFamily; 4] = [ + QueueFamily::Present, + QueueFamily::Normal(q::QueueType::General), + QueueFamily::Normal(q::QueueType::Compute), + QueueFamily::Normal(q::QueueType::Transfer), +]; + +#[derive(Default)] +struct Workarounds { + // On WARP, temporary CPU descriptors are still used by the runtime + // after we call `CopyDescriptors`. + avoid_cpu_descriptor_overwrites: bool, +} + +//Note: fields are dropped in the order of declaration, so we put the +// most owning fields last. +pub struct PhysicalDevice { + features: Features, + properties: PhysicalDeviceProperties, + format_properties: Arc, + private_caps: PrivateCapabilities, + workarounds: Workarounds, + heap_properties: &'static [HeapProperties; NUM_HEAP_PROPERTIES], + memory_properties: adapter::MemoryProperties, + // Indicates that there is currently an active logical device. + // Opening the same adapter multiple times will return the same D3D12Device again. + is_open: Arc>, + adapter: native::WeakPtr, + library: Arc, +} + +impl fmt::Debug for PhysicalDevice { + fn fmt(&self, fmt: &mut fmt::Formatter) -> fmt::Result { + fmt.write_str("PhysicalDevice") + } +} + +unsafe impl Send for PhysicalDevice {} +unsafe impl Sync for PhysicalDevice {} + +impl adapter::PhysicalDevice for PhysicalDevice { + unsafe fn open( + &self, + families: &[(&QueueFamily, &[q::QueuePriority])], + requested_features: Features, + ) -> Result, hal::device::CreationError> { + let mut open_guard = match self.is_open.try_lock() { + Some(inner) => inner, + None => return Err(hal::device::CreationError::TooManyObjects), + }; + + if !self.features().contains(requested_features) { + return Err(hal::device::CreationError::MissingFeature); + } + + let device_raw = match self + .library + .create_device(self.adapter, native::FeatureLevel::L11_0) + { + Ok((device, hr)) if winerror::SUCCEEDED(hr) => device, + Ok((_, hr)) => { + error!("error on device creation: {:x}", hr); + return Err(hal::device::CreationError::InitializationFailed); + } + Err(e) => panic!("device creation failed with {:?}", e), + }; + + // Always create the presentation queue in case we want to build a swapchain. + let (present_queue, hr_queue) = device_raw.create_command_queue( + QueueFamily::Present.native_type(), + native::Priority::Normal, + native::CommandQueueFlags::empty(), + 0, + ); + if !winerror::SUCCEEDED(hr_queue) { + error!("error on queue creation: {:x}", hr_queue); + } + + let mut device = Device::new(device_raw, &self, present_queue); + device.features = requested_features; + + let queue_groups = families + .iter() + .map(|&(&family, priorities)| { + use hal::queue::QueueFamily as _; + let mut group = q::QueueGroup::new(family.id()); + + let create_idle_event = || native::Event::create(true, false); + + match family { + QueueFamily::Present => { + // Exactly **one** present queue! + // Number of queues need to be larger than 0 else it + // violates the specification. + let queue = Queue { + raw: device.present_queue.clone(), + idle_fence: device.create_raw_fence(false), + idle_event: create_idle_event(), + }; + device.append_queue(queue.clone()); + group.add_queue(queue); + } + QueueFamily::Normal(_) => { + let list_type = family.native_type(); + for _ in 0..priorities.len() { + let (queue, hr_queue) = device_raw.create_command_queue( + list_type, + native::Priority::Normal, + native::CommandQueueFlags::empty(), + 0, + ); + + if winerror::SUCCEEDED(hr_queue) { + let queue = Queue { + raw: queue, + idle_fence: device.create_raw_fence(false), + idle_event: create_idle_event(), + }; + device.append_queue(queue.clone()); + group.add_queue(queue); + } else { + error!("error on queue creation: {:x}", hr_queue); + } + } + } + } + + group + }) + .collect(); + + *open_guard = true; + + Ok(adapter::Gpu { + device, + queue_groups, + }) + } + + fn format_properties(&self, fmt: Option) -> f::Properties { + let idx = fmt.map(|fmt| fmt as usize).unwrap_or(0); + self.format_properties.resolve(idx).properties + } + + fn image_format_properties( + &self, + format: f::Format, + dimensions: u8, + tiling: image::Tiling, + usage: image::Usage, + view_caps: image::ViewCapabilities, + ) -> Option { + conv::map_format(format)?; //filter out unknown formats + let format_info = self.format_properties.resolve(format as usize); + + let supported_usage = { + use hal::image::Usage as U; + let props = match tiling { + image::Tiling::Optimal => format_info.properties.optimal_tiling, + image::Tiling::Linear => format_info.properties.linear_tiling, + }; + let mut flags = U::empty(); + if props.contains(f::ImageFeature::TRANSFER_SRC) { + flags |= U::TRANSFER_SRC; + } + if props.contains(f::ImageFeature::TRANSFER_DST) { + flags |= U::TRANSFER_DST; + } + if props.contains(f::ImageFeature::SAMPLED) { + flags |= U::SAMPLED; + } + if props.contains(f::ImageFeature::STORAGE) { + flags |= U::STORAGE; + } + if props.contains(f::ImageFeature::COLOR_ATTACHMENT) { + flags |= U::COLOR_ATTACHMENT; + } + if props.contains(f::ImageFeature::DEPTH_STENCIL_ATTACHMENT) { + flags |= U::DEPTH_STENCIL_ATTACHMENT; + } + flags + }; + if !supported_usage.contains(usage) { + return None; + } + + let max_resource_size = + (d3d12::D3D12_REQ_RESOURCE_SIZE_IN_MEGABYTES_EXPRESSION_A_TERM as usize) << 20; + Some(match tiling { + image::Tiling::Optimal => image::FormatProperties { + max_extent: match dimensions { + 1 => image::Extent { + width: d3d12::D3D12_REQ_TEXTURE1D_U_DIMENSION, + height: 1, + depth: 1, + }, + 2 => image::Extent { + width: d3d12::D3D12_REQ_TEXTURE2D_U_OR_V_DIMENSION, + height: d3d12::D3D12_REQ_TEXTURE2D_U_OR_V_DIMENSION, + depth: 1, + }, + 3 => image::Extent { + width: d3d12::D3D12_REQ_TEXTURE3D_U_V_OR_W_DIMENSION, + height: d3d12::D3D12_REQ_TEXTURE3D_U_V_OR_W_DIMENSION, + depth: d3d12::D3D12_REQ_TEXTURE3D_U_V_OR_W_DIMENSION, + }, + _ => return None, + }, + max_levels: d3d12::D3D12_REQ_MIP_LEVELS as _, + max_layers: match dimensions { + 1 => d3d12::D3D12_REQ_TEXTURE1D_ARRAY_AXIS_DIMENSION as _, + 2 => d3d12::D3D12_REQ_TEXTURE2D_ARRAY_AXIS_DIMENSION as _, + _ => return None, + }, + sample_count_mask: if dimensions == 2 + && !view_caps.contains(image::ViewCapabilities::KIND_CUBE) + && !usage.contains(image::Usage::STORAGE) + { + format_info.sample_count_mask + } else { + 0x1 + }, + max_resource_size, + }, + image::Tiling::Linear => image::FormatProperties { + max_extent: match dimensions { + 2 => image::Extent { + width: d3d12::D3D12_REQ_TEXTURE2D_U_OR_V_DIMENSION, + height: d3d12::D3D12_REQ_TEXTURE2D_U_OR_V_DIMENSION, + depth: 1, + }, + _ => return None, + }, + max_levels: 1, + max_layers: 1, + sample_count_mask: 0x1, + max_resource_size, + }, + }) + } + + fn memory_properties(&self) -> adapter::MemoryProperties { + self.memory_properties.clone() + } + + fn features(&self) -> Features { + self.features + } + + fn properties(&self) -> PhysicalDeviceProperties { + self.properties + } +} + +#[derive(Clone)] +pub struct Queue { + pub(crate) raw: native::CommandQueue, + idle_fence: native::Fence, + idle_event: native::Event, +} + +impl fmt::Debug for Queue { + fn fmt(&self, fmt: &mut fmt::Formatter) -> fmt::Result { + fmt.write_str("Queue") + } +} + +impl Queue { + unsafe fn destroy(&self) { + handleapi::CloseHandle(self.idle_event.0); + self.idle_fence.destroy(); + self.raw.destroy(); + } + + fn wait_idle_impl(&self) -> Result<(), hal::device::OutOfMemory> { + self.raw.signal(self.idle_fence, 1); + assert_eq!( + winerror::S_OK, + self.idle_fence.set_event_on_completion(self.idle_event, 1) + ); + + unsafe { + synchapi::WaitForSingleObject(self.idle_event.0, winbase::INFINITE); + } + + Ok(()) + } +} + +unsafe impl Send for Queue {} +unsafe impl Sync for Queue {} + +impl q::Queue for Queue { + unsafe fn submit<'a, Ic, Iw, Is>( + &mut self, + command_buffers: Ic, + _wait_semaphores: Iw, + _signal_semaphores: Is, + fence: Option<&mut resource::Fence>, + ) where + Ic: Iterator, + Iw: Iterator, + Is: Iterator, + { + // Reset idle fence and event + // That's safe here due to exclusive access to the queue + self.idle_fence.signal(0); + synchapi::ResetEvent(self.idle_event.0); + + // TODO: semaphores + let lists = command_buffers + .map(|cmd_buf| cmd_buf.as_raw_list()) + .collect::>(); + self.raw + .ExecuteCommandLists(lists.len() as _, lists.as_ptr()); + + if let Some(fence) = fence { + assert_eq!(winerror::S_OK, self.raw.Signal(fence.raw.as_mut_ptr(), 1)); + } + } + + unsafe fn bind_sparse<'a, Iw, Is, Ibi, Ib, Iii, Io, Ii>( + &mut self, + _wait_semaphores: Iw, + _signal_semaphores: Is, + _buffer_memory_binds: Ib, + _image_opaque_memory_binds: Io, + image_memory_binds: Ii, + device: &Device, + fence: Option<&resource::Fence>, + ) where + Ibi: Iterator>, + Ib: Iterator, + Iii: Iterator>, + Io: Iterator, + Ii: Iterator, + Iw: Iterator, + Is: Iterator, + { + // Reset idle fence and event + // That's safe here due to exclusive access to the queue + self.idle_fence.signal(0); + synchapi::ResetEvent(self.idle_event.0); + + // TODO: semaphores + + for (image, binds) in image_memory_binds { + let image = image.borrow_mut(); + + let (bits, image_kind) = match image { + Image::Unbound(unbound) => (unbound.format.surface_desc().bits, unbound.kind), + Image::Bound(bound) => (bound.surface_type.desc().bits, bound.kind), + }; + let block_size = match image_kind { + image::Kind::D1(_, _) => unimplemented!(), + image::Kind::D2(_, _, _, samples) => { + image::get_tile_size(image::TileKind::Flat(samples), bits) + } + image::Kind::D3(_, _, _) => image::get_tile_size(image::TileKind::Volume, bits), + }; + + // TODO avoid allocations + let mut resource_coords = Vec::new(); + let mut region_sizes = Vec::new(); + let mut range_flags = Vec::new(); + let mut heap_range_start_offsets = Vec::new(); + let mut range_tile_counts = Vec::new(); + + let mut heap: *mut d3d12::ID3D12Heap = std::ptr::null_mut(); + for bind in binds { + resource_coords.push(d3d12::D3D12_TILED_RESOURCE_COORDINATE { + X: bind.offset.x as u32, + Y: bind.offset.y as u32, + Z: bind.offset.z as u32, + Subresource: image.calc_subresource( + bind.subresource.level as _, + bind.subresource.layer as _, + 0, + ), + }); + + // Increment one tile if the extent is not a multiple of the block size + // Accessing these IS unsafe, but that is also true of Vulkan as the documentation + // requires an extent multiple of the block size. + let tile_extents = ( + (bind.extent.width / block_size.0 as u32) + + ((bind.extent.width % block_size.0 as u32) != 0) as u32, + (bind.extent.height / block_size.1 as u32) + + ((bind.extent.height % block_size.1 as u32) != 0) as u32, + (bind.extent.depth / block_size.2 as u32) + + ((bind.extent.depth % block_size.2 as u32) != 0) as u32, + ); + let number_tiles = tile_extents.0 * tile_extents.1 * tile_extents.2; + region_sizes.push(d3d12::D3D12_TILE_REGION_SIZE { + NumTiles: number_tiles, + UseBox: 1, + Width: tile_extents.0, + Height: tile_extents.1 as u16, + Depth: tile_extents.2 as u16, + }); + + if let Some((memory, memory_offset)) = bind.memory { + // TODO multiple heap support + // would involve multiple update tile mapping calls + if heap.is_null() { + heap = memory.borrow().heap.as_mut_ptr(); + } else if cfg!(debug_assertions) { + debug_assert_eq!(heap, memory.borrow().heap.as_mut_ptr()); + } + range_flags.push(d3d12::D3D12_TILE_RANGE_FLAG_NONE); + heap_range_start_offsets.push(memory_offset as u32); + } else { + range_flags.push(d3d12::D3D12_TILE_RANGE_FLAG_NULL); + heap_range_start_offsets.push(0); + } + range_tile_counts.push(number_tiles); + } + + match image { + Image::Bound(bound) => { + self.raw.UpdateTileMappings( + bound.resource.as_mut_ptr(), + resource_coords.len() as u32, + resource_coords.as_ptr(), + region_sizes.as_ptr(), + heap, + range_flags.len() as u32, + range_flags.as_ptr(), + heap_range_start_offsets.as_ptr(), + range_tile_counts.as_ptr(), + d3d12::D3D12_TILE_MAPPING_FLAG_NONE, + ); + } + Image::Unbound(image_unbound) => { + let mut resource = native::Resource::null(); + assert_eq!( + winerror::S_OK, + device.raw.clone().CreateReservedResource( + &image_unbound.desc, + d3d12::D3D12_RESOURCE_STATE_COMMON, + std::ptr::null(), + &d3d12::ID3D12Resource::uuidof(), + resource.mut_void(), + ) + ); + + self.raw.UpdateTileMappings( + resource.as_mut_ptr(), + resource_coords.len() as u32, + resource_coords.as_ptr(), + region_sizes.as_ptr(), + heap, + range_flags.len() as u32, + range_flags.as_ptr(), + heap_range_start_offsets.as_ptr(), + range_tile_counts.as_ptr(), + d3d12::D3D12_TILE_MAPPING_FLAG_NONE, + ); + + device.bind_image_resource(resource, image, resource::Place::Swapchain {}); + } + } + } + // TODO sparse buffers and opaque images iterated here + + if let Some(fence) = fence { + assert_eq!(winerror::S_OK, self.raw.Signal(fence.raw.as_mut_ptr(), 1)); + } + } + + unsafe fn present( + &mut self, + surface: &mut window::Surface, + image: window::SwapchainImage, + _wait_semaphore: Option<&mut resource::Semaphore>, + ) -> Result, hal::window::PresentError> { + surface.present(image).map(|()| None) + } + + fn wait_idle(&mut self) -> Result<(), hal::device::OutOfMemory> { + self.wait_idle_impl() + } + + fn timestamp_period(&self) -> f32 { + let mut frequency = 0u64; + unsafe { + self.raw.GetTimestampFrequency(&mut frequency); + } + (1_000_000_000.0 / frequency as f64) as f32 + } +} + +#[derive(Debug, Clone, Copy)] +enum MemoryArchitecture { + NUMA, + UMA, + CacheCoherentUMA, +} + +#[derive(Debug, Clone, Copy)] +pub struct PrivateCapabilities { + heterogeneous_resource_heaps: bool, + memory_architecture: MemoryArchitecture, +} + +#[derive(Clone, Debug)] +struct CmdSignatures { + draw: native::CommandSignature, + draw_indexed: native::CommandSignature, + dispatch: native::CommandSignature, +} + +impl CmdSignatures { + unsafe fn destroy(&self) { + self.draw.destroy(); + self.draw_indexed.destroy(); + self.dispatch.destroy(); + } +} + +// Shared objects between command buffers, owned by the device. +#[derive(Debug)] +struct Shared { + pub signatures: CmdSignatures, + pub service_pipes: internal::ServicePipes, +} + +impl Shared { + unsafe fn destroy(&self) { + self.signatures.destroy(); + self.service_pipes.destroy(); + } +} + +pub struct SamplerStorage { + map: Mutex>, + //TODO: respect the D3D12_REQ_SAMPLER_OBJECT_COUNT_PER_DEVICE limit + pool: Mutex, + heap: resource::DescriptorHeap, + origins: RwLock, +} + +impl SamplerStorage { + unsafe fn destroy(&mut self) { + self.pool.lock().destroy(); + self.heap.destroy(); + } +} + +pub struct Device { + raw: native::Device, + private_caps: PrivateCapabilities, + features: Features, + format_properties: Arc, + heap_properties: &'static [HeapProperties], + // CPU only pools + rtv_pool: Mutex, + dsv_pool: Mutex, + srv_uav_pool: Mutex, + descriptor_updater: Mutex, + // CPU/GPU descriptor heaps + heap_srv_cbv_uav: ( + resource::DescriptorHeap, + Mutex>, + ), + samplers: SamplerStorage, + events: Mutex>, + shared: Arc, + // Present queue exposed by the `Present` queue family. + // Required for swapchain creation. Only a single queue supports presentation. + present_queue: native::CommandQueue, + // List of all queues created from this device, including present queue. + // Needed for `wait_idle`. + queues: Vec, + // Indicates that there is currently an active device. + open: Arc>, + library: Arc, +} + +impl fmt::Debug for Device { + fn fmt(&self, fmt: &mut fmt::Formatter) -> fmt::Result { + fmt.write_str("Device") + } +} + +unsafe impl Send for Device {} //blocked by ComPtr +unsafe impl Sync for Device {} //blocked by ComPtr + +impl Device { + fn new( + device: native::Device, + physical_device: &PhysicalDevice, + present_queue: native::CommandQueue, + ) -> Self { + // Allocate descriptor heaps + let rtv_pool = DescriptorCpuPool::new(device, native::DescriptorHeapType::Rtv); + let dsv_pool = DescriptorCpuPool::new(device, native::DescriptorHeapType::Dsv); + let srv_uav_pool = DescriptorCpuPool::new(device, native::DescriptorHeapType::CbvSrvUav); + + // maximum number of CBV/SRV/UAV descriptors in heap for Tier 1 + let view_capacity = 1_000_000; + let heap_srv_cbv_uav = Self::create_descriptor_heap_impl( + device, + native::DescriptorHeapType::CbvSrvUav, + true, + view_capacity, + ); + let view_range_allocator = RangeAllocator::new(0..(view_capacity as u64)); + + let sampler_pool = DescriptorCpuPool::new(device, native::DescriptorHeapType::Sampler); + let heap_sampler = Self::create_descriptor_heap_impl( + device, + native::DescriptorHeapType::Sampler, + true, + 2_048, + ); + + let descriptor_updater = descriptors_cpu::DescriptorUpdater::new( + device, + physical_device.workarounds.avoid_cpu_descriptor_overwrites, + ); + + let draw_signature = Self::create_command_signature(device, device::CommandSignature::Draw); + let draw_indexed_signature = + Self::create_command_signature(device, device::CommandSignature::DrawIndexed); + let dispatch_signature = + Self::create_command_signature(device, device::CommandSignature::Dispatch); + + let signatures = CmdSignatures { + draw: draw_signature, + draw_indexed: draw_indexed_signature, + dispatch: dispatch_signature, + }; + let service_pipes = + internal::ServicePipes::new(device, Arc::clone(&physical_device.library)); + let shared = Shared { + signatures, + service_pipes, + }; + + Device { + raw: device, + library: Arc::clone(&physical_device.library), + private_caps: physical_device.private_caps, + features: Features::empty(), + format_properties: physical_device.format_properties.clone(), + heap_properties: physical_device.heap_properties, + rtv_pool: Mutex::new(rtv_pool), + dsv_pool: Mutex::new(dsv_pool), + srv_uav_pool: Mutex::new(srv_uav_pool), + descriptor_updater: Mutex::new(descriptor_updater), + heap_srv_cbv_uav: (heap_srv_cbv_uav, Mutex::new(view_range_allocator)), + samplers: SamplerStorage { + map: Mutex::default(), + pool: Mutex::new(sampler_pool), + heap: heap_sampler, + origins: RwLock::default(), + }, + events: Mutex::new(Vec::new()), + shared: Arc::new(shared), + present_queue, + queues: Vec::new(), + open: Arc::clone(&physical_device.is_open), + } + } + + fn append_queue(&mut self, queue: Queue) { + self.queues.push(queue); + } + + /// Get the native d3d12 device. + /// + /// Required for FFI with libraries like RenderDoc. + pub unsafe fn as_raw(&self) -> *mut d3d12::ID3D12Device { + self.raw.as_mut_ptr() + } +} + +impl Drop for Device { + fn drop(&mut self) { + *self.open.lock() = false; + + unsafe { + for queue in &mut self.queues { + let _ = q::Queue::wait_idle(queue); + queue.destroy(); + } + + self.shared.destroy(); + self.heap_srv_cbv_uav.0.destroy(); + self.samplers.destroy(); + self.rtv_pool.lock().destroy(); + self.dsv_pool.lock().destroy(); + self.srv_uav_pool.lock().destroy(); + + self.descriptor_updater.lock().destroy(); + + // Debug tracking alive objects + let (debug_device, hr_debug) = self.raw.cast::(); + if winerror::SUCCEEDED(hr_debug) { + debug_device.ReportLiveDeviceObjects(d3d12sdklayers::D3D12_RLDO_DETAIL); + debug_device.destroy(); + } + + self.raw.destroy(); + } + } +} + +#[derive(Debug)] +pub struct Instance { + pub(crate) factory: native::Factory4, + library: Arc, + lib_dxgi: native::DxgiLib, +} + +impl Drop for Instance { + fn drop(&mut self) { + unsafe { + self.factory.destroy(); + } + } +} + +unsafe impl Send for Instance {} +unsafe impl Sync for Instance {} + +impl hal::Instance for Instance { + fn create(_: &str, _: u32) -> Result { + let lib_main = match native::D3D12Lib::new() { + Ok(lib) => lib, + Err(_) => return Err(hal::UnsupportedBackend), + }; + + #[cfg(debug_assertions)] + { + // Enable debug layer + match lib_main.get_debug_interface() { + Ok((debug_controller, hr)) if winerror::SUCCEEDED(hr) => { + debug_controller.enable_layer(); + unsafe { + debug_controller.Release(); + } + } + _ => { + warn!("Unable to get D3D12 debug interface"); + } + } + } + + let lib_dxgi = native::DxgiLib::new().unwrap(); + + // The `DXGI_CREATE_FACTORY_DEBUG` flag is only allowed to be passed to + // `CreateDXGIFactory2` if the debug interface is actually available. So + // we check for whether it exists first. + let factory_flags = match lib_dxgi.get_debug_interface1() { + Ok((queue, hr)) if winerror::SUCCEEDED(hr) => { + unsafe { queue.destroy() }; + native::FactoryCreationFlags::DEBUG + } + _ => native::FactoryCreationFlags::empty(), + }; + + // Create DXGI factory + let factory = match lib_dxgi.create_factory2(factory_flags) { + Ok((factory, hr)) if winerror::SUCCEEDED(hr) => factory, + Ok((_, hr)) => { + info!("Failed on dxgi factory creation: {:?}", hr); + return Err(hal::UnsupportedBackend); + } + Err(_) => return Err(hal::UnsupportedBackend), + }; + + Ok(Instance { + factory, + library: Arc::new(lib_main), + lib_dxgi, + }) + } + + fn enumerate_adapters(&self) -> Vec> { + use self::memory::Properties; + + // Try to use high performance order by default (returns None on Windows < 1803) + let (use_f6, factory6) = unsafe { + let (f6, hr) = self.factory.cast::(); + if winerror::SUCCEEDED(hr) { + // It's okay to decrement the refcount here because we + // have another reference to the factory already owned by `self`. + f6.destroy(); + (true, f6) + } else { + (false, native::WeakPtr::null()) + } + }; + + // Enumerate adapters + let mut cur_index = 0; + let mut adapters = Vec::new(); + loop { + let adapter = if use_f6 { + let mut adapter2 = native::WeakPtr::::null(); + let hr = unsafe { + factory6.EnumAdapterByGpuPreference( + cur_index, + 2, // HIGH_PERFORMANCE + &dxgi1_2::IDXGIAdapter2::uuidof(), + adapter2.mut_void() as *mut *mut _, + ) + }; + + if hr == winerror::DXGI_ERROR_NOT_FOUND { + break; + } + if !winerror::SUCCEEDED(hr) { + error!("Failed enumerating adapters: 0x{:x}", hr); + break; + } + + adapter2 + } else { + let mut adapter1 = native::WeakPtr::::null(); + let hr1 = unsafe { + self.factory + .EnumAdapters1(cur_index, adapter1.mut_void() as *mut *mut _) + }; + + if hr1 == winerror::DXGI_ERROR_NOT_FOUND { + break; + } + + let (adapter2, hr2) = unsafe { adapter1.cast::() }; + if !winerror::SUCCEEDED(hr2) { + error!("Failed casting to Adapter2: 0x{:x}", hr2); + break; + } + + unsafe { + adapter1.destroy(); + } + adapter2 + }; + + cur_index += 1; + + // Check for D3D12 support + // Create temporary device to get physical device information + let device = match self + .library + .create_device(adapter, native::FeatureLevel::L11_0) + { + Ok((device, hr)) if winerror::SUCCEEDED(hr) => device, + _ => continue, + }; + + // We have found a possible adapter + // acquire the device information + let mut desc: dxgi1_2::DXGI_ADAPTER_DESC2 = unsafe { mem::zeroed() }; + unsafe { + adapter.GetDesc2(&mut desc); + } + + let device_name = { + let len = desc.Description.iter().take_while(|&&c| c != 0).count(); + let name = ::from_wide(&desc.Description[..len]); + name.to_string_lossy().into_owned() + }; + + let mut features_architecture: d3d12::D3D12_FEATURE_DATA_ARCHITECTURE = + unsafe { mem::zeroed() }; + assert_eq!(winerror::S_OK, unsafe { + device.CheckFeatureSupport( + d3d12::D3D12_FEATURE_ARCHITECTURE, + &mut features_architecture as *mut _ as *mut _, + mem::size_of::() as _, + ) + }); + + let mut workarounds = Workarounds::default(); + + let info = adapter::AdapterInfo { + name: device_name, + vendor: desc.VendorId as usize, + device: desc.DeviceId as usize, + device_type: if (desc.Flags & dxgi::DXGI_ADAPTER_FLAG_SOFTWARE) != 0 { + workarounds.avoid_cpu_descriptor_overwrites = true; + adapter::DeviceType::VirtualGpu + } else if features_architecture.CacheCoherentUMA == TRUE { + adapter::DeviceType::IntegratedGpu + } else { + adapter::DeviceType::DiscreteGpu + }, + }; + + let mut features: d3d12::D3D12_FEATURE_DATA_D3D12_OPTIONS = unsafe { mem::zeroed() }; + assert_eq!(winerror::S_OK, unsafe { + device.CheckFeatureSupport( + d3d12::D3D12_FEATURE_D3D12_OPTIONS, + &mut features as *mut _ as *mut _, + mem::size_of::() as _, + ) + }); + + let depth_bounds_test_supported = { + let mut features2: d3d12::D3D12_FEATURE_DATA_D3D12_OPTIONS2 = + unsafe { mem::zeroed() }; + let hr = unsafe { + device.CheckFeatureSupport( + d3d12::D3D12_FEATURE_D3D12_OPTIONS2, + &mut features2 as *mut _ as *mut _, + mem::size_of::() as _, + ) + }; + if hr == winerror::S_OK { + features2.DepthBoundsTestSupported != 0 + } else { + false + } + }; + + let heterogeneous_resource_heaps = + features.ResourceHeapTier != d3d12::D3D12_RESOURCE_HEAP_TIER_1; + + let uma = features_architecture.UMA == TRUE; + let cc_uma = features_architecture.CacheCoherentUMA == TRUE; + + let (memory_architecture, heap_properties) = match (uma, cc_uma) { + (true, true) => (MemoryArchitecture::CacheCoherentUMA, &HEAPS_CCUMA), + (true, false) => (MemoryArchitecture::UMA, &HEAPS_UMA), + (false, _) => (MemoryArchitecture::NUMA, &HEAPS_NUMA), + }; + + // https://msdn.microsoft.com/en-us/library/windows/desktop/dn788678(v=vs.85).aspx + let base_memory_types: [adapter::MemoryType; NUM_HEAP_PROPERTIES] = + match memory_architecture { + MemoryArchitecture::NUMA => [ + // DEFAULT + adapter::MemoryType { + properties: Properties::DEVICE_LOCAL, + heap_index: 0, + }, + // UPLOAD + adapter::MemoryType { + properties: Properties::CPU_VISIBLE | Properties::COHERENT, + heap_index: 1, + }, + // READBACK + adapter::MemoryType { + properties: Properties::CPU_VISIBLE + | Properties::COHERENT + | Properties::CPU_CACHED, + heap_index: 1, + }, + ], + MemoryArchitecture::UMA => [ + // DEFAULT + adapter::MemoryType { + properties: Properties::DEVICE_LOCAL, + heap_index: 0, + }, + // UPLOAD + adapter::MemoryType { + properties: Properties::DEVICE_LOCAL + | Properties::CPU_VISIBLE + | Properties::COHERENT, + heap_index: 0, + }, + // READBACK + adapter::MemoryType { + properties: Properties::DEVICE_LOCAL + | Properties::CPU_VISIBLE + | Properties::COHERENT + | Properties::CPU_CACHED, + heap_index: 0, + }, + ], + MemoryArchitecture::CacheCoherentUMA => [ + // DEFAULT + adapter::MemoryType { + properties: Properties::DEVICE_LOCAL, + heap_index: 0, + }, + // UPLOAD + adapter::MemoryType { + properties: Properties::DEVICE_LOCAL + | Properties::CPU_VISIBLE + | Properties::COHERENT + | Properties::CPU_CACHED, + heap_index: 0, + }, + // READBACK + adapter::MemoryType { + properties: Properties::DEVICE_LOCAL + | Properties::CPU_VISIBLE + | Properties::COHERENT + | Properties::CPU_CACHED, + heap_index: 0, + }, + ], + }; + + let memory_types = if heterogeneous_resource_heaps { + base_memory_types.to_vec() + } else { + // We multiplicate the base memory types depending on the resource usage: + // 0.. 3: Reserved for futures use + // 4.. 6: Buffers + // 7.. 9: Images + // 10..12: Targets + // + // The supported memory types for a resource can be requested by asking for + // the memory requirements. Memory type indices are encoded as bitflags. + // `device::MEM_TYPE_MASK` (0b111) defines the bitmask for one base memory type group. + // The corresponding shift masks (`device::MEM_TYPE_BUFFER_SHIFT`, + // `device::MEM_TYPE_IMAGE_SHIFT`, `device::MEM_TYPE_TARGET_SHIFT`) + // denote the usage group. + let mut types = Vec::new(); + for i in 0..MemoryGroup::NumGroups as _ { + types.extend(base_memory_types.iter().map(|mem_type| { + let mut ty = mem_type.clone(); + + // Images and Targets are not host visible as we can't create + // a corresponding buffer for mapping. + if i == MemoryGroup::ImageOnly as _ || i == MemoryGroup::TargetOnly as _ { + ty.properties.remove(Properties::CPU_VISIBLE); + // Coherent and cached can only be on memory types that are cpu visible + ty.properties.remove(Properties::COHERENT); + ty.properties.remove(Properties::CPU_CACHED); + } + ty + })); + } + types + }; + + let memory_heaps = { + // Get the IDXGIAdapter3 from the created device to query video memory information. + let adapter_id = unsafe { device.GetAdapterLuid() }; + let adapter = { + let mut adapter = native::WeakPtr::::null(); + unsafe { + assert_eq!( + winerror::S_OK, + self.factory.EnumAdapterByLuid( + adapter_id, + &dxgi1_4::IDXGIAdapter3::uuidof(), + adapter.mut_void(), + ) + ); + } + adapter + }; + + let query_memory = |segment: dxgi1_4::DXGI_MEMORY_SEGMENT_GROUP| unsafe { + let mut mem_info: dxgi1_4::DXGI_QUERY_VIDEO_MEMORY_INFO = mem::zeroed(); + assert_eq!( + winerror::S_OK, + adapter.QueryVideoMemoryInfo(0, segment, &mut mem_info) + ); + mem_info.Budget + }; + + let mut heaps = vec![adapter::MemoryHeap { + size: query_memory(dxgi1_4::DXGI_MEMORY_SEGMENT_GROUP_LOCAL), + flags: memory::HeapFlags::DEVICE_LOCAL, + }]; + if let MemoryArchitecture::NUMA = memory_architecture { + heaps.push(adapter::MemoryHeap { + size: query_memory(dxgi1_4::DXGI_MEMORY_SEGMENT_GROUP_NON_LOCAL), + flags: memory::HeapFlags::empty(), + }); + } + heaps + }; + //TODO: find a way to get a tighter bound? + let sample_count_mask = 0x3F; + + // Theoretically vram limited, but in practice 2^20 is the limit + let tier3_practical_descriptor_limit = 1 << 20; + + let full_heap_count = match features.ResourceBindingTier { + d3d12::D3D12_RESOURCE_BINDING_TIER_1 => { + d3d12::D3D12_MAX_SHADER_VISIBLE_DESCRIPTOR_HEAP_SIZE_TIER_1 + } + d3d12::D3D12_RESOURCE_BINDING_TIER_2 => { + d3d12::D3D12_MAX_SHADER_VISIBLE_DESCRIPTOR_HEAP_SIZE_TIER_2 + } + d3d12::D3D12_RESOURCE_BINDING_TIER_3 => tier3_practical_descriptor_limit, + _ => unreachable!(), + } as _; + + let uav_limit = match features.ResourceBindingTier { + d3d12::D3D12_RESOURCE_BINDING_TIER_1 => 8, // conservative, is 64 on feature level 11.1 + d3d12::D3D12_RESOURCE_BINDING_TIER_2 => 64, + d3d12::D3D12_RESOURCE_BINDING_TIER_3 => tier3_practical_descriptor_limit, + _ => unreachable!(), + } as _; + + let mut tiled_resource_features = Features::empty(); + if features.TiledResourcesTier >= d3d12::D3D12_TILED_RESOURCES_TIER_1 { + tiled_resource_features |= Features::SPARSE_BINDING; + tiled_resource_features |= Features::SPARSE_RESIDENCY_IMAGE_2D; + tiled_resource_features |= Features::SPARSE_RESIDENCY_BUFFER; + tiled_resource_features |= Features::SPARSE_RESIDENCY_ALIASED; + tiled_resource_features |= Features::SPARSE_RESIDENCY_2_SAMPLES; + tiled_resource_features |= Features::SPARSE_RESIDENCY_4_SAMPLES; + tiled_resource_features |= Features::SPARSE_RESIDENCY_8_SAMPLES; + tiled_resource_features |= Features::SPARSE_RESIDENCY_16_SAMPLES; + } + if features.TiledResourcesTier >= d3d12::D3D12_TILED_RESOURCES_TIER_3 { + tiled_resource_features |= Features::SPARSE_RESIDENCY_IMAGE_3D; + } + + let conservative_faster_features = if features.ConservativeRasterizationTier + == d3d12::D3D12_CONSERVATIVE_RASTERIZATION_TIER_NOT_SUPPORTED + { + Features::empty() + } else { + Features::CONSERVATIVE_RASTERIZATION + }; + + let physical_device = PhysicalDevice { + library: Arc::clone(&self.library), + adapter, + features: + // TODO: add more features, based on + // https://msdn.microsoft.com/de-de/library/windows/desktop/mt186615(v=vs.85).aspx + Features::ROBUST_BUFFER_ACCESS | + Features::IMAGE_CUBE_ARRAY | + Features::GEOMETRY_SHADER | + Features::TESSELLATION_SHADER | + Features::NON_FILL_POLYGON_MODE | + if depth_bounds_test_supported { Features::DEPTH_BOUNDS } else { Features::empty() } | + //logic_op: false, // Optional on feature level 11_0 + Features::MULTI_DRAW_INDIRECT | + Features::FORMAT_BC | + Features::INSTANCE_RATE | + Features::DEPTH_CLAMP | + Features::SAMPLER_MIP_LOD_BIAS | + Features::SAMPLER_BORDER_COLOR | + Features::MUTABLE_COMPARISON_SAMPLER | + Features::SAMPLER_ANISOTROPY | + Features::TEXTURE_DESCRIPTOR_ARRAY | + Features::BUFFER_DESCRIPTOR_ARRAY | + Features::SAMPLER_MIRROR_CLAMP_EDGE | + Features::NDC_Y_UP | + Features::SHADER_SAMPLED_IMAGE_ARRAY_DYNAMIC_INDEXING | + Features::SHADER_STORAGE_IMAGE_ARRAY_DYNAMIC_INDEXING | + Features::SHADER_STORAGE_BUFFER_ARRAY_DYNAMIC_INDEXING | + Features::SHADER_UNIFORM_BUFFER_ARRAY_DYNAMIC_INDEXING | + Features::SAMPLED_TEXTURE_DESCRIPTOR_INDEXING | + Features::STORAGE_TEXTURE_DESCRIPTOR_INDEXING | + Features::STORAGE_BUFFER_DESCRIPTOR_INDEXING | + Features::UNIFORM_BUFFER_DESCRIPTOR_INDEXING | + Features::UNSIZED_DESCRIPTOR_ARRAY | + Features::DRAW_INDIRECT_COUNT | + tiled_resource_features | + conservative_faster_features, + properties: PhysicalDeviceProperties { + limits: Limits { + //TODO: verify all of these not linked to constants + max_memory_allocation_count: !0, + max_bound_descriptor_sets: MAX_DESCRIPTOR_SETS as u16, + descriptor_limits: hal::DescriptorLimits { + max_per_stage_descriptor_samplers: match features.ResourceBindingTier { + d3d12::D3D12_RESOURCE_BINDING_TIER_1 => 16, + d3d12::D3D12_RESOURCE_BINDING_TIER_2 | d3d12::D3D12_RESOURCE_BINDING_TIER_3 | _ => d3d12::D3D12_MAX_SHADER_VISIBLE_SAMPLER_HEAP_SIZE, + } as _, + max_per_stage_descriptor_uniform_buffers: match features.ResourceBindingTier + { + d3d12::D3D12_RESOURCE_BINDING_TIER_1 | d3d12::D3D12_RESOURCE_BINDING_TIER_2 => { + d3d12::D3D12_COMMONSHADER_CONSTANT_BUFFER_API_SLOT_COUNT + } + d3d12::D3D12_RESOURCE_BINDING_TIER_3 | _ => full_heap_count as _, + } as _, + max_per_stage_descriptor_storage_buffers: uav_limit, + max_per_stage_descriptor_sampled_images: match features.ResourceBindingTier + { + d3d12::D3D12_RESOURCE_BINDING_TIER_1 => 128, + d3d12::D3D12_RESOURCE_BINDING_TIER_2 + | d3d12::D3D12_RESOURCE_BINDING_TIER_3 + | _ => full_heap_count, + } as _, + max_per_stage_descriptor_storage_images: uav_limit, + max_per_stage_resources: !0, + max_descriptor_set_samplers: d3d12::D3D12_MAX_SHADER_VISIBLE_SAMPLER_HEAP_SIZE as _, + max_descriptor_set_uniform_buffers: full_heap_count, + max_descriptor_set_uniform_buffers_dynamic: 8, + max_descriptor_set_storage_buffers: uav_limit, + max_descriptor_set_storage_buffers_dynamic: 4, + max_descriptor_set_sampled_images: full_heap_count, + max_descriptor_set_storage_images: uav_limit, + ..hal::DescriptorLimits::default() // TODO + }, + max_uniform_buffer_range: (d3d12::D3D12_REQ_CONSTANT_BUFFER_ELEMENT_COUNT * 16) + as _, + max_storage_buffer_range: !0, + // Is actually 256, but need space for the descriptors in there, so leave at 128 to discourage explosions + max_push_constants_size: 128, + max_image_1d_size: d3d12::D3D12_REQ_TEXTURE1D_U_DIMENSION as _, + max_image_2d_size: d3d12::D3D12_REQ_TEXTURE2D_U_OR_V_DIMENSION as _, + max_image_3d_size: d3d12::D3D12_REQ_TEXTURE3D_U_V_OR_W_DIMENSION as _, + max_image_cube_size: d3d12::D3D12_REQ_TEXTURECUBE_DIMENSION as _, + max_image_array_layers: d3d12::D3D12_REQ_TEXTURE2D_ARRAY_AXIS_DIMENSION as _, + max_texel_elements: 0, + max_patch_size: 0, + max_viewports: d3d12::D3D12_VIEWPORT_AND_SCISSORRECT_OBJECT_COUNT_PER_PIPELINE as _, + max_viewport_dimensions: [d3d12::D3D12_VIEWPORT_BOUNDS_MAX as _; 2], + max_framebuffer_extent: hal::image::Extent { + width: d3d12::D3D12_REQ_TEXTURE2D_U_OR_V_DIMENSION, + height: d3d12::D3D12_REQ_TEXTURE2D_U_OR_V_DIMENSION, + depth: 1, + }, + max_framebuffer_layers: 1, + max_compute_work_group_count: [ + d3d12::D3D12_CS_DISPATCH_MAX_THREAD_GROUPS_PER_DIMENSION, + d3d12::D3D12_CS_DISPATCH_MAX_THREAD_GROUPS_PER_DIMENSION, + d3d12::D3D12_CS_DISPATCH_MAX_THREAD_GROUPS_PER_DIMENSION, + ], + max_compute_work_group_invocations: d3d12::D3D12_CS_THREAD_GROUP_MAX_THREADS_PER_GROUP as _, + max_compute_work_group_size: [ + d3d12::D3D12_CS_THREAD_GROUP_MAX_X, + d3d12::D3D12_CS_THREAD_GROUP_MAX_Y, + d3d12::D3D12_CS_THREAD_GROUP_MAX_Z, + ], + max_vertex_input_attributes: d3d12::D3D12_IA_VERTEX_INPUT_RESOURCE_SLOT_COUNT as _, + max_vertex_input_bindings: d3d12::D3D12_VS_INPUT_REGISTER_COUNT as _, + max_vertex_input_attribute_offset: 255, // TODO + max_vertex_input_binding_stride: d3d12::D3D12_REQ_MULTI_ELEMENT_STRUCTURE_SIZE_IN_BYTES as _, + max_vertex_output_components: d3d12::D3D12_VS_OUTPUT_REGISTER_COUNT as _, + max_fragment_input_components: d3d12::D3D12_PS_INPUT_REGISTER_COUNT as _, + max_fragment_output_attachments: d3d12::D3D12_PS_OUTPUT_REGISTER_COUNT as _, + max_fragment_dual_source_attachments: 1, + max_fragment_combined_output_resources: (d3d12::D3D12_PS_OUTPUT_REGISTER_COUNT + d3d12::D3D12_PS_CS_UAV_REGISTER_COUNT) as _, + min_texel_buffer_offset_alignment: 1, // TODO + min_uniform_buffer_offset_alignment: d3d12::D3D12_CONSTANT_BUFFER_DATA_PLACEMENT_ALIGNMENT as _, + min_storage_buffer_offset_alignment: 4, // TODO + framebuffer_color_sample_counts: sample_count_mask, + framebuffer_depth_sample_counts: sample_count_mask, + framebuffer_stencil_sample_counts: sample_count_mask, + max_color_attachments: d3d12::D3D12_SIMULTANEOUS_RENDER_TARGET_COUNT as _, + buffer_image_granularity: 1, + non_coherent_atom_size: 1, //TODO: confirm + max_sampler_anisotropy: 16., + optimal_buffer_copy_offset_alignment: d3d12::D3D12_TEXTURE_DATA_PLACEMENT_ALIGNMENT as _, + optimal_buffer_copy_pitch_alignment: d3d12::D3D12_TEXTURE_DATA_PITCH_ALIGNMENT as _, + min_vertex_input_binding_stride_alignment: 1, + ..Limits::default() //TODO + }, + dynamic_pipeline_states: hal::DynamicStates::VIEWPORT + | hal::DynamicStates::SCISSOR + | hal::DynamicStates::BLEND_CONSTANTS + | hal::DynamicStates::STENCIL_REFERENCE, + downlevel: hal::DownlevelProperties::all_enabled(), + ..PhysicalDeviceProperties::default() + }, + format_properties: Arc::new(FormatProperties::new(device)), + private_caps: PrivateCapabilities { + heterogeneous_resource_heaps, + memory_architecture, + }, + workarounds, + heap_properties, + memory_properties: adapter::MemoryProperties { + memory_types, + memory_heaps, + }, + is_open: Arc::new(Mutex::new(false)), + }; + + let queue_families = QUEUE_FAMILIES.to_vec(); + + adapters.push(adapter::Adapter { + info, + physical_device, + queue_families, + }); + } + adapters + } + + unsafe fn create_surface( + &self, + has_handle: &impl raw_window_handle::HasRawWindowHandle, + ) -> Result { + match has_handle.raw_window_handle() { + raw_window_handle::RawWindowHandle::Windows(handle) => { + Ok(self.create_surface_from_hwnd(handle.hwnd)) + } + _ => Err(hal::window::InitError::UnsupportedWindowHandle), + } + } + + unsafe fn destroy_surface(&self, _surface: window::Surface) { + // TODO: Implement Surface cleanup + } +} + +#[derive(Copy, Clone, Debug, Eq, Hash, PartialEq)] +pub enum Backend {} +impl hal::Backend for Backend { + type Instance = Instance; + type PhysicalDevice = PhysicalDevice; + type Device = Device; + type Surface = window::Surface; + + type QueueFamily = QueueFamily; + type Queue = Queue; + type CommandBuffer = command::CommandBuffer; + + type Memory = resource::Memory; + type CommandPool = pool::CommandPool; + + type ShaderModule = resource::ShaderModule; + type RenderPass = resource::RenderPass; + type Framebuffer = resource::Framebuffer; + + type Buffer = resource::Buffer; + type BufferView = resource::BufferView; + type Image = resource::Image; + type ImageView = resource::ImageView; + type Sampler = resource::Sampler; + + type ComputePipeline = resource::ComputePipeline; + type GraphicsPipeline = resource::GraphicsPipeline; + type PipelineLayout = resource::PipelineLayout; + type PipelineCache = (); + type DescriptorSetLayout = resource::DescriptorSetLayout; + type DescriptorPool = resource::DescriptorPool; + type DescriptorSet = resource::DescriptorSet; + + type Fence = resource::Fence; + type Semaphore = resource::Semaphore; + type Event = (); + type QueryPool = resource::QueryPool; +} + +fn validate_line_width(width: f32) { + // Note from the Vulkan spec: + // > If the wide lines feature is not enabled, lineWidth must be 1.0 + // Simply assert and no-op because DX12 never exposes `Features::LINE_WIDTH` + assert_eq!(width, 1.0); +} + +#[derive(Clone, Copy, Debug, Default)] +struct FormatInfo { + properties: f::Properties, + sample_count_mask: u8, +} + +#[derive(Debug)] +pub struct FormatProperties { + info: Box<[Mutex>]>, + device: native::Device, +} + +impl Drop for FormatProperties { + fn drop(&mut self) { + unsafe { + self.device.destroy(); + } + } +} + +impl FormatProperties { + fn new(device: native::Device) -> Self { + let mut buf = Vec::with_capacity(f::NUM_FORMATS); + buf.push(Mutex::new(Some(FormatInfo::default()))); + for _ in 1..f::NUM_FORMATS { + buf.push(Mutex::new(None)) + } + FormatProperties { + info: buf.into_boxed_slice(), + device, + } + } + + fn resolve(&self, idx: usize) -> FormatInfo { + let mut guard = self.info[idx].lock(); + if let Some(info) = *guard { + return info; + } + let format: f::Format = unsafe { mem::transmute(idx as u32) }; + let is_compressed = format.surface_desc().is_compressed(); + let dxgi_format = match conv::map_format(format) { + Some(format) => format, + None => { + let info = FormatInfo::default(); + *guard = Some(info); + return info; + } + }; + + let properties = { + let mut props = f::Properties::default(); + let mut data = d3d12::D3D12_FEATURE_DATA_FORMAT_SUPPORT { + Format: dxgi_format, + Support1: unsafe { mem::zeroed() }, + Support2: unsafe { mem::zeroed() }, + }; + assert_eq!(winerror::S_OK, unsafe { + self.device.CheckFeatureSupport( + d3d12::D3D12_FEATURE_FORMAT_SUPPORT, + &mut data as *mut _ as *mut _, + mem::size_of::() as _, + ) + }); + let can_buffer = 0 != data.Support1 & d3d12::D3D12_FORMAT_SUPPORT1_BUFFER; + let can_image = 0 + != data.Support1 + & (d3d12::D3D12_FORMAT_SUPPORT1_TEXTURE1D + | d3d12::D3D12_FORMAT_SUPPORT1_TEXTURE2D + | d3d12::D3D12_FORMAT_SUPPORT1_TEXTURE3D + | d3d12::D3D12_FORMAT_SUPPORT1_TEXTURECUBE); + let can_linear = can_image && !is_compressed; + if can_image { + props.optimal_tiling |= f::ImageFeature::TRANSFER_SRC + | f::ImageFeature::TRANSFER_DST + | f::ImageFeature::SAMPLED; + } + if can_linear { + props.linear_tiling |= f::ImageFeature::TRANSFER_SRC + | f::ImageFeature::TRANSFER_DST + | f::ImageFeature::SAMPLED + | f::ImageFeature::BLIT_SRC; + } + if data.Support1 & d3d12::D3D12_FORMAT_SUPPORT1_IA_VERTEX_BUFFER != 0 { + props.buffer_features |= f::BufferFeature::VERTEX; + } + if data.Support1 & d3d12::D3D12_FORMAT_SUPPORT1_SHADER_SAMPLE != 0 { + props.optimal_tiling |= f::ImageFeature::SAMPLED_LINEAR; + } + if data.Support1 & d3d12::D3D12_FORMAT_SUPPORT1_RENDER_TARGET != 0 { + props.optimal_tiling |= + f::ImageFeature::COLOR_ATTACHMENT | f::ImageFeature::BLIT_DST; + } + if data.Support1 & d3d12::D3D12_FORMAT_SUPPORT1_BLENDABLE != 0 { + props.optimal_tiling |= f::ImageFeature::COLOR_ATTACHMENT_BLEND; + } + if data.Support1 & d3d12::D3D12_FORMAT_SUPPORT1_DEPTH_STENCIL != 0 { + props.optimal_tiling |= f::ImageFeature::DEPTH_STENCIL_ATTACHMENT; + } + if data.Support1 & d3d12::D3D12_FORMAT_SUPPORT1_SHADER_LOAD != 0 { + //TODO: check d3d12::D3D12_FORMAT_SUPPORT2_UAV_TYPED_LOAD ? + if can_buffer { + props.buffer_features |= f::BufferFeature::UNIFORM_TEXEL; + } + } + if data.Support2 & d3d12::D3D12_FORMAT_SUPPORT2_UAV_ATOMIC_ADD != 0 { + //TODO: other atomic flags? + if can_buffer { + props.buffer_features |= f::BufferFeature::STORAGE_TEXEL_ATOMIC; + } + if can_image { + props.optimal_tiling |= f::ImageFeature::STORAGE_ATOMIC; + } + } + if data.Support2 & d3d12::D3D12_FORMAT_SUPPORT2_UAV_TYPED_STORE != 0 { + if can_buffer { + props.buffer_features |= f::BufferFeature::STORAGE_TEXEL; + } + if can_image { + // Since read-only storage is exposed as SRV, we can guarantee read-only storage + // without checking D3D12_FORMAT_SUPPORT2_UAV_TYPED_LOAD first. + props.optimal_tiling |= f::ImageFeature::STORAGE; + + if data.Support2 & d3d12::D3D12_FORMAT_SUPPORT2_UAV_TYPED_LOAD != 0 { + props.optimal_tiling |= f::ImageFeature::STORAGE_READ_WRITE; + } + } + } + //TODO: blits, linear tiling + props + }; + + let sample_count_mask = if is_compressed { + // just an optimization to avoid the queries + 1 + } else { + let mut mask = 0; + for i in 0..6 { + let mut data = d3d12::D3D12_FEATURE_DATA_MULTISAMPLE_QUALITY_LEVELS { + Format: dxgi_format, + SampleCount: 1 << i, + Flags: 0, + NumQualityLevels: 0, + }; + assert_eq!(winerror::S_OK, unsafe { + self.device.CheckFeatureSupport( + d3d12::D3D12_FEATURE_MULTISAMPLE_QUALITY_LEVELS, + &mut data as *mut _ as *mut _, + mem::size_of::() as _, + ) + }); + if data.NumQualityLevels != 0 { + mask |= 1 << i; + } + } + mask + }; + + let info = FormatInfo { + properties, + sample_count_mask, + }; + *guard = Some(info); + info + } +} diff --git a/third_party/rust/gfx-backend-dx12/src/pool.rs b/third_party/rust/gfx-backend-dx12/src/pool.rs index 74b8a6c9be62..3885e0d056bb 100644 --- a/third_party/rust/gfx-backend-dx12/src/pool.rs +++ b/third_party/rust/gfx-backend-dx12/src/pool.rs @@ -1,186 +1,186 @@ -use std::{fmt, sync::Arc}; - -use parking_lot::Mutex; -use winapi::shared::winerror; - -use crate::{command::CommandBuffer, Backend, Shared}; -use hal::{command, pool}; - -const REUSE_COUNT: usize = 64; - -pub type CommandAllocatorIndex = thunderdome::Index; - -struct AllocatorEntry { - raw: native::CommandAllocator, - active_lists: usize, - total_lists: usize, -} - -#[derive(Default)] -struct CommandManager { - allocators: thunderdome::Arena, - free_allocators: Vec, - lists: Vec, -} - -impl CommandManager { - fn acquire(&mut self, index: CommandAllocatorIndex) -> native::CommandAllocator { - let entry = &mut self.allocators[index]; - assert!(entry.total_lists < REUSE_COUNT); - entry.active_lists += 1; - entry.total_lists += 1; - return entry.raw; - } - - fn release_allocator(&mut self, index: CommandAllocatorIndex) { - let entry = &mut self.allocators[index]; - if entry.total_lists >= REUSE_COUNT { - debug!("Cooling command allocator {:?}", index); - } else { - self.free_allocators.push(index); - } - } - - fn release_list(&mut self, list: native::GraphicsCommandList, index: CommandAllocatorIndex) { - //pre-condition: list must be closed - let entry = &mut self.allocators[index]; - entry.active_lists -= 1; - if entry.active_lists == 0 && entry.total_lists >= REUSE_COUNT { - debug!("Re-warming command allocator {:?}", index); - entry.raw.reset(); - entry.total_lists = 0; - self.free_allocators.push(index); - } - self.lists.push(list); - } -} - -pub struct PoolShared { - device: native::Device, - list_type: native::CmdListType, - manager: Mutex, -} - -impl fmt::Debug for PoolShared { - fn fmt(&self, fmt: &mut fmt::Formatter) -> fmt::Result { - // TODO: print out as struct - fmt.write_str("PoolShared") - } -} - -impl PoolShared { - pub fn acquire(&self) -> (CommandAllocatorIndex, native::GraphicsCommandList) { - let mut man_guard = self.manager.lock(); - let allocator_index = match man_guard.free_allocators.pop() { - Some(index) => index, - None => { - let (raw, hr) = self.device.create_command_allocator(self.list_type); - assert_eq!( - winerror::S_OK, - hr, - "error on command allocator creation: {:x}", - hr - ); - man_guard.allocators.insert(AllocatorEntry { - raw, - active_lists: 0, - total_lists: 0, - }) - } - }; - let raw = man_guard.acquire(allocator_index); - - let list = match man_guard.lists.pop() { - Some(list) => { - list.reset(raw, native::PipelineState::null()); - list - } - None => { - let (command_list, hr) = self.device.create_graphics_command_list( - self.list_type, - raw, - native::PipelineState::null(), - 0, - ); - assert_eq!( - hr, - winerror::S_OK, - "error on command list creation: {:x}", - hr - ); - command_list - } - }; - (allocator_index, list) - } - - pub fn release_allocator(&self, allocator_index: CommandAllocatorIndex) { - self.manager.lock().release_allocator(allocator_index); - } - - pub fn release_list( - &self, - list: native::GraphicsCommandList, - allocator_index: CommandAllocatorIndex, - ) { - self.manager.lock().release_list(list, allocator_index); - } -} - -#[derive(Debug)] -pub struct CommandPool { - shared: Arc, - pool_shared: Arc, -} - -unsafe impl Send for CommandPool {} -unsafe impl Sync for CommandPool {} - -impl CommandPool { - pub(crate) fn new( - device: native::Device, - list_type: native::CmdListType, - shared: &Arc, - _create_flags: pool::CommandPoolCreateFlags, - ) -> Self { - let pool_shared = Arc::new(PoolShared { - device, - list_type, - manager: Mutex::default(), - }); - CommandPool { - shared: Arc::clone(shared), - pool_shared, - } - } -} - -impl pool::CommandPool for CommandPool { - unsafe fn reset(&mut self, _release_resources: bool) { - //TODO: reset all the allocators? - //do nothing. The allocated command buffers would not know - // that this happened, but they should be ready to - // process `begin` as if they are in `Initial` state. - } - - unsafe fn allocate_one(&mut self, level: command::Level) -> CommandBuffer { - // TODO: Implement secondary buffers - assert_eq!(level, command::Level::Primary); - CommandBuffer::new(&self.shared, &self.pool_shared) - } - - unsafe fn free(&mut self, cbufs: I) - where - I: Iterator, - { - let mut man_guard = self.pool_shared.manager.lock(); - for cbuf in cbufs { - if let Some((index, list)) = cbuf.destroy() { - man_guard.release_allocator(index); - if let Some(list) = list { - man_guard.release_list(list, index); - } - } - } - } -} +use std::{fmt, sync::Arc}; + +use parking_lot::Mutex; +use winapi::shared::winerror; + +use crate::{command::CommandBuffer, Backend, Shared}; +use hal::{command, pool}; + +const REUSE_COUNT: usize = 64; + +pub type CommandAllocatorIndex = thunderdome::Index; + +struct AllocatorEntry { + raw: native::CommandAllocator, + active_lists: usize, + total_lists: usize, +} + +#[derive(Default)] +struct CommandManager { + allocators: thunderdome::Arena, + free_allocators: Vec, + lists: Vec, +} + +impl CommandManager { + fn acquire(&mut self, index: CommandAllocatorIndex) -> native::CommandAllocator { + let entry = &mut self.allocators[index]; + assert!(entry.total_lists < REUSE_COUNT); + entry.active_lists += 1; + entry.total_lists += 1; + return entry.raw; + } + + fn release_allocator(&mut self, index: CommandAllocatorIndex) { + let entry = &mut self.allocators[index]; + if entry.total_lists >= REUSE_COUNT { + debug!("Cooling command allocator {:?}", index); + } else { + self.free_allocators.push(index); + } + } + + fn release_list(&mut self, list: native::GraphicsCommandList, index: CommandAllocatorIndex) { + //pre-condition: list must be closed + let entry = &mut self.allocators[index]; + entry.active_lists -= 1; + if entry.active_lists == 0 && entry.total_lists >= REUSE_COUNT { + debug!("Re-warming command allocator {:?}", index); + entry.raw.reset(); + entry.total_lists = 0; + self.free_allocators.push(index); + } + self.lists.push(list); + } +} + +pub struct PoolShared { + device: native::Device, + list_type: native::CmdListType, + manager: Mutex, +} + +impl fmt::Debug for PoolShared { + fn fmt(&self, fmt: &mut fmt::Formatter) -> fmt::Result { + // TODO: print out as struct + fmt.write_str("PoolShared") + } +} + +impl PoolShared { + pub fn acquire(&self) -> (CommandAllocatorIndex, native::GraphicsCommandList) { + let mut man_guard = self.manager.lock(); + let allocator_index = match man_guard.free_allocators.pop() { + Some(index) => index, + None => { + let (raw, hr) = self.device.create_command_allocator(self.list_type); + assert_eq!( + winerror::S_OK, + hr, + "error on command allocator creation: {:x}", + hr + ); + man_guard.allocators.insert(AllocatorEntry { + raw, + active_lists: 0, + total_lists: 0, + }) + } + }; + let raw = man_guard.acquire(allocator_index); + + let list = match man_guard.lists.pop() { + Some(list) => { + list.reset(raw, native::PipelineState::null()); + list + } + None => { + let (command_list, hr) = self.device.create_graphics_command_list( + self.list_type, + raw, + native::PipelineState::null(), + 0, + ); + assert_eq!( + hr, + winerror::S_OK, + "error on command list creation: {:x}", + hr + ); + command_list + } + }; + (allocator_index, list) + } + + pub fn release_allocator(&self, allocator_index: CommandAllocatorIndex) { + self.manager.lock().release_allocator(allocator_index); + } + + pub fn release_list( + &self, + list: native::GraphicsCommandList, + allocator_index: CommandAllocatorIndex, + ) { + self.manager.lock().release_list(list, allocator_index); + } +} + +#[derive(Debug)] +pub struct CommandPool { + shared: Arc, + pool_shared: Arc, +} + +unsafe impl Send for CommandPool {} +unsafe impl Sync for CommandPool {} + +impl CommandPool { + pub(crate) fn new( + device: native::Device, + list_type: native::CmdListType, + shared: &Arc, + _create_flags: pool::CommandPoolCreateFlags, + ) -> Self { + let pool_shared = Arc::new(PoolShared { + device, + list_type, + manager: Mutex::default(), + }); + CommandPool { + shared: Arc::clone(shared), + pool_shared, + } + } +} + +impl pool::CommandPool for CommandPool { + unsafe fn reset(&mut self, _release_resources: bool) { + //TODO: reset all the allocators? + //do nothing. The allocated command buffers would not know + // that this happened, but they should be ready to + // process `begin` as if they are in `Initial` state. + } + + unsafe fn allocate_one(&mut self, level: command::Level) -> CommandBuffer { + // TODO: Implement secondary buffers + assert_eq!(level, command::Level::Primary); + CommandBuffer::new(&self.shared, &self.pool_shared) + } + + unsafe fn free(&mut self, cbufs: I) + where + I: Iterator, + { + let mut man_guard = self.pool_shared.manager.lock(); + for cbuf in cbufs { + if let Some((index, list)) = cbuf.destroy() { + man_guard.release_allocator(index); + if let Some(list) = list { + man_guard.release_list(list, index); + } + } + } + } +} diff --git a/third_party/rust/gfx-backend-dx12/src/resource.rs b/third_party/rust/gfx-backend-dx12/src/resource.rs index 46ae29b02af5..d97d68494906 100644 --- a/third_party/rust/gfx-backend-dx12/src/resource.rs +++ b/third_party/rust/gfx-backend-dx12/src/resource.rs @@ -1,872 +1,871 @@ -use hal::{buffer, format, image, memory, pass, pso}; -use range_alloc::RangeAllocator; - -use parking_lot::RwLock; -use winapi::{ - shared::{dxgiformat::DXGI_FORMAT, minwindef::UINT}, - um::d3d12, -}; - -use std::{collections::BTreeMap, fmt, ops::Range, slice, sync::Arc}; - -use crate::{ - descriptors_cpu::{Handle, MultiCopyAccumulator}, - root_constants::RootConstant, - Backend, DescriptorIndex, MAX_VERTEX_BUFFERS, -}; - -// ShaderModule is either a precompiled if the source comes from HLSL or -// the SPIR-V module doesn't contain specialization constants or push constants -// because they need to be adjusted on pipeline creation. -#[derive(Debug, Hash)] -pub enum ShaderModule { - Compiled(BTreeMap), - Spirv(Vec), -} -unsafe impl Send for ShaderModule {} -unsafe impl Sync for ShaderModule {} - -#[derive(Clone, Debug, Hash)] -pub struct BarrierDesc { - pub(crate) attachment_id: pass::AttachmentId, - pub(crate) states: Range, - pub(crate) flags: d3d12::D3D12_RESOURCE_BARRIER_FLAGS, -} - -impl BarrierDesc { - pub(crate) fn new( - attachment_id: pass::AttachmentId, - states: Range, - ) -> Self { - BarrierDesc { - attachment_id, - states, - flags: d3d12::D3D12_RESOURCE_BARRIER_FLAG_NONE, - } - } - - pub(crate) fn split(self) -> Range { - BarrierDesc { - flags: d3d12::D3D12_RESOURCE_BARRIER_FLAG_BEGIN_ONLY, - ..self.clone() - }..BarrierDesc { - flags: d3d12::D3D12_RESOURCE_BARRIER_FLAG_END_ONLY, - ..self - } - } -} - -#[derive(Clone, Debug, Hash)] -pub struct SubpassDesc { - pub(crate) color_attachments: Vec, - pub(crate) depth_stencil_attachment: Option, - pub(crate) input_attachments: Vec, - pub(crate) resolve_attachments: Vec, - pub(crate) pre_barriers: Vec, - pub(crate) post_barriers: Vec, -} - -impl SubpassDesc { - /// Check if an attachment is used by this sub-pass. - //Note: preserved attachment are not considered used. - pub(crate) fn is_using(&self, at_id: pass::AttachmentId) -> bool { - self.color_attachments - .iter() - .chain(self.depth_stencil_attachment.iter()) - .chain(self.input_attachments.iter()) - .chain(self.resolve_attachments.iter()) - .any(|&(id, _)| id == at_id) - } -} - -#[derive(Clone, Debug, Hash)] -pub struct RenderPass { - pub(crate) attachments: Vec, - pub(crate) subpasses: Vec, - pub(crate) post_barriers: Vec, - pub(crate) raw_name: Vec, -} - -// Indirection layer attribute -> remap -> binding. -// -// Required as vulkan allows attribute offsets larger than the stride. -// Storing the stride specified in the pipeline required for vertex buffer binding. -#[derive(Copy, Clone, Debug)] -pub struct VertexBinding { - // Map into the specified bindings on pipeline creation. - pub mapped_binding: usize, - pub stride: UINT, - // Additional offset to rebase the attributes. - pub offset: u32, -} - -#[derive(Debug)] -pub struct GraphicsPipeline { - pub(crate) raw: native::PipelineState, - pub(crate) shared: Arc, - pub(crate) topology: d3d12::D3D12_PRIMITIVE_TOPOLOGY, - pub(crate) vertex_bindings: [Option; MAX_VERTEX_BUFFERS], - pub(crate) baked_states: pso::BakedStates, -} -unsafe impl Send for GraphicsPipeline {} -unsafe impl Sync for GraphicsPipeline {} - -#[derive(Debug)] -pub struct ComputePipeline { - pub(crate) raw: native::PipelineState, - pub(crate) shared: Arc, -} - -unsafe impl Send for ComputePipeline {} -unsafe impl Sync for ComputePipeline {} - -bitflags! { - pub struct SetTableTypes: u8 { - const SRV_CBV_UAV = 0x1; - const SAMPLERS = 0x2; - } -} - -pub const SRV_CBV_UAV: SetTableTypes = SetTableTypes::SRV_CBV_UAV; -pub const SAMPLERS: SetTableTypes = SetTableTypes::SAMPLERS; - -pub type RootSignatureOffset = usize; - -#[derive(Debug, Hash)] -pub struct RootTable { - pub ty: SetTableTypes, - pub offset: RootSignatureOffset, -} - -#[derive(Debug)] -pub struct RootElement { - pub table: RootTable, - pub mutable_bindings: auxil::FastHashSet, -} - -#[derive(Debug)] -pub struct PipelineShared { - pub(crate) signature: native::RootSignature, - /// Disjunct, sorted vector of root constant ranges. - pub(crate) constants: Vec, - /// A root offset per parameter. - pub(crate) parameter_offsets: Vec, - /// Total number of root slots occupied by the pipeline. - pub(crate) total_slots: u32, -} - -unsafe impl Send for PipelineShared {} -unsafe impl Sync for PipelineShared {} - -#[derive(Debug)] -pub struct PipelineLayout { - pub(crate) shared: Arc, - // Storing for each associated descriptor set layout, which tables we created - // in the root signature. This is required for binding descriptor sets. - pub(crate) elements: Vec, -} - -#[derive(Debug, Clone)] -pub struct Framebuffer { - /// Number of layers in the render area. Required for subpass resolves. - pub(crate) layers: image::Layer, -} - -#[derive(Clone, Debug)] -pub struct BufferUnbound { - pub(crate) requirements: memory::Requirements, - pub(crate) usage: buffer::Usage, - pub(crate) name: Option>, -} - -pub struct BufferBound { - pub(crate) resource: native::Resource, - pub(crate) requirements: memory::Requirements, - pub(crate) clear_uav: Option, -} - -impl fmt::Debug for BufferBound { - fn fmt(&self, fmt: &mut fmt::Formatter) -> fmt::Result { - fmt.write_str("BufferBound") - } -} - -unsafe impl Send for BufferBound {} -unsafe impl Sync for BufferBound {} - -pub enum Buffer { - Unbound(BufferUnbound), - Bound(BufferBound), -} - -impl fmt::Debug for Buffer { - fn fmt(&self, fmt: &mut fmt::Formatter) -> fmt::Result { - fmt.write_str("Buffer") - } -} - -impl Buffer { - pub(crate) fn expect_unbound(&self) -> &BufferUnbound { - match *self { - Buffer::Unbound(ref unbound) => unbound, - Buffer::Bound(_) => panic!("Expected unbound buffer"), - } - } - - pub(crate) fn expect_bound(&self) -> &BufferBound { - match *self { - Buffer::Unbound(_) => panic!("Expected bound buffer"), - Buffer::Bound(ref bound) => bound, - } - } -} - -#[derive(Copy, Clone)] -pub struct BufferView { - // Descriptor handle for uniform texel buffers. - pub(crate) handle_srv: Option, - // Descriptor handle for storage texel buffers. - pub(crate) handle_uav: Option, -} - -impl fmt::Debug for BufferView { - fn fmt(&self, fmt: &mut fmt::Formatter) -> fmt::Result { - fmt.write_str("BufferView") - } -} - -unsafe impl Send for BufferView {} -unsafe impl Sync for BufferView {} - -#[derive(Clone)] -pub enum Place { - Heap { raw: native::Heap, offset: u64 }, - Swapchain {}, -} - -#[derive(Clone)] -pub struct ImageBound { - pub(crate) resource: native::Resource, - pub(crate) place: Place, - pub(crate) surface_type: format::SurfaceType, - pub(crate) kind: image::Kind, - pub(crate) mip_levels: image::Level, - pub(crate) default_view_format: Option, - pub(crate) view_caps: image::ViewCapabilities, - pub(crate) descriptor: d3d12::D3D12_RESOURCE_DESC, - pub(crate) clear_cv: Vec, - pub(crate) clear_dv: Vec, - pub(crate) clear_sv: Vec, - pub(crate) requirements: memory::Requirements, -} - -impl fmt::Debug for ImageBound { - fn fmt(&self, fmt: &mut fmt::Formatter) -> fmt::Result { - fmt.write_str("ImageBound") - } -} - -unsafe impl Send for ImageBound {} -unsafe impl Sync for ImageBound {} - -impl ImageBound { - pub fn calc_subresource(&self, mip_level: UINT, layer: UINT, plane: UINT) -> UINT { - mip_level - + (layer * self.descriptor.MipLevels as UINT) - + (plane * self.descriptor.MipLevels as UINT * self.kind.num_layers() as UINT) - } -} - -#[derive(Clone)] -pub struct ImageUnbound { - pub(crate) desc: d3d12::D3D12_RESOURCE_DESC, - pub(crate) view_format: Option, - pub(crate) dsv_format: Option, - pub(crate) requirements: memory::Requirements, - pub(crate) format: format::Format, - pub(crate) kind: image::Kind, - pub(crate) mip_levels: image::Level, - pub(crate) usage: image::Usage, - pub(crate) tiling: image::Tiling, - pub(crate) view_caps: image::ViewCapabilities, - //TODO: use hal::format::FormatDesc - pub(crate) bytes_per_block: u8, - // Dimension of a texel block (compressed formats). - pub(crate) block_dim: (u8, u8), - pub(crate) name: Option>, -} - -impl fmt::Debug for ImageUnbound { - fn fmt(&self, fmt: &mut fmt::Formatter) -> fmt::Result { - fmt.write_str("ImageUnbound") - } -} - -impl ImageUnbound { - pub fn calc_subresource(&self, mip_level: UINT, layer: UINT, plane: UINT) -> UINT { - mip_level - + (layer * self.desc.MipLevels as UINT) - + (plane * self.desc.MipLevels as UINT * self.kind.num_layers() as UINT) - } -} - -#[derive(Clone)] -pub enum Image { - Unbound(ImageUnbound), - Bound(ImageBound), -} - -impl fmt::Debug for Image { - fn fmt(&self, fmt: &mut fmt::Formatter) -> fmt::Result { - fmt.write_str("Image") - } -} - -impl Image { - pub(crate) fn expect_unbound(&self) -> &ImageUnbound { - match *self { - Image::Unbound(ref unbound) => unbound, - Image::Bound(_) => panic!("Expected unbound image"), - } - } - - pub(crate) fn expect_bound(&self) -> &ImageBound { - match *self { - Image::Unbound(_) => panic!("Expected bound image"), - Image::Bound(ref bound) => bound, - } - } - - pub fn get_desc(&self) -> &d3d12::D3D12_RESOURCE_DESC { - match self { - Image::Bound(i) => &i.descriptor, - Image::Unbound(i) => &i.desc, - } - } - - pub fn calc_subresource(&self, mip_level: UINT, layer: UINT, plane: UINT) -> UINT { - match self { - Image::Bound(i) => i.calc_subresource(mip_level, layer, plane), - Image::Unbound(i) => i.calc_subresource(mip_level, layer, plane), - } - } -} - -#[derive(Copy, Clone)] -pub enum RenderTargetHandle { - None, - Swapchain(native::CpuDescriptor), - Pool(Handle), -} - -impl RenderTargetHandle { - pub fn raw(&self) -> Option { - match *self { - RenderTargetHandle::None => None, - RenderTargetHandle::Swapchain(rtv) => Some(rtv), - RenderTargetHandle::Pool(ref handle) => Some(handle.raw), - } - } -} - -#[derive(Copy, Clone)] -pub struct ImageView { - pub(crate) resource: native::Resource, // weak-ptr owned by image. - pub(crate) handle_srv: Option, - pub(crate) handle_rtv: RenderTargetHandle, - pub(crate) handle_dsv: Option, - pub(crate) handle_uav: Option, - // Required for attachment resolves. - pub(crate) dxgi_format: DXGI_FORMAT, - pub(crate) num_levels: image::Level, - pub(crate) mip_levels: (image::Level, image::Level), - pub(crate) layers: (image::Layer, image::Layer), - pub(crate) kind: image::Kind, -} - -impl fmt::Debug for ImageView { - fn fmt(&self, fmt: &mut fmt::Formatter) -> fmt::Result { - fmt.write_str("ImageView") - } -} - -unsafe impl Send for ImageView {} -unsafe impl Sync for ImageView {} - -impl ImageView { - pub fn calc_subresource(&self, mip_level: UINT, layer: UINT) -> UINT { - mip_level + (layer * self.num_levels as UINT) - } - - pub fn is_swapchain(&self) -> bool { - match self.handle_rtv { - RenderTargetHandle::Swapchain(_) => true, - _ => false, - } - } -} - -pub struct Sampler { - pub(crate) handle: Handle, -} - -impl fmt::Debug for Sampler { - fn fmt(&self, fmt: &mut fmt::Formatter) -> fmt::Result { - fmt.write_str("Sampler") - } -} - -#[derive(Debug)] -pub struct DescriptorSetLayout { - pub(crate) bindings: Vec, -} - -#[derive(Debug)] -pub struct Fence { - pub(crate) raw: native::Fence, -} -unsafe impl Send for Fence {} -unsafe impl Sync for Fence {} - -#[derive(Debug)] -pub struct Semaphore { - pub(crate) raw: native::Fence, -} - -unsafe impl Send for Semaphore {} -unsafe impl Sync for Semaphore {} - -#[derive(Debug)] -pub struct Memory { - pub(crate) heap: native::Heap, - pub(crate) type_id: usize, - pub(crate) size: u64, - // Buffer containing the whole memory for mapping (only for host visible heaps) - pub(crate) resource: Option, -} - -unsafe impl Send for Memory {} -unsafe impl Sync for Memory {} - -bitflags! { - /// A set of D3D12 descriptor types that need to be associated - /// with a single gfx-hal `DescriptorType`. - #[derive(Default)] - pub struct DescriptorContent: u8 { - const CBV = 0x1; - const SRV = 0x2; - const UAV = 0x4; - const SAMPLER = 0x8; - - /// Indicates if the descriptor is a dynamic uniform/storage buffer. - /// Important as dynamic buffers are implemented as root descriptors. - const DYNAMIC = 0x10; - - const VIEW = DescriptorContent::CBV.bits |DescriptorContent::SRV.bits | DescriptorContent::UAV.bits; - } -} - -impl DescriptorContent { - pub fn is_dynamic(&self) -> bool { - self.contains(DescriptorContent::DYNAMIC) - } -} - -impl From for DescriptorContent { - fn from(ty: pso::DescriptorType) -> Self { - use hal::pso::{ - BufferDescriptorFormat as Bdf, BufferDescriptorType as Bdt, DescriptorType as Dt, - ImageDescriptorType as Idt, - }; - - use DescriptorContent as Dc; - - match ty { - Dt::Sampler => Dc::SAMPLER, - Dt::Image { ty } => match ty { - Idt::Storage { read_only: true } => Dc::SRV, - Idt::Storage { read_only: false } => Dc::SRV | Dc::UAV, - Idt::Sampled { with_sampler } => match with_sampler { - true => Dc::SRV | Dc::SAMPLER, - false => Dc::SRV, - }, - }, - Dt::Buffer { ty, format } => match ty { - Bdt::Storage { read_only: true } => match format { - Bdf::Structured { - dynamic_offset: true, - } => Dc::SRV | Dc::DYNAMIC, - Bdf::Structured { - dynamic_offset: false, - } - | Bdf::Texel => Dc::SRV, - }, - Bdt::Storage { read_only: false } => match format { - Bdf::Structured { - dynamic_offset: true, - } => Dc::SRV | Dc::UAV | Dc::DYNAMIC, - Bdf::Structured { - dynamic_offset: false, - } - | Bdf::Texel => Dc::SRV | Dc::UAV, - }, - Bdt::Uniform => match format { - Bdf::Structured { - dynamic_offset: true, - } => Dc::CBV | Dc::DYNAMIC, - Bdf::Structured { - dynamic_offset: false, - } => Dc::CBV, - Bdf::Texel => Dc::SRV, - }, - }, - Dt::InputAttachment => Dc::SRV, - } - } -} - -#[derive(Debug)] -pub struct DescriptorRange { - pub(crate) handle: DualHandle, - pub(crate) ty: pso::DescriptorType, - pub(crate) handle_size: u64, -} - -impl DescriptorRange { - pub(crate) fn at(&self, index: DescriptorIndex) -> native::CpuDescriptor { - assert!(index < self.handle.size); - let ptr = self.handle.cpu.ptr + (self.handle_size * index) as usize; - native::CpuDescriptor { ptr } - } -} - -#[derive(Copy, Clone, Debug)] -pub(crate) struct DynamicDescriptor { - //TODO: just store `DynamicBuffer` here? - pub content: DescriptorContent, - pub gpu_buffer_location: u64, -} - -#[derive(Debug, Default)] -pub struct DescriptorBindingInfo { - pub(crate) count: u64, - pub(crate) view_range: Option, - pub(crate) dynamic_descriptors: Vec, - pub(crate) content: DescriptorContent, -} - -#[derive(Default)] -pub struct DescriptorOrigins { - // For each index on the heap, this array stores the origin CPU handle. - origins: Vec, -} - -impl DescriptorOrigins { - fn find(&self, other: &[native::CpuDescriptor]) -> Option { - //TODO: need a smarter algorithm here! - for i in other.len()..=self.origins.len() { - let base = i - other.len(); - //TODO: use slice comparison when `CpuDescriptor` implements `PartialEq`. - if unsafe { - slice::from_raw_parts(&self.origins[base].ptr, other.len()) - == slice::from_raw_parts(&other[0].ptr, other.len()) - } { - return Some(base as DescriptorIndex); - } - } - None - } - - fn grow(&mut self, other: &[native::CpuDescriptor]) -> DescriptorIndex { - let base = self.origins.len() as DescriptorIndex; - self.origins.extend_from_slice(other); - base - } -} - -pub struct DescriptorSet { - // Required for binding at command buffer - pub(crate) heap_srv_cbv_uav: native::DescriptorHeap, - pub(crate) heap_samplers: native::DescriptorHeap, - pub(crate) sampler_origins: Box<[native::CpuDescriptor]>, - pub(crate) binding_infos: Vec, - pub(crate) first_gpu_sampler: Option, - pub(crate) first_gpu_view: Option, - pub(crate) raw_name: Vec, -} - -impl fmt::Debug for DescriptorSet { - fn fmt(&self, fmt: &mut fmt::Formatter) -> fmt::Result { - fmt.write_str("DescriptorSet") - } -} - -// TODO: is this really safe? -unsafe impl Send for DescriptorSet {} -unsafe impl Sync for DescriptorSet {} - -impl DescriptorSet { - pub fn srv_cbv_uav_gpu_start(&self) -> native::GpuDescriptor { - self.heap_srv_cbv_uav.start_gpu_descriptor() - } - - pub fn sampler_gpu_start(&self) -> native::GpuDescriptor { - self.heap_samplers.start_gpu_descriptor() - } - - pub fn sampler_offset(&self, binding: u32, last_offset: usize) -> usize { - let mut offset = 0; - for bi in &self.binding_infos[..binding as usize] { - if bi.content.contains(DescriptorContent::SAMPLER) { - offset += bi.count as usize; - } - } - if self.binding_infos[binding as usize] - .content - .contains(DescriptorContent::SAMPLER) - { - offset += last_offset; - } - offset - } - - pub fn update_samplers( - &mut self, - heap: &DescriptorHeap, - origins: &RwLock, - accum: &mut MultiCopyAccumulator, - ) { - let start_index = if let Some(index) = { - // explicit variable allows to limit the lifetime of that borrow - let borrow = origins.read(); - borrow.find(&self.sampler_origins) - } { - Some(index) - } else if self.sampler_origins.iter().any(|desc| desc.ptr == 0) { - // set is incomplete, don't try to build it - None - } else { - let base = origins.write().grow(&self.sampler_origins); - // copy the descriptors from their origins into the new location - accum.dst_samplers.add( - heap.cpu_descriptor_at(base), - self.sampler_origins.len() as u32, - ); - for &origin in self.sampler_origins.iter() { - accum.src_samplers.add(origin, 1); - } - Some(base) - }; - - self.first_gpu_sampler = start_index.map(|index| heap.gpu_descriptor_at(index)); - } -} - -#[derive(Copy, Clone)] -pub struct DualHandle { - pub(crate) cpu: native::CpuDescriptor, - pub(crate) gpu: native::GpuDescriptor, - /// How large the block allocated to this handle is. - pub(crate) size: u64, -} - -impl fmt::Debug for DualHandle { - fn fmt(&self, fmt: &mut fmt::Formatter) -> fmt::Result { - fmt.write_str("DualHandle") - } -} - -pub struct DescriptorHeap { - pub(crate) raw: native::DescriptorHeap, - pub(crate) handle_size: u64, - pub(crate) total_handles: u64, - pub(crate) start: DualHandle, -} - -impl fmt::Debug for DescriptorHeap { - fn fmt(&self, fmt: &mut fmt::Formatter) -> fmt::Result { - fmt.write_str("DescriptorHeap") - } -} - -impl DescriptorHeap { - pub(crate) fn at(&self, index: DescriptorIndex, size: u64) -> DualHandle { - assert!(index < self.total_handles); - DualHandle { - cpu: self.cpu_descriptor_at(index), - gpu: self.gpu_descriptor_at(index), - size, - } - } - - pub(crate) fn cpu_descriptor_at(&self, index: u64) -> native::CpuDescriptor { - native::CpuDescriptor { - ptr: self.start.cpu.ptr + (self.handle_size * index) as usize, - } - } - - pub(crate) fn gpu_descriptor_at(&self, index: u64) -> native::GpuDescriptor { - native::GpuDescriptor { - ptr: self.start.gpu.ptr + self.handle_size * index, - } - } - - pub(crate) unsafe fn destroy(&self) { - self.raw.destroy(); - } -} - -/// Slice of an descriptor heap, which is allocated for a pool. -/// Pools will create descriptor sets inside this slice. -#[derive(Debug)] -pub struct DescriptorHeapSlice { - pub(crate) heap: native::DescriptorHeap, // Weak reference, owned by descriptor heap. - pub(crate) start: DualHandle, - pub(crate) handle_size: u64, - pub(crate) range_allocator: RangeAllocator, -} - -impl DescriptorHeapSlice { - pub(crate) fn alloc_handles(&mut self, count: u64) -> Option { - self.range_allocator - .allocate_range(count) - .ok() - .map(|range| DualHandle { - cpu: native::CpuDescriptor { - ptr: self.start.cpu.ptr + (self.handle_size * range.start) as usize, - }, - gpu: native::GpuDescriptor { - ptr: self.start.gpu.ptr + (self.handle_size * range.start) as u64, - }, - size: count, - }) - } - - /// Free handles previously given out by this `DescriptorHeapSlice`. - /// Do not use this with handles not given out by this `DescriptorHeapSlice`. - pub(crate) fn free_handles(&mut self, handle: DualHandle) { - let start = (handle.gpu.ptr - self.start.gpu.ptr) / self.handle_size; - let handle_range = start..start + handle.size; - self.range_allocator.free_range(handle_range); - } - - /// Clear the allocator. - pub(crate) fn clear(&mut self) { - self.range_allocator.reset(); - } -} - -#[derive(Debug)] -pub struct DescriptorPool { - pub(crate) heap_raw_sampler: native::DescriptorHeap, - pub(crate) heap_srv_cbv_uav: DescriptorHeapSlice, - pub(crate) pools: Vec, - pub(crate) max_size: u64, -} -unsafe impl Send for DescriptorPool {} -unsafe impl Sync for DescriptorPool {} - -impl pso::DescriptorPool for DescriptorPool { - unsafe fn allocate_one( - &mut self, - layout: &DescriptorSetLayout, - ) -> Result { - let mut binding_infos = Vec::new(); - let mut first_gpu_view = None; - let mut num_samplers = 0; - - info!("allocate_one"); - for binding in &layout.bindings { - // Add dummy bindings in case of out-of-range or sparse binding layout. - while binding_infos.len() <= binding.binding as usize { - binding_infos.push(DescriptorBindingInfo::default()); - } - let content = DescriptorContent::from(binding.ty); - debug!("\tbinding {:?} with content {:?}", binding, content); - - let (view_range, dynamic_descriptors) = if content.is_dynamic() { - let descriptor = DynamicDescriptor { - content: content ^ DescriptorContent::DYNAMIC, - gpu_buffer_location: 0, - }; - (None, vec![descriptor; binding.count]) - } else { - if content.contains(DescriptorContent::SAMPLER) { - num_samplers += binding.count; - } - - let view_range = if content.intersects(DescriptorContent::VIEW) { - let count = if content.contains(DescriptorContent::SRV | DescriptorContent::UAV) - { - 2 * binding.count as u64 - } else { - binding.count as u64 - }; - debug!("\tview handles: {}", count); - let handle = self - .heap_srv_cbv_uav - .alloc_handles(count) - .ok_or(pso::AllocationError::OutOfPoolMemory)?; - if first_gpu_view.is_none() { - first_gpu_view = Some(handle.gpu); - } - Some(DescriptorRange { - handle, - ty: binding.ty, - handle_size: self.heap_srv_cbv_uav.handle_size, - }) - } else { - None - }; - - (view_range, Vec::new()) - }; - - binding_infos[binding.binding as usize] = DescriptorBindingInfo { - count: binding.count as _, - view_range, - dynamic_descriptors, - content, - }; - } - - Ok(DescriptorSet { - heap_srv_cbv_uav: self.heap_srv_cbv_uav.heap, - heap_samplers: self.heap_raw_sampler, - sampler_origins: vec![native::CpuDescriptor { ptr: 0 }; num_samplers] - .into_boxed_slice(), - binding_infos, - first_gpu_sampler: None, - first_gpu_view, - raw_name: Vec::new(), - }) - } - - unsafe fn free(&mut self, descriptor_sets: I) - where - I: Iterator, - { - for descriptor_set in descriptor_sets { - for binding_info in descriptor_set.binding_infos { - if let Some(view_range) = binding_info.view_range { - if binding_info.content.intersects(DescriptorContent::VIEW) { - self.heap_srv_cbv_uav.free_handles(view_range.handle); - } - } - } - } - } - - unsafe fn reset(&mut self) { - self.heap_srv_cbv_uav.clear(); - } -} - -#[derive(Debug)] -pub struct QueryPool { - pub(crate) raw: native::QueryHeap, - pub(crate) ty: hal::query::Type, -} - -unsafe impl Send for QueryPool {} -unsafe impl Sync for QueryPool {} +use hal::{buffer, format, image, memory, pass, pso}; +use range_alloc::RangeAllocator; + +use parking_lot::RwLock; +use winapi::{ + shared::{dxgiformat::DXGI_FORMAT, minwindef::UINT}, + um::d3d12, +}; + +use std::{collections::BTreeMap, fmt, ops::Range, slice, sync::Arc}; + +use crate::{ + descriptors_cpu::{Handle, MultiCopyAccumulator}, + root_constants::RootConstant, + Backend, DescriptorIndex, MAX_VERTEX_BUFFERS, +}; + +// ShaderModule is either a precompiled if the source comes from HLSL or +// the SPIR-V module doesn't contain specialization constants or push constants +// because they need to be adjusted on pipeline creation. +#[derive(Debug, Hash)] +pub enum ShaderModule { + Compiled(BTreeMap), + Spirv(Vec), +} +unsafe impl Send for ShaderModule {} +unsafe impl Sync for ShaderModule {} + +#[derive(Clone, Debug, Hash)] +pub struct BarrierDesc { + pub(crate) attachment_id: pass::AttachmentId, + pub(crate) states: Range, + pub(crate) flags: d3d12::D3D12_RESOURCE_BARRIER_FLAGS, +} + +impl BarrierDesc { + pub(crate) fn new( + attachment_id: pass::AttachmentId, + states: Range, + ) -> Self { + BarrierDesc { + attachment_id, + states, + flags: d3d12::D3D12_RESOURCE_BARRIER_FLAG_NONE, + } + } + + pub(crate) fn split(self) -> Range { + BarrierDesc { + flags: d3d12::D3D12_RESOURCE_BARRIER_FLAG_BEGIN_ONLY, + ..self.clone() + }..BarrierDesc { + flags: d3d12::D3D12_RESOURCE_BARRIER_FLAG_END_ONLY, + ..self + } + } +} + +#[derive(Clone, Debug, Hash)] +pub struct SubpassDesc { + pub(crate) color_attachments: Vec, + pub(crate) depth_stencil_attachment: Option, + pub(crate) input_attachments: Vec, + pub(crate) resolve_attachments: Vec, + pub(crate) pre_barriers: Vec, + pub(crate) post_barriers: Vec, +} + +impl SubpassDesc { + /// Check if an attachment is used by this sub-pass. + //Note: preserved attachment are not considered used. + pub(crate) fn is_using(&self, at_id: pass::AttachmentId) -> bool { + self.color_attachments + .iter() + .chain(self.depth_stencil_attachment.iter()) + .chain(self.input_attachments.iter()) + .chain(self.resolve_attachments.iter()) + .any(|&(id, _)| id == at_id) + } +} + +#[derive(Clone, Debug, Hash)] +pub struct RenderPass { + pub(crate) attachments: Vec, + pub(crate) subpasses: Vec, + pub(crate) post_barriers: Vec, + pub(crate) raw_name: Vec, +} + +// Indirection layer attribute -> remap -> binding. +// +// Required as vulkan allows attribute offsets larger than the stride. +// Storing the stride specified in the pipeline required for vertex buffer binding. +#[derive(Copy, Clone, Debug)] +pub struct VertexBinding { + // Map into the specified bindings on pipeline creation. + pub mapped_binding: usize, + pub stride: UINT, + // Additional offset to rebase the attributes. + pub offset: u32, +} + +#[derive(Debug)] +pub struct GraphicsPipeline { + pub(crate) raw: native::PipelineState, + pub(crate) shared: Arc, + pub(crate) topology: d3d12::D3D12_PRIMITIVE_TOPOLOGY, + pub(crate) vertex_bindings: [Option; MAX_VERTEX_BUFFERS], + pub(crate) baked_states: pso::BakedStates, +} +unsafe impl Send for GraphicsPipeline {} +unsafe impl Sync for GraphicsPipeline {} + +#[derive(Debug)] +pub struct ComputePipeline { + pub(crate) raw: native::PipelineState, + pub(crate) shared: Arc, +} + +unsafe impl Send for ComputePipeline {} +unsafe impl Sync for ComputePipeline {} + +bitflags! { + pub struct SetTableTypes: u8 { + const SRV_CBV_UAV = 0x1; + const SAMPLERS = 0x2; + } +} + +pub const SRV_CBV_UAV: SetTableTypes = SetTableTypes::SRV_CBV_UAV; +pub const SAMPLERS: SetTableTypes = SetTableTypes::SAMPLERS; + +pub type RootSignatureOffset = usize; + +#[derive(Debug, Hash)] +pub struct RootTable { + pub ty: SetTableTypes, + pub offset: RootSignatureOffset, +} + +#[derive(Debug)] +pub struct RootElement { + pub table: RootTable, + pub mutable_bindings: auxil::FastHashSet, +} + +#[derive(Debug)] +pub struct PipelineShared { + pub(crate) signature: native::RootSignature, + /// Disjunct, sorted vector of root constant ranges. + pub(crate) constants: Vec, + /// A root offset per parameter. + pub(crate) parameter_offsets: Vec, + /// Total number of root slots occupied by the pipeline. + pub(crate) total_slots: u32, +} + +unsafe impl Send for PipelineShared {} +unsafe impl Sync for PipelineShared {} + +#[derive(Debug)] +pub struct PipelineLayout { + pub(crate) shared: Arc, + // Storing for each associated descriptor set layout, which tables we created + // in the root signature. This is required for binding descriptor sets. + pub(crate) elements: Vec, +} + +#[derive(Debug, Clone)] +pub struct Framebuffer { + /// Number of layers in the render area. Required for subpass resolves. + pub(crate) layers: image::Layer, +} + +#[derive(Clone, Debug)] +pub struct BufferUnbound { + pub(crate) requirements: memory::Requirements, + pub(crate) usage: buffer::Usage, + pub(crate) name: Option>, +} + +pub struct BufferBound { + pub(crate) resource: native::Resource, + pub(crate) requirements: memory::Requirements, + pub(crate) clear_uav: Option, +} + +impl fmt::Debug for BufferBound { + fn fmt(&self, fmt: &mut fmt::Formatter) -> fmt::Result { + fmt.write_str("BufferBound") + } +} + +unsafe impl Send for BufferBound {} +unsafe impl Sync for BufferBound {} + +pub enum Buffer { + Unbound(BufferUnbound), + Bound(BufferBound), +} + +impl fmt::Debug for Buffer { + fn fmt(&self, fmt: &mut fmt::Formatter) -> fmt::Result { + fmt.write_str("Buffer") + } +} + +impl Buffer { + pub(crate) fn expect_unbound(&self) -> &BufferUnbound { + match *self { + Buffer::Unbound(ref unbound) => unbound, + Buffer::Bound(_) => panic!("Expected unbound buffer"), + } + } + + pub(crate) fn expect_bound(&self) -> &BufferBound { + match *self { + Buffer::Unbound(_) => panic!("Expected bound buffer"), + Buffer::Bound(ref bound) => bound, + } + } +} + +#[derive(Copy, Clone)] +pub struct BufferView { + // Descriptor handle for uniform texel buffers. + pub(crate) handle_srv: Option, + // Descriptor handle for storage texel buffers. + pub(crate) handle_uav: Option, +} + +impl fmt::Debug for BufferView { + fn fmt(&self, fmt: &mut fmt::Formatter) -> fmt::Result { + fmt.write_str("BufferView") + } +} + +unsafe impl Send for BufferView {} +unsafe impl Sync for BufferView {} + +#[derive(Clone)] +pub enum Place { + Heap { raw: native::Heap, offset: u64 }, + Swapchain {}, +} + +#[derive(Clone)] +pub struct ImageBound { + pub(crate) resource: native::Resource, + pub(crate) place: Place, + pub(crate) surface_type: format::SurfaceType, + pub(crate) kind: image::Kind, + pub(crate) mip_levels: image::Level, + pub(crate) default_view_format: Option, + pub(crate) view_caps: image::ViewCapabilities, + pub(crate) descriptor: d3d12::D3D12_RESOURCE_DESC, + pub(crate) clear_cv: Vec, + pub(crate) clear_dv: Vec, + pub(crate) clear_sv: Vec, + pub(crate) requirements: memory::Requirements, +} + +impl fmt::Debug for ImageBound { + fn fmt(&self, fmt: &mut fmt::Formatter) -> fmt::Result { + fmt.write_str("ImageBound") + } +} + +unsafe impl Send for ImageBound {} +unsafe impl Sync for ImageBound {} + +impl ImageBound { + pub fn calc_subresource(&self, mip_level: UINT, layer: UINT, plane: UINT) -> UINT { + mip_level + + (layer * self.descriptor.MipLevels as UINT) + + (plane * self.descriptor.MipLevels as UINT * self.kind.num_layers() as UINT) + } +} + +#[derive(Clone)] +pub struct ImageUnbound { + pub(crate) desc: d3d12::D3D12_RESOURCE_DESC, + pub(crate) view_format: Option, + pub(crate) dsv_format: Option, + pub(crate) requirements: memory::Requirements, + pub(crate) format: format::Format, + pub(crate) kind: image::Kind, + pub(crate) mip_levels: image::Level, + pub(crate) usage: image::Usage, + pub(crate) tiling: image::Tiling, + pub(crate) view_caps: image::ViewCapabilities, + //TODO: use hal::format::FormatDesc + pub(crate) bytes_per_block: u8, + // Dimension of a texel block (compressed formats). + pub(crate) block_dim: (u8, u8), + pub(crate) name: Option>, +} + +impl fmt::Debug for ImageUnbound { + fn fmt(&self, fmt: &mut fmt::Formatter) -> fmt::Result { + fmt.write_str("ImageUnbound") + } +} + +impl ImageUnbound { + pub fn calc_subresource(&self, mip_level: UINT, layer: UINT, plane: UINT) -> UINT { + mip_level + + (layer * self.desc.MipLevels as UINT) + + (plane * self.desc.MipLevels as UINT * self.kind.num_layers() as UINT) + } +} + +#[derive(Clone)] +pub enum Image { + Unbound(ImageUnbound), + Bound(ImageBound), +} + +impl fmt::Debug for Image { + fn fmt(&self, fmt: &mut fmt::Formatter) -> fmt::Result { + fmt.write_str("Image") + } +} + +impl Image { + pub(crate) fn expect_unbound(&self) -> &ImageUnbound { + match *self { + Image::Unbound(ref unbound) => unbound, + Image::Bound(_) => panic!("Expected unbound image"), + } + } + + pub(crate) fn expect_bound(&self) -> &ImageBound { + match *self { + Image::Unbound(_) => panic!("Expected bound image"), + Image::Bound(ref bound) => bound, + } + } + + pub fn get_desc(&self) -> &d3d12::D3D12_RESOURCE_DESC { + match self { + Image::Bound(i) => &i.descriptor, + Image::Unbound(i) => &i.desc, + } + } + + pub fn calc_subresource(&self, mip_level: UINT, layer: UINT, plane: UINT) -> UINT { + match self { + Image::Bound(i) => i.calc_subresource(mip_level, layer, plane), + Image::Unbound(i) => i.calc_subresource(mip_level, layer, plane), + } + } +} + +#[derive(Copy, Clone)] +pub enum RenderTargetHandle { + None, + Swapchain(native::CpuDescriptor), + Pool(Handle), +} + +impl RenderTargetHandle { + pub fn raw(&self) -> Option { + match *self { + RenderTargetHandle::None => None, + RenderTargetHandle::Swapchain(rtv) => Some(rtv), + RenderTargetHandle::Pool(ref handle) => Some(handle.raw), + } + } +} + +#[derive(Copy, Clone)] +pub struct ImageView { + pub(crate) resource: native::Resource, // weak-ptr owned by image. + pub(crate) handle_srv: Option, + pub(crate) handle_rtv: RenderTargetHandle, + pub(crate) handle_dsv: Option, + pub(crate) handle_uav: Option, + // Required for attachment resolves. + pub(crate) dxgi_format: DXGI_FORMAT, + pub(crate) num_levels: image::Level, + pub(crate) mip_levels: (image::Level, image::Level), + pub(crate) layers: (image::Layer, image::Layer), + pub(crate) kind: image::Kind, +} + +impl fmt::Debug for ImageView { + fn fmt(&self, fmt: &mut fmt::Formatter) -> fmt::Result { + fmt.write_str("ImageView") + } +} + +unsafe impl Send for ImageView {} +unsafe impl Sync for ImageView {} + +impl ImageView { + pub fn calc_subresource(&self, mip_level: UINT, layer: UINT) -> UINT { + mip_level + (layer * self.num_levels as UINT) + } + + pub fn is_swapchain(&self) -> bool { + match self.handle_rtv { + RenderTargetHandle::Swapchain(_) => true, + _ => false, + } + } +} + +pub struct Sampler { + pub(crate) handle: Handle, +} + +impl fmt::Debug for Sampler { + fn fmt(&self, fmt: &mut fmt::Formatter) -> fmt::Result { + fmt.write_str("Sampler") + } +} + +#[derive(Debug)] +pub struct DescriptorSetLayout { + pub(crate) bindings: Vec, +} + +#[derive(Debug)] +pub struct Fence { + pub(crate) raw: native::Fence, +} +unsafe impl Send for Fence {} +unsafe impl Sync for Fence {} + +#[derive(Debug)] +pub struct Semaphore { + pub(crate) raw: native::Fence, +} + +unsafe impl Send for Semaphore {} +unsafe impl Sync for Semaphore {} + +#[derive(Debug)] +pub struct Memory { + pub(crate) heap: native::Heap, + pub(crate) type_id: usize, + pub(crate) size: u64, + // Buffer containing the whole memory for mapping (only for host visible heaps) + pub(crate) resource: Option, +} + +unsafe impl Send for Memory {} +unsafe impl Sync for Memory {} + +bitflags! { + /// A set of D3D12 descriptor types that need to be associated + /// with a single gfx-hal `DescriptorType`. + #[derive(Default)] + pub struct DescriptorContent: u8 { + const CBV = 0x1; + const SRV = 0x2; + const UAV = 0x4; + const SAMPLER = 0x8; + + /// Indicates if the descriptor is a dynamic uniform/storage buffer. + /// Important as dynamic buffers are implemented as root descriptors. + const DYNAMIC = 0x10; + + const VIEW = DescriptorContent::CBV.bits |DescriptorContent::SRV.bits | DescriptorContent::UAV.bits; + } +} + +impl DescriptorContent { + pub fn is_dynamic(&self) -> bool { + self.contains(DescriptorContent::DYNAMIC) + } +} + +impl From for DescriptorContent { + fn from(ty: pso::DescriptorType) -> Self { + use hal::pso::{ + BufferDescriptorFormat as Bdf, BufferDescriptorType as Bdt, DescriptorType as Dt, + ImageDescriptorType as Idt, + }; + + use DescriptorContent as Dc; + + match ty { + Dt::Sampler => Dc::SAMPLER, + Dt::Image { ty } => match ty { + Idt::Storage { read_only: true } => Dc::SRV, + Idt::Storage { read_only: false } => Dc::SRV | Dc::UAV, + Idt::Sampled { with_sampler } => match with_sampler { + true => Dc::SRV | Dc::SAMPLER, + false => Dc::SRV, + }, + }, + Dt::Buffer { ty, format } => match ty { + Bdt::Storage { read_only: true } => match format { + Bdf::Structured { + dynamic_offset: true, + } => Dc::SRV | Dc::DYNAMIC, + Bdf::Structured { + dynamic_offset: false, + } + | Bdf::Texel => Dc::SRV, + }, + Bdt::Storage { read_only: false } => match format { + Bdf::Structured { + dynamic_offset: true, + } => Dc::SRV | Dc::UAV | Dc::DYNAMIC, + Bdf::Structured { + dynamic_offset: false, + } + | Bdf::Texel => Dc::SRV | Dc::UAV, + }, + Bdt::Uniform => match format { + Bdf::Structured { + dynamic_offset: true, + } => Dc::CBV | Dc::DYNAMIC, + Bdf::Structured { + dynamic_offset: false, + } => Dc::CBV, + Bdf::Texel => Dc::SRV, + }, + }, + Dt::InputAttachment => Dc::SRV, + } + } +} + +#[derive(Debug)] +pub struct DescriptorRange { + pub(crate) handle: DualHandle, + pub(crate) ty: pso::DescriptorType, + pub(crate) handle_size: u64, +} + +impl DescriptorRange { + pub(crate) fn at(&self, index: DescriptorIndex) -> native::CpuDescriptor { + assert!(index < self.handle.size); + let ptr = self.handle.cpu.ptr + (self.handle_size * index) as usize; + native::CpuDescriptor { ptr } + } +} + +#[derive(Copy, Clone, Debug)] +pub(crate) struct DynamicDescriptor { + pub content: DescriptorContent, + pub gpu_buffer_location: u64, +} + +#[derive(Debug, Default)] +pub struct DescriptorBindingInfo { + pub(crate) count: u64, + pub(crate) view_range: Option, + pub(crate) dynamic_descriptors: Vec, + pub(crate) content: DescriptorContent, +} + +#[derive(Default)] +pub struct DescriptorOrigins { + // For each index on the heap, this array stores the origin CPU handle. + origins: Vec, +} + +impl DescriptorOrigins { + fn find(&self, other: &[native::CpuDescriptor]) -> Option { + //TODO: need a smarter algorithm here! + for i in other.len()..=self.origins.len() { + let base = i - other.len(); + //TODO: use slice comparison when `CpuDescriptor` implements `PartialEq`. + if unsafe { + slice::from_raw_parts(&self.origins[base].ptr, other.len()) + == slice::from_raw_parts(&other[0].ptr, other.len()) + } { + return Some(base as DescriptorIndex); + } + } + None + } + + fn grow(&mut self, other: &[native::CpuDescriptor]) -> DescriptorIndex { + let base = self.origins.len() as DescriptorIndex; + self.origins.extend_from_slice(other); + base + } +} + +pub struct DescriptorSet { + // Required for binding at command buffer + pub(crate) heap_srv_cbv_uav: native::DescriptorHeap, + pub(crate) heap_samplers: native::DescriptorHeap, + pub(crate) sampler_origins: Box<[native::CpuDescriptor]>, + pub(crate) binding_infos: Vec, + pub(crate) first_gpu_sampler: Option, + pub(crate) first_gpu_view: Option, + pub(crate) raw_name: Vec, +} + +impl fmt::Debug for DescriptorSet { + fn fmt(&self, fmt: &mut fmt::Formatter) -> fmt::Result { + fmt.write_str("DescriptorSet") + } +} + +// TODO: is this really safe? +unsafe impl Send for DescriptorSet {} +unsafe impl Sync for DescriptorSet {} + +impl DescriptorSet { + pub fn srv_cbv_uav_gpu_start(&self) -> native::GpuDescriptor { + self.heap_srv_cbv_uav.start_gpu_descriptor() + } + + pub fn sampler_gpu_start(&self) -> native::GpuDescriptor { + self.heap_samplers.start_gpu_descriptor() + } + + pub fn sampler_offset(&self, binding: u32, last_offset: usize) -> usize { + let mut offset = 0; + for bi in &self.binding_infos[..binding as usize] { + if bi.content.contains(DescriptorContent::SAMPLER) { + offset += bi.count as usize; + } + } + if self.binding_infos[binding as usize] + .content + .contains(DescriptorContent::SAMPLER) + { + offset += last_offset; + } + offset + } + + pub fn update_samplers( + &mut self, + heap: &DescriptorHeap, + origins: &RwLock, + accum: &mut MultiCopyAccumulator, + ) { + let start_index = if let Some(index) = { + // explicit variable allows to limit the lifetime of that borrow + let borrow = origins.read(); + borrow.find(&self.sampler_origins) + } { + Some(index) + } else if self.sampler_origins.iter().any(|desc| desc.ptr == 0) { + // set is incomplete, don't try to build it + None + } else { + let base = origins.write().grow(&self.sampler_origins); + // copy the descriptors from their origins into the new location + accum.dst_samplers.add( + heap.cpu_descriptor_at(base), + self.sampler_origins.len() as u32, + ); + for &origin in self.sampler_origins.iter() { + accum.src_samplers.add(origin, 1); + } + Some(base) + }; + + self.first_gpu_sampler = start_index.map(|index| heap.gpu_descriptor_at(index)); + } +} + +#[derive(Copy, Clone)] +pub struct DualHandle { + pub(crate) cpu: native::CpuDescriptor, + pub(crate) gpu: native::GpuDescriptor, + /// How large the block allocated to this handle is. + pub(crate) size: u64, +} + +impl fmt::Debug for DualHandle { + fn fmt(&self, fmt: &mut fmt::Formatter) -> fmt::Result { + fmt.write_str("DualHandle") + } +} + +pub struct DescriptorHeap { + pub(crate) raw: native::DescriptorHeap, + pub(crate) handle_size: u64, + pub(crate) total_handles: u64, + pub(crate) start: DualHandle, +} + +impl fmt::Debug for DescriptorHeap { + fn fmt(&self, fmt: &mut fmt::Formatter) -> fmt::Result { + fmt.write_str("DescriptorHeap") + } +} + +impl DescriptorHeap { + pub(crate) fn at(&self, index: DescriptorIndex, size: u64) -> DualHandle { + assert!(index < self.total_handles); + DualHandle { + cpu: self.cpu_descriptor_at(index), + gpu: self.gpu_descriptor_at(index), + size, + } + } + + pub(crate) fn cpu_descriptor_at(&self, index: u64) -> native::CpuDescriptor { + native::CpuDescriptor { + ptr: self.start.cpu.ptr + (self.handle_size * index) as usize, + } + } + + pub(crate) fn gpu_descriptor_at(&self, index: u64) -> native::GpuDescriptor { + native::GpuDescriptor { + ptr: self.start.gpu.ptr + self.handle_size * index, + } + } + + pub(crate) unsafe fn destroy(&self) { + self.raw.destroy(); + } +} + +/// Slice of an descriptor heap, which is allocated for a pool. +/// Pools will create descriptor sets inside this slice. +#[derive(Debug)] +pub struct DescriptorHeapSlice { + pub(crate) heap: native::DescriptorHeap, // Weak reference, owned by descriptor heap. + pub(crate) start: DualHandle, + pub(crate) handle_size: u64, + pub(crate) range_allocator: RangeAllocator, +} + +impl DescriptorHeapSlice { + pub(crate) fn alloc_handles(&mut self, count: u64) -> Option { + self.range_allocator + .allocate_range(count) + .ok() + .map(|range| DualHandle { + cpu: native::CpuDescriptor { + ptr: self.start.cpu.ptr + (self.handle_size * range.start) as usize, + }, + gpu: native::GpuDescriptor { + ptr: self.start.gpu.ptr + (self.handle_size * range.start) as u64, + }, + size: count, + }) + } + + /// Free handles previously given out by this `DescriptorHeapSlice`. + /// Do not use this with handles not given out by this `DescriptorHeapSlice`. + pub(crate) fn free_handles(&mut self, handle: DualHandle) { + let start = (handle.gpu.ptr - self.start.gpu.ptr) / self.handle_size; + let handle_range = start..start + handle.size; + self.range_allocator.free_range(handle_range); + } + + /// Clear the allocator. + pub(crate) fn clear(&mut self) { + self.range_allocator.reset(); + } +} + +#[derive(Debug)] +pub struct DescriptorPool { + pub(crate) heap_raw_sampler: native::DescriptorHeap, + pub(crate) heap_srv_cbv_uav: DescriptorHeapSlice, + pub(crate) pools: Vec, + pub(crate) max_size: u64, +} +unsafe impl Send for DescriptorPool {} +unsafe impl Sync for DescriptorPool {} + +impl pso::DescriptorPool for DescriptorPool { + unsafe fn allocate_one( + &mut self, + layout: &DescriptorSetLayout, + ) -> Result { + let mut binding_infos = Vec::new(); + let mut first_gpu_view = None; + let mut num_samplers = 0; + + info!("allocate_one"); + for binding in &layout.bindings { + // Add dummy bindings in case of out-of-range or sparse binding layout. + while binding_infos.len() <= binding.binding as usize { + binding_infos.push(DescriptorBindingInfo::default()); + } + let content = DescriptorContent::from(binding.ty); + debug!("\tbinding {:?} with content {:?}", binding, content); + + let (view_range, dynamic_descriptors) = if content.is_dynamic() { + let descriptor = DynamicDescriptor { + content: content ^ DescriptorContent::DYNAMIC, + gpu_buffer_location: 0, + }; + (None, vec![descriptor; binding.count]) + } else { + if content.contains(DescriptorContent::SAMPLER) { + num_samplers += binding.count; + } + + let view_range = if content.intersects(DescriptorContent::VIEW) { + let count = if content.contains(DescriptorContent::SRV | DescriptorContent::UAV) + { + 2 * binding.count as u64 + } else { + binding.count as u64 + }; + debug!("\tview handles: {}", count); + let handle = self + .heap_srv_cbv_uav + .alloc_handles(count) + .ok_or(pso::AllocationError::OutOfPoolMemory)?; + if first_gpu_view.is_none() { + first_gpu_view = Some(handle.gpu); + } + Some(DescriptorRange { + handle, + ty: binding.ty, + handle_size: self.heap_srv_cbv_uav.handle_size, + }) + } else { + None + }; + + (view_range, Vec::new()) + }; + + binding_infos[binding.binding as usize] = DescriptorBindingInfo { + count: binding.count as _, + view_range, + dynamic_descriptors, + content, + }; + } + + Ok(DescriptorSet { + heap_srv_cbv_uav: self.heap_srv_cbv_uav.heap, + heap_samplers: self.heap_raw_sampler, + sampler_origins: vec![native::CpuDescriptor { ptr: 0 }; num_samplers] + .into_boxed_slice(), + binding_infos, + first_gpu_sampler: None, + first_gpu_view, + raw_name: Vec::new(), + }) + } + + unsafe fn free(&mut self, descriptor_sets: I) + where + I: Iterator, + { + for descriptor_set in descriptor_sets { + for binding_info in descriptor_set.binding_infos { + if let Some(view_range) = binding_info.view_range { + if binding_info.content.intersects(DescriptorContent::VIEW) { + self.heap_srv_cbv_uav.free_handles(view_range.handle); + } + } + } + } + } + + unsafe fn reset(&mut self) { + self.heap_srv_cbv_uav.clear(); + } +} + +#[derive(Debug)] +pub struct QueryPool { + pub(crate) raw: native::QueryHeap, + pub(crate) ty: hal::query::Type, +} + +unsafe impl Send for QueryPool {} +unsafe impl Sync for QueryPool {} diff --git a/third_party/rust/gfx-backend-dx12/src/root_constants.rs b/third_party/rust/gfx-backend-dx12/src/root_constants.rs index b7258e5444e8..0429a508b91a 100644 --- a/third_party/rust/gfx-backend-dx12/src/root_constants.rs +++ b/third_party/rust/gfx-backend-dx12/src/root_constants.rs @@ -1,296 +1,296 @@ -//! Processing push constant ranges -//! -//! This module provides utitlity functions to make push constants root signature -//! compatible. Root constants are non-overlapping, therefore, the push constant -//! ranges passed at pipeline layout creation need to be `split` into disjunct -//! ranges. The disjunct ranges can be then converted into root signature entries. - -use hal::pso; -use std::{cmp::Ordering, ops::Range}; - -#[derive(Clone, Debug, PartialEq, Eq, Hash)] -pub struct RootConstant { - pub stages: pso::ShaderStageFlags, - pub range: Range, -} - -impl RootConstant { - fn is_empty(&self) -> bool { - self.range.end <= self.range.start - } - - // Divide a root constant into two separate ranges depending on the overlap - // with another root constant. - fn divide(self, other: &RootConstant) -> (RootConstant, RootConstant) { - assert!(self.range.start <= other.range.start); - let left = RootConstant { - stages: self.stages, - range: self.range.start..other.range.start, - }; - - let right = RootConstant { - stages: self.stages, - range: other.range.start..self.range.end, - }; - - (left, right) - } -} - -impl PartialOrd for RootConstant { - fn partial_cmp(&self, other: &RootConstant) -> Option { - Some( - self.range - .start - .cmp(&other.range.start) - .then(self.range.end.cmp(&other.range.end)) - .then(self.stages.cmp(&other.stages)), - ) - } -} - -impl Ord for RootConstant { - fn cmp(&self, other: &RootConstant) -> Ordering { - self.partial_cmp(other).unwrap() - } -} - -pub fn split(ranges: I) -> Vec -where - I: IntoIterator)>, -{ - // Frontier of unexplored root constant ranges, sorted descending - // (less element shifting for Vec) regarding to the start of ranges. - let mut ranges = into_vec(ranges); - ranges.sort_by(|a, b| b.cmp(a)); - - // Storing resulting disjunct root constant ranges. - let mut disjunct = Vec::with_capacity(ranges.len()); - - while let Some(cur) = ranges.pop() { - // Run trough all unexplored ranges. After each run the frontier will be - // resorted! - // - // Case 1: Single element remaining - // Push is to the disjunct list, done. - // Case 2: At least two ranges, which possibly overlap - // Divide the first range into a left set and right set, depending - // on the overlap of the two ranges: - // Range 1: |---- left ---||--- right ---| - // Range 2: |--------... - if let Some(mut next) = ranges.pop() { - let (left, mut right) = cur.divide(&next); - if !left.is_empty() { - // The left part is, by definition, disjunct to all other ranges. - // Push all remaining pieces in the frontier, handled by the next - // iteration. - disjunct.push(left); - ranges.push(next); - if !right.is_empty() { - ranges.push(right); - } - } else if !right.is_empty() { - // If the left part is empty this means that both ranges have the - // same start value. The right segment is a candidate for a disjunct - // segment but we haven't checked against other ranges so far. - // Therefore, we push is on the frontier again, but added the - // stage flags from the overlapping segment. - // The second range will be shrunken to be disjunct with the pushed - // segment as we have already processed it. - // In the next iteration we will look again at the push right - // segment and compare it to other elements on the list until we - // have a small enough disjunct segment, which doesn't overlap - // with any part of the frontier. - right.stages |= next.stages; - next.range.start = right.range.end; - ranges.push(right); - if !next.is_empty() { - ranges.push(next); - } - } - } else { - disjunct.push(cur); - } - ranges.sort_by(|a, b| b.cmp(a)); - } - - disjunct -} - -fn into_vec(ranges: I) -> Vec -where - I: IntoIterator)>, -{ - ranges - .into_iter() - .map(|(stages, ref range)| { - debug_assert_eq!(range.start % 4, 0); - debug_assert_eq!(range.end % 4, 0); - RootConstant { - stages, - range: range.start / 4..range.end / 4, - } - }) - .collect() -} - -#[cfg(test)] -mod tests { - use super::*; - - #[test] - fn test_single() { - //TODO: use movable fixed-size ranges when available in Rust - let range = Some((pso::ShaderStageFlags::VERTEX, 0..12)); - assert_eq!(into_vec(range.clone()), split(range)); - } - - #[test] - fn test_overlap_1() { - // Case: - // |----------| - // |------------| - let ranges = vec![ - (pso::ShaderStageFlags::VERTEX, 0..12), - (pso::ShaderStageFlags::FRAGMENT, 8..16), - ]; - - let reference = vec![ - RootConstant { - stages: pso::ShaderStageFlags::VERTEX, - range: 0..2, - }, - RootConstant { - stages: pso::ShaderStageFlags::VERTEX | pso::ShaderStageFlags::FRAGMENT, - range: 2..3, - }, - RootConstant { - stages: pso::ShaderStageFlags::FRAGMENT, - range: 3..4, - }, - ]; - assert_eq!(reference, split(ranges)); - } - - #[test] - fn test_overlap_2() { - // Case: - // |-------------------| - // |------------| - let ranges = vec![ - (pso::ShaderStageFlags::VERTEX, 0..20), - (pso::ShaderStageFlags::FRAGMENT, 8..16), - ]; - - let reference = vec![ - RootConstant { - stages: pso::ShaderStageFlags::VERTEX, - range: 0..2, - }, - RootConstant { - stages: pso::ShaderStageFlags::VERTEX | pso::ShaderStageFlags::FRAGMENT, - range: 2..4, - }, - RootConstant { - stages: pso::ShaderStageFlags::VERTEX, - range: 4..5, - }, - ]; - assert_eq!(reference, split(ranges)); - } - - #[test] - fn test_overlap_4() { - // Case: - // |--------------| - // |------------| - let ranges = vec![ - (pso::ShaderStageFlags::VERTEX, 0..20), - (pso::ShaderStageFlags::FRAGMENT, 0..16), - ]; - - let reference = vec![ - RootConstant { - stages: pso::ShaderStageFlags::VERTEX | pso::ShaderStageFlags::FRAGMENT, - range: 0..4, - }, - RootConstant { - stages: pso::ShaderStageFlags::VERTEX, - range: 4..5, - }, - ]; - assert_eq!(reference, split(ranges)); - } - - #[test] - fn test_equal() { - // Case: - // |-----| - // |-----| - let ranges = vec![ - (pso::ShaderStageFlags::VERTEX, 0..16), - (pso::ShaderStageFlags::FRAGMENT, 0..16), - ]; - - let reference = vec![RootConstant { - stages: pso::ShaderStageFlags::VERTEX | pso::ShaderStageFlags::FRAGMENT, - range: 0..4, - }]; - assert_eq!(reference, split(ranges)); - } - - #[test] - fn test_disjunct() { - // Case: - // |------| - // |------------| - let ranges = vec![ - (pso::ShaderStageFlags::VERTEX, 0..12), - (pso::ShaderStageFlags::FRAGMENT, 12..16), - ]; - assert_eq!(into_vec(ranges.clone()), split(ranges)); - } - - #[test] - fn test_complex() { - let ranges = vec![ - (pso::ShaderStageFlags::VERTEX, 8..40), - (pso::ShaderStageFlags::FRAGMENT, 0..20), - (pso::ShaderStageFlags::GEOMETRY, 24..40), - (pso::ShaderStageFlags::HULL, 16..28), - ]; - - let reference = vec![ - RootConstant { - stages: pso::ShaderStageFlags::FRAGMENT, - range: 0..2, - }, - RootConstant { - stages: pso::ShaderStageFlags::VERTEX | pso::ShaderStageFlags::FRAGMENT, - range: 2..4, - }, - RootConstant { - stages: pso::ShaderStageFlags::VERTEX - | pso::ShaderStageFlags::FRAGMENT - | pso::ShaderStageFlags::HULL, - range: 4..5, - }, - RootConstant { - stages: pso::ShaderStageFlags::VERTEX | pso::ShaderStageFlags::HULL, - range: 5..6, - }, - RootConstant { - stages: pso::ShaderStageFlags::VERTEX - | pso::ShaderStageFlags::GEOMETRY - | pso::ShaderStageFlags::HULL, - range: 6..7, - }, - RootConstant { - stages: pso::ShaderStageFlags::VERTEX | pso::ShaderStageFlags::GEOMETRY, - range: 7..10, - }, - ]; - - assert_eq!(reference, split(ranges)); - } -} +//! Processing push constant ranges +//! +//! This module provides utitlity functions to make push constants root signature +//! compatible. Root constants are non-overlapping, therefore, the push constant +//! ranges passed at pipeline layout creation need to be `split` into disjunct +//! ranges. The disjunct ranges can be then converted into root signature entries. + +use hal::pso; +use std::{cmp::Ordering, ops::Range}; + +#[derive(Clone, Debug, PartialEq, Eq, Hash)] +pub struct RootConstant { + pub stages: pso::ShaderStageFlags, + pub range: Range, +} + +impl RootConstant { + fn is_empty(&self) -> bool { + self.range.end <= self.range.start + } + + // Divide a root constant into two separate ranges depending on the overlap + // with another root constant. + fn divide(self, other: &RootConstant) -> (RootConstant, RootConstant) { + assert!(self.range.start <= other.range.start); + let left = RootConstant { + stages: self.stages, + range: self.range.start..other.range.start, + }; + + let right = RootConstant { + stages: self.stages, + range: other.range.start..self.range.end, + }; + + (left, right) + } +} + +impl PartialOrd for RootConstant { + fn partial_cmp(&self, other: &RootConstant) -> Option { + Some( + self.range + .start + .cmp(&other.range.start) + .then(self.range.end.cmp(&other.range.end)) + .then(self.stages.cmp(&other.stages)), + ) + } +} + +impl Ord for RootConstant { + fn cmp(&self, other: &RootConstant) -> Ordering { + self.partial_cmp(other).unwrap() + } +} + +pub fn split(ranges: I) -> Vec +where + I: IntoIterator)>, +{ + // Frontier of unexplored root constant ranges, sorted descending + // (less element shifting for Vec) regarding to the start of ranges. + let mut ranges = into_vec(ranges); + ranges.sort_by(|a, b| b.cmp(a)); + + // Storing resulting disjunct root constant ranges. + let mut disjunct = Vec::with_capacity(ranges.len()); + + while let Some(cur) = ranges.pop() { + // Run trough all unexplored ranges. After each run the frontier will be + // resorted! + // + // Case 1: Single element remaining + // Push is to the disjunct list, done. + // Case 2: At least two ranges, which possibly overlap + // Divide the first range into a left set and right set, depending + // on the overlap of the two ranges: + // Range 1: |---- left ---||--- right ---| + // Range 2: |--------... + if let Some(mut next) = ranges.pop() { + let (left, mut right) = cur.divide(&next); + if !left.is_empty() { + // The left part is, by definition, disjunct to all other ranges. + // Push all remaining pieces in the frontier, handled by the next + // iteration. + disjunct.push(left); + ranges.push(next); + if !right.is_empty() { + ranges.push(right); + } + } else if !right.is_empty() { + // If the left part is empty this means that both ranges have the + // same start value. The right segment is a candidate for a disjunct + // segment but we haven't checked against other ranges so far. + // Therefore, we push is on the frontier again, but added the + // stage flags from the overlapping segment. + // The second range will be shrunken to be disjunct with the pushed + // segment as we have already processed it. + // In the next iteration we will look again at the push right + // segment and compare it to other elements on the list until we + // have a small enough disjunct segment, which doesn't overlap + // with any part of the frontier. + right.stages |= next.stages; + next.range.start = right.range.end; + ranges.push(right); + if !next.is_empty() { + ranges.push(next); + } + } + } else { + disjunct.push(cur); + } + ranges.sort_by(|a, b| b.cmp(a)); + } + + disjunct +} + +fn into_vec(ranges: I) -> Vec +where + I: IntoIterator)>, +{ + ranges + .into_iter() + .map(|(stages, ref range)| { + debug_assert_eq!(range.start % 4, 0); + debug_assert_eq!(range.end % 4, 0); + RootConstant { + stages, + range: range.start / 4..range.end / 4, + } + }) + .collect() +} + +#[cfg(test)] +mod tests { + use super::*; + + #[test] + fn test_single() { + //TODO: use movable fixed-size ranges when available in Rust + let range = Some((pso::ShaderStageFlags::VERTEX, 0..12)); + assert_eq!(into_vec(range.clone()), split(range)); + } + + #[test] + fn test_overlap_1() { + // Case: + // |----------| + // |------------| + let ranges = vec![ + (pso::ShaderStageFlags::VERTEX, 0..12), + (pso::ShaderStageFlags::FRAGMENT, 8..16), + ]; + + let reference = vec![ + RootConstant { + stages: pso::ShaderStageFlags::VERTEX, + range: 0..2, + }, + RootConstant { + stages: pso::ShaderStageFlags::VERTEX | pso::ShaderStageFlags::FRAGMENT, + range: 2..3, + }, + RootConstant { + stages: pso::ShaderStageFlags::FRAGMENT, + range: 3..4, + }, + ]; + assert_eq!(reference, split(ranges)); + } + + #[test] + fn test_overlap_2() { + // Case: + // |-------------------| + // |------------| + let ranges = vec![ + (pso::ShaderStageFlags::VERTEX, 0..20), + (pso::ShaderStageFlags::FRAGMENT, 8..16), + ]; + + let reference = vec![ + RootConstant { + stages: pso::ShaderStageFlags::VERTEX, + range: 0..2, + }, + RootConstant { + stages: pso::ShaderStageFlags::VERTEX | pso::ShaderStageFlags::FRAGMENT, + range: 2..4, + }, + RootConstant { + stages: pso::ShaderStageFlags::VERTEX, + range: 4..5, + }, + ]; + assert_eq!(reference, split(ranges)); + } + + #[test] + fn test_overlap_4() { + // Case: + // |--------------| + // |------------| + let ranges = vec![ + (pso::ShaderStageFlags::VERTEX, 0..20), + (pso::ShaderStageFlags::FRAGMENT, 0..16), + ]; + + let reference = vec![ + RootConstant { + stages: pso::ShaderStageFlags::VERTEX | pso::ShaderStageFlags::FRAGMENT, + range: 0..4, + }, + RootConstant { + stages: pso::ShaderStageFlags::VERTEX, + range: 4..5, + }, + ]; + assert_eq!(reference, split(ranges)); + } + + #[test] + fn test_equal() { + // Case: + // |-----| + // |-----| + let ranges = vec![ + (pso::ShaderStageFlags::VERTEX, 0..16), + (pso::ShaderStageFlags::FRAGMENT, 0..16), + ]; + + let reference = vec![RootConstant { + stages: pso::ShaderStageFlags::VERTEX | pso::ShaderStageFlags::FRAGMENT, + range: 0..4, + }]; + assert_eq!(reference, split(ranges)); + } + + #[test] + fn test_disjunct() { + // Case: + // |------| + // |------------| + let ranges = vec![ + (pso::ShaderStageFlags::VERTEX, 0..12), + (pso::ShaderStageFlags::FRAGMENT, 12..16), + ]; + assert_eq!(into_vec(ranges.clone()), split(ranges)); + } + + #[test] + fn test_complex() { + let ranges = vec![ + (pso::ShaderStageFlags::VERTEX, 8..40), + (pso::ShaderStageFlags::FRAGMENT, 0..20), + (pso::ShaderStageFlags::GEOMETRY, 24..40), + (pso::ShaderStageFlags::HULL, 16..28), + ]; + + let reference = vec![ + RootConstant { + stages: pso::ShaderStageFlags::FRAGMENT, + range: 0..2, + }, + RootConstant { + stages: pso::ShaderStageFlags::VERTEX | pso::ShaderStageFlags::FRAGMENT, + range: 2..4, + }, + RootConstant { + stages: pso::ShaderStageFlags::VERTEX + | pso::ShaderStageFlags::FRAGMENT + | pso::ShaderStageFlags::HULL, + range: 4..5, + }, + RootConstant { + stages: pso::ShaderStageFlags::VERTEX | pso::ShaderStageFlags::HULL, + range: 5..6, + }, + RootConstant { + stages: pso::ShaderStageFlags::VERTEX + | pso::ShaderStageFlags::GEOMETRY + | pso::ShaderStageFlags::HULL, + range: 6..7, + }, + RootConstant { + stages: pso::ShaderStageFlags::VERTEX | pso::ShaderStageFlags::GEOMETRY, + range: 7..10, + }, + ]; + + assert_eq!(reference, split(ranges)); + } +} diff --git a/third_party/rust/gfx-backend-dx12/src/window.rs b/third_party/rust/gfx-backend-dx12/src/window.rs index 9b99b0760184..bf31c9cbfc73 100644 --- a/third_party/rust/gfx-backend-dx12/src/window.rs +++ b/third_party/rust/gfx-backend-dx12/src/window.rs @@ -1,349 +1,349 @@ -use std::{borrow::Borrow, fmt, mem, os::raw::c_void}; - -use winapi::{ - shared::{ - dxgi, dxgi1_4, dxgi1_5, dxgitype, - minwindef::{BOOL, FALSE, TRUE}, - windef::{HWND, RECT}, - winerror, - }, - um::{d3d12, synchapi, winbase, winnt::HANDLE, winuser::GetClientRect}, -}; - -use crate::{conv, resource as r, Backend, Device, Instance, PhysicalDevice, QueueFamily}; -use hal::{device::Device as _, format as f, image as i, window as w}; - -impl Instance { - pub fn create_surface_from_hwnd(&self, hwnd: *mut c_void) -> Surface { - Surface { - factory: self.factory, - wnd_handle: hwnd as *mut _, - presentation: None, - } - } -} - -#[derive(Debug)] -struct Presentation { - swapchain: Swapchain, - format: f::Format, - size: w::Extent2D, - mode: w::PresentMode, -} - -pub struct Surface { - pub(crate) factory: native::WeakPtr, - pub(crate) wnd_handle: HWND, - presentation: Option, -} - -impl fmt::Debug for Surface { - fn fmt(&self, fmt: &mut fmt::Formatter) -> fmt::Result { - fmt.write_str("Surface") - } -} - -unsafe impl Send for Surface {} -unsafe impl Sync for Surface {} - -impl Surface { - pub(crate) unsafe fn present(&mut self, image: SwapchainImage) -> Result<(), w::PresentError> { - let present = self.presentation.as_mut().unwrap(); - let sc = &mut present.swapchain; - sc.acquired_count -= 1; - - let expected_index = sc.inner.GetCurrentBackBufferIndex() as w::SwapImageIndex; - if image.index != expected_index { - log::warn!( - "Expected frame {}, got frame {}", - expected_index, - image.index - ); - return Err(w::OutOfDate.into()); - } - - let (interval, flags) = match present.mode { - w::PresentMode::IMMEDIATE => (0, dxgi::DXGI_PRESENT_ALLOW_TEARING), - w::PresentMode::FIFO => (1, 0), - _ => (1, 0), // Surface was created with an unsupported present mode, fall back to FIFO - }; - - sc.inner.Present(interval, flags); - Ok(()) - } -} - -impl w::Surface for Surface { - fn supports_queue_family(&self, queue_family: &QueueFamily) -> bool { - match queue_family { - &QueueFamily::Present => true, - _ => false, - } - } - - fn capabilities(&self, _physical_device: &PhysicalDevice) -> w::SurfaceCapabilities { - let current_extent = unsafe { - let mut rect: RECT = mem::zeroed(); - if GetClientRect(self.wnd_handle as *mut _, &mut rect as *mut RECT) == 0 { - panic!("GetClientRect failed"); - } - Some(w::Extent2D { - width: (rect.right - rect.left) as u32, - height: (rect.bottom - rect.top) as u32, - }) - }; - - let allow_tearing = unsafe { - let (f5, hr) = self.factory.cast::(); - if winerror::SUCCEEDED(hr) { - let mut allow_tearing: BOOL = FALSE; - let hr = f5.CheckFeatureSupport( - dxgi1_5::DXGI_FEATURE_PRESENT_ALLOW_TEARING, - &mut allow_tearing as *mut _ as *mut _, - mem::size_of::() as _, - ); - - f5.destroy(); - - winerror::SUCCEEDED(hr) && allow_tearing == TRUE - } else { - false - } - }; - - let mut present_modes = w::PresentMode::FIFO; - if allow_tearing { - present_modes |= w::PresentMode::IMMEDIATE; - } - - w::SurfaceCapabilities { - present_modes, - composite_alpha_modes: w::CompositeAlphaMode::OPAQUE, //TODO - image_count: 2..=16, // we currently use a flip effect which supports 2..=16 buffers - current_extent, - extents: w::Extent2D { - width: 16, - height: 16, - }..=w::Extent2D { - width: 4096, - height: 4096, - }, - max_image_layers: 1, - usage: i::Usage::COLOR_ATTACHMENT | i::Usage::TRANSFER_SRC | i::Usage::TRANSFER_DST, - } - } - - fn supported_formats(&self, _physical_device: &PhysicalDevice) -> Option> { - Some(vec![ - f::Format::Bgra8Srgb, - f::Format::Bgra8Unorm, - f::Format::Rgba8Srgb, - f::Format::Rgba8Unorm, - f::Format::A2b10g10r10Unorm, - f::Format::Rgba16Sfloat, - ]) - } -} - -#[derive(Debug)] -pub struct SwapchainImage { - index: w::SwapImageIndex, - image: r::Image, - view: r::ImageView, -} - -impl Borrow for SwapchainImage { - fn borrow(&self) -> &r::Image { - &self.image - } -} - -impl Borrow for SwapchainImage { - fn borrow(&self) -> &r::ImageView { - &self.view - } -} - -impl w::PresentationSurface for Surface { - type SwapchainImage = SwapchainImage; - - unsafe fn configure_swapchain( - &mut self, - device: &Device, - config: w::SwapchainConfig, - ) -> Result<(), w::SwapchainError> { - assert!(i::Usage::COLOR_ATTACHMENT.contains(config.image_usage)); - - let swapchain = match self.presentation.take() { - Some(present) => { - if present.format == config.format && present.size == config.extent { - self.presentation = Some(present); - return Ok(()); - } - // can't have image resources in flight used by GPU - device.wait_idle().unwrap(); - - let mut flags = dxgi::DXGI_SWAP_CHAIN_FLAG_FRAME_LATENCY_WAITABLE_OBJECT; - if config.present_mode.contains(w::PresentMode::IMMEDIATE) { - flags |= dxgi::DXGI_SWAP_CHAIN_FLAG_ALLOW_TEARING; - } - - let inner = present.swapchain.release_resources(); - let result = inner.ResizeBuffers( - config.image_count, - config.extent.width, - config.extent.height, - conv::map_format_nosrgb(config.format).unwrap(), - flags, - ); - if result != winerror::S_OK { - error!("ResizeBuffers failed with 0x{:x}", result as u32); - return Err(w::SwapchainError::WindowInUse); - } - inner - } - None => { - let (swapchain, _) = - device.create_swapchain_impl(&config, self.wnd_handle, self.factory.clone())?; - swapchain - } - }; - - // Disable automatic Alt+Enter handling by DXGI. - const DXGI_MWA_NO_WINDOW_CHANGES: u32 = 1; - const DXGI_MWA_NO_ALT_ENTER: u32 = 2; - self.factory.MakeWindowAssociation( - self.wnd_handle, - DXGI_MWA_NO_WINDOW_CHANGES | DXGI_MWA_NO_ALT_ENTER, - ); - - self.presentation = Some(Presentation { - swapchain: device.wrap_swapchain(swapchain, &config), - format: config.format, - size: config.extent, - mode: config.present_mode, - }); - Ok(()) - } - - unsafe fn unconfigure_swapchain(&mut self, device: &Device) { - if let Some(mut present) = self.presentation.take() { - let _ = present.swapchain.wait(winbase::INFINITE); - let _ = device.wait_idle(); //TODO: this shouldn't be needed, - // but it complains that the queue is still used otherwise - let inner = present.swapchain.release_resources(); - inner.destroy(); - } - } - - unsafe fn acquire_image( - &mut self, - timeout_ns: u64, - ) -> Result<(SwapchainImage, Option), w::AcquireError> { - let present = self.presentation.as_mut().unwrap(); - let sc = &mut present.swapchain; - - sc.wait((timeout_ns / 1_000_000) as u32)?; - - let base_index = sc.inner.GetCurrentBackBufferIndex() as usize; - let index = (base_index + sc.acquired_count) % sc.resources.len(); - sc.acquired_count += 1; - let resource = sc.resources[index]; - - let kind = i::Kind::D2(present.size.width, present.size.height, 1, 1); - let base_format = present.format.base_format(); - let dxgi_format = conv::map_format(present.format).unwrap(); - let rtv = sc.rtv_heap.at(index as _, 0).cpu; - - let descriptor = d3d12::D3D12_RESOURCE_DESC { - Dimension: d3d12::D3D12_RESOURCE_DIMENSION_TEXTURE2D, - Alignment: 0, - Width: present.size.width as _, - Height: present.size.height as _, - DepthOrArraySize: 1, - MipLevels: 1, - Format: dxgi_format, - SampleDesc: dxgitype::DXGI_SAMPLE_DESC { - Count: 1, - Quality: 0, - }, - Layout: d3d12::D3D12_TEXTURE_LAYOUT_UNKNOWN, - Flags: d3d12::D3D12_RESOURCE_FLAG_NONE, //TODO? - }; - - let image = r::ImageBound { - resource, - place: r::Place::Swapchain {}, - surface_type: base_format.0, - kind, - mip_levels: 1, - default_view_format: None, - view_caps: i::ViewCapabilities::empty(), - descriptor, - clear_cv: Vec::new(), //TODO - clear_dv: Vec::new(), - clear_sv: Vec::new(), - requirements: hal::memory::Requirements { - size: 0, - alignment: 1, - type_mask: 0, - }, - }; - - let swapchain_image = SwapchainImage { - index: index as _, - image: r::Image::Bound(image), - view: r::ImageView { - resource, - handle_srv: None, - handle_rtv: r::RenderTargetHandle::Swapchain(rtv), - handle_uav: None, - handle_dsv: None, - dxgi_format, - num_levels: 1, - mip_levels: (0, 1), - layers: (0, 1), - kind, - }, - }; - - Ok((swapchain_image, None)) - } -} - -#[derive(Debug)] -pub struct Swapchain { - pub(crate) inner: native::WeakPtr, - #[allow(dead_code)] - pub(crate) rtv_heap: r::DescriptorHeap, - // need to associate raw image pointers with the swapchain so they can be properly released - // when the swapchain is destroyed - pub(crate) resources: Vec, - pub(crate) waitable: HANDLE, - pub(crate) usage: i::Usage, - pub(crate) acquired_count: usize, -} - -impl Swapchain { - pub(crate) unsafe fn release_resources(self) -> native::WeakPtr { - for resource in &self.resources { - resource.destroy(); - } - self.rtv_heap.destroy(); - self.inner - } - - pub(crate) fn wait(&mut self, timeout_ms: u32) -> Result<(), w::AcquireError> { - match unsafe { synchapi::WaitForSingleObject(self.waitable, timeout_ms) } { - winbase::WAIT_ABANDONED | winbase::WAIT_FAILED => { - Err(w::AcquireError::DeviceLost(hal::device::DeviceLost)) - } - winbase::WAIT_OBJECT_0 => Ok(()), - winerror::WAIT_TIMEOUT => Err(w::AcquireError::NotReady { timeout: true }), - hr => panic!("Unexpected wait status 0x{:X}", hr), - } - } -} - -unsafe impl Send for Swapchain {} -unsafe impl Sync for Swapchain {} +use std::{borrow::Borrow, fmt, mem, os::raw::c_void}; + +use winapi::{ + shared::{ + dxgi, dxgi1_4, dxgi1_5, dxgitype, + minwindef::{BOOL, FALSE, TRUE}, + windef::{HWND, RECT}, + winerror, + }, + um::{d3d12, synchapi, winbase, winnt::HANDLE, winuser::GetClientRect}, +}; + +use crate::{conv, resource as r, Backend, Device, Instance, PhysicalDevice, QueueFamily}; +use hal::{device::Device as _, format as f, image as i, window as w}; + +impl Instance { + pub fn create_surface_from_hwnd(&self, hwnd: *mut c_void) -> Surface { + Surface { + factory: self.factory, + wnd_handle: hwnd as *mut _, + presentation: None, + } + } +} + +#[derive(Debug)] +struct Presentation { + swapchain: Swapchain, + format: f::Format, + size: w::Extent2D, + mode: w::PresentMode, +} + +pub struct Surface { + pub(crate) factory: native::WeakPtr, + pub(crate) wnd_handle: HWND, + presentation: Option, +} + +impl fmt::Debug for Surface { + fn fmt(&self, fmt: &mut fmt::Formatter) -> fmt::Result { + fmt.write_str("Surface") + } +} + +unsafe impl Send for Surface {} +unsafe impl Sync for Surface {} + +impl Surface { + pub(crate) unsafe fn present(&mut self, image: SwapchainImage) -> Result<(), w::PresentError> { + let present = self.presentation.as_mut().unwrap(); + let sc = &mut present.swapchain; + sc.acquired_count -= 1; + + let expected_index = sc.inner.GetCurrentBackBufferIndex() as w::SwapImageIndex; + if image.index != expected_index { + log::warn!( + "Expected frame {}, got frame {}", + expected_index, + image.index + ); + return Err(w::OutOfDate.into()); + } + + let (interval, flags) = match present.mode { + w::PresentMode::IMMEDIATE => (0, dxgi::DXGI_PRESENT_ALLOW_TEARING), + w::PresentMode::FIFO => (1, 0), + _ => (1, 0), // Surface was created with an unsupported present mode, fall back to FIFO + }; + + sc.inner.Present(interval, flags); + Ok(()) + } +} + +impl w::Surface for Surface { + fn supports_queue_family(&self, queue_family: &QueueFamily) -> bool { + match queue_family { + &QueueFamily::Present => true, + _ => false, + } + } + + fn capabilities(&self, _physical_device: &PhysicalDevice) -> w::SurfaceCapabilities { + let current_extent = unsafe { + let mut rect: RECT = mem::zeroed(); + if GetClientRect(self.wnd_handle as *mut _, &mut rect as *mut RECT) == 0 { + panic!("GetClientRect failed"); + } + Some(w::Extent2D { + width: (rect.right - rect.left) as u32, + height: (rect.bottom - rect.top) as u32, + }) + }; + + let allow_tearing = unsafe { + let (f5, hr) = self.factory.cast::(); + if winerror::SUCCEEDED(hr) { + let mut allow_tearing: BOOL = FALSE; + let hr = f5.CheckFeatureSupport( + dxgi1_5::DXGI_FEATURE_PRESENT_ALLOW_TEARING, + &mut allow_tearing as *mut _ as *mut _, + mem::size_of::() as _, + ); + + f5.destroy(); + + winerror::SUCCEEDED(hr) && allow_tearing == TRUE + } else { + false + } + }; + + let mut present_modes = w::PresentMode::FIFO; + if allow_tearing { + present_modes |= w::PresentMode::IMMEDIATE; + } + + w::SurfaceCapabilities { + present_modes, + composite_alpha_modes: w::CompositeAlphaMode::OPAQUE, //TODO + image_count: 2..=16, // we currently use a flip effect which supports 2..=16 buffers + current_extent, + extents: w::Extent2D { + width: 16, + height: 16, + }..=w::Extent2D { + width: 4096, + height: 4096, + }, + max_image_layers: 1, + usage: i::Usage::COLOR_ATTACHMENT | i::Usage::TRANSFER_SRC | i::Usage::TRANSFER_DST, + } + } + + fn supported_formats(&self, _physical_device: &PhysicalDevice) -> Option> { + Some(vec![ + f::Format::Bgra8Srgb, + f::Format::Bgra8Unorm, + f::Format::Rgba8Srgb, + f::Format::Rgba8Unorm, + f::Format::A2b10g10r10Unorm, + f::Format::Rgba16Sfloat, + ]) + } +} + +#[derive(Debug)] +pub struct SwapchainImage { + index: w::SwapImageIndex, + image: r::Image, + view: r::ImageView, +} + +impl Borrow for SwapchainImage { + fn borrow(&self) -> &r::Image { + &self.image + } +} + +impl Borrow for SwapchainImage { + fn borrow(&self) -> &r::ImageView { + &self.view + } +} + +impl w::PresentationSurface for Surface { + type SwapchainImage = SwapchainImage; + + unsafe fn configure_swapchain( + &mut self, + device: &Device, + config: w::SwapchainConfig, + ) -> Result<(), w::SwapchainError> { + assert!(i::Usage::COLOR_ATTACHMENT.contains(config.image_usage)); + + let swapchain = match self.presentation.take() { + Some(present) => { + if present.format == config.format && present.size == config.extent { + self.presentation = Some(present); + return Ok(()); + } + // can't have image resources in flight used by GPU + device.wait_idle().unwrap(); + + let mut flags = dxgi::DXGI_SWAP_CHAIN_FLAG_FRAME_LATENCY_WAITABLE_OBJECT; + if config.present_mode.contains(w::PresentMode::IMMEDIATE) { + flags |= dxgi::DXGI_SWAP_CHAIN_FLAG_ALLOW_TEARING; + } + + let inner = present.swapchain.release_resources(); + let result = inner.ResizeBuffers( + config.image_count, + config.extent.width, + config.extent.height, + conv::map_format_nosrgb(config.format).unwrap(), + flags, + ); + if result != winerror::S_OK { + error!("ResizeBuffers failed with 0x{:x}", result as u32); + return Err(w::SwapchainError::WindowInUse); + } + inner + } + None => { + let (swapchain, _) = + device.create_swapchain_impl(&config, self.wnd_handle, self.factory.clone())?; + swapchain + } + }; + + // Disable automatic Alt+Enter handling by DXGI. + const DXGI_MWA_NO_WINDOW_CHANGES: u32 = 1; + const DXGI_MWA_NO_ALT_ENTER: u32 = 2; + self.factory.MakeWindowAssociation( + self.wnd_handle, + DXGI_MWA_NO_WINDOW_CHANGES | DXGI_MWA_NO_ALT_ENTER, + ); + + self.presentation = Some(Presentation { + swapchain: device.wrap_swapchain(swapchain, &config), + format: config.format, + size: config.extent, + mode: config.present_mode, + }); + Ok(()) + } + + unsafe fn unconfigure_swapchain(&mut self, device: &Device) { + if let Some(mut present) = self.presentation.take() { + let _ = present.swapchain.wait(winbase::INFINITE); + let _ = device.wait_idle(); //TODO: this shouldn't be needed, + // but it complains that the queue is still used otherwise + let inner = present.swapchain.release_resources(); + inner.destroy(); + } + } + + unsafe fn acquire_image( + &mut self, + timeout_ns: u64, + ) -> Result<(SwapchainImage, Option), w::AcquireError> { + let present = self.presentation.as_mut().unwrap(); + let sc = &mut present.swapchain; + + sc.wait((timeout_ns / 1_000_000) as u32)?; + + let base_index = sc.inner.GetCurrentBackBufferIndex() as usize; + let index = (base_index + sc.acquired_count) % sc.resources.len(); + sc.acquired_count += 1; + let resource = sc.resources[index]; + + let kind = i::Kind::D2(present.size.width, present.size.height, 1, 1); + let base_format = present.format.base_format(); + let dxgi_format = conv::map_format(present.format).unwrap(); + let rtv = sc.rtv_heap.at(index as _, 0).cpu; + + let descriptor = d3d12::D3D12_RESOURCE_DESC { + Dimension: d3d12::D3D12_RESOURCE_DIMENSION_TEXTURE2D, + Alignment: 0, + Width: present.size.width as _, + Height: present.size.height as _, + DepthOrArraySize: 1, + MipLevels: 1, + Format: dxgi_format, + SampleDesc: dxgitype::DXGI_SAMPLE_DESC { + Count: 1, + Quality: 0, + }, + Layout: d3d12::D3D12_TEXTURE_LAYOUT_UNKNOWN, + Flags: d3d12::D3D12_RESOURCE_FLAG_NONE, //TODO? + }; + + let image = r::ImageBound { + resource, + place: r::Place::Swapchain {}, + surface_type: base_format.0, + kind, + mip_levels: 1, + default_view_format: None, + view_caps: i::ViewCapabilities::empty(), + descriptor, + clear_cv: Vec::new(), //TODO + clear_dv: Vec::new(), + clear_sv: Vec::new(), + requirements: hal::memory::Requirements { + size: 0, + alignment: 1, + type_mask: 0, + }, + }; + + let swapchain_image = SwapchainImage { + index: index as _, + image: r::Image::Bound(image), + view: r::ImageView { + resource, + handle_srv: None, + handle_rtv: r::RenderTargetHandle::Swapchain(rtv), + handle_uav: None, + handle_dsv: None, + dxgi_format, + num_levels: 1, + mip_levels: (0, 1), + layers: (0, 1), + kind, + }, + }; + + Ok((swapchain_image, None)) + } +} + +#[derive(Debug)] +pub struct Swapchain { + pub(crate) inner: native::WeakPtr, + #[allow(dead_code)] + pub(crate) rtv_heap: r::DescriptorHeap, + // need to associate raw image pointers with the swapchain so they can be properly released + // when the swapchain is destroyed + pub(crate) resources: Vec, + pub(crate) waitable: HANDLE, + pub(crate) usage: i::Usage, + pub(crate) acquired_count: usize, +} + +impl Swapchain { + pub(crate) unsafe fn release_resources(self) -> native::WeakPtr { + for resource in &self.resources { + resource.destroy(); + } + self.rtv_heap.destroy(); + self.inner + } + + pub(crate) fn wait(&mut self, timeout_ms: u32) -> Result<(), w::AcquireError> { + match unsafe { synchapi::WaitForSingleObject(self.waitable, timeout_ms) } { + winbase::WAIT_ABANDONED | winbase::WAIT_FAILED => { + Err(w::AcquireError::DeviceLost(hal::device::DeviceLost)) + } + winbase::WAIT_OBJECT_0 => Ok(()), + winerror::WAIT_TIMEOUT => Err(w::AcquireError::NotReady { timeout: true }), + hr => panic!("Unexpected wait status 0x{:X}", hr), + } + } +} + +unsafe impl Send for Swapchain {} +unsafe impl Sync for Swapchain {} diff --git a/third_party/rust/gfx-backend-empty/.cargo-checksum.json b/third_party/rust/gfx-backend-empty/.cargo-checksum.json index e5d4c5d343eb..2f89284967de 100644 --- a/third_party/rust/gfx-backend-empty/.cargo-checksum.json +++ b/third_party/rust/gfx-backend-empty/.cargo-checksum.json @@ -1 +1 @@ -{"files":{"Cargo.toml":"4a7e10051a77e85a55cfda15b7257ba14f3e190ab1fd269fc22d44399d53fc81","src/buffer.rs":"1a275e7a1d4bebe594256709e7ee166222518178bd6112cd45c29d4aa6d002dd","src/descriptor.rs":"0689f381dcb0a8603167495373d6cfae9d50f2b5017ab3e364bc838949548827","src/image.rs":"83dba8bae97e06ced4f8030f208566dd773b773be14bc56f10da92dedec041f0","src/lib.rs":"8d0fc7b36f57b0f1e67522c97d69476fb8e244cc52a92259d57ee803f5000d0a","src/memory.rs":"abba2a8943dccf79cc67de3ce33dc534606fe3838220504a1330f773c7bf0ac6"},"package":"29c8f813c47791918aa00dc9c9ddf961d23fa8c2a5d869e6cb8ea84f944820f4"} \ No newline at end of file +{"files":{"Cargo.toml":"fb40d41fae85879147b82135a1e39111778fb70d85cf246b2fa2d09b03152136","src/buffer.rs":"1a275e7a1d4bebe594256709e7ee166222518178bd6112cd45c29d4aa6d002dd","src/descriptor.rs":"0689f381dcb0a8603167495373d6cfae9d50f2b5017ab3e364bc838949548827","src/image.rs":"83dba8bae97e06ced4f8030f208566dd773b773be14bc56f10da92dedec041f0","src/lib.rs":"fa80d2e782f95798488931648c6e26c83470dbf3f8f942c0b3362d723dd3e0e0","src/memory.rs":"abba2a8943dccf79cc67de3ce33dc534606fe3838220504a1330f773c7bf0ac6"},"package":null} \ No newline at end of file diff --git a/third_party/rust/gfx-backend-empty/Cargo.toml b/third_party/rust/gfx-backend-empty/Cargo.toml index 27e1de9d6b1a..0ecded140704 100644 --- a/third_party/rust/gfx-backend-empty/Cargo.toml +++ b/third_party/rust/gfx-backend-empty/Cargo.toml @@ -1,31 +1,17 @@ -# THIS FILE IS AUTOMATICALLY GENERATED BY CARGO -# -# When uploading crates to the registry Cargo will automatically -# "normalize" Cargo.toml files for maximal compatibility -# with all versions of Cargo and also rewrite `path` dependencies -# to registry (e.g., crates.io) dependencies -# -# If you believe there's an error in this file please file an -# issue against the rust-lang/cargo repository. If you're -# editing this file be aware that the upstream Cargo.toml -# will likely look very different (and much more reasonable) - [package] -edition = "2018" name = "gfx-backend-empty" -version = "0.9.0" -authors = ["The Gfx-rs Developers"] +version = "0.8.0" description = "Empty backend for gfx-rs" -documentation = "https://docs.rs/gfx-backend-empty" license = "MIT OR Apache-2.0" +authors = ["The Gfx-rs Developers"] +documentation = "https://docs.rs/gfx-backend-empty" +workspace = "../../.." +edition = "2018" [lib] name = "gfx_backend_empty" -[dependencies.gfx-hal] -version = "0.9" -[dependencies.log] -version = "0.4" - -[dependencies.raw-window-handle] -version = "0.3" +[dependencies] +gfx-hal = { path = "../../hal", version = "0.8" } +raw-window-handle = "0.3" +log = "0.4" diff --git a/third_party/rust/gfx-backend-empty/src/lib.rs b/third_party/rust/gfx-backend-empty/src/lib.rs index 855dc915ffc2..64aca3569510 100644 --- a/third_party/rust/gfx-backend-empty/src/lib.rs +++ b/third_party/rust/gfx-backend-empty/src/lib.rs @@ -10,7 +10,7 @@ use crate::{ memory::Memory, }; -use hal::{adapter, command, device, display, format, pass, pool, pso, query, queue, window}; +use hal::{adapter, command, device, format, pass, pool, pso, query, queue, window}; use log::debug; use std::{borrow::Borrow, ops::Range}; @@ -60,9 +60,6 @@ impl hal::Backend for Backend { type Semaphore = (); type Event = (); type QueryPool = (); - - type Display = (); - type DisplayMode = (); } /// Dummy physical device. @@ -144,30 +141,6 @@ impl adapter::PhysicalDevice for PhysicalDevice { } } - fn external_buffer_properties( - &self, - _usage: hal::buffer::Usage, - _sparse: hal::memory::SparseFlags, - _memory_type: hal::external_memory::ExternalMemoryType, - ) -> hal::external_memory::ExternalMemoryProperties { - unimplemented!() - } - - fn external_image_properties( - &self, - _format: hal::format::Format, - _dimensions: u8, - _tiling: hal::image::Tiling, - _usage: hal::image::Usage, - _view_caps: hal::image::ViewCapabilities, - _memory_type: hal::external_memory::ExternalMemoryType, - ) -> Result< - hal::external_memory::ExternalMemoryProperties, - hal::external_memory::ExternalImagePropertiesError, - > { - unimplemented!() - } - fn features(&self) -> hal::Features { hal::Features::empty() } @@ -182,34 +155,6 @@ impl adapter::PhysicalDevice for PhysicalDevice { ..Default::default() } } - - unsafe fn enumerate_displays(&self) -> Vec> { - unimplemented!(); - } - - unsafe fn enumerate_compatible_planes( - &self, - _display: &display::Display, - ) -> Vec { - unimplemented!(); - } - - unsafe fn create_display_mode( - &self, - _display: &display::Display, - _resolution: (u32, u32), - _refresh_rate: u32, - ) -> Result, display::DisplayModeError> { - unimplemented!(); - } - - unsafe fn create_display_plane<'a>( - &self, - _display: &'a display::DisplayMode, - _plane: &'a display::Plane, - ) -> Result, device::OutOfMemory> { - unimplemented!(); - } } /// Dummy command queue doing nothing. @@ -618,98 +563,6 @@ impl device::Device for Device { unimplemented!("{}", NOT_SUPPORTED_MESSAGE) } - unsafe fn create_allocate_external_buffer( - &self, - _external_memory_type: hal::external_memory::ExternalBufferMemoryType, - _usage: hal::buffer::Usage, - _sparse: hal::memory::SparseFlags, - _type_mask: u32, - _size: u64, - ) -> Result< - ( - ::Buffer, - ::Memory, - ), - hal::external_memory::ExternalResourceError, - > { - unimplemented!() - } - - unsafe fn import_external_buffer( - &self, - _external_memory: hal::external_memory::ExternalBufferMemory, - _usage: hal::buffer::Usage, - _sparse: hal::memory::SparseFlags, - _type_mask: u32, - _size: u64, - ) -> Result< - ( - ::Buffer, - ::Memory, - ), - hal::external_memory::ExternalResourceError, - > { - unimplemented!() - } - - unsafe fn create_allocate_external_image( - &self, - _external_memory_type: hal::external_memory::ExternalImageMemoryType, - _kind: hal::image::Kind, - _num_levels: hal::image::Level, - _format: hal::format::Format, - _tiling: hal::image::Tiling, - _usage: hal::image::Usage, - _sparse: hal::memory::SparseFlags, - _view_caps: hal::image::ViewCapabilities, - _type_mask: u32, - ) -> Result< - ( - ::Image, - ::Memory, - ), - hal::external_memory::ExternalResourceError, - > { - unimplemented!() - } - - unsafe fn import_external_image( - &self, - _external_memory: hal::external_memory::ExternalImageMemory, - _kind: hal::image::Kind, - _num_levels: hal::image::Level, - _format: hal::format::Format, - _tiling: hal::image::Tiling, - _usage: hal::image::Usage, - _sparse: hal::memory::SparseFlags, - _view_caps: hal::image::ViewCapabilities, - _type_mask: u32, - ) -> Result< - ( - ::Image, - ::Memory, - ), - hal::external_memory::ExternalResourceError, - > { - unimplemented!() - } - - unsafe fn export_memory( - &self, - _external_memory_type: hal::external_memory::ExternalMemoryType, - _memory: &::Memory, - ) -> Result - { - unimplemented!() - } - - unsafe fn drm_format_modifier( - &self, - _image: &::Image, - ) -> Option { - None - } - unsafe fn reset_fence(&self, _: &mut ()) -> Result<(), device::OutOfMemory> { Ok(()) } @@ -718,31 +571,6 @@ impl device::Device for Device { Ok(true) } - unsafe fn set_display_power_state( - &self, - _display: &display::Display, - _power_state: &display::control::PowerState, - ) -> Result<(), display::control::DisplayControlError> { - unimplemented!("{}", NOT_SUPPORTED_MESSAGE) - } - - unsafe fn register_device_event( - &self, - _device_event: &display::control::DeviceEvent, - _fence: &mut ::Fence, - ) -> Result<(), display::control::DisplayControlError> { - unimplemented!("{}", NOT_SUPPORTED_MESSAGE) - } - - unsafe fn register_display_event( - &self, - _display: &display::Display, - _display_event: &display::control::DisplayEvent, - _fence: &mut ::Fence, - ) -> Result<(), display::control::DisplayControlError> { - unimplemented!("{}", NOT_SUPPORTED_MESSAGE) - } - fn start_capture(&self) { unimplemented!("{}", NOT_SUPPORTED_MESSAGE) } @@ -1253,15 +1081,4 @@ impl hal::Instance for Instance { } unsafe fn destroy_surface(&self, _surface: Surface) {} - - unsafe fn create_display_plane_surface( - &self, - _display_plane: &display::DisplayPlane, - _plane_stack_index: u32, - _transformation: display::SurfaceTransform, - _alpha: display::DisplayPlaneAlpha, - _image_extent: window::Extent2D, - ) -> Result { - unimplemented!(); - } } diff --git a/third_party/rust/gfx-backend-metal/.cargo-checksum.json b/third_party/rust/gfx-backend-metal/.cargo-checksum.json index e1e63dc376b7..712de547e5e9 100644 --- a/third_party/rust/gfx-backend-metal/.cargo-checksum.json +++ b/third_party/rust/gfx-backend-metal/.cargo-checksum.json @@ -1 +1 @@ -{"files":{"Cargo.toml":"15e46a2bfcf75d8ed3bc5c2d6f52a1bbf3ad7a52dfb9ad57a34c331c62c0a294","README.md":"e08409da89ea3f102953c99fb62661f4e434cdb925e69b8964faf0a93861c338","shaders/blit.metal":"b243873ac0d7ded37b199d17d1a7b53d5332b4a57bfa22f99dcf60273730be45","shaders/clear.metal":"796a612c1cb48e46fc94b7227feaab993d7ddeed293b69e9f09b2dd88e6a1189","shaders/fill.metal":"2642b5df62f8eb2246a442137d083010d2a3132110d9be4eb25b479123098d25","shaders/gfx-shaders-ios-simulator.metallib":"561a9b5d91e03e904bae3ffff20f5325cc2ba0653315ae9b8b790ce91597152c","shaders/gfx-shaders-ios.metallib":"e1aad872cf5a5dc1e7dd22cb37124d22a4dac9f9f682b8cce3a490c8ea90589f","shaders/gfx-shaders-macos.metallib":"c07140adb3699dad71b3d7117c3caba7c1823089067391c8b6c9d308ce077785","shaders/macros.h":"a4550ac7c180935c2edb57aa7a5f8442b53f1f3dc65df8cc800d0afb8289cdeb","src/command.rs":"9069aff0012b514ea35ea07f04ad9404868bfd2070e35ea548d0d830a11b739d","src/conversions.rs":"219be5749629a1e992151e32b18de237b0c0a1e0e9b2f07e4c0d2d826364f9f5","src/device.rs":"7b4a330cfd950cb985236a5d97b613eea2a590688657673622b23fa283b8c32a","src/internal.rs":"042a64ac4f47d915d6c8e3c981641bb7bacefe83080f0058353d36ce92f60f42","src/lib.rs":"d93c93ce31dc3fac6e65af2d154d2f4c611cd1321e20e478639037f97c43bba6","src/native.rs":"3d3baf08f072969e2e190d15f25f18bc6ed7af087f62a0b179a544ec78c51a50","src/pipeline_cache.rs":"7acaa09d28307d893f978710c6970f9826bfa51c374a5c2c9d9765f6b1ef4970","src/soft.rs":"efc89c1755cef9cebc74665105286d5f12b8486bdc95c6a6613398b9eee673b8","src/window.rs":"65c0d9c71e4b75f45889342ac288a0723b6fb124065a3400bef983fc949d9e8b"},"package":"0de85808e2a98994c6af925253f8a9593bc57180ef1ea137deab6d35cc949517"} \ No newline at end of file +{"files":{"Cargo.toml":"0e27bceeff35bc3b7228bd7d5eecde048850f45addb0aa40b8153a7273ced04c","README.md":"e08409da89ea3f102953c99fb62661f4e434cdb925e69b8964faf0a93861c338","shaders/blit.metal":"b243873ac0d7ded37b199d17d1a7b53d5332b4a57bfa22f99dcf60273730be45","shaders/clear.metal":"796a612c1cb48e46fc94b7227feaab993d7ddeed293b69e9f09b2dd88e6a1189","shaders/fill.metal":"2642b5df62f8eb2246a442137d083010d2a3132110d9be4eb25b479123098d25","shaders/gfx-shaders-ios-simulator.metallib":"561a9b5d91e03e904bae3ffff20f5325cc2ba0653315ae9b8b790ce91597152c","shaders/gfx-shaders-ios.metallib":"e1aad872cf5a5dc1e7dd22cb37124d22a4dac9f9f682b8cce3a490c8ea90589f","shaders/gfx-shaders-macos.metallib":"c07140adb3699dad71b3d7117c3caba7c1823089067391c8b6c9d308ce077785","shaders/macros.h":"a4550ac7c180935c2edb57aa7a5f8442b53f1f3dc65df8cc800d0afb8289cdeb","src/command.rs":"cfe006a3ccb73de050c2d8a73b3eb1d355ab145b5e7c1d6de2bff9ed6010156a","src/conversions.rs":"2aa8a7237b5fcc035fff4cdc7e9e46e18fdb2c099eedcba3a2a8bcd63d4c6c74","src/device.rs":"07542305370086860e3c34d6995fc45bf2e32c669e2a8b74dd18297eb21e42c5","src/internal.rs":"042a64ac4f47d915d6c8e3c981641bb7bacefe83080f0058353d36ce92f60f42","src/lib.rs":"573f21c68265b95f19059981e9268e226a268d0c0610f30e6ccd6cdb78b0e94e","src/native.rs":"3d3baf08f072969e2e190d15f25f18bc6ed7af087f62a0b179a544ec78c51a50","src/pipeline_cache.rs":"7acaa09d28307d893f978710c6970f9826bfa51c374a5c2c9d9765f6b1ef4970","src/soft.rs":"efc89c1755cef9cebc74665105286d5f12b8486bdc95c6a6613398b9eee673b8","src/window.rs":"7af58a2bab81530ad27f700021c35c754795ae39a4abf5dd797973f4381561f9"},"package":null} \ No newline at end of file diff --git a/third_party/rust/gfx-backend-metal/Cargo.toml b/third_party/rust/gfx-backend-metal/Cargo.toml index 17b711eb0814..a7f8f85fb28d 100644 --- a/third_party/rust/gfx-backend-metal/Cargo.toml +++ b/third_party/rust/gfx-backend-metal/Cargo.toml @@ -1,104 +1,54 @@ -# THIS FILE IS AUTOMATICALLY GENERATED BY CARGO -# -# When uploading crates to the registry Cargo will automatically -# "normalize" Cargo.toml files for maximal compatibility -# with all versions of Cargo and also rewrite `path` dependencies -# to registry (e.g., crates.io) dependencies -# -# If you believe there's an error in this file please file an -# issue against the rust-lang/cargo repository. If you're -# editing this file be aware that the upstream Cargo.toml -# will likely look very different (and much more reasonable) - [package] -edition = "2018" name = "gfx-backend-metal" -version = "0.9.1" -authors = ["The Gfx-rs Developers"] +version = "0.8.1" description = "Metal API backend for gfx-rs" homepage = "https://github.com/gfx-rs/gfx" -documentation = "https://docs.rs/gfx-backend-metal" -readme = "README.md" +repository = "https://github.com/gfx-rs/gfx" keywords = ["graphics", "gamedev"] license = "MIT OR Apache-2.0" -repository = "https://github.com/gfx-rs/gfx" -[package.metadata.docs.rs] -default-target = "x86_64-apple-darwin" +authors = ["The Gfx-rs Developers"] +readme = "README.md" +documentation = "https://docs.rs/gfx-backend-metal" +workspace = "../../.." +edition = "2018" + +[features] +#TODO: add a feature to enable `profiling`, so that we can CI test it +default = [] +signpost = [] +cross = ["spirv_cross", "auxil", "naga/spv-out"] +pipeline-cache = ["tempfile", "serde", "bincode", "naga/serialize", "naga/deserialize", "naga/spv-out"] [lib] name = "gfx_backend_metal" -[dependencies.arrayvec] -version = "0.5" + +[dependencies] +hal = { package = "gfx-hal", path = "../../hal", version = "0.8" } +range-alloc = { path = "../../auxil/range-alloc", version = "0.1" } +arrayvec = "0.5" +bitflags = "1.0" +copyless = "0.1.4" +fxhash = "0.2.1" +log = "0.4" +dispatch = { version = "0.2", optional = true } +metal = { version = "0.22", features = ["private"] } +foreign-types = "0.3" +objc = "0.2.5" +block = "0.1" +cocoa-foundation = "0.1" +parking_lot = "0.11" +storage-map = "0.3" +raw-window-handle = "0.3" +profiling = { version = "1", default-features = false } +tempfile = { version = "3.2", optional = true } +serde = { version = "1", features = ["serde_derive"], optional = true } +bincode = { version = "1", optional = true } [dependencies.auxil] -version = "0.10" -features = ["spirv_cross"] -optional = true package = "gfx-auxil" - -[dependencies.bincode] -version = "1" -optional = true - -[dependencies.bitflags] -version = "1.0" - -[dependencies.block] -version = "0.1" - -[dependencies.cocoa-foundation] -version = "0.1" - -[dependencies.copyless] -version = "0.1.4" - -[dependencies.core-graphics-types] -version = "0.1.1" - -[dependencies.dispatch] -version = "0.2" -optional = true - -[dependencies.foreign-types] -version = "0.3" - -[dependencies.fxhash] -version = "0.2.1" - -[dependencies.hal] +path = "../../auxil/auxil" version = "0.9" -package = "gfx-hal" - -[dependencies.log] -version = "0.4" - -[dependencies.metal] -version = "0.23" -features = ["private"] - -[dependencies.naga] -version = "0.5" -features = ["spv-in", "msl-out"] - -[dependencies.objc] -version = "0.2.5" - -[dependencies.parking_lot] -version = "0.11" - -[dependencies.profiling] -version = "1" -default-features = false - -[dependencies.range-alloc] -version = "0.1" - -[dependencies.raw-window-handle] -version = "0.3" - -[dependencies.serde] -version = "1" -features = ["serde_derive"] +features = ["spirv_cross"] optional = true [dependencies.spirv_cross] @@ -106,15 +56,12 @@ version = "0.23" features = ["msl"] optional = true -[dependencies.storage-map] -version = "0.3" +[dependencies.naga] +git = "https://github.com/gfx-rs/naga" +tag = "gfx-25" +features = ["spv-in", "msl-out"] -[dependencies.tempfile] -version = "3.2" -optional = true - -[features] -cross = ["spirv_cross", "auxil", "naga/spv-out"] -default = [] -pipeline-cache = ["tempfile", "serde", "bincode", "naga/serialize", "naga/deserialize", "naga/spv-out"] -signpost = [] +# This forces docs.rs to build the crate on mac, otherwise the build fails +# and we get no docs at all. +[package.metadata.docs.rs] +default-target = "x86_64-apple-darwin" diff --git a/third_party/rust/gfx-backend-metal/src/command.rs b/third_party/rust/gfx-backend-metal/src/command.rs index 55b4be4edec3..22657803b3c1 100644 --- a/third_party/rust/gfx-backend-metal/src/command.rs +++ b/third_party/rust/gfx-backend-metal/src/command.rs @@ -775,9 +775,7 @@ impl State { result_sizes.clear(); for br in stage_info.sized_bindings.iter() { // If it's None, this isn't the right time to update the sizes - let size = self - .storage_buffer_length_map - .get(&(br.group as pso::DescriptorSetIndex, br.binding))?; + let size = self.storage_buffer_length_map.get(&(br.group as pso::DescriptorSetIndex, br.binding))?; result_sizes.push(*size); } Some(slot as _) diff --git a/third_party/rust/gfx-backend-metal/src/conversions.rs b/third_party/rust/gfx-backend-metal/src/conversions.rs index 1dc238cea2ec..0247cadaf704 100644 --- a/third_party/rust/gfx-backend-metal/src/conversions.rs +++ b/third_party/rust/gfx-backend-metal/src/conversions.rs @@ -195,7 +195,6 @@ impl PrivateCapabilities { }, optimal_tiling: If::empty(), linear_tiling: If::empty(), - drm_format_properties: Vec::new(), } } }; @@ -470,7 +469,6 @@ impl PrivateCapabilities { | If::TRANSFER_DST | extra_optimal, buffer_features: Bf::all(), - drm_format_properties: Vec::new(), } } } diff --git a/third_party/rust/gfx-backend-metal/src/device.rs b/third_party/rust/gfx-backend-metal/src/device.rs index 4d813e5762fb..8d0919d1f7ca 100644 --- a/third_party/rust/gfx-backend-metal/src/device.rs +++ b/third_party/rust/gfx-backend-metal/src/device.rs @@ -11,7 +11,7 @@ use cocoa_foundation::foundation::NSUInteger; use copyless::VecHelper; use foreign_types::{ForeignType, ForeignTypeRef}; use hal::{ - adapter, buffer, device as d, display, format, image, memory, + adapter, buffer, device as d, format, image, memory, memory::Properties, pass, pool::CommandPoolCreateFlags, @@ -29,7 +29,7 @@ use objc::{ rc::autoreleasepool, runtime::{Object, BOOL, NO}, }; -use parking_lot::{Mutex, MutexGuard}; +use parking_lot::Mutex; use std::collections::BTreeMap; #[cfg(feature = "pipeline-cache")] @@ -308,7 +308,6 @@ impl adapter::PhysicalDevice for PhysicalDevice { linear_tiling: format::ImageFeature::empty(), optimal_tiling: format::ImageFeature::empty(), buffer_features: format::BufferFeature::empty(), - drm_format_properties: Vec::new(), }, } } @@ -392,30 +391,6 @@ impl adapter::PhysicalDevice for PhysicalDevice { } } - fn external_buffer_properties( - &self, - _usage: hal::buffer::Usage, - _sparse: hal::memory::SparseFlags, - _memory_type: hal::external_memory::ExternalMemoryType, - ) -> hal::external_memory::ExternalMemoryProperties { - unimplemented!() - } - - fn external_image_properties( - &self, - _format: hal::format::Format, - _dimensions: u8, - _tiling: hal::image::Tiling, - _usage: hal::image::Usage, - _view_caps: hal::image::ViewCapabilities, - _memory_type: hal::external_memory::ExternalMemoryType, - ) -> Result< - hal::external_memory::ExternalMemoryProperties, - hal::external_memory::ExternalImagePropertiesError, - > { - unimplemented!() - } - fn features(&self) -> hal::Features { use hal::Features as F; let mut features = F::FULL_DRAW_INDEX_U32 @@ -573,34 +548,6 @@ impl adapter::PhysicalDevice for PhysicalDevice { ..hal::PhysicalDeviceProperties::default() } } - - unsafe fn enumerate_displays(&self) -> Vec> { - unimplemented!(); - } - - unsafe fn enumerate_compatible_planes( - &self, - _display: &hal::display::Display, - ) -> Vec { - unimplemented!(); - } - - unsafe fn create_display_mode( - &self, - _display: &hal::display::Display, - _resolution: (u32, u32), - _refresh_rate: u32, - ) -> Result, hal::display::DisplayModeError> { - unimplemented!(); - } - - unsafe fn create_display_plane<'a>( - &self, - _display: &'a hal::display::DisplayMode, - _plane: &'a hal::display::Plane, - ) -> Result, d::OutOfMemory> { - unimplemented!(); - } } pub struct LanguageVersion { @@ -615,11 +562,6 @@ impl LanguageVersion { } impl Device { - /// Provides access to the underlying Metal device. - pub fn lock(&self) -> MutexGuard<'_, metal::Device> { - self.shared.device.lock() - } - fn _is_heap_coherent(&self, heap: &n::MemoryHeap) -> bool { match *heap { n::MemoryHeap::Private => false, @@ -3554,97 +3496,6 @@ impl hal::device::Device for Device { // TODO } - unsafe fn set_display_power_state( - &self, - _display: &display::Display, - _power_state: &display::control::PowerState, - ) -> Result<(), display::control::DisplayControlError> { - unimplemented!() - } - - unsafe fn register_device_event( - &self, - _device_event: &display::control::DeviceEvent, - _fence: &mut ::Fence, - ) -> Result<(), display::control::DisplayControlError> { - unimplemented!() - } - - unsafe fn register_display_event( - &self, - _display: &display::Display, - _display_event: &display::control::DisplayEvent, - _fence: &mut ::Fence, - ) -> Result<(), display::control::DisplayControlError> { - unimplemented!() - } - - unsafe fn create_allocate_external_buffer( - &self, - _external_memory_type: hal::external_memory::ExternalBufferMemoryType, - _usage: hal::buffer::Usage, - _sparse: hal::memory::SparseFlags, - _type_mask: u32, - _size: u64, - ) -> Result<(n::Buffer, n::Memory), hal::external_memory::ExternalResourceError> - { - unimplemented!() - } - - unsafe fn import_external_buffer( - &self, - _external_memory: hal::external_memory::ExternalBufferMemory, - _usage: hal::buffer::Usage, - _sparse: hal::memory::SparseFlags, - _mem_types: u32, - _size: u64, - ) -> Result<(n::Buffer, n::Memory), hal::external_memory::ExternalResourceError> { - unimplemented!() - } - - unsafe fn create_allocate_external_image( - &self, - _external_memory_type: hal::external_memory::ExternalImageMemoryType, - _kind: image::Kind, - _num_levels: image::Level, - _format: hal::format::Format, - _tiling: image::Tiling, - _usage: image::Usage, - _sparse: memory::SparseFlags, - _view_caps: image::ViewCapabilities, - _type_mask: u32, - ) -> Result<(n::Image, n::Memory), hal::external_memory::ExternalResourceError> { - unimplemented!() - } - - unsafe fn import_external_image( - &self, - _external_memory: hal::external_memory::ExternalImageMemory, - _kind: image::Kind, - _num_levels: image::Level, - _format: hal::format::Format, - _tiling: image::Tiling, - _usage: image::Usage, - _sparse: memory::SparseFlags, - _view_caps: image::ViewCapabilities, - _type_mask: u32, - ) -> Result<(n::Image, n::Memory), hal::external_memory::ExternalResourceError> { - unimplemented!() - } - - unsafe fn export_memory( - &self, - _external_memory_type: hal::external_memory::ExternalMemoryType, - _memory: &n::Memory, - ) -> Result - { - unimplemented!() - } - - /// Query the underlying drm format modifier from an image. - unsafe fn drm_format_modifier(&self, _image: &n::Image) -> Option{None} - - fn start_capture(&self) { let device = self.shared.device.lock(); let shared_capture_manager = CaptureManager::shared(); diff --git a/third_party/rust/gfx-backend-metal/src/lib.rs b/third_party/rust/gfx-backend-metal/src/lib.rs index 9366a629604e..83af78342b4d 100644 --- a/third_party/rust/gfx-backend-metal/src/lib.rs +++ b/third_party/rust/gfx-backend-metal/src/lib.rs @@ -71,9 +71,7 @@ use foreign_types::ForeignTypeRef; use metal::MTLFeatureSet; use metal::MTLGPUFamily; use metal::MTLLanguageVersion; -use metal::{MetalLayer, MetalLayerRef}; -use core_graphics_types::base::CGFloat; -use core_graphics_types::geometry::CGSize; +use metal::{CGFloat, CGSize, MetalLayer, MetalLayerRef}; use objc::{ declare::ClassDecl, runtime::{Class, Object, Sel, BOOL, YES}, @@ -319,17 +317,6 @@ impl hal::Instance for Instance { unsafe fn destroy_surface(&self, surface: Surface) { surface.dispose(); } - - unsafe fn create_display_plane_surface( - &self, - _display_plane: &hal::display::DisplayPlane, - _plane_stack_index: u32, - _transformation: hal::display::SurfaceTransform, - _alpha: hal::display::DisplayPlaneAlpha, - _image_extent: hal::window::Extent2D, - ) -> Result { - unimplemented!(); - } } extern "C" fn layer_should_inherit_contents_scale_from_window( @@ -506,9 +493,6 @@ impl hal::Backend for Backend { type Semaphore = native::Semaphore; type Event = native::Event; type QueryPool = native::QueryPool; - - type Display = (); - type DisplayMode = (); } const RESOURCE_HEAP_SUPPORT: &[MTLFeatureSet] = &[ diff --git a/third_party/rust/gfx-backend-metal/src/window.rs b/third_party/rust/gfx-backend-metal/src/window.rs index 2517992fa844..c3462ab6e883 100644 --- a/third_party/rust/gfx-backend-metal/src/window.rs +++ b/third_party/rust/gfx-backend-metal/src/window.rs @@ -72,7 +72,7 @@ impl Surface { }; let can_set_display_sync = is_mac && caps.has_version_at_least(10, 13); let drawable_size = - core_graphics_types::geometry::CGSize::new(config.extent.width as f64, config.extent.height as f64); + metal::CGSize::new(config.extent.width as f64, config.extent.height as f64); match config.composite_alpha_mode { w::CompositeAlphaMode::OPAQUE => render_layer.set_opaque(true), @@ -115,7 +115,7 @@ impl Surface { } fn dimensions(&self) -> w::Extent2D { - let (size, scale): (core_graphics_types::geometry::CGSize, core_graphics_types::base::CGFloat) = match self.view { + let (size, scale): (metal::CGSize, metal::CGFloat) = match self.view { Some(view) if !cfg!(target_os = "macos") => unsafe { let bounds: CGRect = msg_send![view.as_ptr(), bounds]; let window: Option> = msg_send![view.as_ptr(), window]; @@ -126,7 +126,7 @@ impl Surface { Some(screen) => { let screen_space: *mut Object = msg_send![screen.as_ptr(), coordinateSpace]; let rect: CGRect = msg_send![view.as_ptr(), convertRect:bounds toCoordinateSpace:screen_space]; - let scale_factor: core_graphics_types::base::CGFloat = msg_send![screen.as_ptr(), nativeScale]; + let scale_factor: metal::CGFloat = msg_send![screen.as_ptr(), nativeScale]; (rect.size, scale_factor) } None => (bounds.size, 1.0), @@ -136,7 +136,7 @@ impl Surface { let render_layer_borrow = self.render_layer.lock(); let render_layer = render_layer_borrow.as_ref(); let bounds: CGRect = msg_send![render_layer, bounds]; - let contents_scale: core_graphics_types::base::CGFloat = msg_send![render_layer, contentsScale]; + let contents_scale: metal::CGFloat = msg_send![render_layer, contentsScale]; (bounds.size, contents_scale) }, }; @@ -244,6 +244,12 @@ impl w::PresentationSurface for Surface { if !image::Usage::COLOR_ATTACHMENT.contains(config.image_usage) { warn!("Swapchain usage {:?} is not expected", config.image_usage); } + #[cfg(target_os = "macos")] + { + if self.view.is_some() && self.main_thread_id != thread::current().id() { + return Err(w::SwapchainError::WrongThread); + } + } self.swapchain_format = self.configure(&device.shared, &config); Ok(()) } diff --git a/third_party/rust/gfx-backend-vulkan/.cargo-checksum.json b/third_party/rust/gfx-backend-vulkan/.cargo-checksum.json index 3382248c528b..a5350d44acbc 100644 --- a/third_party/rust/gfx-backend-vulkan/.cargo-checksum.json +++ b/third_party/rust/gfx-backend-vulkan/.cargo-checksum.json @@ -1 +1 @@ -{"files":{"Cargo.toml":"e9e7e7ce578ce161697661ad35561180fae31a185f0535bdf83e6d0625a6783a","README.md":"18c8b36bca25cef6642315221fe0713e23054430377cd465fdb2c4fe7f332daf","src/command.rs":"5834a642982bccc7b524702beda8ecbd69b2831f7daa0516387e77fed21fc8a0","src/conv.rs":"04ce2a815fd65b53b52b3f54fa243f4b27a0e9b3ab8809d9b11615b190b198fc","src/device.rs":"d0b82b030782cba3730a6ee1ee56b510fda84d48c234334a7e0e2485ad4a4bf0","src/info.rs":"4a21b54f85ff73c538ca2f57f4d371eb862b5a28f126cd0ecafd37fc6dfd1318","src/lib.rs":"86e59ad1ff9c74445da1bffe1a4dbda723cab49b45befa6fe99e74f7dd77403f","src/native.rs":"23178e5eaac4d86add64f9c44870abdeb0bac8bb7ce7b924b93ff314fc97597d","src/physical_device.rs":"8b02edf8ba2472f082b94b40b1f6790f4f0957e3efce36e14aaadd3dd8bc119d","src/pool.rs":"16d174bb274c2dd2f4b94379835269825695dca581436a57b8942b27811255fc","src/window.rs":"dca36024d0e6b7d352f1c6a0f4c1a490488ffc1838a73a6d54a27587ece1e61e"},"package":"a9861ec855acbbc65c0e4f966d761224886e811dc2c6d413a4776e9293d0e5c0"} \ No newline at end of file +{"files":{"Cargo.toml":"a4dab5b9bb789af14347fb1ebb810b761d8d701f4c07aa020858990cded61af3","README.md":"18c8b36bca25cef6642315221fe0713e23054430377cd465fdb2c4fe7f332daf","src/command.rs":"5834a642982bccc7b524702beda8ecbd69b2831f7daa0516387e77fed21fc8a0","src/conv.rs":"04ce2a815fd65b53b52b3f54fa243f4b27a0e9b3ab8809d9b11615b190b198fc","src/device.rs":"19dee8dea68199fc45f43bfe7674dd766273a7ba7ff902a84583dab1cb973aae","src/info.rs":"4a21b54f85ff73c538ca2f57f4d371eb862b5a28f126cd0ecafd37fc6dfd1318","src/lib.rs":"47d651be740415ab3859bdae82d91a6d99fc962445f162b353c41045f372f0d5","src/native.rs":"72099d0499e72625496bea64d968f650a6d43fe24840ace9b0c419bd60e3a3c6","src/physical_device.rs":"5e62550be60474b44c6235f0b6304fb8fa4f9f36b59106291da35abc4821f622","src/pool.rs":"16d174bb274c2dd2f4b94379835269825695dca581436a57b8942b27811255fc","src/window.rs":"dca36024d0e6b7d352f1c6a0f4c1a490488ffc1838a73a6d54a27587ece1e61e"},"package":null} \ No newline at end of file diff --git a/third_party/rust/gfx-backend-vulkan/Cargo.toml b/third_party/rust/gfx-backend-vulkan/Cargo.toml index 722685048ec0..bb20f3189980 100644 --- a/third_party/rust/gfx-backend-vulkan/Cargo.toml +++ b/third_party/rust/gfx-backend-vulkan/Cargo.toml @@ -1,78 +1,47 @@ -# THIS FILE IS AUTOMATICALLY GENERATED BY CARGO -# -# When uploading crates to the registry Cargo will automatically -# "normalize" Cargo.toml files for maximal compatibility -# with all versions of Cargo and also rewrite `path` dependencies -# to registry (e.g., crates.io) dependencies -# -# If you believe there's an error in this file please file an -# issue against the rust-lang/cargo repository. If you're -# editing this file be aware that the upstream Cargo.toml -# will likely look very different (and much more reasonable) - [package] -edition = "2018" name = "gfx-backend-vulkan" -version = "0.9.0" -authors = ["The Gfx-rs Developers"] +version = "0.8.0" description = "Vulkan API backend for gfx-rs" homepage = "https://github.com/gfx-rs/gfx" -documentation = "https://docs.rs/gfx-backend-vulkan" -readme = "README.md" +repository = "https://github.com/gfx-rs/gfx" keywords = ["graphics", "gamedev"] license = "MIT OR Apache-2.0" -repository = "https://github.com/gfx-rs/gfx" - -[lib] -name = "gfx_backend_vulkan" -[dependencies.arrayvec] -version = "0.5" - -[dependencies.ash] -version = "0.32" - -[dependencies.byteorder] -version = "1" - -[dependencies.gfx-renderdoc] -version = "0.1.0" - -[dependencies.hal] -version = "0.9" -package = "gfx-hal" - -[dependencies.inplace_it] -version = "0.3.3" - -[dependencies.libc] -version = "0.2" -optional = true - -[dependencies.log] -version = "0.4" - -[dependencies.naga] -version = "0.5" -features = ["spv-out"] -optional = true - -[dependencies.parking_lot] -version = "0.11" - -[dependencies.raw-window-handle] -version = "0.3" - -[dependencies.smallvec] -version = "1.0" +authors = ["The Gfx-rs Developers"] +readme = "README.md" +documentation = "https://docs.rs/gfx-backend-vulkan" +workspace = "../../.." +edition = "2018" [features] default = [] use-rtld-next = ["libc"] -[target."cfg(target_os = \"macos\")".dependencies.core-graphics-types] -version = "0.1" -[target."cfg(target_os = \"macos\")".dependencies.objc] -version = "0.2.5" -[target."cfg(windows)".dependencies.winapi] -version = "0.3" -features = ["libloaderapi", "windef", "winuser"] +[lib] +name = "gfx_backend_vulkan" + +[dependencies] +arrayvec = "0.5" +byteorder = "1" +log = "0.4" +libc = { version = "0.2", optional = true } +ash = "0.32" +hal = { path = "../../hal", version = "0.8", package = "gfx-hal" } +parking_lot = "0.11" +smallvec = "1.0" +raw-window-handle = "0.3" +inplace_it = "0.3.3" +renderdoc-sys = "0.7.1" +libloading = "0.7" + +[dependencies.naga] +git = "https://github.com/gfx-rs/naga" +tag = "gfx-25" +features = ["spv-out"] +optional = true + +[target.'cfg(windows)'.dependencies] +winapi = { version = "0.3", features = ["libloaderapi", "windef", "winuser"] } +[target.'cfg(target_os = "macos")'.dependencies] +objc = "0.2.5" +core-graphics-types = "0.1" + diff --git a/third_party/rust/gfx-backend-vulkan/src/device.rs b/third_party/rust/gfx-backend-vulkan/src/device.rs index e01282685971..24d8df8e28e2 100644 --- a/third_party/rust/gfx-backend-vulkan/src/device.rs +++ b/third_party/rust/gfx-backend-vulkan/src/device.rs @@ -1216,12 +1216,37 @@ impl d::Device for super::Device { let is_cube = image .flags .intersects(vk::ImageCreateFlags::CUBE_COMPATIBLE); - let view_type = match conv::map_view_kind(kind, image.ty, is_cube) { - Some(ty) => ty, - None => return Err(image::ViewCreationError::BadKind(kind)), - }; + let mut image_view_info; + let mut info = vk::ImageViewCreateInfo::builder() + .flags(vk::ImageViewCreateFlags::empty()) + .image(image.raw) + .view_type(match conv::map_view_kind(kind, image.ty, is_cube) { + Some(ty) => ty, + None => return Err(image::ViewCreationError::BadKind(kind)), + }) + .format(conv::map_format(format)) + .components(conv::map_swizzle(swizzle)) + .subresource_range(conv::map_subresource_range(&range)); - self.image_view_from_raw(image.raw, view_type, format, swizzle, usage, range) + if self.shared.image_view_usage { + image_view_info = vk::ImageViewUsageCreateInfo::builder() + .usage(conv::map_image_usage(usage)) + .build(); + info = info.push_next(&mut image_view_info); + } + + let result = self.shared.raw.create_image_view(&info, None); + + match result { + Ok(raw) => Ok(n::ImageView { + image: image.raw, + raw, + range, + }), + Err(vk::Result::ERROR_OUT_OF_HOST_MEMORY) => Err(d::OutOfMemory::Host.into()), + Err(vk::Result::ERROR_OUT_OF_DEVICE_MEMORY) => Err(d::OutOfMemory::Device.into()), + _ => unreachable!(), + } } unsafe fn create_descriptor_pool( @@ -1866,1179 +1891,31 @@ impl d::Device for super::Device { .set_object_name(vk::ObjectType::PIPELINE_LAYOUT, pipeline_layout.raw, name) } - unsafe fn set_display_power_state( - &self, - display: &hal::display::Display, - power_state: &hal::display::control::PowerState, - ) -> Result<(), hal::display::control::DisplayControlError> { - let display_control_extension = match self.shared.extension_fns.display_control { - Some(ref display_control_extension) => display_control_extension, - _ => return Err(hal::display::control::DisplayControlError::UnsupportedFeature), - }; - - let vk_power_state = match power_state { - hal::display::control::PowerState::Off => vk::DisplayPowerStateEXT::OFF, - hal::display::control::PowerState::Suspend => vk::DisplayPowerStateEXT::SUSPEND, - hal::display::control::PowerState::On => vk::DisplayPowerStateEXT::ON, - }; - - let vk_power_info = vk::DisplayPowerInfoEXT::builder() - .power_state(vk_power_state) - .build(); - - match display_control_extension.display_power_control_ext( - self.shared.raw.handle(), - display.handle.0, - &vk_power_info, - ) { - vk::Result::SUCCESS => Ok(()), - vk::Result::ERROR_OUT_OF_HOST_MEMORY => { - Err(hal::display::control::DisplayControlError::OutOfHostMemory) - } - _ => unreachable!(), - } - } - - unsafe fn register_device_event( - &self, - device_event: &hal::display::control::DeviceEvent, - fence: &mut ::Fence, - ) -> Result<(), hal::display::control::DisplayControlError> { - let display_control_extension = match self.shared.extension_fns.display_control { - Some(ref display_control_extension) => display_control_extension, - _ => return Err(hal::display::control::DisplayControlError::UnsupportedFeature), - }; - - let vk_device_event = match device_event { - hal::display::control::DeviceEvent::DisplayHotplug => { - vk::DeviceEventTypeEXT::DISPLAY_HOTPLUG - } - }; - - let vk_device_event_info = vk::DeviceEventInfoEXT::builder() - .device_event(vk_device_event) - .build(); - - match display_control_extension.register_device_event_ext( - self.shared.raw.handle(), - &vk_device_event_info, - std::ptr::null(), - &mut fence.0, - ) { - vk::Result::SUCCESS => Ok(()), - vk::Result::ERROR_OUT_OF_HOST_MEMORY => { - Err(hal::display::control::DisplayControlError::OutOfHostMemory) - } - vk::Result::ERROR_FEATURE_NOT_PRESENT => { - Err(hal::display::control::DisplayControlError::UnsupportedFeature) - } // This is a non-standard error returned on Linux Intel Haswell - err => { - error!("Unexpected error: {:#?}", err); - Err(hal::display::control::DisplayControlError::UnsupportedFeature) - } - } - } - - unsafe fn register_display_event( - &self, - display: &hal::display::Display, - display_event: &hal::display::control::DisplayEvent, - fence: &mut ::Fence, - ) -> Result<(), hal::display::control::DisplayControlError> { - let display_control_extension = match self.shared.extension_fns.display_control { - Some(ref display_control_extension) => display_control_extension, - _ => return Err(hal::display::control::DisplayControlError::UnsupportedFeature), - }; - - let vk_display_event = match display_event { - hal::display::control::DisplayEvent::FirstPixelOut => { - vk::DisplayEventTypeEXT::FIRST_PIXEL_OUT - } - }; - - let vk_display_event_info = vk::DisplayEventInfoEXT::builder() - .display_event(vk_display_event) - .build(); - - match display_control_extension.register_display_event_ext( - self.shared.raw.handle(), - display.handle.0, - &vk_display_event_info, - std::ptr::null(), - &mut fence.0, - ) { - vk::Result::SUCCESS => Ok(()), - vk::Result::ERROR_OUT_OF_HOST_MEMORY => { - Err(hal::display::control::DisplayControlError::OutOfHostMemory) - } - _ => unreachable!(), - } - } - - unsafe fn create_allocate_external_buffer( - &self, - external_memory_type: hal::external_memory::ExternalBufferMemoryType, - usage: hal::buffer::Usage, - sparse: hal::memory::SparseFlags, - type_mask: u32, - size: u64, - ) -> Result<(n::Buffer, n::Memory), hal::external_memory::ExternalResourceError> - { - if self.shared.extension_fns.external_memory.is_none() { - panic!( - "This function rely on `Feature::EXTERNAL_MEMORY`, but the feature is not enabled" - ); - } - - let external_memory_type_flags: hal::external_memory::ExternalMemoryTypeFlags = - external_memory_type.into(); - let vk_external_memory_type = - vk::ExternalMemoryHandleTypeFlags::from_raw(external_memory_type_flags.bits()); - - let mut external_buffer_ci = vk::ExternalMemoryBufferCreateInfo::builder() - .handle_types(vk_external_memory_type) - .build(); - - let info = vk::BufferCreateInfo::builder() - .push_next(&mut external_buffer_ci) - .flags(conv::map_buffer_create_flags(sparse)) - .size(size) - .usage(conv::map_buffer_usage(usage)) - .sharing_mode(vk::SharingMode::EXCLUSIVE); // TODO: - - let mut buffer = match self.shared.raw.create_buffer(&info, None) { - Ok(raw) => n::Buffer { raw }, - Err(vk::Result::ERROR_OUT_OF_HOST_MEMORY) => return Err(d::OutOfMemory::Host.into()), - Err(vk::Result::ERROR_OUT_OF_DEVICE_MEMORY) => { - return Err(d::OutOfMemory::Device.into()) - } - Err(vk::Result::ERROR_INVALID_EXTERNAL_HANDLE_KHR) => { - return Err( - hal::external_memory::ExternalResourceError::InvalidExternalHandle, - ) - } - _ => unreachable!(), - }; - - let buffer_req = self.get_buffer_requirements(&buffer); - - let mem_type = match (0..32) - .into_iter() - .find(|id| buffer_req.type_mask & type_mask & (1 << id) != 0) - { - Some(id) => id.into(), - None => return Err(hal::external_memory::ExternalResourceError::NoValidMemoryTypeId), - }; - - let mut export_memori_ai = vk::ExportMemoryAllocateInfo::builder() - .handle_types(vk_external_memory_type) - .build(); - - let mut dedicated_allocation_info = - if self.shared.extension_fns.dedicated_allocation.is_some() { - let dedicated_allocation_info = vk::MemoryDedicatedAllocateInfo::builder() - .buffer(buffer.raw) - .build(); - Some(dedicated_allocation_info) - } else { - None - }; - - let allocate_info = if let Some(dedicated_allocation_info) = &mut dedicated_allocation_info - { - vk::MemoryAllocateInfo::builder().push_next(dedicated_allocation_info) - } else { - vk::MemoryAllocateInfo::builder() - } - .push_next(&mut export_memori_ai) - .allocation_size(buffer_req.size) - .memory_type_index(self.get_ash_memory_type_index(mem_type)); - - let result = self.shared.raw.allocate_memory(&allocate_info, None); - - if result.is_err() { - self.destroy_buffer(buffer); - } - - let memory = match result { - Ok(memory) => n::Memory { raw: memory }, - Err(vk::Result::ERROR_TOO_MANY_OBJECTS) => { - return Err(hal::external_memory::ExternalResourceError::TooManyObjects) - } - Err(vk::Result::ERROR_OUT_OF_HOST_MEMORY) => return Err(d::OutOfMemory::Host.into()), - Err(vk::Result::ERROR_OUT_OF_DEVICE_MEMORY) => { - return Err(d::OutOfMemory::Device.into()) - } - _ => unreachable!(), - }; - - if let Err(err) = self.bind_buffer_memory(&memory, 0, &mut buffer) { - error!("Failed to `bind_buffer_memory`: {:#?}", err); - return Err(match err { - d::BindError::OutOfMemory(out_of_memory) => out_of_memory.into(), - d::BindError::WrongMemory => { - panic!("This error should never happen and it is likely a vulkan backend bug.") - } - d::BindError::OutOfBounds => { - panic!("Since external memory use a dedicated allocation, this should never happen and it is likely a vulkan backend bug.") - } - }); - } - - Ok((buffer, memory)) - } - - unsafe fn import_external_buffer( - &self, - external_memory: hal::external_memory::ExternalBufferMemory, - usage: hal::buffer::Usage, - sparse: hal::memory::SparseFlags, - type_mask: u32, - size: u64, - ) -> Result<(n::Buffer, n::Memory), hal::external_memory::ExternalResourceError> { - if self.shared.extension_fns.external_memory.is_none() { - panic!( - "This function rely on `Feature::EXTERNAL_MEMORY`, but the feature is not enabled" - ); - } - - let external_memory_type = external_memory.external_memory_type(); - let external_memory_type_flags: hal::external_memory::ExternalMemoryTypeFlags = - external_memory_type.into(); - let vk_external_memory_type = - vk::ExternalMemoryHandleTypeFlags::from_raw(external_memory_type_flags.bits()); - - let mut external_buffer_ci = vk::ExternalMemoryBufferCreateInfo::builder() - .handle_types(vk_external_memory_type) - .build(); - - let info = vk::BufferCreateInfo::builder() - .push_next(&mut external_buffer_ci) - .flags(conv::map_buffer_create_flags(sparse)) - .size(size) - .usage(conv::map_buffer_usage(usage)) - .sharing_mode(vk::SharingMode::EXCLUSIVE); // TODO: - - let mut buffer = match self.shared.raw.create_buffer(&info, None) { - Ok(raw) => n::Buffer { raw }, - Err(vk::Result::ERROR_OUT_OF_HOST_MEMORY) => return Err(d::OutOfMemory::Host.into()), - Err(vk::Result::ERROR_OUT_OF_DEVICE_MEMORY) => { - return Err(d::OutOfMemory::Device.into()) - } - Err(vk::Result::ERROR_INVALID_EXTERNAL_HANDLE_KHR) => { - return Err(hal::external_memory::ExternalResourceError::InvalidExternalHandle) - } - _ => unreachable!(), - }; - - let buffer_req = self.get_buffer_requirements(&buffer); - - let mut dedicated_allocation_info = - if self.shared.extension_fns.dedicated_allocation.is_some() { - let dedicated_allocation_info = vk::MemoryDedicatedAllocateInfo::builder() - .buffer(buffer.raw) - .build(); - Some(dedicated_allocation_info) - } else { - None - }; - - let result = match external_memory.platform_memory_type() { - #[cfg(unix)] - hal::external_memory::PlatformMemoryType::Fd => { - let fd = external_memory.fd().unwrap(); - let external_memory_extension = self.shared.extension_fns.external_memory_fd.as_ref().expect("This function rely on `Feature::EXTERNAL_MEMORY`, but the feature is not enabled"); - - #[cfg(any(target_os = "linux", target_os = "android", doc))] - if self.shared.extension_fns.external_memory_dma_buf.is_none() - && external_memory_type_flags - .contains(hal::external_memory::ExternalMemoryTypeFlags::DMA_BUF) - { - panic!("Requested to import a dma buf that is not supported by the system. Use `PhysicalDevice::external_image_properties` to check what is supported on the system."); - } - - let vk_memory_bits = if external_memory_type - == hal::external_memory::ExternalMemoryType::OpaqueFd - { - u32::MAX - } else { - use std::os::unix::io::AsRawFd; - match external_memory_extension - .get_memory_fd_properties_khr(vk_external_memory_type, fd.as_raw_fd()) - { - Ok(memory_handle_properties) => memory_handle_properties.memory_type_bits, - Err(vk::Result::ERROR_OUT_OF_HOST_MEMORY) => { - return Err(d::OutOfMemory::Host.into()) - } - Err(vk::Result::ERROR_INVALID_EXTERNAL_HANDLE_KHR) => { - error!("Failed to get memory fd properties"); - return Err( - hal::external_memory::ExternalResourceError::InvalidExternalHandle, - ); - } - err => { - panic!("Unexpected error: {:#?}", err); - } - } - }; - - let mem_type = match (0..32) - .into_iter() - .find(|id| buffer_req.type_mask & type_mask & (1 << id) & vk_memory_bits != 0) - { - Some(id) => id.into(), - None => return Err(hal::external_memory::ExternalResourceError::NoValidMemoryTypeId), - }; - - let mut import_memory_info = vk::ImportMemoryFdInfoKHR::builder() - .handle_type(vk_external_memory_type) - .fd(**fd) - .build(); - - let allocate_info = - if let Some(dedicated_allocation_info) = &mut dedicated_allocation_info { - vk::MemoryAllocateInfo::builder().push_next(dedicated_allocation_info) - } else { - vk::MemoryAllocateInfo::builder() - } - .push_next(&mut import_memory_info) - .allocation_size(buffer_req.size) - .memory_type_index(self.get_ash_memory_type_index(mem_type)); - - self.shared.raw.allocate_memory(&allocate_info, None) - } - #[cfg(windows)] - hal::external_memory::PlatformMemoryType::Handle => { - let handle = external_memory.handle().unwrap(); - let external_memory_extension = self.shared.extension_fns.external_memory_win32.as_ref().expect("This function rely on `Feature::EXTERNAL_MEMORY`, but the feature is not enabled"); - - let vk_memory_bits = { - use std::os::windows::io::AsRawHandle; - let mut memory_handle_properties = - vk::MemoryWin32HandlePropertiesKHR::builder().build(); - match external_memory_extension.get_memory_win32_handle_properties_khr( - self.shared.raw.handle(), - vk_external_memory_type, - handle.as_raw_handle(), - &mut memory_handle_properties, - ) { - vk::Result::SUCCESS => (), - vk::Result::ERROR_OUT_OF_HOST_MEMORY => { - return Err(d::OutOfMemory::Host.into()) - } - vk::Result::ERROR_INVALID_EXTERNAL_HANDLE_KHR => return Err( - hal::external_memory::ExternalResourceError::InvalidExternalHandle, - ), - _ => unreachable!(), - }; - memory_handle_properties.memory_type_bits - }; - - let mem_type = match (0..32) - .into_iter() - .find(|id| buffer_req.type_mask & type_mask & (1 << id) & vk_memory_bits != 0) - { - Some(id) => id.into(), - None => return Err(hal::external_memory::ExternalResourceError::NoValidMemoryTypeId), - }; - - let mut import_memory_info = vk::ImportMemoryWin32HandleInfoKHR::builder() - .handle_type(vk_external_memory_type) - .handle(**handle) - .build(); - - let allocate_info = - if let Some(dedicated_allocation_info) = &mut dedicated_allocation_info { - vk::MemoryAllocateInfo::builder().push_next(dedicated_allocation_info) - } else { - vk::MemoryAllocateInfo::builder() - } - .push_next(&mut import_memory_info) - .allocation_size(buffer_req.size) - .memory_type_index(self.get_ash_memory_type_index(mem_type)); - - self.shared.raw.allocate_memory(&allocate_info, None) - } - hal::external_memory::PlatformMemoryType::Ptr => { - let ptr = external_memory.ptr().unwrap(); - let external_memory_extension = self.shared.extension_fns.external_memory_host.as_ref().expect("This function rely on `Feature::EXTERNAL_MEMORY`, but the feature is not enabled"); - - let vk_memory_bits = { - let mut memory_ptr_properties = - vk::MemoryHostPointerPropertiesEXT::builder().build(); - match external_memory_extension.get_memory_host_pointer_properties_ext( - self.shared.raw.handle(), - vk_external_memory_type, - ptr.as_raw_ptr(), - &mut memory_ptr_properties, - ) { - vk::Result::SUCCESS => (), - vk::Result::ERROR_OUT_OF_HOST_MEMORY => { - return Err(d::OutOfMemory::Host.into()) - } - vk::Result::ERROR_INVALID_EXTERNAL_HANDLE_KHR => return Err( - hal::external_memory::ExternalResourceError::InvalidExternalHandle, - ), - err => { - panic!("Unexpected error: {:#?}", err); - } - }; - memory_ptr_properties.memory_type_bits - }; - - let mem_type = match (0..32) - .into_iter() - .find(|id| buffer_req.type_mask & type_mask & (1 << id) & vk_memory_bits != 0) - { - Some(id) => id.into(), - None => return Err(hal::external_memory::ExternalResourceError::NoValidMemoryTypeId), - }; - - let mut import_memory_info = vk::ImportMemoryHostPointerInfoEXT::builder() - .handle_type(vk_external_memory_type) - .host_pointer(**ptr) - .build(); - - let allocate_info = - if let Some(dedicated_allocation_info) = &mut dedicated_allocation_info { - vk::MemoryAllocateInfo::builder().push_next(dedicated_allocation_info) - } else { - vk::MemoryAllocateInfo::builder() - } - .push_next(&mut import_memory_info) - .allocation_size(buffer_req.size) - .memory_type_index(self.get_ash_memory_type_index(mem_type)); - - self.shared.raw.allocate_memory(&allocate_info, None) - } - }; - - let memory = match result { - Ok(memory) => n::Memory { raw: memory }, - Err(err) => { - self.destroy_buffer(buffer); - match err { - vk::Result::ERROR_TOO_MANY_OBJECTS => { - return Err(hal::external_memory::ExternalResourceError::TooManyObjects) - } - vk::Result::ERROR_OUT_OF_HOST_MEMORY => return Err(d::OutOfMemory::Host.into()), - vk::Result::ERROR_OUT_OF_DEVICE_MEMORY => { - return Err(d::OutOfMemory::Device.into()) - } - vk::Result::ERROR_INVALID_EXTERNAL_HANDLE_KHR => { - return Err( - hal::external_memory::ExternalResourceError::InvalidExternalHandle, - ) - } - unexpected_error => { - panic!( - "Unexpected error on `allocate_memory`: {:#?}", - unexpected_error - ); - } - } - } - }; - - if let Err(err) = self.bind_buffer_memory(&memory, 0, &mut buffer) { - error!("Failed to `bind_buffer_memory`: {:#?}", err); - return Err(match err { - d::BindError::OutOfMemory(out_of_memory) => out_of_memory.into(), - d::BindError::WrongMemory => { - panic!("This error should never happen and it is likely a vulkan backend bug.") - } - d::BindError::OutOfBounds => { - panic!("Since external memory use a dedicated allocation, this should never happen and it is likely a vulkan backend bug.") - } - }); - } - - Ok((buffer, memory)) - } - unsafe fn create_allocate_external_image( - &self, - external_memory_type: hal::external_memory::ExternalImageMemoryType, - kind: image::Kind, - mip_levels: image::Level, - format: format::Format, - tiling: image::Tiling, - usage: image::Usage, - sparse: memory::SparseFlags, - view_caps: image::ViewCapabilities, - type_mask: u32, - ) -> Result<(n::Image, n::Memory), hal::external_memory::ExternalResourceError> { - if self.shared.extension_fns.external_memory.is_none() { - panic!( - "This function rely on `Feature::EXTERNAL_MEMORY`, but the feature is not enabled" - ); - } - - let flags = conv::map_view_capabilities_sparse(sparse, view_caps); - let extent = conv::map_extent(kind.extent()); - let array_layers = kind.num_layers(); - let samples = kind.num_samples(); - let image_type = match kind { - image::Kind::D1(..) => vk::ImageType::TYPE_1D, - image::Kind::D2(..) => vk::ImageType::TYPE_2D, - image::Kind::D3(..) => vk::ImageType::TYPE_3D, - }; - - //Note: this is a hack, we should expose this in the API instead - let layout = match tiling { - image::Tiling::Linear => vk::ImageLayout::PREINITIALIZED, - image::Tiling::Optimal => vk::ImageLayout::UNDEFINED, - }; - - let external_memory_type_flags: hal::external_memory::ExternalMemoryTypeFlags = - external_memory_type.external_memory_type().into(); - let vk_external_memory_type = - vk::ExternalMemoryHandleTypeFlags::from_raw(external_memory_type_flags.bits()); - - #[cfg(any(target_os = "linux", target_os = "android"))] - //_drm_modifier_list need to be there for lifetime requirements - let (mut drm_modifiers_info, _drm_modifier_list) = - if let hal::external_memory::ExternalImageMemoryType::DmaBuf(dma_modifiers) = - external_memory_type - { - if dma_modifiers.is_empty() {(None, None)} - else { - let drm_modifier_list: Vec = dma_modifiers - .iter() - .filter_map(|drm_modifier| { - use std::convert::TryInto; - match drm_modifier.clone().try_into() { - Ok(drm_modifier) => Some(drm_modifier), - Err(err) => { - error!("Invalid drm format modifier: {:#?}", err); - None - } - } - }) - .collect(); - let image_format_modifier_list = vk::ImageDrmFormatModifierListCreateInfoEXT::builder() - .drm_format_modifiers(drm_modifier_list.as_slice()) - .build(); - (Some(image_format_modifier_list),Some(drm_modifier_list),) - } - } else { - (None, None) - }; - - let mut external_memory_ci = vk::ExternalMemoryImageCreateInfo::builder() - .handle_types(vk_external_memory_type) - .build(); - - let info = vk::ImageCreateInfo::builder() - .push_next(&mut external_memory_ci) - .flags(flags) - .image_type(image_type) - .format(conv::map_format(format)) - .extent(extent.clone()) - .mip_levels(mip_levels as u32) - .array_layers(array_layers as u32) - .samples(conv::map_sample_count_flags(samples)) - .tiling(conv::map_tiling(tiling)) - .usage(conv::map_image_usage(usage)) - .sharing_mode(vk::SharingMode::EXCLUSIVE) // TODO: - .initial_layout(layout); - - #[cfg(any(target_os = "linux", target_os = "android"))] - let info = if let Some(ref mut drm_modifiers_info) = drm_modifiers_info { - info.push_next(drm_modifiers_info) - } else { - info - }; - - let mut image = match self.shared.raw.create_image(&info, None) { - Ok(raw) => n::Image { - raw, - ty: image_type, - flags, - extent, - }, - Err(vk::Result::ERROR_OUT_OF_HOST_MEMORY) => return Err(d::OutOfMemory::Host.into()), - Err(vk::Result::ERROR_OUT_OF_DEVICE_MEMORY) => { - return Err(d::OutOfMemory::Device.into()) - } - Err(unexpected_error) => { - panic!( - "Unexpected error on `create_image`: {:#?}", - unexpected_error - ); - } - }; - - let image_req = self.get_image_requirements(&image); - - let mem_type = match (0..32) - .into_iter() - .find(|id| image_req.type_mask & type_mask & (1 << id) != 0) - { - Some(id) => id.into(), - None => unreachable!(), - }; - - let mut export_memori_ai = vk::ExportMemoryAllocateInfo::builder() - .handle_types(vk_external_memory_type) - .build(); - - let mut dedicated_allocation_info = - if self.shared.extension_fns.dedicated_allocation.is_some() { - let dedicated_allocation_info = vk::MemoryDedicatedAllocateInfo::builder() - .image(image.raw) - .build(); - Some(dedicated_allocation_info) - } else { - None - }; - - let allocate_info = if let Some(dedicated_allocation_info) = &mut dedicated_allocation_info - { - vk::MemoryAllocateInfo::builder().push_next(dedicated_allocation_info) - } else { - vk::MemoryAllocateInfo::builder() - } - .push_next(&mut export_memori_ai) - .allocation_size(image_req.size) - .memory_type_index(self.get_ash_memory_type_index(mem_type)); - - let memory = match self.shared.raw.allocate_memory(&allocate_info, None) { - Ok(memory) => n::Memory { raw: memory }, - Err(err) => { - self.destroy_image(image); - match err { - vk::Result::ERROR_TOO_MANY_OBJECTS => { - return Err( - hal::external_memory::ExternalResourceError::TooManyObjects, - ); - } - vk::Result::ERROR_OUT_OF_HOST_MEMORY => { - return Err(d::OutOfMemory::Host.into()); - } - vk::Result::ERROR_OUT_OF_DEVICE_MEMORY => { - return Err(d::OutOfMemory::Device.into()); - } - unexpected_error => { - panic!("Unexpected error: {:#?}", unexpected_error); - } - } - } - }; - - if let Err(err) = self.bind_image_memory(&memory, 0, &mut image) { - error!("Failed to `bind_image_memory`: {:#?}", err); - return Err(match err { - d::BindError::OutOfMemory(out_of_memory) => out_of_memory.into(), - d::BindError::WrongMemory => { - panic!("This error should never happen and it is likely a vulkan backend bug.") - } - d::BindError::OutOfBounds => { - panic!("Since external memory use a dedicated allocation, this should never happen and it is likely a vulkan backend bug.") - } - }); - } - - Ok((image, memory)) - } - - unsafe fn import_external_image( - &self, - external_memory: hal::external_memory::ExternalImageMemory, - kind: image::Kind, - mip_levels: image::Level, - format: format::Format, - tiling: image::Tiling, - usage: image::Usage, - sparse: memory::SparseFlags, - view_caps: image::ViewCapabilities, - type_mask: u32, - ) -> Result<(n::Image, n::Memory), hal::external_memory::ExternalResourceError> { - if self.shared.extension_fns.external_memory.is_none() { - panic!( - "This function rely on `Feature::EXTERNAL_MEMORY`, but the feature is not enabled" - ); - } - - let flags = conv::map_view_capabilities_sparse(sparse, view_caps); - let extent = conv::map_extent(kind.extent()); - let array_layers = kind.num_layers(); - let samples = kind.num_samples(); - let image_type = match kind { - image::Kind::D1(..) => vk::ImageType::TYPE_1D, - image::Kind::D2(..) => vk::ImageType::TYPE_2D, - image::Kind::D3(..) => vk::ImageType::TYPE_3D, - }; - - //Note: this is a hack, we should expose this in the API instead - let layout = match tiling { - image::Tiling::Linear => vk::ImageLayout::PREINITIALIZED, - image::Tiling::Optimal => vk::ImageLayout::UNDEFINED, - }; - - let external_memory_type = external_memory.external_memory_type(); - let external_memory_type_flags: hal::external_memory::ExternalMemoryTypeFlags = - external_memory_type.into(); - let vk_external_memory_type = - vk::ExternalMemoryHandleTypeFlags::from_raw(external_memory_type_flags.bits()); - - let mut external_memory_ci = vk::ExternalMemoryImageCreateInfo::builder() - .handle_types(vk_external_memory_type) - .build(); - - #[cfg(any(target_os = "linux", target_os = "android"))] - // _subresource_layouts need to be there for lifetime requirements - let (mut drm_format_properties, _subresource_layouts) = - if let hal::external_memory::ExternalImageMemory::DmaBuf( - _fd, - Some(drm_format_properties), - ) = &external_memory - { - use std::convert::TryInto; - match drm_format_properties.drm_modifier.try_into() { - Ok(drm_format_modifier) => { - let subresource_layouts: Vec = drm_format_properties - .plane_layouts - .iter() - .map(|subresource_footprint| vk::SubresourceLayout { - offset: subresource_footprint.slice.start, - size: subresource_footprint.slice.end, - row_pitch: subresource_footprint.row_pitch, - array_pitch: subresource_footprint.array_pitch, - depth_pitch: subresource_footprint.depth_pitch, - }) - .collect(); - - let vk_drm_format_properties = - vk::ImageDrmFormatModifierExplicitCreateInfoEXT::builder() - .drm_format_modifier(drm_format_modifier) - .plane_layouts(subresource_layouts.as_slice()) - .build(); - - (Some(vk_drm_format_properties), subresource_layouts) - } - Err(err) => { - error!("Unknow drm format modifier: {}", err); - (None, Vec::new()) - } - } - } else { - (None, Vec::new()) - }; - - let info = vk::ImageCreateInfo::builder() - .push_next(&mut external_memory_ci) - .flags(flags) - .image_type(image_type) - .format(conv::map_format(format)) - .extent(extent.clone()) - .mip_levels(mip_levels as u32) - .array_layers(array_layers as u32) - .samples(conv::map_sample_count_flags(samples)) - .tiling(conv::map_tiling(tiling)) - .usage(conv::map_image_usage(usage)) - .sharing_mode(vk::SharingMode::EXCLUSIVE) // TODO: - .initial_layout(layout); - - #[cfg(any(target_os = "linux", target_os = "android"))] - let info = if let Some(drm_format_properties) = &mut drm_format_properties { - info.push_next(drm_format_properties) - } else { - info - }; - - let mut image = match self.shared.raw.create_image(&info, None) { - Ok(raw) => n::Image { - raw, - ty: image_type, - flags, - extent, - }, - Err(vk::Result::ERROR_OUT_OF_HOST_MEMORY) => return Err(d::OutOfMemory::Host.into()), - Err(vk::Result::ERROR_OUT_OF_DEVICE_MEMORY) => { - return Err(d::OutOfMemory::Device.into()) - } - Err(unexpected_error) => { - panic!( - "Unexpected error on `create_image`: {:#?}", - unexpected_error - ); - } - }; - - let image_req = self.get_image_requirements(&image); - - let mut dedicated_allocation_info = - if self.shared.extension_fns.dedicated_allocation.is_some() { - let dedicated_allocation_info = vk::MemoryDedicatedAllocateInfo::builder() - .image(image.raw) - .build(); - Some(dedicated_allocation_info) - } else { - None - }; - - let result = match external_memory.platform_memory_type() { - #[cfg(unix)] - hal::external_memory::PlatformMemoryType::Fd => { - let fd = external_memory.fd().unwrap(); - let external_memory_extension = self.shared.extension_fns.external_memory_fd.as_ref().expect("This function rely on `Feature::EXTERNAL_MEMORY`, but the feature is not enabled"); - - #[cfg(any(target_os = "linux", target_os = "android", doc))] - if self.shared.extension_fns.external_memory_dma_buf.is_none() - && external_memory_type == hal::external_memory::ExternalMemoryType::DmaBuf - { - panic!("Requested to import a dma buf that is not supported by the system. Use `PhysicalDevice::external_image_properties` to check what is supported on the system."); - } - - let vk_memory_bits = if external_memory_type - == hal::external_memory::ExternalMemoryType::OpaqueFd - { - u32::MAX - } else { - use std::os::unix::io::AsRawFd; - match external_memory_extension - .get_memory_fd_properties_khr(vk_external_memory_type, fd.as_raw_fd()) - { - Ok(memory_handle_properties) => memory_handle_properties.memory_type_bits, - Err(vk::Result::ERROR_OUT_OF_HOST_MEMORY) => { - return Err(d::OutOfMemory::Host.into()) - } - Err(vk::Result::ERROR_INVALID_EXTERNAL_HANDLE_KHR) => { - error!("Failed to get memory fd properties"); - return Err( - hal::external_memory::ExternalResourceError::InvalidExternalHandle, - ); - } - unexpected_error => { - panic!( - "Unexpected error on `get_memory_fd_properties_khr`: {:#?}", - unexpected_error - ); - } - } - }; - - let mem_type = match (0..32) - .into_iter() - .find(|id| image_req.type_mask & type_mask & (1 << id) & vk_memory_bits != 0) - { - Some(id) => id.into(), - None => return Err(hal::external_memory::ExternalResourceError::NoValidMemoryTypeId), - }; - - let mut import_memory_info = vk::ImportMemoryFdInfoKHR::builder() - .handle_type(vk_external_memory_type) - .fd(**fd) - .build(); - - let allocate_info = vk::MemoryAllocateInfo::builder() - .push_next(&mut import_memory_info) - .allocation_size(image_req.size) - .memory_type_index(self.get_ash_memory_type_index(mem_type)); - - let allocate_info = - if let Some(dedicated_allocation_info) = &mut dedicated_allocation_info { - allocate_info.push_next(dedicated_allocation_info) - } else { - allocate_info - }; - - self.shared.raw.allocate_memory(&allocate_info, None) - } - #[cfg(windows)] - hal::external_memory::PlatformMemoryType::Handle => { - let handle = external_memory.handle().unwrap(); - let external_memory_extension = match self - .shared - .extension_fns - .external_memory_win32 - { - Some(ref functor) => functor, - _ => { - panic!("External memory windows handle extension not supported"); - } - }; - - let vk_memory_bits = { - use std::os::windows::io::AsRawHandle; - let mut memory_handle_properties = - vk::MemoryWin32HandlePropertiesKHR::builder().build(); - match external_memory_extension.get_memory_win32_handle_properties_khr( - self.shared.raw.handle(), - vk_external_memory_type, - handle.as_raw_handle(), - &mut memory_handle_properties, - ) { - vk::Result::SUCCESS => (), - vk::Result::ERROR_OUT_OF_HOST_MEMORY => { - return Err(d::OutOfMemory::Host.into()) - } - vk::Result::ERROR_INVALID_EXTERNAL_HANDLE_KHR => return Err( - hal::external_memory::ExternalResourceError::InvalidExternalHandle, - ), - _ => unreachable!(), - }; - memory_handle_properties.memory_type_bits - }; - - let mem_type = match (0..32) - .into_iter() - .find(|id| image_req.type_mask & type_mask & (1 << id) & vk_memory_bits != 0) - { - Some(id) => id.into(), - None => return Err(hal::external_memory::ExternalResourceError::NoValidMemoryTypeId), - }; - - let mut import_memory_info = vk::ImportMemoryWin32HandleInfoKHR::builder() - .handle_type(vk_external_memory_type) - .handle(**handle) - .build(); - - let allocate_info = - if let Some(dedicated_allocation_info) = &mut dedicated_allocation_info { - vk::MemoryAllocateInfo::builder().push_next(dedicated_allocation_info) - } else { - vk::MemoryAllocateInfo::builder() - } - .push_next(&mut import_memory_info) - .allocation_size(image_req.size) - .memory_type_index(self.get_ash_memory_type_index(mem_type)); - - self.shared.raw.allocate_memory(&allocate_info, None) - } - hal::external_memory::PlatformMemoryType::Ptr => { - let ptr = external_memory.ptr().unwrap(); - let external_memory_extension = self.shared.extension_fns.external_memory_host.as_ref().expect("This function rely on `Feature::EXTERNAL_MEMORY`, but the feature is not enabled"); - - let vk_memory_bits = { - let mut memory_ptr_properties = - vk::MemoryHostPointerPropertiesEXT::builder().build(); - match external_memory_extension.get_memory_host_pointer_properties_ext( - self.shared.raw.handle(), - vk_external_memory_type, - ptr.as_raw_ptr(), - &mut memory_ptr_properties, - ) { - vk::Result::SUCCESS => (), - vk::Result::ERROR_OUT_OF_HOST_MEMORY => { - return Err(d::OutOfMemory::Host.into()) - } - vk::Result::ERROR_INVALID_EXTERNAL_HANDLE_KHR => return Err( - hal::external_memory::ExternalResourceError::InvalidExternalHandle, - ), - unexpected_error => { - panic!("Unexpected error on `get_memory_host_pointer_properties_ext`: {:#?}", unexpected_error); - } - }; - memory_ptr_properties.memory_type_bits - }; - - let mem_type = match (0..32) - .into_iter() - .find(|id| image_req.type_mask & type_mask & (1 << id) & vk_memory_bits != 0) - { - Some(id) => id.into(), - None => return Err(hal::external_memory::ExternalResourceError::NoValidMemoryTypeId), - }; - - let mut import_memory_info = vk::ImportMemoryHostPointerInfoEXT::builder() - .handle_type(vk_external_memory_type) - .host_pointer(**ptr) - .build(); - - let allocate_info = - if let Some(dedicated_allocation_info) = &mut dedicated_allocation_info { - vk::MemoryAllocateInfo::builder().push_next(dedicated_allocation_info) - } else { - vk::MemoryAllocateInfo::builder() - } - .push_next(&mut import_memory_info) - .allocation_size(image_req.size) - .memory_type_index(self.get_ash_memory_type_index(mem_type)); - - self.shared.raw.allocate_memory(&allocate_info, None) - } - }; - - let memory = match result { - Ok(memory) => n::Memory { raw: memory }, - Err(err) => { - self.destroy_image(image); - match err { - vk::Result::ERROR_TOO_MANY_OBJECTS => { - return Err(hal::external_memory::ExternalResourceError::TooManyObjects) - } - vk::Result::ERROR_OUT_OF_HOST_MEMORY => return Err(d::OutOfMemory::Host.into()), - vk::Result::ERROR_OUT_OF_DEVICE_MEMORY => { - return Err(d::OutOfMemory::Device.into()) - } - vk::Result::ERROR_INVALID_EXTERNAL_HANDLE_KHR => { - return Err( - hal::external_memory::ExternalResourceError::InvalidExternalHandle, - ) - } - unexpected_error => { - panic!( - "Unexpected error on `allocate_memory`: {:#?}", - unexpected_error - ); - } - } - } - }; - - if let Err(err) = self.bind_image_memory(&memory, 0, &mut image) { - self.destroy_image(image); - self.free_memory(memory); - error!("Failed to `bind_image_memory`: {:#?}", err); - return Err(match err { - d::BindError::OutOfMemory(out_of_memory) => out_of_memory.into(), - d::BindError::WrongMemory => { - panic!("This error should never happen and it is likely a vulkan backend bug.") - } - d::BindError::OutOfBounds => { - panic!("Since external memory use a dedicated allocation, this should never happen and it is likely a vulkan backend bug.") - } - }); - } - - Ok((image, memory)) - } - - unsafe fn export_memory( - &self, - external_memory_type: hal::external_memory::ExternalMemoryType, - memory: &n::Memory, - ) -> Result - { - // Safety checks - if self.shared.instance.external_memory_capabilities.is_none() { - panic!("External memory not supported"); - }; - - let platform_memory_type: hal::external_memory::PlatformMemoryType = - external_memory_type.clone().into(); - - let external_memory_type_flags: hal::external_memory::ExternalMemoryTypeFlags = - external_memory_type.into(); - let vk_external_memory_type = - vk::ExternalMemoryHandleTypeFlags::from_raw(external_memory_type_flags.bits()); - - match platform_memory_type { - #[cfg(unix)] - hal::external_memory::PlatformMemoryType::Fd => { - let external_memory_extension = match self.shared.extension_fns.external_memory_fd { - Some(ref functor) => functor, - _ => { - panic!("External memory fd not supported"); - } - }; - - let memory_get_info = vk::MemoryGetFdInfoKHR::builder() - .memory(memory.raw) - .handle_type(vk_external_memory_type) - .build(); - - let fd = match external_memory_extension.get_memory_fd(&memory_get_info) { - Ok(fd) => hal::external_memory::Fd::from(fd), - Err(vk::Result::ERROR_TOO_MANY_OBJECTS) => { - return Err(hal::external_memory::ExternalMemoryExportError::TooManyObjects) - } - Err(vk::Result::ERROR_OUT_OF_HOST_MEMORY) => { - return Err( - hal::external_memory::ExternalMemoryExportError::OutOfHostMemory, - ) - } - unexpected_error => { - panic!( - "Unexpected error on `get_memory_fd`: {:#?}", - unexpected_error - ); - } - }; - Ok(hal::external_memory::PlatformMemory::Fd(fd.into())) - } - #[cfg(windows)] - hal::external_memory::PlatformMemoryType::Handle => { - let external_memory_extension = self.shared.extension_fns.external_memory_win32.as_ref().expect("This function rely on `Feature::EXTERNAL_MEMORY`, but the feature is not enabled"); - - let memory_get_info = vk::MemoryGetWin32HandleInfoKHR::builder() - .memory(memory.raw) - .handle_type(vk_external_memory_type) - .build(); - - let mut handle = std::ptr::null_mut(); - match external_memory_extension.get_memory_win32_handle_khr( - self.shared.raw.handle(), - &memory_get_info, - &mut handle, - ) { - vk::Result::SUCCESS => (), - vk::Result::ERROR_TOO_MANY_OBJECTS => { - return Err(hal::external_memory::ExternalMemoryExportError::TooManyObjects) - } - vk::Result::ERROR_OUT_OF_HOST_MEMORY => { - return Err( - hal::external_memory::ExternalMemoryExportError::OutOfHostMemory, - ) - } - unexpected_error => { - panic!( - "Unexpected error on `get_memory_win32_handle_khr`: {:#?}", - unexpected_error - ); - } - } - let handle = hal::external_memory::Handle::from(handle); - Ok(hal::external_memory::PlatformMemory::Handle(handle.into())) - } - hal::external_memory::PlatformMemoryType::Ptr => { - error!("Memory cannot be \"exported\" as host memory pointer. Use intead `Device::map_memory`."); - Err(hal::external_memory::ExternalMemoryExportError::InvalidExternalHandle) - } - } - } - - // This is needed because on non linux and non android systems, the variable image would be not used, - // so a "unused variable" warning will be triggered - #[allow(unused_variables)] - unsafe fn drm_format_modifier(&self, image: &n::Image) -> Option { - #[cfg(any(target_os = "linux", target_os = "android"))] - if let Some(ref extension) = self.shared.extension_fns.image_drm_format_modifier { - let mut image_drm_format_modifier = - vk::ImageDrmFormatModifierPropertiesEXT::builder().build(); - match extension.get_image_drm_format_modifier_properties_ext( - self.shared.raw.handle(), - image.raw, - &mut image_drm_format_modifier, - ) { - vk::Result::SUCCESS => { - return Some(image_drm_format_modifier.drm_format_modifier.into()); - } - vk::Result::ERROR_OUT_OF_HOST_MEMORY => (), - unexpected_error => { - error!( - "Unexpected error on `drm_format_modifier`: {:#?}", - unexpected_error - ); - } - } - } - - None - } - fn start_capture(&self) { unsafe { - self.render_doc - .start_frame_capture(self.shared.raw.handle().as_raw() as *mut _, ptr::null_mut()) + match self.shared.instance.render_doc_entry { + Ok(ref entry) => { + entry.api.StartFrameCapture.unwrap()( + self.shared.raw.handle().as_raw() as *mut _, + ptr::null_mut(), + ); + } + Err(ref cause) => warn!("Could not start render doc frame capture: {}", cause), + }; } } fn stop_capture(&self) { unsafe { - self.render_doc - .end_frame_capture(self.shared.raw.handle().as_raw() as *mut _, ptr::null_mut()) + match self.shared.instance.render_doc_entry { + Ok(ref entry) => { + entry.api.EndFrameCapture.unwrap()( + self.shared.raw.handle().as_raw() as *mut _, + ptr::null_mut(), + ); + } + Err(ref cause) => warn!("Could not stop render doc frame capture: {}", cause), + }; } } } @@ -3167,45 +2044,6 @@ impl super::Device { Ok((swapchain, images)) } - - pub unsafe fn image_view_from_raw( - &self, - raw_image: vk::Image, - view_type: vk::ImageViewType, - format: format::Format, - swizzle: format::Swizzle, - usage: image::Usage, - range: image::SubresourceRange, - ) -> Result { - let mut image_view_info; - let mut info = vk::ImageViewCreateInfo::builder() - .flags(vk::ImageViewCreateFlags::empty()) - .image(raw_image) - .view_type(view_type) - .format(conv::map_format(format)) - .components(conv::map_swizzle(swizzle)) - .subresource_range(conv::map_subresource_range(&range)); - - if self.shared.image_view_usage { - image_view_info = vk::ImageViewUsageCreateInfo::builder() - .usage(conv::map_image_usage(usage)) - .build(); - info = info.push_next(&mut image_view_info); - } - - let result = self.shared.raw.create_image_view(&info, None); - - match result { - Ok(raw) => Ok(n::ImageView { - image: raw_image, - raw, - range, - }), - Err(vk::Result::ERROR_OUT_OF_HOST_MEMORY) => Err(d::OutOfMemory::Host.into()), - Err(vk::Result::ERROR_OUT_OF_DEVICE_MEMORY) => Err(d::OutOfMemory::Device.into()), - _ => unreachable!(), - } - } } #[test] diff --git a/third_party/rust/gfx-backend-vulkan/src/lib.rs b/third_party/rust/gfx-backend-vulkan/src/lib.rs index f1a06b58dee4..3c1a9be5d527 100644 --- a/third_party/rust/gfx-backend-vulkan/src/lib.rs +++ b/third_party/rust/gfx-backend-vulkan/src/lib.rs @@ -28,8 +28,6 @@ extern crate objc; #[cfg(not(feature = "use-rtld-next"))] use ash::Entry; -#[cfg(feature = "use-rtld-next")] -type Entry = ash::EntryCustom<()>; use ash::{ extensions::{ext, khr, nv::MeshShader}, version::{DeviceV1_0, EntryV1_0, InstanceV1_0}, @@ -39,7 +37,7 @@ use ash::{ use hal::{ adapter, device::{DeviceLost, OutOfMemory}, - display, image, memory, + image, memory, pso::PipelineStage, queue, window::{OutOfDate, PresentError, Suboptimal, SurfaceLost}, @@ -55,6 +53,9 @@ use std::{ thread, unreachable, }; +#[cfg(feature = "use-rtld-next")] +use ash::EntryCustom; + mod command; mod conv; mod device; @@ -71,11 +72,10 @@ const ROUGH_MAX_ATTACHMENT_COUNT: usize = 5; pub struct RawInstance { inner: ash::Instance, - handle_is_external: bool, debug_messenger: Option, - get_physical_device_properties: Option>, - display: Option, - external_memory_capabilities: Option>, + get_physical_device_properties: Option, + + pub render_doc_entry: Result, } pub enum DebugMessenger { @@ -97,10 +97,7 @@ impl Drop for RawInstance { } None => {} } - - if !self.handle_is_external { - self.inner.destroy_instance(None); - } + self.inner.destroy_instance(None); } } } @@ -150,13 +147,27 @@ impl Into for u32 { } } +#[repr(C)] +pub struct RenderDocEntry { + api: renderdoc_sys::RENDERDOC_API_1_4_1, + lib: libloading::Library, +} + +unsafe impl Send for RenderDocEntry {} + +unsafe impl Sync for RenderDocEntry {} + pub struct Instance { pub raw: Arc, /// Supported extensions of this instance. pub extensions: Vec<&'static CStr>, + #[cfg(not(feature = "use-rtld-next"))] pub entry: Entry, + + #[cfg(feature = "use-rtld-next")] + pub entry: EntryCustom<()>, } impl fmt::Debug for Instance { @@ -342,242 +353,6 @@ unsafe extern "system" fn debug_report_callback( vk::FALSE } -impl Instance { - pub fn required_extensions( - entry: &Entry, - driver_api_version: Version, - ) -> Result, hal::UnsupportedBackend> { - let instance_extensions = entry - .enumerate_instance_extension_properties() - .map_err(|e| { - info!("Unable to enumerate instance extensions: {:?}", e); - hal::UnsupportedBackend - })?; - - // Check our extensions against the available extensions - let mut extensions: Vec<&'static CStr> = Vec::new(); - extensions.push(khr::Surface::name()); - - // Platform-specific WSI extensions - if cfg!(all( - unix, - not(target_os = "android"), - not(target_os = "macos") - )) { - extensions.push(khr::XlibSurface::name()); - extensions.push(khr::XcbSurface::name()); - extensions.push(khr::WaylandSurface::name()); - } - if cfg!(target_os = "android") { - extensions.push(khr::AndroidSurface::name()); - } - if cfg!(target_os = "windows") { - extensions.push(khr::Win32Surface::name()); - } - if cfg!(target_os = "macos") { - extensions.push(ash::extensions::mvk::MacOSSurface::name()); - } - - extensions.push(ext::DebugUtils::name()); - if cfg!(debug_assertions) { - #[allow(deprecated)] - extensions.push(ext::DebugReport::name()); - } - - extensions.push(vk::KhrGetPhysicalDeviceProperties2Fn::name()); - - // VK_KHR_storage_buffer_storage_class required for `Naga` on Vulkan 1.0 devices - if driver_api_version == Version::V1_0 { - extensions.push(vk::KhrStorageBufferStorageClassFn::name()); - } - - extensions.push(vk::ExtDisplaySurfaceCounterFn::name()); - extensions.push(khr::Display::name()); - - if driver_api_version < Version::V1_1 { - extensions.push(vk::KhrExternalMemoryCapabilitiesFn::name()); - } - - // VK_KHR_storage_buffer_storage_class required for `Naga` on Vulkan 1.0 devices - if driver_api_version == Version::V1_0 { - extensions.push(vk::KhrStorageBufferStorageClassFn::name()); - } - - // Only keep available extensions. - extensions.retain(|&ext| { - if instance_extensions - .iter() - .find(|inst_ext| unsafe { CStr::from_ptr(inst_ext.extension_name.as_ptr()) == ext }) - .is_some() - { - true - } else { - info!("Unable to find extension: {}", ext.to_string_lossy()); - false - } - }); - Ok(extensions) - } - - pub fn required_layers(entry: &Entry) -> Result, hal::UnsupportedBackend> { - let instance_layers = entry.enumerate_instance_layer_properties().map_err(|e| { - info!("Unable to enumerate instance layers: {:?}", e); - hal::UnsupportedBackend - })?; - - // Check requested layers against the available layers - let mut layers: Vec<&'static CStr> = Vec::new(); - if cfg!(debug_assertions) { - layers.push(CStr::from_bytes_with_nul(b"VK_LAYER_KHRONOS_validation\0").unwrap()); - } - - // Only keep available layers. - layers.retain(|&layer| { - if instance_layers - .iter() - .find(|inst_layer| unsafe { - CStr::from_ptr(inst_layer.layer_name.as_ptr()) == layer - }) - .is_some() - { - true - } else { - warn!("Unable to find layer: {}", layer.to_string_lossy()); - false - } - }); - Ok(layers) - } - - /// # Safety - /// `raw_instance` must be created using at least the extensions provided by `Instance::required_extensions()` - /// and the layers provided by `Instance::required_extensions()`. - /// `driver_api_version` must match the version used to create `raw_instance`. - /// `extensions` must match the extensions used to create `raw_instance`. - /// `raw_instance` must be manually destroyed *after* gfx-hal Instance has been dropped. - pub unsafe fn from_raw( - entry: Entry, - raw_instance: ash::Instance, - driver_api_version: Version, - extensions: Vec<&'static CStr>, - ) -> Result { - Instance::inner_create(entry, raw_instance, true, driver_api_version, extensions) - } - - fn inner_create( - entry: Entry, - instance: ash::Instance, - handle_is_external: bool, - driver_api_version: Version, - extensions: Vec<&'static CStr>, - ) -> Result { - if driver_api_version == Version::V1_0 - && !extensions.contains(&vk::KhrStorageBufferStorageClassFn::name()) - { - warn!("Unable to create Vulkan instance. Required VK_KHR_storage_buffer_storage_class extension is not supported"); - return Err(hal::UnsupportedBackend); - } - - let instance_extensions = entry - .enumerate_instance_extension_properties() - .map_err(|e| { - info!("Unable to enumerate instance extensions: {:?}", e); - hal::UnsupportedBackend - })?; - - let get_physical_device_properties = if driver_api_version >= Version::V1_1 { - Some(ExtensionFn::Promoted) - } else { - extensions - .iter() - .find(|&&ext| ext == vk::KhrGetPhysicalDeviceProperties2Fn::name()) - .map(|_| { - ExtensionFn::Extension(vk::KhrGetPhysicalDeviceProperties2Fn::load( - |name| unsafe { - std::mem::transmute( - entry.get_instance_proc_addr(instance.handle(), name.as_ptr()), - ) - }, - )) - }) - }; - - let display = extensions - .iter() - .find(|&&ext| ext == khr::Display::name()) - .map(|_| khr::Display::new(&entry, &instance)); - - let external_memory_capabilities = if driver_api_version >= Version::V1_1 { - Some(ExtensionFn::Promoted) - } else { - extensions - .iter() - .find(|&&ext| ext == vk::KhrExternalMemoryCapabilitiesFn::name()) - .map(|_| { - ExtensionFn::Extension(vk::KhrExternalMemoryCapabilitiesFn::load( - |name| unsafe { - std::mem::transmute( - entry.get_instance_proc_addr(instance.handle(), name.as_ptr()), - ) - }, - )) - }) - }; - - #[allow(deprecated)] // `DebugReport` - let debug_messenger = { - // make sure VK_EXT_debug_utils is available - if instance_extensions.iter().any(|props| unsafe { - CStr::from_ptr(props.extension_name.as_ptr()) == ext::DebugUtils::name() - }) { - let ext = ext::DebugUtils::new(&entry, &instance); - let info = vk::DebugUtilsMessengerCreateInfoEXT::builder() - .flags(vk::DebugUtilsMessengerCreateFlagsEXT::empty()) - .message_severity(vk::DebugUtilsMessageSeverityFlagsEXT::all()) - .message_type(vk::DebugUtilsMessageTypeFlagsEXT::all()) - .pfn_user_callback(Some(debug_utils_messenger_callback)); - let handle = unsafe { ext.create_debug_utils_messenger(&info, None) }.unwrap(); - Some(DebugMessenger::Utils(ext, handle)) - } else if cfg!(debug_assertions) - && instance_extensions.iter().any(|props| unsafe { - CStr::from_ptr(props.extension_name.as_ptr()) == ext::DebugReport::name() - }) - { - let ext = ext::DebugReport::new(&entry, &instance); - let info = vk::DebugReportCallbackCreateInfoEXT::builder() - .flags(vk::DebugReportFlagsEXT::all()) - .pfn_callback(Some(debug_report_callback)); - let handle = unsafe { ext.create_debug_report_callback(&info, None) }.unwrap(); - Some(DebugMessenger::Report(ext, handle)) - } else { - None - } - }; - - Ok(Instance { - raw: Arc::new(RawInstance { - inner: instance, - handle_is_external, - debug_messenger, - get_physical_device_properties, - display, - external_memory_capabilities, - }), - extensions, - entry, - }) - } - - /// # Safety - /// `raw_physical_device` must be created from `self` (or from the inner raw handle) - pub unsafe fn adapter_from_raw( - &self, - raw_physical_device: vk::PhysicalDevice, - ) -> adapter::Adapter { - physical_device::load_adapter(&self.raw, raw_physical_device) - } -} - impl hal::Instance for Instance { fn create(name: &str, version: u32) -> Result { #[cfg(not(feature = "use-rtld-next"))] @@ -590,7 +365,7 @@ impl hal::Instance for Instance { }; #[cfg(feature = "use-rtld-next")] - let entry = Entry::new_custom((), |_, name| unsafe { + let entry = EntryCustom::new_custom((), |_, name| unsafe { libc::dlsym(libc::RTLD_NEXT, name.as_ptr()) }); @@ -634,9 +409,105 @@ impl hal::Instance for Instance { .into() }); - let extensions = Instance::required_extensions(&entry, driver_api_version)?; + let instance_extensions = entry + .enumerate_instance_extension_properties() + .map_err(|e| { + info!("Unable to enumerate instance extensions: {:?}", e); + hal::UnsupportedBackend + })?; - let layers = Instance::required_layers(&entry)?; + let instance_layers = entry.enumerate_instance_layer_properties().map_err(|e| { + info!("Unable to enumerate instance layers: {:?}", e); + hal::UnsupportedBackend + })?; + + // Check our extensions against the available extensions + let extensions = { + let mut extensions: Vec<&'static CStr> = Vec::new(); + extensions.push(khr::Surface::name()); + + // Platform-specific WSI extensions + if cfg!(all( + unix, + not(target_os = "android"), + not(target_os = "macos") + )) { + extensions.push(khr::XlibSurface::name()); + extensions.push(khr::XcbSurface::name()); + extensions.push(khr::WaylandSurface::name()); + } + if cfg!(target_os = "android") { + extensions.push(khr::AndroidSurface::name()); + } + if cfg!(target_os = "windows") { + extensions.push(khr::Win32Surface::name()); + } + if cfg!(target_os = "macos") { + extensions.push(ash::extensions::mvk::MacOSSurface::name()); + } + + extensions.push(ext::DebugUtils::name()); + if cfg!(debug_assertions) { + #[allow(deprecated)] + extensions.push(ext::DebugReport::name()); + } + + extensions.push(vk::KhrGetPhysicalDeviceProperties2Fn::name()); + + // VK_KHR_storage_buffer_storage_class required for `Naga` on Vulkan 1.0 devices + if driver_api_version == Version::V1_0 { + extensions.push(vk::KhrStorageBufferStorageClassFn::name()); + } + + // Only keep available extensions. + extensions.retain(|&ext| { + if instance_extensions + .iter() + .find(|inst_ext| unsafe { + CStr::from_ptr(inst_ext.extension_name.as_ptr()) == ext + }) + .is_some() + { + true + } else { + info!("Unable to find extension: {}", ext.to_string_lossy()); + false + } + }); + extensions + }; + + if driver_api_version == Version::V1_0 + && !extensions.contains(&vk::KhrStorageBufferStorageClassFn::name()) + { + warn!("Unable to create Vulkan instance. Required VK_KHR_storage_buffer_storage_class extension is not supported"); + return Err(hal::UnsupportedBackend); + } + + // Check requested layers against the available layers + let layers = { + let mut layers: Vec<&'static CStr> = Vec::new(); + if cfg!(debug_assertions) { + layers.push(CStr::from_bytes_with_nul(b"VK_LAYER_KHRONOS_validation\0").unwrap()); + } + + // Only keep available layers. + layers.retain(|&layer| { + if instance_layers + .iter() + .find(|inst_layer| unsafe { + CStr::from_ptr(inst_layer.layer_name.as_ptr()) == layer + }) + .is_some() + { + true + } else { + warn!("Unable to find layer: {}", layer.to_string_lossy()); + false + } + }); + layers + }; let instance = { let str_pointers = layers @@ -659,7 +530,58 @@ impl hal::Instance for Instance { hal::UnsupportedBackend })? }; - Instance::inner_create(entry, instance, false, driver_api_version, extensions) + + let get_physical_device_properties = extensions + .iter() + .find(|&&ext| ext == vk::KhrGetPhysicalDeviceProperties2Fn::name()) + .map(|_| { + vk::KhrGetPhysicalDeviceProperties2Fn::load(|name| unsafe { + std::mem::transmute( + entry.get_instance_proc_addr(instance.handle(), name.as_ptr()), + ) + }) + }); + + #[allow(deprecated)] // `DebugReport` + let debug_messenger = { + // make sure VK_EXT_debug_utils is available + if instance_extensions.iter().any(|props| unsafe { + CStr::from_ptr(props.extension_name.as_ptr()) == ext::DebugUtils::name() + }) { + let ext = ext::DebugUtils::new(&entry, &instance); + let info = vk::DebugUtilsMessengerCreateInfoEXT::builder() + .flags(vk::DebugUtilsMessengerCreateFlagsEXT::empty()) + .message_severity(vk::DebugUtilsMessageSeverityFlagsEXT::all()) + .message_type(vk::DebugUtilsMessageTypeFlagsEXT::all()) + .pfn_user_callback(Some(debug_utils_messenger_callback)); + let handle = unsafe { ext.create_debug_utils_messenger(&info, None) }.unwrap(); + Some(DebugMessenger::Utils(ext, handle)) + } else if cfg!(debug_assertions) + && instance_extensions.iter().any(|props| unsafe { + CStr::from_ptr(props.extension_name.as_ptr()) == ext::DebugReport::name() + }) + { + let ext = ext::DebugReport::new(&entry, &instance); + let info = vk::DebugReportCallbackCreateInfoEXT::builder() + .flags(vk::DebugReportFlagsEXT::all()) + .pfn_callback(Some(debug_report_callback)); + let handle = unsafe { ext.create_debug_report_callback(&info, None) }.unwrap(); + Some(DebugMessenger::Report(ext, handle)) + } else { + None + } + }; + + Ok(Instance { + raw: Arc::new(RawInstance { + inner: instance, + debug_messenger, + get_physical_device_properties, + render_doc_entry: unsafe { load_renderdoc_entrypoint() }, + }), + extensions, + entry, + }) } fn enumerate_adapters(&self) -> Vec> { @@ -738,68 +660,51 @@ impl hal::Instance for Instance { .functor .destroy_surface(surface.raw.handle, None); } +} - unsafe fn create_display_plane_surface( - &self, - display_plane: &hal::display::DisplayPlane, - plane_stack_index: u32, - transformation: hal::display::SurfaceTransform, - alpha: hal::display::DisplayPlaneAlpha, - image_extent: hal::window::Extent2D, - ) -> Result { - let display_extension = match &self.raw.display { - Some(display_extension) => display_extension, - None => { - error!("Direct display feature not supported"); - return Err(display::DisplayPlaneSurfaceError::UnsupportedFeature); - } - }; - let surface_transform_flags = hal::display::SurfaceTransformFlags::from(transformation); - let vk_surface_transform_flags = - vk::SurfaceTransformFlagsKHR::from_raw(surface_transform_flags.bits()); - - let display_surface_ci = { - let builder = vk::DisplaySurfaceCreateInfoKHR::builder() - .display_mode(display_plane.display_mode.handle.0) - .plane_index(display_plane.plane.handle) - .plane_stack_index(plane_stack_index) - .image_extent(vk::Extent2D { - width: image_extent.width, - height: image_extent.height, - }) - .transform(vk_surface_transform_flags); - - match alpha { - hal::display::DisplayPlaneAlpha::Opaque => { - builder.alpha_mode(vk::DisplayPlaneAlphaFlagsKHR::OPAQUE) - } - hal::display::DisplayPlaneAlpha::Global(value) => builder - .alpha_mode(vk::DisplayPlaneAlphaFlagsKHR::GLOBAL) - .global_alpha(value), - hal::display::DisplayPlaneAlpha::PerPixel => { - builder.alpha_mode(vk::DisplayPlaneAlphaFlagsKHR::PER_PIXEL) - } - hal::display::DisplayPlaneAlpha::PerPixelPremultiplied => { - builder.alpha_mode(vk::DisplayPlaneAlphaFlagsKHR::PER_PIXEL_PREMULTIPLIED) - } - } - .build() - }; - - let surface = match display_extension - .create_display_plane_surface(&display_surface_ci, None) - { - Ok(surface) => surface, - Err(vk::Result::ERROR_OUT_OF_HOST_MEMORY) => return Err(OutOfMemory::Host.into()), - Err(vk::Result::ERROR_OUT_OF_DEVICE_MEMORY) => return Err(OutOfMemory::Device.into()), - err => panic!( - "Unexpected error on `create_display_plane_surface`: {:#?}", - err - ), - }; - - Ok(self.create_surface_from_vk_surface_khr(surface)) +unsafe fn load_renderdoc_entrypoint() -> Result { + if !cfg!(debug_assertions) { + return Err("RenderDoc support is only enabled with 'debug_assertions'".into()); } + + type GetApiFn = unsafe extern "C" fn(version: u32, out: *mut *mut std::ffi::c_void) -> i32; + + #[cfg(windows)] + let renderdoc_filename = "renderdoc.dll"; + #[cfg(all(unix, not(target_os = "android")))] + let renderdoc_filename = "librenderdoc.so"; + #[cfg(target_os = "android")] + let renderdoc_filename = "libVkLayer_GLES_RenderDoc.so"; + + let renderdoc_lib = libloading::Library::new(renderdoc_filename).map_err(|e| { + format!( + "Unable to load renderdoc library '{}': {:?}", + renderdoc_filename, e + ) + })?; + + let get_api: libloading::Symbol = + renderdoc_lib.get(b"RENDERDOC_GetAPI\0").map_err(|e| { + format!( + "Unable to get RENDERDOC_GetAPI from renderdoc library '{}': {:?}", + renderdoc_filename, e + ) + })?; + let mut obj = std::ptr::null_mut(); + match get_api(10401, &mut obj) { + 1 => Ok(RenderDocEntry { + api: *(obj as *mut renderdoc_sys::RENDERDOC_API_1_4_1), + lib: renderdoc_lib, + }), + return_value => Err(format!( + "Unable to get API from renderdoc library '{}': {}", + renderdoc_filename, return_value + )), + } + .map_err(|error| { + warn!("Error loading renderdoc library: {}", error); + error + }) } #[derive(Debug, Clone)] @@ -829,22 +734,6 @@ impl queue::QueueFamily for QueueFamily { struct DeviceExtensionFunctions { mesh_shaders: Option>, draw_indirect_count: Option>, - display_control: Option, - memory_requirements2: Option>, - // The extension does not have its own functions. - dedicated_allocation: Option>, - // The extension does not have its own functions. - external_memory: Option>, - external_memory_host: Option, - #[cfg(windows)] - external_memory_win32: Option, - #[cfg(unix)] - external_memory_fd: Option, - #[cfg(any(target_os = "linux", target_os = "android"))] - // The extension does not have its own functions. - external_memory_dma_buf: Option<()>, - #[cfg(any(target_os = "linux", target_os = "android"))] - image_drm_format_modifier: Option, } // TODO there's no reason why this can't be unified--the function pointers should all be the same--it's not clear how to do this with `ash`. @@ -869,7 +758,6 @@ impl ExtensionFn { #[doc(hidden)] pub struct RawDevice { raw: ash::Device, - handle_is_external: bool, features: Features, instance: Arc, extension_fns: DeviceExtensionFunctions, @@ -889,10 +777,8 @@ impl fmt::Debug for RawDevice { } impl Drop for RawDevice { fn drop(&mut self) { - if !self.handle_is_external { - unsafe { - self.raw.destroy_device(None); - } + unsafe { + self.raw.destroy_device(None); } } } @@ -1202,7 +1088,6 @@ pub struct Device { shared: Arc, vendor_id: u32, valid_ash_memory_types: u32, - render_doc: gfx_renderdoc::RenderDoc, #[cfg(feature = "naga")] naga_options: naga::back::spv::Options, } @@ -1244,7 +1129,4 @@ impl hal::Backend for Backend { type Semaphore = native::Semaphore; type Event = native::Event; type QueryPool = native::QueryPool; - - type Display = native::Display; - type DisplayMode = native::DisplayMode; } diff --git a/third_party/rust/gfx-backend-vulkan/src/native.rs b/third_party/rust/gfx-backend-vulkan/src/native.rs index e2520a6b8b7d..e5970e81e69c 100644 --- a/third_party/rust/gfx-backend-vulkan/src/native.rs +++ b/third_party/rust/gfx-backend-vulkan/src/native.rs @@ -234,9 +234,3 @@ impl pso::DescriptorPool for DescriptorPool { #[derive(Debug, Hash)] pub struct QueryPool(pub vk::QueryPool); - -#[derive(Debug, Hash)] -pub struct Display(pub vk::DisplayKHR); - -#[derive(Debug, Hash)] -pub struct DisplayMode(pub vk::DisplayModeKHR); diff --git a/third_party/rust/gfx-backend-vulkan/src/physical_device.rs b/third_party/rust/gfx-backend-vulkan/src/physical_device.rs index e6db021308de..4f89bf840d22 100644 --- a/third_party/rust/gfx-backend-vulkan/src/physical_device.rs +++ b/third_party/rust/gfx-backend-vulkan/src/physical_device.rs @@ -4,22 +4,19 @@ use ash::{ vk, }; -#[cfg(unix)] -use ash::extensions::khr::ExternalMemoryFd; - use hal::{ adapter, device::{CreationError, OutOfMemory}, - display, external_memory, format, image, + format, image, pso::PatchSize, - queue, DescriptorLimits, DownlevelProperties, DynamicStates, ExternalMemoryLimits, Features, - Limits, PhysicalDeviceProperties, + queue, DescriptorLimits, DownlevelProperties, DynamicStates, Features, Limits, + PhysicalDeviceProperties, }; use std::{ffi::CStr, fmt, mem, ptr, sync::Arc}; use crate::{ - conv, info, native, Backend, Device, DeviceExtensionFunctions, ExtensionFn, Queue, QueueFamily, + conv, info, Backend, Device, DeviceExtensionFunctions, ExtensionFn, Queue, QueueFamily, RawDevice, RawInstance, Version, }; @@ -442,13 +439,6 @@ impl PhysicalDeviceFeatures { bits |= Features::CONSERVATIVE_RASTERIZATION } - if info.api_version() >= Version::V1_1 - || (info.supports_extension(vk::KhrGetPhysicalDeviceProperties2Fn::name()) - && info.supports_extension(vk::KhrExternalMemoryFn::name())) - { - bits |= Features::EXTERNAL_MEMORY - } - if let Some(ref vulkan_1_2) = self.vulkan_1_2 { if vulkan_1_2.shader_sampled_image_array_non_uniform_indexing != 0 { bits |= Features::SAMPLED_TEXTURE_DESCRIPTOR_INDEXING; @@ -592,35 +582,6 @@ impl PhysicalDeviceInfo { requested_extensions.push(vk::KhrGetDisplayProperties2Fn::name()); // TODO NOT NEEDED, RIGHT? } - if self.supports_extension(vk::ExtDisplayControlFn::name()) { - requested_extensions.push(vk::ExtDisplayControlFn::name()); - } - - if requested_features.contains(Features::EXTERNAL_MEMORY) { - if self.api_version() < Version::V1_1 { - requested_extensions.push(vk::KhrGetPhysicalDeviceProperties2Fn::name()); - requested_extensions.push(vk::KhrExternalMemoryFn::name()); - - // External memory interact with DedicatedAllocation extension, but it is not a strict dependency. - requested_extensions.push(vk::KhrGetMemoryRequirements2Fn::name()); // TODO Functions should be added because they are useful - requested_extensions.push(vk::KhrDedicatedAllocationFn::name()); - } - - requested_extensions.push(vk::ExtExternalMemoryHostFn::name()); - #[cfg(window)] - requested_extensions.push(vk::KhrExternalMemoryWin32Fn::name()); - #[cfg(unix)] - { - requested_extensions.push(vk::KhrExternalMemoryFdFn::name()); - requested_extensions.push(vk::ExtExternalMemoryDmaBufFn::name()); - - requested_extensions.push(vk::KhrBindMemory2Fn::name()); - - requested_extensions.push(vk::KhrImageFormatListFn::name()); - requested_extensions.push(vk::KhrSamplerYcbcrConversionFn::name()); - requested_extensions.push(vk::ExtImageDrmFormatModifierFn::name()); - } - } requested_extensions } @@ -679,21 +640,10 @@ impl PhysicalDeviceInfo { mut_ref.p_next = mem::replace(&mut features2.p_next, mut_ref as *mut _ as *mut _); } - match get_device_properties { - ExtensionFn::Promoted => { - use ash::version::InstanceV1_1; - unsafe { - instance - .inner - .get_physical_device_features2(device, &mut features2); - } - } - ExtensionFn::Extension(get_device_properties) => unsafe { - get_device_properties - .get_physical_device_features2_khr(device, &mut features2 as *mut _); - }, + unsafe { + get_device_properties + .get_physical_device_features2_khr(device, &mut features2 as *mut _); } - features2.features } else { unsafe { instance.inner.get_physical_device_features(device) } @@ -728,346 +678,6 @@ pub struct PhysicalDevice { available_features: Features, } -impl PhysicalDevice { - /// # Safety - /// `raw_device` must be created from `self` (or from the inner raw handle) - /// `raw_device` must be created with `requested_features` - pub unsafe fn gpu_from_raw( - &self, - raw_device: ash::Device, - families: &[(&QueueFamily, &[queue::QueuePriority])], - requested_features: Features, - ) -> Result, CreationError> { - let enabled_extensions = self.enabled_extensions(requested_features)?; - Ok(self.inner_create_gpu( - raw_device, - true, - families, - requested_features, - enabled_extensions, - )) - } - - unsafe fn inner_create_gpu( - &self, - device_raw: ash::Device, - handle_is_external: bool, - families: &[(&QueueFamily, &[queue::QueuePriority])], - requested_features: Features, - enabled_extensions: Vec<&CStr>, - ) -> adapter::Gpu { - let valid_ash_memory_types = { - let mem_properties = self - .instance - .inner - .get_physical_device_memory_properties(self.handle); - mem_properties.memory_types[..mem_properties.memory_type_count as usize] - .iter() - .enumerate() - .fold(0, |u, (i, mem)| { - if self.known_memory_flags.contains(mem.property_flags) { - u | (1 << i) - } else { - u - } - }) - }; - - let supports_vulkan12_imageless_framebuffer = self - .device_features - .vulkan_1_2 - .map_or(false, |features| features.imageless_framebuffer == vk::TRUE); - - let swapchain_fn = Swapchain::new(&self.instance.inner, &device_raw); - - let mesh_fn = if enabled_extensions.contains(&MeshShader::name()) { - Some(ExtensionFn::Extension(MeshShader::new( - &self.instance.inner, - &device_raw, - ))) - } else { - None - }; - - let indirect_count_fn = if enabled_extensions.contains(&DrawIndirectCount::name()) { - Some(ExtensionFn::Extension(DrawIndirectCount::new( - &self.instance.inner, - &device_raw, - ))) - } else if self.device_info.api_version() >= Version::V1_2 { - Some(ExtensionFn::Promoted) - } else { - None - }; - - let display_control = if enabled_extensions.contains(&vk::ExtDisplayControlFn::name()) { - Some(vk::ExtDisplayControlFn::load(|name| { - std::mem::transmute( - self.instance - .inner - .get_device_proc_addr(device_raw.handle(), name.as_ptr()), - ) - })) - } else { - None - }; - - let memory_requirements2 = - if enabled_extensions.contains(&vk::KhrGetMemoryRequirements2Fn::name()) { - Some(ExtensionFn::Extension( - vk::KhrGetMemoryRequirements2Fn::load(|name| { - std::mem::transmute( - self.instance - .inner - .get_device_proc_addr(device_raw.handle(), name.as_ptr()), - ) - }), - )) - } else { - None - }; - - let dedicated_allocation; - let external_memory; - let external_memory_host; - - #[cfg(unix)] - let external_memory_fd; - - #[cfg(any(target_os = "linux", target_os = "android"))] - let external_memory_dma_buf; - - #[cfg(any(target_os = "linux", target_os = "android"))] - let image_drm_format_modifier; - - #[cfg(windows)] - let external_memory_win32; - - if requested_features.contains(Features::EXTERNAL_MEMORY) { - if self.device_info.api_version() < Version::V1_1 { - external_memory = if enabled_extensions.contains(&vk::KhrExternalMemoryFn::name()) { - Some(ExtensionFn::Extension(())) - } else { - None - }; - - // External memory interact with DedicatedAllocation extension, but it is not a strict dependency. - dedicated_allocation = - if enabled_extensions.contains(&vk::KhrDedicatedAllocationFn::name()) { - Some(ExtensionFn::Extension(())) - } else { - None - }; - } else { - external_memory = Some(ExtensionFn::Promoted); - dedicated_allocation = Some(ExtensionFn::Promoted); - } - - external_memory_host = - if enabled_extensions.contains(&vk::ExtExternalMemoryHostFn::name()) { - Some(vk::ExtExternalMemoryHostFn::load(|name| { - std::mem::transmute( - self.instance - .inner - .get_device_proc_addr(device_raw.handle(), name.as_ptr()), - ) - })) - } else { - None - }; - - #[cfg(windows)] - { - external_memory_win32 = - if enabled_extensions.contains(&vk::KhrExternalMemoryWin32Fn::name()) { - Some(vk::KhrExternalMemoryWin32Fn::load(|name| { - std::mem::transmute( - self.instance - .inner - .get_device_proc_addr(device_raw.handle(), name.as_ptr()), - ) - })) - } else { - None - }; - } - #[cfg(unix)] - { - external_memory_fd = if enabled_extensions.contains(&ExternalMemoryFd::name()) { - Some(ExternalMemoryFd::new(&self.instance.inner, &device_raw)) - } else { - None - }; - - #[cfg(any(target_os = "linux", target_os = "android"))] - { - external_memory_dma_buf = - if enabled_extensions.contains(&vk::ExtExternalMemoryDmaBufFn::name()) { - Some(()) - } else { - None - }; - - image_drm_format_modifier = - if enabled_extensions.contains(&vk::ExtImageDrmFormatModifierFn::name()) { - Some(vk::ExtImageDrmFormatModifierFn::load(|name| { - std::mem::transmute( - self.instance - .inner - .get_device_proc_addr(device_raw.handle(), name.as_ptr()), - ) - })) - } else { - None - }; - } - } - } else { - dedicated_allocation = None; - external_memory = None; - external_memory_host = None; - - #[cfg(unix)] - { - external_memory_fd = None; - } - - #[cfg(any(target_os = "linux", target_os = "android"))] - { - external_memory_dma_buf = None; - image_drm_format_modifier = None; - } - - #[cfg(windows)] - { - external_memory_win32 = None; - } - } - - #[cfg(feature = "naga")] - let naga_options = { - use naga::back::spv; - let capabilities = [ - spv::Capability::Shader, - spv::Capability::Matrix, - spv::Capability::InputAttachment, - spv::Capability::Sampled1D, - spv::Capability::Image1D, - spv::Capability::SampledBuffer, - spv::Capability::ImageBuffer, - spv::Capability::ImageQuery, - spv::Capability::DerivativeControl, - //TODO: fill out the rest - ]; - let mut flags = spv::WriterFlags::empty(); - flags.set(spv::WriterFlags::DEBUG, cfg!(debug_assertions)); - flags.set( - spv::WriterFlags::ADJUST_COORDINATE_SPACE, - !requested_features.contains(hal::Features::NDC_Y_UP), - ); - spv::Options { - lang_version: (1, 0), - flags, - capabilities: Some(capabilities.iter().cloned().collect()), - } - }; - - let device = Device { - shared: Arc::new(RawDevice { - raw: device_raw, - handle_is_external, - features: requested_features, - instance: Arc::clone(&self.instance), - extension_fns: DeviceExtensionFunctions { - mesh_shaders: mesh_fn, - draw_indirect_count: indirect_count_fn, - display_control, - memory_requirements2: memory_requirements2, - dedicated_allocation: dedicated_allocation, - external_memory, - external_memory_host, - #[cfg(unix)] - external_memory_fd, - #[cfg(windows)] - external_memory_win32, - #[cfg(any(target_os = "linux", target_os = "android"))] - external_memory_dma_buf, - #[cfg(any(target_os = "linux", target_os = "android"))] - image_drm_format_modifier, - }, - flip_y_requires_shift: self.device_info.api_version() >= Version::V1_1 - || self - .device_info - .supports_extension(vk::KhrMaintenance1Fn::name()), - imageless_framebuffers: supports_vulkan12_imageless_framebuffer - || self - .device_info - .supports_extension(vk::KhrImagelessFramebufferFn::name()), - image_view_usage: self.device_info.api_version() >= Version::V1_1 - || self - .device_info - .supports_extension(vk::KhrMaintenance2Fn::name()), - timestamp_period: self.device_info.properties.limits.timestamp_period, - }), - vendor_id: self.device_info.properties.vendor_id, - valid_ash_memory_types, - render_doc: Default::default(), - #[cfg(feature = "naga")] - naga_options, - }; - - let device_arc = Arc::clone(&device.shared); - let queue_groups = families - .iter() - .map(|&(family, ref priorities)| { - let mut family_raw = - queue::QueueGroup::new(queue::QueueFamilyId(family.index as usize)); - for id in 0..priorities.len() { - let queue_raw = device_arc.raw.get_device_queue(family.index, id as _); - family_raw.add_queue(Queue { - raw: Arc::new(queue_raw), - device: device_arc.clone(), - swapchain_fn: swapchain_fn.clone(), - }); - } - family_raw - }) - .collect(); - - adapter::Gpu { - device, - queue_groups, - } - } - - pub fn enabled_extensions( - &self, - requested_features: Features, - ) -> Result, CreationError> { - use adapter::PhysicalDevice; - - if !self.features().contains(requested_features) { - return Err(CreationError::MissingFeature); - } - - let (supported_extensions, unsupported_extensions) = self - .device_info - .get_required_extensions(requested_features) - .iter() - .partition::, _>(|&&extension| { - self.device_info.supports_extension(extension) - }); - - if !unsupported_extensions.is_empty() { - warn!("Missing extensions: {:?}", unsupported_extensions); - } - - debug!("Supported extensions: {:?}", supported_extensions); - - Ok(supported_extensions) - } -} - pub(crate) fn load_adapter( instance: &Arc, device: vk::PhysicalDevice, @@ -1167,7 +777,44 @@ impl adapter::PhysicalDevice for PhysicalDevice { }) .collect::>(); - let enabled_extensions = self.enabled_extensions(requested_features)?; + if !self.features().contains(requested_features) { + return Err(CreationError::MissingFeature); + } + + let enabled_extensions = { + let (supported_extensions, unsupported_extensions) = self + .device_info + .get_required_extensions(requested_features) + .iter() + .partition::, _>(|&&extension| { + self.device_info.supports_extension(extension) + }); + + if !unsupported_extensions.is_empty() { + warn!("Missing extensions: {:?}", unsupported_extensions); + } + + debug!("Supported extensions: {:?}", supported_extensions); + + supported_extensions + }; + + let valid_ash_memory_types = { + let mem_properties = self + .instance + .inner + .get_physical_device_memory_properties(self.handle); + mem_properties.memory_types[..mem_properties.memory_type_count as usize] + .iter() + .enumerate() + .fold(0, |u, (i, mem)| { + if self.known_memory_flags.contains(mem.property_flags) { + u | (1 << i) + } else { + u + } + }) + }; let supports_vulkan12_imageless_framebuffer = self .device_features @@ -1220,16 +867,116 @@ impl adapter::PhysicalDevice for PhysicalDevice { } }; - Ok(self.inner_create_gpu( - device_raw, - false, - families, - requested_features, - enabled_extensions, - )) + let swapchain_fn = Swapchain::new(&self.instance.inner, &device_raw); + + let mesh_fn = if enabled_extensions.contains(&MeshShader::name()) { + Some(ExtensionFn::Extension(MeshShader::new( + &self.instance.inner, + &device_raw, + ))) + } else { + None + }; + + let indirect_count_fn = if enabled_extensions.contains(&DrawIndirectCount::name()) { + Some(ExtensionFn::Extension(DrawIndirectCount::new( + &self.instance.inner, + &device_raw, + ))) + } else if self.device_info.api_version() >= Version::V1_2 { + Some(ExtensionFn::Promoted) + } else { + None + }; + + #[cfg(feature = "naga")] + let naga_options = { + use naga::back::spv; + let capabilities = [ + spv::Capability::Shader, + spv::Capability::Matrix, + spv::Capability::InputAttachment, + spv::Capability::Sampled1D, + spv::Capability::Image1D, + spv::Capability::SampledBuffer, + spv::Capability::ImageBuffer, + spv::Capability::ImageQuery, + spv::Capability::DerivativeControl, + //TODO: fill out the rest + ]; + let mut flags = spv::WriterFlags::empty(); + flags.set(spv::WriterFlags::DEBUG, cfg!(debug_assertions)); + flags.set( + spv::WriterFlags::ADJUST_COORDINATE_SPACE, + !requested_features.contains(hal::Features::NDC_Y_UP), + ); + spv::Options { + lang_version: (1, 0), + flags, + capabilities: Some(capabilities.iter().cloned().collect()), + } + }; + + let device = Device { + shared: Arc::new(RawDevice { + raw: device_raw, + features: requested_features, + instance: Arc::clone(&self.instance), + extension_fns: DeviceExtensionFunctions { + mesh_shaders: mesh_fn, + draw_indirect_count: indirect_count_fn, + }, + flip_y_requires_shift: self.device_info.api_version() >= Version::V1_1 + || self + .device_info + .supports_extension(vk::KhrMaintenance1Fn::name()), + imageless_framebuffers: supports_vulkan12_imageless_framebuffer + || self + .device_info + .supports_extension(vk::KhrImagelessFramebufferFn::name()), + image_view_usage: self.device_info.api_version() >= Version::V1_1 + || self + .device_info + .supports_extension(vk::KhrMaintenance2Fn::name()), + timestamp_period: self.device_info.properties.limits.timestamp_period, + }), + vendor_id: self.device_info.properties.vendor_id, + valid_ash_memory_types, + #[cfg(feature = "naga")] + naga_options, + }; + + let device_arc = Arc::clone(&device.shared); + let queue_groups = families + .iter() + .map(|&(family, ref priorities)| { + let mut family_raw = + queue::QueueGroup::new(queue::QueueFamilyId(family.index as usize)); + for id in 0..priorities.len() { + let queue_raw = device_arc.raw.get_device_queue(family.index, id as _); + family_raw.add_queue(Queue { + raw: Arc::new(queue_raw), + device: device_arc.clone(), + swapchain_fn: swapchain_fn.clone(), + }); + } + family_raw + }) + .collect(); + + Ok(adapter::Gpu { + device, + queue_groups, + }) } fn format_properties(&self, format: Option) -> format::Properties { + let properties = unsafe { + self.instance.inner.get_physical_device_format_properties( + self.handle, + format.map_or(vk::Format::UNDEFINED, conv::map_format), + ) + }; let supports_transfer_bits = self .device_info .supports_extension(vk::KhrMaintenance1Fn::name()); @@ -1238,87 +985,6 @@ impl adapter::PhysicalDevice for PhysicalDevice { .available_features .contains(Features::SAMPLER_REDUCTION); - let (properties, drm_format_properties) = unsafe { - match self.instance.get_physical_device_properties { - None => { - let format_properties = - self.instance.inner.get_physical_device_format_properties( - self.handle, - format.map_or(vk::Format::UNDEFINED, conv::map_format), - ); - (format_properties, Vec::new()) - } - Some(ref extension) => { - let mut raw_format_modifiers: Vec = Vec::new(); - let mut drm_format_properties = - vk::DrmFormatModifierPropertiesListEXT::builder().build(); - let mut format_properties2 = vk::FormatProperties2::builder() - .push_next(&mut drm_format_properties) - .build(); - // Ash does not implement the "double call" behaviour for this function, so it is implemented here. - match extension { - ExtensionFn::Promoted => { - use ash::version::InstanceV1_1; - self.instance.inner.get_physical_device_format_properties2( - self.handle, - format.map_or(vk::Format::UNDEFINED, conv::map_format), - &mut format_properties2, - ); - raw_format_modifiers.reserve_exact(drm_format_properties.drm_format_modifier_count as usize); - drm_format_properties.p_drm_format_modifier_properties = raw_format_modifiers.as_mut_ptr(); - self.instance.inner.get_physical_device_format_properties2( - self.handle, - format.map_or(vk::Format::UNDEFINED, conv::map_format), - &mut format_properties2, - ); - raw_format_modifiers.set_len(drm_format_properties.drm_format_modifier_count as usize); - } - ExtensionFn::Extension(extension) => { - extension.get_physical_device_format_properties2_khr( - self.handle, - format.map_or(vk::Format::UNDEFINED, conv::map_format), - &mut format_properties2, - ); - raw_format_modifiers.reserve_exact(drm_format_properties.drm_format_modifier_count as usize); - drm_format_properties.p_drm_format_modifier_properties = raw_format_modifiers.as_mut_ptr(); - extension.get_physical_device_format_properties2_khr( - self.handle, - format.map_or(vk::Format::UNDEFINED, conv::map_format), - &mut format_properties2, - ); - raw_format_modifiers.set_len(drm_format_properties.drm_format_modifier_count as usize); - } - } - - let format_modifiers: Vec = raw_format_modifiers - .into_iter() - .filter_map(|format_modifier_properties| { - let format_modifier = format::DrmModifier::from( - format_modifier_properties.drm_format_modifier, - ); - if let format::DrmModifier::Unrecognized(value) = format_modifier { - error!("Unrecognized drm format modifier: {:#?}", value); - None - } else { - Some(format::DrmFormatProperties { - drm_modifier: format_modifier, - plane_count: format_modifier_properties - .drm_format_modifier_plane_count, - valid_usages: conv::map_image_features( - format_modifier_properties - .drm_format_modifier_tiling_features, - supports_transfer_bits, - supports_sampler_filter_minmax, - ), - }) - } - }) - .collect(); - (format_properties2.format_properties, format_modifiers) - } - } - }; - format::Properties { linear_tiling: conv::map_image_features( properties.linear_tiling_features, @@ -1331,7 +997,6 @@ impl adapter::PhysicalDevice for PhysicalDevice { supports_sampler_filter_minmax, ), buffer_features: conv::map_buffer_features(properties.buffer_features), - drm_format_properties, } } @@ -1418,178 +1083,6 @@ impl adapter::PhysicalDevice for PhysicalDevice { } } - fn external_buffer_properties( - &self, - usage: hal::buffer::Usage, - sparse: hal::memory::SparseFlags, - external_memory_type: external_memory::ExternalMemoryType, - ) -> external_memory::ExternalMemoryProperties { - let external_memory_type_flags: hal::external_memory::ExternalMemoryTypeFlags = - external_memory_type.into(); - let vk_external_memory_type = - vk::ExternalMemoryHandleTypeFlags::from_raw(external_memory_type_flags.bits()); - - let external_buffer_info = vk::PhysicalDeviceExternalBufferInfo::builder() - .flags(conv::map_buffer_create_flags(sparse)) - .usage(conv::map_buffer_usage(usage)) - .handle_type(vk_external_memory_type) - .build(); - - let vk_mem_properties = match self.instance.external_memory_capabilities.as_ref() { - Some(ExtensionFn::Extension(external_memory_capabilities_extension)) => { - let mut external_buffer_properties = - vk::ExternalBufferProperties::builder().build(); - unsafe { - external_memory_capabilities_extension - .get_physical_device_external_buffer_properties_khr( - self.handle, - &external_buffer_info, - &mut external_buffer_properties, - ) - }; - external_buffer_properties.external_memory_properties - } - Some(ExtensionFn::Promoted) => { - use ash::version::InstanceV1_1; - let mut external_buffer_properties = - vk::ExternalBufferProperties::builder().build(); - unsafe { - self.instance - .inner - .get_physical_device_external_buffer_properties( - self.handle, - &external_buffer_info, - &mut external_buffer_properties, - ) - } - external_buffer_properties.external_memory_properties - } - None => panic!( - "This function rely on `Feature::EXTERNAL_MEMORY`, but the feature is not enabled" - ), - }; - - let mut external_memory_properties = external_memory::ExternalMemoryProperties::empty(); - if vk_mem_properties - .external_memory_features - .contains(vk::ExternalMemoryFeatureFlags::EXPORTABLE) - { - external_memory_properties |= external_memory::ExternalMemoryProperties::EXPORTABLE; - } - - if vk_mem_properties - .external_memory_features - .contains(vk::ExternalMemoryFeatureFlags::IMPORTABLE) - { - external_memory_properties |= external_memory::ExternalMemoryProperties::IMPORTABLE; - } - - if vk_mem_properties - .export_from_imported_handle_types - .contains(vk_external_memory_type) - { - external_memory_properties |= - external_memory::ExternalMemoryProperties::EXPORTABLE_FROM_IMPORTED; - } - - external_memory_properties - } - - fn external_image_properties( - &self, - format: format::Format, - dimensions: u8, - tiling: image::Tiling, - usage: image::Usage, - view_caps: image::ViewCapabilities, - external_memory_type: external_memory::ExternalMemoryType, - ) -> Result - { - if self.instance.external_memory_capabilities.is_none() { - panic!( - "This function rely on `Feature::EXTERNAL_MEMORY`, but the feature is not enabled" - ); - } - - use ash::version::InstanceV1_1; - let external_memory_type_flags: hal::external_memory::ExternalMemoryTypeFlags = - external_memory_type.into(); - let vk_external_memory_type = - vk::ExternalMemoryHandleTypeFlags::from_raw(external_memory_type_flags.bits()); - - let mut external_image_format_info = vk::PhysicalDeviceExternalImageFormatInfo::builder() - .handle_type(vk_external_memory_type) - .build(); - let image_format_info = vk::PhysicalDeviceImageFormatInfo2::builder() - .push_next(&mut external_image_format_info) - .format(conv::map_format(format)) - .ty(match dimensions { - 1 => vk::ImageType::TYPE_1D, - 2 => vk::ImageType::TYPE_2D, - 3 => vk::ImageType::TYPE_3D, - _ => panic!("Unexpected image dimensionality: {}", dimensions), - }) - .tiling(conv::map_tiling(tiling)) - .usage(conv::map_image_usage(usage)) - .flags(conv::map_view_capabilities(view_caps)) - .build(); - - let mut external_image_format_properties = - vk::ExternalImageFormatProperties::builder().build(); - let mut image_format_properties = vk::ImageFormatProperties2::builder() - .push_next(&mut external_image_format_properties) - .build(); - - match unsafe { - self.instance - .inner - .get_physical_device_image_format_properties2( - self.handle, - &image_format_info, - &mut image_format_properties, - ) - } { - Ok(_) => { - let vk_mem_properties = external_image_format_properties.external_memory_properties; - - let mut external_memory_properties = - external_memory::ExternalMemoryProperties::empty(); - if vk_mem_properties - .external_memory_features - .contains(vk::ExternalMemoryFeatureFlags::EXPORTABLE) - { - external_memory_properties |= - external_memory::ExternalMemoryProperties::EXPORTABLE; - } - - if vk_mem_properties - .external_memory_features - .contains(vk::ExternalMemoryFeatureFlags::IMPORTABLE) - { - external_memory_properties |= - external_memory::ExternalMemoryProperties::IMPORTABLE; - } - - if vk_mem_properties - .export_from_imported_handle_types - .contains(vk_external_memory_type) - { - external_memory_properties |= - external_memory::ExternalMemoryProperties::EXPORTABLE_FROM_IMPORTED; - } - Ok(external_memory_properties) - } - Err(vk::Result::ERROR_OUT_OF_HOST_MEMORY) => Err(OutOfMemory::Host.into()), - Err(vk::Result::ERROR_OUT_OF_DEVICE_MEMORY) => Err(OutOfMemory::Device.into()), - Err(vk::Result::ERROR_FORMAT_NOT_SUPPORTED) => { - Err(external_memory::ExternalImagePropertiesError::FormatNotSupported) - } - Err(err) => { - panic!("Unexpected error: {:#?}", err); - } - } - } - fn features(&self) -> Features { self.available_features } @@ -1709,7 +1202,6 @@ impl adapter::PhysicalDevice for PhysicalDevice { let mut descriptor_indexing_capabilities = hal::DescriptorIndexingProperties::default(); let mut mesh_shader_capabilities = hal::MeshShaderProperties::default(); let mut sampler_reduction_capabilities = hal::SamplerReductionProperties::default(); - let mut external_memory_limits = hal::ExternalMemoryLimits::default(); if let Some(get_physical_device_properties) = self.instance.get_physical_device_properties.as_ref() @@ -1719,32 +1211,16 @@ impl adapter::PhysicalDevice for PhysicalDevice { let mut mesh_shader_properties = vk::PhysicalDeviceMeshShaderPropertiesNV::builder(); let mut sampler_reduction_properties = vk::PhysicalDeviceSamplerFilterMinmaxProperties::builder(); - let mut memory_host_properties = - vk::PhysicalDeviceExternalMemoryHostPropertiesEXT::builder(); - let mut physical_device_properties2 = vk::PhysicalDeviceProperties2::builder() - .push_next(&mut descriptor_indexing_properties) - .push_next(&mut mesh_shader_properties) - .push_next(&mut sampler_reduction_properties) - .push_next(&mut memory_host_properties) - .build(); - - match get_physical_device_properties { - ExtensionFn::Promoted => { - use ash::version::InstanceV1_1; - unsafe { - self.instance.inner.get_physical_device_properties2( - self.handle, - &mut physical_device_properties2, - ); - } - } - ExtensionFn::Extension(get_physical_device_properties) => unsafe { - get_physical_device_properties.get_physical_device_properties2_khr( - self.handle, - &mut physical_device_properties2, - ); - }, + unsafe { + get_physical_device_properties.get_physical_device_properties2_khr( + self.handle, + &mut vk::PhysicalDeviceProperties2::builder() + .push_next(&mut descriptor_indexing_properties) + .push_next(&mut mesh_shader_properties) + .push_next(&mut sampler_reduction_properties) + .build() as *mut _, + ); } descriptor_indexing_capabilities = hal::DescriptorIndexingProperties { @@ -1801,11 +1277,6 @@ impl adapter::PhysicalDevice for PhysicalDevice { .filter_minmax_image_component_mapping == vk::TRUE, }; - - external_memory_limits = ExternalMemoryLimits { - min_imported_host_pointer_alignment: memory_host_properties - .min_imported_host_pointer_alignment, - }; } PhysicalDeviceProperties { @@ -1816,7 +1287,6 @@ impl adapter::PhysicalDevice for PhysicalDevice { performance_caveats: Default::default(), dynamic_pipeline_states: DynamicStates::all(), downlevel: DownlevelProperties::all_enabled(), - external_memory_limits, } } @@ -1873,302 +1343,4 @@ impl adapter::PhysicalDevice for PhysicalDevice { } true } - - unsafe fn enumerate_displays(&self) -> Vec> { - let display_extension = match self.instance.display { - Some(ref display_extension) => display_extension, - None => { - error!("Direct display feature not supported"); - return Vec::new(); - } - }; - - let display_properties = - match display_extension.get_physical_device_display_properties(self.handle) { - Ok(display_properties) => display_properties, - Err(err) => { - match err { - vk::Result::ERROR_OUT_OF_HOST_MEMORY - | vk::Result::ERROR_OUT_OF_DEVICE_MEMORY => error!( - "Error returned on `get_physical_device_display_properties`: {:#?}", - err - ), - err => error!( - "Unexpected error on `get_physical_device_display_properties`: {:#?}", - err - ), - } - return Vec::new(); - } - }; - - let mut displays = Vec::new(); - for display_property in display_properties { - let supported_transforms = hal::display::SurfaceTransformFlags::from_bits( - display_property.supported_transforms.as_raw(), - ) - .unwrap(); - let display_name = if display_property.display_name.is_null() { - None - } else { - Some( - std::ffi::CStr::from_ptr(display_property.display_name) - .to_str() - .unwrap() - .to_owned(), - ) - }; - - let display_info = display::DisplayInfo { - name: display_name, - physical_dimensions: ( - display_property.physical_dimensions.width, - display_property.physical_dimensions.height, - ) - .into(), - physical_resolution: ( - display_property.physical_resolution.width, - display_property.physical_resolution.height, - ) - .into(), - supported_transforms: supported_transforms, - plane_reorder_possible: display_property.plane_reorder_possible == 1, - persistent_content: display_property.persistent_content == 1, - }; - - let display_modes = match display_extension - .get_display_mode_properties(self.handle, display_property.display) - { - Ok(display_modes) => display_modes, - Err(err) => { - match err { - vk::Result::ERROR_OUT_OF_HOST_MEMORY - | vk::Result::ERROR_OUT_OF_DEVICE_MEMORY => error!( - "Error returned on `get_display_mode_properties`: {:#?}", - err - ), - err => error!( - "Unexpected error on `get_display_mode_properties`: {:#?}", - err - ), - } - return Vec::new(); - } - } - .iter() - .map(|display_mode_properties| display::DisplayMode { - handle: native::DisplayMode(display_mode_properties.display_mode), - resolution: ( - display_mode_properties.parameters.visible_region.width, - display_mode_properties.parameters.visible_region.height, - ), - refresh_rate: display_mode_properties.parameters.refresh_rate, - }) - .collect(); - - let display = display::Display { - handle: native::Display(display_property.display), - info: display_info, - modes: display_modes, - }; - - displays.push(display); - } - return displays; - } - - unsafe fn enumerate_compatible_planes( - &self, - display: &display::Display, - ) -> Vec { - let display_extension = match self.instance.display { - Some(ref display_extension) => display_extension, - None => { - error!("Direct display feature not supported"); - return Vec::new(); - } - }; - - match display_extension.get_physical_device_display_plane_properties(self.handle) { - Ok(planes_properties) => { - let mut planes = Vec::new(); - for index in 0..planes_properties.len() { - let compatible_displays = match display_extension - .get_display_plane_supported_displays(self.handle, index as u32) - { - Ok(compatible_displays) => compatible_displays, - Err(err) => { - match err { - vk::Result::ERROR_OUT_OF_HOST_MEMORY | vk::Result::ERROR_OUT_OF_DEVICE_MEMORY => - error!("Error returned on `get_display_plane_supported_displays`: {:#?}",err), - err=>error!("Unexpected error on `get_display_plane_supported_displays`: {:#?}",err) - } - return Vec::new(); - } - }; - if compatible_displays.contains(&display.handle.0) { - planes.push(display::Plane { - handle: index as u32, - z_index: planes_properties[index].current_stack_index, - }); - } - } - planes - } - Err(err) => { - match err { - vk::Result::ERROR_OUT_OF_HOST_MEMORY - | vk::Result::ERROR_OUT_OF_DEVICE_MEMORY => error!( - "Error returned on `get_physical_device_display_plane_properties`: {:#?}", - err - ), - err => error!( - "Unexpected error on `get_physical_device_display_plane_properties`: {:#?}", - err - ), - } - Vec::new() - } - } - } - - unsafe fn create_display_mode( - &self, - display: &display::Display, - resolution: (u32, u32), - refresh_rate: u32, - ) -> Result, display::DisplayModeError> { - let display_extension = self.instance.display.as_ref().unwrap(); - - let display_mode_ci = vk::DisplayModeCreateInfoKHR::builder() - .parameters(vk::DisplayModeParametersKHR { - visible_region: vk::Extent2D { - width: resolution.0, - height: resolution.1, - }, - refresh_rate: refresh_rate, - }) - .build(); - - match display_extension.create_display_mode( - self.handle, - display.handle.0, - &display_mode_ci, - None, - ) { - Ok(display_mode_handle) => Ok(display::DisplayMode { - handle: native::DisplayMode(display_mode_handle), - resolution: resolution, - refresh_rate: refresh_rate, - }), - Err(vk::Result::ERROR_OUT_OF_HOST_MEMORY) => return Err(OutOfMemory::Host.into()), - Err(vk::Result::ERROR_OUT_OF_DEVICE_MEMORY) => return Err(OutOfMemory::Device.into()), - Err(vk::Result::ERROR_INITIALIZATION_FAILED) => { - return Err(display::DisplayModeError::UnsupportedDisplayMode.into()) - } - Err(err) => panic!("Unexpected error on `create_display_mode`: {:#?}", err), - } - } - - unsafe fn create_display_plane<'a>( - &self, - display_mode: &'a display::DisplayMode, - plane: &'a display::Plane, - ) -> Result, OutOfMemory> { - let display_extension = self.instance.display.as_ref().unwrap(); - - let display_plane_capabilities = match display_extension.get_display_plane_capabilities( - self.handle, - display_mode.handle.0, - plane.handle, - ) { - Ok(display_plane_capabilities) => display_plane_capabilities, - Err(vk::Result::ERROR_OUT_OF_HOST_MEMORY) => return Err(OutOfMemory::Host.into()), - Err(vk::Result::ERROR_OUT_OF_DEVICE_MEMORY) => return Err(OutOfMemory::Device.into()), - Err(err) => panic!( - "Unexpected error on `get_display_plane_capabilities`: {:#?}", - err - ), - }; - - let mut supported_alpha_capabilities = Vec::new(); - if display_plane_capabilities - .supported_alpha - .contains(vk::DisplayPlaneAlphaFlagsKHR::OPAQUE) - { - supported_alpha_capabilities.push(display::DisplayPlaneAlpha::Opaque); - } - if display_plane_capabilities - .supported_alpha - .contains(vk::DisplayPlaneAlphaFlagsKHR::GLOBAL) - { - supported_alpha_capabilities.push(display::DisplayPlaneAlpha::Global(1.0)); - } - if display_plane_capabilities - .supported_alpha - .contains(vk::DisplayPlaneAlphaFlagsKHR::PER_PIXEL) - { - supported_alpha_capabilities.push(display::DisplayPlaneAlpha::PerPixel); - } - if display_plane_capabilities - .supported_alpha - .contains(vk::DisplayPlaneAlphaFlagsKHR::PER_PIXEL_PREMULTIPLIED) - { - supported_alpha_capabilities.push(display::DisplayPlaneAlpha::PerPixelPremultiplied); - } - - Ok(display::DisplayPlane { - plane: &plane, - display_mode: &display_mode, - supported_alpha: supported_alpha_capabilities, - src_position: std::ops::Range { - start: ( - display_plane_capabilities.min_src_position.x, - display_plane_capabilities.min_src_position.x, - ) - .into(), - end: ( - display_plane_capabilities.max_src_position.x, - display_plane_capabilities.max_src_position.x, - ) - .into(), - }, - src_extent: std::ops::Range { - start: ( - display_plane_capabilities.min_src_extent.width, - display_plane_capabilities.min_src_extent.height, - ) - .into(), - end: ( - display_plane_capabilities.max_src_extent.width, - display_plane_capabilities.max_src_extent.height, - ) - .into(), - }, - dst_position: std::ops::Range { - start: ( - display_plane_capabilities.min_dst_position.x, - display_plane_capabilities.min_dst_position.x, - ) - .into(), - end: ( - display_plane_capabilities.max_dst_position.x, - display_plane_capabilities.max_dst_position.x, - ) - .into(), - }, - dst_extent: std::ops::Range { - start: ( - display_plane_capabilities.min_dst_extent.width, - display_plane_capabilities.min_dst_extent.height, - ) - .into(), - end: ( - display_plane_capabilities.max_dst_extent.width, - display_plane_capabilities.max_dst_extent.height, - ) - .into(), - }, - }) - } } diff --git a/third_party/rust/gfx-hal/.cargo-checksum.json b/third_party/rust/gfx-hal/.cargo-checksum.json index 3aac1ab61c47..c3a555ffdd13 100644 --- a/third_party/rust/gfx-hal/.cargo-checksum.json +++ b/third_party/rust/gfx-hal/.cargo-checksum.json @@ -1 +1 @@ -{"files":{"Cargo.toml":"3cb968db53c3eb98bcee534a6963a2f1172af2ba668f61ef05fda95f468018ea","src/adapter.rs":"05d3ffef3d5562a08050254f0135bf9f84c4ea4f79d35934352cc64ac6732681","src/buffer.rs":"076031b8d4d8a7478888f4e8f5b142b916118832aedc01605066fe54d7b8840a","src/command/clear.rs":"94c4c7188e925275850359296fa0d6e523b19a6e0b868faea996867e45454a3b","src/command/mod.rs":"73e891e27d591405a881455394865d522c09179bfd2711e075684f314a3c527a","src/command/structs.rs":"00b5850540ae21227c6578866e86cc741d074601239f8b1bbd0342a5e5f74623","src/device.rs":"06110c28244a573042d04ea4f47361eeafd482486a7e79b90195c9c9cfdee5d8","src/display/control.rs":"cf9a4c11b07135be74ec967068628096305e7a6901e87138d62c698f21c7e658","src/display/mod.rs":"a275ca98c939e1051cb2635f1546ede7c30a888f1872be25e2ea131b4e4181da","src/external_memory/errors.rs":"dd2f300fbf1c19d3a7f7ea499d6c25ff1af179d4cad2a11583743d52d97728da","src/external_memory/mod.rs":"401b2d9777147d2c4a03895450552bfaff64f58e0438086bc9a597677d7dcac1","src/format.rs":"c8e7ab9d9bc6acc5bc5b2399fc24aa95ade07ffbd93e7a0a257af236b5451656","src/image.rs":"159faa0e3ba4405dbfe619f362157130b2abda0feaa85afbe6b2f7773c3390f6","src/lib.rs":"ee6dd97fea4963744c6d1280c93491c3d3fc6963fbd8716b89e3dbc32e793453","src/memory.rs":"6d9c78ea00266c71fcf63b372ab0e1329043802b83e579b8a33958335f17bd7a","src/pass.rs":"10e60b840ceca18d1a37003653d14e47c1d9e31948dab554f933c1778e46639f","src/pool.rs":"f8517834b3fffe99a7147188cbc65ffcdb582cded4280f14af0878c34cf836f6","src/pso/compute.rs":"93814076966c4e4658cada975c2615fd8dc919e5c96cb265cc6da102191cc6c8","src/pso/descriptor.rs":"60dcde7c74b41cabe72117e640607302d14dbe4fd42b95f8e9933b0d410fd4c2","src/pso/graphics.rs":"2e4b585e63c54fa3c5b53848d8b7209bc65eab156abb28c326ae9ac33fd034fb","src/pso/input_assembler.rs":"0f75dcbc26e253a3ad531089b969e25b9cc86ec9c938f056a62e82ad36383ccb","src/pso/mod.rs":"4680a7156654941cd9f911cefa6548ea7fd814a02c4c999bb7486398a10ec43c","src/pso/output_merger.rs":"cfd3b9ba8b8ba85c497e8bdb9f594a1bd83b1c12b99f40c833e1be4ceadace80","src/pso/specialization.rs":"ab4c4d9a4907e9aa12ab2bbb92a47f52a2a0ee3dc00d0c1c72c438282161b039","src/query.rs":"5e168845383e97081b23e88e2c287d4bf0318db4e35d9b55c820376c24e99ee1","src/queue/family.rs":"d2ffb5b3b90591796df4ef43895e70e8607c6fc233e13313accba438b8f264b6","src/queue/mod.rs":"c13ad81215a2cc05c3a4d2c936247319398e9a6c8407b4a816a81d1d4e48f480","src/window.rs":"87a281549ea75802f8ffc2e0a0647bb216c9f9d6f85074037a91f0dd9fa7708a"},"package":"7fbb575ea793dd0507b3082f4f2cde62dc9f3cebd98f5cd49ba2a4da97a976fd"} \ No newline at end of file +{"files":{"Cargo.toml":"c6d65851bac36dc5b7d79805ca109ec2ab9b9926958747e37301d7d4fa1ed815","src/adapter.rs":"301e36e78467e229bd7f140336fa4a64005a289f6a2a6139b1182e8e0d37f146","src/buffer.rs":"076031b8d4d8a7478888f4e8f5b142b916118832aedc01605066fe54d7b8840a","src/command/clear.rs":"94c4c7188e925275850359296fa0d6e523b19a6e0b868faea996867e45454a3b","src/command/mod.rs":"73e891e27d591405a881455394865d522c09179bfd2711e075684f314a3c527a","src/command/structs.rs":"00b5850540ae21227c6578866e86cc741d074601239f8b1bbd0342a5e5f74623","src/device.rs":"9ce852b6ba743616c70047b186a6550521b7c79f1d0687bbfb3d43443c408b95","src/format.rs":"5400f77897740ad6cf13d8d26c4a032cc7629ba7b7ff8628ca25ad67c8e79f8c","src/image.rs":"159faa0e3ba4405dbfe619f362157130b2abda0feaa85afbe6b2f7773c3390f6","src/lib.rs":"89f6511bea451cca3efa020a7e0f307578b2dfb83b7d7697337e466b1c53f7f3","src/memory.rs":"6d9c78ea00266c71fcf63b372ab0e1329043802b83e579b8a33958335f17bd7a","src/pass.rs":"10e60b840ceca18d1a37003653d14e47c1d9e31948dab554f933c1778e46639f","src/pool.rs":"f8517834b3fffe99a7147188cbc65ffcdb582cded4280f14af0878c34cf836f6","src/pso/compute.rs":"93814076966c4e4658cada975c2615fd8dc919e5c96cb265cc6da102191cc6c8","src/pso/descriptor.rs":"60dcde7c74b41cabe72117e640607302d14dbe4fd42b95f8e9933b0d410fd4c2","src/pso/graphics.rs":"2e4b585e63c54fa3c5b53848d8b7209bc65eab156abb28c326ae9ac33fd034fb","src/pso/input_assembler.rs":"0f75dcbc26e253a3ad531089b969e25b9cc86ec9c938f056a62e82ad36383ccb","src/pso/mod.rs":"4680a7156654941cd9f911cefa6548ea7fd814a02c4c999bb7486398a10ec43c","src/pso/output_merger.rs":"cfd3b9ba8b8ba85c497e8bdb9f594a1bd83b1c12b99f40c833e1be4ceadace80","src/pso/specialization.rs":"ab4c4d9a4907e9aa12ab2bbb92a47f52a2a0ee3dc00d0c1c72c438282161b039","src/query.rs":"5e168845383e97081b23e88e2c287d4bf0318db4e35d9b55c820376c24e99ee1","src/queue/family.rs":"d2ffb5b3b90591796df4ef43895e70e8607c6fc233e13313accba438b8f264b6","src/queue/mod.rs":"c13ad81215a2cc05c3a4d2c936247319398e9a6c8407b4a816a81d1d4e48f480","src/window.rs":"ded4311ea29a9d5cadd34e5886196d83392b3113abd3889b48eb85a18e630699"},"package":null} \ No newline at end of file diff --git a/third_party/rust/gfx-hal/Cargo.toml b/third_party/rust/gfx-hal/Cargo.toml index 448e8d04435f..01a32ff0bb82 100644 --- a/third_party/rust/gfx-hal/Cargo.toml +++ b/third_party/rust/gfx-hal/Cargo.toml @@ -1,52 +1,29 @@ -# THIS FILE IS AUTOMATICALLY GENERATED BY CARGO -# -# When uploading crates to the registry Cargo will automatically -# "normalize" Cargo.toml files for maximal compatibility -# with all versions of Cargo and also rewrite `path` dependencies -# to registry (e.g., crates.io) dependencies -# -# If you believe there's an error in this file please file an -# issue against the rust-lang/cargo repository. If you're -# editing this file be aware that the upstream Cargo.toml -# will likely look very different (and much more reasonable) - [package] -edition = "2018" name = "gfx-hal" -version = "0.9.0" -authors = ["The Gfx-rs Developers"] +version = "0.8.0" description = "gfx-rs hardware abstraction layer" homepage = "https://github.com/gfx-rs/gfx" -documentation = "https://docs.rs/gfx-hal" +repository = "https://github.com/gfx-rs/gfx" keywords = ["graphics"] license = "MIT OR Apache-2.0" -repository = "https://github.com/gfx-rs/gfx" +authors = ["The Gfx-rs Developers"] +documentation = "https://docs.rs/gfx-hal" +workspace = "../.." +edition = "2018" + +[features] +unstable = [] [lib] name = "gfx_hal" path = "src/lib.rs" -[dependencies.bitflags] -version = "1.0" -[dependencies.external_memory] -version = "0.0.1" -package = "external-memory" +[dependencies] +bitflags = "1.0" +naga = { git = "https://github.com/gfx-rs/naga", tag = "gfx-25" } +raw-window-handle = "0.3" +serde = { version = "1", features = ["serde_derive"], optional = true } +thiserror = "1" -[dependencies.naga] -version = "0.5" - -[dependencies.raw-window-handle] -version = "0.3" - -[dependencies.serde] -version = "1" -features = ["serde_derive"] -optional = true - -[dependencies.thiserror] -version = "1" -[dev-dependencies.gfx-backend-empty] -version = "0.9" - -[features] -unstable = [] +[dev-dependencies] +gfx-backend-empty = { path = "../backend/empty", version = "0.8" } diff --git a/third_party/rust/gfx-hal/src/adapter.rs b/third_party/rust/gfx-hal/src/adapter.rs index ae7b918bc9c8..7490873f98a4 100644 --- a/third_party/rust/gfx-hal/src/adapter.rs +++ b/third_party/rust/gfx-hal/src/adapter.rs @@ -9,7 +9,7 @@ //! of that [backend][crate::Backend]. use crate::{ - buffer, device, display, external_memory, format, image, memory, + device, format, image, memory, queue::{QueueGroup, QueuePriority}, Backend, Features, PhysicalDeviceProperties, }; @@ -115,43 +115,6 @@ pub trait PhysicalDevice: fmt::Debug + Any + Send + Sync { /// Fetch details for the memory regions provided by the device. fn memory_properties(&self) -> MemoryProperties; - /// Get external buffer properties. The parameters specify how the buffer is going to used. - /// # Arguments - /// - /// * `usage` - the usage of the buffer. - /// * `sparse` - the sparse flags of the buffer. - /// * `memory_type` - the external memory type for the buffer. - fn external_buffer_properties( - &self, - usage: buffer::Usage, - sparse: memory::SparseFlags, - memory_type: external_memory::ExternalMemoryType, - ) -> external_memory::ExternalMemoryProperties; - - /// Get external image properties. The parameters specify how the image is going to used. - /// # Arguments - /// - /// * `format` - the format of the image. - /// * `dimensions` - the dimensions of the image. - /// * `tiling` - the tiling mode of the image. - /// * `usage` - the usage of the image. - /// * `view_caps` - the view capabilities of the image. - /// * `external_memory_type` - the external memory type for the image. - /// # Errors - /// - /// - Returns `OutOfMemory` if the implementation goes out of memory during the operation. - /// - Returns `FormatNotSupported` if the implementation does not support the requested image format. - /// - fn external_image_properties( - &self, - format: format::Format, - dimensions: u8, - tiling: image::Tiling, - usage: image::Usage, - view_caps: image::ViewCapabilities, - external_memory_type: external_memory::ExternalMemoryType, - ) -> Result; - /// Returns the features of this `PhysicalDevice`. This usually depends on the graphics API being /// used, as well as the actual platform underneath. fn features(&self) -> Features; @@ -164,52 +127,6 @@ pub trait PhysicalDevice: fmt::Debug + Any + Send + Sync { fn is_valid_cache(&self, _cache: &[u8]) -> bool { false } - - /// Enumerate active displays [surface][display::Display] from display. - /// Please notice that, even if a system has displays attached, they could be not returned because they are managed by some other components. - /// This function only return the display that are available to be managed by the current application. - /// Since, generally, while compositor are running they take the control of every display connected, it could be better to run the application directly from the tty to avoid the return of an empty list. - /// # Arguments - /// - /// * `adapter` - the [adapter][crate::adapter::Adapter] from which the displays will be enumerated. - unsafe fn enumerate_displays(&self) -> Vec>; - - /// Enumerate compatibles planes with the provided display. - /// # Arguments - /// - /// * `display` - display on which the the compatible planes will be listed. - unsafe fn enumerate_compatible_planes( - &self, - display: &display::Display, - ) -> Vec; - - /// Create a new display mode from a display, a resolution, a refresh_rate and the plane index. - /// If the builtin display modes does not satisfy the requirements, this function will try to create a new one. - /// # Arguments - /// - /// * `display` - display on which the display mode will be created. - /// * `resolution` - the desired resolution. - /// * `refresh_rate` - the desired refresh_rate. - unsafe fn create_display_mode( - &self, - display: &display::Display, - resolution: (u32, u32), - refresh_rate: u32, - ) -> Result, display::DisplayModeError>; - - /// Create a display plane from a display, a resolution, a refresh_rate and a plane. - /// If the builtin display modes does not satisfy the requirements, this function will try to create a new one. - /// # Arguments - /// - /// * `display` - display on which the display plane will be created. - /// * `plane` - the plane on which the surface will be rendered on. - /// * `resolution` - the desired resolution. - /// * `refresh_rate` - the desired refresh_rate. - unsafe fn create_display_plane<'a>( - &self, - display: &'a display::DisplayMode, - plane: &'a display::Plane, - ) -> Result, device::OutOfMemory>; } /// The type of a physical graphics device diff --git a/third_party/rust/gfx-hal/src/device.rs b/third_party/rust/gfx-hal/src/device.rs index c85d1c219a20..8cafba88bd3c 100644 --- a/third_party/rust/gfx-hal/src/device.rs +++ b/third_party/rust/gfx-hal/src/device.rs @@ -12,7 +12,7 @@ //! and is used to actually do things. use crate::{ - buffer, display, external_memory, format, image, memory, + buffer, format, image, memory, memory::{Requirements, Segment}, pass, pool::CommandPoolCreateFlags, @@ -714,165 +714,6 @@ pub trait Device: fmt::Debug + Any + Send + Sync { /// validation layers that can print a friendly name when referring to objects in error messages unsafe fn set_pipeline_layout_name(&self, pipeline_layout: &mut B::PipelineLayout, name: &str); - /// Control the power state of the provided display - unsafe fn set_display_power_state( - &self, - display: &display::Display, - power_state: &display::control::PowerState, - ) -> Result<(), display::control::DisplayControlError>; - - /// Register device event - unsafe fn register_device_event( - &self, - device_event: &display::control::DeviceEvent, - fence: &mut B::Fence, - ) -> Result<(), display::control::DisplayControlError>; - - /// Register display event - unsafe fn register_display_event( - &self, - display: &display::Display, - display_event: &display::control::DisplayEvent, - fence: &mut B::Fence, - ) -> Result<(), display::control::DisplayControlError>; - - /// Create, allocate and bind a buffer that can be exported. - /// # Arguments - /// - /// * `external_memory_type_flags` - the external memory types the buffer will be valid to be used. - /// * `usage` - the usage of the buffer. - /// * `sparse` - the sparse flags of the buffer. - /// * `type_mask` - a memory type mask containing all the desired memory type ids. - /// * `size` - the size of the buffer. - /// # Errors - /// - /// - Returns `OutOfMemory` if the implementation goes out of memory during the operation. - /// - Returns `TooManyObjects` if the implementation can allocate buffers no more. - /// - Returns `NoValidMemoryTypeId` if the no one of the desired memory type id is valid for the implementation. - /// - Returns `InvalidExternalHandle` if the requested external memory type is invalid for the implementation. - /// - unsafe fn create_allocate_external_buffer( - &self, - external_memory_type_flags: external_memory::ExternalBufferMemoryType, - usage: buffer::Usage, - sparse: memory::SparseFlags, - type_mask: u32, - size: u64, - ) -> Result<(B::Buffer, B::Memory), external_memory::ExternalResourceError>; - - /// Import external memory as binded buffer and memory. - /// # Arguments - /// - /// * `external_memory` - the external memory types the buffer will be valid to be used. - /// * `usage` - the usage of the buffer. - /// * `sparse` - the sparse flags of the buffer. - /// * `type_mask` - a memory type mask containing all the desired memory type ids. - /// * `size` - the size of the buffer. - /// # Errors - /// - /// - Returns `OutOfMemory` if the implementation goes out of memory during the operation. - /// - Returns `TooManyObjects` if the implementation can allocate buffers no more. - /// - Returns `NoValidMemoryTypeId` if the no one of the desired memory type id is valid for the implementation. - /// - Returns `InvalidExternalHandle` if the requested external memory type is invalid for the implementation. - /// - unsafe fn import_external_buffer( - &self, - external_memory: external_memory::ExternalBufferMemory, - usage: buffer::Usage, - sparse: memory::SparseFlags, - type_mask: u32, - size: u64, - ) -> Result<(B::Buffer, B::Memory), external_memory::ExternalResourceError>; - - /// Create, allocate and bind an image that can be exported. - /// # Arguments - /// - /// * `external_memory` - the external memory types the image will be valid to be used. - /// * `kind` - the image kind. - /// * `mip_levels` - the mip levels of the image. - /// * `format` - the format of the image. - /// * `tiling` - the tiling mode of the image. - /// * `dimensions` - the dimensions of the image. - /// * `usage` - the usage of the image. - /// * `sparse` - the sparse flags of the image. - /// * `view_caps` - the view capabilities of the image. - /// * `type_mask` - a memory type mask containing all the desired memory type ids. - /// # Errors - /// - /// - Returns `OutOfMemory` if the implementation goes out of memory during the operation. - /// - Returns `TooManyObjects` if the implementation can allocate images no more. - /// - Returns `NoValidMemoryTypeId` if the no one of the desired memory type id is valid for the implementation. - /// - Returns `InvalidExternalHandle` if the requested external memory type is invalid for the implementation. - /// - unsafe fn create_allocate_external_image( - &self, - external_memory_type: external_memory::ExternalImageMemoryType, - kind: image::Kind, - mip_levels: image::Level, - format: format::Format, - tiling: image::Tiling, - usage: image::Usage, - sparse: memory::SparseFlags, - view_caps: image::ViewCapabilities, - type_mask: u32, - ) -> Result<(B::Image, B::Memory), external_memory::ExternalResourceError>; - - /// Import external memory as binded image and memory. - /// # Arguments - /// - /// * `external_memory` - the external memory types the image will be valid to be used. - /// * `kind` - the image kind. - /// * `mip_levels` - the mip levels of the image. - /// * `format` - the format of the image. - /// * `tiling` - the tiling mode of the image. - /// * `dimensions` - the dimensions of the image. - /// * `usage` - the usage of the image. - /// * `sparse` - the sparse flags of the image. - /// * `view_caps` - the view capabilities of the image. - /// * `type_mask` - a memory type mask containing all the desired memory type ids. - /// # Errors - /// - /// - Returns `OutOfMemory` if the implementation goes out of memory during the operation. - /// - Returns `TooManyObjects` if the implementation can allocate images no more. - /// - Returns `NoValidMemoryTypeId` if the no one of the desired memory type id is valid for the implementation. - /// - Returns `InvalidExternalHandle` if the requested external memory type is invalid for the implementation. - /// - unsafe fn import_external_image( - &self, - external_memory: external_memory::ExternalImageMemory, - kind: image::Kind, - mip_levels: image::Level, - format: format::Format, - tiling: image::Tiling, - usage: image::Usage, - sparse: memory::SparseFlags, - view_caps: image::ViewCapabilities, - type_mask: u32, - ) -> Result<(B::Image, B::Memory), external_memory::ExternalResourceError>; - - /// Export memory as os type (Fd, Handle or Ptr) based on the requested external memory type. - /// # Arguments - /// - /// * `external_memory_type` - the external memory type the memory will be exported to. - /// * `memory` - the memory object. - /// # Errors - /// - /// - Returns `OutOfMemory` if the implementation goes out of memory during the operation. - /// - Returns `TooManyObjects` if the implementation can allocate images no more. - /// - Returns `InvalidExternalHandle` if the requested external memory type is invalid for the implementation. - /// - unsafe fn export_memory( - &self, - external_memory_type: external_memory::ExternalMemoryType, - memory: &B::Memory, - ) -> Result; - - /// Retrieve the underlying drm format modifier from an image, if any. - /// # Arguments - /// - /// * `image` - the image object. - unsafe fn drm_format_modifier(&self, image: &B::Image) -> Option; - /// Starts frame capture. fn start_capture(&self); diff --git a/third_party/rust/gfx-hal/src/display/control.rs b/third_party/rust/gfx-hal/src/display/control.rs deleted file mode 100644 index 8112a1aafc4f..000000000000 --- a/third_party/rust/gfx-hal/src/display/control.rs +++ /dev/null @@ -1,39 +0,0 @@ -//! Display power control structures. - -/// Error occurring while controlling the display power. -#[derive(Clone, Debug, PartialEq, thiserror::Error)] -pub enum DisplayControlError { - /// Host memory exhausted. - #[error("Out of host memory")] - OutOfHostMemory, - /// The feature is unsupported by the device - #[error("The feature is unsupported by the device")] - UnsupportedFeature, -} - -/// Available power states of a display. -#[derive(Clone, Debug, PartialEq)] -pub enum PowerState { - /// Specifies that the display is powered down - Off, - /// Specifies that the display is put into a low power mode, from which it may be able to transition back to PowerState::On more quickly than if it were in PowerState::Off. - /// This state may be the same as PowerState::Off - Suspend, - /// Specifies that the display is powered on. - On, -} - -/// Device event -#[derive(Clone, Debug, PartialEq)] -pub enum DeviceEvent { - /// Specifies that the fence is signaled when a display is plugged into or unplugged from the specified device. - /// Applications can use this notification to determine when they need to re-enumerate the available displays on a device. - DisplayHotplug, -} - -/// Device event -#[derive(Clone, Debug, PartialEq)] -pub enum DisplayEvent { - /// Specifies that the fence is signaled when the first pixel of the next display refresh cycle leaves the display engine for the display. - FirstPixelOut, -} diff --git a/third_party/rust/gfx-hal/src/display/mod.rs b/third_party/rust/gfx-hal/src/display/mod.rs deleted file mode 100644 index 272d99ec64ae..000000000000 --- a/third_party/rust/gfx-hal/src/display/mod.rs +++ /dev/null @@ -1,245 +0,0 @@ -//! Displays. -//! -//! A display represent a physical display collected from an Adapter - -use crate::{ - window::{Extent2D, Offset2D}, - Backend, -}; -use std::ops::Range; - -pub mod control; - -bitflags! { - /** - List of the hardware display transformations - */ - pub struct SurfaceTransformFlags : u32 { - /// Specify that image content is presented without being transformed. - const IDENTITY = 0x00000001; - /// Specify that image content is rotated 90 degrees clockwise. - const ROTATE_90 = 0x00000002; - /// Specify that image content is rotated 180 degrees clockwise. - const ROTATE_180 = 0x00000004; - /// Specify that image content is rotated 270 degrees clockwise. - const ROTATE_270 = 0x00000008; - /// Specify that image content is mirrored horizontally. - const HORIZONTAL_MIRROR = 0x00000010; - /// Specify that image content is mirrored horizontally, then rotated 90 degrees clockwise. - const HORIZONTAL_MIRROR_ROTATE_90 = 0x00000020; - /// Specify that image content is mirrored horizontally, then rotated 180 degrees clockwise. - const HORIZONTAL_MIRROR_ROTATE_180 = 0x00000040; - /// Specify that image content is mirrored horizontally, then rotated 270 degrees clockwise. - const HORIZONTAL_MIRROR_ROTATE_270 = 0x00000080; - /// Specify that the presentation transform is not specified, and is instead determined by platform-specific considerations and mechanisms outside Vulkan. - const INHERIT = 0x00000100; - } -} -impl From for SurfaceTransformFlags { - fn from(surface_transformation: SurfaceTransform) -> Self { - match surface_transformation { - SurfaceTransform::Identity => Self::IDENTITY, - SurfaceTransform::Rotate90 => Self::ROTATE_90, - SurfaceTransform::Rotate180 => Self::ROTATE_180, - SurfaceTransform::Rotate270 => Self::ROTATE_270, - SurfaceTransform::HorizontalMirror => Self::HORIZONTAL_MIRROR, - SurfaceTransform::HorizontalMirrorRotate90 => Self::HORIZONTAL_MIRROR_ROTATE_90, - SurfaceTransform::HorizontalMirrorRotate180 => Self::HORIZONTAL_MIRROR_ROTATE_180, - SurfaceTransform::HorizontalMirrorRotate270 => Self::HORIZONTAL_MIRROR_ROTATE_270, - SurfaceTransform::Inherit => Self::INHERIT, - } - } -} - -#[derive(Debug, PartialEq)] -#[allow(non_camel_case_types)] -/** -List of the hardware display transformations -*/ -pub enum SurfaceTransform { - /// Specify that image content is presented without being transformed. - Identity, - /// Specify that image content is rotated 90 degrees clockwise. - Rotate90, - /// Specify that image content is rotated 180 degrees clockwise. - Rotate180, - /// Specify that image content is rotated 270 degrees clockwise. - Rotate270, - /// Specify that image content is mirrored horizontally. - HorizontalMirror, - /// Specify that image content is mirrored horizontally, then rotated 90 degrees clockwise. - HorizontalMirrorRotate90, - /// Specify that image content is mirrored horizontally, then rotated 180 degrees clockwise. - HorizontalMirrorRotate180, - /// Specify that image content is mirrored horizontally, then rotated 270 degrees clockwise. - HorizontalMirrorRotate270, - /// Specify that the presentation transform is not specified, and is instead determined by platform-specific considerations and mechanisms outside Vulkan. - Inherit, -} -impl Default for SurfaceTransform { - fn default() -> Self { - Self::Identity - } -} - -/** -General information about the a [display][Display]. -*/ -#[derive(Debug)] -pub struct DisplayInfo { - /// Name of the display. Generally, this will be the name provided by the display’s EDID. - pub name: Option, - /// Physical width and height of the visible portion of the display, in millimeters. - pub physical_dimensions: Extent2D, - /// Physical, native, or preferred resolution of the display. - pub physical_resolution: Extent2D, - /// Description of the supported transforms by the display. - pub supported_transforms: SurfaceTransformFlags, - /// Tells whether the planes on the display can have their z order changed. If true, the application can re-arrange the planes on this display in any order relative to each other. - pub plane_reorder_possible: bool, - /// Tells whether the display supports self-refresh/internal buffering. If true, the application can submit persistent present operations on swapchains created against this display. - pub persistent_content: bool, -} - -bitflags! { - /** - Alpha mode used in display surface creation - */ - pub struct DisplayPlaneAlphaFlags : u32 { - /// Specifies that the source image will be treated as opaque - const OPAQUE = 1; - /// Specifies that the provided global alpha value will be applied to all pixels in the source image. - const GLOBAL = 2; - /// Specifies that the alpha value will be determined by the alpha channel of the source image’s pixels. - /// If the source format contains no alpha values, no blending will be applied. The source alpha values are not premultiplied into the source image’s other color channels. - const PER_PIXEL = 4; - /// Equivalent to PerPixel, except the source alpha values are assumed to be premultiplied into the source image’s other color channels. - const PER_PIXEL_PREMULTIPLIED = 8; - } -} -impl From for DisplayPlaneAlphaFlags { - fn from(display_plane_alpha: DisplayPlaneAlpha) -> Self { - match display_plane_alpha { - DisplayPlaneAlpha::Opaque => Self::OPAQUE, - DisplayPlaneAlpha::Global(_) => Self::GLOBAL, - DisplayPlaneAlpha::PerPixel => Self::PER_PIXEL, - DisplayPlaneAlpha::PerPixelPremultiplied => Self::PER_PIXEL_PREMULTIPLIED, - } - } -} - -/** -Alpha mode used in display surface creation -*/ -#[derive(Debug)] -#[allow(non_camel_case_types)] -pub enum DisplayPlaneAlpha { - /// Specifies that the source image will be treated as opaque - Opaque, - /// Specifies that the provided global alpha value will be applied to all pixels in the source image. - Global(f32), - /// Specifies that the alpha value will be determined by the alpha channel of the source image’s pixels. If the source format contains no alpha values, no blending will be applied. The source alpha values are not premultiplied into the source image’s other color channels. - PerPixel, - /// Equivalent to PerPixel, except the source alpha values are assumed to be premultiplied into the source image’s other color channels. - PerPixelPremultiplied, -} - -impl Default for DisplayPlaneAlpha { - fn default() -> Self { - Self::Opaque - } -} - -/// Error occurring on displays operations. -#[derive(Clone, Debug, PartialEq, thiserror::Error)] -pub enum DisplayError { - /// Out of either host or device memory. - #[error(transparent)] - OutOfMemory(#[from] crate::device::OutOfMemory), - - /// Unsupported feature - #[error("Unsupported feature")] - UnsupportedFeature, -} - -/// Error occurring on display modes operations. -#[derive(Clone, Debug, PartialEq, thiserror::Error)] -pub enum DisplayModeError { - /// Out of either host or device memory. - #[error(transparent)] - OutOfMemory(#[from] crate::device::OutOfMemory), - - /// Unsupported resolution and refresh rate combination - #[error("Unsupported resolution and refresh rate combination")] - UnsupportedDisplayMode, -} - -/// Error occurring while creating a display plane surface. -#[derive(Clone, Debug, PartialEq, thiserror::Error)] -pub enum DisplayPlaneSurfaceError { - /// Out of either host or device memory. - #[error(transparent)] - OutOfMemory(#[from] crate::device::OutOfMemory), - - /// Unsupported feature - #[error("Unsupported feature")] - UnsupportedFeature, -} - -/** -Representation of a display -*/ -#[derive(Debug)] -pub struct Display { - /// The display handle. - pub handle: B::Display, - /// General informations about this display. - pub info: DisplayInfo, - /// Builtin display modes - pub modes: Vec>, -} - -/** -General information about the a [DisplayMode][DisplayMode]. -*/ -#[derive(Debug)] -pub struct DisplayMode { - /// The display mode handle - pub handle: B::DisplayMode, - /// Resolution - pub resolution: (u32, u32), - /// Refresh rate - pub refresh_rate: u32, -} - -/** -Representation of a plane -*/ -#[derive(Debug)] -pub struct Plane { - /// The plane handle. - pub handle: u32, - /// The current index on the z stack. - pub z_index: u32, -} - -/** -Represent a combination of [display mode][DisplayMode] (so [display][Display] and resolution) and a plane -*/ -#[derive(Debug)] -pub struct DisplayPlane<'a, B: Backend> { - /// Display mode - pub display_mode: &'a DisplayMode, - /// Plane index - pub plane: &'a Plane, - /// Supported alpha capabilities - pub supported_alpha: Vec, - /// The minimum and the maximum source rectangle offset supported by this plane using the specified mode. - pub src_position: Range, - /// The minimum and maximum source rectangle size supported by this plane using the specified mode. - pub src_extent: Range, - /// Same as src_position. but applied to destination. - pub dst_position: Range, - /// Same as src_extent. but applied to destination. - pub dst_extent: Range, -} diff --git a/third_party/rust/gfx-hal/src/external_memory/errors.rs b/third_party/rust/gfx-hal/src/external_memory/errors.rs deleted file mode 100644 index 3e8a7eb5da0f..000000000000 --- a/third_party/rust/gfx-hal/src/external_memory/errors.rs +++ /dev/null @@ -1,51 +0,0 @@ -//! Structures and enums related to external memory errors. - -use crate::device::OutOfMemory; - -#[derive(Clone, Debug, PartialEq, thiserror::Error)] -/// Error while enumerating external image properties. Returned from [PhysicalDevice::external_image_properties][crate::adapter::PhysicalDevice::external_image_properties]. -pub enum ExternalImagePropertiesError { - /// Out of either host or device memory. - #[error(transparent)] - OutOfMemory(#[from] OutOfMemory), - - /// Requested image format not supported in combination with other parameters. - #[error("Format not supported")] - FormatNotSupported, -} - -#[derive(Clone, Debug, PartialEq, thiserror::Error)] -/// Error while creating and allocating or importing an external buffer. -pub enum ExternalResourceError { - /// Out of either host or device memory. - #[error(transparent)] - OutOfMemory(#[from] OutOfMemory), - - /// Cannot create any more objects. - #[error("Too many objects")] - TooManyObjects, - - /// All the desired memory type ids are invalid for the implementation.. - #[error("No valid memory type id among the desired ones")] - NoValidMemoryTypeId, - - /// Invalid external handle. - #[error("The used external handle or the combination of them is invalid")] - InvalidExternalHandle, -} - -#[derive(Clone, Debug, PartialEq, thiserror::Error)] -/// Error while exporting a memory. Returned from [Device::export_memory][crate::device::Device::export_memory]. -pub enum ExternalMemoryExportError { - /// Too many objects. - #[error("Too many objects")] - TooManyObjects, - - /// Out of host memory. - #[error("Out of host memory")] - OutOfHostMemory, - - /// Invalid external handle. - #[error("Invalid external handle")] - InvalidExternalHandle, -} diff --git a/third_party/rust/gfx-hal/src/external_memory/mod.rs b/third_party/rust/gfx-hal/src/external_memory/mod.rs deleted file mode 100644 index 42ce2e539084..000000000000 --- a/third_party/rust/gfx-hal/src/external_memory/mod.rs +++ /dev/null @@ -1,19 +0,0 @@ -//! Structures related to the import external memory functionality. - -mod errors; -pub use errors::*; - -pub use external_memory::*; - -bitflags!( - #[cfg_attr(feature = "serde", derive(Serialize, Deserialize))] - /// External memory properties. - pub struct ExternalMemoryProperties: u32 { - /// The memory can be exported using [Device::export_memory][Device::export_memory]. - const EXPORTABLE = (1 << 0); - /// The memory can be imported using [Device::import_external_image][Device::import_external_image] and [Device::import_external_buffer][Device::import_external_buffer]. - const IMPORTABLE = (1 << 1); - /// The memory created using [Device::import_external_image][Device::import_external_image] and [Device::import_external_buffer][Device::import_external_buffer] can be exported using [Device::export_memory][Device::export_memory]. - const EXPORTABLE_FROM_IMPORTED = (1 << 2); - } -); diff --git a/third_party/rust/gfx-hal/src/format.rs b/third_party/rust/gfx-hal/src/format.rs index f69829a433a2..9a44968d56bd 100644 --- a/third_party/rust/gfx-hal/src/format.rs +++ b/third_party/rust/gfx-hal/src/format.rs @@ -9,8 +9,6 @@ //! for instance `R32_G32_B32_A32`. The `ChannelType` specifies how the //! components are interpreted, for instance `Sfloat` or `Sint`. -pub use external_memory::DrmModifier; - bitflags!( /// Bitflags which describe what properties of an image /// a format specifies or does not specify. For example, @@ -128,29 +126,8 @@ impl Default for Swizzle { } } -/// Drm format properties -#[derive(Debug, Clone, PartialEq, Eq, Hash)] -#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))] -pub struct DrmFormatProperties { - /// Drm format modifier. - pub drm_modifier: DrmModifier, - /// Number of memory planes each image crated with `drm_modifier` has. - pub plane_count: u32, - /// Valid image usage with the `drm_modifier`. - pub valid_usages: ImageFeature, -} -impl Default for DrmFormatProperties { - fn default() -> Self { - Self { - drm_modifier: DrmModifier::Invalid, - plane_count: 0, - valid_usages: ImageFeature::default(), - } - } -} - /// Format properties of the physical device. -#[derive(Debug, Default, Clone, PartialEq, Eq, Hash)] +#[derive(Debug, Default, Copy, Clone, PartialEq, Eq, Hash)] #[cfg_attr(feature = "serde", derive(Serialize, Deserialize))] pub struct Properties { /// A bitmask of the features supported when an image with linear tiling is requested. @@ -162,8 +139,6 @@ pub struct Properties { pub optimal_tiling: ImageFeature, /// The features supported by buffers. pub buffer_features: BufferFeature, - /// Drm format properties - pub drm_format_properties: Vec, } //Note: these are detached from Vulkan! diff --git a/third_party/rust/gfx-hal/src/lib.rs b/third_party/rust/gfx-hal/src/lib.rs index 1a3f145e2e02..2f71b8690e0b 100644 --- a/third_party/rust/gfx-hal/src/lib.rs +++ b/third_party/rust/gfx-hal/src/lib.rs @@ -52,8 +52,6 @@ pub mod adapter; pub mod buffer; pub mod command; pub mod device; -pub mod display; -pub mod external_memory; pub mod format; pub mod image; pub mod memory; @@ -291,8 +289,6 @@ bitflags! { const MESH_SHADER_MASK = Features::TASK_SHADER.bits | Features::MESH_SHADER.bits; /// Support sampler min/max reduction mode. const SAMPLER_REDUCTION = 0x0004 << 96; - /// Supports external memory import and export. - const EXTERNAL_MEMORY = 0x0008 << 96; } } @@ -354,8 +350,6 @@ pub struct PhysicalDeviceProperties { pub performance_caveats: PerformanceCaveats, /// Dynamic pipeline states. pub dynamic_pipeline_states: DynamicStates, - /// External memory limits - pub external_memory_limits: ExternalMemoryLimits, } /// @@ -657,21 +651,6 @@ pub enum IndexType { #[error("Backend is not supported on this platform")] pub struct UnsupportedBackend; -#[derive(Clone, Copy, Debug, PartialEq)] -#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))] -/// Physical device limits for external memory management -pub struct ExternalMemoryLimits { - /// Alignment required for an imported host pointer - pub min_imported_host_pointer_alignment: u64, -} -impl Default for ExternalMemoryLimits { - fn default() -> Self { - Self { - min_imported_host_pointer_alignment: 0, - } - } -} - /// An instantiated backend. /// /// Any startup the backend needs to perform will be done when creating the type that implements @@ -733,23 +712,6 @@ pub trait Instance: Any + Send + Sync + Sized { /// # Safety /// unsafe fn destroy_surface(&self, surface: B::Surface); - - /// Create a new [surface][window::Surface] from a [display plane][display::DisplayPlane]. - /// - /// Surfaces can be used to render to windows. - /// - /// # Safety - /// - /// This method can cause undefined behavior if `raw_window_handle` isn't - /// a handle to a valid window for the current platform. - unsafe fn create_display_plane_surface<'a>( - &self, - display_plane: &display::DisplayPlane<'a, B>, - plane_stack_index: u32, - transformation: display::SurfaceTransform, - alpha: display::DisplayPlaneAlpha, - image_extent: window::Extent2D, - ) -> Result; } /// A strongly-typed index to a particular `MemoryType`. @@ -840,8 +802,4 @@ pub trait Backend: 'static + Sized + Eq + Clone + Hash + fmt::Debug + Any + Send type Event: fmt::Debug + Any + Send + Sync; /// The corresponding query pool type for this backend. type QueryPool: fmt::Debug + Any + Send + Sync; - /// The corresponding display type for this backend. - type Display: fmt::Debug + Any + Send + Sync; - /// The corresponding display mode type for this backend - type DisplayMode: fmt::Debug + Any + Send + Sync; } diff --git a/third_party/rust/gfx-hal/src/window.rs b/third_party/rust/gfx-hal/src/window.rs index f6149694390a..2ce847982f42 100644 --- a/third_party/rust/gfx-hal/src/window.rs +++ b/third_party/rust/gfx-hal/src/window.rs @@ -118,21 +118,6 @@ impl From for Extent2D { } } -impl From<(image::Size, image::Size)> for Extent2D { - fn from(tuple: (image::Size, image::Size)) -> Self { - Extent2D { - width: tuple.0, - height: tuple.1, - } - } -} - -impl From for (image::Size, image::Size) { - fn from(extent: Extent2D) -> Self { - (extent.width, extent.height) - } -} - impl Extent2D { /// Convert into a regular image extent. pub fn to_extent(&self) -> image::Extent { @@ -144,26 +129,6 @@ impl Extent2D { } } -/// An offset describes the position of a rectangle, such as -/// a window or texture. -#[derive(Clone, Copy, Debug, PartialEq, PartialOrd)] -#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))] -pub struct Offset2D { - /// X - pub x: image::TexelCoordinate, - /// Y - pub y: image::TexelCoordinate, -} - -impl From<(image::TexelCoordinate, image::TexelCoordinate)> for Offset2D { - fn from(tuple: (image::TexelCoordinate, image::TexelCoordinate)) -> Self { - Offset2D { - x: tuple.0, - y: tuple.1, - } - } -} - /// Describes information about what a `Surface`'s properties are. /// Fetch this with [Surface::capabilities]. #[derive(Debug, Clone)] diff --git a/third_party/rust/gfx-renderdoc/.cargo-checksum.json b/third_party/rust/gfx-renderdoc/.cargo-checksum.json deleted file mode 100644 index e471511de34e..000000000000 --- a/third_party/rust/gfx-renderdoc/.cargo-checksum.json +++ /dev/null @@ -1 +0,0 @@ -{"files":{"Cargo.toml":"1ea91233698a0c4d5ef4913806b932a142ef41c664b77267840bd83ca526aea8","src/lib.rs":"5d602332d6ea72922bf461b0aa6793ebad2f4c6259d579e2dcc28993c87404d8"},"package":"c8027995e247e2426d3a00d13f5191dd56c314bff02dc4b54cbf727f1ba9c40a"} \ No newline at end of file diff --git a/third_party/rust/gfx-renderdoc/Cargo.toml b/third_party/rust/gfx-renderdoc/Cargo.toml deleted file mode 100644 index 98742d4f79b5..000000000000 --- a/third_party/rust/gfx-renderdoc/Cargo.toml +++ /dev/null @@ -1,35 +0,0 @@ -# THIS FILE IS AUTOMATICALLY GENERATED BY CARGO -# -# When uploading crates to the registry Cargo will automatically -# "normalize" Cargo.toml files for maximal compatibility -# with all versions of Cargo and also rewrite `path` dependencies -# to registry (e.g., crates.io) dependencies -# -# If you believe there's an error in this file please file an -# issue against the rust-lang/cargo repository. If you're -# editing this file be aware that the upstream Cargo.toml -# will likely look very different (and much more reasonable) - -[package] -edition = "2018" -name = "gfx-renderdoc" -version = "0.1.0" -authors = ["The Gfx-rs Developers"] -description = "Generic RenderDoc integration used by gfx-rs backends" -homepage = "https://github.com/gfx-rs/gfx" -documentation = "https://docs.rs/gfx-auxil-renderdoc" -keywords = ["renderdoc"] -categories = ["graphics"] -license = "MIT OR Apache-2.0" -repository = "https://github.com/gfx-rs/gfx" - -[lib] -name = "gfx_renderdoc" -[dependencies.libloading] -version = "0.7" - -[dependencies.log] -version = "0.4" - -[dependencies.renderdoc-sys] -version = "0.7.1" diff --git a/third_party/rust/gfx-renderdoc/src/lib.rs b/third_party/rust/gfx-renderdoc/src/lib.rs deleted file mode 100644 index 8c9af3956be0..000000000000 --- a/third_party/rust/gfx-renderdoc/src/lib.rs +++ /dev/null @@ -1,127 +0,0 @@ -#![warn( - trivial_casts, - trivial_numeric_casts, - unused_extern_crates, - unused_import_braces, - unused_qualifications -)] - -//! RenderDoc integration - https://renderdoc.org/ - -/// The dynamically loaded RenderDoc API function table -#[repr(C)] -#[derive(Debug)] -pub struct RenderDocApi { - api: renderdoc_sys::RENDERDOC_API_1_4_1, - lib: libloading::Library, -} - -unsafe impl Send for RenderDocApi {} - -unsafe impl Sync for RenderDocApi {} - -/// RenderDoc API type -#[derive(Debug)] -pub enum RenderDoc { - /// RenderDoc functionality is available - Available { - /// RenderDoc API with function pointers - api: RenderDocApi, - }, - /// RenderDoc functionality is _not_ available - NotAvailable { - /// A description why renderdoc functionality is not available - reason: String, - }, -} - -impl RenderDoc { - pub unsafe fn new() -> Self { - type GetApiFn = unsafe extern "C" fn(version: u32, out: *mut *mut std::ffi::c_void) -> i32; - - #[cfg(windows)] - let renderdoc_filename = "renderdoc.dll"; - #[cfg(all(unix, not(target_os = "android")))] - let renderdoc_filename = "librenderdoc.so"; - #[cfg(target_os = "android")] - let renderdoc_filename = "libVkLayer_GLES_RenderDoc.so"; - - let renderdoc_lib = match libloading::Library::new(renderdoc_filename) { - Ok(lib) => lib, - Err(e) => { - return RenderDoc::NotAvailable { - reason: format!( - "Unable to load renderdoc library '{}': {:?}", - renderdoc_filename, e - ), - } - } - }; - - let get_api: libloading::Symbol = match renderdoc_lib.get(b"RENDERDOC_GetAPI\0") { - Ok(api) => api, - Err(e) => { - return RenderDoc::NotAvailable { - reason: format!( - "Unable to get RENDERDOC_GetAPI from renderdoc library '{}': {:?}", - renderdoc_filename, e - ), - } - } - }; - let mut obj = std::ptr::null_mut(); - match get_api(10401, &mut obj) { - 1 => RenderDoc::Available { - api: RenderDocApi { - api: *(obj as *mut renderdoc_sys::RENDERDOC_API_1_4_1), - lib: renderdoc_lib, - }, - }, - return_value => RenderDoc::NotAvailable { - reason: format!( - "Unable to get API from renderdoc library '{}': {}", - renderdoc_filename, return_value - ), - }, - } - } -} - -impl Default for RenderDoc { - fn default() -> Self { - if !cfg!(debug_assertions) { - return RenderDoc::NotAvailable { - reason: "RenderDoc support is only enabled with 'debug_assertions'".into(), - }; - } - unsafe { Self::new() } - } -} -/// A implementation specific handle -pub type Handle = *mut ::std::os::raw::c_void; - -impl RenderDoc { - /// Start a RenderDoc frame capture - pub unsafe fn start_frame_capture(&self, device_handle: Handle, window_handle: Handle) { - match self { - Self::Available { api: ref entry } => { - entry.api.StartFrameCapture.unwrap()(device_handle, window_handle); - } - Self::NotAvailable { ref reason } => { - log::warn!("Could not start RenderDoc frame capture: {}", reason) - } - }; - } - - /// End a RenderDoc frame capture - pub unsafe fn end_frame_capture(&self, device_handle: Handle, window_handle: Handle) { - match self { - Self::Available { api: ref entry } => { - entry.api.EndFrameCapture.unwrap()(device_handle, window_handle); - } - Self::NotAvailable { ref reason } => { - log::warn!("Could not end RenderDoc frame capture: {}", reason) - } - }; - } -} diff --git a/third_party/rust/metal/.cargo-checksum.json b/third_party/rust/metal/.cargo-checksum.json index 3d3e8904d9c7..d32d9397d3b4 100644 --- a/third_party/rust/metal/.cargo-checksum.json +++ b/third_party/rust/metal/.cargo-checksum.json @@ -1 +1 @@ -{"files":{"Cargo.lock":"d3d9dc8460e01d3db83b66964077b6ba0df2d922ec8a4bdacdbb0e6a32daa07f","Cargo.toml":"9fc77e213e1b193d5e155d21bacf221fb1e34250b0566c14f5edb6b6418a31c1","LICENSE-APACHE":"a60eea817514531668d7e00765731449fe14d059d3249e0bc93b36de45f759f2","LICENSE-MIT":"0621878e61f0d0fda054bcbe02df75192c28bde1ecc8289cbd86aeba2dd72720","Makefile":"6fddc61a94f5b31a65b11c1bef8b19c92bff738716998076c5d49c2834223c75","README.md":"6817e12da257b43352f71f595dcc713adf117c734ebf656e6af2d7d04be27cd6","bors.toml":"c2733ec512a08bf76b6a1ed77270810a0edeb888ba24c2a75679e1eb1bd563a5","examples/argument-buffer/main.rs":"4c1d1d9949bc56628980f84077dafdfa68a853141d6d4dc3e6202f6c61ba11f7","examples/bind/main.rs":"a0c85aad05f08666f9b380a7146a8473a6a6fe0db5d523760373093a0af20e5f","examples/caps/main.rs":"b7be00c1cc9042140d34ea05051152a7035f316f0bdcd31ac5a660a97e0c4f70","examples/circle/README.md":"e1c97cf5252f0d1f2934ace78b5d839c5f45911f3007dbd2925eeceefb8f0af6","examples/circle/main.rs":"91d80c85a358400ceeddf1ab108a7e1052dd208c4ec72cde925d02284d3cf9d4","examples/circle/screenshot.png":"97bf07c85bf02367447b9c8a81707c124e4a3b420fa386b95ba08b21938f4f2a","examples/circle/shaders.metal":"5e4f40efca5bb386204a09e1b983cc6c634fdf1ca9dd4974227313adbf50e8b5","examples/circle/shaders.metallib":"666a9491d795ef9c0b9c122c7ada571cc2c0e8774d2d89e5b4b996f3dc47962b","examples/compute/compute-argument-buffer.metal":"6530bbd6a0101d9db2893805436f5dc877959e81ea97a27014c0fc52fc9fa77b","examples/compute/compute-argument-buffer.rs":"e3de61fd7cc2f14d9d52300e4878601dbc072bc26d9dafc66115df58f94e0470","examples/compute/embedded-lib.rs":"55f701810fa5270c27ca771e713f9f8cf09e124a997b0b03790b38435593a7ea","examples/compute/main.rs":"f16cbf57cd27dc948ff651251ce26e6bd616cb5d989b8dadb4256c73a9bfba4b","examples/compute/shaders.metal":"f2b15551bb5247b88a3029c3d8ef37c6fa04a4a6cca9f90f069894ed6822b4bf","examples/compute/shaders.metallib":"fef91643e60c0ec99ad2bd2f3916299bcc3e6a80038ea27bed59681badfea7d1","examples/events/main.rs":"9cb35381b0a3918bd7d530171de8f7cceafe3d4851c0f430b4aff1f5c2aae749","examples/fence/main.rs":"47741327e62db1d8bd344b6a9ec26ef13ffb0b56b0dd7077c5d926d43faaeff7","examples/headless-render/README.md":"b1c97b52701cfb41fc0b9e269ba7a7a454d9161746198e2f5789f2636f60842d","examples/headless-render/main.rs":"cf0180839e8d09d4bf403ae947365ac18fa17782172986311bfa04b84f88169e","examples/headless-render/screenshot.png":"01d6ea5791b63b0f01190198756446cf313fc25dc64d0138c1b4f62c9f862dd1","examples/library/main.rs":"a1420ec28a471f28a789b75b3ecf5abb699ed352b337747169914812fb98045a","examples/mps/main.rs":"51f34582bf118f171bbb81d22c11407c7a35f381dbbff2d75c6f8e90d22a2aa1","examples/mps/shaders.metal":"155922d6a4184078ae7ee29504a268e1218f07d908f921eef60e5bfa8a793bda","examples/mps/shaders.metallib":"b62451223549b1e7eb90ec3d3534c0ed4cdfdc581c7df3ffcdc4786a5fcacde4","examples/reflection/main.rs":"fa0ade08750e14c8fded19c2b57e3fc1252f4fc0292890aea1d46968a647b600","examples/shader-dylib/main.rs":"d29ab2105131b8c56a6af6453f1d973e2bda5564f84b652c01e4a4e44b7a70f2","examples/shader-dylib/test_dylib.metal":"3469de785c2c0da784e84758fc0da5a81e474ca15588485d9e04398690641cc8","examples/shader-dylib/test_shader.metal":"1a04ff8ab3288b09d14cd35440b2557e92ddedbff9d07c4144a22e9062e6e1e4","examples/window/README.md":"69655cff298e07887fe70e8a13e27d8a87efcd0cc0da4e15485134e064e1aceb","examples/window/main.rs":"09c508013223de859f33fb69410bde30e8d7f04952850504d8b1f8faf7049b1b","examples/window/screenshot.png":"da7369d7cc297c7f7f6bd773c93e7d708d72442c9c5ff5a20c2f2ee6852552dc","examples/window/shaders.metal":"90dee6c752add5c617dfdca93064b2824b44ff8c01ef63986826f6a1531e95d6","examples/window/shaders.metallib":"16fa82beb70bf16c3501970cae0d5028a747a08164337161dc9c2e8965d4c366","src/argument.rs":"78c37356b421f90bf215d435efb961d93ea040e6849790492f399ddb07ac7063","src/buffer.rs":"80c55c8703340bf0d4d1b5eface518fdf82355ccb897883639cbf7e4933a4344","src/capturedescriptor.rs":"7d90b1e7b87fa9da1e38bba9637cd8d7a18a81f8c3f408149058ed4ea20a6894","src/capturemanager.rs":"bdee9e170da371778452513a9ac363a738ea8bfd3f0870d86c6013459c5af010","src/commandbuffer.rs":"0123224dcc0552748f1c2c87385f1f6be2863887f8862fbfd8b3a1562d9f69ba","src/commandqueue.rs":"5b87621ae4495ae04a5de5ce980d0cde31b4bb0d1e31e932d34c49252240c9d9","src/constants.rs":"7be4d901da863f126d158d1042f5482a122fbcf760f7c225b0b1693a55f6bb4b","src/depthstencil.rs":"5bfa4f49940fdc9d12b2af08f7fe710c41f0b7e26bdb6f8709fe17b9a9d7d5eb","src/device.rs":"e65ad152c01065bc10233eca8c77892279cd82dec44b948cb70e55b8fa8ab0d2","src/drawable.rs":"aea0759dc9de002c660e938319dfdfd3e4e82add30ed626ea31e3270698f4b85","src/encoder.rs":"1e84d8a49b71908b09487c87665b64ff4613e2115d0fa6044842597208bb7af7","src/heap.rs":"fcbb7dfaafb1008fadd75fb2b394776c9843fe98a266e237d7a7b4d80e046b06","src/indirect_encoder.rs":"eaf7755de9438022e3908e8426a139c33d4929d7be4b5e056d959eeecd560136","src/lib.rs":"b1fc90132440c270264e575e452f1d2a47420215ca3d08f8212e91f84009e603","src/library.rs":"61d0b8313e51c48ed3f5ee88dfe42b010e4c72ca160c29f94f8e73db7a006c3e","src/mps.rs":"b415be3221754d836fd535f4b5b45ed484d4cc926bd26145e82a9e61d337da4c","src/pipeline/compute.rs":"e4bed01671a466ed460fbc9059ac72dab7cb123a964de63058f0e5aafdc5e2a0","src/pipeline/mod.rs":"5ec3cb524cc03202a3394dad5a7d0b733474f664935353253e76a49aed5513ad","src/pipeline/render.rs":"caaa3698b85ef114a54d305188717f7fb612ac3fc51aabb40dfca236e7f35b1a","src/renderpass.rs":"666ca2a35d2bb96564f105a87f03cfbf87d3fd10e6915e473e8ff4c0db49fdef","src/resource.rs":"617dc7c9b08388b406c7b178f5ff25ee46a54299213a57ab09f5039b2b1e5927","src/sampler.rs":"adabef3520a18c829bfd9b705897873d9adc37ebe88cfa6864154483dd858b9d","src/sync.rs":"4b9f6a9f9425751411c8104fe0ebbe8e1d89db2005d929ada5334962a3301b8d","src/texture.rs":"2df3003396aa8f0552b853dac33ba970a918bc73d183fe0d3dad4bc21ee37e10","src/types.rs":"5a754c8036ff4ab1ec41d01af5fba64f3085f2a9dd1d15d00e907e40b85cf163","src/vertexdescriptor.rs":"5874f54bcc5613adff613ed3e2bb08870b1115ef6d369b21612ce848796b005e"},"package":"79d7d769f1c104b8388294d6594d491d2e21240636f5f94d37f8a0f3d7904450"} \ No newline at end of file +{"files":{"Cargo.lock":"db9809db895bda50311a5856317188aa66f4317775000d8bb2e66780a8c47f25","Cargo.toml":"64f515a2614ae662e87d0daa59a215434e2ec786f7511d0a1a08b8051d821002","LICENSE-APACHE":"a60eea817514531668d7e00765731449fe14d059d3249e0bc93b36de45f759f2","LICENSE-MIT":"0621878e61f0d0fda054bcbe02df75192c28bde1ecc8289cbd86aeba2dd72720","Makefile":"6fddc61a94f5b31a65b11c1bef8b19c92bff738716998076c5d49c2834223c75","README.md":"6817e12da257b43352f71f595dcc713adf117c734ebf656e6af2d7d04be27cd6","bors.toml":"c2733ec512a08bf76b6a1ed77270810a0edeb888ba24c2a75679e1eb1bd563a5","examples/argument-buffer/main.rs":"4c1d1d9949bc56628980f84077dafdfa68a853141d6d4dc3e6202f6c61ba11f7","examples/bind/main.rs":"a0c85aad05f08666f9b380a7146a8473a6a6fe0db5d523760373093a0af20e5f","examples/caps/main.rs":"20d441b939dcd01e71e8c7686fcd2dd203dde1816603192ea8f65796b9984b5d","examples/circle/README.md":"e1c97cf5252f0d1f2934ace78b5d839c5f45911f3007dbd2925eeceefb8f0af6","examples/circle/main.rs":"e3a9ade6357307b48ef3a8072699cf26f9658495945b7b9de8904a691826b80a","examples/circle/screenshot.png":"97bf07c85bf02367447b9c8a81707c124e4a3b420fa386b95ba08b21938f4f2a","examples/circle/shaders.metal":"5e4f40efca5bb386204a09e1b983cc6c634fdf1ca9dd4974227313adbf50e8b5","examples/circle/shaders.metallib":"666a9491d795ef9c0b9c122c7ada571cc2c0e8774d2d89e5b4b996f3dc47962b","examples/compute/compute-argument-buffer.metal":"6530bbd6a0101d9db2893805436f5dc877959e81ea97a27014c0fc52fc9fa77b","examples/compute/compute-argument-buffer.rs":"e3de61fd7cc2f14d9d52300e4878601dbc072bc26d9dafc66115df58f94e0470","examples/compute/embedded-lib.rs":"55f701810fa5270c27ca771e713f9f8cf09e124a997b0b03790b38435593a7ea","examples/compute/main.rs":"f16cbf57cd27dc948ff651251ce26e6bd616cb5d989b8dadb4256c73a9bfba4b","examples/compute/shaders.metal":"f2b15551bb5247b88a3029c3d8ef37c6fa04a4a6cca9f90f069894ed6822b4bf","examples/compute/shaders.metallib":"fef91643e60c0ec99ad2bd2f3916299bcc3e6a80038ea27bed59681badfea7d1","examples/events/main.rs":"9cb35381b0a3918bd7d530171de8f7cceafe3d4851c0f430b4aff1f5c2aae749","examples/fence/main.rs":"47741327e62db1d8bd344b6a9ec26ef13ffb0b56b0dd7077c5d926d43faaeff7","examples/headless-render/README.md":"b1c97b52701cfb41fc0b9e269ba7a7a454d9161746198e2f5789f2636f60842d","examples/headless-render/main.rs":"cf0180839e8d09d4bf403ae947365ac18fa17782172986311bfa04b84f88169e","examples/headless-render/screenshot.png":"01d6ea5791b63b0f01190198756446cf313fc25dc64d0138c1b4f62c9f862dd1","examples/library/main.rs":"a1420ec28a471f28a789b75b3ecf5abb699ed352b337747169914812fb98045a","examples/mps/main.rs":"51f34582bf118f171bbb81d22c11407c7a35f381dbbff2d75c6f8e90d22a2aa1","examples/mps/shaders.metal":"155922d6a4184078ae7ee29504a268e1218f07d908f921eef60e5bfa8a793bda","examples/mps/shaders.metallib":"b62451223549b1e7eb90ec3d3534c0ed4cdfdc581c7df3ffcdc4786a5fcacde4","examples/reflection/main.rs":"fa0ade08750e14c8fded19c2b57e3fc1252f4fc0292890aea1d46968a647b600","examples/shader-dylib/main.rs":"c4ba734e1ff5325a4053d032a3ff69c41b4189db24c19c6de8a1ec92465a0720","examples/shader-dylib/test_dylib.metal":"3469de785c2c0da784e84758fc0da5a81e474ca15588485d9e04398690641cc8","examples/shader-dylib/test_shader.metal":"1a04ff8ab3288b09d14cd35440b2557e92ddedbff9d07c4144a22e9062e6e1e4","examples/window/README.md":"69655cff298e07887fe70e8a13e27d8a87efcd0cc0da4e15485134e064e1aceb","examples/window/main.rs":"57b956895ec339214d2cd9350c9ca9659e07062b86ba4279f60749a8bcc18f35","examples/window/screenshot.png":"da7369d7cc297c7f7f6bd773c93e7d708d72442c9c5ff5a20c2f2ee6852552dc","examples/window/shaders.metal":"90dee6c752add5c617dfdca93064b2824b44ff8c01ef63986826f6a1531e95d6","examples/window/shaders.metallib":"16fa82beb70bf16c3501970cae0d5028a747a08164337161dc9c2e8965d4c366","src/argument.rs":"cbea626cb84b60bc1c3c033eb7c56cacb1c31fd4233307ce851e7c6a49b17271","src/buffer.rs":"80c55c8703340bf0d4d1b5eface518fdf82355ccb897883639cbf7e4933a4344","src/capturedescriptor.rs":"7d90b1e7b87fa9da1e38bba9637cd8d7a18a81f8c3f408149058ed4ea20a6894","src/capturemanager.rs":"bdee9e170da371778452513a9ac363a738ea8bfd3f0870d86c6013459c5af010","src/commandbuffer.rs":"0123224dcc0552748f1c2c87385f1f6be2863887f8862fbfd8b3a1562d9f69ba","src/commandqueue.rs":"5b87621ae4495ae04a5de5ce980d0cde31b4bb0d1e31e932d34c49252240c9d9","src/constants.rs":"7be4d901da863f126d158d1042f5482a122fbcf760f7c225b0b1693a55f6bb4b","src/depthstencil.rs":"5bfa4f49940fdc9d12b2af08f7fe710c41f0b7e26bdb6f8709fe17b9a9d7d5eb","src/device.rs":"8a9fae53b1906c0eee38657822130bee37ab8652f6ae38987d78fe3f3a142cd0","src/drawable.rs":"191ed7d3e370544df49e861119d7d1681c2aadfdea105a3f4db2721cad643e33","src/encoder.rs":"2b168327b5d798698ca2eea5ad92360d53081924910cd480ce0d9cb24be134b3","src/heap.rs":"2470f9e71ba994ae1bd008c41b18c461dbe94fbb2353d7639f9db5696dfb44e3","src/indirect_encoder.rs":"b3e3fe78e69fc97431c82e42c925cc1751953c43c7f7706fd77ca649b610800d","src/lib.rs":"707bf5c13771bba05de6bf4500821945b44a23389fc2ce44aa7aff21a0def110","src/library.rs":"8e05f9be0763ec7468365a90e5abd92681c9a029bb213dbe946728d4c3093d13","src/mps.rs":"b415be3221754d836fd535f4b5b45ed484d4cc926bd26145e82a9e61d337da4c","src/pipeline/compute.rs":"a1d71d809e8296f037b1fd7e1cdd0ee5821fe9f119a0b8b754edf377bb1ed7f3","src/pipeline/mod.rs":"5ec3cb524cc03202a3394dad5a7d0b733474f664935353253e76a49aed5513ad","src/pipeline/render.rs":"cd9cbb491fcc558a2a2a37f83c5b087dcf66bff1a1f2ae8ce6a95de59b385a69","src/renderpass.rs":"163f119c4ae196663336c3327f827623e7e4e5ac16c43e8b2eed0317929521f0","src/resource.rs":"c0d21eb409fa3eb796be1f71a542717f6079e35c4d730dceee96e8496517ff8f","src/sampler.rs":"3c3b2f8a5f7c6f817e9153ff63223512bbfcf7037fc76741f81ac46a3ddc1431","src/sync.rs":"4b9f6a9f9425751411c8104fe0ebbe8e1d89db2005d929ada5334962a3301b8d","src/texture.rs":"7b6e134957053fcaf085411adfad041dec74b60902b6a7cb01351430c0b65c88","src/types.rs":"a14c7148eeebe3eb5f7e90b1add7a2109825a52b115d2aa57fd999eb67b70f08","src/vertexdescriptor.rs":"b36244b752406769dd0930a607b8f5b9c191b9045e973dd2cce6d85443f318ca"},"package":"1c12e48c737ee9a55e8bb2352bcde588f79ae308d3529ee888f7cc0f469b5777"} \ No newline at end of file diff --git a/third_party/rust/metal/Cargo.lock b/third_party/rust/metal/Cargo.lock index d834d18d6566..8947a8718468 100644 --- a/third_party/rust/metal/Cargo.lock +++ b/third_party/rust/metal/Cargo.lock @@ -438,12 +438,12 @@ dependencies = [ [[package]] name = "metal" -version = "0.23.0" +version = "0.22.0" dependencies = [ "bitflags", "block", "cocoa 0.24.0", - "core-graphics-types", + "cocoa-foundation", "cty", "dispatch", "foreign-types", diff --git a/third_party/rust/metal/Cargo.toml b/third_party/rust/metal/Cargo.toml index 63eb6447cab9..911ef973efd8 100644 --- a/third_party/rust/metal/Cargo.toml +++ b/third_party/rust/metal/Cargo.toml @@ -13,7 +13,7 @@ [package] edition = "2018" name = "metal" -version = "0.23.0" +version = "0.22.0" authors = ["GFX Developers"] exclude = ["guide/**/*", "examples/texture/**/*", "tests/**/*", "Cargo.lock", "target/**/*"] description = "Rust bindings for Metal" @@ -79,7 +79,7 @@ version = "1" [dependencies.block] version = "0.1.6" -[dependencies.core-graphics-types] +[dependencies.cocoa-foundation] version = "0.1" [dependencies.dispatch] diff --git a/third_party/rust/metal/examples/caps/main.rs b/third_party/rust/metal/examples/caps/main.rs index ae8fca4f0aed..9420938459ca 100644 --- a/third_party/rust/metal/examples/caps/main.rs +++ b/third_party/rust/metal/examples/caps/main.rs @@ -25,7 +25,7 @@ fn main() { println!("Headless: {:?}", device.is_headless()); println!("D24S8: {:?}", device.d24_s8_supported()); } - println!("maxBufferLength: {} Mb", device.max_buffer_length() >> 20); + println!("maxBufferLength: {} Mb", device.max_buffer_length()>>20); println!( "Indirect argument buffer: {:?}", device.argument_buffers_support() diff --git a/third_party/rust/metal/examples/circle/main.rs b/third_party/rust/metal/examples/circle/main.rs index 18da704421a7..528d4761eebc 100644 --- a/third_party/rust/metal/examples/circle/main.rs +++ b/third_party/rust/metal/examples/circle/main.rs @@ -1,13 +1,12 @@ use metal::*; +use winit::platform::macos::WindowExtMacOS; use winit::{ event::{Event, WindowEvent}, event_loop::{ControlFlow, EventLoop}, - platform::macos::WindowExtMacOS, }; use cocoa::{appkit::NSView, base::id as cocoa_id}; -use core_graphics_types::geometry::CGSize; use objc::{rc::autoreleasepool, runtime::YES}; diff --git a/third_party/rust/metal/examples/shader-dylib/main.rs b/third_party/rust/metal/examples/shader-dylib/main.rs index b713e20e0672..14baacd0448c 100644 --- a/third_party/rust/metal/examples/shader-dylib/main.rs +++ b/third_party/rust/metal/examples/shader-dylib/main.rs @@ -1,17 +1,15 @@ use cocoa::{appkit::NSView, base::id as cocoa_id}; -use core_graphics_types::geometry::CGSize; use metal::*; use objc::{rc::autoreleasepool, runtime::YES}; +use std::mem; +use winit::platform::macos::WindowExtMacOS; use winit::{ event::{Event, WindowEvent}, event_loop::ControlFlow, - platform::macos::WindowExtMacOS, }; -use std::mem; - struct App { pub device: Device, pub command_queue: CommandQueue, diff --git a/third_party/rust/metal/examples/window/main.rs b/third_party/rust/metal/examples/window/main.rs index 08936e82fc6d..5df53e858f17 100644 --- a/third_party/rust/metal/examples/window/main.rs +++ b/third_party/rust/metal/examples/window/main.rs @@ -8,7 +8,6 @@ extern crate objc; use cocoa::{appkit::NSView, base::id as cocoa_id}; -use core_graphics_types::geometry::CGSize; use metal::*; use objc::{rc::autoreleasepool, runtime::YES}; diff --git a/third_party/rust/metal/src/argument.rs b/third_party/rust/metal/src/argument.rs index 74cc0742ab09..e8fdc6f48686 100644 --- a/third_party/rust/metal/src/argument.rs +++ b/third_party/rust/metal/src/argument.rs @@ -5,7 +5,9 @@ // http://opensource.org/licenses/MIT>, at your option. This file may not be // copied, modified, or distributed except according to those terms. -use super::{Array, MTLTextureType, NSUInteger}; +use crate::{Array, MTLTextureType}; + +use cocoa_foundation::foundation::NSUInteger; use objc::runtime::{NO, YES}; #[repr(u64)] diff --git a/third_party/rust/metal/src/device.rs b/third_party/rust/metal/src/device.rs index 4b5c19bd394d..7dc85b9f2728 100644 --- a/third_party/rust/metal/src/device.rs +++ b/third_party/rust/metal/src/device.rs @@ -5,13 +5,18 @@ // http://opensource.org/licenses/MIT>, at your option. This file may not be // copied, modified, or distributed except according to those terms. -use super::*; - use block::{Block, ConcreteBlock}; +use cocoa_foundation::base::id; +use cocoa_foundation::foundation::NSUInteger; use foreign_types::ForeignType; use objc::runtime::{Object, NO, YES}; -use std::{ffi::CStr, os::raw::c_char, path::Path, ptr}; +use super::*; + +use std::ffi::CStr; +use std::os::raw::c_char; +use std::path::Path; +use std::ptr; #[allow(non_camel_case_types)] #[repr(u64)] @@ -1416,9 +1421,9 @@ extern "C" { } #[allow(non_camel_case_types)] -type dispatch_data_t = *mut Object; +type dispatch_data_t = id; #[allow(non_camel_case_types)] -pub type dispatch_queue_t = *mut Object; +pub type dispatch_queue_t = id; #[allow(non_camel_case_types)] type dispatch_block_t = *const Block<(), ()>; @@ -1700,12 +1705,16 @@ impl DeviceRef { src: &str, options: &CompileOptionsRef, ) -> Result { - let source = nsstring_from_str(src); + use cocoa_foundation::base::nil as cocoa_nil; + use cocoa_foundation::foundation::NSString as cocoa_NSString; + unsafe { + let source = cocoa_NSString::alloc(cocoa_nil).init_str(src); let mut err: *mut Object = ptr::null_mut(); let library: *mut MTLLibrary = msg_send![self, newLibraryWithSource:source options:options error:&mut err]; + let () = msg_send![source, release]; if !err.is_null() { let desc: *mut Object = msg_send![err, localizedDescription]; let compile_error: *const c_char = msg_send![desc, UTF8String]; @@ -1723,12 +1732,18 @@ impl DeviceRef { } pub fn new_library_with_file>(&self, file: P) -> Result { - let filename = nsstring_from_str(file.as_ref().to_string_lossy().as_ref()); + use cocoa_foundation::base::nil as cocoa_nil; + use cocoa_foundation::foundation::NSString as cocoa_NSString; + unsafe { + let filename = + cocoa_NSString::alloc(cocoa_nil).init_str(file.as_ref().to_string_lossy().as_ref()); + let library: *mut MTLLibrary = try_objc! { err => msg_send![self, newLibraryWithFile:filename.as_ref() error:&mut err] }; + Ok(Library::from_ptr(library)) } } @@ -1899,21 +1914,6 @@ impl DeviceRef { } } - pub fn new_buffer_with_bytes_no_copy( - &self, - bytes: *const std::ffi::c_void, - length: NSUInteger, - options: MTLResourceOptions, - deallocator: Option<&Block<(*const std::ffi::c_void, NSUInteger), ()>>, - ) -> Buffer { - unsafe { - msg_send![self, newBufferWithBytesNoCopy:bytes - length:length - options:options - deallocator:deallocator] - } - } - pub fn new_buffer_with_data( &self, bytes: *const std::ffi::c_void, diff --git a/third_party/rust/metal/src/drawable.rs b/third_party/rust/metal/src/drawable.rs index a1ab789b7f0e..d119b0afd420 100644 --- a/third_party/rust/metal/src/drawable.rs +++ b/third_party/rust/metal/src/drawable.rs @@ -5,7 +5,7 @@ // http://opensource.org/licenses/MIT>, at your option. This file may not be // copied, modified, or distributed except according to those terms. -use super::NSUInteger; +use cocoa_foundation::foundation::NSUInteger; pub enum MTLDrawable {} diff --git a/third_party/rust/metal/src/encoder.rs b/third_party/rust/metal/src/encoder.rs index a1113445aeed..50a89826f6ed 100644 --- a/third_party/rust/metal/src/encoder.rs +++ b/third_party/rust/metal/src/encoder.rs @@ -7,6 +7,8 @@ use super::*; +use cocoa_foundation::foundation::{NSInteger, NSUInteger}; + use std::ops::Range; #[repr(u64)] @@ -65,16 +67,10 @@ pub enum MTLTriangleFillMode { } bitflags! { - /// https://developer.apple.com/documentation/metal/mtlblitoption #[allow(non_upper_case_globals)] pub struct MTLBlitOption: NSUInteger { - /// https://developer.apple.com/documentation/metal/mtlblitoption/mtlblitoptionnone - const None = 0; - /// https://developer.apple.com/documentation/metal/mtlblitoption/mtlblitoptiondepthfromdepthstencil const DepthFromDepthStencil = 1 << 0; - /// https://developer.apple.com/documentation/metal/mtlblitoption/mtlblitoptionstencilfromdepthstencil const StencilFromDepthStencil = 1 << 1; - /// https://developer.apple.com/documentation/metal/mtlblitoption/mtlblitoptionrowlinearpvrtc const RowLinearPVRTC = 1 << 2; } } @@ -807,7 +803,6 @@ impl BlitCommandEncoderRef { } } - /// https://developer.apple.com/documentation/metal/mtlblitcommandencoder/1400756-copy pub fn copy_from_texture_to_buffer( &self, source_texture: &TextureRef, diff --git a/third_party/rust/metal/src/heap.rs b/third_party/rust/metal/src/heap.rs index 9d60142a7bfc..7861c504dc5b 100644 --- a/third_party/rust/metal/src/heap.rs +++ b/third_party/rust/metal/src/heap.rs @@ -7,6 +7,8 @@ use super::*; +use cocoa_foundation::foundation::NSUInteger; + /// Only available on macos(10.15), ios(13.0) #[repr(u64)] #[derive(Clone, Copy, PartialEq, Eq, Hash, Debug)] diff --git a/third_party/rust/metal/src/indirect_encoder.rs b/third_party/rust/metal/src/indirect_encoder.rs index 8862a30a9c16..bdd2d0012386 100644 --- a/third_party/rust/metal/src/indirect_encoder.rs +++ b/third_party/rust/metal/src/indirect_encoder.rs @@ -1,5 +1,7 @@ use super::*; +use cocoa_foundation::foundation::NSUInteger; + bitflags! { #[allow(non_upper_case_globals)] pub struct MTLIndirectCommandType: NSUInteger { diff --git a/third_party/rust/metal/src/lib.rs b/third_party/rust/metal/src/lib.rs index 1eca90bd4794..86c8468bc1bd 100644 --- a/third_party/rust/metal/src/lib.rs +++ b/third_party/rust/metal/src/lib.rs @@ -17,26 +17,33 @@ extern crate objc; #[macro_use] extern crate foreign_types; -use std::{ - borrow::{Borrow, ToOwned}, - marker::PhantomData, - mem, - ops::Deref, - os::raw::c_void, -}; +use std::borrow::{Borrow, ToOwned}; +use std::marker::PhantomData; +use std::mem; +use std::ops::Deref; +use std::os::raw::c_void; -use core_graphics_types::{base::CGFloat, geometry::CGSize}; +use cocoa_foundation::foundation::NSUInteger; use foreign_types::ForeignType; use objc::runtime::{Object, NO, YES}; #[cfg(target_pointer_width = "64")] -pub type NSInteger = i64; +pub type CGFloat = f64; #[cfg(not(target_pointer_width = "64"))] -pub type NSInteger = i32; -#[cfg(target_pointer_width = "64")] -pub type NSUInteger = u64; -#[cfg(target_pointer_width = "32")] -pub type NSUInteger = u32; +pub type CGFloat = f32; + +#[repr(C)] +#[derive(Clone, Copy, Debug, Default, PartialEq)] +pub struct CGSize { + pub width: CGFloat, + pub height: CGFloat, +} + +impl CGSize { + pub fn new(width: f64, height: f64) -> Self { + CGSize { width, height } + } +} #[repr(C)] #[derive(Copy, Clone)] diff --git a/third_party/rust/metal/src/library.rs b/third_party/rust/metal/src/library.rs index 894b8ceb1495..93cbfefc611e 100644 --- a/third_party/rust/metal/src/library.rs +++ b/third_party/rust/metal/src/library.rs @@ -7,6 +7,7 @@ use super::*; +use cocoa_foundation::foundation::NSUInteger; use foreign_types::ForeignType; use objc::runtime::{Object, BOOL, NO, YES}; diff --git a/third_party/rust/metal/src/pipeline/compute.rs b/third_party/rust/metal/src/pipeline/compute.rs index 138b561cb32e..744eca388144 100644 --- a/third_party/rust/metal/src/pipeline/compute.rs +++ b/third_party/rust/metal/src/pipeline/compute.rs @@ -7,6 +7,7 @@ use super::*; +use cocoa_foundation::foundation::NSUInteger; use objc::runtime::{NO, YES}; #[repr(u64)] diff --git a/third_party/rust/metal/src/pipeline/render.rs b/third_party/rust/metal/src/pipeline/render.rs index 31f90fd3b8d0..dc59d70ecc3e 100644 --- a/third_party/rust/metal/src/pipeline/render.rs +++ b/third_party/rust/metal/src/pipeline/render.rs @@ -7,6 +7,7 @@ use super::*; +use cocoa_foundation::foundation::NSUInteger; use objc::runtime::{NO, YES}; #[repr(u64)] diff --git a/third_party/rust/metal/src/renderpass.rs b/third_party/rust/metal/src/renderpass.rs index ed4f60a43da2..4d1e867eb741 100644 --- a/third_party/rust/metal/src/renderpass.rs +++ b/third_party/rust/metal/src/renderpass.rs @@ -7,6 +7,8 @@ use super::*; +use cocoa_foundation::foundation::NSUInteger; + #[repr(u64)] #[derive(Copy, Clone, Debug)] pub enum MTLLoadAction { @@ -255,9 +257,11 @@ foreign_obj_type! { } impl RenderPassDescriptor { - /// Creates a default render pass descriptor with no attachments. pub fn new<'a>() -> &'a RenderPassDescriptorRef { - unsafe { msg_send![class!(MTLRenderPassDescriptor), renderPassDescriptor] } + unsafe { + let class = class!(MTLRenderPassDescriptorInternal); + msg_send![class, renderPassDescriptor] + } } } diff --git a/third_party/rust/metal/src/resource.rs b/third_party/rust/metal/src/resource.rs index 7c742bc185fe..0fafd658ada4 100644 --- a/third_party/rust/metal/src/resource.rs +++ b/third_party/rust/metal/src/resource.rs @@ -5,7 +5,8 @@ // http://opensource.org/licenses/MIT>, at your option. This file may not be // copied, modified, or distributed except according to those terms. -use super::{DeviceRef, HeapRef, NSUInteger}; +use super::{DeviceRef, HeapRef}; +use cocoa_foundation::foundation::NSUInteger; use objc::runtime::{NO, YES}; #[repr(u64)] diff --git a/third_party/rust/metal/src/sampler.rs b/third_party/rust/metal/src/sampler.rs index 3dd871a3b48b..d63eacb08c93 100644 --- a/third_party/rust/metal/src/sampler.rs +++ b/third_party/rust/metal/src/sampler.rs @@ -5,7 +5,10 @@ // http://opensource.org/licenses/MIT>, at your option. This file may not be // copied, modified, or distributed except according to those terms. -use super::{depthstencil::MTLCompareFunction, DeviceRef, NSUInteger}; +use cocoa_foundation::foundation::NSUInteger; + +use crate::depthstencil::MTLCompareFunction; +use crate::DeviceRef; #[repr(u64)] #[derive(Copy, Clone, Debug, Eq, PartialEq, Hash)] diff --git a/third_party/rust/metal/src/texture.rs b/third_party/rust/metal/src/texture.rs index 6bcd50ce630e..c49a450f1106 100644 --- a/third_party/rust/metal/src/texture.rs +++ b/third_party/rust/metal/src/texture.rs @@ -7,6 +7,7 @@ use super::*; +use cocoa_foundation::foundation::NSUInteger; use objc::runtime::{NO, YES}; #[repr(u64)] diff --git a/third_party/rust/metal/src/types.rs b/third_party/rust/metal/src/types.rs index 3ea937f06154..ddd4efd40189 100644 --- a/third_party/rust/metal/src/types.rs +++ b/third_party/rust/metal/src/types.rs @@ -5,7 +5,7 @@ // http://opensource.org/licenses/MIT>, at your option. This file may not be // copied, modified, or distributed except according to those terms. -use super::NSUInteger; +use cocoa_foundation::foundation::NSUInteger; use std::default::Default; #[repr(C)] diff --git a/third_party/rust/metal/src/vertexdescriptor.rs b/third_party/rust/metal/src/vertexdescriptor.rs index 201e1e30ad31..5fba52f65952 100644 --- a/third_party/rust/metal/src/vertexdescriptor.rs +++ b/third_party/rust/metal/src/vertexdescriptor.rs @@ -5,7 +5,7 @@ // http://opensource.org/licenses/MIT>, at your option. This file may not be // copied, modified, or distributed except according to those terms. -use super::NSUInteger; +use cocoa_foundation::foundation::NSUInteger; #[repr(u64)] #[allow(non_camel_case_types)] diff --git a/third_party/rust/naga/.cargo-checksum.json b/third_party/rust/naga/.cargo-checksum.json index b2d396714eb9..02412604900d 100644 --- a/third_party/rust/naga/.cargo-checksum.json +++ b/third_party/rust/naga/.cargo-checksum.json @@ -1 +1 @@ -{"files":{"CHANGELOG.md":"5d48b7d8172e1ad984374a4f8ff3932f1804d4943a74d0dcd5d574f1c725c6bc","Cargo.toml":"00270872d0f56c545324f37c20d42d14843ac8b9ef289aad0944cda0fa845cdc","LICENSE-APACHE":"c71d239df91726fc519c6eb72d318ec65820627232b2f796219e87dcf35d0ab4","LICENSE-MIT":"ca3be8518f5ef097669cea882643fc532025f29972def4fda49df885565a0480","Makefile":"ee07b374ffb73b5f6508557cc617599dad7a9313433b2e346bdd9e1866f4642e","README.md":"c4acd1120c70c29745e967885f7be1bf63c2ab1e0117bf14a0bc6b6e84f14712","src/arena.rs":"b57ecba85ea4bffd5d4a54b3bb3ed00555afe173892d19ed8e543b656e553eb0","src/back/dot/mod.rs":"97e8fa7c8197a690d0d53b45c5b024ae6d962ff8b1f1615e624fa18a22afcf24","src/back/glsl/features.rs":"5c930bbaa8df39e5deb2d7622058fed83faa8f1d84907e41c0121c10970553ba","src/back/glsl/keywords.rs":"3f23b1e63e99a7056c3b223524d5d37000ef7316ae9df25532a726a1157d1dcd","src/back/glsl/mod.rs":"ea2340e17c03283ee37e67825a13b37d5184c2b1f009772d0ba23480321da119","src/back/hlsl/keywords.rs":"53381f5cbfa819e436757f34410ebfdeb9227f144a772c6b9a2c9443a0ec21cd","src/back/hlsl/mod.rs":"30393384460af3909720236bf8f7a44f9645c2a06077c7765f096f4c0e98985f","src/back/hlsl/writer.rs":"654970da2988b0e034a9f11557a98fd57cce81910d7042795580b2ade757e025","src/back/mod.rs":"28bff448fc2de246a6d478d5d086f2fadb642c4233e787c5302df522ee94c941","src/back/msl/keywords.rs":"868d9873ccf3eb70c78c10badae6c627d1dddebd808d1279b87d2fcc959af6aa","src/back/msl/mod.rs":"bbcf519d60b066c823fc0f0450d4a0718594c5c3d63870b2978fc02652e85496","src/back/msl/sampler.rs":"19a905f5eb11d9dad769b60694d1ed7a16ad36ce92b57f9bf70f0a60cd0df1ee","src/back/msl/writer.rs":"0adfc7319c797f96a28d103612bcb262183f74de5e26fe0053925ffa6de059a2","src/back/spv/helpers.rs":"98dacb36b64ab2e702d98bce49682b0a02dc4c726881b6301d54bcd16f1c881e","src/back/spv/instructions.rs":"5e7e7904833782d96748e9a6d92ce98950b7279051834abd874003db9175eb6d","src/back/spv/layout.rs":"173ec4d496ab53becdd6a223073d142e2a4b2a25d297b9f9f67059777613b5c2","src/back/spv/mod.rs":"87ef58d7a6680d0cce0033d9507862424034c0a5b1f5486493f1a01364aa9212","src/back/spv/writer.rs":"fa806e47575f56fc152d72b00339314057cbf9ef2a36195f5051420aa649945e","src/back/wgsl/keywords.rs":"d3e775d961265db10d57060db586e5e584d33a4febb8ef527471cb1c0b6ae5d1","src/back/wgsl/mod.rs":"c095062009d9a75db47069cd6ce268f2464219f3b28eefd16381d87ca02d4856","src/back/wgsl/writer.rs":"e256c87ebbad54238a6ea1b61d5d7242d9eb994f3130e4d0c8be593d147d8506","src/front/glsl/ast.rs":"a5f20c913c8fd6de9e5c4eb462c6e0b020397fc01824ff1baac0a7459aaf0e1c","src/front/glsl/constants.rs":"0ebcf92cf5d01e7ad1c4618790b6f45103a19fa43f479d60547e4d2cd96e011c","src/front/glsl/error.rs":"5804d448555ac5d0b68c29f2fcfba4ce295dcfc9291c4accb334e35ecd27a108","src/front/glsl/functions.rs":"cef6ad6e59429b900c1817ea54d2a7ef647413e01a57ed8f3d8f6ee5b449b9a7","src/front/glsl/lex.rs":"7f8048bbbd016821e61c8f89ab0d9f33ddb8190f18b5a26dcd1470b322efc053","src/front/glsl/mod.rs":"ba9611d379713eee51cd21483623e309fce0f95cb3ce6d73450a3a93778f52d4","src/front/glsl/parser.rs":"426c22812ef62d1943d9cf5d68731c15ada46ed3f0be50310e624386e55a3a94","src/front/glsl/parser_tests.rs":"b8bd1a838a8951f7a8523b3e035fcaea2ba2a9b438ae611d7e874632e0b2f58e","src/front/glsl/token.rs":"a5c4a356981d285124a227eb8af6b6393d57ac658c5d42e734eb7d74d3cea1e0","src/front/glsl/types.rs":"15bffe9885a952db4c9bd1ab6b4b300147289e36f4e8a1dd2e473733187630e8","src/front/glsl/variables.rs":"da9f7abdf2efa54c2e5afb5f17c4230392544522835e9e9fe32c70cfb7f132b5","src/front/mod.rs":"7af6b2ac8ced2cab458be416f7abc6a4946f3a26166abe00e469254851fa3ca1","src/front/spv/convert.rs":"a341a28a33d47f05b9683c79e4de797d50a9c90da9899f1a757990fadbc38fe1","src/front/spv/error.rs":"dcc94e0523b47a94b8e9453967ace96795afd1ec232eaee8f7dc46f3fd49cf90","src/front/spv/flow.rs":"7bdbd9340f41a3f4163aeaec9cc808b2216304dadfd18f4d91abbb4b37e12404","src/front/spv/function.rs":"d0363089464e5048496c26685463bc6517ad38570647e21547ff3b0d0d609edc","src/front/spv/image.rs":"659d0656067531ff98884e57f1ecdbf4168fd3563e8e7f8ded7e2225ff259bd8","src/front/spv/mod.rs":"2d14d99e9ffe0f28b582165abd73e4ef7e9a34ccf172ca8a746aaebdfece9dfb","src/front/spv/null.rs":"fb93c76a5f7b3412bf0246de836abe56ad3cf1d55d396290b5276be35236b85c","src/front/wgsl/conv.rs":"b576d1f73b2cc88b4442dc61c925ae7cdf8f4d082620eea543525322962db448","src/front/wgsl/lexer.rs":"484038ee057282ac4d6d7297b4b0d1994a2d8feae3dfed8ce2e050034a4ca7f8","src/front/wgsl/mod.rs":"9b896b93cf240f787af717e167a09f5b8a1500217feaed569b3d3f0cd4b99d6a","src/front/wgsl/tests.rs":"1fc45a721fc359b49b8f48863a3b42f1eb77e73f2bf6883dcdbb9e3e0b425e93","src/lib.rs":"89eeccc13e96634e90ac9c2f4d4d1ee5cd2052a04656fdde614255a1ed9da480","src/proc/interpolator.rs":"94cd36eefa80bcbeaf58023f13cf44d0172ce0db7faab5dc8560a38405fcbea6","src/proc/layouter.rs":"470d27709765675bcb8e0ea952162b23910bffa311cfe694e6b957ed2b8680d2","src/proc/mod.rs":"18c35a2189c802a1b2b199518c0f8b39ae5a25843199a04bc00cc9d818f55b91","src/proc/namer.rs":"5dff838bf6a156741aea5fdf87338742bce930522bf3763ac14fc4ee6a05eeaf","src/proc/terminator.rs":"db284e5dd3cf740d96cbf926487061779a2cdccafaded213fd98993a6e31416d","src/proc/typifier.rs":"ca25d4e91a5bf1533d6106ce91a7c1aee5c8c1316cbabcf26cf335a9ebaca05d","src/valid/analyzer.rs":"cf5d94cf21cde3e84e81692c8de4b09ececc69dbea1eae2dc16eb96b77546b6e","src/valid/compose.rs":"f8a0cfa702ba9dd0b7167916c6d82bea38486be81d272a3e67905b4913e11974","src/valid/expression.rs":"8beabefe8c1dadd7976edfb6f138bb5b9e29f6a13668644828ad062ce8fd3e69","src/valid/function.rs":"1fe289cfc8f1b6b23f9fec66655cbc2394de89e55f726dc65309bc780fabc616","src/valid/interface.rs":"a47712ba591821eabfda6585ad378a2c1d7b457755da21d4bd3e8e46bb38f771","src/valid/mod.rs":"68902c023ea1051bf37258df77ad0532c190fea7fbea6309d5d2564726fc2a7d","src/valid/type.rs":"f6887da2153d9f710675cbfc73b5b0c5b430277506ebe496fb1e5da7d73ff6ab"},"package":"ef670817eef03d356d5a509ea275e7dd3a78ea9e24261ea3cb2dfed1abb08f64"} \ No newline at end of file +{"files":{".github/workflows/lazy.yml":"c505cf780bcf965b666043f5012abade2203b7c9b99ced7b019cd65b9eea09c0",".github/workflows/pipeline.yml":"b41678ffa4abc4f33b7911233d9f12622a750cf2898b2e231210c25722733403",".github/workflows/validation-linux.yml":"99698620a9b78eab6b32b9f576b6b5cb1c136445efd863132846e03e99c7df21",".github/workflows/validation-macos.yml":"e55a7c1bebf7ea04947d0d95f9ead375d27c80e0ac109403de083df6ad8aa5c8",".github/workflows/validation-windows.yml":"95aec09c0572c9997392ddc9a50abaa7947bb1274c147b8bc27315a5b5b4a1cd",".monocodus":"9be77868b35e2af7e3da0e95f7b3282ca573cd19cbec20c6a6a30435ce6d58c2","CHANGELOG.md":"4f0d048a70d409c3c4172bc245ed515296e1ea8457a05fcbae0ba118205b727a","Cargo.toml":"b1b071d22e5eea44fb56d90dd648d9c3762379ef588438fb157426e1bc5a5c44","LICENSE-APACHE":"c71d239df91726fc519c6eb72d318ec65820627232b2f796219e87dcf35d0ab4","LICENSE-MIT":"ca3be8518f5ef097669cea882643fc532025f29972def4fda49df885565a0480","Makefile":"56dba418a17a36b56a1d00b33d5d2cae2a0ddb08d3ba9fddba5e06a813a53340","README.md":"4433ca316295d48867b905d8cee595e55e6cf7219ab0ead37221047143582232","bin/naga.rs":"48d60f46508cdfca9416c0d26daedd990edb9b9d0b1a63199cf4b05a39f10407","src/arena.rs":"b57ecba85ea4bffd5d4a54b3bb3ed00555afe173892d19ed8e543b656e553eb0","src/back/dot/mod.rs":"ffbac601ff44bc1602db85209f57c07641b613929f657cd7b127f17df6f278e9","src/back/glsl/features.rs":"fbb16c92a86114ae6d05f61170bee234fdb8f7bd913e05c621296a4c845a98b0","src/back/glsl/keywords.rs":"3f23b1e63e99a7056c3b223524d5d37000ef7316ae9df25532a726a1157d1dcd","src/back/glsl/mod.rs":"c22aeae2a91a7e789981be68b24281d19d10899cae8238565ccf7749e9ea98d5","src/back/hlsl/keywords.rs":"5cd1d637458c6db2ddad2f2fde23d72bce1b6ef1c976101c920f4d978a40fab0","src/back/hlsl/mod.rs":"3e4fcae33758dec1c419d8ad10f4ab09782b49bf675c6f505d88c2d23b071b20","src/back/hlsl/writer.rs":"b244476836448b5de36755ecdea61b13cc52025ecf286dfc192d18a48ae2e5e2","src/back/mod.rs":"2d2097f604131fab676b0bceccd6358a63735fdae87924b49599df8a780a1991","src/back/msl/keywords.rs":"868d9873ccf3eb70c78c10badae6c627d1dddebd808d1279b87d2fcc959af6aa","src/back/msl/mod.rs":"1183ce30963b5de6ca526313b63235af32bdbf4a3a4cf91ce041bfa75930ce42","src/back/msl/sampler.rs":"19a905f5eb11d9dad769b60694d1ed7a16ad36ce92b57f9bf70f0a60cd0df1ee","src/back/msl/writer.rs":"9ec6c1fc9e392cb8980c066d11507b8bce1bd807a6b643cd236830a84f567f4f","src/back/spv/helpers.rs":"98dacb36b64ab2e702d98bce49682b0a02dc4c726881b6301d54bcd16f1c881e","src/back/spv/instructions.rs":"5e7e7904833782d96748e9a6d92ce98950b7279051834abd874003db9175eb6d","src/back/spv/layout.rs":"173ec4d496ab53becdd6a223073d142e2a4b2a25d297b9f9f67059777613b5c2","src/back/spv/mod.rs":"87ef58d7a6680d0cce0033d9507862424034c0a5b1f5486493f1a01364aa9212","src/back/spv/writer.rs":"53039b1295d6277403427ea4b72be9cdeebc79b57cf82a499b0712ecf626964e","src/back/wgsl/keywords.rs":"d3e775d961265db10d57060db586e5e584d33a4febb8ef527471cb1c0b6ae5d1","src/back/wgsl/mod.rs":"534752cea52f776721c36afc4ca7a40cf059ffc088884a3c570402af606061f0","src/back/wgsl/writer.rs":"716e0325b73c1d5ec6a01fffa881d821585167dad31633ed702a922d551edcbf","src/front/glsl/ast.rs":"fcaab60404b09832be71270215f6a185b9ab97829b636b978c3f814132fa47db","src/front/glsl/constants.rs":"3e5a5a176cba108d909b06bc7de32f3f9d0429007f52fd80e2adb94513a222a5","src/front/glsl/error.rs":"98d99a2129b73372d64ca04d9a8967e93b32278772d4bbcbc9412364fea4bf80","src/front/glsl/functions.rs":"1758b6b3db1975ee27950aa3536de7b9b990b51d3c3bffca4a86b77d1bb28ef5","src/front/glsl/lex.rs":"0d934d8fd7559e33582c4778060b740cc1a35dbb71113b51989ae67a5c39f225","src/front/glsl/mod.rs":"4efdc9232741467b698acaaf79e499902c6b72e17ad35449bd0b0424122eaaab","src/front/glsl/parser.rs":"525c6d3979fc04ceb23dd62e1502528bfb24dc8769a4742a5368cee755c685a2","src/front/glsl/parser_tests.rs":"619056f38390b2964baaafa17e2652239cbd45f1395a42926ac237d927d316aa","src/front/glsl/token.rs":"8c8d311d43a9991ae71e1eddc2e9a54435cd3fa853819ab15349bfcbd81afe1c","src/front/glsl/types.rs":"15bffe9885a952db4c9bd1ab6b4b300147289e36f4e8a1dd2e473733187630e8","src/front/glsl/variables.rs":"30e1218f667001fcdd1aa266ad4c8d72ff0fb4fc4b75c0ecd521c26772c4deca","src/front/mod.rs":"6e5fbaaed1d7e3f98e2f757361b6804190ed6a874d3b1e998dad7b2891f712c8","src/front/spv/convert.rs":"a341a28a33d47f05b9683c79e4de797d50a9c90da9899f1a757990fadbc38fe1","src/front/spv/error.rs":"199a70f70cbe9f9e4957eaf2db5b228b452e3aa11aac3884e4ca741f4231be72","src/front/spv/flow.rs":"54be50cda486254cbfc7290eac655b0c567137e6cdc95f77420ac5f8dc7a76eb","src/front/spv/function.rs":"ed54b8a3e45e3de7396125e25e29c7b5c8401da26c33c5e3bec46a45ce913e10","src/front/spv/image.rs":"659d0656067531ff98884e57f1ecdbf4168fd3563e8e7f8ded7e2225ff259bd8","src/front/spv/mod.rs":"a37f5ee26125fcf5c38bdbd1dbc45da46c3043e940609d196f6e981a0bf8a2dc","src/front/spv/null.rs":"fb93c76a5f7b3412bf0246de836abe56ad3cf1d55d396290b5276be35236b85c","src/front/wgsl/conv.rs":"b63bb922a287feece6033f37143a38366f95393ec4cafafd1d49562448b926a5","src/front/wgsl/layout.rs":"872cb0aed253e673441726515ebff06f7370387a99a60bb5676e2a29d8129a8c","src/front/wgsl/lexer.rs":"be5700fb3fb57be8b9cf92e3e5b3a942db76d1f9cb9c74aaab6eb1f0ef0c981b","src/front/wgsl/mod.rs":"3d0d4d62ea2600bc9abf18aba91d74040facf51750214f5884ffe53b46b89168","src/front/wgsl/tests.rs":"1fc45a721fc359b49b8f48863a3b42f1eb77e73f2bf6883dcdbb9e3e0b425e93","src/lib.rs":"f6746eb9b98907d9b0d448ceb0957737e8d88457b3f952b7fb0e3745ae770c25","src/proc/interpolator.rs":"3020d3c64351bbd219e0d9c11edf4b009c356f9035ff52be90293918500c52b6","src/proc/mod.rs":"109c0fda8f3cb407dbc709402577b2dd9c87e55ba1d54151736bd55777f6cadb","src/proc/namer.rs":"05da5daeadfa4c62173fbb3b9d89e05d6f0fe36c4d80c62efe557f5db8b92669","src/proc/terminator.rs":"db284e5dd3cf740d96cbf926487061779a2cdccafaded213fd98993a6e31416d","src/proc/typifier.rs":"c16aeb1262f9e191170e4994689b829b173041f914074e00a65fae1d85b18d53","src/valid/analyzer.rs":"fde819bd7c8a69ebfe5aebad6d087f74cd91bb16262f0696dd474893f3d2d767","src/valid/compose.rs":"f8a0cfa702ba9dd0b7167916c6d82bea38486be81d272a3e67905b4913e11974","src/valid/expression.rs":"74b18c41eff44cbbd03c553626c1dc93b5d44a66aad3a9003f0eb502c8992215","src/valid/function.rs":"5e41858dd6493338c4920bc3dc4c11339093b60a4917979ddec3f6e6dcfffa78","src/valid/interface.rs":"56db622f0c7bd5a4ba59e0168fe3c12c8613e0fb38c40f51980fb53af9c94f98","src/valid/mod.rs":"f1c5d0dc5081b4c0a8c56cfff8e3893c0a35bf908bba8bc98bbb73c9c38be279","src/valid/type.rs":"b6bafd800d425e2b781b7611d62b15838b6cdb53513d1567db89aa427362c767","tests/cases/glsl_constant_expression.vert":"89e1ec69f019ea1c91db65ea1509c81a32841b55bc812a4f14caacd9123351eb","tests/cases/glsl_if_preprocessor.vert":"3b7f98c2592f2d4f17dc55896e31b5896111b0f5546e4abbb3abadfeffadfec8","tests/cases/glsl_phong_lighting.frag":"97fe2c7a9ba918256469c8a8810b300664655ee78c62ee50ca5165df6ce50eb1","tests/cases/glsl_preprocessor_abuse.vert":"86ed48d088256d3095a3dfb20d4d3f719b7dbc370c046399bf3147e5847d407a","tests/cases/glsl_vertex_test_shader.vert":"fdd38521bdef9f6edd437f4530af06bd2b9770cb6c9cee35d0f9fb24135590fd","tests/in/access.param.ron":"0750a9af70c632d364b8aaaf4f250696541ea6836ecea1634e2cc153d5476444","tests/in/access.wgsl":"a8d957ac4cdcc3aa085139c6ef74c75ace3fe0daf3c2fd280dd14bd2fc2da2f1","tests/in/boids.param.ron":"53d136ecfdc4427032462594cc1704b3d9f9059231873714b032a99e3247fcb1","tests/in/boids.wgsl":"94cb4bf333aecc4d2b4b15e08b47b46fb66c41027ea1bf2ab11b7ec8f4e7cd27","tests/in/collatz.param.ron":"3f8d5da105996d29d771568ccd886586577f3ec3ef9b1e96989bfd7b7dac7a73","tests/in/collatz.wgsl":"34e740dbf4bdbda2c4d20ea1984d2dfa263ad7b09823ebb857a6a3d85af5e1bb","tests/in/control-flow.param.ron":"1006df1c4817ed7efbddfb8a87eab9416a025d859fbe0653ac07a4f681cb4833","tests/in/control-flow.wgsl":"cd1c9d602e5456e55450ae57a6800a28a04b52b4bffedd68d4708f88988ce92a","tests/in/empty.param.ron":"1006df1c4817ed7efbddfb8a87eab9416a025d859fbe0653ac07a4f681cb4833","tests/in/empty.wgsl":"626dc2c8b00c02b40cf74a87154511068a46fa586c8780765bb506fab5f60c6c","tests/in/extra.param.ron":"d27ddc5e153f8cee2cbcc812542df7ee0523cfcbbbe8f14072d6dad245a009a4","tests/in/extra.wgsl":"93d00d41e037fefc41c6c31f58a412dad19b6ee132c308dadb9a3791349ec21a","tests/in/image.param.ron":"d6490ec332dde52dd0ed6c834e13fa8464cbb33dd350b7db06c8c9bfab881b42","tests/in/image.wgsl":"410ef17efd39cde6ca4643f833d92459f45054bd50314170ac1b70e29fa53d51","tests/in/interpolate.param.ron":"6361c41dbdbbd40c459db4e98c6c45155569246c0dfb582718269f2bced38588","tests/in/interpolate.wgsl":"9977ed39cfbd625c4e212df581407a50979983d9dbcf7edfae708c405db234e5","tests/in/operators.param.ron":"49db98dc08e20de135dc6349e22fc9a43c6f36fdb5b60e4f149bebec22599ec2","tests/in/operators.wgsl":"4c32316418e97f897a0e9ef100598699b058cceb3ef1077fec88c245d3d18510","tests/in/quad-glsl.glsl":"ef2f105c06b15406353b604b686c1e707fa8a49b66985b4e616d59591be4cbff","tests/in/quad-glsl.param.ron":"08dcf65eaadbf7e35aa5de51255bbaa31d4d9dd64247d0d746722a2f086c219e","tests/in/quad-vert.spv":"c194227c35a6b69613c577b5548713ebb058fc9ffbc2d04de4fe673f07b8cb30","tests/in/quad.param.ron":"08dcf65eaadbf7e35aa5de51255bbaa31d4d9dd64247d0d746722a2f086c219e","tests/in/quad.wgsl":"3d90b78b6c97458b8fd044643b5eb76089f1fef72c49be942928be26aee2322d","tests/in/shadow.param.ron":"070919b0c4c1c0fc7bfd916a9a3a9daedde4447135ebb409af35d20f8b973d35","tests/in/shadow.spv":"5a239809a04e7f8937330296394899e423ee86578c692e3581baaadbe36194d5","tests/in/shadow.wgsl":"60ba5cfb0357d8b4b1ade54d951cc2550cc3c438af21037f46457f64d6167c97","tests/in/skybox.param.ron":"0ddbfd11334b236457e83b378e9b3985d1dbc1e9478c6c7e9119f297f937a225","tests/in/skybox.wgsl":"6c5a4cdd6d4a0326ac691dc4a351d2dc7c2c92ceaa62c3e89b718e5a68578775","tests/out/access.msl":"03a4b7e6ed93979a3a8703c14ca17f594ee7a250146f1e8c3eea0e3b81da965a","tests/out/access.spvasm":"cf651d90101830fd832621e788a1a61ec60282db088c6e6c8fd6a24000f00ac9","tests/out/boids.Compute.glsl":"8b51d2f8cbba15d01554dda4ff458dac97a803f08af762cc55bd9e7f4f333cc0","tests/out/boids.msl":"26641ed2fcf6f664f91539a706ce2c1849d670fd078b33d4d239b2fc299e19ee","tests/out/boids.spvasm":"042ef66b277f5fe9f792df0934fc2fd79bb1836265d0c8679fb81ef27831febe","tests/out/collatz.info.ron":"5a1867fbcab44ab0f984abf29dcbd18f3a443b3e4f61876c01e42d5efce09755","tests/out/collatz.msl":"9237b43828726f6bfa7472499afd3e9e8f05df4fa395356b8ddcb6f49a37c5e3","tests/out/collatz.ron":"248d732cbd348343821744c162df4abcd9eacc7c38554233a98ae1e5da21636d","tests/out/collatz.spvasm":"c8c269a4b85054b2eddd46b3a7a81e3cbc796c97f6051923dc911af2a1726d25","tests/out/control-flow.Compute.glsl":"0d63281f341fcb712034e8887a55ac121cd77829fd119032e0c989b39857fc74","tests/out/control-flow.msl":"fe09c7dfe0c849e9e26fd0879a1c7b66067f621d34a38f86135394f30183ffa4","tests/out/control-flow.spvasm":"2cc5502e03bca6584d50a91511cc97901bc1591a2f8200deae671e6f58985289","tests/out/empty.Compute.glsl":"e480da0379e21d9bd05139526ed9946740a530714cf51f0c30fe863da19cb448","tests/out/empty.Compute.hlsl":"a4f05628334b0fede21ffcd24bb4dabc10717993eb8c2d7b5722cb8d1a34aabf","tests/out/empty.hlsl":"0ee78abc7d8ff0edad1b792199c17a9b0a8521d52f87427f996474e334956610","tests/out/empty.msl":"abbb0c34e41482aaac71db58342a3e0fdf97aecb9c103140a91823750dd84333","tests/out/empty.spvasm":"41f3f9c7e3565b68330e9435bea9b1e4ca9844dd26a29a2a821947a20558d818","tests/out/empty.wgsl":"a58ee8af34dbde93255f5e88b16c56e8f511803707d4cef02534a8b20bf55cbf","tests/out/extra.msl":"4d18eea059b6d2f3456b3061a6254da2acb963dfc64d7de00472de268db0ba0c","tests/out/extra.spvasm":"a76b2ec91815181d84a0f1cc059106529675ca9029417eb3e18ec302e7652c44","tests/out/image.msl":"616e9d0db3f9440de56e47be0920f9113ca03ff44ad5a9d11bb9cfb71ccddf31","tests/out/image.spvasm":"0a1fdaa30c360e8958cda56a6307d96fef155e1df2c3cf6b41ada4b75417051b","tests/out/interpolate.Fragment.glsl":"9037751e4708a9b363299537c3290aefbc1162ba243c42cad8bbbf35fa64db00","tests/out/interpolate.Vertex.glsl":"aaed5578af062a60dc8f3f3d9807f1b86bd6ab64fca72b59dcb6254bd67bc315","tests/out/interpolate.msl":"d549fd209965285b9b990278d08f146e37e89c9817e96f902280e826c67a0d65","tests/out/interpolate.spvasm":"685fc961e82b23c53ecf2912d333c77508bd086414d4491ed8bc93497b24709e","tests/out/operators.Vertex.glsl":"01c869f34559149617fc5c329d903f5fb8f6ccd47d7269b84f88f8965b743f83","tests/out/operators.msl":"13666453d900c496ffff3bcf7fcbf6fe03702053b888190ef5b12e6b2964e81d","tests/out/operators.spvasm":"853e7cc8c0d2618a20dc036bd4945b9f34d68cf19e1a3e44db4b62f1ed0e845e","tests/out/quad-vert.Vertex.glsl":"14235088815e90e67878be3ecd28d5508f9be36f504d0956337450a15271ffd3","tests/out/quad-vert.msl":"8f450702bb3771f88f13d8db3c78056ab3331ec6bdd43404bc42b5be94359abf","tests/out/quad-vert.wgsl":"574065de78e244e1f27d66b4773a89a08bd94a7eee7c1264681a740687bf19a5","tests/out/quad.Fragment.glsl":"e3ba9d67634c5c44628a02bc39af5eefd873a33ca065e0c3898f33148c420091","tests/out/quad.Vertex.glsl":"c78660cc0bc7b6801f70d7ec3f387f362b9ae88f79f9c421e890e18bf005c63f","tests/out/quad.dot":"8b734904753b9bcbf38b8907d82eead5fca6986061e43ac08d967c3109462c69","tests/out/quad.msl":"5a19baa26981ba6e8a1da06734a4c2e787db9494b55e4b125da157f56cd80033","tests/out/quad.spvasm":"32736cdce5db49d1131085486a02df0c9ea0ed016032801cf3002111d5a04a2f","tests/out/quad.wgsl":"9b479f4a744579418c20f421e9f7a747463ed91bb6aeeb0ccf35b92483d1071e","tests/out/shadow.Fragment.glsl":"74e460cf3b6de4ad8942142cb798b137bbf316ded224ddf2dda6a1da4e42f53c","tests/out/shadow.info.ron":"9b2a941b3a24b66106656b9790541a5839b2c431740ee47b668290c351555c06","tests/out/shadow.msl":"6f16ef4fbbd2c97ed0f23441604b8cdf4ebdecee7a42f7d2ab8157eccfe60bff","tests/out/shadow.ron":"b1cc1dcb6965f133638fb6d2596a3e473dde421571562c438424c322bbe97e53","tests/out/shadow.spvasm":"697e07deae5ca975f95f9d1697f5cac5432500c52295763ce4bd7968d5f8de80","tests/out/skybox.Fragment.glsl":"e3de0ce91dfd5a1f6045176919e42ffe17efa3a2b33a7dce39e7ec95ffb40fbb","tests/out/skybox.Vertex.glsl":"94394635eff53cefd282747b5c7bc8b04b964f9090765ef359ebab7cb247c302","tests/out/skybox.msl":"b47df24d4854b360624d86f9baf5f841aca4c1e9b429c72a45b363ed45932cd6","tests/out/skybox.spvasm":"d98a633e3805547ea5b60b5bb3367feaddd5e19d10a0b0fbcfa8103ee31e01d4","tests/snapshots.rs":"c41e9797bfa45e9664a9dcc6a96a9b3381ee175cdf6484af858c7134730769f9","tests/wgsl-errors.rs":"1575e4a680d06458cd4e3d8326d77e4ae6ea754f5b137995a19fc1af3391fd46"},"package":null} \ No newline at end of file diff --git a/third_party/rust/naga/.github/workflows/lazy.yml b/third_party/rust/naga/.github/workflows/lazy.yml new file mode 100644 index 000000000000..7344fdac09bc --- /dev/null +++ b/third_party/rust/naga/.github/workflows/lazy.yml @@ -0,0 +1,28 @@ +# Lazy jobs running on master post merges. +name: lazy +on: + push: + branches: [master] + +jobs: + coverage: + name: Coverage + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v2 + - uses: actions-rs/toolchain@v1 + with: + toolchain: stable + - name: Generate report + uses: actions-rs/tarpaulin@v0.1 + with: + args: '--tests --all-features' + - name: Upload to codecov.io + uses: codecov/codecov-action@v1 + with: + token: ${{ secrets.CODECOV_TOKEN }} + - name: Archive code coverage results + uses: actions/upload-artifact@v1 + with: + name: code-coverage-report + path: cobertura.xml diff --git a/third_party/rust/naga/.github/workflows/pipeline.yml b/third_party/rust/naga/.github/workflows/pipeline.yml new file mode 100644 index 000000000000..57d80e5a2119 --- /dev/null +++ b/third_party/rust/naga/.github/workflows/pipeline.yml @@ -0,0 +1,44 @@ +# Regular testing. +name: pipeline +on: [push, pull_request] + +jobs: + test: + name: Test + runs-on: ubuntu-latest + strategy: + matrix: + rust: [1.43.0, nightly] + steps: + - uses: actions/checkout@v2 + - uses: actions-rs/toolchain@v1 + with: + profile: minimal + toolchain: ${{ matrix.rust }} + override: true + - uses: actions-rs/cargo@v1 + name: Default test + with: + command: test + - uses: actions-rs/cargo@v1 + name: Test all features + with: + command: test + args: --all-features + - name: Check snapshots + run: git diff --exit-code -- tests/out + clippy: + name: Clippy + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v2 + - uses: actions-rs/toolchain@v1 + with: + profile: minimal + toolchain: stable + override: true + - run: rustup component add clippy + - uses: actions-rs/cargo@v1 + with: + command: clippy + args: --all-features -- -D warnings diff --git a/third_party/rust/naga/.github/workflows/validation-linux.yml b/third_party/rust/naga/.github/workflows/validation-linux.yml new file mode 100644 index 000000000000..997b80783f71 --- /dev/null +++ b/third_party/rust/naga/.github/workflows/validation-linux.yml @@ -0,0 +1,21 @@ +name: validation-linux +on: + pull_request: + paths: + - 'tests/out/*.spvasm' + - 'tests/out/*.glsl' + - 'tests/out/*.dot' + - 'tests/out/*.wgsl' + +jobs: + validate-linux: + name: SPIR-V + GLSL + runs-on: ubuntu-20.04 + steps: + - uses: actions/checkout@v2 + - name: Install tools + run: sudo apt-get install spirv-tools glslang-tools graphviz + - run: make validate-spv + - run: make validate-glsl + - run: make validate-dot + - run: make validate-wgsl diff --git a/third_party/rust/naga/.github/workflows/validation-macos.yml b/third_party/rust/naga/.github/workflows/validation-macos.yml new file mode 100644 index 000000000000..89a3b13f5106 --- /dev/null +++ b/third_party/rust/naga/.github/workflows/validation-macos.yml @@ -0,0 +1,13 @@ +name: validation-macos +on: + pull_request: + paths: + - 'tests/out/*.msl' + +jobs: + validate-macos: + name: MSL + runs-on: macos-latest + steps: + - uses: actions/checkout@v2 + - run: make validate-msl diff --git a/third_party/rust/naga/.github/workflows/validation-windows.yml b/third_party/rust/naga/.github/workflows/validation-windows.yml new file mode 100644 index 000000000000..95ff1c06b2bd --- /dev/null +++ b/third_party/rust/naga/.github/workflows/validation-windows.yml @@ -0,0 +1,16 @@ +name: validation-windows +on: + pull_request: + paths: + - 'tests/out/*.hlsl' + +jobs: + validate-windows: + name: HLSL + runs-on: windows-latest + steps: + - uses: actions/checkout@v2 + - name: Add DirectXShaderCompiler + uses: napokue/setup-dxc@v1.0.0 + - run: make validate-hlsl + shell: sh \ No newline at end of file diff --git a/third_party/rust/naga/.monocodus b/third_party/rust/naga/.monocodus new file mode 100644 index 000000000000..7900103af173 --- /dev/null +++ b/third_party/rust/naga/.monocodus @@ -0,0 +1,7 @@ +version: 1.1.0 + +rust: + formatter: + name: rustfmt + repo_checkers: + - name: rust-clippy diff --git a/third_party/rust/naga/CHANGELOG.md b/third_party/rust/naga/CHANGELOG.md index 51c9655d89aa..112921fae4d3 100644 --- a/third_party/rust/naga/CHANGELOG.md +++ b/third_party/rust/naga/CHANGELOG.md @@ -1,56 +1,5 @@ # Change Log -## v0.5 (2021-06-18) - - development release for wgpu-0.9 - - API: - - barriers - - dynamic indexing of matrices and arrays is only allowed on variables - - validator now accepts a list of IR capabilities to allow - - improved documentation - - Infrastructure: - - much richer test suite, focused around consuming or emitting WGSL - - lazy testing on large shader corpuses - - the binary is moved to a sub-crate "naga-cli" - - Frontends: - - GLSL frontend: - - rewritten from scratch and effectively revived, no longer depends on `pomelo` - - only supports 440/450/460 versions for now - - has optional support for codespan messages - - SPIRV frontend has improved CFG resolution (still with issues unresolved) - - WGSL got better error messages, workgroup memory support - - Backends: - - general: better expression naming and emitting - - new HLSL backend (in progress) - - MSL: - - support `ArraySize` expression - - better texture sampling instructions - - GLSL: - - multisampling on GLES - - WGSL is vastly improved and now usable - -### v0.4.2 (2021-05-28) - - SPIR-V frontend: - - fix image stores - - fix matrix stride check - - SPIR-V backend: - - fix auto-deriving the capabilities - - GLSL backend: - - support sample interpolation - - write out swizzled vector accesses - -### v0.4.1 (2021-05-14) - - numerous additions and improvements to SPIR-V frontend: - - int8, in16, int64 - - null constant initializers for structs and matrices - - `OpArrayLength`, `OpCopyMemory`, `OpInBoundsAccessChain`, `OpLogicalXxxEqual` - - outer product - - fix struct size alignment - - initialize built-ins with default values - - fix read-only decorations on struct members - - fix struct size alignment in WGSL - - fix `fwidth` in WGSL - - fix scalars arrays in GLSL backend - ## v0.4 (2021-04-29) - development release for wgpu-0.8 - API: diff --git a/third_party/rust/naga/Cargo.toml b/third_party/rust/naga/Cargo.toml index 6c4913beb4dc..c0f34d8bd930 100644 --- a/third_party/rust/naga/Cargo.toml +++ b/third_party/rust/naga/Cargo.toml @@ -1,101 +1,55 @@ -# THIS FILE IS AUTOMATICALLY GENERATED BY CARGO -# -# When uploading crates to the registry Cargo will automatically -# "normalize" Cargo.toml files for maximal compatibility -# with all versions of Cargo and also rewrite `path` dependencies -# to registry (e.g., crates.io) dependencies -# -# If you believe there's an error in this file please file an -# issue against the rust-lang/cargo repository. If you're -# editing this file be aware that the upstream Cargo.toml -# will likely look very different (and much more reasonable) - [package] -edition = "2018" name = "naga" -version = "0.5.0" +version = "0.4.0" authors = ["Naga Developers"] -exclude = ["bin/**/*", "tests/**/*", "Cargo.lock", "target/**/*"] +edition = "2018" description = "Shader translation infrastructure" homepage = "https://github.com/gfx-rs/naga" +repository = "https://github.com/gfx-rs/naga" keywords = ["shader", "SPIR-V", "GLSL", "MSL"] license = "MIT OR Apache-2.0" -repository = "https://github.com/gfx-rs/naga/tree/v0.5" -resolver = "2" + [package.metadata.docs.rs] all-features = true -[dependencies.bit-set] -version = "0.5" -[dependencies.bitflags] -version = "1" - -[dependencies.codespan-reporting] -version = "0.11.0" -optional = true - -[dependencies.fxhash] -version = "0.2" - -[dependencies.log] -version = "0.4" - -[dependencies.num-traits] -version = "0.2" - -[dependencies.petgraph] -version = "0.5" -optional = true - -[dependencies.pp-rs] -version = "0.2.1" -optional = true - -[dependencies.rose_tree] -version = "0.2" -optional = true - -[dependencies.serde] -version = "1.0" -features = ["derive"] -optional = true - -[dependencies.spirv] -version = "1.5" -optional = true -package = "spirv_headers" - -[dependencies.thiserror] -version = "1.0.21" -[dev-dependencies.diff] -version = "0.1" - -[dev-dependencies.ron] -version = "0.6" - -[dev-dependencies.rspirv] -version = "0.7" - -[dev-dependencies.serde] -version = "1.0" -features = ["derive"] - -[dev-dependencies.spirv] -version = "1.5" -features = ["deserialize"] -package = "spirv_headers" +[dependencies] +bitflags = "1" +bit-set = "0.5" +codespan-reporting = { version = "0.11.0", optional = true } +fxhash = "0.2" +log = "0.4" +num-traits = "0.2" +spirv = { package = "spirv_headers", version = "1.5", optional = true } +pomelo = { version = "0.1.4", optional = true } +thiserror = "1.0.21" +serde = { version = "1.0", features = ["derive"], optional = true } +petgraph = { version ="0.5", optional = true } +rose_tree = { version ="0.2", optional = true } +pp-rs = { version = "0.1", optional = true } +#env_logger = "0.8" # uncomment temporarily for developing with the binary target [features] default = [] -deserialize = ["serde"] dot-out = [] -glsl-in = ["pp-rs"] -glsl-out = ["petgraph"] +glsl-in = ["pomelo", "pp-rs"] glsl-validate = [] -hlsl-out = [] +glsl-out = ["petgraph"] msl-out = [] serialize = ["serde"] +deserialize = ["serde"] spv-in = ["petgraph", "spirv", "rose_tree"] spv-out = ["spirv"] wgsl-in = ["codespan-reporting"] wgsl-out = [] +hlsl-out = [] + +[[bin]] +name = "naga" +path = "bin/naga.rs" + +[dev-dependencies] +diff = "0.1" +ron = "0.6" +serde = { version = "1.0", features = ["derive"] } +spirv = { package = "spirv_headers", version = "1.5", features = ["deserialize"] } +rspirv = "0.7" diff --git a/third_party/rust/naga/Makefile b/third_party/rust/naga/Makefile index 016f7a37e9f0..397cbaf5bf0b 100644 --- a/third_party/rust/naga/Makefile +++ b/third_party/rust/naga/Makefile @@ -5,8 +5,8 @@ SNAPSHOTS_OUT=tests/out all: cargo fmt - cargo test --all-features --workspace - cargo clippy --all-features --workspace -- -D warnings + cargo test --all-features + cargo clippy --all-features clean: rm *.metal *.air *.metallib *.vert *.frag *.comp *.spv @@ -61,20 +61,12 @@ validate-dot: $(SNAPSHOTS_OUT)/*.dot validate-wgsl: $(SNAPSHOTS_OUT)/*.wgsl @set -e && for file in $^ ; do \ echo "Validating" $${file#"$(SNAPSHOTS_OUT)/"}; \ - cargo run $${file}; \ + cargo run --features wgsl-in $${file}; \ done validate-hlsl: $(SNAPSHOTS_OUT)/*.hlsl - @set -e && for file in $^ ; do \ - echo "Validating" $${file#"$(SNAPSHOTS_OUT)/"}; \ - config="$$(dirname $${file})/$$(basename $${file}).config"; \ - vertex=""\ - fragment="" \ - compute="" \ - . $${config}; \ - DXC_PARAMS="-Wno-parentheses-equality -Zi -Qembed_debug;" \ - [ ! -z "$${vertex}" ] && echo "Vertex Stage:" && dxc $${file} -T $${vertex} -E $${vertex_name} $${DXC_PARAMS} > /dev/null; \ - [ ! -z "$${fragment}" ] && echo "Fragment Stage:" && dxc $${file} -T $${fragment} -E $${fragment_name} $${DXC_PARAMS} > /dev/null; \ - [ ! -z "$${compute}" ] && echo "Compute Stage:" && dxc $${file} -T $${compute} -E $${compute_name} $${DXC_PARAMS} > /dev/null; \ - echo "======================"; \ + @set -e && for file in $(SNAPSHOTS_OUT)/*.Compute.hlsl ; do \ + echo "Validating" $${file#"$(SNAPSHOTS_OUT)/"};\ + dxc $${file} -T cs_6_0;\ done + diff --git a/third_party/rust/naga/README.md b/third_party/rust/naga/README.md index 044d4d99aba7..cd8420c72be8 100644 --- a/third_party/rust/naga/README.md +++ b/third_party/rust/naga/README.md @@ -4,7 +4,6 @@ [![Crates.io](https://img.shields.io/crates/v/naga.svg?label=naga)](https://crates.io/crates/naga) [![Docs.rs](https://docs.rs/naga/badge.svg)](https://docs.rs/naga) [![Build Status](https://github.com/gfx-rs/naga/workflows/pipeline/badge.svg)](https://github.com/gfx-rs/naga/actions) -![MSRV](https://img.shields.io/badge/rustc-1.43+-blue.svg) [![codecov.io](https://codecov.io/gh/gfx-rs/naga/branch/master/graph/badge.svg?token=9VOKYO8BM2)](https://codecov.io/gh/gfx-rs/naga) The shader translation library for the needs of [wgpu](https://github.com/gfx-rs/wgpu) and [gfx-rs](https://github.com/gfx-rs/gfx) projects. @@ -17,14 +16,14 @@ Front-end | Status | Feature | Notes | --------------- | ------------------ | ------- | ----- | SPIR-V (binary) | :white_check_mark: | spv-in | | WGSL | :white_check_mark: | wgsl-in | Fully validated | -GLSL | :ok: | glsl-in | | +GLSL | :construction: | glsl-in | Temporarily broken | Back-end | Status | Feature | Notes | --------------- | ------------------ | -------- | ----- | SPIR-V | :white_check_mark: | spv-out | | -WGSL | :ok: | wgsl-out | | +WGSL | :construction: | wgsl-out | | Metal | :white_check_mark: | msl-out | | -HLSL | :construction: | hlsl-out | Shader Model 5.0+ (DirectX 11+) | +HLSL | :construction: | hlsl-out | | GLSL | :ok: | glsl-out | | AIR | | | | DXIL/DXIR | | | | @@ -37,15 +36,15 @@ DOT (GraphViz) | :ok: | dot-out | Not a shading language | Naga includes a default binary target, which allows to test the conversion of different code paths. ```bash -cargo run my_shader.wgsl # validate only -cargo run my_shader.spv my_shader.txt # dump the IR module into a file -cargo run my_shader.spv my_shader.metal --flow-dir flow-dir # convert the SPV to Metal, also dump the SPIR-V flow graph to `flow-dir` -cargo run my_shader.wgsl my_shader.vert --profile es310 # convert the WGSL to GLSL vertex stage under ES 3.20 profile +cargo run --features wgsl-in -- my_shader.wgsl # validate only +cargo run --features spv-in -- my_shader.spv my_shader.txt # dump the IR module into a file +cargo run --features spv-in,msl-out -- my_shader.spv my_shader.metal --flow-dir flow-dir # convert the SPV to Metal, also dump the SPIR-V flow graph to `flow-dir` +cargo run --features wgsl-in,glsl-out -- my_shader.wgsl my_shader.vert --profile es310 # convert the WGSL to GLSL vertex stage under ES 3.20 profile ``` ## Development workflow -The main instrument aiding the development is the good old `cargo test --all-features --workspace`, +The main instrument aiding the development is the good old `cargo test --all-features`, which will run the unit tests, and also update all the snapshots. You'll see these changes in git before committing the code. diff --git a/third_party/rust/naga/bin/naga.rs b/third_party/rust/naga/bin/naga.rs new file mode 100644 index 000000000000..09c1f64dc3a4 --- /dev/null +++ b/third_party/rust/naga/bin/naga.rs @@ -0,0 +1,298 @@ +#![allow(clippy::manual_strip)] +#[allow(unused_imports)] +use std::fs; +use std::{env, error::Error, path::Path}; + +#[derive(Default)] +struct Parameters { + validation_flags: naga::valid::ValidationFlags, + #[cfg(feature = "spv-in")] + spv_adjust_coordinate_space: bool, + #[cfg(feature = "spv-in")] + spv_flow_dump_prefix: Option, + #[cfg(feature = "spv-out")] + spv: naga::back::spv::Options, + #[cfg(feature = "msl-out")] + msl: naga::back::msl::Options, + #[cfg(feature = "glsl-out")] + glsl: naga::back::glsl::Options, +} + +trait PrettyResult { + type Target; + fn unwrap_pretty(self) -> Self::Target; +} + +fn print_err(error: impl Error) { + eprintln!("{}:", error); + let mut e = error.source(); + while let Some(source) = e { + eprintln!("\t{}", source); + e = source.source(); + } +} + +impl PrettyResult for Result { + type Target = T; + fn unwrap_pretty(self) -> T { + match self { + Result::Ok(value) => value, + Result::Err(error) => { + print_err(error); + std::process::exit(1); + } + } + } +} + +fn main() { + //env_logger::init(); // uncomment during development + + let mut input_path = None; + let mut output_paths = Vec::new(); + //TODO: read the parameters from RON? + #[allow(unused_mut)] + let mut params = Parameters::default(); + + let mut args = env::args(); + let _ = args.next().unwrap(); + #[allow(clippy::while_let_on_iterator)] + while let Some(arg) = args.next() { + //TODO: use `strip_prefix` when MSRV reaches 1.45.0 + if arg.starts_with("--") { + match &arg[2..] { + "validate" => { + let value = args.next().unwrap().parse().unwrap(); + params.validation_flags = + naga::valid::ValidationFlags::from_bits(value).unwrap(); + } + #[cfg(feature = "spv-in")] + "flow-dir" => params.spv_flow_dump_prefix = args.next(), + #[cfg(feature = "glsl-out")] + "entry-point" => params.glsl.entry_point = args.next().unwrap(), + #[cfg(feature = "glsl-out")] + "profile" => { + use naga::back::glsl::Version; + let string = args.next().unwrap(); + //TODO: use `strip_prefix` in 1.45.0 + params.glsl.version = if string.starts_with("core") { + Version::Desktop(string[4..].parse().unwrap_or(330)) + } else if string.starts_with("es") { + Version::Embedded(string[2..].parse().unwrap_or(310)) + } else { + panic!("Unknown profile: {}", string) + }; + } + other => log::warn!("Unknown parameter: {}", other), + } + } else if input_path.is_none() { + input_path = Some(arg); + } else { + output_paths.push(arg); + } + } + + let input_path = match input_path { + Some(ref string) => string, + None => { + println!("Call with []"); + return; + } + }; + let module = match Path::new(input_path) + .extension() + .expect("Input has no extension?") + .to_str() + .unwrap() + { + #[cfg(feature = "spv-in")] + "spv" => { + let options = naga::front::spv::Options { + adjust_coordinate_space: params.spv_adjust_coordinate_space, + strict_capabilities: false, + flow_graph_dump_prefix: params.spv_flow_dump_prefix.map(std::path::PathBuf::from), + }; + let input = fs::read(input_path).unwrap(); + naga::front::spv::parse_u8_slice(&input, &options).unwrap() + } + #[cfg(feature = "wgsl-in")] + "wgsl" => { + let input = fs::read_to_string(input_path).unwrap(); + let result = naga::front::wgsl::parse_str(&input); + match result { + Ok(v) => v, + Err(ref e) => { + e.emit_to_stderr(&input); + panic!("unable to parse WGSL"); + } + } + } + #[cfg(feature = "glsl-in")] + "vert" => { + let input = fs::read_to_string(input_path).unwrap(); + let mut entry_points = naga::FastHashMap::default(); + entry_points.insert("main".to_string(), naga::ShaderStage::Vertex); + naga::front::glsl::parse_str( + &input, + &naga::front::glsl::Options { + entry_points, + defines: Default::default(), + }, + ) + .unwrap_pretty() + } + #[cfg(feature = "glsl-in")] + "frag" => { + let input = fs::read_to_string(input_path).unwrap(); + let mut entry_points = naga::FastHashMap::default(); + entry_points.insert("main".to_string(), naga::ShaderStage::Fragment); + naga::front::glsl::parse_str( + &input, + &naga::front::glsl::Options { + entry_points, + defines: Default::default(), + }, + ) + .unwrap_pretty() + } + #[cfg(feature = "glsl-in")] + "comp" => { + let input = fs::read_to_string(input_path).unwrap(); + let mut entry_points = naga::FastHashMap::default(); + entry_points.insert("main".to_string(), naga::ShaderStage::Compute); + naga::front::glsl::parse_str( + &input, + &naga::front::glsl::Options { + entry_points, + defines: Default::default(), + }, + ) + .unwrap_pretty() + } + other => { + if true { + // prevent "unreachable_code" warnings + panic!("Unknown input extension: {}", other); + } + naga::Module::default() + } + }; + + // validate the IR + let info = match naga::valid::Validator::new( + params.validation_flags, + naga::valid::Capabilities::all(), + ) + .validate(&module) + { + Ok(info) => Some(info), + Err(error) => { + print_err(error); + None + } + }; + + if output_paths.is_empty() { + if info.is_some() { + println!("Validation successful"); + return; + } else { + std::process::exit(!0); + } + } + + for output_path in output_paths { + match Path::new(&output_path) + .extension() + .expect("Output has no extension?") + .to_str() + .unwrap() + { + "txt" => { + use std::io::Write; + + let mut file = fs::File::create(output_path).unwrap(); + writeln!(file, "{:#?}", module).unwrap(); + if let Some(ref info) = info { + writeln!(file).unwrap(); + writeln!(file, "{:#?}", info).unwrap(); + } + } + #[cfg(feature = "msl-out")] + "metal" => { + use naga::back::msl; + + let pipeline_options = msl::PipelineOptions::default(); + let (msl, _) = msl::write_string( + &module, + info.as_ref().unwrap(), + ¶ms.msl, + &pipeline_options, + ) + .unwrap_pretty(); + fs::write(output_path, msl).unwrap(); + } + #[cfg(feature = "spv-out")] + "spv" => { + use naga::back::spv; + + let spv = + spv::write_vec(&module, info.as_ref().unwrap(), ¶ms.spv).unwrap_pretty(); + let bytes = spv + .iter() + .fold(Vec::with_capacity(spv.len() * 4), |mut v, w| { + v.extend_from_slice(&w.to_le_bytes()); + v + }); + + fs::write(output_path, bytes.as_slice()).unwrap(); + } + #[cfg(feature = "glsl-out")] + stage @ "vert" | stage @ "frag" | stage @ "comp" => { + use naga::back::glsl; + + params.glsl.shader_stage = match stage { + "vert" => naga::ShaderStage::Vertex, + "frag" => naga::ShaderStage::Fragment, + "comp" => naga::ShaderStage::Compute, + _ => unreachable!(), + }; + + let mut buffer = String::new(); + let mut writer = + glsl::Writer::new(&mut buffer, &module, info.as_ref().unwrap(), ¶ms.glsl) + .unwrap_pretty(); + writer.write().unwrap(); + fs::write(output_path, buffer).unwrap(); + } + #[cfg(feature = "dot-out")] + "dot" => { + use naga::back::dot; + + let output = dot::write(&module, info.as_ref()).unwrap(); + fs::write(output_path, output).unwrap(); + } + #[cfg(feature = "hlsl-out")] + "hlsl" => { + use naga::back::hlsl; + + let hlsl = hlsl::write_string(&module).unwrap_pretty(); + fs::write(output_path, hlsl).unwrap(); + } + #[cfg(feature = "wgsl-out")] + "wgsl" => { + use naga::back::wgsl; + + let wgsl = wgsl::write_string(&module, info.as_ref().unwrap()).unwrap_pretty(); + fs::write(output_path, wgsl).unwrap(); + } + other => { + let _ = params; + println!( + "Unknown output extension: {}, forgot to enable a feature?", + other + ); + } + } + } +} diff --git a/third_party/rust/naga/src/back/dot/mod.rs b/third_party/rust/naga/src/back/dot/mod.rs index 2659ddc49c98..487a5f946427 100644 --- a/third_party/rust/naga/src/back/dot/mod.rs +++ b/third_party/rust/naga/src/back/dot/mod.rs @@ -9,7 +9,10 @@ use crate::{ valid::{FunctionInfo, ModuleInfo}, }; -use std::fmt::{Error as FmtError, Write as _}; +use std::{ + borrow::Cow, + fmt::{Error as FmtError, Write as _}, +}; #[derive(Default)] struct StatementGraph { @@ -174,16 +177,16 @@ fn write_fun( E::Access { base, index } => { edges.insert("base", base); edges.insert("index", index); - ("Access".into(), 1) + (Cow::Borrowed("Access"), 1) } E::AccessIndex { base, index } => { edges.insert("base", base); - (format!("AccessIndex[{}]", index).into(), 1) + (Cow::Owned(format!("AccessIndex[{}]", index)), 1) } - E::Constant(_) => ("Constant".into(), 2), + E::Constant(_) => (Cow::Borrowed("Constant"), 2), E::Splat { size, value } => { edges.insert("value", value); - (format!("Splat{:?}", size).into(), 3) + (Cow::Owned(format!("Splat{:?}", size)), 3) } E::Swizzle { size, @@ -191,24 +194,27 @@ fn write_fun( pattern, } => { edges.insert("vector", vector); - (format!("Swizzle{:?}", &pattern[..size as usize]).into(), 3) + ( + Cow::Owned(format!("Swizzle{:?}", &pattern[..size as usize])), + 3, + ) } E::Compose { ref components, .. } => { payload = Some(Payload::Arguments(components)); - ("Compose".into(), 3) + (Cow::Borrowed("Compose"), 3) } - E::FunctionArgument(index) => (format!("Argument[{}]", index).into(), 1), + E::FunctionArgument(index) => (Cow::Owned(format!("Argument[{}]", index)), 1), E::GlobalVariable(h) => { payload = Some(Payload::Global(h)); - ("Global".into(), 2) + (Cow::Borrowed("Global"), 2) } E::LocalVariable(h) => { payload = Some(Payload::Local(h)); - ("Local".into(), 1) + (Cow::Borrowed("Local"), 1) } E::Load { pointer } => { edges.insert("pointer", pointer); - ("Load".into(), 4) + (Cow::Borrowed("Load"), 4) } E::ImageSample { image, @@ -242,7 +248,7 @@ fn write_fun( if let Some(expr) = depth_ref { edges.insert("depth_ref", expr); } - ("ImageSample".into(), 5) + (Cow::Borrowed("ImageSample"), 5) } E::ImageLoad { image, @@ -258,7 +264,7 @@ fn write_fun( if let Some(expr) = index { edges.insert("index", expr); } - ("ImageLoad".into(), 5) + (Cow::Borrowed("ImageLoad"), 5) } E::ImageQuery { image, query } => { edges.insert("image", image); @@ -267,20 +273,20 @@ fn write_fun( if let Some(expr) = level { edges.insert("level", expr); } - std::borrow::Cow::from("ImageSize") + Cow::Borrowed("ImageSize") } - _ => format!("{:?}", query).into(), + _ => Cow::Owned(format!("{:?}", query)), }; (args, 7) } E::Unary { op, expr } => { edges.insert("expr", expr); - (format!("{:?}", op).into(), 6) + (Cow::Owned(format!("{:?}", op)), 6) } E::Binary { op, left, right } => { edges.insert("left", left); edges.insert("right", right); - (format!("{:?}", op).into(), 6) + (Cow::Owned(format!("{:?}", op)), 6) } E::Select { condition, @@ -290,15 +296,15 @@ fn write_fun( edges.insert("condition", condition); edges.insert("accept", accept); edges.insert("reject", reject); - ("Select".into(), 3) + (Cow::Borrowed("Select"), 3) } E::Derivative { axis, expr } => { edges.insert("", expr); - (format!("d{:?}", axis).into(), 8) + (Cow::Owned(format!("d{:?}", axis)), 8) } E::Relational { fun, argument } => { edges.insert("arg", argument); - (format!("{:?}", fun).into(), 6) + (Cow::Owned(format!("{:?}", fun)), 6) } E::Math { fun, @@ -313,7 +319,7 @@ fn write_fun( if let Some(expr) = arg2 { edges.insert("arg2", expr); } - (format!("{:?}", fun).into(), 7) + (Cow::Owned(format!("{:?}", fun)), 7) } E::As { kind, @@ -325,12 +331,12 @@ fn write_fun( Some(width) => format!("Convert<{:?},{}>", kind, width), None => format!("Bitcast<{:?}>", kind), }; - (string.into(), 3) + (Cow::Owned(string), 3) } - E::Call(_function) => ("Call".into(), 4), + E::Call(_function) => (Cow::Borrowed("Call"), 4), E::ArrayLength(expr) => { edges.insert("", expr); - ("ArrayLength".into(), 7) + (Cow::Borrowed("ArrayLength"), 7) } }; diff --git a/third_party/rust/naga/src/back/glsl/features.rs b/third_party/rust/naga/src/back/glsl/features.rs index 957b02347617..b78352fdeaab 100644 --- a/third_party/rust/naga/src/back/glsl/features.rs +++ b/third_party/rust/naga/src/back/glsl/features.rs @@ -30,8 +30,6 @@ bitflags::bitflags! { const SAMPLE_QUALIFIER = 1 << 12; const CLIP_DISTANCE = 1 << 13; const CULL_DISTANCE = 1 << 14; - // Sample ID - const SAMPLE_VARIABLES = 1 << 15; } } @@ -97,7 +95,6 @@ impl FeaturesManager { // gl_ClipDistance is supported by core versions > 1.3 and aren't supported by an es versions without extensions check_feature!(CLIP_DISTANCE, 130, 300); check_feature!(CULL_DISTANCE, 450, 300); - check_feature!(SAMPLE_VARIABLES, 400, 300); // Return an error if there are missing features if missing.is_empty() { @@ -186,11 +183,6 @@ impl FeaturesManager { // writeln!(out, "#extension GL_EXT_clip_cull_distance : require")?; } - if self.0.contains(Features::SAMPLE_VARIABLES) && version.is_es() { - // https://www.khronos.org/registry/OpenGL/extensions/OES/OES_sample_variables.txt - writeln!(out, "#extension GL_OES_sample_variables : require")?; - } - Ok(()) } } @@ -318,9 +310,6 @@ impl<'a, W> Writer<'a, W> { crate::BuiltIn::CullDistance => { self.features.request(Features::CULL_DISTANCE) } - crate::BuiltIn::SampleIndex => { - self.features.request(Features::SAMPLE_VARIABLES) - } _ => {} }, Binding::Location { diff --git a/third_party/rust/naga/src/back/glsl/mod.rs b/third_party/rust/naga/src/back/glsl/mod.rs index 3a85a6c9ba39..ef2f14933697 100644 --- a/third_party/rust/naga/src/back/glsl/mod.rs +++ b/third_party/rust/naga/src/back/glsl/mod.rs @@ -106,7 +106,7 @@ impl Version { /// Checks if the version supports explicit `layout(location=)` qualifiers. fn supports_explicit_locations(&self) -> bool { - *self >= Version::Embedded(300) || *self >= Version::Desktop(410) + *self >= Version::Embedded(310) || *self >= Version::Desktop(410) } } @@ -194,11 +194,9 @@ struct FunctionCtx<'a> { info: &'a FunctionInfo, /// The expression arena of the current function being written expressions: &'a Arena, - /// Map of expressions that have associated variable names - named_expressions: &'a crate::NamedExpressions, } -impl<'a> FunctionCtx<'_> { +impl<'a> FunctionCtx<'a> { /// Helper method that generates a [`NameKey`](crate::proc::NameKey) for a local in the current function fn name_key(&self, local: Handle) -> NameKey { match self.func { @@ -297,6 +295,9 @@ pub enum Error { /// A scalar with an unsupported width was requested #[error("A scalar with an unsupported width was requested: {0:?} {1:?}")] UnsupportedScalar(ScalarKind, Bytes), + /// [`Interpolation::Patch`](crate::Interpolation::Patch) isn't supported + #[error("Patch interpolation isn't supported")] + PatchInterpolationNotSupported, /// A image was used with multiple samplers, this isn't supported #[error("A image was used with multiple samplers")] ImageMultipleSamplers, @@ -319,7 +320,6 @@ pub struct Writer<'a, W> { // Internal State /// Features manager used to store all the needed features and write them features: FeaturesManager, - namer: Namer, /// A map with all the names needed for writing the module /// (generated by a [`Namer`](crate::proc::Namer)) names: FastHashMap, @@ -332,7 +332,7 @@ pub struct Writer<'a, W> { /// Used to generate a unique number for blocks block_id: IdGenerator, /// Set of expressions that have associated temporary variables - named_expressions: crate::NamedExpressions, + cached_expressions: FastHashMap, String>, } impl<'a, W: Write> Writer<'a, W> { @@ -363,8 +363,7 @@ impl<'a, W: Write> Writer<'a, W> { // Generate a map with names required to write the module let mut names = FastHashMap::default(); - let mut namer = Namer::default(); - namer.reset(module, keywords::RESERVED_KEYWORDS, &["gl_"], &mut names); + Namer::default().reset(module, keywords::RESERVED_KEYWORDS, &["gl_"], &mut names); // Build the instance let mut this = Self { @@ -372,7 +371,7 @@ impl<'a, W: Write> Writer<'a, W> { info, out, options, - namer, + features: FeaturesManager::new(), names, reflection_names: FastHashMap::default(), @@ -380,7 +379,7 @@ impl<'a, W: Write> Writer<'a, W> { entry_point_idx: ep_idx as u16, block_id: IdGenerator::default(), - named_expressions: crate::NamedExpressions::default(), + cached_expressions: FastHashMap::default(), }; // Find all features required to print this module @@ -521,11 +520,7 @@ impl<'a, W: Write> Writer<'a, W> { // glsl has no concept of samplers so we just ignore it TypeInner::Sampler { .. } => continue, // All other globals are written by `write_global` - _ => { - self.write_global(handle, global)?; - // Add a newline (only for readability) - writeln!(self.out)?; - } + _ => self.write_global(handle, global)?, } } @@ -569,27 +564,6 @@ impl<'a, W: Write> Writer<'a, W> { self.collect_reflection_info() } - fn write_array_size(&mut self, size: ArraySize) -> BackendResult { - write!(self.out, "[")?; - - // Write the array size - // Writes nothing if `ArraySize::Dynamic` - // Panics if `ArraySize::Constant` has a constant that isn't an uint - match size { - ArraySize::Constant(const_handle) => match self.module.constants[const_handle].inner { - ConstantInner::Scalar { - width: _, - value: ScalarValue::Uint(size), - } => write!(self.out, "{}", size)?, - _ => unreachable!(), - }, - ArraySize::Dynamic => (), - } - - write!(self.out, "]")?; - Ok(()) - } - /// Helper method used to write value types /// /// # Notes @@ -641,7 +615,27 @@ impl<'a, W: Write> Writer<'a, W> { // GLSL arrays are written as `type name[size]` // Current code is written arrays only as `[size]` // Base `type` and `name` should be written outside - TypeInner::Array { size, .. } => self.write_array_size(size)?, + TypeInner::Array { base: _, size, .. } => { + write!(self.out, "[")?; + + // Write the array size + // Writes nothing if `ArraySize::Dynamic` + // Panics if `ArraySize::Constant` has a constant that isn't an uint + match size { + ArraySize::Constant(const_handle) => { + match self.module.constants[const_handle].inner { + ConstantInner::Scalar { + width: _, + value: ScalarValue::Uint(size), + } => write!(self.out, "{}", size)?, + _ => unreachable!(), + } + } + ArraySize::Dynamic => (), + } + + write!(self.out, "]")? + } // Panic if either Image, Sampler, Pointer, or a Struct is being written // // Write all variants instead of `_` so that if new variants are added a @@ -669,7 +663,7 @@ impl<'a, W: Write> Writer<'a, W> { // glsl has no pointer types so just write types as normal and loads are skipped TypeInner::Pointer { base, .. } => self.write_type(base), TypeInner::Struct { - top_level: true, + level: crate::StructLevel::Root, ref members, span: _, } => self.write_struct(true, ty, members), @@ -680,8 +674,6 @@ impl<'a, W: Write> Writer<'a, W> { write!(self.out, "{}", name)?; Ok(()) } - // glsl array has the size separated from the base type - TypeInner::Array { base, .. } => self.write_type(base), ref other => self.write_value_type(other), } } @@ -751,7 +743,8 @@ impl<'a, W: Write> Writer<'a, W> { if let Some(storage_class) = glsl_storage_class(global.class) { write!(self.out, "{} ", storage_class)?; } else if let TypeInner::Struct { - top_level: true, .. + level: crate::StructLevel::Root, + .. } = self.module.types[global.ty].inner { write!(self.out, "struct ")?; @@ -764,14 +757,14 @@ impl<'a, W: Write> Writer<'a, W> { // Finally write the global name and end the global with a `;` and a newline // Leading space is important let global_name = self.get_global_name(handle, global); - write!(self.out, " {}", global_name)?; - if let TypeInner::Array { size, .. } = self.module.types[global.ty].inner { - self.write_array_size(size)?; - } - if let Some(default_value) = zero_init_value_str(&self.module.types[global.ty].inner) { - write!(self.out, " = {}", default_value)?; - }; - writeln!(self.out, ";")?; + let global_str = + if let Some(default_value) = zero_init_value_str(&self.module.types[global.ty].inner) { + format!("{} = {}", global_name, default_value) + } else { + global_name + }; + writeln!(self.out, " {};", global_str)?; + writeln!(self.out)?; Ok(()) } @@ -830,9 +823,7 @@ impl<'a, W: Write> Writer<'a, W> { } // Write the storage class - if !emit_interpolation_and_auxiliary - && self.options.version.supports_explicit_locations() - { + if self.options.version.supports_explicit_locations() { write!(self.out, "layout(location = {}) ", location)?; } @@ -889,10 +880,9 @@ impl<'a, W: Write> Writer<'a, W> { func: ty, info, expressions: &func.expressions, - named_expressions: &func.named_expressions, }; - self.named_expressions.clear(); + self.cached_expressions.clear(); // Write the function header // @@ -1128,11 +1118,7 @@ impl<'a, W: Write> Writer<'a, W> { write!(self.out, "{}", INDENT)?; match self.module.types[member.ty].inner { - TypeInner::Array { - base, - size, - stride: _, - } => { + TypeInner::Array { base, .. } => { // GLSL arrays are written as `type name[size]` let ty_name = match self.module.types[base].inner { // Write scalar type by backend so as not to depend on the front-end implementation @@ -1149,7 +1135,7 @@ impl<'a, W: Write> Writer<'a, W> { &self.names[&NameKey::StructMember(handle, idx as u32)] )?; // Write [size] - self.write_array_size(size)?; + self.write_type(member.ty)?; // Newline is important writeln!(self.out, ";")?; } @@ -1195,24 +1181,30 @@ impl<'a, W: Write> Writer<'a, W> { // This is where we can generate intermediate constants for some expression types. Statement::Emit(ref range) => { for handle in range.clone() { - let expr_name = if let Some(name) = ctx.named_expressions.get(&handle) { - // Front end provides names for all variables at the start of writing. - // But we write them to step by step. We need to recache them - // Otherwise, we could accidentally write variable name instead of full expression. - // Also, we use sanitized names! It defense backend from generating variable with name from reserved keywords. - Some(self.namer.call_unique(name)) - } else { - let min_ref_count = ctx.expressions[handle].bake_ref_count(); - if min_ref_count <= ctx.info[handle].ref_count { - Some(format!("_expr{}", handle.index())) - } else { - None - } - }; - - if let Some(name) = expr_name { + let min_ref_count = ctx.expressions[handle].bake_ref_count(); + if min_ref_count <= ctx.info[handle].ref_count { write!(self.out, "{}", INDENT.repeat(indent))?; - self.write_named_expr(handle, name, ctx)?; + match ctx.info[handle].ty { + TypeResolution::Handle(ty_handle) => { + match self.module.types[ty_handle].inner { + TypeInner::Struct { .. } => { + let ty_name = &self.names[&NameKey::Type(ty_handle)]; + write!(self.out, "{}", ty_name)?; + } + _ => { + self.write_type(ty_handle)?; + } + } + } + TypeResolution::Value(ref inner) => { + self.write_value_type(inner)?; + } + } + let name = format!("_expr{}", handle.index()); + write!(self.out, " {} = ", name)?; + self.write_expr(handle, ctx)?; + writeln!(self.out, ";")?; + self.cached_expressions.insert(handle, name); } } } @@ -1242,7 +1234,7 @@ impl<'a, W: Write> Writer<'a, W> { ref reject, } => { write!(self.out, "{}", INDENT.repeat(indent))?; - write!(self.out, "if (")?; + write!(self.out, "if(")?; self.write_expr(condition, ctx)?; writeln!(self.out, ") {{")?; @@ -1491,7 +1483,7 @@ impl<'a, W: Write> Writer<'a, W> { let result = self.module.functions[function].result.as_ref().unwrap(); self.write_type(result.ty)?; write!(self.out, " {} = ", name)?; - self.named_expressions.insert(expr, name); + self.cached_expressions.insert(expr, name); } write!(self.out, "{}(", &self.names[&NameKey::Function(function)])?; self.write_slice(arguments, |this, _, arg| this.write_expr(*arg, ctx))?; @@ -1507,11 +1499,10 @@ impl<'a, W: Write> Writer<'a, W> { /// # Notes /// Doesn't add any newlines or leading/trailing spaces fn write_expr(&mut self, expr: Handle, ctx: &FunctionCtx<'_>) -> BackendResult { - if let Some(name) = self.named_expressions.get(&expr) { + if let Some(name) = self.cached_expressions.get(&expr) { write!(self.out, "{}", name)?; return Ok(()); } - match ctx.expressions[expr] { // `Access` is applied to arrays, vectors and matrices and is written as indexing Expression::Access { base, index } => { @@ -1537,11 +1528,8 @@ impl<'a, W: Write> Writer<'a, W> { }; match *resolved { - TypeInner::Vector { .. } => { - // Write vector access as a swizzle - write!(self.out, ".{}", COMPONENTS[index as usize])? - } - TypeInner::Matrix { .. } + TypeInner::Vector { .. } + | TypeInner::Matrix { .. } | TypeInner::Array { .. } | TypeInner::ValuePointer { .. } => write!(self.out, "[{}]", index)?, TypeInner::Struct { .. } => { @@ -2115,45 +2103,6 @@ impl<'a, W: Write> Writer<'a, W> { Ok(()) } - fn write_named_expr( - &mut self, - handle: Handle, - name: String, - ctx: &FunctionCtx, - ) -> BackendResult { - match ctx.info[handle].ty { - TypeResolution::Handle(ty_handle) => match self.module.types[ty_handle].inner { - TypeInner::Struct { .. } => { - let ty_name = &self.names[&NameKey::Type(ty_handle)]; - write!(self.out, "{}", ty_name)?; - } - _ => { - self.write_type(ty_handle)?; - } - }, - TypeResolution::Value(ref inner) => { - self.write_value_type(inner)?; - } - } - - let base_ty_res = &ctx.info[handle].ty; - let resolved = base_ty_res.inner_with(&self.module.types); - - // If rhs is a array type, we should write temp variable as a dynamic array - let array_str = if let TypeInner::Array { .. } = *resolved { - "[]" - } else { - "" - }; - - write!(self.out, " {}{} = ", name, array_str)?; - self.write_expr(handle, ctx)?; - writeln!(self.out, ";")?; - self.named_expressions.insert(handle, name); - - Ok(()) - } - /// Helper method used to produce the reflection info that's returned to the user /// /// It takes an iterator of [`Function`](crate::Function) references instead of diff --git a/third_party/rust/naga/src/back/hlsl/keywords.rs b/third_party/rust/naga/src/back/hlsl/keywords.rs index 1c19c5112ae1..e4ce79f3664f 100644 --- a/third_party/rust/naga/src/back/hlsl/keywords.rs +++ b/third_party/rust/naga/src/back/hlsl/keywords.rs @@ -1,163 +1 @@ -/// HLSL Reserved Words -/// https://docs.microsoft.com/en-us/windows/win32/direct3dhlsl/dx-graphics-hlsl-appendix-keywords -/// https://docs.microsoft.com/en-us/windows/win32/direct3dhlsl/dx-graphics-hlsl-appendix-reserved-words -pub const RESERVED: &[&str] = &[ - "AppendStructuredBuffer", - "asm", - "asm_fragment", - "BlendState", - "bool", - "break", - "Buffer", - "ByteAddressBuffer", - "case", - "cbuffer", - "centroid", - "class", - "column_major", - "compile", - "compile_fragment", - "CompileShader", - "const", - "continue", - "ComputeShader", - "ConsumeStructuredBuffer", - "default", - "DepthStencilState", - "DepthStencilView", - "discard", - "do", - "double", - "DomainShader", - "dword", - "else", - "export", - "extern", - "false", - "float", - "for", - "fxgroup", - "GeometryShader", - "groupshared", - "half", - "Hullshader", - "if", - "in", - "inline", - "inout", - "InputPatch", - "int", - "interface", - "line", - "lineadj", - "linear", - "LineStream", - "matrix", - "min16float", - "min10float", - "min16int", - "min12int", - "min16uint", - "namespace", - "nointerpolation", - "noperspective", - "NULL", - "out", - "OutputPatch", - "packoffset", - "pass", - "pixelfragment", - "PixelShader", - "point", - "PointStream", - "precise", - "RasterizerState", - "RenderTargetView", - "return", - "register", - "row_major", - "RWBuffer", - "RWByteAddressBuffer", - "RWStructuredBuffer", - "RWTexture1D", - "RWTexture1DArray", - "RWTexture2D", - "RWTexture2DArray", - "RWTexture3D", - "sample", - "sampler", - "SamplerState", - "SamplerComparisonState", - "shared", - "snorm", - "stateblock", - "stateblock_state", - "static", - "string", - "struct", - "switch", - "StructuredBuffer", - "tbuffer", - "technique", - "technique10", - "technique11", - "texture", - "Texture1D", - "Texture1DArray", - "Texture2D", - "Texture2DArray", - "Texture2DMS", - "Texture2DMSArray", - "Texture3D", - "TextureCube", - "TextureCubeArray", - "true", - "typedef", - "triangle", - "triangleadj", - "TriangleStream", - "uint", - "uniform", - "unorm", - "unsigned", - "vector", - "vertexfragment", - "VertexShader", - "void", - "volatile", - "while", - "auto", - "case", - "catch", - "char", - "class", - "const_cast", - "default", - "delete", - "dynamic_cast", - "enum", - "explicit", - "friend", - "goto", - "long", - "mutable", - "new", - "operator", - "private", - "protected", - "public", - "reinterpret_cast", - "short", - "signed", - "sizeof", - "static_cast", - "template", - "this", - "throw", - "try", - "typename", - "union", - "unsigned", - "using", - "virtual", -]; +pub const RESERVED: &[&str] = &[]; diff --git a/third_party/rust/naga/src/back/hlsl/mod.rs b/third_party/rust/naga/src/back/hlsl/mod.rs index 3b94239a05a1..9cc22d9d109b 100644 --- a/third_party/rust/naga/src/back/hlsl/mod.rs +++ b/third_party/rust/naga/src/back/hlsl/mod.rs @@ -6,67 +6,15 @@ use thiserror::Error; pub use writer::Writer; -pub const DEFAULT_SHADER_MODEL: u16 = 50; -#[derive(Debug, Copy, Clone, PartialEq, PartialOrd)] -pub struct ShaderModel(u16); - -impl ShaderModel { - pub fn new(shader_model: u16) -> Self { - Self(shader_model) - } -} - -impl Default for ShaderModel { - fn default() -> Self { - Self(DEFAULT_SHADER_MODEL) - } -} - -/// Structure that contains the configuration used in the [`Writer`](Writer) -#[derive(Debug, Clone)] -pub struct Options { - /// The hlsl shader model to be used - pub shader_model: ShaderModel, - /// The vertex entry point name in generated shader - pub vertex_entry_point_name: String, - /// The fragment entry point name in generated shader - pub fragment_entry_point_name: String, - /// The comput entry point name in generated shader - pub compute_entry_point_name: String, -} - -impl Default for Options { - fn default() -> Self { - Options { - shader_model: ShaderModel(50), - vertex_entry_point_name: String::from("vert_main"), - fragment_entry_point_name: String::from("frag_main"), - compute_entry_point_name: String::from("comp_main"), - } - } -} - #[derive(Error, Debug)] pub enum Error { #[error(transparent)] IoError(#[from] FmtError), - #[error("BuiltIn {0:?} is not supported")] - UnsupportedShaderModel(ShaderModel), - #[error("A scalar with an unsupported width was requested: {0:?} {1:?}")] - UnsupportedScalar(crate::ScalarKind, crate::Bytes), - #[error("{0}")] - Unimplemented(String), // TODO: Error used only during development - #[error("{0}")] - Custom(String), } -pub fn write_string( - module: &crate::Module, - info: &crate::valid::ModuleInfo, - options: &Options, -) -> Result { - let mut w = Writer::new(String::new(), options); - w.write(module, info)?; +pub fn write_string(module: &crate::Module) -> Result { + let mut w = Writer::new(String::new()); + w.write(module)?; let output = w.finish(); Ok(output) } diff --git a/third_party/rust/naga/src/back/hlsl/writer.rs b/third_party/rust/naga/src/back/hlsl/writer.rs index 9dc899277fa9..a0030a92be4a 100644 --- a/third_party/rust/naga/src/back/hlsl/writer.rs +++ b/third_party/rust/naga/src/back/hlsl/writer.rs @@ -1,176 +1,38 @@ -//TODO: temp -#![allow(dead_code)] -use super::{Error, Options, ShaderModel}; -use crate::{ - back::{hlsl::keywords::RESERVED, vector_size_str}, - proc::{EntryPointIndex, NameKey, Namer, TypeResolution}, - valid::{FunctionInfo, ModuleInfo}, - Arena, ArraySize, BuiltIn, Bytes, Constant, ConstantInner, Expression, FastHashMap, Function, - GlobalVariable, Handle, ImageDimension, LocalVariable, Module, ScalarKind, ScalarValue, - ShaderStage, Statement, StructMember, Type, TypeInner, -}; +use super::Error; +use crate::back::hlsl::keywords::RESERVED; +use crate::proc::{NameKey, Namer}; +use crate::{FastHashMap, ShaderStage}; use std::fmt::Write; const INDENT: &str = " "; -const COMPONENTS: &[char] = &['x', 'y', 'z', 'w']; -const LOCATION_SEMANTIC: &str = "LOC"; -const BAKE_PREFIX: &str = "_e"; -/// Shorthand result used internally by the backend -type BackendResult = Result<(), Error>; - -/// Stores the current function type (either a regular function or an entry point) -/// -/// Also stores data needed to identify it (handle for a regular function or index for an entry point) -// TODO: copy-paste from glsl-out, wgsl-out -enum FunctionType { - /// A regular function and it's handle - Function(Handle), - /// A entry point and it's index - EntryPoint(EntryPointIndex), -} - -/// Helper structure that stores data needed when writing the function -// TODO: copy-paste from glsl-out, wgsl-out -struct FunctionCtx<'a> { - /// The current function type being written - ty: FunctionType, - /// Analysis about the function - info: &'a FunctionInfo, - /// The expression arena of the current function being written - expressions: &'a Arena, - /// Map of expressions that have associated variable names - named_expressions: &'a crate::NamedExpressions, -} - -impl<'a> FunctionCtx<'_> { - /// Helper method that generates a [`NameKey`](crate::proc::NameKey) for a local in the current function - fn name_key(&self, local: Handle) -> NameKey { - match self.ty { - FunctionType::Function(handle) => NameKey::FunctionLocal(handle, local), - FunctionType::EntryPoint(idx) => NameKey::EntryPointLocal(idx, local), - } - } -} - -struct EntryPointBinding { - stage: ShaderStage, - name: String, - members: Vec, -} - -struct EpStructMember { - pub name: String, - pub ty: Handle, - pub binding: Option, -} - -pub struct Writer<'a, W> { +pub struct Writer { out: W, names: FastHashMap, namer: Namer, - options: &'a Options, - ep_inputs: Vec>, - named_expressions: crate::NamedExpressions, } -impl<'a, W: Write> Writer<'a, W> { - pub fn new(out: W, options: &'a Options) -> Self { - Self { +impl Writer { + pub fn new(out: W) -> Self { + Writer { out, names: FastHashMap::default(), namer: Namer::default(), - options, - ep_inputs: Vec::with_capacity(3), - named_expressions: crate::NamedExpressions::default(), } } - fn reset(&mut self, module: &Module) { + pub fn write(&mut self, module: &crate::Module) -> Result<(), Error> { self.names.clear(); self.namer.reset(module, RESERVED, &[], &mut self.names); - self.named_expressions.clear(); - self.ep_inputs.clear(); - } - pub fn write(&mut self, module: &Module, info: &ModuleInfo) -> BackendResult { - if self.options.shader_model < ShaderModel::default() { - return Err(Error::UnsupportedShaderModel(self.options.shader_model)); - } - - self.reset(module); - - // Write all constants - // For example, input wgsl shader: - // ```wgsl - // let c_scale: f32 = 1.2; - // return VertexOutput(uv, vec4(c_scale * pos, 0.0, 1.0)); - // ``` - // - // Output shader: - // ```hlsl - // static const float c_scale = 1.2; - // const VertexOutput vertexoutput1 = { vertexinput.uv3, float4((c_scale * vertexinput.pos1), 0.0, 1.0) }; - // ``` - // - // If we remove `write_global_constant` `c_scale` will be inlined. - for (handle, constant) in module.constants.iter() { - if constant.name.is_some() { - self.write_global_constant(module, &constant.inner, handle)?; - } - } - - // Write all structs - for (handle, ty) in module.types.iter() { - if let TypeInner::Struct { - top_level, - ref members, - .. - } = ty.inner - { - self.write_struct(module, handle, top_level, members)?; - writeln!(self.out)?; - } - } - - // Write all globals - for (ty, _) in module.global_variables.iter() { - self.write_global(module, ty)?; - } - - if !module.global_variables.is_empty() { - // Add extra newline for readability + for (ep_index, ep) in module.entry_points.iter().enumerate() { + let fun = &ep.function; + let fun_name = &self.names[&NameKey::EntryPoint(ep_index as _)]; writeln!(self.out)?; - } - // Write all entry points wrapped structs - for (index, ep) in module.entry_points.iter().enumerate() { - self.write_ep_input_struct(module, &ep.function, ep.stage, index)?; - } - - // Write all regular functions - for (handle, function) in module.functions.iter() { - let info = &info[handle]; - let ctx = FunctionCtx { - ty: FunctionType::Function(handle), - info, - expressions: &function.expressions, - named_expressions: &function.named_expressions, - }; - let name = self.names[&NameKey::Function(handle)].clone(); - - self.write_function(module, name.as_str(), function, &ctx)?; - - writeln!(self.out)?; - } - - // Write all entry points - for (index, ep) in module.entry_points.iter().enumerate() { - let ctx = FunctionCtx { - ty: FunctionType::EntryPoint(index as u16), - info: info.get_entry_point(index), - expressions: &ep.function.expressions, - named_expressions: &ep.function.named_expressions, + let return_type_name = match fun.result { + None => "void", + _ => "", }; if ep.stage == ShaderStage::Compute { @@ -183,863 +45,31 @@ impl<'a, W: Write> Writer<'a, W> { )?; } - let name = match ep.stage { - ShaderStage::Vertex => &self.options.vertex_entry_point_name, - ShaderStage::Fragment => &self.options.fragment_entry_point_name, - ShaderStage::Compute => &self.options.compute_entry_point_name, - }; + writeln!( + self.out, + "{} {}({}", + return_type_name, + fun_name, + if fun.arguments.is_empty() { ")" } else { "" } + )?; - self.write_function(module, name, &ep.function, &ctx)?; - - if index < module.entry_points.len() - 1 { - writeln!(self.out)?; - } + // TODO Support arguments + self.write_block(&ep.function.body)?; } - Ok(()) } - fn write_binding(&mut self, binding: &crate::Binding) -> BackendResult { - match *binding { - crate::Binding::BuiltIn(builtin) => { - write!(self.out, " : {}", builtin_str(builtin))?; - } - crate::Binding::Location { location, .. } => { - write!(self.out, " : {}{}", LOCATION_SEMANTIC, location)?; - } - } - - Ok(()) - } - - fn write_ep_input_struct( - &mut self, - module: &Module, - func: &Function, - stage: ShaderStage, - index: usize, - ) -> BackendResult { - if !func.arguments.is_empty() { - let struct_name = self.namer.call_unique(match stage { - ShaderStage::Vertex => "VertexInput", - ShaderStage::Fragment => "FragmentInput", - ShaderStage::Compute => "ComputeInput", - }); - - let mut members = Vec::with_capacity(func.arguments.len()); - - write!(self.out, "struct {}", &struct_name)?; - writeln!(self.out, " {{")?; - - for arg in func.arguments.iter() { - let member_name = if let Some(ref name) = arg.name { - name - } else { - "member" - }; - let member = EpStructMember { - name: self.namer.call_unique(member_name), - ty: arg.ty, - binding: arg.binding.clone(), - }; - - write!(self.out, "{}", INDENT)?; - self.write_type(module, member.ty)?; - write!(self.out, " {}", &member.name)?; - if let Some(ref binding) = member.binding { - self.write_binding(binding)?; - } - write!(self.out, ";")?; - writeln!(self.out)?; - - members.push(member); - } - - writeln!(self.out, "}};")?; - writeln!(self.out)?; - - let ep_input = EntryPointBinding { - stage, - name: struct_name, - members, - }; - - self.ep_inputs.insert(index, Some(ep_input)); - } - - Ok(()) - } - - /// Helper method used to write global variables - /// # Notes - /// Always adds a newline - fn write_global(&mut self, module: &Module, handle: Handle) -> BackendResult { - let global = &module.global_variables[handle]; - let inner = &module.types[global.ty].inner; - - let (storage_class, register_ty) = match *inner { - TypeInner::Image { .. } => ("", "t"), - TypeInner::Sampler { .. } => ("", "s"), - TypeInner::Struct { .. } | TypeInner::Vector { .. } => ("static ", ""), - // TODO: other register ty https://docs.microsoft.com/en-us/windows/win32/direct3dhlsl/dx-graphics-hlsl-variable-register - _ => return Err(Error::Unimplemented(format!("register_ty {:?}", inner))), - }; - - write!(self.out, "{}", storage_class)?; - self.write_type(module, global.ty)?; - if let TypeInner::Array { size, .. } = module.types[global.ty].inner { - self.write_array_size(module, size)?; - } - write!( - self.out, - " {}", - &self.names[&NameKey::GlobalVariable(handle)] - )?; - - if let Some(ref binding) = global.binding { - writeln!(self.out, " : register({}{});", register_ty, binding.binding)?; - } else { - if let Some(init) = global.init { - write!(self.out, " = ")?; - self.write_constant(module, init)?; - } - writeln!(self.out, ";")?; - } - - Ok(()) - } - - /// Helper method used to write global constants - /// - /// # Notes - /// Ends in a newline - fn write_global_constant( - &mut self, - _module: &Module, - inner: &ConstantInner, - handle: Handle, - ) -> BackendResult { - match *inner { - ConstantInner::Scalar { - width: _, - ref value, - } => { - write!(self.out, "static const ")?; - // Write type - match *value { - crate::ScalarValue::Sint(_) => write!(self.out, "int")?, - crate::ScalarValue::Uint(_) => write!(self.out, "uint")?, - crate::ScalarValue::Float(_) => write!(self.out, "float")?, - crate::ScalarValue::Bool(_) => write!(self.out, "bool")?, - }; - let name = &self.names[&NameKey::Constant(handle)]; - write!(self.out, " {} = ", name)?; - - // Second match required to avoid heap allocation by `format!()` - match *value { - crate::ScalarValue::Sint(value) => write!(self.out, "{}", value)?, - crate::ScalarValue::Uint(value) => write!(self.out, "{}", value)?, - crate::ScalarValue::Float(value) => { - // Floats are written using `Debug` instead of `Display` because it always appends the - // decimal part even it's zero - write!(self.out, "{:?}", value)? - } - crate::ScalarValue::Bool(value) => write!(self.out, "{}", value)?, - }; - writeln!(self.out, ";")?; - } - ConstantInner::Composite { .. } => { - return Err(Error::Unimplemented(format!( - "write_global_constant Composite {:?}", - inner - ))) - } - } - // End with extra newline for readability - writeln!(self.out)?; - Ok(()) - } - - // copy-paste from glsl-out - fn write_array_size(&mut self, module: &Module, size: ArraySize) -> BackendResult { - write!(self.out, "[")?; - - // Write the array size - // Writes nothing if `ArraySize::Dynamic` - // Panics if `ArraySize::Constant` has a constant that isn't an uint - match size { - ArraySize::Constant(const_handle) => match module.constants[const_handle].inner { - ConstantInner::Scalar { - width: _, - value: ScalarValue::Uint(size), - } => write!(self.out, "{}", size)?, - _ => unreachable!(), - }, - ArraySize::Dynamic => (), - } - - write!(self.out, "]")?; - Ok(()) - } - - /// Helper method used to write structs - /// - /// # Notes - /// Ends in a newline - fn write_struct( - &mut self, - module: &Module, - handle: Handle, - _block: bool, - members: &[StructMember], - ) -> BackendResult { - // Write struct name - write!(self.out, "struct {}", self.names[&NameKey::Type(handle)])?; - writeln!(self.out, " {{")?; - - for (index, member) in members.iter().enumerate() { - // The indentation is only for readability - write!(self.out, "{}", INDENT)?; - - match module.types[member.ty].inner { - TypeInner::Array { - base, - size, - stride: _, - } => { - // HLSL arrays are written as `type name[size]` - let ty_name = match module.types[base].inner { - // Write scalar type by backend so as not to depend on the front-end implementation - // Name returned from frontend can be generated (type1, float1, etc.) - TypeInner::Scalar { kind, width } => scalar_kind_str(kind, width)?, - _ => &self.names[&NameKey::Type(base)], - }; - - // Write `type` and `name` - write!(self.out, "{}", ty_name)?; - write!( - self.out, - " {}", - &self.names[&NameKey::StructMember(handle, index as u32)] - )?; - // Write [size] - self.write_array_size(module, size)?; - } - _ => { - // Write the member type and name - self.write_type(module, member.ty)?; - write!( - self.out, - " {}", - &self.names[&NameKey::StructMember(handle, index as u32)] - )?; - } - } - - if let Some(ref binding) = member.binding { - self.write_binding(binding)?; - }; - write!(self.out, ";")?; - writeln!(self.out)?; - } - - writeln!(self.out, "}};")?; - Ok(()) - } - - /// Helper method used to write non image/sampler types - /// - /// # Notes - /// Adds no trailing or leading whitespace - fn write_type(&mut self, module: &Module, ty: Handle) -> BackendResult { - let inner = &module.types[ty].inner; - match *inner { - TypeInner::Struct { .. } => write!(self.out, "{}", self.names[&NameKey::Type(ty)])?, - // hlsl array has the size separated from the base type - TypeInner::Array { base, .. } => self.write_type(module, base)?, - ref other => self.write_value_type(module, other)?, - } - - Ok(()) - } - - /// Helper method used to write value types - /// - /// # Notes - /// Adds no trailing or leading whitespace - fn write_value_type(&mut self, module: &Module, inner: &TypeInner) -> BackendResult { - match *inner { - TypeInner::Scalar { kind, width } => { - write!(self.out, "{}", scalar_kind_str(kind, width)?)?; - } - TypeInner::Vector { size, kind, width } => { - write!( - self.out, - "{}{}", - scalar_kind_str(kind, width)?, - vector_size_str(size) - )?; - } - TypeInner::Matrix { - columns, - rows, - width, - } => { - // The IR supports only float matrix - // https://docs.microsoft.com/en-us/windows/win32/direct3dhlsl/dx-graphics-hlsl-matrix - write!( - self.out, - "{}{}x{}", - scalar_kind_str(ScalarKind::Float, width)?, - vector_size_str(columns), - vector_size_str(rows), - )?; - } - TypeInner::Image { - dim, - arrayed: _, //TODO: - class, - } => { - let dim_str = image_dimension_str(dim); - if let crate::ImageClass::Sampled { kind, multi: false } = class { - write!( - self.out, - "Texture{}<{}4>", - dim_str, - scalar_kind_str(kind, 4)? - )? - } else { - return Err(Error::Unimplemented(format!( - "write_value_type {:?}", - inner - ))); - } - } - TypeInner::Sampler { comparison: false } => { - write!(self.out, "SamplerState")?; - } - // HLSL arrays are written as `type name[size]` - // Current code is written arrays only as `[size]` - // Base `type` and `name` should be written outside - TypeInner::Array { size, .. } => { - self.write_array_size(module, size)?; - } - _ => { - return Err(Error::Unimplemented(format!( - "write_value_type {:?}", - inner - ))) - } - } - - Ok(()) - } - - /// Helper method used to write structs - /// # Notes - /// Ends in a newline - fn write_function( - &mut self, - module: &Module, - name: &str, - func: &Function, - func_ctx: &FunctionCtx<'_>, - ) -> BackendResult { - // Function Declaration Syntax - https://docs.microsoft.com/en-us/windows/win32/direct3dhlsl/dx-graphics-hlsl-function-syntax - if let Some(ref result) = func.result { - self.write_type(module, result.ty)?; - } else { - write!(self.out, "void")?; - } - - // Write function name - write!(self.out, " {}(", name)?; - - // Write function arguments for non entry point functions - match func_ctx.ty { - FunctionType::Function(handle) => { - for (index, arg) in func.arguments.iter().enumerate() { - // Write argument type - self.write_type(module, arg.ty)?; - - let argument_name = - &self.names[&NameKey::FunctionArgument(handle, index as u32)]; - - // Write argument name. Space is important. - write!(self.out, " {}", argument_name)?; - if index < func.arguments.len() - 1 { - // Add a separator between args - write!(self.out, ", ")?; - } - } - } - FunctionType::EntryPoint(index) => { - // EntryPoint arguments wrapped into structure - if !self.ep_inputs.is_empty() { - if let Some(ref ep_input) = self.ep_inputs[index as usize] { - write!( - self.out, - "{} {}", - ep_input.name, - self.namer - .call_unique(ep_input.name.to_lowercase().as_str()) - )?; - } - } - } - } - // Ends of arguments - write!(self.out, ")")?; - - // Write semantic if it present - let stage = match func_ctx.ty { - FunctionType::EntryPoint(index) => Some(module.entry_points[index as usize].stage), - _ => None, - }; - if let Some(ref result) = func.result { - if let Some(ref binding) = result.binding { - match *binding { - crate::Binding::BuiltIn(builtin) => { - write!(self.out, " : {}", builtin_str(builtin))?; - } - crate::Binding::Location { location, .. } => { - if stage == Some(ShaderStage::Fragment) { - write!(self.out, " : SV_Target{}", location)?; - } - } - } - } - } - - // Function body start - writeln!(self.out)?; + fn write_block(&mut self, statements: &[crate::Statement]) -> Result<(), Error> { writeln!(self.out, "{{")?; - // Write function local variables - for (handle, local) in func.local_variables.iter() { - // Write indentation (only for readability) - write!(self.out, "{}", INDENT)?; - // Write the local name - // The leading space is important - write!(self.out, "var {}: ", self.names[&func_ctx.name_key(handle)])?; - - // Write the local type - self.write_type(module, local.ty)?; - - // Write the local initializer if needed - if let Some(init) = local.init { - // Put the equal signal only if there's a initializer - // The leading and trailing spaces aren't needed but help with readability - write!(self.out, " = ")?; - - // Write the constant - // `write_constant` adds no trailing or leading space/newline - self.write_constant(module, init)?; + for statement in statements { + if let crate::Statement::Return { value: None } = *statement { + writeln!(self.out, "{}return;", INDENT)?; } - - // Finish the local with `;` and add a newline (only for readability) - writeln!(self.out, ";")? - } - - if !func.local_variables.is_empty() { - writeln!(self.out)?; - } - - // Write the function body (statement list) - for sta in func.body.iter() { - // The indentation should always be 1 when writing the function body - self.write_stmt(module, sta, func_ctx, 1)?; } writeln!(self.out, "}}")?; - self.named_expressions.clear(); - - Ok(()) - } - - /// Helper method used to write statements - /// - /// # Notes - /// Always adds a newline - fn write_stmt( - &mut self, - module: &Module, - stmt: &Statement, - func_ctx: &FunctionCtx<'_>, - indent: usize, - ) -> BackendResult { - match *stmt { - Statement::Emit(ref range) => { - for handle in range.clone() { - let expr_name = if let Some(name) = func_ctx.named_expressions.get(&handle) { - // Front end provides names for all variables at the start of writing. - // But we write them to step by step. We need to recache them - // Otherwise, we could accidentally write variable name instead of full expression. - // Also, we use sanitized names! It defense backend from generating variable with name from reserved keywords. - Some(self.namer.call_unique(name)) - } else { - let min_ref_count = func_ctx.expressions[handle].bake_ref_count(); - if min_ref_count <= func_ctx.info[handle].ref_count { - Some(format!("_expr{}", handle.index())) - } else { - None - } - }; - - if let Some(name) = expr_name { - write!(self.out, "{}", INDENT.repeat(indent))?; - self.write_named_expr(module, handle, name, func_ctx)?; - } - } - } - // TODO: copy-paste from glsl-out - Statement::Block(ref block) => { - write!(self.out, "{}", INDENT.repeat(indent))?; - writeln!(self.out, "{{")?; - for sta in block.iter() { - // Increase the indentation to help with readability - self.write_stmt(module, sta, func_ctx, indent + 1)? - } - writeln!(self.out, "{}}}", INDENT.repeat(indent))? - } - // TODO: copy-paste from glsl-out - Statement::If { - condition, - ref accept, - ref reject, - } => { - write!(self.out, "{}", INDENT.repeat(indent))?; - write!(self.out, "if (")?; - self.write_expr(module, condition, func_ctx)?; - writeln!(self.out, ") {{")?; - - for sta in accept { - // Increase indentation to help with readability - self.write_stmt(module, sta, func_ctx, indent + 1)?; - } - - // If there are no statements in the reject block we skip writing it - // This is only for readability - if !reject.is_empty() { - writeln!(self.out, "{}}} else {{", INDENT.repeat(indent))?; - - for sta in reject { - // Increase indentation to help with readability - self.write_stmt(module, sta, func_ctx, indent + 1)?; - } - } - - writeln!(self.out, "{}}}", INDENT.repeat(indent))? - } - // TODO: copy-paste from glsl-out - Statement::Kill => writeln!(self.out, "{}discard;", INDENT.repeat(indent))?, - Statement::Return { value: None } => { - writeln!(self.out, "{}return;", INDENT.repeat(indent))?; - } - Statement::Return { value: Some(expr) } => { - let base_ty_res = &func_ctx.info[expr].ty; - let mut resolved = base_ty_res.inner_with(&module.types); - if let TypeInner::Pointer { base, class: _ } = *resolved { - resolved = &module.types[base].inner; - } - - if let TypeInner::Struct { .. } = *resolved { - // We can safery unwrap here, since we now we working with struct - let ty = base_ty_res.handle().unwrap(); - let struct_name = &self.names[&NameKey::Type(ty)]; - let variable_name = self.namer.call_unique(struct_name.as_str()).to_lowercase(); - write!( - self.out, - "{}const {} {} = ", - INDENT.repeat(indent), - struct_name, - variable_name - )?; - self.write_expr(module, expr, func_ctx)?; - writeln!(self.out)?; - writeln!( - self.out, - "{}return {};", - INDENT.repeat(indent), - variable_name - )?; - } else { - write!(self.out, "{}return ", INDENT.repeat(indent))?; - self.write_expr(module, expr, func_ctx)?; - writeln!(self.out, ";")? - } - } - Statement::Store { pointer, value } => { - write!(self.out, "{}", INDENT.repeat(indent))?; - self.write_expr(module, pointer, func_ctx)?; - write!(self.out, " = ")?; - self.write_expr(module, value, func_ctx)?; - writeln!(self.out, ";")? - } - Statement::Call { - function, - ref arguments, - result, - } => { - write!(self.out, "{}", INDENT.repeat(indent))?; - if let Some(expr) = result { - let name = format!("{}{}", BAKE_PREFIX, expr.index()); - write!(self.out, "const {} = ", name)?; - self.write_expr(module, expr, func_ctx)?; - self.named_expressions.insert(expr, name); - writeln!(self.out, ";")? - } - let func_name = &self.names[&NameKey::Function(function)]; - write!(self.out, "{}(", func_name)?; - for (index, argument) in arguments.iter().enumerate() { - self.write_expr(module, *argument, func_ctx)?; - // Only write a comma if isn't the last element - if index != arguments.len().saturating_sub(1) { - // The leading space is for readability only - write!(self.out, ", ")?; - } - } - writeln!(self.out, ");")? - } - _ => return Err(Error::Unimplemented(format!("write_stmt {:?}", stmt))), - } - - Ok(()) - } - - /// Helper method to write expressions - /// - /// # Notes - /// Doesn't add any newlines or leading/trailing spaces - fn write_expr( - &mut self, - module: &Module, - expr: Handle, - func_ctx: &FunctionCtx<'_>, - ) -> BackendResult { - if let Some(name) = self.named_expressions.get(&expr) { - write!(self.out, "{}", name)?; - return Ok(()); - } - - let expression = &func_ctx.expressions[expr]; - - match *expression { - Expression::Constant(constant) => self.write_constant(module, constant)?, - Expression::Compose { ty, ref components } => { - let is_struct = if let TypeInner::Struct { .. } = module.types[ty].inner { - true - } else { - false - }; - if is_struct { - write!(self.out, "{{ ")?; - } else { - self.write_type(module, ty)?; - write!(self.out, "(")?; - } - for (index, component) in components.iter().enumerate() { - self.write_expr(module, *component, func_ctx)?; - // Only write a comma if isn't the last element - if index != components.len().saturating_sub(1) { - // The leading space is for readability only - write!(self.out, ", ")?; - } - } - if is_struct { - write!(self.out, " }};")? - } else { - write!(self.out, ")")? - } - } - // TODO: copy-paste from wgsl-out - Expression::Binary { op, left, right } => { - write!(self.out, "(")?; - self.write_expr(module, left, func_ctx)?; - write!(self.out, " {} ", crate::back::binary_operation_str(op))?; - self.write_expr(module, right, func_ctx)?; - write!(self.out, ")")?; - } - // TODO: copy-paste from glsl-out - Expression::AccessIndex { base, index } => { - self.write_expr(module, base, func_ctx)?; - - let base_ty_res = &func_ctx.info[base].ty; - let mut resolved = base_ty_res.inner_with(&module.types); - let base_ty_handle = match *resolved { - TypeInner::Pointer { base, class: _ } => { - resolved = &module.types[base].inner; - Some(base) - } - _ => base_ty_res.handle(), - }; - - match *resolved { - TypeInner::Vector { .. } => { - // Write vector access as a swizzle - write!(self.out, ".{}", COMPONENTS[index as usize])? - } - TypeInner::Matrix { .. } - | TypeInner::Array { .. } - | TypeInner::ValuePointer { .. } => write!(self.out, "[{}]", index)?, - TypeInner::Struct { .. } => { - // This will never panic in case the type is a `Struct`, this is not true - // for other types so we can only check while inside this match arm - let ty = base_ty_handle.unwrap(); - - write!( - self.out, - ".{}", - &self.names[&NameKey::StructMember(ty, index)] - )? - } - ref other => return Err(Error::Custom(format!("Cannot index {:?}", other))), - } - } - Expression::FunctionArgument(pos) => { - match func_ctx.ty { - FunctionType::Function(handle) => { - let name = &self.names[&NameKey::FunctionArgument(handle, pos)]; - write!(self.out, "{}", name)?; - } - FunctionType::EntryPoint(index) => { - // EntryPoint arguments wrapped into structure - // We can safery unwrap here, because if we write function arguments it means, that ep_input struct already exists - let ep_input = self.ep_inputs[index as usize].as_ref().unwrap(); - let member_name = &ep_input.members[pos as usize].name; - write!( - self.out, - "{}.{}", - &ep_input.name.to_lowercase(), - member_name - )? - } - }; - } - Expression::ImageSample { - image, - sampler, // TODO: - coordinate, // TODO: - array_index: _, // TODO: - offset: _, // TODO: - level: _, // TODO: - depth_ref: _, // TODO: - } => { - // TODO: others - self.write_expr(module, image, func_ctx)?; - write!(self.out, ".Sample(")?; - self.write_expr(module, sampler, func_ctx)?; - write!(self.out, ", ")?; - self.write_expr(module, coordinate, func_ctx)?; - write!(self.out, ")")?; - } - // TODO: copy-paste from wgsl-out - Expression::GlobalVariable(handle) => { - let name = &self.names[&NameKey::GlobalVariable(handle)]; - write!(self.out, "{}", name)?; - } - Expression::Load { pointer } => self.write_expr(module, pointer, func_ctx)?, - _ => return Err(Error::Unimplemented(format!("write_expr {:?}", expression))), - } - - Ok(()) - } - - /// Helper method used to write constants - /// - /// # Notes - /// Doesn't add any newlines or leading/trailing spaces - fn write_constant(&mut self, module: &Module, handle: Handle) -> BackendResult { - let constant = &module.constants[handle]; - match constant.inner { - crate::ConstantInner::Scalar { - width: _, - ref value, - } => { - if constant.name.is_some() { - write!(self.out, "{}", &self.names[&NameKey::Constant(handle)])?; - } else { - self.write_scalar_value(*value)?; - } - } - crate::ConstantInner::Composite { ty, ref components } => { - let (open_b, close_b) = match module.types[ty].inner { - TypeInner::Struct { .. } => ("{ ", " }"), - _ => { - // We should write type only for non struct constants - self.write_type(module, ty)?; - ("(", ")") - } - }; - write!(self.out, "{}", open_b)?; - for (index, constant) in components.iter().enumerate() { - self.write_constant(module, *constant)?; - // Only write a comma if isn't the last element - if index != components.len().saturating_sub(1) { - // The leading space is for readability only - write!(self.out, ", ")?; - } - } - write!(self.out, "{}", close_b)?; - } - } - - Ok(()) - } - - /// Helper method used to write [`ScalarValue`](ScalarValue) - /// - /// # Notes - /// Adds no trailing or leading whitespace - fn write_scalar_value(&mut self, value: ScalarValue) -> BackendResult { - match value { - ScalarValue::Sint(value) => write!(self.out, "{}", value)?, - ScalarValue::Uint(value) => write!(self.out, "{}u", value)?, - // Floats are written using `Debug` instead of `Display` because it always appends the - // decimal part even it's zero - ScalarValue::Float(value) => write!(self.out, "{:?}", value)?, - ScalarValue::Bool(value) => write!(self.out, "{}", value)?, - } - - Ok(()) - } - - fn write_named_expr( - &mut self, - module: &Module, - handle: Handle, - name: String, - ctx: &FunctionCtx, - ) -> BackendResult { - match ctx.info[handle].ty { - TypeResolution::Handle(ty_handle) => match module.types[ty_handle].inner { - TypeInner::Struct { .. } => { - let ty_name = &self.names[&NameKey::Type(ty_handle)]; - write!(self.out, "{}", ty_name)?; - } - _ => { - self.write_type(module, ty_handle)?; - } - }, - TypeResolution::Value(ref inner) => { - self.write_value_type(module, inner)?; - } - } - - let base_ty_res = &ctx.info[handle].ty; - let resolved = base_ty_res.inner_with(&module.types); - - write!(self.out, " {}", name)?; - // If rhs is a array type, we should write array size - if let TypeInner::Array { size, .. } = *resolved { - self.write_array_size(module, size)?; - } - write!(self.out, " = ")?; - self.write_expr(module, handle, ctx)?; - writeln!(self.out, ";")?; - self.named_expressions.insert(handle, name); - Ok(()) } @@ -1047,53 +77,3 @@ impl<'a, W: Write> Writer<'a, W> { self.out } } - -fn image_dimension_str(dim: ImageDimension) -> &'static str { - match dim { - ImageDimension::D1 => "1D", - ImageDimension::D2 => "2D", - ImageDimension::D3 => "3D", - ImageDimension::Cube => "Cube", - } -} - -fn builtin_str(built_in: BuiltIn) -> &'static str { - match built_in { - BuiltIn::Position => "SV_Position", - // vertex - BuiltIn::ClipDistance => "SV_ClipDistance", - BuiltIn::CullDistance => "SV_CullDistance", - BuiltIn::InstanceIndex => "SV_InstanceID", - // based on this page https://docs.microsoft.com/en-us/windows/uwp/gaming/glsl-to-hlsl-reference#comparing-opengl-es-20-with-direct3d-11 - // No meaning unless you target Direct3D 9 - BuiltIn::PointSize => "PSIZE", - BuiltIn::VertexIndex => "SV_VertexID", - // fragment - BuiltIn::FragDepth => "SV_Depth", - BuiltIn::FrontFacing => "SV_IsFrontFace", - BuiltIn::SampleIndex => "SV_SampleIndex", - BuiltIn::SampleMask => "SV_Coverage", - // compute - BuiltIn::GlobalInvocationId => "SV_DispatchThreadID", - BuiltIn::LocalInvocationId => "SV_GroupThreadID", - BuiltIn::LocalInvocationIndex => "SV_GroupIndex", - BuiltIn::WorkGroupId => "SV_GroupID", - _ => todo!("builtin_str {:?}", built_in), - } -} - -/// Helper function that returns scalar related strings -/// https://docs.microsoft.com/en-us/windows/win32/direct3dhlsl/dx-graphics-hlsl-scalar -fn scalar_kind_str(kind: ScalarKind, width: Bytes) -> Result<&'static str, Error> { - match kind { - ScalarKind::Sint => Ok("int"), - ScalarKind::Uint => Ok("uint"), - ScalarKind::Float => match width { - 2 => Ok("half"), - 4 => Ok("float"), - 8 => Ok("double"), - _ => Err(Error::UnsupportedScalar(kind, width)), - }, - ScalarKind::Bool => Ok("bool"), - } -} diff --git a/third_party/rust/naga/src/back/mod.rs b/third_party/rust/naga/src/back/mod.rs index 49c7c568966d..336833249799 100644 --- a/third_party/rust/naga/src/back/mod.rs +++ b/third_party/rust/naga/src/back/mod.rs @@ -28,10 +28,6 @@ impl crate::Expression { crate::Expression::ImageSample { .. } | crate::Expression::ImageLoad { .. } => 1, // derivatives use the control flow crate::Expression::Derivative { .. } => 1, - // TODO: We need a better fix for named `Load` expressions - // More info - https://github.com/gfx-rs/naga/pull/914 - // And https://github.com/gfx-rs/naga/issues/910 - crate::Expression::Load { .. } => 1, // cache expressions that are referenced multiple times _ => 2, } @@ -40,7 +36,7 @@ impl crate::Expression { /// Helper function that returns the string corresponding to the [`BinaryOperator`](crate::BinaryOperator) /// # Notes -/// Used by `glsl-out`, `msl-out`, `wgsl-out`, `hlsl-out`. +/// Used by `glsl-out`, `msl-out`, `wgsl-out`. #[allow(dead_code)] fn binary_operation_str(op: crate::BinaryOperator) -> &'static str { use crate::BinaryOperator as Bo; @@ -68,7 +64,7 @@ fn binary_operation_str(op: crate::BinaryOperator) -> &'static str { /// Helper function that returns the string corresponding to the [`VectorSize`](crate::VectorSize) /// # Notes -/// Used by `msl-out`, `wgsl-out`, `hlsl-out`. +/// Used by `msl-out`, `wgsl-out`. #[allow(dead_code)] fn vector_size_str(size: crate::VectorSize) -> &'static str { match size { diff --git a/third_party/rust/naga/src/back/msl/mod.rs b/third_party/rust/naga/src/back/msl/mod.rs index 738eb9c93bfe..54cfd91ab6d8 100644 --- a/third_party/rust/naga/src/back/msl/mod.rs +++ b/third_party/rust/naga/src/back/msl/mod.rs @@ -77,7 +77,7 @@ pub struct PerStageResources { /// The slot of a buffer that contains an array of `u32`, /// one for the size of each bound buffer that contains a runtime array, - /// in order of [`crate::GlobalVariable`] declarations. + /// in order of [`GlobalVariable`] declarations. #[cfg_attr(feature = "deserialize", serde(default))] pub sizes_buffer: Option, } diff --git a/third_party/rust/naga/src/back/msl/writer.rs b/third_party/rust/naga/src/back/msl/writer.rs index 7301c4f06b6d..819a4f15fb24 100644 --- a/third_party/rust/naga/src/back/msl/writer.rs +++ b/third_party/rust/naga/src/back/msl/writer.rs @@ -9,6 +9,7 @@ use crate::{ valid::{Capabilities, FunctionInfo, GlobalUse, ModuleInfo}, FastHashMap, }; +use bit_set::BitSet; use std::{ fmt::{Display, Error as FmtError, Formatter, Write}, iter, @@ -287,7 +288,7 @@ impl<'a> Display for ConstantContext<'a> { pub struct Writer { out: W, names: FastHashMap, - named_expressions: crate::NamedExpressions, + named_expressions: BitSet, namer: Namer, runtime_sized_buffers: FastHashMap, usize>, #[cfg(test)] @@ -448,7 +449,7 @@ impl Writer { Writer { out, names: FastHashMap::default(), - named_expressions: crate::NamedExpressions::default(), + named_expressions: BitSet::new(), namer: Namer::default(), runtime_sized_buffers: FastHashMap::default(), #[cfg(test)] @@ -709,8 +710,8 @@ impl Writer { self.put_expression_stack_pointers .insert(&expr_handle as *const _ as *const ()); - if let Some(name) = self.named_expressions.get(&expr_handle) { - write!(self.out, "{}", name)?; + if self.named_expressions.contains(expr_handle.index()) { + write!(self.out, "{}{}", BAKE_PREFIX, expr_handle.index())?; return Ok(()); } @@ -765,7 +766,7 @@ impl Writer { write!(self.out, "[{}]", index)?; } crate::TypeInner::Array { .. } => { - write!(self.out, ".{}[{}]", WRAPPED_ARRAY_FIELD, index)?; + write!(self.out, "[{}]", index)?; } _ => { // unexpected indexing, should fail validation @@ -853,7 +854,7 @@ impl Writer { members, span, index as usize, - context.module, + &context.module, ), _ => None, } @@ -1243,7 +1244,6 @@ impl Writer { &mut self, handle: Handle, context: &ExpressionContext, - name: &str, ) -> Result<(), Error> { match context.info[handle].ty { TypeResolution::Handle(ty_handle) => { @@ -1285,8 +1285,7 @@ impl Writer { } //TODO: figure out the naming scheme that wouldn't collide with user names. - write!(self.out, " {} = ", name)?; - + write!(self.out, " {}{} = ", BAKE_PREFIX, handle.index())?; Ok(()) } @@ -1307,30 +1306,14 @@ impl Writer { match *statement { crate::Statement::Emit(ref range) => { for handle in range.clone() { - let expr_name = if let Some(name) = - context.expression.function.named_expressions.get(&handle) - { - // Front end provides names for all variables at the start of writing. - // But we write them to step by step. We need to recache them - // Otherwise, we could accidentally write variable name instead of full expression. - // Also, we use sanitized names! It defense backend from generating variable with name from reserved keywords. - Some(self.namer.call_unique(name)) - } else { - let min_ref_count = - context.expression.function.expressions[handle].bake_ref_count(); - if min_ref_count <= context.expression.info[handle].ref_count { - Some(format!("{}{}", BAKE_PREFIX, handle.index())) - } else { - None - } - }; - - if let Some(name) = expr_name { + let min_ref_count = + context.expression.function.expressions[handle].bake_ref_count(); + if min_ref_count <= context.expression.info[handle].ref_count { write!(self.out, "{}", level)?; - self.start_baking_expression(handle, &context.expression, &name)?; + self.start_baking_expression(handle, &context.expression)?; self.put_expression(handle, &context.expression, true)?; - self.named_expressions.insert(handle, name); writeln!(self.out, ";")?; + self.named_expressions.insert(handle.index()); } } } @@ -1505,9 +1488,8 @@ impl Writer { } => { write!(self.out, "{}", level)?; if let Some(expr) = result { - let name = format!("{}{}", BAKE_PREFIX, expr.index()); - self.start_baking_expression(expr, &context.expression, &name)?; - self.named_expressions.insert(expr, name); + self.start_baking_expression(expr, &context.expression)?; + self.named_expressions.insert(expr.index()); } let fun_name = &self.names[&NameKey::Function(function)]; write!(self.out, "{}(", fun_name)?; @@ -1556,7 +1538,7 @@ impl Writer { for statement in statements { if let crate::Statement::Emit(ref range) = *statement { for handle in range.clone() { - self.named_expressions.remove(&handle); + self.named_expressions.remove(handle.index()); } } } @@ -2080,7 +2062,7 @@ impl Writer { _ => continue, }; varying_count += 1; - let name = &self.names[name_key]; + let name = &self.names[&name_key]; let ty_name = TypeContext { handle: ty, arena: &module.types, @@ -2177,7 +2159,7 @@ impl Writer { Some(ref binding @ &crate::Binding::BuiltIn(..)) => binding, _ => continue, }; - let name = &self.names[name_key]; + let name = &self.names[&name_key]; let ty_name = TypeContext { handle: ty, arena: &module.types, @@ -2446,7 +2428,7 @@ fn test_stack_size() { let stack_size = addresses.end - addresses.start; // check the size (in debug only) // last observed macOS value: 17664 - if stack_size < 14000 || stack_size > 19000 { + if stack_size < 17000 || stack_size > 19000 { panic!("`put_expression` stack size {} has changed!", stack_size); } } @@ -2461,7 +2443,7 @@ fn test_stack_size() { let stack_size = addresses.end - addresses.start; // check the size (in debug only) // last observed macOS value: 13600 - if stack_size < 11000 || stack_size > 16000 { + if stack_size < 12000 || stack_size > 14500 { panic!("`put_block` stack size {} has changed!", stack_size); } } diff --git a/third_party/rust/naga/src/back/spv/writer.rs b/third_party/rust/naga/src/back/spv/writer.rs index 62afb85fda2c..894466658513 100644 --- a/third_party/rust/naga/src/back/spv/writer.rs +++ b/third_party/rust/naga/src/back/spv/writer.rs @@ -72,6 +72,7 @@ struct Function { signature: Option, parameters: Vec, variables: crate::FastHashMap, LocalVariable>, + internal_variables: Vec, blocks: Vec, entry_point_context: Option, } @@ -88,6 +89,9 @@ impl Function { for local_var in self.variables.values() { local_var.instruction.to_words(sink); } + for internal_var in self.internal_variables.iter() { + internal_var.instruction.to_words(sink); + } } for instruction in block.body.iter() { instruction.to_words(sink); @@ -270,6 +274,8 @@ struct GlobalVariable { /// prelude block (and reset before every function) as `OpLoad` of the variable. /// It is then used for all the global ops, such as `OpImageSample`. handle_id: Word, + /// SPIR-V storage class. + class: spirv::StorageClass, } pub struct Writer { @@ -277,7 +283,7 @@ pub struct Writer { logical_layout: LogicalLayout, id_gen: IdGenerator, capabilities: crate::FastHashSet, - forbidden_caps: Option<&'static [spirv::Capability]>, + strict_capabilities: bool, debugs: Vec, annotations: Vec, flags: WriterFlags, @@ -307,22 +313,19 @@ impl Writer { let gl450_ext_inst_id = id_gen.next(); let void_type = id_gen.next(); - let (capabilities, forbidden_caps) = match options.capabilities { - Some(ref caps) => (caps.clone(), None), - None => { - let mut caps = crate::FastHashSet::default(); - caps.insert(spirv::Capability::Shader); - let forbidden: &[_] = &[spirv::Capability::Kernel]; - (caps, Some(forbidden)) - } - }; - Ok(Writer { physical_layout: PhysicalLayout::new(raw_version), logical_layout: LogicalLayout::default(), id_gen, - capabilities, - forbidden_caps, + capabilities: match options.capabilities { + Some(ref caps) => caps.clone(), + None => { + let mut caps = crate::FastHashSet::default(); + caps.insert(spirv::Capability::Shader); + caps + } + }, + strict_capabilities: options.capabilities.is_some(), debugs: vec![], annotations: vec![], flags: options.flags, @@ -341,42 +344,49 @@ impl Writer { } fn check(&mut self, capabilities: &[spirv::Capability]) -> Result<(), Error> { - if capabilities.is_empty() - || capabilities - .iter() - .any(|cap| self.capabilities.contains(cap)) - { - return Ok(()); - } - if let Some(forbidden) = self.forbidden_caps { - // take the first allowed capability, blindly - if let Some(&cap) = capabilities.iter().find(|cap| !forbidden.contains(cap)) { - self.capabilities.insert(cap); - return Ok(()); + if self.strict_capabilities { + if capabilities.is_empty() + || capabilities + .iter() + .any(|cap| self.capabilities.contains(cap)) + { + Ok(()) + } else { + Err(Error::MissingCapabilities(capabilities.to_vec())) } + } else { + self.capabilities.extend(capabilities); + Ok(()) } - Err(Error::MissingCapabilities(capabilities.to_vec())) } - fn get_type_id(&mut self, lookup_ty: LookupType) -> Result { + fn get_type_id( + &mut self, + arena: &Arena, + lookup_ty: LookupType, + ) -> Result { if let Entry::Occupied(e) = self.lookup_type.entry(lookup_ty) { Ok(*e.get()) } else { match lookup_ty { LookupType::Handle(_handle) => unreachable!("Handles are populated at start"), - LookupType::Local(local_ty) => self.write_type_declaration_local(local_ty), + LookupType::Local(local_ty) => self.write_type_declaration_local(arena, local_ty), } } } - fn get_expression_type_id(&mut self, tr: &TypeResolution) -> Result { + fn get_expression_type_id( + &mut self, + arena: &Arena, + tr: &TypeResolution, + ) -> Result { let lookup_ty = match *tr { TypeResolution::Handle(ty_handle) => LookupType::Handle(ty_handle), TypeResolution::Value(ref inner) => { LookupType::Local(self.physical_layout.make_local(inner).unwrap()) } }; - self.get_type_id(lookup_ty) + self.get_type_id(arena, lookup_ty) } fn get_pointer_id( @@ -385,7 +395,7 @@ impl Writer { handle: Handle, class: spirv::StorageClass, ) -> Result { - let ty_id = self.get_type_id(LookupType::Handle(handle))?; + let ty_id = self.get_type_id(arena, LookupType::Handle(handle))?; if let crate::TypeInner::Pointer { .. } = arena[handle].inner { return Ok(ty_id); } @@ -453,7 +463,8 @@ impl Writer { let mut parameter_type_ids = Vec::with_capacity(ir_function.arguments.len()); for argument in ir_function.arguments.iter() { let class = spirv::StorageClass::Input; - let argument_type_id = self.get_type_id(LookupType::Handle(argument.ty))?; + let argument_type_id = + self.get_type_id(&ir_module.types, LookupType::Handle(argument.ty))?; if let Some(ref mut list) = varying_ids { let id = if let Some(ref binding) = argument.binding { let name = argument.name.as_ref().map(AsRef::as_ref); @@ -471,7 +482,8 @@ impl Writer { let struct_id = self.id_gen.next(); let mut constituent_ids = Vec::with_capacity(members.len()); for member in members { - let type_id = self.get_type_id(LookupType::Handle(member.ty))?; + let type_id = + self.get_type_id(&ir_module.types, LookupType::Handle(member.ty))?; let name = member.name.as_ref().map(AsRef::as_ref); let binding = member.binding.as_ref().unwrap(); let varying_id = @@ -506,7 +518,8 @@ impl Writer { if let Some(ref mut list) = varying_ids { let class = spirv::StorageClass::Output; if let Some(ref binding) = result.binding { - let type_id = self.get_type_id(LookupType::Handle(result.ty))?; + let type_id = + self.get_type_id(&ir_module.types, LookupType::Handle(result.ty))?; let varying_id = self.write_varying(ir_module, class, None, result.ty, binding)?; list.push(varying_id); @@ -519,7 +532,8 @@ impl Writer { ir_module.types[result.ty].inner { for member in members { - let type_id = self.get_type_id(LookupType::Handle(member.ty))?; + let type_id = + self.get_type_id(&ir_module.types, LookupType::Handle(member.ty))?; let name = member.name.as_ref().map(AsRef::as_ref); let binding = member.binding.as_ref().unwrap(); let varying_id = @@ -536,7 +550,7 @@ impl Writer { } self.void_type } else { - self.get_type_id(LookupType::Handle(result.ty))? + self.get_type_id(&ir_module.types, LookupType::Handle(result.ty))? } } None => self.void_type, @@ -576,7 +590,7 @@ impl Writer { continue; } let id = self.id_gen.next(); - let result_type_id = self.get_type_id(LookupType::Handle(var.ty))?; + let result_type_id = self.get_type_id(&ir_module.types, LookupType::Handle(var.ty))?; let gv = &mut self.global_variables[handle.index()]; prelude .body @@ -711,7 +725,7 @@ impl Writer { } Instruction::type_int(id, bits, signedness) } - Sk::Float => { + crate::ScalarKind::Float => { if bits == 64 { self.capabilities.insert(spirv::Capability::Float64); } @@ -721,7 +735,11 @@ impl Writer { } } - fn write_type_declaration_local(&mut self, local_ty: LocalType) -> Result { + fn write_type_declaration_local( + &mut self, + arena: &Arena, + local_ty: LocalType, + ) -> Result { let id = self.id_gen.next(); let instruction = match local_ty { LocalType::Value { @@ -736,12 +754,15 @@ impl Writer { width, pointer_class: None, } => { - let scalar_id = self.get_type_id(LookupType::Local(LocalType::Value { - vector_size: None, - kind, - width, - pointer_class: None, - }))?; + let scalar_id = self.get_type_id( + arena, + LookupType::Local(LocalType::Value { + vector_size: None, + kind, + width, + pointer_class: None, + }), + )?; Instruction::type_vector(id, scalar_id, size) } LocalType::Matrix { @@ -749,16 +770,19 @@ impl Writer { rows, width, } => { - let vector_id = self.get_type_id(LookupType::Local(LocalType::Value { - vector_size: Some(rows), - kind: crate::ScalarKind::Float, - width, - pointer_class: None, - }))?; + let vector_id = self.get_type_id( + arena, + LookupType::Local(LocalType::Value { + vector_size: Some(rows), + kind: crate::ScalarKind::Float, + width, + pointer_class: None, + }), + )?; Instruction::type_matrix(id, vector_id, columns) } LocalType::Pointer { base, class } => { - let type_id = self.get_type_id(LookupType::Handle(base))?; + let type_id = self.get_type_id(arena, LookupType::Handle(base))?; Instruction::type_pointer(id, class, type_id) } LocalType::Value { @@ -767,12 +791,15 @@ impl Writer { width, pointer_class: Some(class), } => { - let type_id = self.get_type_id(LookupType::Local(LocalType::Value { - vector_size, - kind, - width, - pointer_class: None, - }))?; + let type_id = self.get_type_id( + arena, + LookupType::Local(LocalType::Value { + vector_size, + kind, + width, + pointer_class: None, + }), + )?; Instruction::type_pointer(id, class, type_id) } // all the samplers and image types go through `write_type_declaration_arena` @@ -825,12 +852,15 @@ impl Writer { let instruction = match ty.inner { crate::TypeInner::Scalar { kind, width } => self.make_scalar(id, kind, width), crate::TypeInner::Vector { size, kind, width } => { - let scalar_id = self.get_type_id(LookupType::Local(LocalType::Value { - vector_size: None, - kind, - width, - pointer_class: None, - }))?; + let scalar_id = self.get_type_id( + arena, + LookupType::Local(LocalType::Value { + vector_size: None, + kind, + width, + pointer_class: None, + }), + )?; Instruction::type_vector(id, scalar_id, size) } crate::TypeInner::Matrix { @@ -838,12 +868,15 @@ impl Writer { rows, width, } => { - let vector_id = self.get_type_id(LookupType::Local(LocalType::Value { - vector_size: Some(rows), - kind: crate::ScalarKind::Float, - width, - pointer_class: None, - }))?; + let vector_id = self.get_type_id( + arena, + LookupType::Local(LocalType::Value { + vector_size: Some(rows), + kind: crate::ScalarKind::Float, + width, + pointer_class: None, + }), + )?; Instruction::type_matrix(id, vector_id, columns) } crate::TypeInner::Image { @@ -854,15 +887,7 @@ impl Writer { let kind = match class { crate::ImageClass::Sampled { kind, multi: _ } => kind, crate::ImageClass::Depth => crate::ScalarKind::Float, - crate::ImageClass::Storage(format) => { - let required_caps: &[_] = match dim { - crate::ImageDimension::D1 => &[spirv::Capability::Image1D], - crate::ImageDimension::Cube => &[spirv::Capability::ImageCubeArray], - _ => &[], - }; - self.check(required_caps)?; - format.into() - } + crate::ImageClass::Storage(format) => format.into(), }; let local_type = LocalType::Value { vector_size: None, @@ -870,9 +895,9 @@ impl Writer { width: 4, pointer_class: None, }; + let type_id = self.get_type_id(arena, LookupType::Local(local_type))?; let dim = map_dim(dim); self.check(dim.required_capabilities())?; - let type_id = self.get_type_id(LookupType::Local(local_type))?; Instruction::type_image(id, type_id, dim, arrayed, class) } crate::TypeInner::Sampler { comparison: _ } => Instruction::type_sampler(id), @@ -881,7 +906,7 @@ impl Writer { self.decorate(id, Decoration::ArrayStride, &[stride]); } - let type_id = self.get_type_id(LookupType::Handle(base))?; + let type_id = self.get_type_id(arena, LookupType::Handle(base))?; match size { crate::ArraySize::Constant(const_handle) => { let length_id = self.constant_ids[const_handle.index()]; @@ -891,11 +916,11 @@ impl Writer { } } crate::TypeInner::Struct { - top_level, + ref level, ref members, span: _, } => { - if top_level { + if let crate::StructLevel::Root = *level { self.decorate(id, Decoration::Block, &[]); } @@ -947,13 +972,13 @@ impl Writer { )); } - let member_id = self.get_type_id(LookupType::Handle(member.ty))?; + let member_id = self.get_type_id(arena, LookupType::Handle(member.ty))?; member_ids.push(member_id); } Instruction::type_struct(id, member_ids.as_slice()) } crate::TypeInner::Pointer { base, class } => { - let type_id = self.get_type_id(LookupType::Handle(base))?; + let type_id = self.get_type_id(arena, LookupType::Handle(base))?; let raw_class = map_storage_class(class); Instruction::type_pointer(id, raw_class, type_id) } @@ -964,12 +989,15 @@ impl Writer { class, } => { let raw_class = map_storage_class(class); - let type_id = self.get_type_id(LookupType::Local(LocalType::Value { - vector_size: size, - kind, - width, - pointer_class: None, - }))?; + let type_id = self.get_type_id( + arena, + LookupType::Local(LocalType::Value { + vector_size: size, + kind, + width, + pointer_class: None, + }), + )?; Instruction::type_pointer(id, raw_class, type_id) } }; @@ -978,20 +1006,25 @@ impl Writer { Ok(id) } - fn get_index_constant(&mut self, index: Word) -> Result { - self.get_constant_scalar(crate::ScalarValue::Uint(index as _), 4) + fn get_index_constant( + &mut self, + index: Word, + types: &Arena, + ) -> Result { + self.get_constant_scalar(crate::ScalarValue::Uint(index as _), 4, types) } fn get_constant_scalar( &mut self, value: crate::ScalarValue, width: crate::Bytes, + types: &Arena, ) -> Result { if let Some(&id) = self.cached_constants.get(&(value, width)) { return Ok(id); } let id = self.id_gen.next(); - self.write_constant_scalar(id, &value, width, None)?; + self.write_constant_scalar(id, &value, width, None, types)?; self.cached_constants.insert((value, width), id); Ok(id) } @@ -1002,18 +1035,22 @@ impl Writer { value: &crate::ScalarValue, width: crate::Bytes, debug_name: Option<&String>, + types: &Arena, ) -> Result<(), Error> { if self.flags.contains(WriterFlags::DEBUG) { if let Some(name) = debug_name { self.debugs.push(Instruction::name(id, name)); } } - let type_id = self.get_type_id(LookupType::Local(LocalType::Value { - vector_size: None, - kind: value.scalar_kind(), - width, - pointer_class: None, - }))?; + let type_id = self.get_type_id( + types, + LookupType::Local(LocalType::Value { + vector_size: None, + kind: value.scalar_kind(), + width, + pointer_class: None, + }), + )?; let (solo, pair); let instruction = match *value { crate::ScalarValue::Sint(val) => { @@ -1072,6 +1109,7 @@ impl Writer { id: Word, ty: Handle, components: &[Handle], + types: &Arena, ) -> Result<(), Error> { let mut constituent_ids = Vec::with_capacity(components.len()); for constituent in components.iter() { @@ -1079,7 +1117,7 @@ impl Writer { constituent_ids.push(constituent_id); } - let type_id = self.get_type_id(LookupType::Handle(ty))?; + let type_id = self.get_type_id(types, LookupType::Handle(ty))?; Instruction::constant_composite(type_id, id, constituent_ids.as_slice()) .to_words(&mut self.logical_layout.declarations); Ok(()) @@ -1178,7 +1216,7 @@ impl Writer { &mut self, ir_module: &crate::Module, global_variable: &crate::GlobalVariable, - ) -> Result<(Instruction, Word), Error> { + ) -> Result<(Instruction, Word, spirv::StorageClass), Error> { let id = self.id_gen.next(); let class = map_storage_class(global_variable.class); @@ -1213,7 +1251,7 @@ impl Writer { } // TODO Initializer is optional and not (yet) included in the IR - Ok((instruction, id)) + Ok((instruction, id, class)) } fn get_function_type(&mut self, lookup_function_type: LookupFunctionType) -> Word { @@ -1247,13 +1285,15 @@ impl Writer { let coordinate_id = self.cached[coordinates]; Ok(if let Some(array_index) = array_index { - let coordinate_scalar_type_id = - self.get_type_id(LookupType::Local(LocalType::Value { + let coordinate_scalar_type_id = self.get_type_id( + &ir_module.types, + LookupType::Local(LocalType::Value { vector_size: None, kind: crate::ScalarKind::Float, width: 4, pointer_class: None, - }))?; + }), + )?; let mut constituent_ids = [0u32; 4]; let size = match *fun_info[coordinates].ty.inner_with(&ir_module.types) { @@ -1298,13 +1338,15 @@ impl Writer { ); block.body.push(cast_instruction); - let extended_coordinate_type_id = - self.get_type_id(LookupType::Local(LocalType::Value { + let extended_coordinate_type_id = self.get_type_id( + &ir_module.types, + LookupType::Local(LocalType::Value { vector_size: Some(size), kind: crate::ScalarKind::Float, width: 4, pointer_class: None, - }))?; + }), + )?; let id = self.id_gen.next(); block.body.push(Instruction::composite_construct( @@ -1318,6 +1360,62 @@ impl Writer { }) } + #[allow(clippy::too_many_arguments)] + fn promote_access_expression_to_variable( + &mut self, + ir_types: &Arena, + result_type_id: Word, + container_id: Word, + container_resolution: &TypeResolution, + index_id: Word, + element_ty: Handle, + block: &mut Block, + ) -> Result<(Word, LocalVariable), Error> { + let container_type_id = self.get_expression_type_id(ir_types, container_resolution)?; + let pointer_type_id = self.id_gen.next(); + Instruction::type_pointer( + pointer_type_id, + spirv::StorageClass::Function, + container_type_id, + ) + .to_words(&mut self.logical_layout.declarations); + + let variable = { + let id = self.id_gen.next(); + LocalVariable { + id, + instruction: Instruction::variable( + pointer_type_id, + id, + spirv::StorageClass::Function, + None, + ), + } + }; + block + .body + .push(Instruction::store(variable.id, container_id, None)); + + let element_pointer_id = self.id_gen.next(); + let element_pointer_type_id = + self.get_pointer_id(ir_types, element_ty, spirv::StorageClass::Function)?; + block.body.push(Instruction::access_chain( + element_pointer_type_id, + element_pointer_id, + variable.id, + &[index_id], + )); + let id = self.id_gen.next(); + block.body.push(Instruction::load( + result_type_id, + id, + element_pointer_id, + None, + )); + + Ok((id, variable)) + } + fn is_intermediate( &self, expr_handle: Handle, @@ -1349,7 +1447,8 @@ impl Writer { block: &mut Block, function: &mut Function, ) -> Result<(), Error> { - let result_type_id = self.get_expression_type_id(&fun_info[expr_handle].ty)?; + let result_type_id = + self.get_expression_type_id(&ir_module.types, &fun_info[expr_handle].ty)?; let id = match ir_function.expressions[expr_handle] { crate::Expression::Access { base, index: _ } @@ -1371,10 +1470,20 @@ impl Writer { )); id } - crate::TypeInner::Array { .. } => { - return Err(Error::Validation( - "dynamic indexing of arrays not permitted", - )); + crate::TypeInner::Array { + base: ty_element, .. + } => { + let (id, variable) = self.promote_access_expression_to_variable( + &ir_module.types, + result_type_id, + base_id, + &fun_info[base].ty, + index_id, + ty_element, + block, + )?; + function.internal_variables.push(variable); + id } ref other => { log::error!( @@ -1766,8 +1875,14 @@ impl Writer { } crate::Expression::LocalVariable(variable) => function.variables[&variable].id, crate::Expression::Load { pointer } => { - let pointer_id = - self.write_expression_pointer(ir_function, fun_info, pointer, block, function)?; + let (pointer_id, _) = self.write_expression_pointer( + ir_module, + ir_function, + fun_info, + pointer, + block, + function, + )?; let id = self.id_gen.next(); block @@ -1839,13 +1954,15 @@ impl Writer { } => { // Vulkan doesn't know about our `Depth` class, and it returns `vec4`, // so we need to grab the first component out of it. - let load_result_type_id = - self.get_type_id(LookupType::Local(LocalType::Value { + let load_result_type_id = self.get_type_id( + &ir_module.types, + LookupType::Local(LocalType::Value { vector_size: Some(crate::VectorSize::Quad), kind: crate::ScalarKind::Float, width: 4, pointer_class: None, - }))?; + }), + )?; Instruction::image_fetch(load_result_type_id, id, image_id, coordinate_id) } _ => Instruction::image_fetch(result_type_id, id, image_id, coordinate_id), @@ -1902,20 +2019,26 @@ impl Writer { _ => false, }; let sample_result_type_id = if needs_sub_access { - self.get_type_id(LookupType::Local(LocalType::Value { - vector_size: Some(crate::VectorSize::Quad), - kind: crate::ScalarKind::Float, - width: 4, - pointer_class: None, - }))? + self.get_type_id( + &ir_module.types, + LookupType::Local(LocalType::Value { + vector_size: Some(crate::VectorSize::Quad), + kind: crate::ScalarKind::Float, + width: 4, + pointer_class: None, + }), + )? } else { result_type_id }; // OpTypeSampledImage - let image_type_id = self.get_type_id(LookupType::Handle(image_type))?; - let sampled_image_type_id = - self.get_type_id(LookupType::Local(LocalType::SampledImage { image_type_id }))?; + let image_type_id = + self.get_type_id(&ir_module.types, LookupType::Handle(image_type))?; + let sampled_image_type_id = self.get_type_id( + &ir_module.types, + LookupType::Local(LocalType::SampledImage { image_type_id }), + )?; let sampler_id = self.get_expression_global(ir_function, sampler); let coordinate_id = self.write_texture_coordinates( @@ -1950,8 +2073,11 @@ impl Writer { depth_id, ); - let zero_id = - self.get_constant_scalar(crate::ScalarValue::Float(0.0), 4)?; + let zero_id = self.get_constant_scalar( + crate::ScalarValue::Float(0.0), + 4, + &ir_module.types, + )?; mask |= spirv::ImageOperands::LOD; inst.add_operand(mask.bits()); @@ -1959,20 +2085,14 @@ impl Writer { inst } - crate::SampleLevel::Auto => { - let mut inst = Instruction::image_sample( - sample_result_type_id, - id, - SampleLod::Implicit, - sampled_image_id, - coordinate_id, - depth_id, - ); - if !mask.is_empty() { - inst.add_operand(mask.bits()); - } - inst - } + crate::SampleLevel::Auto => Instruction::image_sample( + sample_result_type_id, + id, + SampleLod::Implicit, + sampled_image_id, + coordinate_id, + depth_id, + ), crate::SampleLevel::Exact(lod_handle) => { let mut inst = Instruction::image_sample( sample_result_type_id, @@ -2002,7 +2122,6 @@ impl Writer { let bias_id = self.cached[bias_handle]; mask |= spirv::ImageOperands::BIAS; - inst.add_operand(mask.bits()); inst.add_operand(bias_id); inst @@ -2020,7 +2139,6 @@ impl Writer { let x_id = self.cached[x]; let y_id = self.cached[y]; mask |= spirv::ImageOperands::GRAD; - inst.add_operand(mask.bits()); inst.add_operand(x_id); inst.add_operand(y_id); @@ -2094,8 +2212,6 @@ impl Writer { } }; - self.check(&[spirv::Capability::ImageQuery])?; - match query { Iq::Size { level } => { let dim_coords = match dim { @@ -2111,12 +2227,15 @@ impl Writer { 4 => Some(crate::VectorSize::Quad), _ => None, }; - self.get_type_id(LookupType::Local(LocalType::Value { - vector_size, - kind: crate::ScalarKind::Sint, - width: 4, - pointer_class: None, - }))? + self.get_type_id( + &ir_module.types, + LookupType::Local(LocalType::Value { + vector_size, + kind: crate::ScalarKind::Sint, + width: 4, + pointer_class: None, + }), + )? }; let (query_op, level_id) = match class { @@ -2124,12 +2243,11 @@ impl Writer { _ => { let level_id = match level { Some(expr) => self.cached[expr], - None => self.get_index_constant(0)?, + None => self.get_index_constant(0, &ir_module.types)?, }; (spirv::Op::ImageQuerySizeLod, Some(level_id)) } }; - // The ID of the vector returned by SPIR-V, which contains the dimensions // as well as the layer count. let id_extended = self.id_gen.next(); @@ -2179,13 +2297,15 @@ impl Writer { Id::D2 | Id::Cube => crate::VectorSize::Tri, Id::D3 => crate::VectorSize::Quad, }; - let extended_size_type_id = - self.get_type_id(LookupType::Local(LocalType::Value { + let extended_size_type_id = self.get_type_id( + &ir_module.types, + LookupType::Local(LocalType::Value { vector_size: Some(vec_size), kind: crate::ScalarKind::Sint, width: 4, pointer_class: None, - }))?; + }), + )?; let id_extended = self.id_gen.next(); let mut inst = Instruction::image_query( spirv::Op::ImageQuerySizeLod, @@ -2193,7 +2313,7 @@ impl Writer { id_extended, image_id, ); - inst.add_operand(self.get_index_constant(0)?); + inst.add_operand(self.get_index_constant(0, &ir_module.types)?); block.body.push(inst); let id = self.id_gen.next(); block.body.push(Instruction::composite_extract( @@ -2274,24 +2394,25 @@ impl Writer { } /// Write a left-hand-side expression, returning an `id` of the pointer. - fn write_expression_pointer( + fn write_expression_pointer<'a>( &mut self, + ir_module: &'a crate::Module, ir_function: &crate::Function, fun_info: &FunctionInfo, mut expr_handle: Handle, block: &mut Block, function: &mut Function, - ) -> Result { + ) -> Result<(Word, spirv::StorageClass), Error> { let result_lookup_ty = match fun_info[expr_handle].ty { TypeResolution::Handle(ty_handle) => LookupType::Handle(ty_handle), TypeResolution::Value(ref inner) => { LookupType::Local(self.physical_layout.make_local(inner).unwrap()) } }; - let result_type_id = self.get_type_id(result_lookup_ty)?; + let result_type_id = self.get_type_id(&ir_module.types, result_lookup_ty)?; self.temp_list.clear(); - let root_id = loop { + let (root_id, class) = loop { expr_handle = match ir_function.expressions[expr_handle] { crate::Expression::Access { base, index } => { let index_id = self.cached[index]; @@ -2299,21 +2420,21 @@ impl Writer { base } crate::Expression::AccessIndex { base, index } => { - let const_id = self.get_index_constant(index)?; + let const_id = self.get_index_constant(index, &ir_module.types)?; self.temp_list.push(const_id); base } crate::Expression::GlobalVariable(handle) => { let gv = &self.global_variables[handle.index()]; - break gv.id; + break (gv.id, gv.class); } crate::Expression::LocalVariable(variable) => { let local_var = &function.variables[&variable]; - break local_var.id; + break (local_var.id, spirv::StorageClass::Function); } crate::Expression::FunctionArgument(index) => { let id = function.parameters[index as usize].result_id.unwrap(); - break id; + break (id, spirv::StorageClass::Function); } ref other => unimplemented!("Unexpected pointer expression {:?}", other), } @@ -2332,7 +2453,7 @@ impl Writer { )); id }; - Ok(id) + Ok((id, class)) } fn get_expression_global( @@ -2356,6 +2477,7 @@ impl Writer { &mut self, value_id: Word, ir_result: &crate::FunctionResult, + type_arena: &Arena, result_members: &[ResultMember], body: &mut Vec, ) -> Result<(), Error> { @@ -2382,13 +2504,16 @@ impl Writer { && res_member.built_in == Some(crate::BuiltIn::Position) { let access_id = self.id_gen.next(); - let float_ptr_type_id = self.get_type_id(LookupType::Local(LocalType::Value { - vector_size: None, - kind: crate::ScalarKind::Float, - width: 4, - pointer_class: Some(spirv::StorageClass::Output), - }))?; - let index_y_id = self.get_index_constant(1)?; + let float_ptr_type_id = self.get_type_id( + type_arena, + LookupType::Local(LocalType::Value { + vector_size: None, + kind: crate::ScalarKind::Float, + width: 4, + pointer_class: Some(spirv::StorageClass::Output), + }), + )?; + let index_y_id = self.get_index_constant(1, type_arena)?; body.push(Instruction::access_chain( float_ptr_type_id, access_id, @@ -2397,12 +2522,15 @@ impl Writer { )); let load_id = self.id_gen.next(); - let float_type_id = self.get_type_id(LookupType::Local(LocalType::Value { - vector_size: None, - kind: crate::ScalarKind::Float, - width: 4, - pointer_class: None, - }))?; + let float_type_id = self.get_type_id( + type_arena, + LookupType::Local(LocalType::Value { + vector_size: None, + kind: crate::ScalarKind::Float, + width: 4, + pointer_class: None, + }), + )?; body.push(Instruction::load(float_type_id, load_id, access_id, None)); let neg_id = self.id_gen.next(); @@ -2656,6 +2784,7 @@ impl Writer { self.write_entry_point_return( value_id, ir_function.result.as_ref().unwrap(), + &ir_module.types, &context.results, &mut block.body, )?; @@ -2686,9 +2815,12 @@ impl Writer { spirv::MemorySemantics::WORKGROUP_MEMORY, flags.contains(crate::Barrier::WORK_GROUP), ); - let exec_scope_id = self.get_index_constant(spirv::Scope::Workgroup as u32)?; - let mem_scope_id = self.get_index_constant(memory_scope as u32)?; - let semantics_id = self.get_index_constant(semantics.bits())?; + let exec_scope_id = + self.get_index_constant(spirv::Scope::Workgroup as u32, &ir_module.types)?; + let mem_scope_id = + self.get_index_constant(memory_scope as u32, &ir_module.types)?; + let semantics_id = + self.get_index_constant(semantics.bits(), &ir_module.types)?; block.body.push(Instruction::control_barrier( exec_scope_id, mem_scope_id, @@ -2696,7 +2828,8 @@ impl Writer { )); } crate::Statement::Store { pointer, value } => { - let pointer_id = self.write_expression_pointer( + let (pointer_id, _) = self.write_expression_pointer( + ir_module, ir_function, fun_info, pointer, @@ -2749,7 +2882,7 @@ impl Writer { .as_ref() .unwrap() .ty; - self.get_type_id(LookupType::Handle(ty_handle))? + self.get_type_id(&ir_module.types, LookupType::Handle(ty_handle))? } None => self.void_type, }; @@ -2774,7 +2907,8 @@ impl Writer { Some(ref result) if function.entry_point_context.is_none() => { // create a Null and return it let null_id = self.id_gen.next(); - let type_id = self.get_type_id(LookupType::Handle(result.ty))?; + let type_id = + self.get_type_id(&ir_module.types, LookupType::Handle(result.ty))?; Instruction::constant_null(type_id, null_id) .to_words(&mut self.logical_layout.declarations); Instruction::return_value(null_id) @@ -2825,10 +2959,16 @@ impl Writer { self.constant_ids[handle.index()] = match constant.name { Some(ref name) => { let id = self.id_gen.next(); - self.write_constant_scalar(id, value, width, Some(name))?; + self.write_constant_scalar( + id, + value, + width, + Some(name), + &ir_module.types, + )?; id } - None => self.get_constant_scalar(*value, width)?, + None => self.get_constant_scalar(*value, width, &ir_module.types)?, }; } } @@ -2851,7 +2991,7 @@ impl Writer { self.debugs.push(Instruction::name(id, name)); } } - self.write_constant_composite(id, ty, components)?; + self.write_constant_composite(id, ty, components, &ir_module.types)?; } } } @@ -2860,10 +3000,13 @@ impl Writer { // now write all globals self.global_variables.clear(); for (_, var) in ir_module.global_variables.iter() { - let (instruction, id) = self.write_global_variable(ir_module, var)?; + let (instruction, id, class) = self.write_global_variable(ir_module, var)?; instruction.to_words(&mut self.logical_layout.declarations); - self.global_variables - .push(GlobalVariable { id, handle_id: 0 }); + self.global_variables.push(GlobalVariable { + id, + handle_id: 0, + class, + }); } // all functions diff --git a/third_party/rust/naga/src/back/wgsl/mod.rs b/third_party/rust/naga/src/back/wgsl/mod.rs index 6408b5125138..1ca762af9ceb 100644 --- a/third_party/rust/naga/src/back/wgsl/mod.rs +++ b/third_party/rust/naga/src/back/wgsl/mod.rs @@ -13,8 +13,6 @@ pub enum Error { Custom(String), #[error("{0}")] Unimplemented(String), // TODO: Error used only during development - #[error("Unsupported math function: {0:?}")] - UnsupportedMathFunction(crate::MathFunction), } pub fn write_string( diff --git a/third_party/rust/naga/src/back/wgsl/writer.rs b/third_party/rust/naga/src/back/wgsl/writer.rs index f37bd202ca78..f906d3fbaa06 100644 --- a/third_party/rust/naga/src/back/wgsl/writer.rs +++ b/third_party/rust/naga/src/back/wgsl/writer.rs @@ -1,13 +1,15 @@ +// TODO: temp +#![allow(dead_code)] use super::Error; use crate::{ back::{binary_operation_str, vector_size_str, wgsl::keywords::RESERVED}, proc::{EntryPointIndex, NameKey, Namer, TypeResolution}, valid::{FunctionInfo, ModuleInfo}, - Arena, ArraySize, Binding, Constant, ConstantInner, Expression, FastHashMap, Function, - GlobalVariable, Handle, ImageClass, ImageDimension, Interpolation, LocalVariable, Module, - SampleLevel, Sampling, ScalarKind, ScalarValue, ShaderStage, Statement, StorageClass, - StorageFormat, StructMember, Type, TypeInner, + Arena, ArraySize, Binding, Constant, Expression, FastHashMap, Function, GlobalVariable, Handle, + ImageClass, ImageDimension, Interpolation, Module, Sampling, ScalarKind, ScalarValue, + ShaderStage, Statement, StorageFormat, StructLevel, StructMember, Type, TypeInner, }; +use bit_set::BitSet; use std::fmt::Write; const INDENT: &str = " "; @@ -52,26 +54,13 @@ struct FunctionCtx<'a> { info: &'a FunctionInfo, /// The expression arena of the current function being written expressions: &'a Arena, - /// Map of expressions that have associated variable names - named_expressions: &'a crate::NamedExpressions, -} - -impl<'a> FunctionCtx<'_> { - /// Helper method that generates a [`NameKey`](crate::proc::NameKey) for a local in the current function - fn name_key(&self, local: Handle) -> NameKey { - match self.ty { - FunctionType::Function(handle) => NameKey::FunctionLocal(handle, local), - FunctionType::EntryPoint(idx) => NameKey::EntryPointLocal(idx, local), - } - } } pub struct Writer { out: W, names: FastHashMap, namer: Namer, - named_expressions: crate::NamedExpressions, - ep_results: Vec<(ShaderStage, Handle)>, + named_expressions: BitSet, } impl Writer { @@ -80,8 +69,7 @@ impl Writer { out, names: FastHashMap::default(), namer: Namer::default(), - named_expressions: crate::NamedExpressions::default(), - ep_results: vec![], + named_expressions: BitSet::new(), } } @@ -89,28 +77,19 @@ impl Writer { self.names.clear(); self.namer.reset(module, RESERVED, &[], &mut self.names); self.named_expressions.clear(); - self.ep_results.clear(); } pub fn write(&mut self, module: &Module, info: &ModuleInfo) -> BackendResult { self.reset(module); - // Save all ep result types - for (_, ep) in module.entry_points.iter().enumerate() { - if let Some(ref result) = ep.function.result { - self.ep_results.push((ep.stage, result.ty)); - } - } - // Write all structs for (handle, ty) in module.types.iter() { if let TypeInner::Struct { - top_level, - ref members, - .. + level, ref members, .. } = ty.inner { - self.write_struct(module, handle, top_level, members)?; + let block = level == StructLevel::Root; + self.write_struct(module, handle, block, members)?; writeln!(self.out)?; } } @@ -118,13 +97,13 @@ impl Writer { // Write all constants for (handle, constant) in module.constants.iter() { if constant.name.is_some() { - self.write_global_constant(module, &constant.inner, handle)?; + self.write_global_constant(&constant, handle)?; } } // Write all globals for (ty, global) in module.global_variables.iter() { - self.write_global(module, global, ty)?; + self.write_global(&module, &global, ty)?; } if !module.global_variables.is_empty() { @@ -140,11 +119,10 @@ impl Writer { ty: FunctionType::Function(handle), info: fun_info, expressions: &function.expressions, - named_expressions: &function.named_expressions, }; // Write the function - self.write_function(module, function, &func_ctx)?; + self.write_function(&module, &function, &func_ctx)?; writeln!(self.out)?; } @@ -165,11 +143,10 @@ impl Writer { let func_ctx = FunctionCtx { ty: FunctionType::EntryPoint(index as u16), - info: info.get_entry_point(index), + info: &info.get_entry_point(index), expressions: &ep.function.expressions, - named_expressions: &ep.function.named_expressions, }; - self.write_function(module, &ep.function, &func_ctx)?; + self.write_function(&module, &ep.function, &func_ctx)?; if index < module.entry_points.len() - 1 { writeln!(self.out)?; @@ -186,7 +163,7 @@ impl Writer { fn write_scalar_value(&mut self, value: ScalarValue) -> BackendResult { match value { ScalarValue::Sint(value) => write!(self.out, "{}", value)?, - ScalarValue::Uint(value) => write!(self.out, "{}u", value)?, + ScalarValue::Uint(value) => write!(self.out, "{}", value)?, // Floats are written using `Debug` instead of `Display` because it always appends the // decimal part even it's zero ScalarValue::Float(value) => write!(self.out, "{:?}", value)?, @@ -196,29 +173,6 @@ impl Writer { Ok(()) } - /// Helper method used to write stuct name - /// - /// # Notes - /// Adds no trailing or leading whitespace - fn write_struct_name(&mut self, module: &Module, handle: Handle) -> BackendResult { - if module.types[handle].name.is_none() { - if let Some(&(stage, _)) = self.ep_results.iter().find(|&&(_, ty)| ty == handle) { - let name = match stage { - ShaderStage::Compute => "ComputeOutput", - ShaderStage::Fragment => "FragmentOutput", - ShaderStage::Vertex => "VertexOutput", - }; - - write!(self.out, "{}", name)?; - return Ok(()); - } - } - - write!(self.out, "{}", self.names[&NameKey::Type(handle)])?; - - Ok(()) - } - /// Helper method used to write structs /// https://gpuweb.github.io/gpuweb/wgsl/#functions /// @@ -268,11 +222,14 @@ impl Writer { // Write function return type if let Some(ref result) = func.result { - write!(self.out, " -> ")?; if let Some(ref binding) = result.binding { + write!(self.out, " -> ")?; self.write_attributes(&map_binding_to_attribute(binding), true)?; + self.write_type(module, result.ty)?; + } else { + let struct_name = &self.names[&NameKey::Type(result.ty)].clone(); + write!(self.out, " -> {}", struct_name)?; } - self.write_type(module, result.ty)?; } write!(self.out, " {{")?; @@ -285,10 +242,14 @@ impl Writer { // Write the local name // The leading space is important - write!(self.out, "var {}: ", self.names[&func_ctx.name_key(handle)])?; + let name_key = match func_ctx.ty { + FunctionType::Function(func_handle) => NameKey::FunctionLocal(func_handle, handle), + FunctionType::EntryPoint(idx) => NameKey::EntryPointLocal(idx, handle), + }; + write!(self.out, "var {}: ", self.names[&name_key])?; // Write the local type - self.write_type(module, local.ty)?; + self.write_type(&module, local.ty)?; // Write the local initializer if needed if let Some(init) = local.init { @@ -312,7 +273,7 @@ impl Writer { // Write the function body (statement list) for sta in func.body.iter() { // The indentation should always be 1 when writing the function body - self.write_stmt(module, sta, func_ctx, 1)?; + self.write_stmt(&module, sta, &func_ctx, 1)?; } writeln!(self.out, "}}")?; @@ -422,10 +383,8 @@ impl Writer { self.write_attributes(&[Attribute::Block], false)?; writeln!(self.out)?; } - - write!(self.out, "struct ")?; - self.write_struct_name(module, handle)?; - write!(self.out, " {{")?; + let name = &self.names[&NameKey::Type(handle)].clone(); + write!(self.out, "struct {} {{", name)?; writeln!(self.out)?; for (index, member) in members.iter().enumerate() { // Skip struct member with unsupported built in @@ -472,7 +431,12 @@ impl Writer { fn write_type(&mut self, module: &Module, ty: Handle) -> BackendResult { let inner = &module.types[ty].inner; match *inner { - TypeInner::Struct { .. } => self.write_struct_name(module, ty)?, + TypeInner::Struct { .. } => { + // Get the struct name + let name = &self.names[&NameKey::Type(ty)]; + write!(self.out, "{}", name)?; + return Ok(()); + } ref other => self.write_value_type(module, other)?, } @@ -507,10 +471,10 @@ impl Writer { let (class_str, multisampled_str, scalar_str) = match class { ImageClass::Sampled { kind, multi } => ( "", - if multi { "multisampled_" } else { "" }, + if multi { "multisampled" } else { "" }, format!("<{}>", scalar_kind_str(kind)), ), - ImageClass::Depth => ("depth_", "", String::from("")), + ImageClass::Depth => ("depth", "", String::from("")), ImageClass::Storage(storage_format) => ( "storage_", "", @@ -580,52 +544,13 @@ impl Writer { match *stmt { Statement::Emit(ref range) => { for handle in range.clone() { - let expr_name = if let Some(name) = func_ctx.named_expressions.get(&handle) { - // Front end provides names for all variables at the start of writing. - // But we write them to step by step. We need to recache them - // Otherwise, we could accidentally write variable name instead of full expression. - // Also, we use sanitized names! It defense backend from generating variable with name from reserved keywords. - Some(self.namer.call_unique(name)) - } else { - let expr = &func_ctx.expressions[handle]; - let min_ref_count = expr.bake_ref_count(); - // Forcefully creating baking expressions in some cases to help with readability - let required_baking_expr = match *expr { - Expression::ImageLoad { .. } - | Expression::ImageQuery { .. } - | Expression::ImageSample { .. } => true, - _ => false, - }; - if min_ref_count <= func_ctx.info[handle].ref_count || required_baking_expr - { - // If expression contains unsupported builtin we should skip it - if let Expression::Load { pointer } = func_ctx.expressions[handle] { - if let Expression::AccessIndex { base, index } = - func_ctx.expressions[pointer] - { - if access_to_unsupported_builtin( - base, - index, - module, - func_ctx.info, - ) { - return Ok(()); - } - } - } - - Some(format!("{}{}", BAKE_PREFIX, handle.index())) - } else { - None - } - }; - - if let Some(name) = expr_name { + let min_ref_count = func_ctx.expressions[handle].bake_ref_count(); + if min_ref_count <= func_ctx.info[handle].ref_count { write!(self.out, "{}", INDENT.repeat(indent))?; - self.start_named_expr(module, handle, func_ctx, &name)?; - self.write_expr(module, handle, func_ctx)?; - self.named_expressions.insert(handle, name); + self.start_baking_expr(module, handle, &func_ctx)?; + self.write_expr(module, handle, &func_ctx)?; writeln!(self.out, ";")?; + self.named_expressions.insert(handle.index()); } } } @@ -664,7 +589,7 @@ impl Writer { if let Some(return_value) = value { // The leading space is important write!(self.out, " ")?; - self.write_expr(module, return_value, func_ctx)?; + self.write_expr(module, return_value, &func_ctx)?; } writeln!(self.out, ";")?; } @@ -673,32 +598,23 @@ impl Writer { write!(self.out, "{}", INDENT.repeat(indent))?; writeln!(self.out, "discard;")? } + // TODO: copy-paste from glsl-out Statement::Store { pointer, value } => { - // WGSL does not support all SPIR-V builtins and we should skip it in generated shaders. - // We already skip them when we generate struct type. - // Now we need to find expression that used struct with ignored builtins - if let Expression::AccessIndex { base, index } = func_ctx.expressions[pointer] { - if access_to_unsupported_builtin(base, index, module, func_ctx.info) { - return Ok(()); - } - } write!(self.out, "{}", INDENT.repeat(indent))?; self.write_expr(module, pointer, func_ctx)?; write!(self.out, " = ")?; self.write_expr(module, value, func_ctx)?; writeln!(self.out, ";")? } - Statement::Call { + crate::Statement::Call { function, ref arguments, result, } => { write!(self.out, "{}", INDENT.repeat(indent))?; if let Some(expr) = result { - let name = format!("{}{}", BAKE_PREFIX, expr.index()); - self.start_named_expr(module, expr, func_ctx, &name)?; - self.write_expr(module, expr, func_ctx)?; - self.named_expressions.insert(expr, name); + self.start_baking_expr(module, expr, &func_ctx)?; + self.named_expressions.insert(expr.index()); } let func_name = &self.names[&NameKey::Function(function)]; write!(self.out, "{}(", func_name)?; @@ -712,147 +628,41 @@ impl Writer { } writeln!(self.out, ");")? } - Statement::ImageStore { - image, - coordinate, - array_index, - value, - } => { - write!(self.out, "{}", INDENT.repeat(indent))?; - write!(self.out, "textureStore(")?; - self.write_expr(module, image, func_ctx)?; - write!(self.out, ", ")?; - self.write_expr(module, coordinate, func_ctx)?; - if let Some(array_index_expr) = array_index { - write!(self.out, ", ")?; - self.write_expr(module, array_index_expr, func_ctx)?; - } - write!(self.out, ", ")?; - self.write_expr(module, value, func_ctx)?; - writeln!(self.out, ");")?; - } - // TODO: copy-paste from glsl-out - Statement::Block(ref block) => { - write!(self.out, "{}", INDENT.repeat(indent))?; - writeln!(self.out, "{{")?; - for sta in block.iter() { - // Increase the indentation to help with readability - self.write_stmt(module, sta, func_ctx, indent + 1)? - } - writeln!(self.out, "{}}}", INDENT.repeat(indent))? - } - Statement::Switch { - selector, - ref cases, - ref default, - } => { - // Start the switch - write!(self.out, "{}", INDENT.repeat(indent))?; - write!(self.out, "switch(")?; - self.write_expr(module, selector, func_ctx)?; - writeln!(self.out, ") {{")?; - - // Write all cases - let mut write_case = true; - let all_fall_through = cases - .iter() - .all(|case| case.fall_through && case.body.is_empty()); - if !cases.is_empty() { - for case in cases { - if write_case { - write!(self.out, "{}case ", INDENT.repeat(indent + 1))?; - } - if !all_fall_through && case.fall_through && case.body.is_empty() { - write_case = false; - write!(self.out, "{}, ", case.value)?; - continue; - } else { - write_case = true; - writeln!(self.out, "{}: {{", case.value)?; - } - - for sta in case.body.iter() { - self.write_stmt(module, sta, func_ctx, indent + 2)?; - } - - if case.fall_through { - writeln!(self.out, "{}fallthrough;", INDENT.repeat(indent + 2))?; - } - } - - writeln!(self.out, "{}}}", INDENT.repeat(indent + 1))?; - } - - if !default.is_empty() { - writeln!(self.out, "{}default: {{", INDENT.repeat(indent + 1))?; - - for sta in default { - self.write_stmt(module, sta, func_ctx, indent + 2)?; - } - - writeln!(self.out, "{}}}", INDENT.repeat(indent + 1))? - } - - writeln!(self.out, "{}}}", INDENT.repeat(indent))? - } - Statement::Loop { - ref body, - ref continuing, - } => { - write!(self.out, "{}", INDENT.repeat(indent))?; - writeln!(self.out, "loop {{")?; - - for sta in body.iter() { - self.write_stmt(module, sta, func_ctx, indent + 1)?; - } - - if !continuing.is_empty() { - writeln!(self.out, "{}continuing {{", INDENT.repeat(indent + 1))?; - for sta in continuing.iter() { - self.write_stmt(module, sta, func_ctx, indent + 2)?; - } - writeln!(self.out, "{}}}", INDENT.repeat(indent + 1))?; - } - - writeln!(self.out, "{}}}", INDENT.repeat(indent))? - } - Statement::Break => { - writeln!(self.out, "{}break;", INDENT.repeat(indent))?; - } - Statement::Continue => { - writeln!(self.out, "{}continue;", INDENT.repeat(indent))?; - } - Statement::Barrier(barrier) => { - if barrier.contains(crate::Barrier::STORAGE) { - writeln!(self.out, "{}storageBarrier();", INDENT.repeat(indent))?; - } - - if barrier.contains(crate::Barrier::WORK_GROUP) { - writeln!(self.out, "{}workgroupBarrier();", INDENT.repeat(indent))?; - } + _ => { + return Err(Error::Unimplemented(format!("write_stmt {:?}", stmt))); } } Ok(()) } - fn start_named_expr( + fn start_baking_expr( &mut self, module: &Module, handle: Handle, - func_ctx: &FunctionCtx, - name: &str, + context: &FunctionCtx, ) -> BackendResult { // Write variable name - write!(self.out, "let {}: ", name)?; - let ty = &func_ctx.info[handle].ty; + write!(self.out, "let {}{}: ", BAKE_PREFIX, handle.index())?; + let ty = &context.info[handle].ty; // Write variable type match *ty { - TypeResolution::Handle(handle) => { - self.write_type(module, handle)?; + TypeResolution::Handle(ty_handle) => { + self.write_type(module, ty_handle)?; } - TypeResolution::Value(ref inner) => { - self.write_value_type(module, inner)?; + TypeResolution::Value(crate::TypeInner::Scalar { kind, .. }) => { + write!(self.out, "{}", scalar_kind_str(kind))?; + } + TypeResolution::Value(crate::TypeInner::Vector { size, kind, .. }) => { + write!( + self.out, + "vec{}<{}>", + vector_size_str(size), + scalar_kind_str(kind), + )?; + } + _ => { + return Err(Error::Unimplemented(format!("start_baking_expr {:?}", ty))); } } @@ -870,17 +680,17 @@ impl Writer { expr: Handle, func_ctx: &FunctionCtx<'_>, ) -> BackendResult { - if let Some(name) = self.named_expressions.get(&expr) { - write!(self.out, "{}", name)?; + let expression = &func_ctx.expressions[expr]; + + if self.named_expressions.contains(expr.index()) { + write!(self.out, "{}{}", BAKE_PREFIX, expr.index())?; return Ok(()); } - let expression = &func_ctx.expressions[expr]; - match *expression { Expression::Constant(constant) => self.write_constant(module, constant)?, Expression::Compose { ty, ref components } => { - self.write_type(module, ty)?; + self.write_type(&module, ty)?; write!(self.out, "(")?; // !spv-in specific notes! // WGSL does not support all SPIR-V builtins and we should skip it in generated shaders. @@ -897,11 +707,35 @@ impl Writer { for component in components { let mut skip_component = false; if let Expression::Load { pointer } = func_ctx.expressions[*component] { - if let Expression::AccessIndex { base, index } = - func_ctx.expressions[pointer] + if let Expression::AccessIndex { + base, + index: access_index, + } = func_ctx.expressions[pointer] { - if access_to_unsupported_builtin(base, index, module, func_ctx.info) { - skip_component = true; + let base_ty_res = &func_ctx.info[base].ty; + let resolved = base_ty_res.inner_with(&module.types); + if let TypeInner::Pointer { + base: pointer_base_handle, + .. + } = *resolved + { + // Let's check that we try to access a struct member with unsupported built-in and skip it. + if let TypeInner::Struct { ref members, .. } = + module.types[pointer_base_handle].inner + { + if let Some(Binding::BuiltIn(builtin)) = + members[access_index as usize].binding + { + if builtin_str(builtin).is_none() { + // glslang why you did this with us... + log::warn!( + "Skip component with unsupported builtin {:?}", + builtin + ); + skip_component = true; + } + } + } } } } @@ -915,7 +749,7 @@ impl Writer { // non spv-in specific notes! // Real `Expression::Compose` logic generates here. for (index, component) in components_to_write.iter().enumerate() { - self.write_expr(module, *component, func_ctx)?; + self.write_expr(module, *component, &func_ctx)?; // Only write a comma if isn't the last element if index != components_to_write.len().saturating_sub(1) { // The leading space is for readability only @@ -935,11 +769,11 @@ impl Writer { write!(self.out, "{}", name)?; } Expression::Binary { op, left, right } => { - write!(self.out, "(")?; self.write_expr(module, left, func_ctx)?; - write!(self.out, " {} ", binary_operation_str(op))?; + + write!(self.out, " {} ", binary_operation_str(op),)?; + self.write_expr(module, right, func_ctx)?; - write!(self.out, ")")?; } // TODO: copy-paste from glsl-out Expression::Access { base, index } => { @@ -963,11 +797,8 @@ impl Writer { }; match *resolved { - TypeInner::Vector { .. } => { - // Write vector access as a swizzle - write!(self.out, ".{}", COMPONENTS[index as usize])? - } - TypeInner::Matrix { .. } + TypeInner::Vector { .. } + | TypeInner::Matrix { .. } | TypeInner::Array { .. } | TypeInner::ValuePointer { .. } => write!(self.out, "[{}]", index)?, TypeInner::Struct { .. } => { @@ -988,130 +819,47 @@ impl Writer { image, sampler, coordinate, - array_index, - offset, + array_index: _, + offset: _, level, - depth_ref, + depth_ref: _, } => { - let suffix_cmp = match depth_ref { - Some(_) => "Compare", - None => "", + // TODO: other texture functions + // TODO: comments + let fun_name = match level { + crate::SampleLevel::Auto => "textureSample", + _ => { + return Err(Error::Unimplemented(format!( + "expression_imagesample_level {:?}", + level + ))); + } }; - let suffix_level = match level { - SampleLevel::Auto => "", - SampleLevel::Zero | SampleLevel::Exact(_) => "Level", - SampleLevel::Bias(_) => "Bias", - SampleLevel::Gradient { .. } => "Grad", - }; - - write!(self.out, "textureSample{}{}(", suffix_cmp, suffix_level)?; + write!(self.out, "{}(", fun_name)?; self.write_expr(module, image, func_ctx)?; write!(self.out, ", ")?; self.write_expr(module, sampler, func_ctx)?; write!(self.out, ", ")?; self.write_expr(module, coordinate, func_ctx)?; - - if let Some(array_index) = array_index { - write!(self.out, ", ")?; - self.write_expr(module, array_index, func_ctx)?; - } - - if let Some(depth_ref) = depth_ref { - write!(self.out, ", ")?; - self.write_expr(module, depth_ref, func_ctx)?; - } - - match level { - SampleLevel::Auto => {} - SampleLevel::Zero => { - // Level 0 is implied for depth comparison - if depth_ref.is_none() { - write!(self.out, ", 0.0")?; - } - } - SampleLevel::Exact(expr) => { - write!(self.out, ", ")?; - self.write_expr(module, expr, func_ctx)?; - } - SampleLevel::Bias(expr) => { - write!(self.out, ", ")?; - self.write_expr(module, expr, func_ctx)?; - } - SampleLevel::Gradient { x, y } => { - write!(self.out, ", ")?; - self.write_expr(module, x, func_ctx)?; - write!(self.out, ", ")?; - self.write_expr(module, y, func_ctx)?; - } - } - - if let Some(offset) = offset { - write!(self.out, ", ")?; - self.write_constant(module, offset)?; - } - - write!(self.out, ")")?; - } - Expression::ImageQuery { image, query } => { - let texture_function = match query { - crate::ImageQuery::Size { .. } => "textureDimensions", - crate::ImageQuery::NumLevels => "textureNumLevels", - crate::ImageQuery::NumLayers => "textureNumLayers", - crate::ImageQuery::NumSamples => "textureNumSamples", - }; - - write!(self.out, "{}(", texture_function)?; - self.write_expr(module, image, func_ctx)?; - if let crate::ImageQuery::Size { level: Some(level) } = query { - write!(self.out, ", ")?; - self.write_expr(module, level, func_ctx)?; - }; - write!(self.out, ")")?; - } - Expression::ImageLoad { - image, - coordinate, - array_index, - index, - } => { - write!(self.out, "textureLoad(")?; - self.write_expr(module, image, func_ctx)?; - write!(self.out, ", ")?; - self.write_expr(module, coordinate, func_ctx)?; - if let Some(array_index) = array_index { - write!(self.out, ", ")?; - self.write_expr(module, array_index, func_ctx)?; - } - if let Some(index) = index { - write!(self.out, ", ")?; - self.write_expr(module, index, func_ctx)?; - } write!(self.out, ")")?; } + // TODO: copy-paste from msl-out Expression::GlobalVariable(handle) => { let name = &self.names[&NameKey::GlobalVariable(handle)]; write!(self.out, "{}", name)?; } - Expression::As { expr, kind, .. } => { + Expression::As { + expr, + kind, + convert: _, //TODO: + } => { let inner = func_ctx.info[expr].ty.inner_with(&module.types); - match *inner { + let op = match *inner { TypeInner::Matrix { columns, rows, .. } => { - write!( - self.out, - "mat{}x{}", - vector_size_str(columns), - vector_size_str(rows) - )?; + format!("mat{}x{}", vector_size_str(columns), vector_size_str(rows)) } - TypeInner::Vector { size, .. } => { - write!( - self.out, - "vec{}<{}>", - vector_size_str(size), - scalar_kind_str(kind) - )?; - } - TypeInner::Scalar { .. } => write!(self.out, "{}", scalar_kind_str(kind))?, + TypeInner::Vector { size, .. } => format!("vec{}", vector_size_str(size)), + TypeInner::Scalar { kind, .. } => String::from(scalar_kind_str(kind)), _ => { return Err(Error::Unimplemented(format!( "write_expr expression::as {:?}", @@ -1119,7 +867,8 @@ impl Writer { ))); } }; - write!(self.out, "(")?; + let scalar = scalar_kind_str(kind); + write!(self.out, "{}<{}>(", op, scalar)?; self.write_expr(module, expr, func_ctx)?; write!(self.out, ")")?; } @@ -1141,15 +890,19 @@ impl Writer { self.write_expr(module, value, func_ctx)?; write!(self.out, ")")?; } + //TODO: add pointer logic Expression::Load { pointer } => self.write_expr(module, pointer, func_ctx)?, Expression::LocalVariable(handle) => { - write!(self.out, "{}", self.names[&func_ctx.name_key(handle)])? + let name_key = match func_ctx.ty { + FunctionType::Function(func_handle) => { + NameKey::FunctionLocal(func_handle, handle) + } + FunctionType::EntryPoint(idx) => NameKey::EntryPointLocal(idx, handle), + }; + write!(self.out, "{}", self.names[&name_key])? } Expression::ArrayLength(expr) => { write!(self.out, "arrayLength(")?; - if is_deref_required(expr, module, func_ctx.info) { - write!(self.out, "&")?; - }; self.write_expr(module, expr, func_ctx)?; write!(self.out, ")")?; } @@ -1162,60 +915,13 @@ impl Writer { use crate::MathFunction as Mf; let fun_name = match fun { - Mf::Abs => "abs", - Mf::Min => "min", - Mf::Max => "max", - Mf::Clamp => "clamp", - // trigonometry - Mf::Cos => "cos", - Mf::Cosh => "cosh", - Mf::Sin => "sin", - Mf::Sinh => "sinh", - Mf::Tan => "tan", - Mf::Tanh => "tanh", - Mf::Acos => "acos", - Mf::Asin => "asin", - Mf::Atan => "atan", - Mf::Atan2 => "atan2", - // decomposition - Mf::Ceil => "ceil", - Mf::Floor => "floor", - Mf::Round => "round", - Mf::Fract => "fract", - Mf::Trunc => "trunc", - Mf::Modf => "modf", - Mf::Frexp => "frexp", - Mf::Ldexp => "ldexp", - // exponent - Mf::Exp => "exp", - Mf::Exp2 => "exp2", - Mf::Log => "log", - Mf::Log2 => "log2", - Mf::Pow => "pow", - // geometry - Mf::Dot => "dot", - Mf::Outer => "outerProduct", - Mf::Cross => "cross", - Mf::Distance => "distance", Mf::Length => "length", - Mf::Normalize => "normalize", - Mf::FaceForward => "faceForward", - Mf::Reflect => "reflect", - // computational - Mf::Sign => "sign", - Mf::Fma => "fma", Mf::Mix => "mix", - Mf::Step => "step", - Mf::SmoothStep => "smoothStep", - Mf::Sqrt => "sqrt", - Mf::InverseSqrt => "inverseSqrt", - Mf::Transpose => "transpose", - Mf::Determinant => "determinant", - // bits - Mf::CountOneBits => "countOneBits", - Mf::ReverseBits => "reverseBits", _ => { - return Err(Error::UnsupportedMathFunction(fun)); + return Err(Error::Unimplemented(format!( + "write_expr Math func {:?}", + fun + ))); } }; @@ -1242,66 +948,9 @@ impl Writer { self.out.write_char(COMPONENTS[sc as usize])?; } } - Expression::Unary { op, expr } => { - let unary = match op { - crate::UnaryOperator::Negate => "-", - crate::UnaryOperator::Not => { - match *func_ctx.info[expr].ty.inner_with(&module.types) { - TypeInner::Scalar { - kind: ScalarKind::Bool, - .. - } - | TypeInner::Vector { .. } => "!", - _ => "~", - } - } - }; - - write!(self.out, "{}(", unary)?; - self.write_expr(module, expr, func_ctx)?; - - write!(self.out, ")")? + _ => { + return Err(Error::Unimplemented(format!("write_expr {:?}", expression))); } - Expression::Select { - condition, - accept, - reject, - } => { - write!(self.out, "select(")?; - self.write_expr(module, accept, func_ctx)?; - write!(self.out, ", ")?; - self.write_expr(module, reject, func_ctx)?; - write!(self.out, ", ")?; - self.write_expr(module, condition, func_ctx)?; - write!(self.out, ")")? - } - Expression::Derivative { axis, expr } => { - let op = match axis { - crate::DerivativeAxis::X => "dpdx", - crate::DerivativeAxis::Y => "dpdy", - crate::DerivativeAxis::Width => "fwidth", - }; - write!(self.out, "{}(", op)?; - self.write_expr(module, expr, func_ctx)?; - write!(self.out, ")")? - } - Expression::Relational { fun, argument } => { - let fun_name = match fun { - crate::RelationalFunction::IsFinite => "isFinite", - crate::RelationalFunction::IsInf => "isInf", - crate::RelationalFunction::IsNan => "isNan", - crate::RelationalFunction::IsNormal => "isNormal", - crate::RelationalFunction::All => "all", - crate::RelationalFunction::Any => "any", - }; - write!(self.out, "{}(", fun_name)?; - - self.write_expr(module, argument, func_ctx)?; - - write!(self.out, ")")? - } - // Nothing to do here, since call expression already cached - Expression::Call(_) => {} } Ok(()) @@ -1329,25 +978,14 @@ impl Writer { writeln!(self.out)?; } - // First write global name and storage class if supported - write!(self.out, "var")?; - if let Some(storage_class) = storage_class_str(global.class) { - write!(self.out, "<{}>", storage_class)?; - } - write!(self.out, " {}: ", name)?; + // First write only global name + write!(self.out, "var {}: ", name)?; // Write access attribute if present if !global.storage_access.is_empty() { self.write_attributes(&[Attribute::Access(global.storage_access)], true)?; } // Write global type self.write_type(module, global.ty)?; - - // Write initializer - if let Some(init) = global.init { - write!(self.out, " = ")?; - self.write_constant(module, init)?; - } - // End with semicolon writeln!(self.out, ";")?; @@ -1365,8 +1003,8 @@ impl Writer { width: _, ref value, } => { - if constant.name.is_some() { - write!(self.out, "{}", self.names[&NameKey::Constant(handle)])?; + if let Some(ref name) = constant.name { + write!(self.out, "{}", name)?; } else { self.write_scalar_value(*value)?; } @@ -1375,25 +1013,8 @@ impl Writer { self.write_type(module, ty)?; write!(self.out, "(")?; - let members = match module.types[ty].inner { - TypeInner::Struct { ref members, .. } => Some(members), - _ => None, - }; - // Write the comma separated constants for (index, constant) in components.iter().enumerate() { - if let Some(&Binding::BuiltIn(builtin)) = - members.and_then(|members| members.get(index)?.binding.as_ref()) - { - if builtin_str(builtin).is_none() { - log::warn!( - "Skip constant for struct member with unsupported builtin {:?}", - builtin - ); - continue; - } - } - self.write_constant(module, *constant)?; // Only write a comma if isn't the last element if index != components.len().saturating_sub(1) { @@ -1414,11 +1035,10 @@ impl Writer { /// Ends in a newline fn write_global_constant( &mut self, - module: &Module, - inner: &ConstantInner, + constant: &Constant, handle: Handle, ) -> BackendResult { - match *inner { + match constant.inner { crate::ConstantInner::Scalar { width: _, ref value, @@ -1432,7 +1052,7 @@ impl Writer { write!(self.out, "i32 = {}", value)?; } crate::ScalarValue::Uint(value) => { - write!(self.out, "u32 = {}u", value)?; + write!(self.out, "u32 = {}", value)?; } crate::ScalarValue::Float(value) => { // Floats are written using `Debug` instead of `Display` because it always appends the @@ -1443,33 +1063,18 @@ impl Writer { write!(self.out, "bool = {}", value)?; } }; - // End with semicolon + // End with semicolon and extra newline for readability writeln!(self.out, ";")?; + writeln!(self.out)?; } - ConstantInner::Composite { ty, ref components } => { - let name = self.names[&NameKey::Constant(handle)].clone(); - // First write only constant name - write!(self.out, "let {}: ", name)?; - // Next write constant type - self.write_type(module, ty)?; - - write!(self.out, " = ")?; - self.write_type(module, ty)?; - - write!(self.out, "(")?; - for (index, constant) in components.iter().enumerate() { - self.write_constant(module, *constant)?; - // Only write a comma if isn't the last element - if index != components.len().saturating_sub(1) { - // The leading space is for readability only - write!(self.out, ", ")?; - } - } - write!(self.out, ");")?; + _ => { + return Err(Error::Unimplemented(format!( + "write_global_constant {:?}", + constant.inner + ))); } } - // End with extra newline for readability - writeln!(self.out)?; + Ok(()) } @@ -1570,17 +1175,6 @@ fn sampling_str(sampling: Sampling) -> &'static str { } } -fn storage_class_str(storage_class: StorageClass) -> Option<&'static str> { - match storage_class { - StorageClass::Private => Some("private"), - StorageClass::Uniform => Some("uniform"), - StorageClass::Storage => Some("storage"), - StorageClass::PushConstant => Some("push_constant"), - StorageClass::WorkGroup => Some("workgroup"), - StorageClass::Function | StorageClass::Handle => None, - } -} - fn map_binding_to_attribute(binding: &Binding) -> Vec { match *binding { Binding::BuiltIn(built_in) => vec![Attribute::BuiltIn(built_in)], @@ -1594,44 +1188,3 @@ fn map_binding_to_attribute(binding: &Binding) -> Vec { ], } } - -fn is_deref_required(expr: Handle, module: &Module, info: &FunctionInfo) -> bool { - let base_ty_res = &info[expr].ty; - let resolved = base_ty_res.inner_with(&module.types); - match *resolved { - TypeInner::Pointer { base, class: _ } => match module.types[base].inner { - TypeInner::Scalar { .. } | TypeInner::Vector { .. } | TypeInner::Array { .. } => true, - _ => false, - }, - TypeInner::ValuePointer { .. } => true, - _ => false, - } -} - -/// Helper function that check that expression don't access to structure member with unsupported builtin. -fn access_to_unsupported_builtin( - expr: Handle, - index: u32, - module: &Module, - info: &FunctionInfo, -) -> bool { - let base_ty_res = &info[expr].ty; - let resolved = base_ty_res.inner_with(&module.types); - if let TypeInner::Pointer { - base: pointer_base_handle, - .. - } = *resolved - { - // Let's check that we try to access a struct member with unsupported built-in and skip it. - if let TypeInner::Struct { ref members, .. } = module.types[pointer_base_handle].inner { - if let Some(Binding::BuiltIn(builtin)) = members[index as usize].binding { - if builtin_str(builtin).is_none() { - log::warn!("Skip component with unsupported builtin {:?}", builtin); - return true; - } - } - } - } - - false -} diff --git a/third_party/rust/naga/src/front/glsl/ast.rs b/third_party/rust/naga/src/front/glsl/ast.rs index aff74e9f2382..aaa8a7d80bc6 100644 --- a/third_party/rust/naga/src/front/glsl/ast.rs +++ b/third_party/rust/naga/src/front/glsl/ast.rs @@ -1,99 +1,21 @@ -use super::{ - super::{Emitter, Typifier}, - constants::ConstantSolver, - error::ErrorKind, - SourceMetadata, -}; +use super::{super::Typifier, constants::ConstantSolver, error::ErrorKind}; use crate::{ - proc::ResolveContext, Arena, BinaryOperator, Binding, Block, Constant, Expression, FastHashMap, - Function, FunctionArgument, GlobalVariable, Handle, Interpolation, LocalVariable, Module, - RelationalFunction, ResourceBinding, Sampling, ScalarKind, ScalarValue, ShaderStage, Statement, - StorageClass, Type, TypeInner, UnaryOperator, VectorSize, + proc::ResolveContext, Arena, ArraySize, BinaryOperator, Binding, Constant, Expression, + FastHashMap, Function, FunctionArgument, GlobalVariable, Handle, Interpolation, LocalVariable, + Module, RelationalFunction, ResourceBinding, Sampling, ShaderStage, Statement, StorageClass, + Type, UnaryOperator, }; -use core::convert::TryFrom; - -#[derive(Debug, Clone, Copy)] -pub enum GlobalLookupKind { - Variable(Handle), - Constant(Handle), - BlockSelect(Handle, u32), -} - -#[derive(Debug, Clone, Copy)] -pub struct GlobalLookup { - pub kind: GlobalLookupKind, - pub entry_arg: Option, - pub mutable: bool, -} - -#[derive(Debug, PartialEq, Eq, Hash)] -pub struct FunctionSignature { - pub name: String, - pub parameters: Vec>, -} - -#[derive(Debug, Clone)] -pub struct FunctionDeclaration { - pub qualifiers: Vec, - pub handle: Handle, - /// Wheter this function was already defined or is just a prototype - pub defined: bool, - /// Wheter or not this function returns void (nothing) - pub void: bool, -} - -bitflags::bitflags! { - pub struct EntryArgUse: u32 { - const READ = 0x1; - const WRITE = 0x2; - } -} - -bitflags::bitflags! { - pub struct PrologueStage: u32 { - const VERTEX = 0x1; - const FRAGMENT = 0x2; - const COMPUTE = 0x4; - } -} - -impl From for PrologueStage { - fn from(stage: ShaderStage) -> Self { - match stage { - ShaderStage::Vertex => PrologueStage::VERTEX, - ShaderStage::Fragment => PrologueStage::FRAGMENT, - ShaderStage::Compute => PrologueStage::COMPUTE, - } - } -} - -#[derive(Debug)] -pub struct EntryArg { - pub name: Option, - pub binding: Binding, - pub handle: Handle, - pub prologue: PrologueStage, -} #[derive(Debug)] pub struct Program<'a> { pub version: u16, pub profile: Profile, pub entry_points: &'a FastHashMap, - - pub workgroup_size: [u32; 3], - pub early_fragment_tests: bool, - - pub lookup_function: FastHashMap, + pub lookup_function: FastHashMap>, pub lookup_type: FastHashMap>, - - pub global_variables: Vec<(String, GlobalLookup)>, - - pub entry_args: Vec, - pub entries: Vec<(String, ShaderStage, Handle)>, - // TODO: More efficient representation - pub function_arg_use: Vec>, - + pub lookup_global_variables: FastHashMap>, + pub lookup_constants: FastHashMap>, + pub context: Context, pub module: Module, } @@ -103,239 +25,201 @@ impl<'a> Program<'a> { version: 0, profile: Profile::Core, entry_points, - - workgroup_size: [1; 3], - early_fragment_tests: false, - lookup_function: FastHashMap::default(), lookup_type: FastHashMap::default(), - global_variables: Vec::new(), - - entry_args: Vec::new(), - entries: Vec::new(), - function_arg_use: Vec::new(), - + lookup_global_variables: FastHashMap::default(), + lookup_constants: FastHashMap::default(), + context: Context { + expressions: Arena::::new(), + local_variables: Arena::::new(), + arguments: Vec::new(), + scopes: vec![FastHashMap::default()], + lookup_global_var_exps: FastHashMap::default(), + lookup_constant_exps: FastHashMap::default(), + typifier: Typifier::new(), + }, module: Module::default(), } } - pub fn resolve_type<'b>( - &'b mut self, - context: &'b mut Context, - handle: Handle, - meta: SourceMetadata, - ) -> Result<&'b TypeInner, ErrorKind> { - let resolve_ctx = ResolveContext { - constants: &self.module.constants, - types: &self.module.types, - global_vars: &self.module.global_variables, - local_vars: context.locals, - functions: &self.module.functions, - arguments: context.arguments, - }; - match context - .typifier - .grow(handle, context.expressions, &resolve_ctx) - { - //TODO: better error report - Err(error) => Err(ErrorKind::SemanticError( - meta, - format!("Can't resolve type: {:?}", error).into(), - )), - Ok(()) => Ok(context.typifier.get(handle, &self.module.types)), - } + pub fn binary_expr( + &mut self, + op: BinaryOperator, + left: &ExpressionRule, + right: &ExpressionRule, + ) -> ExpressionRule { + ExpressionRule::from_expression(self.context.expressions.append(Expression::Binary { + op, + left: left.expression, + right: right.expression, + })) } - pub fn resolve_handle( + pub fn unary_expr(&mut self, op: UnaryOperator, tgt: &ExpressionRule) -> ExpressionRule { + ExpressionRule::from_expression(self.context.expressions.append(Expression::Unary { + op, + expr: tgt.expression, + })) + } + + /// Helper function to insert equality expressions, this handles the special + /// case of `vec1 == vec2` and `vec1 != vec2` since in the IR they are + /// represented as `all(equal(vec1, vec2))` and `any(notEqual(vec1, vec2))` + pub fn equality_expr( + &mut self, + equals: bool, + left: &ExpressionRule, + right: &ExpressionRule, + ) -> Result { + let left_is_vector = match *self.resolve_type(left.expression)? { + crate::TypeInner::Vector { .. } => true, + _ => false, + }; + + let right_is_vector = match *self.resolve_type(right.expression)? { + crate::TypeInner::Vector { .. } => true, + _ => false, + }; + + let (op, fun) = match equals { + true => (BinaryOperator::Equal, RelationalFunction::All), + false => (BinaryOperator::NotEqual, RelationalFunction::Any), + }; + + let expr = + ExpressionRule::from_expression(self.context.expressions.append(Expression::Binary { + op, + left: left.expression, + right: right.expression, + })); + + Ok(if left_is_vector && right_is_vector { + ExpressionRule::from_expression(self.context.expressions.append( + Expression::Relational { + fun, + argument: expr.expression, + }, + )) + } else { + expr + }) + } + + pub fn resolve_type( &mut self, - context: &mut Context, handle: Handle, - meta: SourceMetadata, - ) -> Result, ErrorKind> { + ) -> Result<&crate::TypeInner, ErrorKind> { let resolve_ctx = ResolveContext { constants: &self.module.constants, - types: &self.module.types, global_vars: &self.module.global_variables, - local_vars: context.locals, + local_vars: &self.context.local_variables, functions: &self.module.functions, - arguments: context.arguments, + arguments: &self.context.arguments, }; - match context - .typifier - .grow(handle, context.expressions, &resolve_ctx) - { + match self.context.typifier.grow( + handle, + &self.context.expressions, + &mut self.module.types, + &resolve_ctx, + ) { //TODO: better error report Err(error) => Err(ErrorKind::SemanticError( - meta, format!("Can't resolve type: {:?}", error).into(), )), - Ok(()) => Ok(context.typifier.get_handle(handle, &mut self.module.types)), + Ok(()) => Ok(self.context.typifier.get(handle, &self.module.types)), } } pub fn solve_constant( &mut self, - ctx: &Context, root: Handle, - meta: SourceMetadata, ) -> Result, ErrorKind> { let mut solver = ConstantSolver { types: &self.module.types, - expressions: ctx.expressions, + expressions: &self.context.expressions, constants: &mut self.module.constants, }; - solver.solve(root).map_err(|e| (meta, e).into()) + solver + .solve(root) + .map_err(|_| ErrorKind::SemanticError("Can't solve constant".into())) + } + + pub fn type_size(&self, ty: Handle) -> Result { + Ok(match self.module.types[ty].inner { + crate::TypeInner::Scalar { width, .. } => width, + crate::TypeInner::Vector { size, width, .. } => size as u8 * width, + crate::TypeInner::Matrix { + columns, + rows, + width, + } => columns as u8 * rows as u8 * width, + crate::TypeInner::Pointer { .. } => { + return Err(ErrorKind::NotImplemented("type size of pointer")) + } + crate::TypeInner::ValuePointer { .. } => { + return Err(ErrorKind::NotImplemented("type size of value pointer")) + } + crate::TypeInner::Array { size, stride, .. } => { + stride as u8 + * match size { + ArraySize::Dynamic => { + return Err(ErrorKind::NotImplemented("type size of dynamic array")) + } + ArraySize::Constant(constant) => { + match self.module.constants[constant].inner { + crate::ConstantInner::Scalar { width, .. } => width, + crate::ConstantInner::Composite { .. } => { + return Err(ErrorKind::NotImplemented( + "type size of array with composite item size", + )) + } + } + } + } + } + crate::TypeInner::Struct { .. } => { + return Err(ErrorKind::NotImplemented("type size of struct")) + } + crate::TypeInner::Image { .. } => { + return Err(ErrorKind::NotImplemented("type size of image")) + } + crate::TypeInner::Sampler { .. } => { + return Err(ErrorKind::NotImplemented("type size of sampler")) + } + }) } } -#[derive(Debug, PartialEq)] +#[derive(Debug)] pub enum Profile { Core, } #[derive(Debug)] -pub struct Context<'function> { - expressions: &'function mut Arena, - pub locals: &'function mut Arena, - pub arguments: &'function mut Vec, - pub arg_use: Vec, - +pub struct Context { + pub expressions: Arena, + pub local_variables: Arena, + pub arguments: Vec, //TODO: Find less allocation heavy representation - pub scopes: Vec>, - pub lookup_global_var_exps: FastHashMap, - pub samplers: FastHashMap, Handle>, + pub scopes: Vec>>, + pub lookup_global_var_exps: FastHashMap>, + pub lookup_constant_exps: FastHashMap>, pub typifier: Typifier, - - pub hir_exprs: Arena, - emitter: Emitter, } -impl<'function> Context<'function> { - pub fn new( - program: &mut Program, - body: &mut Block, - expressions: &'function mut Arena, - locals: &'function mut Arena, - arguments: &'function mut Vec, - ) -> Self { - let mut this = Context { - expressions, - locals, - arguments, - arg_use: vec![EntryArgUse::empty(); program.entry_args.len()], - - scopes: vec![FastHashMap::default()], - lookup_global_var_exps: FastHashMap::with_capacity_and_hasher( - program.global_variables.len(), - Default::default(), - ), - typifier: Typifier::new(), - samplers: FastHashMap::default(), - - hir_exprs: Arena::default(), - emitter: Emitter::default(), - }; - - this.emit_start(); - - for &(ref name, lookup) in program.global_variables.iter() { - this.emit_flush(body); - let GlobalLookup { - kind, - entry_arg, - mutable, - } = lookup; - let (expr, load) = match kind { - GlobalLookupKind::Variable(v) => { - let res = ( - this.expressions.append(Expression::GlobalVariable(v)), - program.module.global_variables[v].class != StorageClass::Handle, - ); - this.emit_start(); - - res - } - GlobalLookupKind::BlockSelect(handle, index) => { - let base = this.expressions.append(Expression::GlobalVariable(handle)); - this.emit_start(); - let expr = this - .expressions - .append(Expression::AccessIndex { base, index }); - - (expr, { - let ty = program.module.global_variables[handle].ty; - - match program.module.types[ty].inner { - TypeInner::Struct { ref members, .. } => { - if let TypeInner::Array { - size: crate::ArraySize::Dynamic, - .. - } = program.module.types[members[index as usize].ty].inner - { - false - } else { - true - } - } - _ => true, - } - }) - } - GlobalLookupKind::Constant(v) => { - let res = (this.expressions.append(Expression::Constant(v)), false); - this.emit_start(); - res - } - }; - - let var = VariableReference { - expr, - load, - mutable, - entry_arg, - }; - - this.lookup_global_var_exps.insert(name.into(), var); - } - - this - } - - pub fn emit_start(&mut self) { - self.emitter.start(self.expressions) - } - - pub fn emit_flush(&mut self, body: &mut Block) { - body.extend(self.emitter.finish(self.expressions)) - } - - pub fn add_expression(&mut self, expr: Expression, body: &mut Block) -> Handle { - if expr.needs_pre_emit() { - self.emit_flush(body); - let handle = self.expressions.append(expr); - self.emit_start(); - handle - } else { - self.expressions.append(expr) - } - } - - pub fn lookup_local_var(&self, name: &str) -> Option { +impl Context { + pub fn lookup_local_var(&self, name: &str) -> Option> { for scope in self.scopes.iter().rev() { if let Some(var) = scope.get(name) { - return Some(var.clone()); + return Some(*var); } } None } - pub fn lookup_global_var(&mut self, name: &str) -> Option { - self.lookup_global_var_exps.get(name).cloned() - } - #[cfg(feature = "glsl-validate")] - pub fn lookup_local_var_current_scope(&self, name: &str) -> Option { + pub fn lookup_local_var_current_scope(&self, name: &str) -> Option> { if let Some(current) = self.scopes.last() { current.get(name).cloned() } else { @@ -343,94 +227,25 @@ impl<'function> Context<'function> { } } + pub fn clear_scopes(&mut self) { + self.scopes.clear(); + self.scopes.push(FastHashMap::default()); + } + /// Add variable to current scope - pub fn add_local_var(&mut self, name: String, expr: Handle, mutable: bool) { + pub fn add_local_var(&mut self, name: String, handle: Handle) { if let Some(current) = self.scopes.last_mut() { - (*current).insert( - name, - VariableReference { - expr, - load: true, - mutable, - entry_arg: None, - }, - ); + let expr = self + .expressions + .append(Expression::Load { pointer: handle }); + (*current).insert(name, expr); } } /// Add function argument to current scope - pub fn add_function_arg( - &mut self, - program: &mut Program, - sig: &mut FunctionSignature, - body: &mut Block, - name: Option, - ty: Handle, - qualifier: ParameterQualifier, - ) { - let index = self.arguments.len(); - let mut arg = FunctionArgument { - name: name.clone(), - ty, - binding: None, - }; - sig.parameters.push(ty); - - if qualifier.is_lhs() { - arg.ty = program.module.types.fetch_or_append(Type { - name: None, - inner: TypeInner::Pointer { - base: arg.ty, - class: StorageClass::Function, - }, - }) - } - - self.arguments.push(arg); - - if let Some(name) = name { - let expr = self.add_expression(Expression::FunctionArgument(index as u32), body); - let mutable = qualifier != ParameterQualifier::Const; - let load = qualifier.is_lhs(); - - if mutable && !load { - let handle = self.locals.append(LocalVariable { - name: Some(name.clone()), - ty, - init: None, - }); - let local_expr = self.add_expression(Expression::LocalVariable(handle), body); - - self.emit_flush(body); - self.emit_start(); - - body.push(Statement::Store { - pointer: local_expr, - value: expr, - }); - - if let Some(current) = self.scopes.last_mut() { - (*current).insert( - name, - VariableReference { - expr: local_expr, - load: true, - mutable, - entry_arg: None, - }, - ); - } - } else if let Some(current) = self.scopes.last_mut() { - (*current).insert( - name, - VariableReference { - expr, - load, - mutable, - entry_arg: None, - }, - ); - } + pub fn add_function_arg(&mut self, name: String, expr: Handle) { + if let Some(current) = self.scopes.last_mut() { + (*current).insert(name, expr); } } @@ -442,530 +257,54 @@ impl<'function> Context<'function> { pub fn remove_current_scope(&mut self) { self.scopes.pop(); } +} - pub fn lower_expect( - &mut self, - program: &mut Program, - expr: Handle, - lhs: bool, - body: &mut Block, - ) -> Result<(Handle, SourceMetadata), ErrorKind> { - let (maybe_expr, meta) = self.lower(program, expr, lhs, body)?; +#[derive(Debug)] +pub struct ExpressionRule { + pub expression: Handle, + pub statements: Vec, + pub sampler: Option>, +} - let expr = match maybe_expr { - Some(e) => e, - None => { - return Err(ErrorKind::SemanticError( - meta, - "Expression returns void".into(), - )) - } - }; - - Ok((expr, meta)) - } - - pub fn lower( - &mut self, - program: &mut Program, - expr: Handle, - lhs: bool, - body: &mut Block, - ) -> Result<(Option>, SourceMetadata), ErrorKind> { - let HirExpr { kind, meta } = self.hir_exprs[expr].clone(); - - let handle = match kind { - HirExprKind::Access { base, index } => { - let base = self.lower_expect(program, base, true, body)?.0; - let (index, index_meta) = self.lower_expect(program, index, false, body)?; - - let pointer = program - .solve_constant(self, index, index_meta) - .ok() - .and_then(|constant| { - Some(self.add_expression( - Expression::AccessIndex { - base, - index: match program.module.constants[constant].inner { - crate::ConstantInner::Scalar { - value: ScalarValue::Uint(i), - .. - } => u32::try_from(i).ok()?, - crate::ConstantInner::Scalar { - value: ScalarValue::Sint(i), - .. - } => u32::try_from(i).ok()?, - _ => return None, - }, - }, - body, - )) - }) - .unwrap_or_else(|| { - self.add_expression(Expression::Access { base, index }, body) - }); - - if let TypeInner::Pointer { .. } = *program.resolve_type(self, pointer, meta)? { - if !lhs { - return Ok(( - Some(self.add_expression(Expression::Load { pointer }, body)), - meta, - )); - } - } - - pointer - } - HirExprKind::Select { base, field } => { - let base = self.lower_expect(program, base, lhs, body)?.0; - - program.field_selection(self, lhs, body, base, &field, meta)? - } - HirExprKind::Constant(constant) if !lhs => { - self.add_expression(Expression::Constant(constant), body) - } - HirExprKind::Binary { left, op, right } if !lhs => { - let (mut left, left_meta) = self.lower_expect(program, left, false, body)?; - let (mut right, right_meta) = self.lower_expect(program, right, false, body)?; - - self.binary_implicit_conversion( - program, &mut left, left_meta, &mut right, right_meta, - )?; - - if let BinaryOperator::Equal | BinaryOperator::NotEqual = op { - let equals = op == BinaryOperator::Equal; - let (left_is_vector, left_dims) = - match *program.resolve_type(self, left, left_meta)? { - crate::TypeInner::Vector { .. } => (true, 1), - crate::TypeInner::Matrix { .. } => (false, 2), - _ => (false, 0), - }; - - let (right_is_vector, right_dims) = - match *program.resolve_type(self, right, right_meta)? { - crate::TypeInner::Vector { .. } => (true, 1), - crate::TypeInner::Matrix { .. } => (false, 2), - _ => (false, 0), - }; - - let (op, fun) = match equals { - true => (BinaryOperator::Equal, RelationalFunction::All), - false => (BinaryOperator::NotEqual, RelationalFunction::Any), - }; - - let argument = self - .expressions - .append(Expression::Binary { op, left, right }); - - if left_dims != right_dims { - return Err(ErrorKind::SemanticError(meta, "Cannot compare".into())); - } else if left_is_vector && right_is_vector { - self.add_expression(Expression::Relational { fun, argument }, body) - } else { - argument - } - } else { - self.add_expression(Expression::Binary { left, op, right }, body) - } - } - HirExprKind::Unary { op, expr } if !lhs => { - let expr = self.lower_expect(program, expr, false, body)?.0; - - self.add_expression(Expression::Unary { op, expr }, body) - } - HirExprKind::Variable(var) => { - if lhs { - if !var.mutable { - return Err(ErrorKind::SemanticError( - meta, - "Variable cannot be used in LHS position".into(), - )); - } - - if let Some(idx) = var.entry_arg { - self.arg_use[idx] |= EntryArgUse::WRITE - } - - var.expr - } else { - if let Some(idx) = var.entry_arg { - self.arg_use[idx] |= EntryArgUse::READ - } - - if var.load { - self.add_expression(Expression::Load { pointer: var.expr }, body) - } else { - var.expr - } - } - } - HirExprKind::Call(call) if !lhs => { - let maybe_expr = program.function_call(self, body, call.kind, &call.args, meta)?; - return Ok((maybe_expr, meta)); - } - HirExprKind::Conditional { - condition, - accept, - reject, - } if !lhs => { - let condition = self.lower_expect(program, condition, false, body)?.0; - let (mut accept, accept_meta) = self.lower_expect(program, accept, false, body)?; - let (mut reject, reject_meta) = self.lower_expect(program, reject, false, body)?; - - self.binary_implicit_conversion( - program, - &mut accept, - accept_meta, - &mut reject, - reject_meta, - )?; - - self.add_expression( - Expression::Select { - condition, - accept, - reject, - }, - body, - ) - } - HirExprKind::Assign { tgt, value } if !lhs => { - let (pointer, ptr_meta) = self.lower_expect(program, tgt, true, body)?; - let (mut value, value_meta) = self.lower_expect(program, value, false, body)?; - - let ptr_kind = match *program.resolve_type(self, pointer, ptr_meta)? { - TypeInner::Pointer { base, .. } => { - program.module.types[base].inner.scalar_kind() - } - ref ty => ty.scalar_kind(), - }; - - if let Some(kind) = ptr_kind { - self.implicit_conversion(program, &mut value, value_meta, kind)?; - } - - if let Expression::Swizzle { - size, - mut vector, - pattern, - } = self.expressions[pointer] - { - // Stores to swizzled values are not directly supported, - // lower them as series of per-component stores. - let size = match size { - VectorSize::Bi => 2, - VectorSize::Tri => 3, - VectorSize::Quad => 4, - }; - - if let Expression::Load { pointer } = self.expressions[vector] { - vector = pointer; - } - - #[allow(clippy::needless_range_loop)] - for index in 0..size { - let dst = self.add_expression( - Expression::AccessIndex { - base: vector, - index: pattern[index].index(), - }, - body, - ); - let src = self.add_expression( - Expression::AccessIndex { - base: value, - index: index as u32, - }, - body, - ); - - self.emit_flush(body); - self.emit_start(); - - body.push(Statement::Store { - pointer: dst, - value: src, - }); - } - } else { - self.emit_flush(body); - self.emit_start(); - - body.push(Statement::Store { pointer, value }); - } - - value - } - HirExprKind::IncDec { - increment, - postfix, - expr, - } => { - let op = match increment { - true => BinaryOperator::Add, - false => BinaryOperator::Subtract, - }; - - let pointer = self.lower_expect(program, expr, true, body)?.0; - let left = self.add_expression(Expression::Load { pointer }, body); - - let uint = if let Some(kind) = program.resolve_type(self, left, meta)?.scalar_kind() - { - match kind { - ScalarKind::Sint => false, - ScalarKind::Uint => true, - _ => { - return Err(ErrorKind::SemanticError( - meta, - "Increment/decrement operations must operate in integers".into(), - )) - } - } - } else { - return Err(ErrorKind::SemanticError( - meta, - "Increment/decrement operations must operate in integers".into(), - )); - }; - - let one = program.module.constants.append(Constant { - name: None, - specialization: None, - inner: crate::ConstantInner::Scalar { - width: 4, - value: match uint { - true => crate::ScalarValue::Uint(1), - false => crate::ScalarValue::Sint(1), - }, - }, - }); - let right = self.add_expression(Expression::Constant(one), body); - - let value = self.add_expression(Expression::Binary { op, left, right }, body); - - if postfix { - let local = self.locals.append(LocalVariable { - name: None, - ty: program.module.types.fetch_or_append(Type { - name: None, - inner: TypeInner::Scalar { - kind: match uint { - true => ScalarKind::Uint, - false => ScalarKind::Sint, - }, - width: 4, - }, - }), - init: None, - }); - - let expr = self.add_expression(Expression::LocalVariable(local), body); - let load = self.add_expression(Expression::Load { pointer: expr }, body); - - self.emit_flush(body); - self.emit_start(); - - body.push(Statement::Store { - pointer: expr, - value: left, - }); - - self.emit_flush(body); - self.emit_start(); - - body.push(Statement::Store { pointer, value }); - - load - } else { - self.emit_flush(body); - self.emit_start(); - - body.push(Statement::Store { pointer, value }); - - left - } - } - _ => { - return Err(ErrorKind::SemanticError( - meta, - format!("{:?} cannot be in the left hand side", self.hir_exprs[expr]).into(), - )) - } - }; - - Ok((Some(handle), meta)) - } - - pub fn expr_scalar_kind( - &mut self, - program: &mut Program, - expr: Handle, - meta: SourceMetadata, - ) -> Result, ErrorKind> { - Ok(program.resolve_type(self, expr, meta)?.scalar_kind()) - } - - pub fn expr_power( - &mut self, - program: &mut Program, - expr: Handle, - meta: SourceMetadata, - ) -> Result, ErrorKind> { - Ok(self - .expr_scalar_kind(program, expr, meta)? - .and_then(type_power)) - } - - pub fn get_expression(&self, expr: Handle) -> &Expression { - &self.expressions[expr] - } - - pub fn implicit_conversion( - &mut self, - program: &mut Program, - expr: &mut Handle, - meta: SourceMetadata, - kind: ScalarKind, - ) -> Result<(), ErrorKind> { - if let (Some(tgt_power), Some(expr_power)) = - (type_power(kind), self.expr_power(program, *expr, meta)?) - { - if tgt_power > expr_power { - *expr = self.expressions.append(Expression::As { - expr: *expr, - kind, - convert: None, - }) - } +impl ExpressionRule { + pub fn from_expression(expression: Handle) -> ExpressionRule { + ExpressionRule { + expression, + statements: vec![], + sampler: None, } - - Ok(()) } - - pub fn binary_implicit_conversion( - &mut self, - program: &mut Program, - left: &mut Handle, - left_meta: SourceMetadata, - right: &mut Handle, - right_meta: SourceMetadata, - ) -> Result<(), ErrorKind> { - let left_kind = self.expr_scalar_kind(program, *left, left_meta)?; - let right_kind = self.expr_scalar_kind(program, *right, right_meta)?; - - if let (Some((left_power, left_kind)), Some((right_power, right_kind))) = ( - left_kind.and_then(|kind| Some((type_power(kind)?, kind))), - right_kind.and_then(|kind| Some((type_power(kind)?, kind))), - ) { - match left_power.cmp(&right_power) { - std::cmp::Ordering::Less => { - *left = self.expressions.append(Expression::As { - expr: *left, - kind: right_kind, - convert: None, - }) - } - std::cmp::Ordering::Equal => {} - std::cmp::Ordering::Greater => { - *right = self.expressions.append(Expression::As { - expr: *right, - kind: left_kind, - convert: None, - }) - } - } - } - - Ok(()) - } -} - -fn type_power(kind: ScalarKind) -> Option { - Some(match kind { - ScalarKind::Sint => 0, - ScalarKind::Uint => 1, - ScalarKind::Float => 2, - ScalarKind::Bool => return None, - }) -} - -#[derive(Debug, Clone)] -pub struct VariableReference { - pub expr: Handle, - pub load: bool, - pub mutable: bool, - pub entry_arg: Option, -} - -#[derive(Debug, Clone)] -pub struct HirExpr { - pub kind: HirExprKind, - pub meta: SourceMetadata, -} - -#[derive(Debug, Clone)] -pub enum HirExprKind { - Access { - base: Handle, - index: Handle, - }, - Select { - base: Handle, - field: String, - }, - Constant(Handle), - Binary { - left: Handle, - op: BinaryOperator, - right: Handle, - }, - Unary { - op: UnaryOperator, - expr: Handle, - }, - Variable(VariableReference), - Call(FunctionCall), - Conditional { - condition: Handle, - accept: Handle, - reject: Handle, - }, - Assign { - tgt: Handle, - value: Handle, - }, - IncDec { - increment: bool, - postfix: bool, - expr: Handle, - }, } #[derive(Debug)] pub enum TypeQualifier { StorageQualifier(StorageQualifier), - Interpolation(Interpolation), ResourceBinding(ResourceBinding), - Location(u32), - WorkGroupSize(usize, u32), + Binding(Binding), + Interpolation(Interpolation), Sampling(Sampling), - Layout(StructLayout), - EarlyFragmentTests, } -#[derive(Debug, Clone)] +#[derive(Debug)] +pub struct VarDeclaration { + pub type_qualifiers: Vec, + pub ids_initializers: Vec<(Option, Option)>, + pub ty: Handle, +} + +#[derive(Debug)] pub enum FunctionCallKind { TypeConstructor(Handle), Function(String), } -#[derive(Debug, Clone)] +#[derive(Debug)] pub struct FunctionCall { pub kind: FunctionCallKind, - pub args: Vec>, + pub args: Vec, } -#[derive(Debug, Clone, Copy, PartialEq)] +#[derive(Debug, Clone, Copy)] pub enum StorageQualifier { StorageClass(StorageClass), Input, @@ -973,26 +312,9 @@ pub enum StorageQualifier { Const, } -#[derive(Debug, Clone, Copy)] +#[derive(Debug, Clone)] pub enum StructLayout { - Std140, - Std430, -} - -#[derive(Debug, Clone, PartialEq, Copy)] -pub enum ParameterQualifier { - In, - Out, - InOut, - Const, -} - -impl ParameterQualifier { - /// Returns true if the argument should be passed as a lhs expression - pub fn is_lhs(&self) -> bool { - match *self { - ParameterQualifier::Out | ParameterQualifier::InOut => true, - _ => false, - } - } + Binding(Binding), + Resource(ResourceBinding), + PushConstant, } diff --git a/third_party/rust/naga/src/front/glsl/constants.rs b/third_party/rust/naga/src/front/glsl/constants.rs index 1143b90f3427..1df4a9576687 100644 --- a/third_party/rust/naga/src/front/glsl/constants.rs +++ b/third_party/rust/naga/src/front/glsl/constants.rs @@ -51,8 +51,6 @@ pub enum ConstantSolvingError { InvalidBinaryOpArgs, #[error("Splat/swizzle type is not registered")] DestinationTypeNotFound, - #[error("Not implemented: {0}")] - NotImplemented(String), } impl<'a> ConstantSolver<'a> { @@ -154,9 +152,7 @@ impl<'a> ConstantSolver<'a> { self.binary_op(op, left_constant, right_constant) } - Expression::Math { fun, .. } => { - Err(ConstantSolvingError::NotImplemented(format!("{:?}", fun))) - } + Expression::Math { .. } => todo!(), Expression::As { convert, expr, diff --git a/third_party/rust/naga/src/front/glsl/error.rs b/third_party/rust/naga/src/front/glsl/error.rs index 00443914251e..a0daaccf18d7 100644 --- a/third_party/rust/naga/src/front/glsl/error.rs +++ b/third_party/rust/naga/src/front/glsl/error.rs @@ -1,126 +1,88 @@ -use super::{ - constants::ConstantSolvingError, - token::{SourceMetadata, Token, TokenValue}, -}; -use std::borrow::Cow; -use thiserror::Error; +use super::parser::Token; +use super::token::TokenMetadata; +use std::{borrow::Cow, fmt, io}; -fn join_with_comma(list: &[ExpectedToken]) -> String { - let mut string = "".to_string(); - for (i, val) in list.iter().enumerate() { - string.push_str(&val.to_string()); - match i { - i if i == list.len() - 1 => {} - i if i == list.len() - 2 => string.push_str(" or "), - _ => string.push_str(", "), - } - } - string -} - -#[derive(Debug, PartialEq)] -pub enum ExpectedToken { - Token(TokenValue), - TypeName, - Identifier, - IntLiteral, - FloatLiteral, - BoolLiteral, - Eof, -} -impl From for ExpectedToken { - fn from(token: TokenValue) -> Self { - ExpectedToken::Token(token) - } -} -impl std::fmt::Display for ExpectedToken { - fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { - match *self { - ExpectedToken::Token(ref token) => write!(f, "{:?}", token), - ExpectedToken::TypeName => write!(f, "a type"), - ExpectedToken::Identifier => write!(f, "identifier"), - ExpectedToken::IntLiteral => write!(f, "integer literal"), - ExpectedToken::FloatLiteral => write!(f, "float literal"), - ExpectedToken::BoolLiteral => write!(f, "bool literal"), - ExpectedToken::Eof => write!(f, "end of file"), - } - } -} - -#[derive(Debug, Error)] -#[cfg_attr(test, derive(PartialEq))] +//TODO: use `thiserror` +#[derive(Debug)] pub enum ErrorKind { - #[error("Unexpected end of file")] EndOfFile, - #[error("Invalid profile: {1}")] - InvalidProfile(SourceMetadata, String), - #[error("Invalid version: {1}")] - InvalidVersion(SourceMetadata, u64), - #[error("Expected {}, found {0}", join_with_comma(.1))] - InvalidToken(Token, Vec), - #[error("Not implemented {0}")] + InvalidInput, + InvalidProfile(TokenMetadata, String), + InvalidToken(Token), + InvalidVersion(TokenMetadata, i64), + IoError(io::Error), + ParserFail, + ParserStackOverflow, NotImplemented(&'static str), - #[error("Unknown variable: {1}")] - UnknownVariable(SourceMetadata, String), - #[error("Unknown type: {1}")] - UnknownType(SourceMetadata, String), - #[error("Unknown field: {1}")] - UnknownField(SourceMetadata, String), - #[error("Unknown layout qualifier: {1}")] - UnknownLayoutQualifier(SourceMetadata, String), + UnknownVariable(TokenMetadata, String), + UnknownField(TokenMetadata, String), #[cfg(feature = "glsl-validate")] - #[error("Variable already declared: {1}")] - VariableAlreadyDeclared(SourceMetadata, String), - #[error("{1}")] - SemanticError(SourceMetadata, Cow<'static, str>), + VariableAlreadyDeclared(String), + ExpectedConstant, + SemanticError(Cow<'static, str>), + PreprocessorError(String), + WrongNumberArgs(String, usize, usize), } -impl ErrorKind { - /// Returns the TokenMetadata if available - pub fn metadata(&self) -> Option { +impl fmt::Display for ErrorKind { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { match *self { - ErrorKind::UnknownVariable(metadata, _) - | ErrorKind::InvalidProfile(metadata, _) - | ErrorKind::InvalidVersion(metadata, _) - | ErrorKind::UnknownLayoutQualifier(metadata, _) - | ErrorKind::SemanticError(metadata, _) - | ErrorKind::UnknownField(metadata, _) => Some(metadata), + ErrorKind::EndOfFile => write!(f, "Unexpected end of file"), + ErrorKind::InvalidInput => write!(f, "InvalidInput"), + ErrorKind::InvalidProfile(ref meta, ref val) => { + write!(f, "Invalid profile {} at {:?}", val, meta) + } + ErrorKind::InvalidToken(ref token) => write!(f, "Invalid Token {:?}", token), + ErrorKind::InvalidVersion(ref meta, ref val) => { + write!(f, "Invalid version {} at {:?}", val, meta) + } + ErrorKind::IoError(ref error) => write!(f, "IO Error {}", error), + ErrorKind::ParserFail => write!(f, "Parser failed"), + ErrorKind::ParserStackOverflow => write!(f, "Parser stack overflow"), + ErrorKind::NotImplemented(ref msg) => write!(f, "Not implemented: {}", msg), + ErrorKind::UnknownVariable(ref meta, ref val) => { + write!(f, "Unknown variable {} at {:?}", val, meta) + } + ErrorKind::UnknownField(ref meta, ref val) => { + write!(f, "Unknown field {} at {:?}", val, meta) + } #[cfg(feature = "glsl-validate")] - ErrorKind::VariableAlreadyDeclared(metadata, _) => Some(metadata), - ErrorKind::InvalidToken(ref token, _) => Some(token.meta), - _ => None, + ErrorKind::VariableAlreadyDeclared(ref val) => { + write!(f, "Variable {} already declared in current scope", val) + } + ErrorKind::ExpectedConstant => write!(f, "Expected constant"), + ErrorKind::SemanticError(ref msg) => write!(f, "Semantic error: {}", msg), + ErrorKind::PreprocessorError(ref val) => write!(f, "Preprocessor error: {}", val), + ErrorKind::WrongNumberArgs(ref fun, expected, actual) => { + write!(f, "{} requires {} args, got {}", fun, expected, actual) + } } } - - pub(crate) fn wrong_function_args( - name: String, - expected: usize, - got: usize, - meta: SourceMetadata, - ) -> Self { - let msg = format!( - "Function \"{}\" expects {} arguments, got {}", - name, expected, got - ); - - ErrorKind::SemanticError(meta, msg.into()) - } } -impl From<(SourceMetadata, ConstantSolvingError)> for ErrorKind { - fn from((meta, err): (SourceMetadata, ConstantSolvingError)) -> Self { - ErrorKind::SemanticError(meta, err.to_string().into()) - } -} - -#[derive(Debug, Error)] -#[error("{kind}")] +#[derive(Debug)] pub struct ParseError { pub kind: ErrorKind, } +impl fmt::Display for ParseError { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + write!(f, "{:?}", self) + } +} + +impl From for ParseError { + fn from(error: io::Error) -> Self { + ParseError { + kind: ErrorKind::IoError(error), + } + } +} + impl From for ParseError { fn from(kind: ErrorKind) -> Self { ParseError { kind } } } + +impl std::error::Error for ParseError {} diff --git a/third_party/rust/naga/src/front/glsl/functions.rs b/third_party/rust/naga/src/front/glsl/functions.rs index 9095f4c4b104..98033dc9b5b5 100644 --- a/third_party/rust/naga/src/front/glsl/functions.rs +++ b/third_party/rust/naga/src/front/glsl/functions.rs @@ -1,218 +1,166 @@ +use super::super::Typifier; use crate::{ - proc::ensure_block_returns, Arena, BinaryOperator, Block, EntryPoint, Expression, Function, - FunctionArgument, FunctionResult, Handle, LocalVariable, MathFunction, RelationalFunction, - SampleLevel, ScalarKind, Statement, StructMember, SwizzleComponent, Type, TypeInner, + proc::ensure_block_returns, BinaryOperator, Block, EntryPoint, Expression, Function, + MathFunction, RelationalFunction, SampleLevel, ScalarKind, TypeInner, }; -use super::{ast::*, error::ErrorKind, SourceMetadata}; +use super::{ast::*, error::ErrorKind}; impl Program<'_> { - pub fn function_call( - &mut self, - ctx: &mut Context, - body: &mut Block, - fc: FunctionCallKind, - raw_args: &[Handle], - meta: SourceMetadata, - ) -> Result>, ErrorKind> { - let args: Vec<_> = raw_args - .iter() - .map(|e| ctx.lower_expect(self, *e, false, body)) - .collect::>()?; - - match fc { + pub fn function_call(&mut self, fc: FunctionCall) -> Result { + match fc.kind { FunctionCallKind::TypeConstructor(ty) => { - let h = if args.len() == 1 { - let is_vec = match *self.resolve_type(ctx, args[0].0, args[0].1)? { + let h = if fc.args.len() == 1 { + let is_vec = match *self.resolve_type(fc.args[0].expression)? { TypeInner::Vector { .. } => true, _ => false, }; match self.module.types[ty].inner { - TypeInner::Vector { size, kind, .. } if !is_vec => { - let (mut value, meta) = args[0]; - ctx.implicit_conversion(self, &mut value, meta, kind)?; - - ctx.add_expression(Expression::Splat { size, value }, body) + TypeInner::Vector { size, .. } if !is_vec => { + self.context.expressions.append(Expression::Splat { + size, + value: fc.args[0].expression, + }) } - TypeInner::Scalar { kind, width } => ctx.add_expression( - Expression::As { + TypeInner::Scalar { kind, width } + | TypeInner::Vector { kind, width, .. } => { + self.context.expressions.append(Expression::As { kind, - expr: args[0].0, + expr: fc.args[0].expression, convert: Some(width), - }, - body, - ), - TypeInner::Vector { size, kind, width } => { - let expr = ctx.add_expression( - Expression::Swizzle { - size, - vector: args[0].0, - pattern: SwizzleComponent::XYZW, - }, - body, - ); - - ctx.add_expression( - Expression::As { - kind, - expr, - convert: Some(width), - }, - body, - ) + }) } - TypeInner::Matrix { columns, rows, .. } => { - // TODO: casts - // `Expression::As` doesn't support matrix width - // casts so we need to do some extra work for casts + TypeInner::Matrix { + columns, + rows, + width, + } => { + let value = self.context.expressions.append(Expression::As { + kind: ScalarKind::Float, + expr: fc.args[0].expression, + convert: Some(width), + }); - let (mut value, meta) = args[0]; - ctx.implicit_conversion(self, &mut value, meta, ScalarKind::Float)?; - let column = match *self.resolve_type(ctx, args[0].0, args[0].1)? { - TypeInner::Scalar { .. } => ctx - .add_expression(Expression::Splat { size: rows, value }, body), - TypeInner::Matrix { .. } => { - let mut components = Vec::new(); - - for n in 0..columns as u32 { - let vector = ctx.add_expression( - Expression::AccessIndex { - base: value, - index: n, - }, - body, - ); - - let c = ctx.add_expression( - Expression::Swizzle { - size: rows, - vector, - pattern: SwizzleComponent::XYZW, - }, - body, - ); - - components.push(c) - } - - let h = ctx.add_expression( - Expression::Compose { ty, components }, - body, - ); - - return Ok(Some(h)); - } - _ => value, + let column = if is_vec { + value + } else { + self.context + .expressions + .append(Expression::Splat { size: rows, value }) }; let columns = std::iter::repeat(column).take(columns as usize).collect(); - ctx.add_expression( - Expression::Compose { - ty, - components: columns, - }, - body, - ) - } - TypeInner::Struct { .. } => ctx.add_expression( - Expression::Compose { + self.context.expressions.append(Expression::Compose { ty, - components: args.into_iter().map(|arg| arg.0).collect(), - }, - body, - ), - _ => return Err(ErrorKind::SemanticError(meta, "Bad cast".into())), + components: columns, + }) + } + _ => return Err(ErrorKind::SemanticError("Bad cast".into())), } } else { - let mut components = Vec::with_capacity(args.len()); - - for (mut arg, meta) in args.iter().copied() { - if let Some(kind) = self.module.types[ty].inner.scalar_kind() { - ctx.implicit_conversion(self, &mut arg, meta, kind)?; - } - components.push(arg) - } - - ctx.add_expression(Expression::Compose { ty, components }, body) + self.context.expressions.append(Expression::Compose { + ty, + components: fc.args.iter().map(|a| a.expression).collect(), + }) }; - Ok(Some(h)) + Ok(ExpressionRule { + expression: h, + statements: fc + .args + .into_iter() + .map(|a| a.statements) + .flatten() + .collect(), + sampler: None, + }) } FunctionCallKind::Function(name) => { match name.as_str() { "sampler2D" => { - if args.len() != 2 { - return Err(ErrorKind::wrong_function_args(name, 2, args.len(), meta)); + if fc.args.len() != 2 { + return Err(ErrorKind::WrongNumberArgs(name, 2, fc.args.len())); } - ctx.samplers.insert(args[0].0, args[1].0); - Ok(Some(args[0].0)) + Ok(ExpressionRule { + expression: fc.args[0].expression, + sampler: Some(fc.args[1].expression), + statements: fc + .args + .into_iter() + .map(|a| a.statements) + .flatten() + .collect(), + }) } "texture" => { - if args.len() != 2 { - return Err(ErrorKind::wrong_function_args(name, 2, args.len(), meta)); + if fc.args.len() != 2 { + return Err(ErrorKind::WrongNumberArgs(name, 2, fc.args.len())); } - if let Some(sampler) = ctx.samplers.get(&args[0].0).copied() { - Ok(Some(ctx.add_expression( - Expression::ImageSample { - image: args[0].0, - sampler, - coordinate: args[1].0, - array_index: None, //TODO - offset: None, //TODO - level: SampleLevel::Auto, - depth_ref: None, - }, - body, - ))) + if let Some(sampler) = fc.args[0].sampler { + Ok(ExpressionRule { + expression: self.context.expressions.append( + Expression::ImageSample { + image: fc.args[0].expression, + sampler, + coordinate: fc.args[1].expression, + array_index: None, //TODO + offset: None, //TODO + level: SampleLevel::Auto, + depth_ref: None, + }, + ), + sampler: None, + statements: fc + .args + .into_iter() + .map(|a| a.statements) + .flatten() + .collect(), + }) } else { - Err(ErrorKind::SemanticError(meta, "Bad call to texture".into())) + Err(ErrorKind::SemanticError("Bad call to texture".into())) } } "textureLod" => { - if args.len() != 3 { - return Err(ErrorKind::wrong_function_args(name, 3, args.len(), meta)); + if fc.args.len() != 3 { + return Err(ErrorKind::WrongNumberArgs(name, 3, fc.args.len())); } - let exact = ctx.add_expression( - Expression::As { - kind: crate::ScalarKind::Float, - expr: args[2].0, - convert: Some(4), - }, - body, - ); - if let Some(sampler) = ctx.samplers.get(&args[0].0).copied() { - Ok(Some(ctx.add_expression( - Expression::ImageSample { - image: args[0].0, - sampler, - coordinate: args[1].0, - array_index: None, //TODO - offset: None, //TODO - level: SampleLevel::Exact(exact), - depth_ref: None, - }, - body, - ))) + if let Some(sampler) = fc.args[0].sampler { + Ok(ExpressionRule { + expression: self.context.expressions.append( + Expression::ImageSample { + image: fc.args[0].expression, + sampler, + coordinate: fc.args[1].expression, + array_index: None, //TODO + offset: None, //TODO + level: SampleLevel::Exact(fc.args[2].expression), + depth_ref: None, + }, + ), + sampler: None, + statements: fc + .args + .into_iter() + .map(|a| a.statements) + .flatten() + .collect(), + }) } else { - Err(ErrorKind::SemanticError( - meta, - "Bad call to textureLod".into(), - )) + Err(ErrorKind::SemanticError("Bad call to textureLod".into())) } } "ceil" | "round" | "floor" | "fract" | "trunc" | "sin" | "abs" | "sqrt" | "inversesqrt" | "exp" | "exp2" | "sign" | "transpose" | "inverse" - | "normalize" | "sinh" | "cos" | "cosh" | "tan" | "tanh" | "acos" | "asin" - | "log" | "log2" | "length" | "determinant" | "bitCount" - | "bitfieldReverse" => { - if args.len() != 1 { - return Err(ErrorKind::wrong_function_args(name, 1, args.len(), meta)); + | "normalize" => { + if fc.args.len() != 1 { + return Err(ErrorKind::WrongNumberArgs(name, 1, fc.args.len())); } - Ok(Some(ctx.add_expression( - Expression::Math { + Ok(ExpressionRule { + expression: self.context.expressions.append(Expression::Math { fun: match name.as_str() { "ceil" => MathFunction::Ceil, "round" => MathFunction::Round, @@ -229,132 +177,62 @@ impl Program<'_> { "transpose" => MathFunction::Transpose, "inverse" => MathFunction::Inverse, "normalize" => MathFunction::Normalize, - "sinh" => MathFunction::Sinh, - "cos" => MathFunction::Cos, - "cosh" => MathFunction::Cosh, - "tan" => MathFunction::Tan, - "tanh" => MathFunction::Tanh, - "acos" => MathFunction::Acos, - "asin" => MathFunction::Asin, - "log" => MathFunction::Log, - "log2" => MathFunction::Log2, - "length" => MathFunction::Length, - "determinant" => MathFunction::Determinant, - "bitCount" => MathFunction::CountOneBits, - "bitfieldReverse" => MathFunction::ReverseBits, _ => unreachable!(), }, - arg: args[0].0, + arg: fc.args[0].expression, arg1: None, arg2: None, - }, - body, - ))) + }), + sampler: None, + statements: fc.args.into_iter().flat_map(|a| a.statements).collect(), + }) } - "atan" => { - let expr = match args.len() { - 1 => Expression::Math { - fun: MathFunction::Atan, - arg: args[0].0, - arg1: None, - arg2: None, - }, - 2 => Expression::Math { - fun: MathFunction::Atan2, - arg: args[0].0, - arg1: Some(args[1].0), - arg2: None, - }, - _ => { - return Err(ErrorKind::wrong_function_args( - name, - 2, - args.len(), - meta, - )) - } - }; - Ok(Some(ctx.add_expression(expr, body))) - } - "mod" => { - if args.len() != 2 { - return Err(ErrorKind::wrong_function_args(name, 2, args.len(), meta)); + "pow" | "dot" | "max" => { + if fc.args.len() != 2 { + return Err(ErrorKind::WrongNumberArgs(name, 2, fc.args.len())); } - - let (mut left, left_meta) = args[0]; - let (mut right, right_meta) = args[1]; - - ctx.binary_implicit_conversion( - self, &mut left, left_meta, &mut right, right_meta, - )?; - - Ok(Some(ctx.add_expression( - Expression::Binary { - op: BinaryOperator::Modulo, - left, - right, - }, - body, - ))) - } - "pow" | "dot" | "max" | "min" | "reflect" | "cross" | "outerProduct" - | "distance" | "step" | "modf" | "frexp" | "ldexp" => { - if args.len() != 2 { - return Err(ErrorKind::wrong_function_args(name, 2, args.len(), meta)); - } - Ok(Some(ctx.add_expression( - Expression::Math { + Ok(ExpressionRule { + expression: self.context.expressions.append(Expression::Math { fun: match name.as_str() { "pow" => MathFunction::Pow, "dot" => MathFunction::Dot, "max" => MathFunction::Max, - "min" => MathFunction::Min, - "reflect" => MathFunction::Reflect, - "cross" => MathFunction::Cross, - "outerProduct" => MathFunction::Outer, - "distance" => MathFunction::Distance, - "step" => MathFunction::Step, - "modf" => MathFunction::Modf, - "frexp" => MathFunction::Frexp, - "ldexp" => MathFunction::Ldexp, _ => unreachable!(), }, - arg: args[0].0, - arg1: Some(args[1].0), + arg: fc.args[0].expression, + arg1: Some(fc.args[1].expression), arg2: None, - }, - body, - ))) + }), + sampler: None, + statements: fc.args.into_iter().flat_map(|a| a.statements).collect(), + }) } - "mix" | "clamp" | "faceforward" | "refract" | "fma" | "smoothstep" => { - if args.len() != 3 { - return Err(ErrorKind::wrong_function_args(name, 3, args.len(), meta)); + "mix" | "clamp" => { + if fc.args.len() != 3 { + return Err(ErrorKind::WrongNumberArgs(name, 3, fc.args.len())); } - Ok(Some(ctx.add_expression( - Expression::Math { + Ok(ExpressionRule { + expression: self.context.expressions.append(Expression::Math { fun: match name.as_str() { "mix" => MathFunction::Mix, "clamp" => MathFunction::Clamp, - "faceforward" => MathFunction::FaceForward, - "refract" => MathFunction::Refract, - "fma" => MathFunction::Fma, - "smoothstep" => MathFunction::SmoothStep, _ => unreachable!(), }, - arg: args[0].0, - arg1: Some(args[1].0), - arg2: Some(args[2].0), - }, - body, - ))) + arg: fc.args[0].expression, + arg1: Some(fc.args[1].expression), + arg2: Some(fc.args[2].expression), + }), + sampler: None, + statements: fc.args.into_iter().flat_map(|a| a.statements).collect(), + }) } "lessThan" | "greaterThan" | "lessThanEqual" | "greaterThanEqual" | "equal" | "notEqual" => { - if args.len() != 2 { - return Err(ErrorKind::wrong_function_args(name, 2, args.len(), meta)); + if fc.args.len() != 2 { + return Err(ErrorKind::WrongNumberArgs(name, 2, fc.args.len())); } - Ok(Some(ctx.add_expression( - Expression::Binary { + Ok(ExpressionRule { + expression: self.context.expressions.append(Expression::Binary { op: match name.as_str() { "lessThan" => BinaryOperator::Less, "greaterThan" => BinaryOperator::Greater, @@ -364,116 +242,38 @@ impl Program<'_> { "notEqual" => BinaryOperator::NotEqual, _ => unreachable!(), }, - left: args[0].0, - right: args[1].0, - }, - body, - ))) + left: fc.args[0].expression, + right: fc.args[1].expression, + }), + sampler: None, + statements: fc.args.into_iter().flat_map(|a| a.statements).collect(), + }) } - "isinf" | "isnan" | "all" | "any" => { - let fun = match name.as_str() { - "isinf" => RelationalFunction::IsInf, - "isnan" => RelationalFunction::IsNan, - "all" => RelationalFunction::All, - "any" => RelationalFunction::Any, - _ => unreachable!(), - }; - - Ok(Some( - self.parse_relational_fun(ctx, body, name, &args, fun, meta)?, - )) - } - _ => { - let mut parameters = Vec::new(); - - for (e, meta) in args { - let handle = self.resolve_handle(ctx, e, meta)?; - - parameters.push(handle) - } - - let sig = FunctionSignature { name, parameters }; - - let fun = self - .lookup_function - .get(&sig) - .ok_or_else(|| { - ErrorKind::SemanticError( - meta, - // FIXME: Proper signature display - format!("Unknown function: {:?}", sig).into(), - ) - })? - .clone(); - - let mut arguments = Vec::with_capacity(raw_args.len()); - let mut proxy_writes = Vec::new(); - for (qualifier, expr) in fun.qualifiers.iter().zip(raw_args.iter()) { - let handle = ctx.lower_expect(self, *expr, qualifier.is_lhs(), body)?.0; - if qualifier.is_lhs() - && matches! { ctx.get_expression(handle), &Expression::Swizzle { .. } } - { - let meta = ctx.hir_exprs[*expr].meta; - let ty = self.resolve_handle(ctx, handle, meta)?; - let temp_var = ctx.locals.append(LocalVariable { - name: None, - ty, - init: None, - }); - let temp_expr = - ctx.add_expression(Expression::LocalVariable(temp_var), body); - - body.push(Statement::Store { - pointer: temp_expr, - value: handle, - }); - - arguments.push(temp_expr); - proxy_writes.push((*expr, temp_expr)); - } else { - arguments.push(handle); - } - } - - ctx.emit_flush(body); - - let result = if !fun.void { - Some(ctx.add_expression(Expression::Call(fun.handle), body)) - } else { - None - }; - - body.push(crate::Statement::Call { - function: fun.handle, + "isinf" => self.parse_relational_fun(name, fc.args, RelationalFunction::IsInf), + "isnan" => self.parse_relational_fun(name, fc.args, RelationalFunction::IsNan), + "all" => self.parse_relational_fun(name, fc.args, RelationalFunction::All), + "any" => self.parse_relational_fun(name, fc.args, RelationalFunction::Any), + func_name => { + let function = *self.lookup_function.get(func_name).ok_or_else(|| { + ErrorKind::SemanticError( + format!("Unknown function: {}", func_name).into(), + ) + })?; + let arguments: Vec<_> = fc.args.iter().map(|a| a.expression).collect(); + let mut statements: Vec<_> = + fc.args.into_iter().flat_map(|a| a.statements).collect(); + let expression = + self.context.expressions.append(Expression::Call(function)); + statements.push(crate::Statement::Call { + function, arguments, - result, + result: Some(expression), }); - - ctx.emit_start(); - for (tgt, pointer) in proxy_writes { - let temp_ref = ctx.hir_exprs.append(HirExpr { - kind: HirExprKind::Variable(VariableReference { - expr: pointer, - load: true, - mutable: true, - entry_arg: None, - }), - meta, - }); - let assign = ctx.hir_exprs.append(HirExpr { - kind: HirExprKind::Assign { - tgt, - value: temp_ref, - }, - meta, - }); - - let _ = ctx.lower_expect(self, assign, false, body)?; - } - ctx.emit_flush(body); - ctx.emit_start(); - - Ok(result) + Ok(ExpressionRule { + expression, + sampler: None, + statements, + }) } } } @@ -482,279 +282,97 @@ impl Program<'_> { pub fn parse_relational_fun( &mut self, - ctx: &mut Context, - body: &mut Block, name: String, - args: &[(Handle, SourceMetadata)], + args: Vec, fun: RelationalFunction, - meta: SourceMetadata, - ) -> Result, ErrorKind> { + ) -> Result { if args.len() != 1 { - return Err(ErrorKind::wrong_function_args(name, 1, args.len(), meta)); + return Err(ErrorKind::WrongNumberArgs(name, 1, args.len())); } - - Ok(ctx.add_expression( - Expression::Relational { + Ok(ExpressionRule { + expression: self.context.expressions.append(Expression::Relational { fun, - argument: args[0].0, - }, - body, - )) - } - - pub fn add_function( - &mut self, - mut function: Function, - sig: FunctionSignature, - qualifiers: Vec, - meta: SourceMetadata, - ) -> Result, ErrorKind> { - ensure_block_returns(&mut function.body); - let stage = self.entry_points.get(&sig.name); - - Ok(if let Some(&stage) = stage { - let handle = self.module.functions.append(function); - self.entries.push((sig.name, stage, handle)); - self.function_arg_use.push(Vec::new()); - handle - } else { - let void = function.result.is_none(); - - if let Some(decl) = self.lookup_function.get_mut(&sig) { - if decl.defined { - return Err(ErrorKind::SemanticError( - meta, - "Function already defined".into(), - )); - } - - decl.defined = true; - *self.module.functions.get_mut(decl.handle) = function; - decl.handle - } else { - self.function_arg_use.push(Vec::new()); - let handle = self.module.functions.append(function); - self.lookup_function.insert( - sig, - FunctionDeclaration { - qualifiers, - handle, - defined: true, - void, - }, - ); - handle - } + argument: args[0].expression, + }), + sampler: None, + statements: args.into_iter().flat_map(|a| a.statements).collect(), }) } - pub fn add_prototype( - &mut self, - function: Function, - sig: FunctionSignature, - qualifiers: Vec, - meta: SourceMetadata, - ) -> Result<(), ErrorKind> { - let void = function.result.is_none(); - - self.function_arg_use.push(Vec::new()); - let handle = self.module.functions.append(function); - - if self - .lookup_function - .insert( - sig, - FunctionDeclaration { - qualifiers, - handle, - defined: false, - void, - }, - ) - .is_some() - { - return Err(ErrorKind::SemanticError( - meta, - "Prototype already defined".into(), - )); - } - - Ok(()) - } - - fn check_call_global( - &self, - caller: Handle, - function_arg_use: &mut [Vec], - stmt: &Statement, - ) { - match *stmt { - Statement::Block(ref block) => { - for stmt in block { - self.check_call_global(caller, function_arg_use, stmt) - } - } - Statement::If { - ref accept, - ref reject, - .. - } => { - for stmt in accept.iter().chain(reject.iter()) { - self.check_call_global(caller, function_arg_use, stmt) - } - } - Statement::Switch { - ref cases, - ref default, - .. - } => { - for stmt in cases - .iter() - .flat_map(|c| c.body.iter()) - .chain(default.iter()) - { - self.check_call_global(caller, function_arg_use, stmt) - } - } - Statement::Loop { - ref body, - ref continuing, - } => { - for stmt in body.iter().chain(continuing.iter()) { - self.check_call_global(caller, function_arg_use, stmt) - } - } - Statement::Call { function, .. } => { - let callee_len = function_arg_use[function.index()].len(); - let caller_len = function_arg_use[caller.index()].len(); - function_arg_use[caller.index()].extend( - std::iter::repeat(EntryArgUse::empty()) - .take(callee_len.saturating_sub(caller_len)), - ); - - for i in 0..callee_len.min(caller_len) { - let callee_use = function_arg_use[function.index()][i]; - function_arg_use[caller.index()][i] |= callee_use - } - } - _ => {} - } - } - - pub fn add_entry_points(&mut self) { - let mut function_arg_use = Vec::new(); - std::mem::swap(&mut self.function_arg_use, &mut function_arg_use); - - for (handle, function) in self.module.functions.iter() { - for stmt in function.body.iter() { - self.check_call_global(handle, &mut function_arg_use, stmt) - } - } - - for (name, stage, function) in self.entries.iter().cloned() { - let mut arguments = Vec::new(); - let mut expressions = Arena::new(); - let mut body = Vec::new(); - - for (i, arg) in self.entry_args.iter().enumerate() { - if function_arg_use[function.index()] - .get(i) - .map_or(true, |u| !u.contains(EntryArgUse::READ)) - || !arg.prologue.contains(stage.into()) - { - continue; - } - - let ty = self.module.global_variables[arg.handle].ty; - let idx = arguments.len() as u32; - - arguments.push(FunctionArgument { - name: arg.name.clone(), - ty, - binding: Some(arg.binding.clone()), - }); - - let pointer = expressions.append(Expression::GlobalVariable(arg.handle)); - let value = expressions.append(Expression::FunctionArgument(idx)); - - body.push(Statement::Store { pointer, value }); - } - - body.push(Statement::Call { - function, - arguments: Vec::new(), - result: None, - }); - - let mut span = 0; - let mut members = Vec::new(); - let mut components = Vec::new(); - - for (i, arg) in self.entry_args.iter().enumerate() { - if function_arg_use[function.index()] - .get(i) - .map_or(true, |u| !u.contains(EntryArgUse::WRITE)) - { - continue; - } - - let ty = self.module.global_variables[arg.handle].ty; - - members.push(StructMember { - name: arg.name.clone(), - ty, - binding: Some(arg.binding.clone()), - offset: span, - }); - - span += self.module.types[ty].inner.span(&self.module.constants); - - let pointer = expressions.append(Expression::GlobalVariable(arg.handle)); - let len = expressions.len(); - let load = expressions.append(Expression::Load { pointer }); - body.push(Statement::Emit(expressions.range_from(len))); - components.push(load) - } - - let (ty, value) = if !components.is_empty() { - let ty = self.module.types.append(Type { - name: None, - inner: TypeInner::Struct { - top_level: false, - members, - span, - }, - }); - - let len = expressions.len(); - let res = expressions.append(Expression::Compose { ty, components }); - body.push(Statement::Emit(expressions.range_from(len))); - - (Some(ty), Some(res)) + pub fn add_function_prelude(&mut self) { + for (var_handle, var) in self.module.global_variables.iter() { + if let Some(name) = var.name.as_ref() { + let expr = self + .context + .expressions + .append(Expression::GlobalVariable(var_handle)); + self.context + .lookup_global_var_exps + .insert(name.clone(), expr); } else { - (None, None) - }; + let ty = &self.module.types[var.ty]; + // anonymous structs + if let TypeInner::Struct { ref members, .. } = ty.inner { + let base = self + .context + .expressions + .append(Expression::GlobalVariable(var_handle)); + for (idx, member) in members.iter().enumerate() { + if let Some(name) = member.name.as_ref() { + let exp = self.context.expressions.append(Expression::AccessIndex { + base, + index: idx as u32, + }); + self.context + .lookup_global_var_exps + .insert(name.clone(), exp); + } + } + } + } + } - body.push(Statement::Return { value }); + for (handle, constant) in self.module.constants.iter() { + if let Some(name) = constant.name.as_ref() { + let expr = self + .context + .expressions + .append(Expression::Constant(handle)); + self.context.lookup_constant_exps.insert(name.clone(), expr); + } + } + } + pub fn function_definition(&mut self, mut f: Function, mut block: Block) -> Function { + std::mem::swap(&mut f.expressions, &mut self.context.expressions); + std::mem::swap(&mut f.local_variables, &mut self.context.local_variables); + std::mem::swap(&mut f.arguments, &mut self.context.arguments); + self.context.clear_scopes(); + self.context.lookup_global_var_exps.clear(); + self.context.typifier = Typifier::new(); + ensure_block_returns(&mut block); + f.body = block; + f + } + + pub fn declare_function(&mut self, f: Function) -> Result<(), ErrorKind> { + let name = f + .name + .clone() + .ok_or_else(|| ErrorKind::SemanticError("Unnamed function".into()))?; + let stage = self.entry_points.get(&name); + if let Some(&stage) = stage { self.module.entry_points.push(EntryPoint { name, stage, - early_depth_test: Some(crate::EarlyDepthTest { conservative: None }) - .filter(|_| self.early_fragment_tests && stage == crate::ShaderStage::Fragment), - workgroup_size: if let crate::ShaderStage::Compute = stage { - self.workgroup_size - } else { - [0; 3] - }, - function: Function { - arguments, - expressions, - body, - result: ty.map(|ty| FunctionResult { ty, binding: None }), - ..Default::default() - }, + early_depth_test: None, + workgroup_size: [0; 3], //TODO + function: f, }); + } else { + let handle = self.module.functions.append(f); + self.lookup_function.insert(name, handle); } + Ok(()) } } diff --git a/third_party/rust/naga/src/front/glsl/lex.rs b/third_party/rust/naga/src/front/glsl/lex.rs index fb203dd6cbe0..08874dfeea9a 100644 --- a/third_party/rust/naga/src/front/glsl/lex.rs +++ b/third_party/rust/naga/src/front/glsl/lex.rs @@ -1,11 +1,8 @@ -use super::{ - token::{SourceMetadata, Token, TokenValue}, - types::parse_type, -}; +use super::{parser::Token, token::TokenMetadata, types::parse_type}; use crate::FastHashMap; use pp_rs::{ pp::Preprocessor, - token::{Punct, Token as PPToken, TokenValue as PPTokenValue}, + token::{Punct, Token as PPToken, TokenValue}, }; use std::collections::VecDeque; @@ -30,152 +27,151 @@ impl<'a> Lexer<'a> { impl<'a> Iterator for Lexer<'a> { type Item = Token; fn next(&mut self) -> Option { - let mut meta = SourceMetadata::default(); + let mut meta = TokenMetadata { + line: 0, + chars: 0..0, + }; let pp_token = match self.tokens.pop_front() { Some(t) => t, None => match self.pp.next()? { Ok(t) => t, Err((err, loc)) => { - meta.start = loc.start as usize; - meta.end = loc.end as usize; - return Some(Token { - value: TokenValue::Unknown(err), - meta, - }); + meta.line = loc.line as usize; + meta.chars.start = loc.pos as usize; + //TODO: proper location end + meta.chars.end = loc.pos as usize + 1; + return Some(Token::Unknown((meta, err))); } }, }; - meta.start = pp_token.location.start as usize; - meta.end = pp_token.location.end as usize; - let value = match pp_token.value { - PPTokenValue::Extension(extension) => { + meta.line = pp_token.location.line as usize; + meta.chars.start = pp_token.location.pos as usize; + //TODO: proper location end + meta.chars.end = pp_token.location.pos as usize + 1; + Some(match pp_token.value { + TokenValue::Extension(extension) => { for t in extension.tokens { self.tokens.push_back(t); } - TokenValue::Extension + Token::Extension((meta, ())) } - PPTokenValue::Float(float) => TokenValue::FloatConstant(float), - PPTokenValue::Ident(ident) => { + TokenValue::Float(float) => Token::FloatConstant((meta, float.value)), + TokenValue::Ident(ident) => { match ident.as_str() { - "layout" => TokenValue::Layout, - "in" => TokenValue::In, - "out" => TokenValue::Out, - "uniform" => TokenValue::Uniform, - "buffer" => TokenValue::Buffer, - "flat" => TokenValue::Interpolation(crate::Interpolation::Flat), - "noperspective" => TokenValue::Interpolation(crate::Interpolation::Linear), - "smooth" => TokenValue::Interpolation(crate::Interpolation::Perspective), - "centroid" => TokenValue::Sampling(crate::Sampling::Centroid), - "sample" => TokenValue::Sampling(crate::Sampling::Sample), - "const" => TokenValue::Const, - "inout" => TokenValue::InOut, + "layout" => Token::Layout(meta), + "in" => Token::In(meta), + "out" => Token::Out(meta), + "uniform" => Token::Uniform(meta), + "flat" => Token::Interpolation((meta, crate::Interpolation::Flat)), + "noperspective" => Token::Interpolation((meta, crate::Interpolation::Linear)), + "smooth" => Token::Interpolation((meta, crate::Interpolation::Perspective)), + "centroid" => Token::Sampling((meta, crate::Sampling::Centroid)), + "sample" => Token::Sampling((meta, crate::Sampling::Sample)), // values - "true" => TokenValue::BoolConstant(true), - "false" => TokenValue::BoolConstant(false), + "true" => Token::BoolConstant((meta, true)), + "false" => Token::BoolConstant((meta, false)), // jump statements - "continue" => TokenValue::Continue, - "break" => TokenValue::Break, - "return" => TokenValue::Return, - "discard" => TokenValue::Discard, + "continue" => Token::Continue(meta), + "break" => Token::Break(meta), + "return" => Token::Return(meta), + "discard" => Token::Discard(meta), // selection statements - "if" => TokenValue::If, - "else" => TokenValue::Else, - "switch" => TokenValue::Switch, - "case" => TokenValue::Case, - "default" => TokenValue::Default, + "if" => Token::If(meta), + "else" => Token::Else(meta), + "switch" => Token::Switch(meta), + "case" => Token::Case(meta), + "default" => Token::Default(meta), // iteration statements - "while" => TokenValue::While, - "do" => TokenValue::Do, - "for" => TokenValue::For, + "while" => Token::While(meta), + "do" => Token::Do(meta), + "for" => Token::For(meta), // types - "void" => TokenValue::Void, - "struct" => TokenValue::Struct, + "void" => Token::Void(meta), + "const" => Token::Const(meta), + word => match parse_type(word) { - Some(t) => TokenValue::TypeName(t), - None => TokenValue::Identifier(String::from(word)), + Some(t) => Token::TypeName((meta, t)), + None => Token::Identifier((meta, String::from(word))), }, } } - PPTokenValue::Integer(integer) => TokenValue::IntConstant(integer), - PPTokenValue::Punct(punct) => match punct { + //TODO: unsigned etc + TokenValue::Integer(integer) => Token::IntConstant((meta, integer.value as i64)), + TokenValue::Punct(punct) => match punct { // Compound assignments - Punct::AddAssign => TokenValue::AddAssign, - Punct::SubAssign => TokenValue::SubAssign, - Punct::MulAssign => TokenValue::MulAssign, - Punct::DivAssign => TokenValue::DivAssign, - Punct::ModAssign => TokenValue::ModAssign, - Punct::LeftShiftAssign => TokenValue::LeftShiftAssign, - Punct::RightShiftAssign => TokenValue::RightShiftAssign, - Punct::AndAssign => TokenValue::AndAssign, - Punct::XorAssign => TokenValue::XorAssign, - Punct::OrAssign => TokenValue::OrAssign, + Punct::AddAssign => Token::AddAssign(meta), + Punct::SubAssign => Token::SubAssign(meta), + Punct::MulAssign => Token::MulAssign(meta), + Punct::DivAssign => Token::DivAssign(meta), + Punct::ModAssign => Token::ModAssign(meta), + Punct::LeftShiftAssign => Token::LeftAssign(meta), + Punct::RightShiftAssign => Token::RightAssign(meta), + Punct::AndAssign => Token::AndAssign(meta), + Punct::XorAssign => Token::XorAssign(meta), + Punct::OrAssign => Token::OrAssign(meta), // Two character punctuation - Punct::Increment => TokenValue::Increment, - Punct::Decrement => TokenValue::Decrement, - Punct::LogicalAnd => TokenValue::LogicalAnd, - Punct::LogicalOr => TokenValue::LogicalOr, - Punct::LogicalXor => TokenValue::LogicalXor, - Punct::LessEqual => TokenValue::LessEqual, - Punct::GreaterEqual => TokenValue::GreaterEqual, - Punct::EqualEqual => TokenValue::Equal, - Punct::NotEqual => TokenValue::NotEqual, - Punct::LeftShift => TokenValue::LeftShift, - Punct::RightShift => TokenValue::RightShift, + Punct::Increment => Token::IncOp(meta), + Punct::Decrement => Token::DecOp(meta), + Punct::LogicalAnd => Token::AndOp(meta), + Punct::LogicalOr => Token::OrOp(meta), + Punct::LogicalXor => Token::XorOp(meta), + Punct::LessEqual => Token::LeOp(meta), + Punct::GreaterEqual => Token::GeOp(meta), + Punct::EqualEqual => Token::EqOp(meta), + Punct::NotEqual => Token::NeOp(meta), + Punct::LeftShift => Token::LeftOp(meta), + Punct::RightShift => Token::RightOp(meta), // Parenthesis or similar - Punct::LeftBrace => TokenValue::LeftBrace, - Punct::RightBrace => TokenValue::RightBrace, - Punct::LeftParen => TokenValue::LeftParen, - Punct::RightParen => TokenValue::RightParen, - Punct::LeftBracket => TokenValue::LeftBracket, - Punct::RightBracket => TokenValue::RightBracket, + Punct::LeftBrace => Token::LeftBrace(meta), + Punct::RightBrace => Token::RightBrace(meta), + Punct::LeftParen => Token::LeftParen(meta), + Punct::RightParen => Token::RightParen(meta), + Punct::LeftBracket => Token::LeftBracket(meta), + Punct::RightBracket => Token::RightBracket(meta), // Other one character punctuation - Punct::LeftAngle => TokenValue::LeftAngle, - Punct::RightAngle => TokenValue::RightAngle, - Punct::Semicolon => TokenValue::Semicolon, - Punct::Comma => TokenValue::Comma, - Punct::Colon => TokenValue::Colon, - Punct::Dot => TokenValue::Dot, - Punct::Equal => TokenValue::Assign, - Punct::Bang => TokenValue::Bang, - Punct::Minus => TokenValue::Dash, - Punct::Tilde => TokenValue::Tilde, - Punct::Plus => TokenValue::Plus, - Punct::Star => TokenValue::Star, - Punct::Slash => TokenValue::Slash, - Punct::Percent => TokenValue::Percent, - Punct::Pipe => TokenValue::VerticalBar, - Punct::Caret => TokenValue::Caret, - Punct::Ampersand => TokenValue::Ampersand, - Punct::Question => TokenValue::Question, + Punct::LeftAngle => Token::LeftAngle(meta), + Punct::RightAngle => Token::RightAngle(meta), + Punct::Semicolon => Token::Semicolon(meta), + Punct::Comma => Token::Comma(meta), + Punct::Colon => Token::Colon(meta), + Punct::Dot => Token::Dot(meta), + Punct::Equal => Token::Equal(meta), + Punct::Bang => Token::Bang(meta), + Punct::Minus => Token::Dash(meta), + Punct::Tilde => Token::Tilde(meta), + Punct::Plus => Token::Plus(meta), + Punct::Star => Token::Star(meta), + Punct::Slash => Token::Slash(meta), + Punct::Percent => Token::Percent(meta), + Punct::Pipe => Token::VerticalBar(meta), + Punct::Caret => Token::Caret(meta), + Punct::Ampersand => Token::Ampersand(meta), + Punct::Question => Token::Question(meta), }, - PPTokenValue::Pragma(pragma) => { + TokenValue::Pragma(pragma) => { for t in pragma.tokens { self.tokens.push_back(t); } - TokenValue::Pragma + Token::Pragma((meta, ())) } - PPTokenValue::Version(version) => { + TokenValue::Version(version) => { for t in version.tokens { self.tokens.push_back(t); } - TokenValue::Version + Token::Version(meta) } - }; - - Some(Token { value, meta }) + }) } } #[cfg(test)] mod tests { - use pp_rs::token::Integer; - use super::{ - super::token::{SourceMetadata, Token, TokenValue}, + super::{parser::Token::*, token::TokenMetadata}, Lexer, }; @@ -187,63 +183,65 @@ mod tests { let mut lex = Lexer::new("#version 450\nvoid main () {}", &defines); assert_eq!( lex.next().unwrap(), - Token { - value: TokenValue::Version, - meta: SourceMetadata { start: 1, end: 8 } - } + Version(TokenMetadata { + line: 1, + chars: 1..2 //TODO + }) ); assert_eq!( lex.next().unwrap(), - Token { - value: TokenValue::IntConstant(Integer { - signed: true, - value: 450, - width: 32 - }), - meta: SourceMetadata { start: 9, end: 12 }, - } + IntConstant(( + TokenMetadata { + line: 1, + chars: 9..10 //TODO + }, + 450 + )) ); assert_eq!( lex.next().unwrap(), - Token { - value: TokenValue::Void, - meta: SourceMetadata { start: 13, end: 17 } - } + Void(TokenMetadata { + line: 2, + chars: 0..1 //TODO + }) ); assert_eq!( lex.next().unwrap(), - Token { - value: TokenValue::Identifier("main".into()), - meta: SourceMetadata { start: 18, end: 22 } - } + Identifier(( + TokenMetadata { + line: 2, + chars: 5..6 //TODO + }, + "main".into() + )) ); assert_eq!( lex.next().unwrap(), - Token { - value: TokenValue::LeftParen, - meta: SourceMetadata { start: 23, end: 24 } - } + LeftParen(TokenMetadata { + line: 2, + chars: 10..11 //TODO + }) ); assert_eq!( lex.next().unwrap(), - Token { - value: TokenValue::RightParen, - meta: SourceMetadata { start: 24, end: 25 } - } + RightParen(TokenMetadata { + line: 2, + chars: 11..12 //TODO + }) ); assert_eq!( lex.next().unwrap(), - Token { - value: TokenValue::LeftBrace, - meta: SourceMetadata { start: 26, end: 27 } - } + LeftBrace(TokenMetadata { + line: 2, + chars: 13..14 //TODO + }) ); assert_eq!( lex.next().unwrap(), - Token { - value: TokenValue::RightBrace, - meta: SourceMetadata { start: 27, end: 28 } - } + RightBrace(TokenMetadata { + line: 2, + chars: 14..15 //TODO + }) ); assert_eq!(lex.next(), None); } diff --git a/third_party/rust/naga/src/front/glsl/mod.rs b/third_party/rust/naga/src/front/glsl/mod.rs index 41e6967db14a..36b26b907879 100644 --- a/third_party/rust/naga/src/front/glsl/mod.rs +++ b/third_party/rust/naga/src/front/glsl/mod.rs @@ -1,5 +1,6 @@ pub use error::ErrorKind; -pub use token::{SourceMetadata, Token}; +pub use parser::Token; +pub use token::TokenMetadata; use crate::{FastHashMap, Module, ShaderStage}; @@ -28,8 +29,11 @@ pub fn parse_str(source: &str, options: &Options) -> Result let mut program = Program::new(&options.entry_points); let lex = lex::Lexer::new(source, &options.defines); - let mut parser = parser::Parser::new(&mut program, lex); - parser.parse()?; + let mut parser = parser::Parser::new(&mut program); + for token in lex { + parser.parse(token)?; + } + parser.end_of_input()?; Ok(program.module) } diff --git a/third_party/rust/naga/src/front/glsl/parser.rs b/third_party/rust/naga/src/front/glsl/parser.rs index 9cc881cb13ac..3f26863abce4 100644 --- a/third_party/rust/naga/src/front/glsl/parser.rs +++ b/third_party/rust/naga/src/front/glsl/parser.rs @@ -1,1849 +1,1243 @@ -use super::{ - ast::{ - Context, FunctionCall, FunctionCallKind, FunctionSignature, GlobalLookup, GlobalLookupKind, - HirExpr, HirExprKind, ParameterQualifier, Profile, StorageQualifier, StructLayout, - TypeQualifier, - }, - error::ErrorKind, - lex::Lexer, - token::{SourceMetadata, Token, TokenValue}, - variables::{GlobalOrConstant, VarDeclaration}, - Program, -}; -use crate::{ - arena::Handle, front::glsl::error::ExpectedToken, Arena, ArraySize, BinaryOperator, Block, - Constant, ConstantInner, Expression, Function, FunctionResult, ResourceBinding, ScalarValue, - Statement, StorageClass, StructMember, SwitchCase, Type, TypeInner, UnaryOperator, -}; -use core::convert::TryFrom; -use std::{iter::Peekable, mem}; - -type Result = std::result::Result; - -pub struct Parser<'source, 'program, 'options> { - program: &'program mut Program<'options>, - lexer: Peekable>, -} - -impl<'source, 'program, 'options> Parser<'source, 'program, 'options> { - pub fn new(program: &'program mut Program<'options>, lexer: Lexer<'source>) -> Self { - Parser { - program, - lexer: lexer.peekable(), +#![allow( + unused_braces, + clippy::panic, + clippy::needless_lifetimes, + clippy::upper_case_acronyms +)] +use pomelo::pomelo; +pomelo! { + //%verbose; + %include { + use super::super::{error::ErrorKind, token::*, ast::*}; + use crate::{ + BOOL_WIDTH, + Arena, ArraySize, BinaryOperator, Binding, Block, Constant, + ConstantInner, Expression, + Function, FunctionArgument, FunctionResult, + GlobalVariable, Handle, Interpolation, + LocalVariable, ResourceBinding, Sampling, ScalarValue, ScalarKind, + Statement, StorageAccess, StorageClass, StructMember, + SwitchCase, Type, TypeInner, UnaryOperator, + }; + use pp_rs::token::PreprocessorError; + } + %token #[derive(Debug)] #[cfg_attr(test, derive(PartialEq))] pub enum Token {}; + %parser pub struct Parser<'a, 'b> {}; + %extra_argument &'a mut Program<'b>; + %extra_token TokenMetadata; + %error ErrorKind; + %syntax_error { + match token { + Some(token) => Err(ErrorKind::InvalidToken(token)), + None => Err(ErrorKind::EndOfFile), } } + %parse_fail { + ErrorKind::ParserFail + } + %stack_overflow { + ErrorKind::ParserStackOverflow + } - fn expect_ident(&mut self) -> Result<(String, SourceMetadata)> { - let token = self.bump()?; + %type Unknown PreprocessorError; + %type Pragma (); + %type Extension (); - match token.value { - TokenValue::Identifier(name) => Ok((name, token.meta)), - _ => Err(ErrorKind::InvalidToken( - token, - vec![ExpectedToken::Identifier], - )), + %type Identifier String; + // constants + %type IntConstant i64; + %type UintConstant u64; + %type FloatConstant f32; + %type BoolConstant bool; + %type DoubleConstant f64; + %type String String; + // function + %type function_prototype Function; + %type function_declarator Function; + %type function_header Function; + %type function_header_with_parameters (Function, Vec); + %type function_definition Function; + + // statements + %type compound_statement Block; + %type compound_statement_no_new_scope Block; + %type statement_list Block; + %type statement Statement; + %type simple_statement Statement; + %type expression_statement Statement; + %type declaration_statement Statement; + %type jump_statement Statement; + %type iteration_statement Statement; + %type selection_statement Statement; + %type switch_statement_list Vec<(Option, Block, bool)>; + %type switch_statement (Option, Block, bool); + %type for_init_statement Statement; + %type for_rest_statement (Option, Option); + %type condition_opt Option; + + // expressions + %type unary_expression ExpressionRule; + %type postfix_expression ExpressionRule; + %type primary_expression ExpressionRule; + %type variable_identifier ExpressionRule; + + %type function_call ExpressionRule; + %type function_call_or_method FunctionCall; + %type function_call_generic FunctionCall; + %type function_call_header_no_parameters FunctionCall; + %type function_call_header_with_parameters FunctionCall; + %type function_call_header FunctionCall; + %type function_identifier FunctionCallKind; + + %type parameter_declarator FunctionArgument; + %type parameter_declaration FunctionArgument; + %type parameter_type_specifier Handle; + + %type multiplicative_expression ExpressionRule; + %type additive_expression ExpressionRule; + %type shift_expression ExpressionRule; + %type relational_expression ExpressionRule; + %type equality_expression ExpressionRule; + %type and_expression ExpressionRule; + %type exclusive_or_expression ExpressionRule; + %type inclusive_or_expression ExpressionRule; + %type logical_and_expression ExpressionRule; + %type logical_xor_expression ExpressionRule; + %type logical_or_expression ExpressionRule; + %type conditional_expression ExpressionRule; + + %type assignment_expression ExpressionRule; + %type assignment_operator BinaryOperator; + %type expression ExpressionRule; + %type constant_expression Handle; + + %type initializer ExpressionRule; + + // declarations + %type declaration Option; + %type init_declarator_list VarDeclaration; + %type single_declaration VarDeclaration; + %type layout_qualifier StructLayout; + %type layout_qualifier_id_list Vec<(String, u32)>; + %type layout_qualifier_id (String, u32); + %type type_qualifier Vec; + %type single_type_qualifier TypeQualifier; + %type storage_qualifier StorageQualifier; + %type interpolation_qualifier Interpolation; + %type Interpolation Interpolation; + %type sampling_qualifier Sampling; + %type Sampling Sampling; + + // types + %type fully_specified_type (Vec, Option>); + %type type_specifier Option>; + %type type_specifier_nonarray Option; + %type struct_specifier Type; + %type struct_declaration_list Vec; + %type struct_declaration Vec; + %type struct_declarator_list Vec; + %type struct_declarator String; + + %type TypeName Type; + + // precedence + %right Else; + + root ::= version_pragma translation_unit; + version_pragma ::= Version IntConstant(V) Identifier?(P) { + match V.1 { + 440 => (), + 450 => (), + 460 => (), + _ => return Err(ErrorKind::InvalidVersion(V.0, V.1)) } - } - - fn expect(&mut self, value: TokenValue) -> Result { - let token = self.bump()?; - - if token.value != value { - Err(ErrorKind::InvalidToken(token, vec![value.into()])) - } else { - Ok(token) - } - } - - fn bump(&mut self) -> Result { - self.lexer.next().ok_or(ErrorKind::EndOfFile) - } - - /// Returns None on the end of the file rather than an error like other methods - fn bump_if(&mut self, value: TokenValue) -> Option { - if self.lexer.peek().filter(|t| t.value == value).is_some() { - self.bump().ok() - } else { - None - } - } - - fn expect_peek(&mut self) -> Result<&Token> { - self.lexer.peek().ok_or(ErrorKind::EndOfFile) - } - - pub fn parse(&mut self) -> Result<()> { - self.parse_version()?; - - while self.lexer.peek().is_some() { - self.parse_external_declaration()?; - } - - self.program.add_entry_points(); - - Ok(()) - } - - fn parse_version(&mut self) -> Result<()> { - self.expect(TokenValue::Version)?; - - let version = self.bump()?; - match version.value { - TokenValue::IntConstant(i) => match i.value { - 440 | 450 | 460 => self.program.version = i.value as u16, - _ => return Err(ErrorKind::InvalidVersion(version.meta, i.value)), - }, - _ => { - return Err(ErrorKind::InvalidToken( - version, - vec![ExpectedToken::IntLiteral], - )) - } - } - - let profile = self.lexer.peek(); - self.program.profile = match profile { - Some(&Token { - value: TokenValue::Identifier(_), - .. - }) => { - let (name, meta) = self.expect_ident()?; - - match name.as_str() { + extra.version = V.1 as u16; + extra.profile = match P { + Some((meta, profile)) => { + match profile.as_str() { "core" => Profile::Core, - _ => return Err(ErrorKind::InvalidProfile(meta, name)), + _ => return Err(ErrorKind::InvalidProfile(meta, profile)) } - } - _ => Profile::Core, - }; - - Ok(()) - } - - /// Parses an optional array_specifier returning `Ok(None)` if there is no - /// LeftBracket - fn parse_array_specifier(&mut self) -> Result> { - if self.bump_if(TokenValue::LeftBracket).is_some() { - if self.bump_if(TokenValue::RightBracket).is_some() { - return Ok(Some(ArraySize::Dynamic)); - } - - let (constant, _) = self.parse_constant_expression()?; - self.expect(TokenValue::RightBracket)?; - Ok(Some(ArraySize::Constant(constant))) - } else { - Ok(None) - } - } - - fn parse_type(&mut self) -> Result<(Option>, SourceMetadata)> { - let token = self.bump()?; - let handle = match token.value { - TokenValue::Void => None, - TokenValue::TypeName(ty) => Some(self.program.module.types.fetch_or_append(ty)), - TokenValue::Struct => { - let ty_name = self.expect_ident()?.0; - self.expect(TokenValue::LeftBrace)?; - let mut members = Vec::new(); - let span = self.parse_struct_declaration_list(&mut members)?; - self.expect(TokenValue::RightBrace)?; - - let ty = self.program.module.types.append(Type { - name: Some(ty_name.clone()), - inner: TypeInner::Struct { - top_level: false, - members, - span, - }, - }); - self.program.lookup_type.insert(ty_name, ty); - Some(ty) - } - TokenValue::Identifier(ident) => match self.program.lookup_type.get(&ident) { - Some(ty) => Some(*ty), - None => return Err(ErrorKind::UnknownType(token.meta, ident)), }, - _ => { - return Err(ErrorKind::InvalidToken( - token, - vec![ - TokenValue::Void.into(), - TokenValue::Struct.into(), - ExpectedToken::TypeName, - ], - )) - } - }; - - let size = self.parse_array_specifier()?; - Ok((handle.map(|ty| self.maybe_array(ty, size)), token.meta)) - } - - fn parse_type_non_void(&mut self) -> Result<(Handle, SourceMetadata)> { - let (maybe_ty, meta) = self.parse_type()?; - let ty = - maybe_ty.ok_or_else(|| ErrorKind::SemanticError(meta, "Type can't be void".into()))?; - - Ok((ty, meta)) - } - - fn maybe_array(&mut self, base: Handle, size: Option) -> Handle { - size.map(|size| { - self.program.module.types.fetch_or_append(Type { - name: None, - inner: TypeInner::Array { - base, - size, - stride: self.program.module.types[base] - .inner - .span(&self.program.module.constants), - }, - }) - }) - .unwrap_or(base) - } - - fn peek_type_qualifier(&mut self) -> bool { - self.lexer.peek().map_or(false, |t| match t.value { - TokenValue::Interpolation(_) - | TokenValue::Sampling(_) - | TokenValue::Const - | TokenValue::In - | TokenValue::Out - | TokenValue::Uniform - | TokenValue::Buffer - | TokenValue::Layout => true, - _ => false, - }) - } - - fn parse_type_qualifiers(&mut self) -> Result> { - let mut qualifiers = Vec::new(); - - while self.peek_type_qualifier() { - let token = self.bump()?; - - // Handle layout qualifiers outside the match since this can push multiple values - if token.value == TokenValue::Layout { - self.parse_layout_qualifier_id_list(&mut qualifiers)?; - continue; - } - - qualifiers.push(( - match token.value { - TokenValue::Interpolation(i) => TypeQualifier::Interpolation(i), - TokenValue::Const => TypeQualifier::StorageQualifier(StorageQualifier::Const), - TokenValue::In => TypeQualifier::StorageQualifier(StorageQualifier::Input), - TokenValue::Out => TypeQualifier::StorageQualifier(StorageQualifier::Output), - TokenValue::Uniform => TypeQualifier::StorageQualifier( - StorageQualifier::StorageClass(StorageClass::Uniform), - ), - TokenValue::Buffer => TypeQualifier::StorageQualifier( - StorageQualifier::StorageClass(StorageClass::Storage), - ), - TokenValue::Sampling(s) => TypeQualifier::Sampling(s), - - _ => unreachable!(), - }, - token.meta, - )) + None => Profile::Core, } + }; - Ok(qualifiers) - } - - fn parse_layout_qualifier_id_list( - &mut self, - qualifiers: &mut Vec<(TypeQualifier, SourceMetadata)>, - ) -> Result<()> { - // We need both of these to produce a ResourceBinding - let mut group = None; - let mut binding = None; - - self.expect(TokenValue::LeftParen)?; - loop { - self.parse_layout_qualifier_id(qualifiers, &mut group, &mut binding)?; - - if self.bump_if(TokenValue::Comma).is_some() { - continue; - } - - break; - } - self.expect(TokenValue::RightParen)?; - - match (group, binding) { - (Some((group, group_meta)), Some((binding, binding_meta))) => qualifiers.push(( - TypeQualifier::ResourceBinding(ResourceBinding { group, binding }), - group_meta.union(&binding_meta), - )), - // Produce an error if we have one of group or binding but not the other - (Some((_, meta)), None) => { - return Err(ErrorKind::SemanticError( - meta, - "set specified with no binding".into(), - )) - } - (None, Some((_, meta))) => { - return Err(ErrorKind::SemanticError( - meta, - "binding specified with no set".into(), - )) - } - (None, None) => (), - } - - Ok(()) - } - - fn parse_uint_constant(&mut self) -> Result<(u32, SourceMetadata)> { - let (value, meta) = self.parse_constant_expression()?; - - let int = match self.program.module.constants[value].inner { - ConstantInner::Scalar { - value: ScalarValue::Uint(int), - .. - } => u32::try_from(int) - .map_err(|_| ErrorKind::SemanticError(meta, "int constant overflows".into())), - ConstantInner::Scalar { - value: ScalarValue::Sint(int), - .. - } => u32::try_from(int) - .map_err(|_| ErrorKind::SemanticError(meta, "int constant overflows".into())), - _ => Err(ErrorKind::SemanticError( - meta, - "Expected a uint constant".into(), - )), - }?; - - Ok((int, meta)) - } - - fn parse_layout_qualifier_id( - &mut self, - qualifiers: &mut Vec<(TypeQualifier, SourceMetadata)>, - group: &mut Option<(u32, SourceMetadata)>, - binding: &mut Option<(u32, SourceMetadata)>, - ) -> Result<()> { - // layout_qualifier_id: - // IDENTIFIER - // IDENTIFIER EQUAL constant_expression - // SHARED - let mut token = self.bump()?; - match token.value { - TokenValue::Identifier(name) => { - if self.bump_if(TokenValue::Assign).is_some() { - let (value, end_meta) = self.parse_uint_constant()?; - token.meta = token.meta.union(&end_meta); - - qualifiers.push(( - match name.as_str() { - "location" => TypeQualifier::Location(value), - "set" => { - *group = Some((value, end_meta)); - return Ok(()); - } - "binding" => { - *binding = Some((value, end_meta)); - return Ok(()); - } - "local_size_x" => TypeQualifier::WorkGroupSize(0, value), - "local_size_y" => TypeQualifier::WorkGroupSize(1, value), - "local_size_z" => TypeQualifier::WorkGroupSize(2, value), - _ => return Err(ErrorKind::UnknownLayoutQualifier(token.meta, name)), - }, - token.meta, - )) - } else { - match name.as_str() { - "push_constant" => { - qualifiers.push(( - TypeQualifier::StorageQualifier(StorageQualifier::StorageClass( - StorageClass::PushConstant, - )), - token.meta, - )); - qualifiers - .push((TypeQualifier::Layout(StructLayout::Std430), token.meta)); - } - "std140" => qualifiers - .push((TypeQualifier::Layout(StructLayout::Std140), token.meta)), - "std430" => qualifiers - .push((TypeQualifier::Layout(StructLayout::Std430), token.meta)), - "early_fragment_tests" => { - qualifiers.push((TypeQualifier::EarlyFragmentTests, token.meta)) - } - _ => return Err(ErrorKind::UnknownLayoutQualifier(token.meta, name)), - } - }; - - Ok(()) - } - // TODO: handle Shared? - _ => Err(ErrorKind::InvalidToken( - token, - vec![ExpectedToken::Identifier], - )), - } - } - - fn parse_constant_expression(&mut self) -> Result<(Handle, SourceMetadata)> { - let mut expressions = Arena::new(); - let mut locals = Arena::new(); - let mut arguments = Vec::new(); - let mut block = Block::new(); - - let mut ctx = Context::new( - self.program, - &mut block, - &mut expressions, - &mut locals, - &mut arguments, - ); - - let expr = self.parse_conditional(&mut ctx, &mut block, None)?; - let (root, meta) = ctx.lower_expect(self.program, expr, false, &mut block)?; - - Ok((self.program.solve_constant(&ctx, root, meta)?, meta)) - } - - fn parse_external_declaration(&mut self) -> Result<()> { - // TODO: Create body and expressions arena to be used in all entry - // points to handle this case - // ```glsl - // // This is valid and the body of main will contain the assignment - // float b; - // float a = b = 1; - // - // void main() {} - // ``` - let (mut e, mut l, mut a) = (Arena::new(), Arena::new(), Vec::new()); - let mut body = Block::new(); - let mut ctx = Context::new(self.program, &mut body, &mut e, &mut l, &mut a); - - if !self.parse_declaration(&mut ctx, &mut body, true)? { - let token = self.bump()?; - match token.value { - TokenValue::Semicolon if self.program.version == 460 => Ok(()), - _ => { - let expected = match self.program.version { - 460 => vec![TokenValue::Semicolon.into(), ExpectedToken::Eof], - _ => vec![ExpectedToken::Eof], - }; - Err(ErrorKind::InvalidToken(token, expected)) - } - } - } else { - Ok(()) - } - } - - fn peek_type_name(&mut self) -> bool { - let program = &self.program; - self.lexer.peek().map_or(false, |t| match t.value { - TokenValue::TypeName(_) | TokenValue::Void => true, - TokenValue::Struct => true, - TokenValue::Identifier(ref ident) => program.lookup_type.contains_key(ident), - _ => false, - }) - } - - fn peek_parameter_qualifier(&mut self) -> bool { - self.lexer.peek().map_or(false, |t| match t.value { - TokenValue::In | TokenValue::Out | TokenValue::InOut | TokenValue::Const => true, - _ => false, - }) - } - - /// Returns the parsed `ParameterQualifier` or `ParameterQualifier::In` - fn parse_parameter_qualifier(&mut self) -> ParameterQualifier { - if self.peek_parameter_qualifier() { - match self.bump().unwrap().value { - TokenValue::In => ParameterQualifier::In, - TokenValue::Out => ParameterQualifier::Out, - TokenValue::InOut => ParameterQualifier::InOut, - TokenValue::Const => ParameterQualifier::Const, - _ => unreachable!(), - } - } else { - ParameterQualifier::In - } - } - - fn parse_initializer( - &mut self, - ty: Handle, - ctx: &mut Context, - body: &mut Block, - ) -> Result<(Handle, SourceMetadata)> { - // initializer: - // assignment_expression - // LEFT_BRACE initializer_list RIGHT_BRACE - // LEFT_BRACE initializer_list COMMA RIGHT_BRACE - // - // initializer_list: - // initializer - // initializer_list COMMA initializer - if let Some(Token { mut meta, .. }) = self.bump_if(TokenValue::LeftBrace) { - // initializer_list - let mut components = Vec::new(); - loop { - // TODO: Change type - components.push(self.parse_initializer(ty, ctx, body)?.0); - - let token = self.bump()?; - match token.value { - TokenValue::Comma => { - if let Some(Token { meta: end_meta, .. }) = - self.bump_if(TokenValue::RightBrace) - { - meta = meta.union(&end_meta); - break; - } - } - TokenValue::RightBrace => { - meta = meta.union(&token.meta); - break; - } - _ => { - return Err(ErrorKind::InvalidToken( - token, - vec![TokenValue::Comma.into(), TokenValue::RightBrace.into()], - )) - } - } - } - - Ok(( - ctx.add_expression(Expression::Compose { ty, components }, body), - meta, - )) - } else { - let expr = self.parse_assignment(ctx, body)?; - Ok(ctx.lower_expect(self.program, expr, false, body)?) - } - } - - // Note: caller preparsed the type and qualifiers - // Note: caller skips this if the fallthrough token is not expected to be consumed here so this - // produced Error::InvalidToken if it isn't consumed - fn parse_init_declarator_list( - &mut self, - ty: Handle, - mut fallthrough: Option, - ctx: &mut DeclarationContext, - ) -> Result<()> { - // init_declarator_list: - // single_declaration - // init_declarator_list COMMA IDENTIFIER - // init_declarator_list COMMA IDENTIFIER array_specifier - // init_declarator_list COMMA IDENTIFIER array_specifier EQUAL initializer - // init_declarator_list COMMA IDENTIFIER EQUAL initializer - // - // single_declaration: - // fully_specified_type - // fully_specified_type IDENTIFIER - // fully_specified_type IDENTIFIER array_specifier - // fully_specified_type IDENTIFIER array_specifier EQUAL initializer - // fully_specified_type IDENTIFIER EQUAL initializer - - // Consume any leading comma, e.g. this is valid: `float, a=1;` - if fallthrough - .as_ref() - .or_else(|| self.lexer.peek()) - .filter(|t| t.value == TokenValue::Comma) - .is_some() - { - fallthrough.take().or_else(|| self.lexer.next()); - } - - loop { - let token = fallthrough - .take() - .ok_or(ErrorKind::EndOfFile) - .or_else(|_| self.bump())?; - let name = match token.value { - TokenValue::Semicolon => break, - TokenValue::Identifier(name) => name, - _ => { - return Err(ErrorKind::InvalidToken( - token, - vec![ExpectedToken::Identifier, TokenValue::Semicolon.into()], - )) - } - }; - let mut meta = token.meta; - - // array_specifier - // array_specifier EQUAL initializer - // EQUAL initializer - - // parse an array specifier if it exists - // NOTE: unlike other parse methods this one doesn't expect an array specifier and - // returns Ok(None) rather than an error if there is not one - let array_specifier = self.parse_array_specifier()?; - let ty = self.maybe_array(ty, array_specifier); - - let init = self - .bump_if(TokenValue::Assign) - .map::, _>(|_| { - let (mut expr, init_meta) = self.parse_initializer(ty, ctx.ctx, ctx.body)?; - - if let Some(kind) = self.program.module.types[ty].inner.scalar_kind() { - ctx.ctx - .implicit_conversion(self.program, &mut expr, init_meta, kind)?; - } - - meta = meta.union(&init_meta); - - Ok((expr, init_meta)) - }) - .transpose()?; - - // TODO: Should we try to make constants here? - // This is mostly a hack because we don't yet support adding - // bodies to entry points for variable initialization - let maybe_constant = - init.and_then(|(root, meta)| self.program.solve_constant(ctx.ctx, root, meta).ok()); - - let pointer = ctx.add_var(self.program, ty, name, maybe_constant, meta)?; - - if let Some((value, _)) = init.filter(|_| maybe_constant.is_none()) { - ctx.flush_expressions(); - ctx.body.push(Statement::Store { pointer, value }); - } - - let token = self.bump()?; - match token.value { - TokenValue::Semicolon => break, - TokenValue::Comma => {} - _ => { - return Err(ErrorKind::InvalidToken( - token, - vec![TokenValue::Comma.into(), TokenValue::Semicolon.into()], - )) - } - } - } - - Ok(()) - } - - /// `external` whether or not we are in a global or local context - fn parse_declaration( - &mut self, - ctx: &mut Context, - body: &mut Block, - external: bool, - ) -> Result { - //declaration: - // function_prototype SEMICOLON - // - // init_declarator_list SEMICOLON - // PRECISION precision_qualifier type_specifier SEMICOLON - // - // type_qualifier IDENTIFIER LEFT_BRACE struct_declaration_list RIGHT_BRACE SEMICOLON - // type_qualifier IDENTIFIER LEFT_BRACE struct_declaration_list RIGHT_BRACE IDENTIFIER SEMICOLON - // type_qualifier IDENTIFIER LEFT_BRACE struct_declaration_list RIGHT_BRACE IDENTIFIER array_specifier SEMICOLON - // type_qualifier SEMICOLON type_qualifier IDENTIFIER SEMICOLON - // type_qualifier IDENTIFIER identifier_list SEMICOLON - - if self.peek_type_qualifier() || self.peek_type_name() { - let qualifiers = self.parse_type_qualifiers()?; - - if self.peek_type_name() { - // This branch handles variables and function prototypes and if - // external is true also function definitions - let (ty, mut meta) = self.parse_type()?; - - let token = self.bump()?; - let token_fallthrough = match token.value { - TokenValue::Identifier(name) => match self.expect_peek()?.value { - TokenValue::LeftParen => { - // This branch handles function definition and prototypes - self.bump()?; - - let result = ty.map(|ty| FunctionResult { ty, binding: None }); - let mut expressions = Arena::new(); - let mut local_variables = Arena::new(); - let mut arguments = Vec::new(); - let mut parameters = Vec::new(); - let mut body = Block::new(); - let mut sig = FunctionSignature { - name: name.clone(), - parameters: Vec::new(), - }; - - let mut context = Context::new( - self.program, - &mut body, - &mut expressions, - &mut local_variables, - &mut arguments, - ); - - self.parse_function_args( - &mut context, - &mut body, - &mut parameters, - &mut sig, - )?; - - let end_meta = self.expect(TokenValue::RightParen)?.meta; - meta = meta.union(&end_meta); - - let token = self.bump()?; - return match token.value { - TokenValue::Semicolon => { - // This branch handles function prototypes - self.program.add_prototype( - Function { - name: Some(name), - result, - arguments, - ..Default::default() - }, - sig, - parameters, - meta, - )?; - - Ok(true) - } - TokenValue::LeftBrace if external => { - // This branch handles function definitions - // as you can see by the guard this branch - // only happens if external is also true - - // parse the body - self.parse_compound_statement(&mut context, &mut body)?; - - let Context { arg_use, .. } = context; - let handle = self.program.add_function( - Function { - name: Some(name), - result, - expressions, - named_expressions: crate::FastHashMap::default(), - local_variables, - arguments, - body, - }, - sig, - parameters, - meta, - )?; - - self.program.function_arg_use[handle.index()] = arg_use; - - Ok(true) - } - _ if external => Err(ErrorKind::InvalidToken( - token, - vec![ - TokenValue::LeftBrace.into(), - TokenValue::Semicolon.into(), - ], - )), - _ => Err(ErrorKind::InvalidToken( - token, - vec![TokenValue::Semicolon.into()], - )), - }; - } - // Pass the token to the init_declator_list parser - _ => Token { - value: TokenValue::Identifier(name), - meta: token.meta, - }, - }, - // Pass the token to the init_declator_list parser - _ => token, - }; - - // If program execution has reached here then this will be a - // init_declarator_list - // token_falltrough will have a token that was already bumped - if let Some(ty) = ty { - let mut ctx = DeclarationContext { - qualifiers, - external, - ctx, - body, - }; - - self.parse_init_declarator_list(ty, Some(token_fallthrough), &mut ctx)?; - } else { - return Err(ErrorKind::SemanticError( - meta, - "Declaration cannot have void type".into(), - )); - } - - Ok(true) - } else { - // This branch handles struct definitions and modifiers like - // ```glsl - // layout(early_fragment_tests); - // ``` - let token = self.bump()?; - match token.value { - TokenValue::Identifier(ty_name) => { - if self.bump_if(TokenValue::LeftBrace).is_some() { - self.parse_block_declaration(&qualifiers, ty_name, token.meta) - } else { - //TODO: declaration - // type_qualifier IDENTIFIER SEMICOLON - // type_qualifier IDENTIFIER identifier_list SEMICOLON - todo!() - } - } - TokenValue::Semicolon => { - for &(ref qualifier, meta) in qualifiers.iter() { - match *qualifier { - TypeQualifier::WorkGroupSize(i, value) => { - self.program.workgroup_size[i] = value - } - TypeQualifier::EarlyFragmentTests => { - self.program.early_fragment_tests = true; - } - TypeQualifier::StorageQualifier(_) => { - // TODO: Maybe add some checks here - // This is needed because of cases like - // layout(early_fragment_tests) in; - } - _ => { - return Err(ErrorKind::SemanticError( - meta, - "Qualifier not supported as standalone".into(), - )); - } - } - } - - Ok(true) - } - _ => Err(ErrorKind::InvalidToken( - token, - vec![ExpectedToken::Identifier, TokenValue::Semicolon.into()], - )), - } - } - } else { - Ok(false) - } - } - - fn parse_block_declaration( - &mut self, - qualifiers: &[(TypeQualifier, SourceMetadata)], - ty_name: String, - mut meta: SourceMetadata, - ) -> Result { - let mut members = Vec::new(); - let span = self.parse_struct_declaration_list(&mut members)?; - self.expect(TokenValue::RightBrace)?; - - let mut ty = self.program.module.types.append(Type { - name: Some(ty_name), - inner: TypeInner::Struct { - top_level: true, - members: members.clone(), - span, + // expression + variable_identifier ::= Identifier(v) { + let var = extra.lookup_variable(&v.1)?; + match var { + Some(expression) => { + ExpressionRule::from_expression(expression) }, - }); - - let token = self.bump()?; - let name = match token.value { - TokenValue::Semicolon => None, - TokenValue::Identifier(name) => { - if let Some(size) = self.parse_array_specifier()? { - ty = self.program.module.types.fetch_or_append(Type { - name: None, - inner: TypeInner::Array { - base: ty, - size, - stride: self.program.module.types[ty] - .inner - .span(&self.program.module.constants), - }, - }); - } - - self.expect(TokenValue::Semicolon)?; - - Some(name) - } - _ => { - return Err(ErrorKind::InvalidToken( - token, - vec![ExpectedToken::Identifier, TokenValue::Semicolon.into()], - )) - } - }; - meta = meta.union(&token.meta); - - let global = self.program.add_global_var(VarDeclaration { - qualifiers, - ty, - name, - init: None, - meta, - })?; - - for (i, k) in members - .into_iter() - .enumerate() - .filter_map(|(i, m)| m.name.map(|s| (i as u32, s))) - { - self.program.global_variables.push(( - k, - GlobalLookup { - kind: match global { - GlobalOrConstant::Global(handle) => { - GlobalLookupKind::BlockSelect(handle, i) - } - GlobalOrConstant::Constant(handle) => GlobalLookupKind::Constant(handle), - }, - entry_arg: None, - mutable: true, - }, - )); - } - - Ok(true) - } - - // TODO: Accept layout arguments - fn parse_struct_declaration_list(&mut self, members: &mut Vec) -> Result { - let mut span = 0; - - loop { - // TODO: type_qualifier - - let ty = self.parse_type_non_void()?.0; - let name = self.expect_ident()?.0; - - let array_specifier = self.parse_array_specifier()?; - let ty = self.maybe_array(ty, array_specifier); - - self.expect(TokenValue::Semicolon)?; - - members.push(StructMember { - name: Some(name), - ty, - binding: None, - offset: span, - }); - - span += self.program.module.types[ty] - .inner - .span(&self.program.module.constants); - - if let TokenValue::RightBrace = self.expect_peek()?.value { - break; + None => { + return Err(ErrorKind::UnknownVariable(v.0, v.1)); } } - - Ok(span) } - fn parse_primary(&mut self, ctx: &mut Context, body: &mut Block) -> Result> { - let mut token = self.bump()?; - - let (width, value) = match token.value { - TokenValue::IntConstant(int) => ( - (int.width / 8) as u8, - if int.signed { - ScalarValue::Sint(int.value as i64) - } else { - ScalarValue::Uint(int.value) - }, - ), - TokenValue::FloatConstant(float) => ( - (float.width / 8) as u8, - ScalarValue::Float(float.value as f64), - ), - TokenValue::BoolConstant(value) => (1, ScalarValue::Bool(value)), - TokenValue::LeftParen => { - let expr = self.parse_expression(ctx, body)?; - let meta = self.expect(TokenValue::RightParen)?.meta; - - token.meta = token.meta.union(&meta); - - return Ok(expr); - } - _ => { - return Err(ErrorKind::InvalidToken( - token, - vec![ - TokenValue::LeftParen.into(), - ExpectedToken::IntLiteral, - ExpectedToken::FloatLiteral, - ExpectedToken::BoolLiteral, - ], - )) - } - }; - - let handle = self.program.module.constants.append(Constant { + primary_expression ::= variable_identifier; + primary_expression ::= IntConstant(i) { + let ch = extra.module.constants.fetch_or_append(Constant { name: None, specialization: None, - inner: ConstantInner::Scalar { width, value }, + inner: ConstantInner::Scalar { + width: 4, + value: ScalarValue::Sint(i.1), + }, + }); + ExpressionRule::from_expression( + extra.context.expressions.append(Expression::Constant(ch)) + ) + } + primary_expression ::= UintConstant(i) { + let ch = extra.module.constants.fetch_or_append(Constant { + name: None, + specialization: None, + inner: ConstantInner::Scalar { + width: 4, + value: ScalarValue::Uint(i.1), + }, + }); + ExpressionRule::from_expression( + extra.context.expressions.append(Expression::Constant(ch)) + ) + } + primary_expression ::= FloatConstant(f) { + let ch = extra.module.constants.fetch_or_append(Constant { + name: None, + specialization: None, + inner: ConstantInner::Scalar { + width: 4, + value: ScalarValue::Float(f.1 as f64), + }, + }); + ExpressionRule::from_expression( + extra.context.expressions.append(Expression::Constant(ch)) + ) + } + primary_expression ::= BoolConstant(b) { + let ch = extra.module.constants.fetch_or_append(Constant { + name: None, + specialization: None, + inner: ConstantInner::Scalar { + width: BOOL_WIDTH, + value: ScalarValue::Bool(b.1) + }, + }); + ExpressionRule::from_expression( + extra.context.expressions.append(Expression::Constant(ch)) + ) + } + primary_expression ::= DoubleConstant(f) { + let ch = extra.module.constants.fetch_or_append(Constant { + name: None, + specialization: None, + inner: ConstantInner::Scalar { + width: 8, + value: ScalarValue::Float(f.1), + }, + }); + ExpressionRule::from_expression( + extra.context.expressions.append(Expression::Constant(ch)) + ) + } + primary_expression ::= LeftParen expression(e) RightParen { + e + } + + postfix_expression ::= primary_expression; + postfix_expression ::= postfix_expression LeftBracket integer_expression RightBracket { + //TODO + return Err(ErrorKind::NotImplemented("[]")) + } + postfix_expression ::= function_call; + postfix_expression ::= postfix_expression(e) Dot Identifier(i) /* FieldSelection in spec */ { + //TODO: how will this work as l-value? + let expression = extra.field_selection(e.expression, &*i.1, i.0)?; + ExpressionRule { expression, statements: e.statements, sampler: None } + } + postfix_expression ::= postfix_expression(pe) IncOp { + //TODO + return Err(ErrorKind::NotImplemented("post++")) + } + postfix_expression ::= postfix_expression(pe) DecOp { + //TODO + return Err(ErrorKind::NotImplemented("post--")) + } + + integer_expression ::= expression; + + function_call ::= function_call_or_method(fc) { + extra.function_call(fc)? + } + function_call_or_method ::= function_call_generic; + function_call_generic ::= function_call_header_with_parameters(h) RightParen { + h + } + function_call_generic ::= function_call_header_no_parameters(h) RightParen { + h + } + function_call_header_no_parameters ::= function_call_header(h) Void { + h + } + function_call_header_no_parameters ::= function_call_header; + function_call_header_with_parameters ::= function_call_header(mut h) assignment_expression(ae) { + h.args.push(ae); + h + } + function_call_header_with_parameters ::= function_call_header_with_parameters(mut h) Comma assignment_expression(ae) { + h.args.push(ae); + h + } + function_call_header ::= function_identifier(i) LeftParen { + FunctionCall { + kind: i, + args: vec![], + } + } + + // Grammar Note: Constructors look like functions, but lexical analysis recognized most of them as + // keywords. They are now recognized through “type_specifier”. + function_identifier ::= type_specifier(t) { + if let Some(ty) = t { + FunctionCallKind::TypeConstructor(ty) + } else { + return Err(ErrorKind::NotImplemented("bad type ctor")) + } + } + + //TODO + // Methods (.length), subroutine array calls, and identifiers are recognized through postfix_expression. + // function_identifier ::= postfix_expression(e) { + // FunctionCallKind::Function(e.expression) + // } + + // Simplification of above + function_identifier ::= Identifier(i) { + FunctionCallKind::Function(i.1) + } + + + unary_expression ::= postfix_expression; + + unary_expression ::= IncOp unary_expression { + //TODO + return Err(ErrorKind::NotImplemented("++pre")) + } + unary_expression ::= DecOp unary_expression { + //TODO + return Err(ErrorKind::NotImplemented("--pre")) + } + unary_expression ::= Plus unary_expression(tgt) { + tgt + } + unary_expression ::= Dash unary_expression(tgt) { + extra.unary_expr(UnaryOperator::Negate, &tgt) + } + unary_expression ::= Bang unary_expression(tgt) { + if let TypeInner::Scalar { kind: ScalarKind::Bool, .. } = *extra.resolve_type(tgt.expression)? { + extra.unary_expr(UnaryOperator::Not, &tgt) + } else { + return Err(ErrorKind::SemanticError("Cannot apply '!' to non bool type".into())) + } + } + unary_expression ::= Tilde unary_expression(tgt) { + if extra.resolve_type(tgt.expression)?.scalar_kind() != Some(ScalarKind::Bool) { + extra.unary_expr(UnaryOperator::Not, &tgt) + } else { + return Err(ErrorKind::SemanticError("Cannot apply '~' to type".into())) + } + } + + multiplicative_expression ::= unary_expression; + multiplicative_expression ::= multiplicative_expression(left) Star unary_expression(right) { + extra.binary_expr(BinaryOperator::Multiply, &left, &right) + } + multiplicative_expression ::= multiplicative_expression(left) Slash unary_expression(right) { + extra.binary_expr(BinaryOperator::Divide, &left, &right) + } + multiplicative_expression ::= multiplicative_expression(left) Percent unary_expression(right) { + extra.binary_expr(BinaryOperator::Modulo, &left, &right) + } + additive_expression ::= multiplicative_expression; + additive_expression ::= additive_expression(left) Plus multiplicative_expression(right) { + extra.binary_expr(BinaryOperator::Add, &left, &right) + } + additive_expression ::= additive_expression(left) Dash multiplicative_expression(right) { + extra.binary_expr(BinaryOperator::Subtract, &left, &right) + } + shift_expression ::= additive_expression; + shift_expression ::= shift_expression(left) LeftOp additive_expression(right) { + extra.binary_expr(BinaryOperator::ShiftLeft, &left, &right) + } + shift_expression ::= shift_expression(left) RightOp additive_expression(right) { + extra.binary_expr(BinaryOperator::ShiftRight, &left, &right) + } + relational_expression ::= shift_expression; + relational_expression ::= relational_expression(left) LeftAngle shift_expression(right) { + extra.binary_expr(BinaryOperator::Less, &left, &right) + } + relational_expression ::= relational_expression(left) RightAngle shift_expression(right) { + extra.binary_expr(BinaryOperator::Greater, &left, &right) + } + relational_expression ::= relational_expression(left) LeOp shift_expression(right) { + extra.binary_expr(BinaryOperator::LessEqual, &left, &right) + } + relational_expression ::= relational_expression(left) GeOp shift_expression(right) { + extra.binary_expr(BinaryOperator::GreaterEqual, &left, &right) + } + equality_expression ::= relational_expression; + equality_expression ::= equality_expression(left) EqOp relational_expression(right) { + extra.equality_expr(true, &left, &right)? + } + equality_expression ::= equality_expression(left) NeOp relational_expression(right) { + extra.equality_expr(false, &left, &right)? + } + and_expression ::= equality_expression; + and_expression ::= and_expression(left) Ampersand equality_expression(right) { + extra.binary_expr(BinaryOperator::And, &left, &right) + } + exclusive_or_expression ::= and_expression; + exclusive_or_expression ::= exclusive_or_expression(left) Caret and_expression(right) { + extra.binary_expr(BinaryOperator::ExclusiveOr, &left, &right) + } + inclusive_or_expression ::= exclusive_or_expression; + inclusive_or_expression ::= inclusive_or_expression(left) VerticalBar exclusive_or_expression(right) { + extra.binary_expr(BinaryOperator::InclusiveOr, &left, &right) + } + logical_and_expression ::= inclusive_or_expression; + logical_and_expression ::= logical_and_expression(left) AndOp inclusive_or_expression(right) { + extra.binary_expr(BinaryOperator::LogicalAnd, &left, &right) + } + logical_xor_expression ::= logical_and_expression; + logical_xor_expression ::= logical_xor_expression(left) XorOp logical_and_expression(right) { + let exp1 = extra.binary_expr(BinaryOperator::LogicalOr, &left, &right); + let exp2 = { + let tmp = extra.binary_expr(BinaryOperator::LogicalAnd, &left, &right).expression; + ExpressionRule::from_expression(extra.context.expressions.append(Expression::Unary { op: UnaryOperator::Not, expr: tmp })) + }; + extra.binary_expr(BinaryOperator::LogicalAnd, &exp1, &exp2) + } + logical_or_expression ::= logical_xor_expression; + logical_or_expression ::= logical_or_expression(left) OrOp logical_xor_expression(right) { + extra.binary_expr(BinaryOperator::LogicalOr, &left, &right) + } + + conditional_expression ::= logical_or_expression; + conditional_expression ::= logical_or_expression Question expression Colon assignment_expression(ae) { + //TODO: how to do ternary here in naga? + return Err(ErrorKind::NotImplemented("ternary exp")) + } + + assignment_expression ::= conditional_expression; + assignment_expression ::= unary_expression(mut pointer) assignment_operator(op) assignment_expression(value) { + pointer.statements.extend(value.statements); + match op { + BinaryOperator::Equal => { + pointer.statements.push(Statement::Store{ + pointer: pointer.expression, + value: value.expression + }); + pointer + }, + _ => { + let h = extra.context.expressions.append( + Expression::Binary{ + op, + left: pointer.expression, + right: value.expression, + } + ); + pointer.statements.push(Statement::Store{ + pointer: pointer.expression, + value: h, + }); + pointer + } + } + } + + assignment_operator ::= Equal { + BinaryOperator::Equal + } + assignment_operator ::= MulAssign { + BinaryOperator::Multiply + } + assignment_operator ::= DivAssign { + BinaryOperator::Divide + } + assignment_operator ::= ModAssign { + BinaryOperator::Modulo + } + assignment_operator ::= AddAssign { + BinaryOperator::Add + } + assignment_operator ::= SubAssign { + BinaryOperator::Subtract + } + assignment_operator ::= LeftAssign { + BinaryOperator::ShiftLeft + } + assignment_operator ::= RightAssign { + BinaryOperator::ShiftRight + } + assignment_operator ::= AndAssign { + BinaryOperator::And + } + assignment_operator ::= XorAssign { + BinaryOperator::ExclusiveOr + } + assignment_operator ::= OrAssign { + BinaryOperator::InclusiveOr + } + + expression ::= assignment_expression; + expression ::= expression(e) Comma assignment_expression(mut ae) { + ae.statements.extend(e.statements); + ExpressionRule { + expression: e.expression, + statements: ae.statements, + sampler: None, + } + } + + //TODO: properly handle constant expressions + // constant_expression ::= conditional_expression(e) { + // if let Expression::Constant(h) = extra.context.expressions[e] { + // h + // } else { + // return Err(ErrorKind::ExpectedConstant) + // } + // } + + // declaration + declaration ::= init_declarator_list(idl) Semicolon { + Some(idl) + } + + declaration ::= type_qualifier(t) Identifier(i) LeftBrace + struct_declaration_list(sdl) RightBrace Semicolon { + if i.1 == "gl_PerVertex" { + None + } else { + let level = if t.is_empty() { + //TODO + crate::StructLevel::Normal { alignment: crate::Alignment::new(1).unwrap() } + } else { + crate::StructLevel::Root + }; + Some(VarDeclaration { + type_qualifiers: t, + ids_initializers: vec![(None, None)], + ty: extra.module.types.fetch_or_append(Type{ + name: Some(i.1), + inner: TypeInner::Struct { + level, + members: sdl, + span: 0, //TODO + }, + }), + }) + } + } + + declaration ::= type_qualifier(t) Identifier(i1) LeftBrace + struct_declaration_list(sdl) RightBrace Identifier(i2) Semicolon { + let level = if t.is_empty() { + //TODO + crate::StructLevel::Normal { alignment: crate::Alignment::new(1).unwrap() } + } else { + crate::StructLevel::Root + }; + Some(VarDeclaration { + type_qualifiers: t, + ids_initializers: vec![(Some(i2.1), None)], + ty: extra.module.types.fetch_or_append(Type{ + name: Some(i1.1), + inner: TypeInner::Struct { + level, + members: sdl, + span: 0, //TODO + }, + }), + }) + } + + // declaration ::= type_qualifier(t) Identifier(i1) LeftBrace + // struct_declaration_list RightBrace Identifier(i2) array_specifier Semicolon; + + init_declarator_list ::= single_declaration; + init_declarator_list ::= init_declarator_list(mut idl) Comma Identifier(i) { + idl.ids_initializers.push((Some(i.1), None)); + idl + } + // init_declarator_list ::= init_declarator_list Comma Identifier array_specifier; + // init_declarator_list ::= init_declarator_list Comma Identifier array_specifier Equal initializer; + init_declarator_list ::= init_declarator_list(mut idl) Comma Identifier(i) Equal initializer(init) { + idl.ids_initializers.push((Some(i.1), Some(init))); + idl + } + + single_declaration ::= fully_specified_type(t) { + let ty = t.1.ok_or_else(||ErrorKind::SemanticError("Empty type for declaration".into()))?; + + VarDeclaration { + type_qualifiers: t.0, + ids_initializers: vec![], + ty, + } + } + single_declaration ::= fully_specified_type(t) Identifier(i) { + let ty = t.1.ok_or_else(|| ErrorKind::SemanticError("Empty type for declaration".into()))?; + + VarDeclaration { + type_qualifiers: t.0, + ids_initializers: vec![(Some(i.1), None)], + ty, + } + } + + single_declaration ::= fully_specified_type(t) Identifier(i) LeftBracket additive_expression(exp) RightBracket { + let ty_item = t.1.ok_or_else(|| ErrorKind::SemanticError("Empty type for declaration".into()))?; + + let array_size = if let Ok(constant) = extra.solve_constant(exp.expression) { + ArraySize::Constant(constant) + } else { + ArraySize::Dynamic + }; + + let item_size = extra.type_size(ty_item)?; + + let ty = extra.module.types.fetch_or_append(Type { + inner: TypeInner::Array { + base: ty_item, + size: array_size, + stride: item_size as u32, + }, + name: None, }); - Ok(ctx.hir_exprs.append(HirExpr { - kind: HirExprKind::Constant(handle), - meta: token.meta, - })) + VarDeclaration { + type_qualifiers: t.0, + ids_initializers: vec![(Some(i.1), None)], + ty, + } } - fn parse_function_call_args( - &mut self, - ctx: &mut Context, - body: &mut Block, - meta: &mut SourceMetadata, - ) -> Result>> { - let mut args = Vec::new(); - if let Some(token) = self.bump_if(TokenValue::RightParen) { - *meta = meta.union(&token.meta); + // single_declaration ::= fully_specified_type Identifier array_specifier; + // single_declaration ::= fully_specified_type Identifier array_specifier Equal initializer; + single_declaration ::= fully_specified_type(t) Identifier(i) Equal initializer(init) { + let ty = t.1.ok_or_else(|| ErrorKind::SemanticError("Empty type for declaration".into()))?; + + VarDeclaration { + type_qualifiers: t.0, + ids_initializers: vec![(Some(i.1), Some(init))], + ty, + } + } + + fully_specified_type ::= type_specifier(t) { + (vec![], t) + } + fully_specified_type ::= type_qualifier(q) type_specifier(t) { + (q,t) + } + + interpolation_qualifier ::= Interpolation((_, i)) { + i + } + + sampling_qualifier ::= Sampling((_, s)) { + s + } + + layout_qualifier ::= Layout LeftParen layout_qualifier_id_list(l) RightParen { + if let Some(&(_, location)) = l.iter().find(|&q| q.0.as_str() == "location") { + let interpolation = None; //TODO + let sampling = None; //TODO + StructLayout::Binding(Binding::Location { location, interpolation, sampling }) + } else if let Some(&(_, binding)) = l.iter().find(|&q| q.0.as_str() == "binding") { + let group = if let Some(&(_, set)) = l.iter().find(|&q| q.0.as_str() == "set") { + set + } else { + 0 + }; + StructLayout::Resource(ResourceBinding{ group, binding }) + } else if l.iter().any(|q| q.0.as_str() == "push_constant") { + StructLayout::PushConstant } else { - loop { - args.push(self.parse_assignment(ctx, body)?); - - let token = self.bump()?; - match token.value { - TokenValue::Comma => {} - TokenValue::RightParen => { - *meta = meta.union(&token.meta); - break; - } - _ => { - return Err(ErrorKind::InvalidToken( - token, - vec![TokenValue::Comma.into(), TokenValue::RightParen.into()], - )) - } - } - } + return Err(ErrorKind::NotImplemented("unsupported layout qualifier(s)")); } + } + layout_qualifier_id_list ::= layout_qualifier_id(lqi) { + vec![lqi] + } + layout_qualifier_id_list ::= layout_qualifier_id_list(mut l) Comma layout_qualifier_id(lqi) { + l.push(lqi); + l + } + layout_qualifier_id ::= Identifier(i) { + (i.1, 0) + } + //TODO: handle full constant_expression instead of IntConstant + layout_qualifier_id ::= Identifier(i) Equal IntConstant(ic) { + (i.1, ic.1 as u32) + } + // layout_qualifier_id ::= Shared; - Ok(args) + // precise_qualifier ::= Precise; + + type_qualifier ::= single_type_qualifier(t) { + vec![t] + } + type_qualifier ::= type_qualifier(mut l) single_type_qualifier(t) { + l.push(t); + l } - fn parse_postfix(&mut self, ctx: &mut Context, body: &mut Block) -> Result> { - let mut base = match self.expect_peek()?.value { - TokenValue::Identifier(_) => { - let (name, mut meta) = self.expect_ident()?; + single_type_qualifier ::= storage_qualifier(s) { + TypeQualifier::StorageQualifier(s) + } + single_type_qualifier ::= layout_qualifier(l) { + match l { + StructLayout::Binding(b) => TypeQualifier::Binding(b), + StructLayout::Resource(b) => TypeQualifier::ResourceBinding(b), + StructLayout::PushConstant => TypeQualifier::StorageQualifier(StorageQualifier::StorageClass(StorageClass::PushConstant)), + } + } + // single_type_qualifier ::= precision_qualifier; + single_type_qualifier ::= interpolation_qualifier(i) { + TypeQualifier::Interpolation(i) + } + single_type_qualifier ::= sampling_qualifier(i) { + TypeQualifier::Sampling(i) + } + // single_type_qualifier ::= invariant_qualifier; + // single_type_qualifier ::= precise_qualifier; - let expr = if self.bump_if(TokenValue::LeftParen).is_some() { - let args = self.parse_function_call_args(ctx, body, &mut meta)?; + storage_qualifier ::= Const { + StorageQualifier::Const + } + // storage_qualifier ::= InOut; + storage_qualifier ::= In { + StorageQualifier::Input + } + storage_qualifier ::= Out { + StorageQualifier::Output + } + // storage_qualifier ::= Centroid; + // storage_qualifier ::= Patch; + // storage_qualifier ::= Sample; + storage_qualifier ::= Uniform { + StorageQualifier::StorageClass(StorageClass::Uniform) + } + //TODO: other storage qualifiers - let kind = match self.program.lookup_type.get(&name) { - Some(ty) => FunctionCallKind::TypeConstructor(*ty), - None => FunctionCallKind::Function(name), - }; - - HirExpr { - kind: HirExprKind::Call(FunctionCall { kind, args }), - meta, - } - } else { - let var = match self.program.lookup_variable(ctx, body, &name)? { - Some(var) => var, - None => return Err(ErrorKind::UnknownVariable(meta, name)), - }; - - HirExpr { - kind: HirExprKind::Variable(var), - meta, - } - }; - - ctx.hir_exprs.append(expr) + type_specifier ::= type_specifier_nonarray(t) { + t.map(|t| { + let name = t.name.clone(); + let handle = extra.module.types.fetch_or_append(t); + if let Some(name) = name { + extra.lookup_type.insert(name, handle); } - TokenValue::TypeName(_) => { - let Token { value, mut meta } = self.bump()?; + handle + }) + } + //TODO: array - let handle = if let TokenValue::TypeName(ty) = value { - self.program.module.types.fetch_or_append(ty) - } else { - unreachable!() - }; + type_specifier_nonarray ::= Void { + None + } + type_specifier_nonarray ::= TypeName(t) { + Some(t.1) + }; + type_specifier_nonarray ::= struct_specifier(s) { + Some(s) + } - self.expect(TokenValue::LeftParen)?; - let args = self.parse_function_call_args(ctx, body, &mut meta)?; - - ctx.hir_exprs.append(HirExpr { - kind: HirExprKind::Call(FunctionCall { - kind: FunctionCallKind::TypeConstructor(handle), - args, - }), - meta, - }) + // struct + struct_specifier ::= Struct Identifier(i) LeftBrace struct_declaration_list RightBrace { + Type{ + name: Some(i.1), + inner: TypeInner::Struct { + level: crate::StructLevel::Normal { alignment: crate::Alignment::new(1).unwrap() }, + members: vec![], + span: 0, + } + } + } + //struct_specifier ::= Struct LeftBrace struct_declaration_list RightBrace; + + struct_declaration_list ::= struct_declaration(sd) { + sd + } + struct_declaration_list ::= struct_declaration_list(mut sdl) struct_declaration(sd) { + sdl.extend(sd); + sdl + } + + struct_declaration ::= type_specifier(t) struct_declarator_list(sdl) Semicolon { + if let Some(ty) = t { + sdl.iter().map(|name| StructMember { + name: Some(name.clone()), + ty, + binding: None, //TODO + //TODO: if the struct is a uniform struct, these values have to reflect + // std140 layout. Otherwise, std430. + offset: 0, + }).collect() + } else { + return Err(ErrorKind::SemanticError("Struct member can't be void".into())) + } + } + //struct_declaration ::= type_qualifier type_specifier struct_declarator_list Semicolon; + + struct_declarator_list ::= struct_declarator(sd) { + vec![sd] + } + struct_declarator_list ::= struct_declarator_list(mut sdl) Comma struct_declarator(sd) { + sdl.push(sd); + sdl + } + + struct_declarator ::= Identifier(i) { + i.1 + } + //struct_declarator ::= Identifier array_specifier; + + + initializer ::= assignment_expression; + // initializer ::= LeftBrace initializer_list RightBrace; + // initializer ::= LeftBrace initializer_list Comma RightBrace; + + // initializer_list ::= initializer; + // initializer_list ::= initializer_list Comma initializer; + + declaration_statement ::= declaration(d) { + let mut statements = Vec::::new(); + // local variables + if let Some(d) = d { + for (id, initializer) in d.ids_initializers { + let id = id.ok_or_else(|| ErrorKind::SemanticError("Local var must be named".into()))?; + // check if already declared in current scope + #[cfg(feature = "glsl-validate")] + { + if extra.context.lookup_local_var_current_scope(&id).is_some() { + return Err(ErrorKind::VariableAlreadyDeclared(id)) + } + } + let mut init_exp: Option> = None; + let localVar = extra.context.local_variables.append( + LocalVariable { + name: Some(id.clone()), + ty: d.ty, + init: initializer.map(|i| { + statements.extend(i.statements); + if let Expression::Constant(constant) = extra.context.expressions[i.expression] { + Some(constant) + } else { + init_exp = Some(i.expression); + None + } + }).flatten(), + } + ); + let exp = extra.context.expressions.append(Expression::LocalVariable(localVar)); + extra.context.add_local_var(id, exp); + + if let Some(value) = init_exp { + statements.push( + Statement::Store { + pointer: exp, + value, + } + ); + } } - _ => self.parse_primary(ctx, body)?, }; - - while let TokenValue::LeftBracket - | TokenValue::Dot - | TokenValue::Increment - | TokenValue::Decrement = self.expect_peek()?.value - { - let Token { value, meta } = self.bump()?; - - match value { - TokenValue::LeftBracket => { - let index = self.parse_expression(ctx, body)?; - let end_meta = self.expect(TokenValue::RightBracket)?.meta; - - base = ctx.hir_exprs.append(HirExpr { - kind: HirExprKind::Access { base, index }, - meta: meta.union(&end_meta), - }) - } - TokenValue::Dot => { - let (field, end_meta) = self.expect_ident()?; - - base = ctx.hir_exprs.append(HirExpr { - kind: HirExprKind::Select { base, field }, - meta: meta.union(&end_meta), - }) - } - TokenValue::Increment => { - base = ctx.hir_exprs.append(HirExpr { - kind: HirExprKind::IncDec { - increment: true, - postfix: true, - expr: base, - }, - meta, - }) - } - TokenValue::Decrement => { - base = ctx.hir_exprs.append(HirExpr { - kind: HirExprKind::IncDec { - increment: false, - postfix: true, - expr: base, - }, - meta, - }) - } - _ => unreachable!(), - } + match statements.len() { + 1 => statements.remove(0), + _ => Statement::Block(statements), } - - Ok(base) } - fn parse_unary(&mut self, ctx: &mut Context, body: &mut Block) -> Result> { - // TODO: prefix inc/dec - Ok(match self.expect_peek()?.value { - TokenValue::Plus | TokenValue::Dash | TokenValue::Bang | TokenValue::Tilde => { - let Token { value, meta } = self.bump()?; - - let expr = self.parse_unary(ctx, body)?; - let end_meta = ctx.hir_exprs[expr].meta; - - let kind = match value { - TokenValue::Dash => HirExprKind::Unary { - op: UnaryOperator::Negate, - expr, - }, - TokenValue::Bang | TokenValue::Tilde => HirExprKind::Unary { - op: UnaryOperator::Not, - expr, - }, - _ => return Ok(expr), - }; - - ctx.hir_exprs.append(HirExpr { - kind, - meta: meta.union(&end_meta), - }) - } - TokenValue::Increment | TokenValue::Decrement => { - let Token { value, meta } = self.bump()?; - - let expr = self.parse_unary(ctx, body)?; - - ctx.hir_exprs.append(HirExpr { - kind: HirExprKind::IncDec { - increment: match value { - TokenValue::Increment => true, - TokenValue::Decrement => false, - _ => unreachable!(), - }, - postfix: false, - expr, - }, - meta, - }) - } - _ => self.parse_postfix(ctx, body)?, - }) + // statement + statement ::= compound_statement(cs) { + Statement::Block(cs) } + statement ::= simple_statement; - fn parse_binary( - &mut self, - ctx: &mut Context, - body: &mut Block, - passtrough: Option>, - min_bp: u8, - ) -> Result> { - let mut left = passtrough - .ok_or(ErrorKind::EndOfFile /* Dummy error */) - .or_else(|_| self.parse_unary(ctx, body))?; - let start_meta = ctx.hir_exprs[left].meta; + simple_statement ::= declaration_statement; + simple_statement ::= expression_statement; + simple_statement ::= selection_statement; + simple_statement ::= jump_statement; + simple_statement ::= iteration_statement; - while let Some((l_bp, r_bp)) = binding_power(&self.expect_peek()?.value) { - if l_bp < min_bp { - break; - } - let Token { value, .. } = self.bump()?; - - let right = self.parse_binary(ctx, body, None, r_bp)?; - let end_meta = ctx.hir_exprs[right].meta; - - left = ctx.hir_exprs.append(HirExpr { - kind: HirExprKind::Binary { - left, - op: match value { - TokenValue::LogicalOr => BinaryOperator::LogicalOr, - TokenValue::LogicalXor => BinaryOperator::NotEqual, - TokenValue::LogicalAnd => BinaryOperator::LogicalAnd, - TokenValue::VerticalBar => BinaryOperator::InclusiveOr, - TokenValue::Caret => BinaryOperator::ExclusiveOr, - TokenValue::Ampersand => BinaryOperator::And, - TokenValue::Equal => BinaryOperator::Equal, - TokenValue::NotEqual => BinaryOperator::NotEqual, - TokenValue::GreaterEqual => BinaryOperator::GreaterEqual, - TokenValue::LessEqual => BinaryOperator::LessEqual, - TokenValue::LeftAngle => BinaryOperator::Less, - TokenValue::RightAngle => BinaryOperator::Greater, - TokenValue::LeftShift => BinaryOperator::ShiftLeft, - TokenValue::RightShift => BinaryOperator::ShiftRight, - TokenValue::Plus => BinaryOperator::Add, - TokenValue::Dash => BinaryOperator::Subtract, - TokenValue::Star => BinaryOperator::Multiply, - TokenValue::Slash => BinaryOperator::Divide, - TokenValue::Percent => BinaryOperator::Modulo, - _ => unreachable!(), - }, - right, - }, - meta: start_meta.union(&end_meta), - }) + selection_statement ::= If LeftParen expression(e) RightParen statement(s1) Else statement(s2) { + Statement::If { + condition: e.expression, + accept: vec![s1], + reject: vec![s2], } - - Ok(left) } - fn parse_conditional( - &mut self, - ctx: &mut Context, - body: &mut Block, - passtrough: Option>, - ) -> Result> { - let mut condition = self.parse_binary(ctx, body, passtrough, 0)?; - let start_meta = ctx.hir_exprs[condition].meta; - - if self.bump_if(TokenValue::Question).is_some() { - let accept = self.parse_expression(ctx, body)?; - self.expect(TokenValue::Colon)?; - let reject = self.parse_assignment(ctx, body)?; - let end_meta = ctx.hir_exprs[reject].meta; - - condition = ctx.hir_exprs.append(HirExpr { - kind: HirExprKind::Conditional { - condition, - accept, - reject, - }, - meta: start_meta.union(&end_meta), - }) + selection_statement ::= If LeftParen expression(e) RightParen statement(s) [Else] { + Statement::If { + condition: e.expression, + accept: vec![s], + reject: vec![], } - - Ok(condition) } - fn parse_assignment(&mut self, ctx: &mut Context, body: &mut Block) -> Result> { - let tgt = self.parse_unary(ctx, body)?; - let start_meta = ctx.hir_exprs[tgt].meta; - - Ok(match self.expect_peek()?.value { - TokenValue::Assign => { - self.bump()?; - let value = self.parse_assignment(ctx, body)?; - let end_meta = ctx.hir_exprs[value].meta; - - ctx.hir_exprs.append(HirExpr { - kind: HirExprKind::Assign { tgt, value }, - meta: start_meta.union(&end_meta), - }) - } - TokenValue::OrAssign - | TokenValue::AndAssign - | TokenValue::AddAssign - | TokenValue::DivAssign - | TokenValue::ModAssign - | TokenValue::SubAssign - | TokenValue::MulAssign - | TokenValue::LeftShiftAssign - | TokenValue::RightShiftAssign - | TokenValue::XorAssign => { - let token = self.bump()?; - let right = self.parse_assignment(ctx, body)?; - let end_meta = ctx.hir_exprs[right].meta; - - let value = ctx.hir_exprs.append(HirExpr { - meta: start_meta.union(&end_meta), - kind: HirExprKind::Binary { - left: tgt, - op: match token.value { - TokenValue::OrAssign => BinaryOperator::InclusiveOr, - TokenValue::AndAssign => BinaryOperator::And, - TokenValue::AddAssign => BinaryOperator::Add, - TokenValue::DivAssign => BinaryOperator::Divide, - TokenValue::ModAssign => BinaryOperator::Modulo, - TokenValue::SubAssign => BinaryOperator::Subtract, - TokenValue::MulAssign => BinaryOperator::Multiply, - TokenValue::LeftShiftAssign => BinaryOperator::ShiftLeft, - TokenValue::RightShiftAssign => BinaryOperator::ShiftRight, - TokenValue::XorAssign => BinaryOperator::ExclusiveOr, - _ => unreachable!(), - }, - right, - }, + selection_statement ::= Switch LeftParen expression(e) RightParen LeftBrace switch_statement_list(ls) RightBrace { + let mut default = Vec::new(); + let mut cases = Vec::new(); + for (v, body, fall_through) in ls { + if let Some(value) = v { + cases.push(SwitchCase { + value, + body, + fall_through, }); - - ctx.hir_exprs.append(HirExpr { - kind: HirExprKind::Assign { tgt, value }, - meta: start_meta.union(&end_meta), - }) + } else { + default.extend_from_slice(&body); } - _ => self.parse_conditional(ctx, body, Some(tgt))?, - }) + } + Statement::Switch { + selector: e.expression, + cases, + default, + } } - fn parse_expression(&mut self, ctx: &mut Context, body: &mut Block) -> Result> { - let mut expr = self.parse_assignment(ctx, body)?; - - while let TokenValue::Comma = self.expect_peek()?.value { - self.bump()?; - expr = self.parse_assignment(ctx, body)?; - } - - Ok(expr) + switch_statement_list ::= { + vec![] + } + switch_statement_list ::= switch_statement_list(mut ssl) switch_statement((v, sl, ft)) { + ssl.push((v, sl, ft)); + ssl + } + switch_statement ::= Case IntConstant(v) Colon statement_list(sl) { + let fall_through = match sl.last() { + Some(&Statement::Break) => false, + _ => true, + }; + (Some(v.1 as i32), sl, fall_through) + } + switch_statement ::= Default Colon statement_list(sl) { + let fall_through = match sl.last() { + Some(&Statement::Break) => true, + _ => false, + }; + (None, sl, fall_through) } - fn parse_statement(&mut self, ctx: &mut Context, body: &mut Block) -> Result<()> { - // TODO: This prevents snippets like the following from working - // ```glsl - // vec4(1.0); - // ``` - // But this would require us to add lookahead to also support - // declarations and since this statement is very unlikely and most - // likely an error, for now we don't support it - if self.peek_type_name() || self.peek_type_qualifier() { - self.parse_declaration(ctx, body, false)?; - return Ok(()); + iteration_statement ::= While LeftParen expression(e) RightParen compound_statement_no_new_scope(sl) { + let mut body = Vec::with_capacity(sl.len() + 1); + body.push( + Statement::If { + condition: e.expression, + accept: vec![Statement::Break], + reject: vec![], + } + ); + body.extend_from_slice(&sl); + Statement::Loop { + body, + continuing: vec![], } + } - match self.expect_peek()?.value { - TokenValue::Continue => { - self.bump()?; - body.push(Statement::Continue); - self.expect(TokenValue::Semicolon)?; + iteration_statement ::= Do compound_statement(sl) While LeftParen expression(e) RightParen { + let mut body = sl; + body.push( + Statement::If { + condition: e.expression, + accept: vec![Statement::Break], + reject: vec![], } - TokenValue::Break => { - self.bump()?; - body.push(Statement::Break); - self.expect(TokenValue::Semicolon)?; - } - TokenValue::Return => { - self.bump()?; - let value = match self.expect_peek()?.value { - TokenValue::Semicolon => { - self.bump()?; - None - } - _ => { - // TODO: Implicit conversions - let expr = self.parse_expression(ctx, body)?; - self.expect(TokenValue::Semicolon)?; - Some(ctx.lower_expect(self.program, expr, false, body)?.0) - } - }; + ); + Statement::Loop { + body, + continuing: vec![], + } + } - ctx.emit_flush(body); - ctx.emit_start(); - - body.push(Statement::Return { value }) - } - TokenValue::Discard => { - self.bump()?; - body.push(Statement::Kill); - self.expect(TokenValue::Semicolon)?; - } - TokenValue::If => { - self.bump()?; - - self.expect(TokenValue::LeftParen)?; - let condition = { - let expr = self.parse_expression(ctx, body)?; - ctx.lower_expect(self.program, expr, false, body)?.0 - }; - self.expect(TokenValue::RightParen)?; - - ctx.emit_flush(body); - ctx.emit_start(); - - let mut accept = Block::new(); - self.parse_statement(ctx, &mut accept)?; - - let mut reject = Block::new(); - if self.bump_if(TokenValue::Else).is_some() { - self.parse_statement(ctx, &mut reject)?; - } - - body.push(Statement::If { - condition, - accept, - reject, - }); - } - TokenValue::Switch => { - self.bump()?; - - self.expect(TokenValue::LeftParen)?; - // TODO: Implicit conversions - let selector = { - let expr = self.parse_expression(ctx, body)?; - ctx.lower_expect(self.program, expr, false, body)?.0 - }; - self.expect(TokenValue::RightParen)?; - - ctx.emit_flush(body); - ctx.emit_start(); - - let mut cases = Vec::new(); - let mut default = Block::new(); - - self.expect(TokenValue::LeftBrace)?; - loop { - match self.expect_peek()?.value { - TokenValue::Case => { - self.bump()?; - let value = { - let expr = self.parse_expression(ctx, body)?; - let (root, meta) = - ctx.lower_expect(self.program, expr, false, body)?; - let constant = self.program.solve_constant(ctx, root, meta)?; - - match self.program.module.constants[constant].inner { - ConstantInner::Scalar { - value: ScalarValue::Sint(int), - .. - } => int as i32, - ConstantInner::Scalar { - value: ScalarValue::Uint(int), - .. - } => int as i32, - _ => { - return Err(ErrorKind::SemanticError( - meta, - "Case values can only be integers".into(), - )) - } - } - }; - - self.expect(TokenValue::Colon)?; - - let mut body = Block::new(); - - loop { - match self.expect_peek()?.value { - TokenValue::Case - | TokenValue::Default - | TokenValue::RightBrace => break, - _ => self.parse_statement(ctx, &mut body)?, - } - } - - let fall_through = body.iter().any(|s| { - mem::discriminant(s) == mem::discriminant(&Statement::Break) - }); - - cases.push(SwitchCase { - value, - body, - fall_through, - }) - } - TokenValue::Default => { - let Token { meta, .. } = self.bump()?; - self.expect(TokenValue::Colon)?; - - if !default.is_empty() { - return Err(ErrorKind::SemanticError( - meta, - "Can only have one default case per switch statement".into(), - )); - } - - loop { - match self.expect_peek()?.value { - TokenValue::Case | TokenValue::RightBrace => break, - _ => self.parse_statement(ctx, &mut &mut default)?, - } - } - } - TokenValue::RightBrace => { - self.bump()?; - break; - } - _ => { - return Err(ErrorKind::InvalidToken( - self.bump()?, - vec![ - TokenValue::Case.into(), - TokenValue::Default.into(), - TokenValue::RightBrace.into(), - ], - )) - } - } - } - - body.push(Statement::Switch { - selector, - cases, - default, - }); - } - TokenValue::While => { - self.bump()?; - - let mut loop_body = Block::new(); - - self.expect(TokenValue::LeftParen)?; - let root = self.parse_expression(ctx, &mut loop_body)?; - self.expect(TokenValue::RightParen)?; - - let expr = ctx - .lower_expect(self.program, root, false, &mut loop_body)? - .0; - let condition = ctx.add_expression( - Expression::Unary { - op: UnaryOperator::Not, - expr, - }, - &mut loop_body, - ); - - ctx.emit_flush(&mut loop_body); - ctx.emit_start(); - - loop_body.push(Statement::If { - condition, + iteration_statement ::= For LeftParen for_init_statement(s_init) for_rest_statement((cond_e, loop_e)) RightParen compound_statement_no_new_scope(sl) { + let mut body = Vec::with_capacity(sl.len() + 2); + if let Some(cond_e) = cond_e { + body.push( + Statement::If { + condition: cond_e.expression, accept: vec![Statement::Break], - reject: Block::new(), - }); - - self.parse_statement(ctx, &mut loop_body)?; - - body.push(Statement::Loop { - body: loop_body, - continuing: Block::new(), - }) - } - TokenValue::Do => { - self.bump()?; - - let mut loop_body = Block::new(); - self.parse_statement(ctx, &mut loop_body)?; - - self.expect(TokenValue::While)?; - self.expect(TokenValue::LeftParen)?; - let root = self.parse_expression(ctx, &mut loop_body)?; - self.expect(TokenValue::RightParen)?; - - let expr = ctx - .lower_expect(self.program, root, false, &mut loop_body)? - .0; - let condition = ctx.add_expression( - Expression::Unary { - op: UnaryOperator::Not, - expr, - }, - &mut loop_body, - ); - - ctx.emit_flush(&mut loop_body); - ctx.emit_start(); - - loop_body.push(Statement::If { - condition, - accept: vec![Statement::Break], - reject: Block::new(), - }); - - body.push(Statement::Loop { - body: loop_body, - continuing: Block::new(), - }) - } - TokenValue::For => { - self.bump()?; - - ctx.push_scope(); - self.expect(TokenValue::LeftParen)?; - - if self.bump_if(TokenValue::Semicolon).is_none() { - if self.peek_type_name() || self.peek_type_qualifier() { - self.parse_declaration(ctx, body, false)?; - } else { - self.parse_expression(ctx, body)?; - self.expect(TokenValue::Semicolon)?; - } + reject: vec![], } + ); + } + body.extend_from_slice(&sl); + if let Some(loop_e) = loop_e { + body.extend_from_slice(&loop_e.statements); + } + Statement::Block(vec![ + s_init, + Statement::Loop { + body, + continuing: vec![], + } + ]) + } - ctx.emit_flush(body); - ctx.emit_start(); + for_init_statement ::= expression_statement; + for_init_statement ::= declaration_statement; + for_rest_statement ::= condition_opt(c) Semicolon { + (c, None) + } + for_rest_statement ::= condition_opt(c) Semicolon expression(e) { + (c, Some(e)) + } - let (mut block, mut continuing) = (Block::new(), Block::new()); + condition_opt ::= { + None + } + condition_opt ::= conditional_expression(c) { + Some(c) + } - if self.bump_if(TokenValue::Semicolon).is_none() { - let expr = if self.peek_type_name() || self.peek_type_qualifier() { - let qualifiers = self.parse_type_qualifiers()?; - let (ty, meta) = self.parse_type_non_void()?; - let name = self.expect_ident()?.0; + compound_statement ::= LeftBrace RightBrace { + vec![] + } + compound_statement ::= left_brace_scope statement_list(sl) RightBrace { + extra.context.remove_current_scope(); + sl + } - self.expect(TokenValue::Assign)?; + // extra rule to add scope before statement_list + left_brace_scope ::= LeftBrace { + extra.context.push_scope(); + } - let (value, end_meta) = self.parse_initializer(ty, ctx, &mut block)?; - let decl = VarDeclaration { - qualifiers: &qualifiers, - ty, - name: Some(name), - init: None, - meta: meta.union(&end_meta), + compound_statement_no_new_scope ::= LeftBrace RightBrace { + vec![] + } + compound_statement_no_new_scope ::= LeftBrace statement_list(sl) RightBrace { + sl + } + + statement_list ::= statement(s) { + //TODO: catch this earlier and don't populate the statements + match s { + Statement::Block(ref block) if block.is_empty() => vec![], + _ => vec![s], + } + } + statement_list ::= statement_list(mut ss) statement(s) { ss.push(s); ss } + + expression_statement ::= Semicolon { + Statement::Block(Vec::new()) + } + expression_statement ::= expression(mut e) Semicolon { + match e.statements.len() { + 1 => e.statements.remove(0), + _ => Statement::Block(e.statements), + } + } + + + + // function + function_prototype ::= function_declarator(f) RightParen { + extra.add_function_prelude(); + f + } + function_declarator ::= function_header; + function_declarator ::= function_header_with_parameters((f, args)) { + for (pos, arg) in args.into_iter().enumerate() { + if let Some(name) = arg.name.clone() { + let exp = extra.context.expressions.append(Expression::FunctionArgument(pos as u32)); + extra.context.add_function_arg(name, exp); + } + extra.context.arguments.push(arg); + } + f + } + function_header ::= fully_specified_type(t) Identifier(n) LeftParen { + Function { + name: Some(n.1), + arguments: vec![], + result: t.1.map(|ty| FunctionResult { ty, binding: None }), + local_variables: Arena::::new(), + expressions: Arena::::new(), + body: vec![], + } + } + function_header_with_parameters ::= function_header(h) parameter_declaration(p) { + (h, vec![p]) + } + function_header_with_parameters ::= function_header_with_parameters((h, mut args)) Comma parameter_declaration(p) { + args.push(p); + (h, args) + } + parameter_declarator ::= parameter_type_specifier(ty) Identifier(n) { + FunctionArgument { name: Some(n.1), ty, binding: None } + } + // parameter_declarator ::= type_specifier(ty) Identifier(ident) array_specifier; + parameter_declaration ::= In? parameter_declarator(arg) { arg }; + parameter_declaration ::= In? parameter_type_specifier(ty) { + FunctionArgument { name: None, ty, binding: None } + } + + parameter_type_specifier ::= type_specifier(t) { + if let Some(ty) = t { + ty + } else { + return Err(ErrorKind::SemanticError("Function parameter can't be void".into())) + } + } + + jump_statement ::= Continue Semicolon { + Statement::Continue + } + jump_statement ::= Break Semicolon { + Statement::Break + } + jump_statement ::= Return Semicolon { + Statement::Return { value: None } + } + jump_statement ::= Return expression(mut e) Semicolon { + let ret = Statement::Return{ value: Some(e.expression) }; + if !e.statements.is_empty() { + e.statements.push(ret); + Statement::Block(e.statements) + } else { + ret + } + } + jump_statement ::= Discard Semicolon { + Statement::Kill + } // Fragment shader only + + // Grammar Note: No 'goto'. Gotos are not supported. + + // misc + translation_unit ::= external_declaration; + translation_unit ::= translation_unit external_declaration; + + external_declaration ::= function_definition(f) { + extra.declare_function(f)? + } + external_declaration ::= declaration(d) { + if let Some(d) = d { + // TODO: handle multiple storage qualifiers + let storage = d.type_qualifiers.iter().find_map(|tq| { + if let TypeQualifier::StorageQualifier(sc) = *tq { Some(sc) } else { None } + }).unwrap_or(StorageQualifier::StorageClass(StorageClass::Private)); + + match storage { + StorageQualifier::StorageClass(storage_class) => { + // TODO: Check that the storage qualifiers allow for the bindings + let binding = d.type_qualifiers.iter().find_map(|tq| { + if let TypeQualifier::ResourceBinding(ref b) = *tq { Some(b.clone()) } else { None } + }); + for (id, initializer) in d.ids_initializers { + let init = initializer.map(|init| extra.solve_constant(init.expression)).transpose()?; + + // use StorageClass::Handle for texture and sampler uniforms + let class = if storage_class == StorageClass::Uniform { + match extra.module.types[d.ty].inner { + TypeInner::Image{..} | TypeInner::Sampler{..} => StorageClass::Handle, + _ => storage_class, + } + } else { + storage_class }; - let pointer = self.program.add_local_var(ctx, &mut block, decl)?; - - ctx.emit_flush(&mut block); - ctx.emit_start(); - - block.push(Statement::Store { pointer, value }); - - value - } else { - let root = self.parse_expression(ctx, &mut block)?; - ctx.lower_expect(self.program, root, false, &mut block)?.0 - }; - - let condition = ctx.add_expression( - Expression::Unary { - op: UnaryOperator::Not, - expr, - }, - &mut block, - ); - - ctx.emit_flush(&mut block); - ctx.emit_start(); - - block.push(Statement::If { - condition, - accept: vec![Statement::Break], - reject: Block::new(), - }); - - self.expect(TokenValue::Semicolon)?; - } - - match self.expect_peek()?.value { - TokenValue::RightParen => {} - _ => { - let rest = self.parse_expression(ctx, &mut continuing)?; - ctx.lower(self.program, rest, false, &mut continuing)?; - } - } - - self.expect(TokenValue::RightParen)?; - - self.parse_statement(ctx, &mut block)?; - - body.push(Statement::Loop { - body: block, - continuing, - }); - - ctx.remove_current_scope(); - } - TokenValue::LeftBrace => { - self.bump()?; - - let mut block = Block::new(); - ctx.push_scope(); - - self.parse_compound_statement(ctx, &mut block)?; - - ctx.remove_current_scope(); - body.push(Statement::Block(block)); - } - TokenValue::Plus - | TokenValue::Dash - | TokenValue::Bang - | TokenValue::Tilde - | TokenValue::LeftParen - | TokenValue::Identifier(_) - | TokenValue::TypeName(_) - | TokenValue::IntConstant(_) - | TokenValue::BoolConstant(_) - | TokenValue::FloatConstant(_) => { - let expr = self.parse_expression(ctx, body)?; - ctx.lower(self.program, expr, false, body)?; - self.expect(TokenValue::Semicolon)?; - } - TokenValue::Semicolon => { - self.bump()?; - } - _ => {} - } - - Ok(()) - } - - fn parse_compound_statement(&mut self, ctx: &mut Context, body: &mut Block) -> Result<()> { - loop { - if self.bump_if(TokenValue::RightBrace).is_some() { - break; - } - - self.parse_statement(ctx, body)?; - } - - Ok(()) - } - - fn parse_function_args( - &mut self, - context: &mut Context, - body: &mut Block, - parameters: &mut Vec, - sig: &mut FunctionSignature, - ) -> Result<()> { - loop { - if self.peek_type_name() || self.peek_parameter_qualifier() { - let qualifier = self.parse_parameter_qualifier(); - parameters.push(qualifier); - let ty = self.parse_type_non_void()?.0; - - match self.expect_peek()?.value { - TokenValue::Comma => { - self.bump()?; - context.add_function_arg(&mut self.program, sig, body, None, ty, qualifier); - continue; - } - TokenValue::Identifier(_) => { - let name = self.expect_ident()?.0; - - let size = self.parse_array_specifier()?; - let ty = self.maybe_array(ty, size); - - context.add_function_arg( - &mut self.program, - sig, - body, - Some(name), - ty, - qualifier, + let h = extra.module.global_variables.fetch_or_append( + GlobalVariable { + name: id.clone(), + class, + binding: binding.clone(), + ty: d.ty, + init, + storage_access: StorageAccess::empty(), //TODO + }, ); - - if self.bump_if(TokenValue::Comma).is_some() { - continue; + if let Some(id) = id { + extra.lookup_global_variables.insert(id, h); + } + } + } + StorageQualifier::Input => { + let mut binding = d.type_qualifiers.iter().find_map(|tq| { + if let TypeQualifier::Binding(ref b) = *tq { Some(b.clone()) } else { None } + }); + let interpolation = d.type_qualifiers.iter().find_map(|tq| { + if let TypeQualifier::Interpolation(interp) = *tq { Some(interp) } else { None } + }); + let sampling = d.type_qualifiers.iter().find_map(|tq| { + if let TypeQualifier::Sampling(samp) = *tq { Some(samp) } else { None } + }); + if let Some(Binding::Location { + interpolation: ref mut interp, + sampling: ref mut samp, + .. + }) = binding { + *interp = interpolation; + *samp = sampling; + } + + for (id, _initializer) in d.ids_initializers { + if let Some(id) = id { + //TODO! + let expr = extra.context.expressions.append(Expression::FunctionArgument(0)); + extra.context.lookup_global_var_exps.insert(id, expr); + } + } + } + StorageQualifier::Output => { + let _binding = d.type_qualifiers.iter().find_map(|tq| { + if let TypeQualifier::Binding(ref b) = *tq { Some(b.clone()) } else { None } + }); + for (id, _initializer) in d.ids_initializers { + if let Some(id) = id { + //TODO! + let expr = extra.context.expressions.append(Expression::FunctionArgument(0)); + extra.context.lookup_global_var_exps.insert(id, expr); + } + } + } + StorageQualifier::Const => { + for (id, initializer) in d.ids_initializers { + if let Some(init) = initializer { + let constant = extra.solve_constant(init.expression)?; + let inner = extra.module.constants[constant].inner.clone(); + + let h = extra.module.constants.fetch_or_append( + Constant { + name: id.clone(), + specialization: None, // TODO + inner + }, + ); + if let Some(id) = id { + extra.lookup_constants.insert(id.clone(), h); + let expr = extra.context.expressions.append(Expression::Constant(h)); + extra.context.lookup_constant_exps.insert(id, expr); + } + } else { + return Err(ErrorKind::SemanticError("Constants must have an initializer".into())) } - - break; } - _ => break, } } - - break; - } - - Ok(()) - } -} - -struct DeclarationContext<'ctx, 'fun> { - qualifiers: Vec<(TypeQualifier, SourceMetadata)>, - external: bool, - - ctx: &'ctx mut Context<'fun>, - body: &'ctx mut Block, -} - -impl<'ctx, 'fun> DeclarationContext<'ctx, 'fun> { - fn add_var( - &mut self, - program: &mut Program, - ty: Handle, - name: String, - init: Option>, - meta: SourceMetadata, - ) -> Result> { - let decl = VarDeclaration { - qualifiers: &self.qualifiers, - ty, - name: Some(name), - init, - meta, - }; - - match self.external { - true => { - let global = program.add_global_var(decl)?; - let expr = match global { - GlobalOrConstant::Global(handle) => Expression::GlobalVariable(handle), - GlobalOrConstant::Constant(handle) => Expression::Constant(handle), - }; - Ok(self.ctx.add_expression(expr, self.body)) - } - false => program.add_local_var(self.ctx, self.body, decl), } } - fn flush_expressions(&mut self) { - self.ctx.emit_flush(self.body); - self.ctx.emit_start() - } + function_definition ::= function_prototype(f) compound_statement_no_new_scope(cs) { + extra.function_definition(f, cs) + }; } -fn binding_power(value: &TokenValue) -> Option<(u8, u8)> { - Some(match *value { - TokenValue::LogicalOr => (1, 2), - TokenValue::LogicalXor => (3, 4), - TokenValue::LogicalAnd => (5, 6), - TokenValue::VerticalBar => (7, 8), - TokenValue::Caret => (9, 10), - TokenValue::Ampersand => (11, 12), - TokenValue::Equal | TokenValue::NotEqual => (13, 14), - TokenValue::GreaterEqual - | TokenValue::LessEqual - | TokenValue::LeftAngle - | TokenValue::RightAngle => (15, 16), - TokenValue::LeftShift | TokenValue::RightShift => (17, 18), - TokenValue::Plus | TokenValue::Dash => (19, 20), - TokenValue::Star | TokenValue::Slash | TokenValue::Percent => (21, 22), - _ => return None, - }) -} +pub use parser::*; diff --git a/third_party/rust/naga/src/front/glsl/parser_tests.rs b/third_party/rust/naga/src/front/glsl/parser_tests.rs index 40281ed36882..de0b4db411fb 100644 --- a/third_party/rust/naga/src/front/glsl/parser_tests.rs +++ b/third_party/rust/naga/src/front/glsl/parser_tests.rs @@ -1,14 +1,8 @@ -use pp_rs::token::PreprocessorError; - +use super::ast::Program; +use super::error::ErrorKind; use super::lex::Lexer; use super::parser; -use super::{ast::Profile, error::ErrorKind}; -use super::{ast::Program, SourceMetadata}; -use crate::front::glsl::error::ExpectedToken; -use crate::{ - front::glsl::{token::TokenValue, Token}, - ShaderStage, -}; +use crate::ShaderStage; fn parse_program<'a>( source: &str, @@ -17,9 +11,12 @@ fn parse_program<'a>( let mut program = Program::new(entry_points); let defines = crate::FastHashMap::default(); let lex = Lexer::new(source, &defines); - let mut parser = parser::Parser::new(&mut program, lex); + let mut parser = parser::Parser::new(&mut program); - parser.parse()?; + for token in lex { + parser.parse(token)?; + } + parser.end_of_input()?; Ok(program) } @@ -29,46 +26,61 @@ fn version() { entry_points.insert("".to_string(), ShaderStage::Vertex); // invalid versions assert_eq!( - parse_program("#version 99000", &entry_points) - .err() - .unwrap(), - ErrorKind::InvalidVersion(SourceMetadata { start: 9, end: 14 }, 99000), + format!( + "{:?}", + parse_program("#version 99000", &entry_points) + .err() + .unwrap() + ), + "InvalidVersion(TokenMetadata { line: 1, chars: 9..10 }, 99000)" //TODO: location ); assert_eq!( - parse_program("#version 449", &entry_points).err().unwrap(), - ErrorKind::InvalidVersion(SourceMetadata { start: 9, end: 12 }, 449) + format!( + "{:?}", + parse_program("#version 449", &entry_points).err().unwrap() + ), + "InvalidVersion(TokenMetadata { line: 1, chars: 9..10 }, 449)" //TODO: location ); assert_eq!( - parse_program("#version 450 smart", &entry_points) - .err() - .unwrap(), - ErrorKind::InvalidProfile(SourceMetadata { start: 13, end: 18 }, "smart".into()) + format!( + "{:?}", + parse_program("#version 450 smart", &entry_points) + .err() + .unwrap() + ), + "InvalidProfile(TokenMetadata { line: 1, chars: 13..14 }, \"smart\")" //TODO: location ); assert_eq!( - parse_program("#version 450\nvoid f(){} #version 450", &entry_points) - .err() - .unwrap(), - ErrorKind::InvalidToken( - Token { - value: TokenValue::Unknown(PreprocessorError::UnexpectedHash), - meta: SourceMetadata { start: 24, end: 25 } - }, - vec![ExpectedToken::Eof] - ) + format!( + "{:?}", + parse_program("#version 450\nvoid f(){} #version 450", &entry_points) + .err() + .unwrap() + ), + "InvalidToken(Unknown((TokenMetadata { line: 2, chars: 11..12 }, UnexpectedHash)))" ); // valid versions let program = parse_program(" # version 450\nvoid main() {}", &entry_points).unwrap(); - assert_eq!((program.version, program.profile), (450, Profile::Core)); + assert_eq!( + format!("{:?}", (program.version, program.profile)), + "(450, Core)" + ); let program = parse_program("#version 450\nvoid main() {}", &entry_points).unwrap(); - assert_eq!((program.version, program.profile), (450, Profile::Core)); + assert_eq!( + format!("{:?}", (program.version, program.profile)), + "(450, Core)" + ); let program = parse_program("#version 450 core\nvoid main() {}", &entry_points).unwrap(); - assert_eq!((program.version, program.profile), (450, Profile::Core)); + assert_eq!( + format!("{:?}", (program.version, program.profile)), + "(450, Core)" + ); } #[test] @@ -150,7 +162,6 @@ fn control_flow() { for(int i = 0; i < 10;) { x = x + 2; } - for(;;); return x; } "#, @@ -159,95 +170,6 @@ fn control_flow() { .unwrap(); } -#[test] -fn declarations() { - let mut entry_points = crate::FastHashMap::default(); - entry_points.insert("".to_string(), ShaderStage::Fragment); - - let _program = parse_program( - r#" - #version 450 - layout(location = 0) in vec2 v_uv; - layout(location = 0) out vec4 o_color; - layout(set = 1, binding = 1) uniform texture2D tex; - layout(set = 1, binding = 2) uniform sampler tex_sampler; - - layout(early_fragment_tests) in; - "#, - &entry_points, - ) - .unwrap(); - - let _program = parse_program( - r#" - #version 450 - layout(std140, set = 2, binding = 0) - uniform u_locals { - vec3 model_offs; - float load_time; - ivec4 atlas_offs; - }; - "#, - &entry_points, - ) - .unwrap(); - - let _program = parse_program( - r#" - #version 450 - layout(push_constant, set = 2, binding = 0) - uniform u_locals { - vec3 model_offs; - float load_time; - ivec4 atlas_offs; - }; - "#, - &entry_points, - ) - .unwrap(); - - let _program = parse_program( - r#" - #version 450 - layout(std430, set = 2, binding = 0) - uniform u_locals { - vec3 model_offs; - float load_time; - ivec4 atlas_offs; - }; - "#, - &entry_points, - ) - .unwrap(); - - let _program = parse_program( - r#" - #version 450 - layout(std140, set = 2, binding = 0) - uniform u_locals { - vec3 model_offs; - float load_time; - } block_var; - - void main() { - load_time * model_offs; - block_var.load_time * block_var.model_offs; - } - "#, - &entry_points, - ) - .unwrap(); - - let _program = parse_program( - r#" - #version 450 - float vector = vec4(1.0 / 17.0, 9.0 / 17.0, 3.0 / 17.0, 11.0 / 17.0); - "#, - &entry_points, - ) - .unwrap(); -} - #[test] fn textures() { let mut entry_points = crate::FastHashMap::default(); @@ -274,17 +196,18 @@ fn functions() { let mut entry_points = crate::FastHashMap::default(); entry_points.insert("".to_string(), ShaderStage::Vertex); - parse_program( - r#" - # version 450 - void test1(float); - void test1(float) {} + // TODO: Add support for function prototypes + // parse_program( + // r#" + // # version 450 + // void test1(float); + // void test1(float) {} - void main() {} - "#, - &entry_points, - ) - .unwrap(); + // void main() {} + // "#, + // ShaderStage::Vertex, + // ) + // .unwrap(); parse_program( r#" @@ -292,7 +215,7 @@ fn functions() { void test2(float a) {} void test3(float a, float b) {} void test4(float, float) {} - + void main() {} "#, &entry_points, @@ -303,7 +226,7 @@ fn functions() { r#" # version 450 float test(float a) { return a; } - + void main() {} "#, &entry_points, @@ -320,103 +243,6 @@ fn functions() { &entry_points, ) .unwrap(); - - // Function overloading - parse_program( - r#" - # version 450 - float test(vec2 p); - float test(vec3 p); - float test(vec4 p); - - float test(vec2 p) { - return p.x; - } - - float test(vec3 p) { - return p.x; - } - - float test(vec4 p) { - return p.x; - } - "#, - &entry_points, - ) - .unwrap(); - - assert_eq!( - parse_program( - r#" - # version 450 - int test(vec4 p) { - return p.x; - } - - float test(vec4 p) { - return p.x; - } - "#, - &entry_points - ) - .err() - .unwrap(), - ErrorKind::SemanticError( - SourceMetadata { - start: 134, - end: 152 - }, - "Function already defined".into() - ) - ); - - println!(); - - let _program = parse_program( - r#" - # version 450 - float callee(uint q) { - return float(q); - } - - float caller() { - callee(1u); - } - "#, - &entry_points, - ) - .unwrap(); - - // Nested function call - let _program = parse_program( - r#" - # version 450 - layout(set = 0, binding = 1) uniform texture2D t_noise; - layout(set = 0, binding = 2) uniform sampler s_noise; - - void main() { - textureLod(sampler2D(t_noise, s_noise), vec2(1.0), 0); - } - "#, - &entry_points, - ) - .unwrap(); - - parse_program( - r#" - # version 450 - void fun(vec2 in_parameter, out float out_parameter) { - ivec2 _ = ivec2(in_parameter); - } - - void main() { - float a; - fun(vec2(1.0), a); - } - "#, - &entry_points, - ) - .unwrap(); } #[test] @@ -431,7 +257,7 @@ fn constants() { # version 450 const float a = 1.0; float global = a; - const float b = a; + const flat float b = a; "#, &entry_points, ) @@ -451,141 +277,29 @@ fn constants() { } ); + assert_eq!( + constants.next().unwrap().1, + &Constant { + name: Some(String::from("a")), + specialization: None, + inner: ConstantInner::Scalar { + width: 4, + value: ScalarValue::Float(1.0) + } + } + ); + + assert_eq!( + constants.next().unwrap().1, + &Constant { + name: Some(String::from("b")), + specialization: None, + inner: ConstantInner::Scalar { + width: 4, + value: ScalarValue::Float(1.0) + } + } + ); + assert!(constants.next().is_none()); } - -#[test] -fn implicit_conversions() { - let mut entry_points = crate::FastHashMap::default(); - entry_points.insert("".to_string(), ShaderStage::Vertex); - - parse_program( - r#" - # version 450 - void main() { - mat4 a = mat4(1); - float b = 1u; - float c = 1 + 2.0; - } - "#, - &entry_points, - ) - .unwrap(); -} - -#[test] -fn structs() { - let mut entry_points = crate::FastHashMap::default(); - entry_points.insert("".to_string(), ShaderStage::Fragment); - - parse_program( - r#" - # version 450 - Test { - vec4 pos; - } xx; - "#, - &entry_points, - ) - .unwrap_err(); - - parse_program( - r#" - # version 450 - struct Test { - vec4 pos; - }; - "#, - &entry_points, - ) - .unwrap(); - - parse_program( - r#" - # version 450 - const int NUM_VECS = 42; - struct Test { - vec4 vecs[NUM_VECS]; - }; - "#, - &entry_points, - ) - .unwrap(); - - parse_program( - r#" - # version 450 - struct Hello { - vec4 test; - } test() { - return Hello( vec4(1.0) ); - } - "#, - &entry_points, - ) - .unwrap(); - - parse_program( - r#" - # version 450 - struct Test {}; - "#, - &entry_points, - ) - .unwrap_err(); - - parse_program( - r#" - # version 450 - inout struct Test { - vec4 x; - }; - "#, - &entry_points, - ) - .unwrap_err(); -} - -#[test] -fn swizzles() { - let mut entry_points = crate::FastHashMap::default(); - entry_points.insert("".to_string(), ShaderStage::Fragment); - - parse_program( - r#" - # version 450 - void main() { - vec4 v = vec4(1); - v.xyz = vec3(2); - v.x = 5.0; - v.xyz.zxy.yx.xy = vec2(5.0, 1.0); - } - "#, - &entry_points, - ) - .unwrap(); - - parse_program( - r#" - # version 450 - void main() { - vec4 v = vec4(1); - v.xx = vec2(5.0); - } - "#, - &entry_points, - ) - .unwrap_err(); - - parse_program( - r#" - # version 450 - void main() { - vec3 v = vec3(1); - v.w = 2.0; - } - "#, - &entry_points, - ) - .unwrap_err(); -} diff --git a/third_party/rust/naga/src/front/glsl/token.rs b/third_party/rust/naga/src/front/glsl/token.rs index 7d01ffb77cfb..7b849d361f18 100644 --- a/third_party/rust/naga/src/front/glsl/token.rs +++ b/third_party/rust/naga/src/front/glsl/token.rs @@ -1,136 +1,8 @@ -pub use pp_rs::token::{Float, Integer, PreprocessorError}; +use std::ops::Range; -use crate::{Interpolation, Sampling, Type}; -use std::{fmt, ops::Range}; - -#[derive(Debug, Clone, Copy, Default)] +#[derive(Debug, Clone)] #[cfg_attr(test, derive(PartialEq))] -pub struct SourceMetadata { - /// Byte offset into the source where the first char starts - pub start: usize, - /// Byte offset into the source where the first char not belonging to this - /// source metadata starts - pub end: usize, -} - -impl SourceMetadata { - pub fn union(&self, other: &Self) -> Self { - SourceMetadata { - start: self.start.min(other.start), - end: self.end.max(other.end), - } - } -} - -impl From for Range { - fn from(meta: SourceMetadata) -> Self { - meta.start..meta.end - } -} - -#[derive(Debug)] -#[cfg_attr(test, derive(PartialEq))] -pub struct Token { - pub value: TokenValue, - pub meta: SourceMetadata, -} - -#[derive(Debug, PartialEq)] -pub enum TokenValue { - Unknown(PreprocessorError), - Identifier(String), - - Extension, - Version, - Pragma, - - FloatConstant(Float), - IntConstant(Integer), - BoolConstant(bool), - - Layout, - In, - Out, - InOut, - Uniform, - Buffer, - Const, - Interpolation(Interpolation), - Sampling(Sampling), - - Continue, - Break, - Return, - Discard, - - If, - Else, - Switch, - Case, - Default, - While, - Do, - For, - - Void, - Struct, - TypeName(Type), - - Assign, - AddAssign, - SubAssign, - MulAssign, - DivAssign, - ModAssign, - LeftShiftAssign, - RightShiftAssign, - AndAssign, - XorAssign, - OrAssign, - - Increment, - Decrement, - - LogicalOr, - LogicalAnd, - LogicalXor, - - LessEqual, - GreaterEqual, - Equal, - NotEqual, - - LeftShift, - RightShift, - - LeftBrace, - RightBrace, - LeftParen, - RightParen, - LeftBracket, - RightBracket, - LeftAngle, - RightAngle, - - Comma, - Semicolon, - Colon, - Dot, - Bang, - Dash, - Tilde, - Plus, - Star, - Slash, - Percent, - VerticalBar, - Caret, - Ampersand, - Question, -} - -impl fmt::Display for Token { - fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { - write!(f, "{:?}", self.value) - } +pub struct TokenMetadata { + pub line: usize, + pub chars: Range, } diff --git a/third_party/rust/naga/src/front/glsl/variables.rs b/third_party/rust/naga/src/front/glsl/variables.rs index c3fb8c7660b2..1adf2596442e 100644 --- a/third_party/rust/naga/src/front/glsl/variables.rs +++ b/third_party/rust/naga/src/front/glsl/variables.rs @@ -1,565 +1,219 @@ -use crate::{ - Binding, Block, BuiltIn, Constant, Expression, GlobalVariable, Handle, ImageClass, - Interpolation, LocalVariable, ScalarKind, StorageAccess, StorageClass, SwizzleComponent, Type, - TypeInner, VectorSize, -}; +use crate::{Expression, Handle, Type, TypeInner, VectorSize}; use super::ast::*; use super::error::ErrorKind; -use super::token::SourceMetadata; - -pub struct VarDeclaration<'a> { - pub qualifiers: &'a [(TypeQualifier, SourceMetadata)], - pub ty: Handle, - pub name: Option, - pub init: Option>, - pub meta: SourceMetadata, -} - -pub enum GlobalOrConstant { - Global(Handle), - Constant(Handle), -} +use super::token::TokenMetadata; impl Program<'_> { - pub fn lookup_variable( - &mut self, - ctx: &mut Context, - body: &mut Block, - name: &str, - ) -> Result, ErrorKind> { - if let Some(local_var) = ctx.lookup_local_var(name) { + pub fn lookup_variable(&mut self, name: &str) -> Result>, ErrorKind> { + if let Some(local_var) = self.context.lookup_local_var(name) { return Ok(Some(local_var)); } - if let Some(global_var) = ctx.lookup_global_var(name) { - return Ok(Some(global_var)); + if let Some(global_var) = self.context.lookup_global_var_exps.get(name) { + return Ok(Some(*global_var)); + } + if let Some(constant) = self.context.lookup_constant_exps.get(name) { + return Ok(Some(*constant)); } - - let mut add_builtin = |inner, builtin, mutable, prologue| { - let ty = self - .module - .types - .fetch_or_append(Type { name: None, inner }); - - let handle = self.module.global_variables.append(GlobalVariable { - name: Some(name.into()), - class: StorageClass::Private, - binding: None, - ty, - init: None, - storage_access: StorageAccess::empty(), - }); - - let idx = self.entry_args.len(); - self.entry_args.push(EntryArg { - name: None, - binding: Binding::BuiltIn(builtin), - handle, - prologue, - }); - - self.global_variables.push(( - name.into(), - GlobalLookup { - kind: GlobalLookupKind::Variable(handle), - entry_arg: Some(idx), - mutable, - }, - )); - ctx.arg_use.push(EntryArgUse::empty()); - - let expr = ctx.add_expression(Expression::GlobalVariable(handle), body); - ctx.lookup_global_var_exps.insert( - name.into(), - VariableReference { - expr, - load: true, - mutable, - entry_arg: Some(idx), - }, - ); - - Ok(ctx.lookup_global_var(name)) - }; match name { - "gl_Position" => add_builtin( - TypeInner::Vector { - size: VectorSize::Quad, - kind: ScalarKind::Float, - width: 4, - }, - BuiltIn::Position, - true, - PrologueStage::FRAGMENT, - ), - "gl_VertexIndex" => add_builtin( - TypeInner::Scalar { + "gl_Position" => { + /*let h = self.module.global_variables.append(GlobalVariable { + name: Some(name.into()), + class: StorageClass::Output, + binding: Some(Binding::BuiltIn(BuiltIn::Position)), + ty: self.module.types.fetch_or_append(Type { + name: None, + inner: TypeInner::Vector { + size: VectorSize::Quad, + kind: ScalarKind::Float, + width: 4, + }, + }), + init: None, + interpolation: None, + storage_access: StorageAccess::empty(), + }); + self.lookup_global_variables.insert(name.into(), h); + let exp = self + .context + .expressions + .append(Expression::GlobalVariable(h));*/ + let exp = self + .context + .expressions + .append(Expression::FunctionArgument(0)); //TODO + self.context.lookup_global_var_exps.insert(name.into(), exp); + + Ok(Some(exp)) + } + "gl_VertexIndex" => { + /* TODO + let h = self.module.global_variables.append(GlobalVariable { + name: Some(name.into()), + class: StorageClass::Input, + binding: Some(Binding::BuiltIn(BuiltIn::VertexIndex)), + ty: self.module.types.fetch_or_append(Type { + name: None, + inner: TypeInner::Scalar { + kind: ScalarKind::Uint, + width: 4, + }, + }), + init: None, + interpolation: None, + storage_access: StorageAccess::empty(), + }); + self.lookup_global_variables.insert(name.into(), h); + let mut expr = self + .context + .expressions + .append(Expression::GlobalVariable(h)); + expr = self.context.expressions.append(Expression::As { + expr, kind: ScalarKind::Sint, - width: 4, - }, - BuiltIn::VertexIndex, - false, - PrologueStage::VERTEX, - ), - "gl_InstanceIndex" => add_builtin( - TypeInner::Scalar { + convert: true, + }); + */ + let expr = self + .context + .expressions + .append(Expression::FunctionArgument(0)); //TODO + self.context + .lookup_global_var_exps + .insert(name.into(), expr); + + Ok(Some(expr)) + } + "gl_InstanceIndex" => { + /* TODO + let h = self.module.global_variables.append(GlobalVariable { + name: Some(name.into()), + class: StorageClass::Input, + binding: Some(Binding::BuiltIn(BuiltIn::InstanceIndex)), + ty: self.module.types.fetch_or_append(Type { + name: None, + inner: TypeInner::Scalar { + kind: ScalarKind::Uint, + width: 4, + }, + }), + init: None, + interpolation: None, + storage_access: StorageAccess::empty(), + }); + self.lookup_global_variables.insert(name.into(), h); + let mut expr = self + .context + .expressions + .append(Expression::GlobalVariable(h)); + expr = self.context.expressions.append(Expression::As { + expr, kind: ScalarKind::Sint, - width: 4, - }, - BuiltIn::InstanceIndex, - false, - PrologueStage::VERTEX, - ), - "gl_GlobalInvocationID" => add_builtin( - TypeInner::Vector { - size: VectorSize::Tri, - kind: ScalarKind::Uint, - width: 4, - }, - BuiltIn::GlobalInvocationId, - false, - PrologueStage::COMPUTE, - ), - "gl_FrontFacing" => add_builtin( - TypeInner::Scalar { - kind: ScalarKind::Bool, - width: 1, - }, - BuiltIn::FrontFacing, - false, - PrologueStage::FRAGMENT, - ), + convert: true, + }); + */ + let expr = self + .context + .expressions + .append(Expression::FunctionArgument(0)); //TODO + self.context + .lookup_global_var_exps + .insert(name.into(), expr); + + Ok(Some(expr)) + } _ => Ok(None), } } pub fn field_selection( &mut self, - ctx: &mut Context, - lhs: bool, - body: &mut Block, expression: Handle, name: &str, - meta: SourceMetadata, + meta: TokenMetadata, ) -> Result, ErrorKind> { - let (ty, is_pointer) = match *self.resolve_type(ctx, expression, meta)? { - TypeInner::Pointer { base, .. } => (&self.module.types[base].inner, true), - ref ty => (ty, false), - }; - match *ty { + match *self.resolve_type(expression)? { TypeInner::Struct { ref members, .. } => { let index = members .iter() .position(|m| m.name == Some(name.into())) .ok_or_else(|| ErrorKind::UnknownField(meta, name.into()))?; - Ok(ctx.add_expression( - Expression::AccessIndex { - base: expression, - index: index as u32, - }, - body, - )) + Ok(self.context.expressions.append(Expression::AccessIndex { + base: expression, + index: index as u32, + })) } // swizzles (xyzw, rgba, stpq) - TypeInner::Vector { size, .. } => { + TypeInner::Vector { size, kind, width } => { let check_swizzle_components = |comps: &str| { name.chars() .map(|c| { comps .find(c) - .filter(|i| *i < size as usize) - .map(|i| SwizzleComponent::from_index(i as u32)) + .and_then(|i| if i < size as usize { Some(i) } else { None }) + }) + .fold(Some(Vec::::new()), |acc, cur| { + cur.and_then(|i| { + acc.map(|mut v| { + v.push(i); + v + }) + }) }) - .collect::>>() }; - let components = check_swizzle_components("xyzw") + let indices = check_swizzle_components("xyzw") .or_else(|| check_swizzle_components("rgba")) .or_else(|| check_swizzle_components("stpq")); - if let Some(components) = components { - if lhs { - let not_unique = (1..components.len()) - .any(|i| components[i..].contains(&components[i - 1])); - if not_unique { - return Err(ErrorKind::SemanticError( - meta, - format!( - "swizzle cannot have duplicate components in left-hand-side expression for \"{:?}\"", - name - ) - .into(), - )); - } - } - - let mut pattern = [SwizzleComponent::X; 4]; - for (pat, component) in pattern.iter_mut().zip(&components) { - *pat = *component; - } - - // flatten nested swizzles (vec.zyx.xy.x => vec.z) - let mut expression = expression; - if let Expression::Swizzle { - size: _, - vector, - pattern: ref src_pattern, - } = *ctx.get_expression(expression) - { - expression = vector; - for pat in &mut pattern { - *pat = src_pattern[pat.index() as usize]; - } - } - - let size = match components.len() { - 1 => { - // only single element swizzle, like pos.y, just return that component. - if lhs { - // Because of possible nested swizzles, like pos.xy.x, we have to unwrap the potential load expr. - if let Expression::Load { ref pointer } = - *ctx.get_expression(expression) - { - expression = *pointer; - } - } - return Ok(ctx.add_expression( - Expression::AccessIndex { - base: expression, - index: pattern[0].index(), + if let Some(v) = indices { + let components: Vec> = v + .iter() + .map(|idx| { + self.context.expressions.append(Expression::AccessIndex { + base: expression, + index: *idx as u32, + }) + }) + .collect(); + if components.len() == 1 { + // only single element swizzle, like pos.y, just return that component + Ok(components[0]) + } else { + Ok(self.context.expressions.append(Expression::Compose { + ty: self.module.types.fetch_or_append(Type { + name: None, + inner: TypeInner::Vector { + kind, + width, + size: match components.len() { + 2 => VectorSize::Bi, + 3 => VectorSize::Tri, + 4 => VectorSize::Quad, + _ => { + return Err(ErrorKind::SemanticError( + format!( + "Bad swizzle size for \"{:?}\": {:?}", + name, v + ) + .into(), + )); + } + }, }, - body, - )); - } - 2 => VectorSize::Bi, - 3 => VectorSize::Tri, - 4 => VectorSize::Quad, - _ => { - return Err(ErrorKind::SemanticError( - meta, - format!("Bad swizzle size for \"{:?}\"", name).into(), - )); - } - }; - - if is_pointer { - // NOTE: for lhs expression, this extra load ends up as an unused expr, because the - // assignment will extract the pointer and use it directly anyway. Unfortunately we - // need it for validation to pass, as swizzles cannot operate on pointer values. - expression = ctx.add_expression( - Expression::Load { - pointer: expression, - }, - body, - ); + }), + components, + })) } - - Ok(ctx.add_expression( - Expression::Swizzle { - size, - vector: expression, - pattern, - }, - body, - )) } else { Err(ErrorKind::SemanticError( - meta, format!("Invalid swizzle for vector \"{}\"", name).into(), )) } } _ => Err(ErrorKind::SemanticError( - meta, format!("Can't lookup field on this type \"{}\"", name).into(), )), } } - - pub fn add_global_var( - &mut self, - VarDeclaration { - qualifiers, - ty, - name, - init, - meta, - }: VarDeclaration, - ) -> Result { - let mut storage = StorageQualifier::StorageClass(StorageClass::Private); - let mut interpolation = None; - let mut binding = None; - let mut location = None; - let mut sampling = None; - let mut layout = None; - - for &(ref qualifier, meta) in qualifiers { - match *qualifier { - TypeQualifier::StorageQualifier(s) => { - if StorageQualifier::StorageClass(StorageClass::PushConstant) == storage - && s == StorageQualifier::StorageClass(StorageClass::Uniform) - { - // Ignore the Uniform qualifier if the class was already set to PushConstant - continue; - } else if StorageQualifier::StorageClass(StorageClass::Private) != storage { - return Err(ErrorKind::SemanticError( - meta, - "Cannot use more than one storage qualifier per declaration".into(), - )); - } - - storage = s; - } - TypeQualifier::Interpolation(i) => { - if interpolation.is_some() { - return Err(ErrorKind::SemanticError( - meta, - "Cannot use more than one interpolation qualifier per declaration" - .into(), - )); - } - - interpolation = Some(i); - } - TypeQualifier::ResourceBinding(ref r) => { - if binding.is_some() { - return Err(ErrorKind::SemanticError( - meta, - "Cannot use more than one binding per declaration".into(), - )); - } - - binding = Some(r.clone()); - } - TypeQualifier::Location(l) => { - if location.is_some() { - return Err(ErrorKind::SemanticError( - meta, - "Cannot use more than one binding per declaration".into(), - )); - } - - location = Some(l); - } - TypeQualifier::Sampling(s) => { - if sampling.is_some() { - return Err(ErrorKind::SemanticError( - meta, - "Cannot use more than one sampling qualifier per declaration".into(), - )); - } - - sampling = Some(s); - } - TypeQualifier::Layout(ref l) => { - if layout.is_some() { - return Err(ErrorKind::SemanticError( - meta, - "Cannot use more than one layout qualifier per declaration".into(), - )); - } - - layout = Some(l); - } - _ => { - return Err(ErrorKind::SemanticError( - meta, - "Qualifier not supported in globals".into(), - )); - } - } - } - - if binding.is_some() && storage != StorageQualifier::StorageClass(StorageClass::Uniform) { - match storage { - StorageQualifier::StorageClass(StorageClass::PushConstant) - | StorageQualifier::StorageClass(StorageClass::Uniform) - | StorageQualifier::StorageClass(StorageClass::Storage) => {} - _ => { - return Err(ErrorKind::SemanticError( - meta, - "binding requires uniform or buffer storage qualifier".into(), - )) - } - } - } - - if (sampling.is_some() || interpolation.is_some()) && location.is_none() { - return Err(ErrorKind::SemanticError( - meta, - "Sampling and interpolation qualifiers can only be used in in/out variables".into(), - )); - } - - if let Some(location) = location { - let input = storage == StorageQualifier::Input; - let prologue = if input { - PrologueStage::all() - } else { - PrologueStage::empty() - }; - let interpolation = self.module.types[ty].inner.scalar_kind().map(|kind| { - if let ScalarKind::Float = kind { - Interpolation::Perspective - } else { - Interpolation::Flat - } - }); - - let handle = self.module.global_variables.append(GlobalVariable { - name: name.clone(), - class: StorageClass::Private, - binding: None, - ty, - init, - storage_access: StorageAccess::empty(), - }); - - let idx = self.entry_args.len(); - self.entry_args.push(EntryArg { - name: name.clone(), - binding: Binding::Location { - location, - interpolation, - sampling, - }, - handle, - prologue, - }); - - if let Some(name) = name { - self.global_variables.push(( - name, - GlobalLookup { - kind: GlobalLookupKind::Variable(handle), - entry_arg: Some(idx), - mutable: !input, - }, - )); - } - - return Ok(GlobalOrConstant::Global(handle)); - } else if let StorageQualifier::Const = storage { - let init = init.ok_or_else(|| { - ErrorKind::SemanticError(meta, "const values must have an initializer".into()) - })?; - if let Some(name) = name { - self.global_variables.push(( - name, - GlobalLookup { - kind: GlobalLookupKind::Constant(init), - entry_arg: None, - mutable: false, - }, - )); - } - return Ok(GlobalOrConstant::Constant(init)); - } - - let (class, storage_access) = match self.module.types[ty].inner { - TypeInner::Image { class, .. } => ( - StorageClass::Handle, - if let ImageClass::Storage(_) = class { - // TODO: Add support for qualifiers such as readonly, - // writeonly and readwrite - StorageAccess::all() - } else { - StorageAccess::empty() - }, - ), - TypeInner::Sampler { .. } => (StorageClass::Handle, StorageAccess::empty()), - _ => { - if let StorageQualifier::StorageClass(StorageClass::Storage) = storage { - (StorageClass::Storage, StorageAccess::all()) - } else { - ( - match storage { - StorageQualifier::StorageClass(class) => class, - _ => StorageClass::Private, - }, - StorageAccess::empty(), - ) - } - } - }; - - let handle = self.module.global_variables.append(GlobalVariable { - name: name.clone(), - class, - binding, - ty, - init, - storage_access, - }); - - if let Some(name) = name { - self.global_variables.push(( - name, - GlobalLookup { - kind: GlobalLookupKind::Variable(handle), - entry_arg: None, - mutable: true, - }, - )); - } - - Ok(GlobalOrConstant::Global(handle)) - } - - pub fn add_local_var( - &mut self, - ctx: &mut Context, - body: &mut Block, - #[cfg_attr(not(feature = "glsl-validate"), allow(unused_variables))] - VarDeclaration { - qualifiers, - ty, - name, - init, - meta, - }: VarDeclaration, - ) -> Result, ErrorKind> { - #[cfg(feature = "glsl-validate")] - if let Some(ref name) = name { - if ctx.lookup_local_var_current_scope(name).is_some() { - return Err(ErrorKind::VariableAlreadyDeclared(meta, name.clone())); - } - } - - let mut mutable = true; - - for &(ref qualifier, meta) in qualifiers { - match *qualifier { - TypeQualifier::StorageQualifier(StorageQualifier::Const) => { - if !mutable { - return Err(ErrorKind::SemanticError( - meta, - "Cannot use more than one constant qualifier per declaration".into(), - )); - } - - mutable = false; - } - _ => { - return Err(ErrorKind::SemanticError( - meta, - "Qualifier not supported in locals".into(), - )); - } - } - } - - let handle = ctx.locals.append(LocalVariable { - name: name.clone(), - ty, - init, - }); - let expr = ctx.add_expression(Expression::LocalVariable(handle), body); - - if let Some(name) = name { - ctx.add_local_var(name, expr, mutable); - } - - Ok(expr) - } } diff --git a/third_party/rust/naga/src/front/mod.rs b/third_party/rust/naga/src/front/mod.rs index 45c93143ec9e..aa39bc259592 100644 --- a/third_party/rust/naga/src/front/mod.rs +++ b/third_party/rust/naga/src/front/mod.rs @@ -14,7 +14,7 @@ use crate::{ /// Helper class to emit expressions #[allow(dead_code)] -#[derive(Default, Debug)] +#[derive(Default)] struct Emitter { start_len: Option, } @@ -69,39 +69,16 @@ impl Typifier { self.resolutions[expr_handle.index()].inner_with(types) } - pub fn get_handle( - &mut self, - expr_handle: Handle, - types: &mut Arena, - ) -> Handle { - let mut dummy = TypeResolution::Value(crate::TypeInner::Sampler { comparison: false }); - let res = &mut self.resolutions[expr_handle.index()]; - - std::mem::swap(&mut dummy, res); - - let v = match dummy { - TypeResolution::Handle(h) => h, - TypeResolution::Value(inner) => { - let h = types.fetch_or_append(crate::Type { name: None, inner }); - dummy = TypeResolution::Handle(h); - h - } - }; - - std::mem::swap(&mut dummy, res); - - v - } - pub fn grow( &mut self, expr_handle: Handle, expressions: &Arena, + types: &mut Arena, ctx: &ResolveContext, ) -> Result<(), ResolveError> { if self.resolutions.len() <= expr_handle.index() { for (eh, expr) in expressions.iter().skip(self.resolutions.len()) { - let resolution = ctx.resolve(expr, |h| &self.resolutions[h.index()])?; + let resolution = ctx.resolve(expr, types, |h| &self.resolutions[h.index()])?; log::debug!("Resolving {:?} = {:?} : {:?}", eh, expr, resolution); self.resolutions.push(resolution); } diff --git a/third_party/rust/naga/src/front/spv/error.rs b/third_party/rust/naga/src/front/spv/error.rs index 688127a2714d..1ae8b23137d1 100644 --- a/third_party/rust/naga/src/front/spv/error.rs +++ b/third_party/rust/naga/src/front/spv/error.rs @@ -108,4 +108,6 @@ pub enum Error { #[error("invalid barrier memory semantics %{0}")] InvalidBarrierMemorySemantics(spirv::Word), // incomplete implementation errors + #[error("unsupported matrix stride {0}")] + UnsupportedMatrixStride(spirv::Word), } diff --git a/third_party/rust/naga/src/front/spv/flow.rs b/third_party/rust/naga/src/front/spv/flow.rs index c7c8b82af661..19db91ffda56 100644 --- a/third_party/rust/naga/src/front/spv/flow.rs +++ b/third_party/rust/naga/src/front/spv/flow.rs @@ -521,7 +521,7 @@ impl FlowGraph { lookup_expression: &FastHashMap, ) { for node_index in self.flow.node_indices() { - let phis = std::mem::take(&mut self.flow[node_index].phis); + let phis = std::mem::replace(&mut self.flow[node_index].phis, Vec::new()); for phi in phis.iter() { for &(variable_id, parent_id) in phi.variables.iter() { let variable = &lookup_expression[&variable_id]; @@ -612,53 +612,24 @@ impl FlowGraph { intended_merge }; - let mut result: crate::Block = std::mem::take(&mut self.flow[node_index].block); + let mut result: crate::Block = + std::mem::replace(&mut self.flow[node_index].block, Vec::new()); let mut accept_stop_nodes = stop_nodes.clone(); accept_stop_nodes.insert(merge_node_index); accept_stop_nodes.insert(intended_merge); accept_stop_nodes.insert(then_end_index); - let mut reject_stop_nodes = stop_nodes.clone(); reject_stop_nodes.insert(merge_node_index); reject_stop_nodes.insert(intended_merge); reject_stop_nodes.insert(else_end_index); - let mut accept = - self.convert_to_naga_traverse(true_node_index, accept_stop_nodes)?; - let mut reject = - self.convert_to_naga_traverse(false_node_index, reject_stop_nodes)?; - - // If the true/false block of a header is breaking from switch or loop we add a break statement after its statements - for &mut (target_index, ref mut statements) in [ - (true_node_index, &mut accept), - (false_node_index, &mut reject), - ] - .iter_mut() - { - if let Some(ControlFlowNodeType::Break) = self.flow[target_index].ty { - let edge = *self - .flow - .edges_directed(target_index, Direction::Outgoing) - .next() - .unwrap() - .weight(); - if edge == ControlFlowEdgeType::SwitchBreak - || edge == ControlFlowEdgeType::LoopBreak - { - // Do not add break if already has one as the last statement - if let Some(&crate::Statement::Break) = statements.last() { - } else { - statements.push(crate::Statement::Break); - } - } - } - } - result.push(crate::Statement::If { condition, - accept, - reject, + accept: self + .convert_to_naga_traverse(true_node_index, accept_stop_nodes)?, + reject: self + .convert_to_naga_traverse(false_node_index, reject_stop_nodes)?, }); result.extend(self.convert_to_naga_traverse(merge_node_index, stop_nodes)?); @@ -672,7 +643,8 @@ impl FlowGraph { } => { let merge_node_index = self.block_to_node[&self.flow[node_index].merge.unwrap().merge_block_id]; - let mut result: crate::Block = std::mem::take(&mut self.flow[node_index].block); + let mut result: crate::Block = + std::mem::replace(&mut self.flow[node_index].block, Vec::new()); let mut cases = Vec::with_capacity(targets.len()); let mut stop_nodes_cases = stop_nodes.clone(); @@ -729,10 +701,11 @@ impl FlowGraph { .unwrap() .target(); - std::mem::take(&mut self.flow[continue_edge].block) + std::mem::replace(&mut self.flow[continue_edge].block, Vec::new()) }; - let mut body: crate::Block = std::mem::take(&mut self.flow[node_index].block); + let mut body: crate::Block = + std::mem::replace(&mut self.flow[node_index].block, Vec::new()); let mut stop_nodes_merge = stop_nodes.clone(); stop_nodes_merge.insert(merge_node_index); @@ -777,7 +750,8 @@ impl FlowGraph { Ok(result) } Some(ControlFlowNodeType::Break) => { - let mut result: crate::Block = std::mem::take(&mut self.flow[node_index].block); + let mut result: crate::Block = + std::mem::replace(&mut self.flow[node_index].block, Vec::new()); match self.flow[node_index].terminator { Terminator::BranchConditional { condition, @@ -823,22 +797,22 @@ impl FlowGraph { Terminator::Branch { target_id } => { let target_index = self.block_to_node[&target_id]; - let edge = - self.flow[self.flow.find_edge(node_index, target_index).unwrap()]; + let edge = self.flow[self.flow.find_edge(node_index, target_index).unwrap()]; if edge == ControlFlowEdgeType::LoopBreak { result.push(crate::Statement::Break); } - } + }, _ => return Err(Error::InvalidTerminator), }; Ok(result) } - Some(ControlFlowNodeType::Continue) | Some(ControlFlowNodeType::Back) => { - Ok(std::mem::take(&mut self.flow[node_index].block)) - } + Some(ControlFlowNodeType::Continue) | Some(ControlFlowNodeType::Back) => Ok( + std::mem::replace(&mut self.flow[node_index].block, Vec::new()), + ), Some(ControlFlowNodeType::Kill) => { - let mut result: crate::Block = std::mem::take(&mut self.flow[node_index].block); + let mut result: crate::Block = + std::mem::replace(&mut self.flow[node_index].block, Vec::new()); result.push(crate::Statement::Kill); Ok(result) } @@ -847,19 +821,24 @@ impl FlowGraph { Terminator::Return { value } => value, _ => return Err(Error::InvalidTerminator), }; - let mut result: crate::Block = std::mem::take(&mut self.flow[node_index].block); + let mut result: crate::Block = + std::mem::replace(&mut self.flow[node_index].block, Vec::new()); result.push(crate::Statement::Return { value }); Ok(result) } Some(ControlFlowNodeType::Merge) | None => match self.flow[node_index].terminator { Terminator::Branch { target_id } => { - let mut result: crate::Block = std::mem::take(&mut self.flow[node_index].block); + let mut result: crate::Block = + std::mem::replace(&mut self.flow[node_index].block, Vec::new()); result.extend( self.convert_to_naga_traverse(self.block_to_node[&target_id], stop_nodes)?, ); Ok(result) } - _ => Ok(std::mem::take(&mut self.flow[node_index].block)), + _ => Ok(std::mem::replace( + &mut self.flow[node_index].block, + Vec::new(), + )), }, } } @@ -872,32 +851,21 @@ impl FlowGraph { for node_index in self.flow.node_indices() { let node = &self.flow[node_index]; - - let node_name = match node.ty { - Some(ControlFlowNodeType::Header) => { - if self.constructs[node.construct].ty == ConstructType::Case { - "Switch" - } else { - "If" - } - } - Some(ControlFlowNodeType::Loop) => "Loop", - Some(ControlFlowNodeType::Merge) => "", - Some(ControlFlowNodeType::Break) => "Break", - Some(ControlFlowNodeType::Continue) => "Continue", - Some(ControlFlowNodeType::Back) => "Back", - Some(ControlFlowNodeType::Kill) => "Kill", - Some(ControlFlowNodeType::Return) => "Return", - None => "Unlabeled", + let shape = if self.constructs[node.construct].ty == ConstructType::Case + && node.ty != Some(ControlFlowNodeType::Header) + { + "point" + } else { + "ellipse" }; - writeln!( output, - "{} [ label = \"%{}({}) {}\" shape=ellipse ]", + "{} [ label = \"%{}({}) {:?}\" shape={} ]", node_index.index(), node.id, node_index.index(), - node_name, + node.ty, + shape )?; } diff --git a/third_party/rust/naga/src/front/spv/function.rs b/third_party/rust/naga/src/front/spv/function.rs index 7c055465431d..449146e48756 100644 --- a/third_party/rust/naga/src/front/spv/function.rs +++ b/third_party/rust/naga/src/front/spv/function.rs @@ -28,7 +28,7 @@ pub enum Terminator { /// /// switch(SELECTOR) { /// case TARGET_LITERAL#: { - /// TARGET_BLOCK# + /// TARGET_BLOCK# /// } /// default: { /// DEFAULT @@ -90,7 +90,6 @@ impl> super::Parser { }, local_variables: Arena::new(), expressions: self.make_expression_storage(), - named_expressions: crate::FastHashMap::default(), body: Vec::new(), } }; @@ -195,7 +194,6 @@ impl> super::Parser { result: None, local_variables: Arena::new(), expressions: Arena::new(), - named_expressions: crate::FastHashMap::default(), body: Vec::new(), }; @@ -355,7 +353,9 @@ impl> super::Parser { let ty = module.types.append(crate::Type { name: None, inner: crate::TypeInner::Struct { - top_level: false, + level: crate::StructLevel::Normal { + alignment: crate::Alignment::new(1).unwrap(), + }, members, span: 0xFFFF, // shouldn't matter }, diff --git a/third_party/rust/naga/src/front/spv/mod.rs b/third_party/rust/naga/src/front/spv/mod.rs index 0ad9a002e37f..7b62bce56194 100644 --- a/third_party/rust/naga/src/front/spv/mod.rs +++ b/third_party/rust/naga/src/front/spv/mod.rs @@ -40,7 +40,6 @@ use function::*; use crate::{ arena::{Arena, Handle}, - proc::{Alignment, Layouter}, FastHashMap, }; @@ -193,6 +192,28 @@ impl crate::ImageDimension { } } +//TODO: should this be shared with `crate::proc::wgsl::layout` logic? +fn get_alignment(ty: Handle, arena: &Arena) -> crate::Alignment { + use crate::TypeInner as Ti; + match arena[ty].inner { + Ti::Scalar { width, .. } => crate::Alignment::new(width as u32).unwrap(), + Ti::Vector { size, width, .. } + | Ti::Matrix { + rows: size, width, .. + } => { + let count = if size >= crate::VectorSize::Tri { 4 } else { 2 }; + crate::Alignment::new((count * width) as u32).unwrap() + } + Ti::Pointer { .. } | Ti::ValuePointer { .. } => crate::Alignment::new(1).unwrap(), + Ti::Array { stride, .. } => crate::Alignment::new(stride).unwrap(), + Ti::Struct { ref level, .. } => match *level { + crate::StructLevel::Root => crate::Alignment::new(1).unwrap(), + crate::StructLevel::Normal { alignment } => alignment, + }, + Ti::Image { .. } | Ti::Sampler { .. } => crate::Alignment::new(1).unwrap(), + } +} + type MemberIndex = u32; #[derive(Clone, Debug, Default, PartialEq)] @@ -374,7 +395,6 @@ impl Default for Options { pub struct Parser { data: I, state: ModuleState, - layouter: Layouter, temp_bytes: Vec, ext_glsl_id: Option, future_decor: FastHashMap, @@ -412,7 +432,6 @@ impl> Parser { Parser { data, state: ModuleState::Empty, - layouter: Layouter::default(), temp_bytes: Vec::new(), ext_glsl_id: None, future_decor: FastHashMap::default(), @@ -905,7 +924,7 @@ impl> Parser { log::trace!("\t\t\tlooking up expr {:?}", base_id); let mut acex = { // the base type has to be a pointer, - // so we dereference it here for the traversal + // so we derefernce it here for the traversal let lexp = self.lookup_expression.lookup(base_id)?; let lty = self.lookup_type.lookup(lexp.type_id)?; AccessExpression { @@ -1478,11 +1497,9 @@ impl> Parser { } Op::ImageWrite => { let extra = inst.expect_at_least(4)?; - block.extend(emitter.finish(expressions)); let stmt = self.parse_image_write(extra, type_arena, global_arena, expressions)?; block.push(stmt); - emitter.start(expressions); } Op::ImageFetch | Op::ImageRead => { let extra = inst.expect_at_least(5)?; @@ -2215,7 +2232,7 @@ impl> Parser { } => { if let [S::Break] = reject[..] { // uplift "accept" into the parent - let extracted = mem::take(accept); + let extracted = mem::replace(accept, Vec::new()); statements.splice(i + 1..i + 1, extracted.into_iter()); } else { self.patch_statements(reject)?; @@ -2229,7 +2246,7 @@ impl> Parser { } => { if cases.is_empty() { // uplift "default" into the parent - let extracted = mem::take(default); + let extracted = mem::replace(default, Vec::new()); statements.splice(i + 1..i + 1, extracted.into_iter()); } else { for case in cases.iter_mut() { @@ -2302,7 +2319,6 @@ impl> Parser { self.index_constants.push(handle); } - self.layouter.clear(); self.dummy_functions = Arena::new(); self.lookup_function.clear(); self.function_call_graph.clear(); @@ -2930,11 +2946,7 @@ impl> Parser { let parent_decor = self.future_decor.remove(&id); let block_decor = parent_decor.as_ref().and_then(|decor| decor.block.clone()); - self.layouter - .update(&module.types, &module.constants) - .unwrap(); - - let mut struct_alignment = Alignment::new(1).unwrap(); + let mut struct_alignment = crate::Alignment::new(1).unwrap(); let mut members = Vec::::with_capacity(inst.wc as usize - 2); let mut member_lookups = Vec::with_capacity(members.capacity()); let mut storage_access = crate::StorageAccess::empty(); @@ -2965,30 +2977,20 @@ impl> Parser { let offset = decor.offset.unwrap_or(0); if let crate::TypeInner::Matrix { - columns, + columns: _, rows, width, } = module.types[ty].inner { if let Some(stride) = decor.matrix_stride { - let rounded_rows = if rows > crate::VectorSize::Bi { - 4 - } else { - rows as u32 - }; - if stride.get() != rounded_rows * (width as u32) { - log::warn!( - "Unexpected matrix stride {} for an {}x{} matrix with scalar width={}", - stride.get(), - columns as u8, - rows as u8, - width, - ); + if stride.get() != (rows as u32) * (width as u32) { + return Err(Error::UnsupportedMatrixStride(stride.get())); } } } - struct_alignment = struct_alignment.max(self.layouter[ty].alignment); + let alignment = get_alignment(ty, &module.types); + struct_alignment = struct_alignment.max(alignment); members.push(crate::StructMember { name: decor.name, @@ -2999,7 +3001,12 @@ impl> Parser { } let inner = crate::TypeInner::Struct { - top_level: block_decor.is_some(), + level: match block_decor { + Some(_) => crate::StructLevel::Root, + None => crate::StructLevel::Normal { + alignment: struct_alignment, + }, + }, span: match members.last() { Some(member) => { let end = member.offset + module.types[member.ty].inner.span(&module.constants); diff --git a/third_party/rust/naga/src/front/wgsl/conv.rs b/third_party/rust/naga/src/front/wgsl/conv.rs index deb9643df948..f9b79290d1cf 100644 --- a/third_party/rust/naga/src/front/wgsl/conv.rs +++ b/third_party/rust/naga/src/front/wgsl/conv.rs @@ -1,17 +1,16 @@ -use super::{Error, Span}; +use super::Error; -pub fn map_storage_class(word: &str, span: Span) -> Result> { +pub fn map_storage_class(word: &str) -> Result> { match word { "private" => Ok(crate::StorageClass::Private), - "workgroup" => Ok(crate::StorageClass::WorkGroup), "uniform" => Ok(crate::StorageClass::Uniform), "storage" => Ok(crate::StorageClass::Storage), "push_constant" => Ok(crate::StorageClass::PushConstant), - _ => Err(Error::UnknownStorageClass(span)), + _ => Err(Error::UnknownStorageClass(word)), } } -pub fn map_built_in(word: &str, span: Span) -> Result> { +pub fn map_built_in(word: &str) -> Result> { Ok(match word { "position" => crate::BuiltIn::Position, // vertex @@ -28,37 +27,37 @@ pub fn map_built_in(word: &str, span: Span) -> Result> "local_invocation_index" => crate::BuiltIn::LocalInvocationIndex, "workgroup_id" => crate::BuiltIn::WorkGroupId, "workgroup_size" => crate::BuiltIn::WorkGroupSize, - _ => return Err(Error::UnknownBuiltin(span)), + _ => return Err(Error::UnknownBuiltin(word)), }) } -pub fn map_shader_stage(word: &str, span: Span) -> Result> { +pub fn map_shader_stage(word: &str) -> Result> { match word { "vertex" => Ok(crate::ShaderStage::Vertex), "fragment" => Ok(crate::ShaderStage::Fragment), "compute" => Ok(crate::ShaderStage::Compute), - _ => Err(Error::UnknownShaderStage(span)), + _ => Err(Error::UnknownShaderStage(word)), } } -pub fn map_interpolation(word: &str, span: Span) -> Result> { +pub fn map_interpolation(word: &str) -> Result> { match word { "linear" => Ok(crate::Interpolation::Linear), "flat" => Ok(crate::Interpolation::Flat), "perspective" => Ok(crate::Interpolation::Perspective), - _ => Err(Error::UnknownAttribute(span)), + _ => Err(Error::UnknownAttribute(word)), } } -pub fn map_sampling(word: &str, span: Span) -> Result> { +pub fn map_sampling(word: &str) -> Result> { match word { "centroid" => Ok(crate::Sampling::Centroid), "sample" => Ok(crate::Sampling::Sample), - _ => Err(Error::UnknownAttribute(span)), + _ => Err(Error::UnknownAttribute(word)), } } -pub fn map_storage_format(word: &str, span: Span) -> Result> { +pub fn map_storage_format(word: &str) -> Result> { use crate::StorageFormat as Sf; Ok(match word { "r8unorm" => Sf::R8Unorm, @@ -93,7 +92,7 @@ pub fn map_storage_format(word: &str, span: Span) -> Result Sf::Rgba32Uint, "rgba32sint" => Sf::Rgba32Sint, "rgba32float" => Sf::Rgba32Float, - _ => return Err(Error::UnknownStorageFormat(span)), + _ => return Err(Error::UnknownStorageFormat(word)), }) } @@ -119,7 +118,7 @@ pub fn map_derivative_axis(word: &str) -> Option { match word { "dpdx" => Some(crate::DerivativeAxis::X), "dpdy" => Some(crate::DerivativeAxis::Y), - "fwidth" => Some(crate::DerivativeAxis::Width), + "dwidth" => Some(crate::DerivativeAxis::Width), _ => None, } } @@ -196,15 +195,12 @@ pub fn map_standard_fun(word: &str) -> Option { }) } -pub fn map_conservative_depth( - word: &str, - span: Span, -) -> Result> { +pub fn map_conservative_depth(word: &str) -> Result> { use crate::ConservativeDepth as Cd; match word { "greater_equal" => Ok(Cd::GreaterEqual), "less_equal" => Ok(Cd::LessEqual), "unchanged" => Ok(Cd::Unchanged), - _ => Err(Error::UnknownConservativeDepth(span)), + _ => Err(Error::UnknownConservativeDepth(word)), } } diff --git a/third_party/rust/naga/src/proc/layouter.rs b/third_party/rust/naga/src/front/wgsl/layout.rs similarity index 58% rename from third_party/rust/naga/src/proc/layouter.rs rename to third_party/rust/naga/src/front/wgsl/layout.rs index d6c67e0aa42f..e17d5548e1d9 100644 --- a/third_party/rust/naga/src/proc/layouter.rs +++ b/third_party/rust/naga/src/front/wgsl/layout.rs @@ -1,15 +1,13 @@ use crate::arena::{Arena, Handle}; use std::{num::NonZeroU32, ops}; -pub type Alignment = NonZeroU32; - /// Alignment information for a type. #[derive(Clone, Copy, Debug, Hash, PartialEq)] #[cfg_attr(feature = "serialize", derive(serde::Serialize))] #[cfg_attr(feature = "deserialize", derive(serde::Deserialize))] pub struct TypeLayout { pub size: u32, - pub alignment: Alignment, + pub alignment: crate::Alignment, } /// Helper processor that derives the sizes of all types. @@ -22,23 +20,12 @@ pub struct Layouter { layouts: Vec, } -impl ops::Index> for Layouter { - type Output = TypeLayout; - fn index(&self, handle: Handle) -> &TypeLayout { - &self.layouts[handle.index()] - } -} - -#[derive(Clone, Copy, Debug, PartialEq, thiserror::Error)] -#[error("Base type {0:?} is out of bounds")] -pub struct InvalidBaseType(pub Handle); - impl Layouter { pub fn clear(&mut self) { self.layouts.clear(); } - pub fn round_up(alignment: Alignment, offset: u32) -> u32 { + pub fn round_up(alignment: crate::Alignment, offset: u32) -> u32 { match offset & (alignment.get() - 1) { 0 => offset, other => offset + alignment.get() - other, @@ -49,9 +36,9 @@ impl Layouter { &self, offset: u32, ty: Handle, - align: Option, + align: Option, size: Option, - ) -> (ops::Range, Alignment) { + ) -> (ops::Range, crate::Alignment) { let layout = self.layouts[ty.index()]; let alignment = align.unwrap_or(layout.alignment); let start = Self::round_up(alignment, offset); @@ -62,19 +49,14 @@ impl Layouter { (start..start + span, alignment) } - pub fn update( - &mut self, - types: &Arena, - constants: &Arena, - ) -> Result<(), InvalidBaseType> { + pub fn update(&mut self, types: &Arena, constants: &Arena) { use crate::TypeInner as Ti; - - for (ty_handle, ty) in types.iter().skip(self.layouts.len()) { + for (_, ty) in types.iter().skip(self.layouts.len()) { let size = ty.inner.span(constants); let layout = match ty.inner { Ti::Scalar { width, .. } => TypeLayout { size, - alignment: Alignment::new(width as u32).unwrap(), + alignment: crate::Alignment::new(width as u32).unwrap(), }, Ti::Vector { size: vec_size, @@ -88,7 +70,7 @@ impl Layouter { } else { 2 }; - Alignment::new((count * width) as u32).unwrap() + crate::Alignment::new((count * width) as u32).unwrap() }, }, Ti::Matrix { @@ -99,52 +81,35 @@ impl Layouter { size, alignment: { let count = if rows >= crate::VectorSize::Tri { 4 } else { 2 }; - Alignment::new((count * width) as u32).unwrap() + crate::Alignment::new((count * width) as u32).unwrap() }, }, Ti::Pointer { .. } | Ti::ValuePointer { .. } => TypeLayout { size, - alignment: Alignment::new(1).unwrap(), + alignment: crate::Alignment::new(1).unwrap(), }, - Ti::Array { - base, - stride: _, - size: _, - } => TypeLayout { + Ti::Array { stride, .. } => TypeLayout { size, - alignment: if base < ty_handle { - self[base].alignment - } else { - return Err(InvalidBaseType(base)); - }, + alignment: crate::Alignment::new(stride).unwrap(), }, Ti::Struct { - top_level: _, + ref level, + members: _, span, - ref members, - } => { - let mut alignment = Alignment::new(1).unwrap(); - for member in members { - alignment = if member.ty < ty_handle { - alignment.max(self[member.ty].alignment) - } else { - return Err(InvalidBaseType(member.ty)); - }; - } - TypeLayout { - size: span, - alignment, - } - } + } => TypeLayout { + size: span, + alignment: match *level { + crate::StructLevel::Root => crate::Alignment::new(1).unwrap(), + crate::StructLevel::Normal { alignment } => alignment, + }, + }, Ti::Image { .. } | Ti::Sampler { .. } => TypeLayout { size, - alignment: Alignment::new(1).unwrap(), + alignment: crate::Alignment::new(1).unwrap(), }, }; debug_assert!(ty.inner.span(constants) <= layout.size); self.layouts.push(layout); } - - Ok(()) } } diff --git a/third_party/rust/naga/src/front/wgsl/lexer.rs b/third_party/rust/naga/src/front/wgsl/lexer.rs index 015b7b99d702..a6a5eead3982 100644 --- a/third_party/rust/naga/src/front/wgsl/lexer.rs +++ b/third_party/rust/naga/src/front/wgsl/lexer.rs @@ -1,4 +1,4 @@ -use super::{conv, Error, ExpectedToken, Span, Token, TokenSpan}; +use super::{conv, Error, Span, Token, TokenSpan}; fn _consume_str<'a>(input: &'a str, what: &str) -> Option<&'a str> { if input.starts_with(what) { @@ -171,25 +171,6 @@ impl<'a> Lexer<'a> { self.source.len() - self.input.len()..self.source.len() } - /// Calls the function with a lexer and returns the result of the function as well as the span for everything the function parsed - /// - /// # Examples - /// ```ignore - /// let lexer = Lexer::new("5"); - /// let (value, span) = lexer.capture_span(Lexer::next_uint_literal); - /// assert_eq!(value, 5); - /// ``` - #[inline] - pub fn capture_span( - &mut self, - inner: impl FnOnce(&mut Self) -> Result, - ) -> Result<(T, Span), E> { - let start = self.current_byte_offset(); - let res = inner(self)?; - let end = self.current_byte_offset(); - Ok((res, start..end)) - } - fn peek_token_and_rest(&mut self) -> (TokenSpan<'a>, &'a str) { let mut cloned = self.clone(); let token = cloned.next(); @@ -233,36 +214,41 @@ impl<'a> Lexer<'a> { token } - pub(super) fn expect_span( - &mut self, - expected: Token<'a>, - ) -> Result, Error<'a>> { + pub(super) fn expect(&mut self, expected: Token<'a>) -> Result<(), Error<'a>> { let next = self.next(); if next.0 == expected { - Ok(next.1) + Ok(()) } else { - Err(Error::Unexpected(next, ExpectedToken::Token(expected))) + let description = match expected { + Token::Separator(_) => "separator", + Token::DoubleColon => "::", + Token::Paren(_) => "paren", + Token::DoubleParen(_) => "double paren", + Token::Number { .. } => "number", + Token::String(string) => string, + Token::Word(word) => word, + Token::Operation(_) => "operation", + Token::LogicalOperation(_) => "logical op", + Token::ShiftOperation(_) => "shift op", + Token::Arrow => "->", + Token::Unknown(_) => "unknown", + Token::UnterminatedString => "string", + Token::Trivia => "trivia", + Token::End => "", + }; + Err(Error::Unexpected(next, description)) } } - pub(super) fn expect(&mut self, expected: Token<'a>) -> Result<(), Error<'a>> { - self.expect_span(expected)?; - Ok(()) - } - pub(super) fn expect_generic_paren(&mut self, expected: char) -> Result<(), Error<'a>> { let next = self.next_generic(); if next.0 == Token::Paren(expected) { Ok(()) } else { - Err(Error::Unexpected( - next, - ExpectedToken::Token(Token::Paren(expected)), - )) + Err(Error::Unexpected(next, "paren")) } } - /// If the next token matches it is skipped and true is returned pub(super) fn skip(&mut self, what: Token<'_>) -> bool { let (peeked_token, rest) = self.peek_token_and_rest(); if peeked_token.0 == what { @@ -276,81 +262,55 @@ impl<'a> Lexer<'a> { pub(super) fn next_ident_with_span(&mut self) -> Result<(&'a str, Span), Error<'a>> { match self.next() { (Token::Word(word), span) => Ok((word, span)), - other => Err(Error::Unexpected(other, ExpectedToken::Identifier)), + other => Err(Error::Unexpected(other, "identifier")), } } pub(super) fn next_ident(&mut self) -> Result<&'a str, Error<'a>> { match self.next() { (Token::Word(word), _) => Ok(word), - other => Err(Error::Unexpected(other, ExpectedToken::Identifier)), + other => Err(Error::Unexpected(other, "identifier")), } } fn _next_float_literal(&mut self) -> Result> { match self.next() { - (Token::Number { value, .. }, span) => { - value.parse().map_err(|e| Error::BadFloat(span, e)) - } - other => Err(Error::Unexpected(other, ExpectedToken::Float)), + (Token::Number { value, .. }, span) => value.parse().map_err(|_| Error::BadFloat(span)), + other => Err(Error::Unexpected(other, "floating-point literal")), } } pub(super) fn next_uint_literal(&mut self) -> Result> { match self.next() { (Token::Number { value, .. }, span) => { - let v = value.parse(); - v.map_err(|e| Error::BadU32(span, e)) + value.parse().map_err(|_| Error::BadInteger(span)) } - other => Err(Error::Unexpected(other, ExpectedToken::Uint)), + other => Err(Error::Unexpected(other, "unsigned integer literal")), } } pub(super) fn next_sint_literal(&mut self) -> Result> { match self.next() { (Token::Number { value, .. }, span) => { - value.parse().map_err(|e| Error::BadI32(span, e)) + value.parse().map_err(|_| Error::BadInteger(span)) } - other => Err(Error::Unexpected(other, ExpectedToken::Sint)), + other => Err(Error::Unexpected(other, "signed integer literal")), } } - /// Parses a generic scalar type, for example ``. pub(super) fn next_scalar_generic( &mut self, ) -> Result<(crate::ScalarKind, crate::Bytes), Error<'a>> { self.expect_generic_paren('<')?; - let pair = match self.next() { - (Token::Word(word), span) => { - conv::get_scalar_type(word).ok_or(Error::UnknownScalarType(span)) - } - (_, span) => Err(Error::UnknownScalarType(span)), - }?; - self.expect_generic_paren('>')?; - Ok(pair) - } - - /// Parses a generic scalar type, for example ``. - /// - /// Returns the span covering the inner type, excluding the brackets. - pub(super) fn next_scalar_generic_with_span( - &mut self, - ) -> Result<(crate::ScalarKind, crate::Bytes, Span), Error<'a>> { - self.expect_generic_paren('<')?; - let pair = match self.next() { - (Token::Word(word), span) => conv::get_scalar_type(word) - .map(|(a, b)| (a, b, span.clone())) - .ok_or(Error::UnknownScalarType(span)), - (_, span) => Err(Error::UnknownScalarType(span)), - }?; + let word = self.next_ident()?; + let pair = conv::get_scalar_type(word).ok_or(Error::UnknownScalarType(word))?; self.expect_generic_paren('>')?; Ok(pair) } pub(super) fn next_format_generic(&mut self) -> Result> { self.expect(Token::Paren('<'))?; - let (ident, ident_span) = self.next_ident_with_span()?; - let format = conv::map_storage_format(ident, ident_span)?; + let format = conv::map_storage_format(self.next_ident()?)?; self.expect(Token::Paren('>'))?; Ok(format) } diff --git a/third_party/rust/naga/src/front/wgsl/mod.rs b/third_party/rust/naga/src/front/wgsl/mod.rs index bab096fe3c86..51ebde41749b 100644 --- a/third_party/rust/naga/src/front/wgsl/mod.rs +++ b/third_party/rust/naga/src/front/wgsl/mod.rs @@ -3,16 +3,15 @@ //! [wgsl]: https://gpuweb.github.io/gpuweb/wgsl.html mod conv; +mod layout; mod lexer; #[cfg(test)] mod tests; use crate::{ arena::{Arena, Handle}, - proc::{ - ensure_block_returns, Alignment, Layouter, ResolveContext, ResolveError, TypeResolution, - }, - ConstantInner, FastHashMap, ScalarValue, + proc::{ensure_block_returns, ResolveContext, ResolveError, TypeResolution}, + FastHashMap, }; use self::lexer::Lexer; @@ -25,11 +24,9 @@ use codespan_reporting::{ }, }; use std::{ - borrow::Cow, - convert::TryFrom, io::{self, Write}, iter, - num::{NonZeroU32, ParseFloatError, ParseIntError}, + num::NonZeroU32, ops, }; use thiserror::Error; @@ -60,102 +57,60 @@ pub enum Token<'a> { End, } -#[derive(Copy, Clone, Debug, PartialEq)] -pub enum ExpectedToken<'a> { - Token(Token<'a>), - Identifier, - Float, - Uint, - Sint, - Constant, - /// Expected: constant, parenthesized expression, identifier - PrimaryExpression, - /// Expected: ']]', ',' - AttributeSeparator, - /// Expected: '}', identifier - FieldName, - /// Expected: ']]', 'access', 'stride' - TypeAttribute, - /// Expected: ';', '{', word - Statement, - /// Expected: 'case', 'default', '}' - SwitchItem, - /// Expected: ',', ')' - WorkgroupSizeSeparator, - /// Expected: 'struct', 'let', 'var', 'type', ';', 'fn', eof - GlobalItem, - /// Expected: ']]', 'size', 'align' - StructAttribute, -} - #[derive(Clone, Debug, Error)] pub enum Error<'a> { #[error("")] - Unexpected(TokenSpan<'a>, ExpectedToken<'a>), + Unexpected(TokenSpan<'a>, &'a str), #[error("")] - BadU32(Span, ParseIntError), + BadInteger(Span), #[error("")] - BadI32(Span, ParseIntError), - #[error("")] - BadFloat(Span, ParseFloatError), - #[error("")] - BadU32Constant(Span), + BadFloat(Span), #[error("")] BadScalarWidth(Span, &'a str), #[error("")] BadAccessor(Span), - #[error("bad texture`")] - BadTexture(Span), + #[error("bad texture {0}`")] + BadTexture(&'a str), #[error("bad texture coordinate")] BadCoordinate, - #[error("invalid type cast")] - BadTypeCast { - span: Span, - from_type: String, - to_type: String, - }, - #[error("bad texture sample type. Only f32, i32 and u32 are valid")] - BadTextureSampleType { - span: Span, - kind: crate::ScalarKind, - width: u8, - }, + #[error("invalid type cast to `{0}`")] + BadTypeCast(&'a str), #[error(transparent)] InvalidResolve(ResolveError), #[error("for(;;) initializer is not an assignment or a function call")] - InvalidForInitializer(Span), + InvalidForInitializer, #[error("resource type {0:?} is invalid")] InvalidResourceType(Handle), #[error("unknown import: `{0}`")] UnknownImport(&'a str), - #[error("unknown storage class")] - UnknownStorageClass(Span), - #[error("unknown attribute")] - UnknownAttribute(Span), + #[error("unknown storage class: `{0}`")] + UnknownStorageClass(&'a str), + #[error("unknown attribute: `{0}`")] + UnknownAttribute(&'a str), #[error("unknown scalar kind: `{0}`")] UnknownScalarKind(&'a str), - #[error("unknown builtin")] - UnknownBuiltin(Span), + #[error("unknown builtin: `{0}`")] + UnknownBuiltin(&'a str), #[error("unknown access: `{0}`")] UnknownAccess(&'a str), - #[error("unknown shader stage")] - UnknownShaderStage(Span), - #[error("unknown identifier: `{1}`")] - UnknownIdent(Span, &'a str), - #[error("unknown scalar type")] - UnknownScalarType(Span), - #[error("unknown type")] - UnknownType(Span), + #[error("unknown shader stage: `{0}`")] + UnknownShaderStage(&'a str), + #[error("unknown identifier: `{0}`")] + UnknownIdent(&'a str), + #[error("unknown scalar type: `{0}`")] + UnknownScalarType(&'a str), + #[error("unknown type: `{0}`")] + UnknownType(&'a str), #[error("unknown function: `{0}`")] UnknownFunction(&'a str), - #[error("unknown storage format")] - UnknownStorageFormat(Span), - #[error("unknown conservative depth")] - UnknownConservativeDepth(Span), + #[error("unknown storage format: `{0}`")] + UnknownStorageFormat(&'a str), + #[error("unknown conservative depth: `{0}`")] + UnknownConservativeDepth(&'a str), #[error("array stride must not be 0")] - ZeroStride(Span), - #[error("struct member size or alignment must not be 0")] - ZeroSizeOrAlign(Span), + ZeroStride, + #[error("struct member size or array must not be 0")] + ZeroSizeOrAlign, #[error("not a composite type: {0:?}")] NotCompositeType(Handle), #[error("Input/output binding is not consistent: location {0:?}, built-in {1:?}, interpolation {2:?}, and sampling {3:?}")] @@ -178,182 +133,47 @@ pub enum Error<'a> { impl<'a> Error<'a> { fn as_parse_error(&self, source: &'a str) -> ParseError { match *self { - Error::Unexpected((_, ref unexpected_span), expected) => { - let expected_str = match expected { - ExpectedToken::Token(token) => { - match token { - Token::Separator(c) => format!("'{}'", c), - Token::DoubleColon => "'::'".to_string(), - Token::Paren(c) => format!("'{}'", c), - Token::DoubleParen(c) => format!("'{}{}'", c, c), - Token::Number { value, .. } => { - format!("number ({})", value) - } - Token::String(s) => format!("string literal ('{}')", s.to_string()), - Token::Word(s) => s.to_string(), - Token::Operation(c) => format!("operation ('{}')", c), - Token::LogicalOperation(c) => format!("logical operation ('{}')", c), - Token::ShiftOperation(c) => format!("bitshift ('{}{}')", c, c), - Token::Arrow => "->".to_string(), - Token::Unknown(c) => format!("unkown ('{}')", c), - Token::UnterminatedString => "unterminated string".to_string(), - Token::Trivia => "trivia".to_string(), - Token::End => "end".to_string(), - } - } - ExpectedToken::Identifier => "identifier".to_string(), - ExpectedToken::Float => "floating point literal".to_string(), - ExpectedToken::Uint => "non-negative integer literal".to_string(), - ExpectedToken::Sint => "integer literal".to_string(), - ExpectedToken::Constant => "constant".to_string(), - ExpectedToken::PrimaryExpression => "expression".to_string(), - ExpectedToken::AttributeSeparator => "attribute separator (',') or an end of the attribute list (']]')".to_string(), - ExpectedToken::FieldName => "field name or a closing curly bracket to signify the end of the struct".to_string(), - ExpectedToken::TypeAttribute => "type attribute ('access' or 'stride') or and of the attribute list (']]')".to_string(), - ExpectedToken::Statement => "statement".to_string(), - ExpectedToken::SwitchItem => "switch item ('case' or 'default') or a closing curly bracket to signify the end of the switch statement ('}')".to_string(), - ExpectedToken::WorkgroupSizeSeparator => "workgroup size separator (',') or a closing parenthesis".to_string(), - ExpectedToken::GlobalItem => "global item ('struct', 'let', 'var', 'type', ';', 'fn') or the end of the file".to_string(), - ExpectedToken::StructAttribute => "struct attribute ('size' or 'align') or an end of the attribute list (']]')".to_string(), - }; - ParseError { - message: format!( - "expected {}, found '{}'", - expected_str, - &source[unexpected_span.clone()], - ), - labels: vec![( - unexpected_span.clone(), - format!("expected {}", expected_str).into(), - )], - notes: vec![], - } - }, - Error::BadU32(ref bad_span, ref err) => ParseError { + Error::Unexpected((_, ref unexpected_span), expected) => ParseError { message: format!( - "expected non-negative integer literal, found `{}`", - &source[bad_span.clone()], + "expected {}, found '{}'", + expected, + &source[unexpected_span.clone()], ), - labels: vec![(bad_span.clone(), "expected positive integer".into())], - notes: vec![err.to_string()], + labels: vec![(unexpected_span.clone(), format!("expected {}", expected))], + notes: vec![], }, - Error::BadI32(ref bad_span, ref err) => ParseError { + Error::BadInteger(ref bad_span) => ParseError { message: format!( "expected integer literal, found `{}`", &source[bad_span.clone()], ), - labels: vec![(bad_span.clone(), "expected integer".into())], - notes: vec![err.to_string()], + labels: vec![(bad_span.clone(), "expected integer".to_string())], + notes: vec![], }, - Error::BadFloat(ref bad_span, ref err) => ParseError { + Error::BadFloat(ref bad_span) => ParseError { message: format!( "expected floating-point literal, found `{}`", &source[bad_span.clone()], ), - labels: vec![(bad_span.clone(), "expected floating-point literal".into())], - notes: vec![err.to_string()], - }, - Error::BadU32Constant(ref bad_span) => ParseError { - message: format!( - "expected non-negative integer constant expression, found `{}`", - &source[bad_span.clone()], - ), - labels: vec![(bad_span.clone(), "expected non-negative integer".into())], + labels: vec![( + bad_span.clone(), + "expected floating-point literal".to_string(), + )], notes: vec![], }, - Error::BadScalarWidth(ref bad_span, width) => ParseError { message: format!("invalid width of `{}` for literal", width,), - labels: vec![(bad_span.clone(), "invalid width".into())], - notes: vec!["valid widths are 8, 16, 32, 64".to_string()], + labels: vec![(bad_span.clone(), "invalid width".to_string())], + notes: vec!["valid width is 32".to_string()], }, Error::BadAccessor(ref accessor_span) => ParseError { message: format!( "invalid field accessor `{}`", &source[accessor_span.clone()], ), - labels: vec![(accessor_span.clone(), "invalid accessor".into())], + labels: vec![(accessor_span.clone(), "invalid accessor".to_string())], notes: vec![], }, - Error::UnknownIdent(ref ident_span, ident) => ParseError { - message: format!("no definition in scope for identifier: '{}'", ident), - labels: vec![(ident_span.clone(), "unknown identifier".into())], - notes: vec![], - }, - Error::UnknownScalarType(ref bad_span) => ParseError { - message: format!("unknown scalar type: '{}'", &source[bad_span.clone()]), - labels: vec![(bad_span.clone(), "unknown scalar type".into())], - notes: vec!["Valid scalar types are f16, f32, f64, i8, i16, i32, i64, u8, u16, u32, u64, bool".into()], - }, - Error::BadTextureSampleType { ref span, kind, width } => ParseError { - message: format!("texture sample type must be one of f32, i32 or u32, but found {}", kind.to_wgsl(width)), - labels: vec![(span.clone(), "must be one of f32, i32 or u32".into())], - notes: vec![], - }, - Error::BadTexture(ref bad_span) => ParseError { - message: format!("expected an image, but found '{}' which is not an image", &source[bad_span.clone()]), - labels: vec![(bad_span.clone(), "not an image".into())], - notes: vec![], - }, - Error::BadTypeCast { ref span, ref from_type, ref to_type } => { - let msg = format!("cannot cast a {} to a {}", from_type, to_type); - ParseError { - message: msg.clone(), - labels: vec![(span.clone(), msg.into())], - notes: vec![], - } - }, - Error::InvalidForInitializer(ref bad_span) => ParseError { - message: format!("for(;;) initializer is not an assignment or a function call: '{}'", &source[bad_span.clone()]), - labels: vec![(bad_span.clone(), "not an assignment or function call".into())], - notes: vec![], - }, - Error::UnknownStorageClass(ref bad_span) => ParseError { - message: format!("unknown storage class: '{}'", &source[bad_span.clone()]), - labels: vec![(bad_span.clone(), "unknown storage class".into())], - notes: vec![], - }, - Error::UnknownAttribute(ref bad_span) => ParseError { - message: format!("unknown attribute: '{}'", &source[bad_span.clone()]), - labels: vec![(bad_span.clone(), "unknown attribute".into())], - notes: vec![], - }, - Error::UnknownBuiltin(ref bad_span) => ParseError { - message: format!("unknown builtin: '{}'", &source[bad_span.clone()]), - labels: vec![(bad_span.clone(), "unknown builtin".into())], - notes: vec![], - }, - Error::UnknownShaderStage(ref bad_span) => ParseError { - message: format!("unknown shader stage: '{}'", &source[bad_span.clone()]), - labels: vec![(bad_span.clone(), "unknown shader stage".into())], - notes: vec![], - }, - Error::UnknownStorageFormat(ref bad_span) => ParseError { - message: format!("unknown storage format: '{}'", &source[bad_span.clone()]), - labels: vec![(bad_span.clone(), "unknown storage format".into())], - notes: vec![], - }, - Error::UnknownConservativeDepth(ref bad_span) => ParseError { - message: format!("unknown conservative depth: '{}'", &source[bad_span.clone()]), - labels: vec![(bad_span.clone(), "unknown conservative depth".into())], - notes: vec![], - }, - Error::UnknownType(ref bad_span) => ParseError { - message: format!("unknown type: '{}'", &source[bad_span.clone()]), - labels: vec![(bad_span.clone(), "unknown type".into())], - notes: vec![], - }, - Error::ZeroStride(ref bad_span) => ParseError { - message: "array stride must not be zero".to_string(), - labels: vec![(bad_span.clone(), "array stride must not be zero".into())], - notes: vec![], - }, - Error::ZeroSizeOrAlign(ref bad_span) => ParseError { - message: "struct member size or alignment must not be 0".to_string(), - labels: vec![(bad_span.clone(), "struct member size or alignment must not be 0".into())], - notes: vec![], - }, - ref error => ParseError { message: error.to_string(), labels: vec![], @@ -363,236 +183,14 @@ impl<'a> Error<'a> { } } -impl crate::StorageFormat { - pub fn to_wgsl(self) -> &'static str { - use crate::StorageFormat as Sf; - match self { - Sf::R8Unorm => "r8unorm", - Sf::R8Snorm => "r8snorm", - Sf::R8Uint => "r8uint", - Sf::R8Sint => "r8sint", - Sf::R16Uint => "r16uint", - Sf::R16Sint => "r16sint", - Sf::R16Float => "r16float", - Sf::Rg8Unorm => "rg8unorm", - Sf::Rg8Snorm => "rg8snorm", - Sf::Rg8Uint => "rg8uint", - Sf::Rg8Sint => "rg8sint", - Sf::R32Uint => "r32uint", - Sf::R32Sint => "r32sint", - Sf::R32Float => "r32float", - Sf::Rg16Uint => "rg16uint", - Sf::Rg16Sint => "rg16sint", - Sf::Rg16Float => "rg16float", - Sf::Rgba8Unorm => "rgba8unorm", - Sf::Rgba8Snorm => "rgba8snorm", - Sf::Rgba8Uint => "rgba8uint", - Sf::Rgba8Sint => "rgba8sint", - Sf::Rgb10a2Unorm => "rgb10a2unorm", - Sf::Rg11b10Float => "rg11b10float", - Sf::Rg32Uint => "rg32uint", - Sf::Rg32Sint => "rg32sint", - Sf::Rg32Float => "rg32float", - Sf::Rgba16Uint => "rgba16uint", - Sf::Rgba16Sint => "rgba16sint", - Sf::Rgba16Float => "rgba16float", - Sf::Rgba32Uint => "rgba32uint", - Sf::Rgba32Sint => "rgba32sint", - Sf::Rgba32Float => "rgba32float", - } - } -} - -impl crate::TypeInner { - /// Formats the type as it is written in wgsl. - /// - /// For example `vec3`. - /// - /// Note: The names of a `TypeInner::Struct` is not known. Therefore this method will simply return "struct" for them. - pub fn to_wgsl( - &self, - types: &Arena, - constants: &Arena, - ) -> String { - match *self { - crate::TypeInner::Scalar { kind, width } => kind.to_wgsl(width), - crate::TypeInner::Vector { size, kind, width } => { - format!("vec{}<{}>", size as u32, kind.to_wgsl(width)) - } - crate::TypeInner::Matrix { - columns, - rows, - width, - } => { - format!( - "mat{}x{}<{}>", - columns as u32, - rows as u32, - crate::ScalarKind::Float.to_wgsl(width), - ) - } - crate::TypeInner::Pointer { base, .. } => { - let base = &types[base]; - let name = base.name.as_deref().unwrap_or("unknown"); - format!("*{}", name) - } - crate::TypeInner::ValuePointer { kind, width, .. } => { - format!("*{}", kind.to_wgsl(width)) - } - crate::TypeInner::Array { base, size, .. } => { - let member_type = &types[base]; - let base = member_type.name.as_deref().unwrap_or("unknown"); - match size { - crate::ArraySize::Constant(size) => { - let size = constants[size].name.as_deref().unwrap_or("unknown"); - format!("{}[{}]", base, size) - } - crate::ArraySize::Dynamic => format!("{}[]", base), - } - } - crate::TypeInner::Struct { .. } => { - // TODO: Actually output the struct? - "struct".to_string() - } - crate::TypeInner::Image { - dim, - arrayed, - class, - } => { - let dim_suffix = match dim { - crate::ImageDimension::D1 => "_1d", - crate::ImageDimension::D2 => "_2d", - crate::ImageDimension::D3 => "_3d", - crate::ImageDimension::Cube => "_cube", - }; - let array_suffix = if arrayed { "_array" } else { "" }; - - let class_suffix = match class { - crate::ImageClass::Sampled { multi: true, .. } => "_multisampled", - crate::ImageClass::Depth => "_depth", - _ => "", - }; - - let type_in_brackets = match class { - crate::ImageClass::Sampled { kind, .. } => { - // Note: The only valid widths are 4 bytes wide. - // The lexer has already verified this, so we can safely assume it here. - // https://gpuweb.github.io/gpuweb/wgsl/#sampled-texture-type - let element_type = kind.to_wgsl(4); - format!("<{}>", element_type) - } - crate::ImageClass::Depth => String::new(), - crate::ImageClass::Storage(format) => { - format!("<{}>", format.to_wgsl()) - } - }; - - format!( - "texture{}{}{}{}", - class_suffix, dim_suffix, array_suffix, type_in_brackets - ) - } - crate::TypeInner::Sampler { .. } => "sampler".to_string(), - } - } -} - -mod type_inner_tests { - #[test] - fn to_wgsl() { - let mut types = crate::Arena::new(); - let mut constants = crate::Arena::new(); - let c = constants.append(crate::Constant { - name: Some("C".to_string()), - specialization: None, - inner: crate::ConstantInner::Scalar { - width: 4, - value: crate::ScalarValue::Uint(32), - }, - }); - - let mytype1 = types.append(crate::Type { - name: Some("MyType1".to_string()), - inner: crate::TypeInner::Struct { - top_level: true, - members: vec![], - span: 0, - }, - }); - let mytype2 = types.append(crate::Type { - name: Some("MyType2".to_string()), - inner: crate::TypeInner::Struct { - top_level: true, - members: vec![], - span: 0, - }, - }); - - let array = crate::TypeInner::Array { - base: mytype1, - stride: 4, - size: crate::ArraySize::Constant(c), - }; - assert_eq!(array.to_wgsl(&types, &constants), "MyType1[C]"); - - let mat = crate::TypeInner::Matrix { - rows: crate::VectorSize::Quad, - columns: crate::VectorSize::Bi, - width: 8, - }; - assert_eq!(mat.to_wgsl(&types, &constants), "mat2x4"); - - let ptr = crate::TypeInner::Pointer { - base: mytype2, - class: crate::StorageClass::Storage, - }; - assert_eq!(ptr.to_wgsl(&types, &constants), "*MyType2"); - - let img1 = crate::TypeInner::Image { - dim: crate::ImageDimension::D2, - arrayed: false, - class: crate::ImageClass::Sampled { - kind: crate::ScalarKind::Float, - multi: true, - }, - }; - assert_eq!( - img1.to_wgsl(&types, &constants), - "texture_multisampled_2d" - ); - - let img2 = crate::TypeInner::Image { - dim: crate::ImageDimension::Cube, - arrayed: true, - class: crate::ImageClass::Depth, - }; - assert_eq!(img2.to_wgsl(&types, &constants), "texture_depth_cube_array"); - } -} - -impl crate::ScalarKind { - /// Format a scalar kind+width as a type is written in wgsl. - /// - /// Examples: `f32`, `u64`, `bool`. - fn to_wgsl(self, width: u8) -> String { - let prefix = match self { - crate::ScalarKind::Sint => "i", - crate::ScalarKind::Uint => "u", - crate::ScalarKind::Float => "f", - crate::ScalarKind::Bool => return "bool".to_string(), - }; - format!("{}{}", prefix, width * 8) - } -} - trait StringValueLookup<'a> { type Value; - fn lookup(&self, key: &'a str, span: Span) -> Result>; + fn lookup(&self, key: &'a str) -> Result>; } impl<'a> StringValueLookup<'a> for FastHashMap<&'a str, Handle> { type Value = Handle; - fn lookup(&self, key: &'a str, span: Span) -> Result> { - self.get(key).cloned().ok_or(Error::UnknownIdent(span, key)) + fn lookup(&self, key: &'a str) -> Result> { + self.get(key).cloned().ok_or(Error::UnknownIdent(key)) } } @@ -601,7 +199,6 @@ struct StatementContext<'input, 'temp, 'out> { typifier: &'temp mut super::Typifier, variables: &'out mut Arena, expressions: &'out mut Arena, - named_expressions: &'out mut FastHashMap, String>, types: &'out mut Arena, constants: &'out mut Arena, global_vars: &'out Arena, @@ -616,7 +213,6 @@ impl<'a, 'temp> StatementContext<'a, 'temp, '_> { typifier: self.typifier, variables: self.variables, expressions: self.expressions, - named_expressions: self.named_expressions, types: self.types, constants: self.constants, global_vars: self.global_vars, @@ -691,29 +287,27 @@ impl<'a> ExpressionContext<'a, '_, '_> { ) -> Result<&crate::TypeInner, Error<'a>> { let resolve_ctx = ResolveContext { constants: self.constants, - types: self.types, global_vars: self.global_vars, local_vars: self.local_vars, functions: self.functions, arguments: self.arguments, }; - match self.typifier.grow(handle, self.expressions, &resolve_ctx) { + match self + .typifier + .grow(handle, self.expressions, self.types, &resolve_ctx) + { Err(e) => Err(Error::InvalidResolve(e)), Ok(()) => Ok(self.typifier.get(handle, self.types)), } } - fn prepare_sampling( - &mut self, - image_name: &'a str, - span: Span, - ) -> Result> { - let image = self.lookup_ident.lookup(image_name, span.clone())?; + fn prepare_sampling(&mut self, image_name: &'a str) -> Result> { + let image = self.lookup_ident.lookup(image_name)?; Ok(SamplingContext { image, arrayed: match *self.resolve_type(image)? { crate::TypeInner::Image { arrayed, .. } => arrayed, - _ => return Err(Error::BadTexture(span)), + _ => return Err(Error::BadTexture(image_name)), }, }) } @@ -870,12 +464,7 @@ struct BindingParser { } impl BindingParser { - fn parse<'a>( - &mut self, - lexer: &mut Lexer<'a>, - name: &'a str, - name_span: Span, - ) -> Result<(), Error<'a>> { + fn parse<'a>(&mut self, lexer: &mut Lexer<'a>, name: &'a str) -> Result<(), Error<'a>> { match name { "location" => { lexer.expect(Token::Paren('('))?; @@ -884,21 +473,21 @@ impl BindingParser { } "builtin" => { lexer.expect(Token::Paren('('))?; - let (raw, span) = lexer.next_ident_with_span()?; - self.built_in = Some(conv::map_built_in(raw, span)?); + let raw = lexer.next_ident()?; + self.built_in = Some(conv::map_built_in(raw)?); lexer.expect(Token::Paren(')'))?; } "interpolate" => { lexer.expect(Token::Paren('('))?; - let (raw, span) = lexer.next_ident_with_span()?; - self.interpolation = Some(conv::map_interpolation(raw, span)?); + let raw = lexer.next_ident()?; + self.interpolation = Some(conv::map_interpolation(raw)?); if lexer.skip(Token::Separator(',')) { - let (raw, span) = lexer.next_ident_with_span()?; - self.sampling = Some(conv::map_sampling(raw, span)?); + let raw = lexer.next_ident()?; + self.sampling = Some(conv::map_sampling(raw)?); } lexer.expect(Token::Paren(')'))?; } - _ => return Err(Error::UnknownAttribute(name_span)), + _ => return Err(Error::UnknownAttribute(name)), } Ok(()) } @@ -944,7 +533,7 @@ struct ParsedVariable<'a> { #[derive(Clone, Debug)] pub struct ParseError { message: String, - labels: Vec<(Span, Cow<'static, str>)>, + labels: Vec<(Span, String)>, notes: Vec, } @@ -1018,7 +607,7 @@ impl std::error::Error for ParseError { pub struct Parser { scopes: Vec, lookup_type: FastHashMap>, - layouter: Layouter, + layouter: layout::Layouter, } impl Parser { @@ -1035,21 +624,21 @@ impl Parser { ty: char, width: &'a str, token: TokenSpan<'a>, - ) -> Result> { + ) -> Result> { let span = token.1; let value = match ty { 'i' => word .parse() .map(crate::ScalarValue::Sint) - .map_err(|e| Error::BadI32(span.clone(), e))?, + .map_err(|_| Error::BadInteger(span.clone()))?, 'u' => word .parse() .map(crate::ScalarValue::Uint) - .map_err(|e| Error::BadU32(span.clone(), e))?, + .map_err(|_| Error::BadInteger(span.clone()))?, 'f' => word .parse() .map(crate::ScalarValue::Float) - .map_err(|e| Error::BadFloat(span.clone(), e))?, + .map_err(|_| Error::BadFloat(span.clone()))?, _ => unreachable!(), }; Ok(crate::ConstantInner::Scalar { @@ -1058,9 +647,9 @@ impl Parser { 4 } else { match width.parse::() { - Ok(bits) if (bits % 8) == 0 => Ok(bits / 8), - _ => Err(Error::BadScalarWidth(span, width)), - }? + Ok(bits) => bits / 8, + Err(_) => return Err(Error::BadScalarWidth(span, width)), + } }, }) } @@ -1155,12 +744,12 @@ impl Parser { match name { "textureSample" => { lexer.open_arguments()?; - let (image_name, image_span) = lexer.next_ident_with_span()?; + let image_name = lexer.next_ident()?; lexer.expect(Token::Separator(','))?; - let (sampler_name, sampler_span) = lexer.next_ident_with_span()?; + let sampler_name = lexer.next_ident()?; lexer.expect(Token::Separator(','))?; let coordinate = self.parse_general_expression(lexer, ctx.reborrow())?; - let sc = ctx.prepare_sampling(image_name, image_span)?; + let sc = ctx.prepare_sampling(image_name)?; let array_index = if sc.arrayed { lexer.expect(Token::Separator(','))?; Some(self.parse_general_expression(lexer, ctx.reborrow())?) @@ -1175,7 +764,7 @@ impl Parser { lexer.close_arguments()?; crate::Expression::ImageSample { image: sc.image, - sampler: ctx.lookup_ident.lookup(sampler_name, sampler_span)?, + sampler: ctx.lookup_ident.lookup(sampler_name)?, coordinate, array_index, offset, @@ -1185,12 +774,12 @@ impl Parser { } "textureSampleLevel" => { lexer.open_arguments()?; - let (image_name, image_span) = lexer.next_ident_with_span()?; + let image_name = lexer.next_ident()?; lexer.expect(Token::Separator(','))?; - let (sampler_name, sampler_span) = lexer.next_ident_with_span()?; + let sampler_name = lexer.next_ident()?; lexer.expect(Token::Separator(','))?; let coordinate = self.parse_general_expression(lexer, ctx.reborrow())?; - let sc = ctx.prepare_sampling(image_name, image_span)?; + let sc = ctx.prepare_sampling(image_name)?; let array_index = if sc.arrayed { lexer.expect(Token::Separator(','))?; Some(self.parse_general_expression(lexer, ctx.reborrow())?) @@ -1207,7 +796,7 @@ impl Parser { lexer.close_arguments()?; crate::Expression::ImageSample { image: sc.image, - sampler: ctx.lookup_ident.lookup(sampler_name, sampler_span)?, + sampler: ctx.lookup_ident.lookup(sampler_name)?, coordinate, array_index, offset, @@ -1217,12 +806,12 @@ impl Parser { } "textureSampleBias" => { lexer.open_arguments()?; - let (image_name, image_span) = lexer.next_ident_with_span()?; + let image_name = lexer.next_ident()?; lexer.expect(Token::Separator(','))?; - let (sampler_name, sampler_span) = lexer.next_ident_with_span()?; + let sampler_name = lexer.next_ident()?; lexer.expect(Token::Separator(','))?; let coordinate = self.parse_general_expression(lexer, ctx.reborrow())?; - let sc = ctx.prepare_sampling(image_name, image_span)?; + let sc = ctx.prepare_sampling(image_name)?; let array_index = if sc.arrayed { lexer.expect(Token::Separator(','))?; Some(self.parse_general_expression(lexer, ctx.reborrow())?) @@ -1239,7 +828,7 @@ impl Parser { lexer.close_arguments()?; crate::Expression::ImageSample { image: sc.image, - sampler: ctx.lookup_ident.lookup(sampler_name, sampler_span)?, + sampler: ctx.lookup_ident.lookup(sampler_name)?, coordinate, array_index, offset, @@ -1249,12 +838,12 @@ impl Parser { } "textureSampleGrad" => { lexer.open_arguments()?; - let (image_name, image_span) = lexer.next_ident_with_span()?; + let image_name = lexer.next_ident()?; lexer.expect(Token::Separator(','))?; - let (sampler_name, sampler_span) = lexer.next_ident_with_span()?; + let sampler_name = lexer.next_ident()?; lexer.expect(Token::Separator(','))?; let coordinate = self.parse_general_expression(lexer, ctx.reborrow())?; - let sc = ctx.prepare_sampling(image_name, image_span)?; + let sc = ctx.prepare_sampling(image_name)?; let array_index = if sc.arrayed { lexer.expect(Token::Separator(','))?; Some(self.parse_general_expression(lexer, ctx.reborrow())?) @@ -1273,7 +862,7 @@ impl Parser { lexer.close_arguments()?; crate::Expression::ImageSample { image: sc.image, - sampler: ctx.lookup_ident.lookup(sampler_name, sampler_span)?, + sampler: ctx.lookup_ident.lookup(sampler_name)?, coordinate, array_index, offset, @@ -1283,12 +872,12 @@ impl Parser { } "textureSampleCompare" => { lexer.open_arguments()?; - let (image_name, image_span) = lexer.next_ident_with_span()?; + let image_name = lexer.next_ident()?; lexer.expect(Token::Separator(','))?; - let (sampler_name, sampler_span) = lexer.next_ident_with_span()?; + let sampler_name = lexer.next_ident()?; lexer.expect(Token::Separator(','))?; let coordinate = self.parse_general_expression(lexer, ctx.reborrow())?; - let sc = ctx.prepare_sampling(image_name, image_span)?; + let sc = ctx.prepare_sampling(image_name)?; let array_index = if sc.arrayed { lexer.expect(Token::Separator(','))?; Some(self.parse_general_expression(lexer, ctx.reborrow())?) @@ -1305,39 +894,7 @@ impl Parser { lexer.close_arguments()?; crate::Expression::ImageSample { image: sc.image, - sampler: ctx.lookup_ident.lookup(sampler_name, sampler_span)?, - coordinate, - array_index, - offset, - level: crate::SampleLevel::Auto, - depth_ref: Some(reference), - } - } - "textureSampleCompareLevel" => { - lexer.open_arguments()?; - let (image_name, image_span) = lexer.next_ident_with_span()?; - lexer.expect(Token::Separator(','))?; - let (sampler_name, sampler_span) = lexer.next_ident_with_span()?; - lexer.expect(Token::Separator(','))?; - let coordinate = self.parse_general_expression(lexer, ctx.reborrow())?; - let sc = ctx.prepare_sampling(image_name, image_span)?; - let array_index = if sc.arrayed { - lexer.expect(Token::Separator(','))?; - Some(self.parse_general_expression(lexer, ctx.reborrow())?) - } else { - None - }; - lexer.expect(Token::Separator(','))?; - let reference = self.parse_general_expression(lexer, ctx.reborrow())?; - let offset = if lexer.skip(Token::Separator(',')) { - Some(self.parse_const_expression(lexer, ctx.types, ctx.constants)?) - } else { - None - }; - lexer.close_arguments()?; - crate::Expression::ImageSample { - image: sc.image, - sampler: ctx.lookup_ident.lookup(sampler_name, sampler_span)?, + sampler: ctx.lookup_ident.lookup(sampler_name)?, coordinate, array_index, offset, @@ -1347,13 +904,13 @@ impl Parser { } "textureLoad" => { lexer.open_arguments()?; - let (image_name, image_span) = lexer.next_ident_with_span()?; - let image = ctx.lookup_ident.lookup(image_name, image_span.clone())?; + let image_name = lexer.next_ident()?; + let image = ctx.lookup_ident.lookup(image_name)?; lexer.expect(Token::Separator(','))?; let coordinate = self.parse_general_expression(lexer, ctx.reborrow())?; let (class, arrayed) = match *ctx.resolve_type(image)? { crate::TypeInner::Image { class, arrayed, .. } => (class, arrayed), - _ => return Err(Error::BadTexture(image_span)), + _ => return Err(Error::BadTexture(image_name)), }; let array_index = if arrayed { lexer.expect(Token::Separator(','))?; @@ -1379,8 +936,8 @@ impl Parser { } "textureDimensions" => { lexer.open_arguments()?; - let (image_name, image_span) = lexer.next_ident_with_span()?; - let image = ctx.lookup_ident.lookup(image_name, image_span)?; + let image_name = lexer.next_ident()?; + let image = ctx.lookup_ident.lookup(image_name)?; let level = if lexer.skip(Token::Separator(',')) { let expr = self.parse_general_expression(lexer, ctx.reborrow())?; Some(expr) @@ -1395,8 +952,8 @@ impl Parser { } "textureNumLevels" => { lexer.open_arguments()?; - let (image_name, image_span) = lexer.next_ident_with_span()?; - let image = ctx.lookup_ident.lookup(image_name, image_span)?; + let image_name = lexer.next_ident()?; + let image = ctx.lookup_ident.lookup(image_name)?; lexer.close_arguments()?; crate::Expression::ImageQuery { image, @@ -1405,8 +962,8 @@ impl Parser { } "textureNumLayers" => { lexer.open_arguments()?; - let (image_name, image_span) = lexer.next_ident_with_span()?; - let image = ctx.lookup_ident.lookup(image_name, image_span)?; + let image_name = lexer.next_ident()?; + let image = ctx.lookup_ident.lookup(image_name)?; lexer.close_arguments()?; crate::Expression::ImageQuery { image, @@ -1415,8 +972,8 @@ impl Parser { } "textureNumSamples" => { lexer.open_arguments()?; - let (image_name, image_span) = lexer.next_ident_with_span()?; - let image = ctx.lookup_ident.lookup(image_name, image_span)?; + let image_name = lexer.next_ident()?; + let image = ctx.lookup_ident.lookup(image_name)?; lexer.close_arguments()?; crate::Expression::ImageQuery { image, @@ -1449,99 +1006,6 @@ impl Parser { Ok(Some(ctx.expressions.append(expr))) } - fn parse_construction<'a>( - &mut self, - lexer: &mut Lexer<'a>, - type_name: &'a str, - mut ctx: ExpressionContext<'a, '_, '_>, - ) -> Result>, Error<'a>> { - let ty_resolution = match self.lookup_type.get(type_name) { - Some(&handle) => TypeResolution::Handle(handle), - None => match self.parse_type_decl_impl( - lexer, - TypeAttributes::default(), - type_name, - ctx.types, - ctx.constants, - )? { - Some(inner) => TypeResolution::Value(inner), - None => return Ok(None), - }, - }; - - let mut components = Vec::new(); - let (last_component, arguments_span) = lexer.capture_span(|lexer| { - lexer.open_arguments()?; - let mut last_component = self.parse_general_expression(lexer, ctx.reborrow())?; - - while lexer.next_argument()? { - components.push(last_component); - last_component = self.parse_general_expression(lexer, ctx.reborrow())?; - } - - Ok(last_component) - })?; - - let expr = if components.is_empty() - && ty_resolution.inner_with(ctx.types).scalar_kind().is_some() - { - // We can't use the `TypeInner` returned by this because - // `resolve_type` borrows context mutably. - // Use it to insert into the right maps, - // and then grab it again immutably. - ctx.resolve_type(last_component)?; - - match ( - ty_resolution.inner_with(ctx.types), - ctx.typifier.get(last_component, ctx.types), - ) { - (&crate::TypeInner::Vector { size, .. }, &crate::TypeInner::Scalar { .. }) => { - crate::Expression::Splat { - size, - value: last_component, - } - } - ( - &crate::TypeInner::Scalar { kind, width, .. }, - &crate::TypeInner::Scalar { .. }, - ) - | ( - &crate::TypeInner::Vector { kind, width, .. }, - &crate::TypeInner::Vector { .. }, - ) => crate::Expression::As { - expr: last_component, - kind, - convert: Some(width), - }, - (&crate::TypeInner::Matrix { width, .. }, &crate::TypeInner::Matrix { .. }) => { - crate::Expression::As { - expr: last_component, - kind: crate::ScalarKind::Float, - convert: Some(width), - } - } - (to_type, from_type) => { - return Err(Error::BadTypeCast { - span: arguments_span, - from_type: from_type.to_wgsl(ctx.types, ctx.constants), - to_type: to_type.to_wgsl(ctx.types, ctx.constants), - }); - } - } - } else { - let ty = match ty_resolution { - TypeResolution::Handle(handle) => handle, - TypeResolution::Value(inner) => { - ctx.types.fetch_or_append(crate::Type { name: None, inner }) - } - }; - components.push(last_component); - crate::Expression::Compose { ty, components } - }; - - Ok(Some(ctx.expressions.append(expr))) - } - fn parse_const_expression_impl<'a>( &mut self, first_token_span: TokenSpan<'a>, @@ -1554,10 +1018,15 @@ impl Parser { let inner = match first_token_span { (Token::Word("true"), _) => crate::ConstantInner::boolean(true), (Token::Word("false"), _) => crate::ConstantInner::boolean(false), - (Token::Number { value, ty, width }, _) => { - Self::get_constant_inner(value, ty, width, first_token_span)? - } - (Token::Word(name), name_span) => { + ( + Token::Number { + ref value, + ref ty, + ref width, + }, + _, + ) => Self::get_constant_inner(*value, *ty, *width, first_token_span)?, + (Token::Word(name), _) => { // look for an existing constant first for (handle, var) in const_arena.iter() { match var.name { @@ -1571,7 +1040,6 @@ impl Parser { let composite_ty = self.parse_type_decl_name( lexer, name, - name_span, None, TypeAttributes::default(), type_arena, @@ -1590,7 +1058,7 @@ impl Parser { components, } } - other => return Err(Error::Unexpected(other, ExpectedToken::Constant)), + other => return Err(Error::Unexpected(other, "constant")), }; let handle = if let Some(name) = register_name { @@ -1645,7 +1113,7 @@ impl Parser { ctx.emitter.start(ctx.expressions); expr } - (Token::Word(word), span) => { + (Token::Word(word), _) => { if let Some(&expr) = ctx.lookup_ident.get(word) { expr } else if let Some(expr) = @@ -1653,13 +1121,87 @@ impl Parser { { //TODO: resolve the duplicate call in `parse_singular_expression` expr - } else if let Some(expr) = self.parse_construction(lexer, word, ctx.reborrow())? { - expr } else { - return Err(Error::UnknownIdent(span, word)); + let ty_resolution = match self.lookup_type.get(word) { + Some(&handle) => TypeResolution::Handle(handle), + None => { + let inner = self.parse_type_decl_impl( + lexer, + TypeAttributes::default(), + word, + ctx.types, + ctx.constants, + )?; + TypeResolution::Value(inner) + } + }; + + lexer.open_arguments()?; + let mut components = Vec::new(); + let mut last_component = + self.parse_general_expression(lexer, ctx.reborrow())?; + while lexer.next_argument()? { + components.push(last_component); + last_component = self.parse_general_expression(lexer, ctx.reborrow())?; + } + + let expr = if components.is_empty() + && ty_resolution.inner_with(ctx.types).scalar_kind().is_some() + { + // We can't use the `TypeInner` returned by this because + // `resolve_type` borrows context mutably. + // Use it to insert into the right maps, + // and then grab it again immutably. + ctx.resolve_type(last_component)?; + match ( + ty_resolution.inner_with(ctx.types), + ctx.typifier.get(last_component, ctx.types), + ) { + ( + &crate::TypeInner::Vector { size, .. }, + &crate::TypeInner::Scalar { .. }, + ) => crate::Expression::Splat { + size, + value: last_component, + }, + ( + &crate::TypeInner::Scalar { kind, width, .. }, + &crate::TypeInner::Scalar { .. }, + ) + | ( + &crate::TypeInner::Vector { kind, width, .. }, + &crate::TypeInner::Vector { .. }, + ) => crate::Expression::As { + expr: last_component, + kind, + convert: Some(width), + }, + ( + &crate::TypeInner::Matrix { width, .. }, + &crate::TypeInner::Matrix { .. }, + ) => crate::Expression::As { + expr: last_component, + kind: crate::ScalarKind::Float, + convert: Some(width), + }, + _ => { + return Err(Error::BadTypeCast(word)); + } + } + } else { + let ty = match ty_resolution { + TypeResolution::Handle(handle) => handle, + TypeResolution::Value(inner) => { + ctx.types.fetch_or_append(crate::Type { name: None, inner }) + } + }; + components.push(last_component); + crate::Expression::Compose { ty, components } + }; + ctx.expressions.append(expr) } } - other => return Err(Error::Unexpected(other, ExpectedToken::PrimaryExpression)), + other => return Err(Error::Unexpected(other, "primary expression")), }; self.scopes.pop(); Ok(handle) @@ -1749,34 +1291,12 @@ impl Parser { } } Token::Paren('[') => { - let (_, open_brace_span) = lexer.next(); + let _ = lexer.next(); let index = self.parse_general_expression(lexer, ctx.reborrow())?; - let close_brace_span = lexer.expect_span(Token::Paren(']'))?; - - if let crate::Expression::Constant(constant) = ctx.expressions[index] { - let expr_span = open_brace_span.end..close_brace_span.start; - - let index = match ctx.constants[constant].inner { - ConstantInner::Scalar { - value: ScalarValue::Uint(int), - .. - } => u32::try_from(int).map_err(|_| Error::BadU32Constant(expr_span)), - ConstantInner::Scalar { - value: ScalarValue::Sint(int), - .. - } => u32::try_from(int).map_err(|_| Error::BadU32Constant(expr_span)), - _ => Err(Error::BadU32Constant(expr_span)), - }?; - - crate::Expression::AccessIndex { - base: handle, - index, - } - } else { - crate::Expression::Access { - base: handle, - index, - } + lexer.expect(Token::Paren(']'))?; + crate::Expression::Access { + base: handle, + index, } } _ => { @@ -2007,8 +1527,8 @@ impl Parser { self.scopes.push(Scope::VariableDecl); let mut class = None; if lexer.skip(Token::Paren('<')) { - let (class_str, span) = lexer.next_ident_with_span()?; - class = Some(conv::map_storage_class(class_str, span)?); + let class_str = lexer.next_ident()?; + class = Some(conv::map_storage_class(class_str)?); lexer.expect(Token::Paren('>'))?; } let name = lexer.next_ident()?; @@ -2037,9 +1557,9 @@ impl Parser { lexer: &mut Lexer<'a>, type_arena: &mut Arena, const_arena: &mut Arena, - ) -> Result<(Vec, u32), Error<'a>> { + ) -> Result<(Vec, u32, crate::Alignment), Error<'a>> { let mut offset = 0; - let mut alignment = Alignment::new(1).unwrap(); + let mut alignment = crate::Alignment::new(1).unwrap(); let mut members = Vec::new(); lexer.expect(Token::Paren('{'))?; @@ -2057,38 +1577,27 @@ impl Parser { (Token::Separator(','), _) if !ready => { ready = true; } - (Token::Word(word), word_span) if ready => { + (Token::Word(word), _) if ready => { match word { "size" => { lexer.expect(Token::Paren('('))?; - let (value, span) = - lexer.capture_span(Lexer::next_uint_literal)?; + let value = lexer.next_uint_literal()?; lexer.expect(Token::Paren(')'))?; - size = Some( - NonZeroU32::new(value) - .ok_or(Error::ZeroSizeOrAlign(span))?, - ); + size = + Some(NonZeroU32::new(value).ok_or(Error::ZeroSizeOrAlign)?); } "align" => { lexer.expect(Token::Paren('('))?; - let (value, span) = - lexer.capture_span(Lexer::next_uint_literal)?; + let value = lexer.next_uint_literal()?; lexer.expect(Token::Paren(')'))?; - align = Some( - NonZeroU32::new(value) - .ok_or(Error::ZeroSizeOrAlign(span))?, - ); + align = + Some(NonZeroU32::new(value).ok_or(Error::ZeroSizeOrAlign)?); } - _ => bind_parser.parse(lexer, word, word_span)?, + _ => bind_parser.parse(lexer, word)?, } ready = false; } - other if ready => { - return Err(Error::Unexpected(other, ExpectedToken::StructAttribute)) - } - other => { - return Err(Error::Unexpected(other, ExpectedToken::AttributeSeparator)) - } + other => return Err(Error::Unexpected(other, "attribute separator")), } } self.scopes.pop(); @@ -2097,17 +1606,16 @@ impl Parser { let name = match lexer.next() { (Token::Word(word), _) => word, (Token::Paren('}'), _) => { - let span = Layouter::round_up(alignment, offset); - return Ok((members, span)); + let span = layout::Layouter::round_up(alignment, offset); + return Ok((members, span, alignment)); } - other => return Err(Error::Unexpected(other, ExpectedToken::FieldName)), + other => return Err(Error::Unexpected(other, "field name")), }; lexer.expect(Token::Separator(':'))?; let (ty, _access) = self.parse_type_decl(lexer, None, type_arena, const_arena)?; lexer.expect(Token::Separator(';'))?; - self.layouter.update(type_arena, const_arena).unwrap(); - + self.layouter.update(type_arena, const_arena); let (range, align) = self.layouter.member_placement(offset, ty, align, size); alignment = alignment.max(align); offset = range.end; @@ -2128,11 +1636,11 @@ impl Parser { word: &'a str, type_arena: &mut Arena, const_arena: &mut Arena, - ) -> Result, Error<'a>> { + ) -> Result> { if let Some((kind, width)) = conv::get_scalar_type(word) { - return Ok(Some(crate::TypeInner::Scalar { kind, width })); + return Ok(crate::TypeInner::Scalar { kind, width }); } - Ok(Some(match word { + Ok(match word { "vec2" => { let (kind, width) = lexer.next_scalar_generic()?; crate::TypeInner::Vector { @@ -2231,8 +1739,7 @@ impl Parser { } "ptr" => { lexer.expect_generic_paren('<')?; - let (ident, span) = lexer.next_ident_with_span()?; - let class = conv::map_storage_class(ident, span)?; + let class = conv::map_storage_class(lexer.next_ident()?)?; lexer.expect(Token::Separator(','))?; let (base, _access) = self.parse_type_decl(lexer, None, type_arena, const_arena)?; lexer.expect_generic_paren('>')?; @@ -2259,8 +1766,7 @@ impl Parser { "sampler" => crate::TypeInner::Sampler { comparison: false }, "sampler_comparison" => crate::TypeInner::Sampler { comparison: true }, "texture_1d" => { - let (kind, width, span) = lexer.next_scalar_generic_with_span()?; - Self::check_texture_sample_type(kind, width, span)?; + let (kind, _) = lexer.next_scalar_generic()?; crate::TypeInner::Image { dim: crate::ImageDimension::D1, arrayed: false, @@ -2268,8 +1774,7 @@ impl Parser { } } "texture_1d_array" => { - let (kind, width, span) = lexer.next_scalar_generic_with_span()?; - Self::check_texture_sample_type(kind, width, span)?; + let (kind, _) = lexer.next_scalar_generic()?; crate::TypeInner::Image { dim: crate::ImageDimension::D1, arrayed: true, @@ -2277,8 +1782,7 @@ impl Parser { } } "texture_2d" => { - let (kind, width, span) = lexer.next_scalar_generic_with_span()?; - Self::check_texture_sample_type(kind, width, span)?; + let (kind, _) = lexer.next_scalar_generic()?; crate::TypeInner::Image { dim: crate::ImageDimension::D2, arrayed: false, @@ -2286,8 +1790,7 @@ impl Parser { } } "texture_2d_array" => { - let (kind, width, span) = lexer.next_scalar_generic_with_span()?; - Self::check_texture_sample_type(kind, width, span)?; + let (kind, _) = lexer.next_scalar_generic()?; crate::TypeInner::Image { dim: crate::ImageDimension::D2, arrayed: true, @@ -2295,8 +1798,7 @@ impl Parser { } } "texture_3d" => { - let (kind, width, span) = lexer.next_scalar_generic_with_span()?; - Self::check_texture_sample_type(kind, width, span)?; + let (kind, _) = lexer.next_scalar_generic()?; crate::TypeInner::Image { dim: crate::ImageDimension::D3, arrayed: false, @@ -2304,8 +1806,7 @@ impl Parser { } } "texture_cube" => { - let (kind, width, span) = lexer.next_scalar_generic_with_span()?; - Self::check_texture_sample_type(kind, width, span)?; + let (kind, _) = lexer.next_scalar_generic()?; crate::TypeInner::Image { dim: crate::ImageDimension::Cube, arrayed: false, @@ -2313,8 +1814,7 @@ impl Parser { } } "texture_cube_array" => { - let (kind, width, span) = lexer.next_scalar_generic_with_span()?; - Self::check_texture_sample_type(kind, width, span)?; + let (kind, _) = lexer.next_scalar_generic()?; crate::TypeInner::Image { dim: crate::ImageDimension::Cube, arrayed: true, @@ -2322,8 +1822,7 @@ impl Parser { } } "texture_multisampled_2d" => { - let (kind, width, span) = lexer.next_scalar_generic_with_span()?; - Self::check_texture_sample_type(kind, width, span)?; + let (kind, _) = lexer.next_scalar_generic()?; crate::TypeInner::Image { dim: crate::ImageDimension::D2, arrayed: false, @@ -2331,8 +1830,7 @@ impl Parser { } } "texture_multisampled_2d_array" => { - let (kind, width, span) = lexer.next_scalar_generic_with_span()?; - Self::check_texture_sample_type(kind, width, span)?; + let (kind, _) = lexer.next_scalar_generic()?; crate::TypeInner::Image { dim: crate::ImageDimension::D2, arrayed: true, @@ -2399,30 +1897,15 @@ impl Parser { class: crate::ImageClass::Storage(format), } } - _ => return Ok(None), - })) - } - - fn check_texture_sample_type( - kind: crate::ScalarKind, - width: u8, - span: Span, - ) -> Result<(), Error<'static>> { - use crate::ScalarKind::*; - // Validate according to https://gpuweb.github.io/gpuweb/wgsl/#sampled-texture-type - match (kind, width) { - (Float, 4) | (Sint, 4) | (Uint, 4) => Ok(()), - _ => Err(Error::BadTextureSampleType { span, kind, width }), - } + _ => return Err(Error::UnknownType(word)), + }) } /// Parse type declaration of a given name and attribute. - #[allow(clippy::too_many_arguments)] fn parse_type_decl_name<'a>( &mut self, lexer: &mut Lexer<'a>, name: &'a str, - name_span: Span, debug_name: Option<&'a str>, attribute: TypeAttributes, type_arena: &mut Arena, @@ -2431,13 +1914,12 @@ impl Parser { Ok(match self.lookup_type.get(name) { Some(&handle) => handle, None => { - match self.parse_type_decl_impl(lexer, attribute, name, type_arena, const_arena)? { - Some(inner) => type_arena.fetch_or_append(crate::Type { - name: debug_name.map(|s| s.to_string()), - inner, - }), - None => return Err(Error::UnknownType(name_span)), - } + let inner = + self.parse_type_decl_impl(lexer, attribute, name, type_arena, const_arena)?; + type_arena.fetch_or_append(crate::Type { + name: debug_name.map(|s| s.to_string()), + inner, + }) } }) } @@ -2468,29 +1950,22 @@ impl Parser { } (Token::Word("stride"), _) => { lexer.expect(Token::Paren('('))?; - let (stride, span) = lexer.capture_span(Lexer::next_uint_literal)?; - attribute.stride = - Some(NonZeroU32::new(stride).ok_or(Error::ZeroStride(span))?); + attribute.stride = Some( + NonZeroU32::new(lexer.next_uint_literal()?).ok_or(Error::ZeroStride)?, + ); lexer.expect(Token::Paren(')'))?; } (Token::DoubleParen(']'), _) => break, - other => return Err(Error::Unexpected(other, ExpectedToken::TypeAttribute)), + other => return Err(Error::Unexpected(other, "type attribute")), } } self.scopes.pop(); } let storage_access = attribute.access; - let (name, name_span) = lexer.next_ident_with_span()?; - let handle = self.parse_type_decl_name( - lexer, - name, - name_span, - debug_name, - attribute, - type_arena, - const_arena, - )?; + let name = lexer.next_ident()?; + let handle = + self.parse_type_decl_name(lexer, name, debug_name, attribute, type_arena, const_arena)?; self.scopes.pop(); Ok((handle, storage_access)) } @@ -2556,7 +2031,7 @@ impl Parser { return Ok(()); } (Token::Word(word), _) => word, - other => return Err(Error::Unexpected(other, ExpectedToken::Statement)), + other => return Err(Error::Unexpected(other, "statement")), }; self.scopes.push(Scope::Statement); @@ -2594,9 +2069,6 @@ impl Parser { } block.extend(emitter.finish(context.expressions)); context.lookup_ident.insert(name, expr_id); - context - .named_expressions - .insert(expr_id, String::from(name)); } "var" => { enum Init { @@ -2768,7 +2240,7 @@ impl Parser { default = self.parse_block(lexer, context.reborrow(), false)?; } (Token::Paren('}'), _) => break, - other => return Err(Error::Unexpected(other, ExpectedToken::SwitchItem)), + other => return Err(Error::Unexpected(other, "switch item")), } } @@ -2801,19 +2273,16 @@ impl Parser { lexer.expect(Token::Paren('('))?; if !lexer.skip(Token::Separator(';')) { let num_statements = block.len(); - let (_, span) = lexer.capture_span(|lexer| { - self.parse_statement( - lexer, - context.reborrow(), - block, - is_uniform_control_flow, - ) - })?; - + self.parse_statement( + lexer, + context.reborrow(), + block, + is_uniform_control_flow, + )?; if block.len() != num_statements { match *block.last().unwrap() { crate::Statement::Store { .. } | crate::Statement::Call { .. } => {} - _ => return Err(Error::InvalidForInitializer(span)), + _ => return Err(Error::InvalidForInitializer), } } }; @@ -2870,15 +2339,13 @@ impl Parser { "textureStore" => { emitter.start(context.expressions); lexer.open_arguments()?; - let (image_name, image_span) = lexer.next_ident_with_span()?; - let image = context - .lookup_ident - .lookup(image_name, image_span.clone())?; + let image_name = lexer.next_ident()?; + let image = context.lookup_ident.lookup(image_name)?; lexer.expect(Token::Separator(','))?; let mut expr_context = context.as_expression(block, &mut emitter); let arrayed = match *expr_context.resolve_type(image)? { crate::TypeInner::Image { arrayed, .. } => arrayed, - _ => return Err(Error::BadTexture(image_span)), + _ => return Err(Error::BadTexture(image_name)), }; let coordinate = self.parse_general_expression(lexer, expr_context)?; let array_index = if arrayed { @@ -2948,14 +2415,14 @@ impl Parser { let mut bind_parser = BindingParser::default(); self.scopes.push(Scope::Attribute); loop { - let (word, span) = lexer.next_ident_with_span()?; - bind_parser.parse(lexer, word, span)?; + let word = lexer.next_ident()?; + bind_parser.parse(lexer, word)?; match lexer.next() { (Token::DoubleParen(']'), _) => { break; } (Token::Separator(','), _) => {} - other => return Err(Error::Unexpected(other, ExpectedToken::AttributeSeparator)), + other => return Err(Error::Unexpected(other, "attribute separator")), } } self.scopes.pop(); @@ -2984,10 +2451,7 @@ impl Parser { let mut ready = true; while !lexer.skip(Token::Paren(')')) { if !ready { - return Err(Error::Unexpected( - lexer.next(), - ExpectedToken::Token(Token::Separator(',')), - )); + return Err(Error::Unexpected(lexer.next(), "comma")); } let binding = self.parse_varying_binding(lexer)?; let (param_name, param_type, _access) = @@ -3019,13 +2483,11 @@ impl Parser { result, local_variables: Arena::new(), expressions, - named_expressions: crate::NamedExpressions::default(), body: Vec::new(), }; // read body let mut typifier = super::Typifier::new(); - let mut named_expressions = crate::FastHashMap::default(); fun.body = self.parse_block( lexer, StatementContext { @@ -3033,7 +2495,6 @@ impl Parser { typifier: &mut typifier, variables: &mut fun.local_variables, expressions: &mut fun.expressions, - named_expressions: &mut named_expressions, types: &mut module.types, constants: &mut module.constants, global_vars: &module.global_variables, @@ -3047,9 +2508,6 @@ impl Parser { // done self.scopes.pop(); - // Set named expressions after block parsing ends - fun.named_expressions = named_expressions; - Ok((fun, fun_name)) } @@ -3071,27 +2529,26 @@ impl Parser { let (mut bind_index, mut bind_group) = (None, None); self.scopes.push(Scope::Attribute); loop { - match lexer.next_ident_with_span()? { - ("binding", _) => { + match lexer.next_ident()? { + "binding" => { lexer.expect(Token::Paren('('))?; bind_index = Some(lexer.next_uint_literal()?); lexer.expect(Token::Paren(')'))?; } - ("block", _) => { + "block" => { is_block = true; } - ("group", _) => { + "group" => { lexer.expect(Token::Paren('('))?; bind_group = Some(lexer.next_uint_literal()?); lexer.expect(Token::Paren(')'))?; } - ("stage", _) => { + "stage" => { lexer.expect(Token::Paren('('))?; - let (ident, ident_span) = lexer.next_ident_with_span()?; - stage = Some(conv::map_shader_stage(ident, ident_span)?); + stage = Some(conv::map_shader_stage(lexer.next_ident()?)?); lexer.expect(Token::Paren(')'))?; } - ("workgroup_size", _) => { + "workgroup_size" => { lexer.expect(Token::Paren('('))?; for (i, size) in workgroup_size.iter_mut().enumerate() { *size = lexer.next_uint_literal()?; @@ -3101,7 +2558,7 @@ impl Parser { other => { return Err(Error::Unexpected( other, - ExpectedToken::WorkgroupSizeSeparator, + "workgroup size separator", )) } } @@ -3112,10 +2569,9 @@ impl Parser { } } } - ("early_depth_test", _) => { + "early_depth_test" => { let conservative = if lexer.skip(Token::Paren('(')) { - let (ident, ident_span) = lexer.next_ident_with_span()?; - let value = conv::map_conservative_depth(ident, ident_span)?; + let value = conv::map_conservative_depth(lexer.next_ident()?)?; lexer.expect(Token::Paren(')'))?; Some(value) } else { @@ -3123,16 +2579,14 @@ impl Parser { }; early_depth_test = Some(crate::EarlyDepthTest { conservative }); } - (_, word_span) => return Err(Error::UnknownAttribute(word_span)), + word => return Err(Error::UnknownAttribute(word)), } match lexer.next() { (Token::DoubleParen(']'), _) => { break; } (Token::Separator(','), _) => {} - other => { - return Err(Error::Unexpected(other, ExpectedToken::AttributeSeparator)) - } + other => return Err(Error::Unexpected(other, "attribute separator")), } } if let (Some(group), Some(index)) = (bind_group, bind_index) { @@ -3149,12 +2603,16 @@ impl Parser { (Token::Separator(';'), _) => {} (Token::Word("struct"), _) => { let name = lexer.next_ident()?; - let (members, span) = + let (members, span, alignment) = self.parse_struct_body(lexer, &mut module.types, &mut module.constants)?; let ty = module.types.fetch_or_append(crate::Type { name: Some(name.to_string()), inner: crate::TypeInner::Struct { - top_level: is_block, + level: if is_block { + crate::StructLevel::Root + } else { + crate::StructLevel::Normal { alignment } + }, members, span, }, @@ -3242,7 +2700,7 @@ impl Parser { } (Token::Word("fn"), _) => { let (function, name) = - self.parse_function_decl(lexer, module, lookup_global_expression)?; + self.parse_function_decl(lexer, module, &lookup_global_expression)?; match stage { Some(stage) => module.entry_points.push(crate::EntryPoint { name: name.to_string(), @@ -3257,7 +2715,7 @@ impl Parser { } } (Token::End, _) => return Ok(false), - other => return Err(Error::Unexpected(other, ExpectedToken::GlobalItem)), + other => return Err(Error::Unexpected(other, "global item")), } match binding { diff --git a/third_party/rust/naga/src/lib.rs b/third_party/rust/naga/src/lib.rs index b2ab989b9bce..058a5a9e8ef2 100644 --- a/third_party/rust/naga/src/lib.rs +++ b/third_party/rust/naga/src/lib.rs @@ -1,108 +1,22 @@ /*! Universal shader translator. -The central structure of the crate is [`Module`]. A `Module` contains: +The central structure of the crate is [`Module`]. -- [`EntryPoint`]s, the main functions for pipeline stages like vertex shading or - fragment shading, +To improve performance and reduce memory usage, most structures are stored +in an [`Arena`], and can be retrieved using the corresponding [`Handle`]. -- [`Function`]s, representing functions used by `EntryPoint`s and other `Function`s, +Functions are described in terms of statement trees. A statement may have +branching control flow, and it may be mutating. Statements refer to expressions. -- [`Constant`]s and [`GlobalVariable`]s used by `EntryPoint`s and `Function`s, and - -- [`Type`]s used by the above. - -The body of an `EntryPoint` or `Function` is represented using two types: - -- An [`Expression`] produces a value, but has no side effects or control flow. - `Expressions` include variable references, unary and binary operators, and so - on. - -- A [`Statement`] can have side effects and structured control flow. - `Statement`s do not produce a value, other than by storing one in some - designated place. `Statements` include blocks, conditionals, and loops, but also - operations that have side effects, like stores and function calls. - -`Statement`s form a tree, with pointers into the DAG of `Expression`s. - -Restricting side effects to statements simplifies analysis and code generation. -A Naga backend can generate code to evaluate an `Expression` however and -whenever it pleases, as long as it is certain to observe the side effects of all -previously executed `Statement`s. - -Many `Statement` variants use the [`Block`] type, which is simply `Vec`, -representing a series of statements executed in order. The body of an -`EntryPoint`s or `Function` is a `Block`, and `Statement` has a -[`Block`][Statement::Block] variant. - -To improve translator performance and reduce memory usage, most structures are -stored in an [`Arena`]. An `Arena` stores a series of `T` values, indexed by -[`Handle`](Handle) values, which are just wrappers around integer indexes. -For example, a `Function`'s expressions are stored in an `Arena`, -and compound expressions refer to their sub-expressions via `Handle` -values. (When examining the serialized form of a `Module`, note that the first -element of an `Arena` has an index of 1, not 0.) - -## Function Calls - -The Naga IR's representation of function calls is unusual. Most languages treat -function calls as expressions, but because calls may have side effects, Naga -represents them with [`Statement::Call`]. A call statement may designate a -particular `Expression` to represent its return value, if any, which can be used -by subsequent statements and their expressions. - -## `Expression` evaluation time and scope - -While the order of execution of [`Statement`]s is apparent from their structure, -it is not so obvious exactly when a given [`Expression`] should be evaluated, -since many `Statement`s and `Expression`s may refer to its value. But it is -essential to clearly specify an expression's evaluation time, since that -determines which statements' effects the expression should observe. It is also -helpful to backends to limit the visibility of an `Expression`'s value to a -portion of the statement tree. - -An `Expression` may only be used, whether by a `Statement` or another -`Expression`, if one of the following is true: - -- The expression is an [`Expression::Constant`], [`Expression::FunctionArgument`], or - [`Expression::GlobalVariable`]. - -- The expression is an [`Expression::LocalVariable`] that is either the - `pointer` (destination) of a [`Statement::Store`], or initialized by some - previously executed `Statement::Store`. - -- The expression is the `result` of a [`Statement::Call`], representing the - call's return value. The call must be 'in scope' for the use (see below). - -- The expression is included in the range of some [`Statement::Emit`] that is - 'in scope' for the use (see below). The [`Expression::needs_pre_emit`] method - returns `true` if the given expression does *not* need to be covered by an - `Emit` statement. - -The scope of an expression evaluated by an `Emit` statement covers the -subsequent expressions in that `Emit`, any following statements in the `Block` -to which that `Emit` belongs (if any) and their sub-statements (if any). - -If a `Call` statement has a `result` expression, then it is in scope for any -statements following the `Call` in the `Block` to which that `Call` belongs (if -any) and their sub-statements (if any). - -This means that, for example, an expression evaluated by some statement in a -nested `Block` is not available in the `Block`'s parents. Such a value would -need to be stored in a local variable to be carried upwards in the statement -tree. - -## Variables - -An [`Expression::LocalVariable`] or [`Expression::GlobalVariable`] produces a -pointer value referring to the variable's value. To retrieve the variable's -value, use an [`Expression::Load`], with the variable expression as its -`pointer` operand. To assign to a variable, use a [`Statement::Store`] with the -variable expression as its `pointer` operand. - -As an exception, [`Expression::GlobalVariable`]s referring to -[`GlobalVariable`]s whose `class` is [`StorageClass::Handle`] produce the -variable's value directly; no `Load` is needed. These global variables refer to -opaque values like samplers and images. +Expressions form a DAG, without any side effects. Expressions need to be +emitted in order to take effect. This happens in one of the following ways: + 1. Constants and function arguments are implicitly emitted at function start. + This corresponds to `expression.needs_pre_emit()` condition. + 2. Local and global variables are implicitly emitted. However, in order to use parts + of them in right-hand-side expressions, the `Expression::Load` must be explicitly emitted, + with an exception of `StorageClass::Handle` global variables. + 3. Result of `Statement::Call` is automatically emitted. + 4. `Statement::Emit` range is explicitly emitted. !*/ @@ -136,6 +50,7 @@ pub use crate::arena::{Arena, Handle, Range}; use std::{ collections::{HashMap, HashSet}, hash::BuildHasherDefault, + num::NonZeroU32, }; #[cfg(feature = "deserialize")] @@ -151,9 +66,6 @@ pub type FastHashMap = HashMap> /// Hash set that is faster but not resilient to DoS attacks. pub type FastHashSet = HashSet>; -/// Map of expressions that have associated variable names -pub(crate) type NamedExpressions = FastHashMap, String>; - /// Early fragment tests. In a standard situation if a driver determines that it is possible to /// switch on early depth test it will. Typical situations when early depth test is switched off: /// - Calling ```discard``` in a shader. @@ -257,6 +169,7 @@ pub enum BuiltIn { /// Number of bytes per scalar. pub type Bytes = u8; +pub type Alignment = NonZeroU32; /// Number of components in a vector. #[repr(u8)] @@ -445,6 +358,18 @@ pub enum ImageClass { Storage(StorageFormat), } +/// Qualifier of the type level, at which a struct can be used. +#[derive(Clone, Copy, Debug, Hash, Eq, Ord, PartialEq, PartialOrd)] +#[cfg_attr(feature = "serialize", derive(Serialize))] +#[cfg_attr(feature = "deserialize", derive(Deserialize))] +pub enum StructLevel { + /// This is a root level struct, it can't be nested inside + /// other composite types. + Root, + /// This is a normal struct, and it has to be aligned for nesting. + Normal { alignment: Alignment }, +} + /// A data type declared in the module. #[derive(Debug, PartialEq)] #[cfg_attr(feature = "serialize", derive(Serialize))] @@ -495,9 +420,8 @@ pub enum TypeInner { }, /// User-defined structure. Struct { - top_level: bool, + level: StructLevel, members: Vec, - //TODO: should this be unaligned? span: u32, }, /// Possibly multidimensional array of texels. @@ -562,7 +486,7 @@ pub enum Binding { } /// Pipeline binding information for global resources. -#[derive(Clone, Debug, Hash, PartialEq)] +#[derive(Clone, Debug, PartialEq)] #[cfg_attr(feature = "serialize", derive(Serialize))] #[cfg_attr(feature = "deserialize", derive(Deserialize))] pub struct ResourceBinding { @@ -795,58 +719,9 @@ bitflags::bitflags! { #[cfg_attr(feature = "deserialize", derive(Deserialize))] pub enum Expression { /// Array access with a computed index. - /// - /// ## Typing rules - /// - /// The `base` operand must be some composite type: [`Vector`], [`Matrix`], - /// [`Array`], a [`Pointer`] to one of those, or a [`ValuePointer`] with a - /// `size`. - /// - /// The `index` operand must be an integer, signed or unsigned. - /// - /// Indexing a [`Vector`] or [`Array`] produces a value of its element type. - /// Indexing a [`Matrix`] produces a [`Vector`]. - /// - /// Indexing a [`Pointer`] to an [`Array`] produces a [`Pointer`] to its - /// `base` type, taking on the `Pointer`'s storage class. - /// - /// Indexing a [`Pointer`] to a [`Vector`] produces a [`ValuePointer`] whose - /// size is `None`, taking on the [`Vector`]'s scalar kind and width and the - /// [`Pointer`]'s storage class. - /// - /// Indexing a [`Pointer`] to a [`Matrix`] produces a [`ValuePointer`] for a - /// column of the matrix: its size is the matrix's height, its `kind` is - /// [`Float`], and it inherits the [`Matrix`]'s width and the [`Pointer`]'s - /// storage class. - /// - /// ## Dynamic indexing restrictions - /// - /// To accommodate restrictions in some of the shader languages that Naga - /// targets, it is not permitted to subscript a matrix or array with a - /// dynamically computed index unless that matrix or array appears behind a - /// pointer. In other words, if the inner type of `base` is [`Array`] or - /// [`Matrix`], then `index` must be a constant. But if the type of `base` - /// is a [`Pointer`] to an array or matrix or a [`ValuePointer`] with a - /// `size`, then the index may be any expression of integer type. - /// - /// You can use the [`Expression::is_dynamic_index`] method to determine - /// whether a given index expression requires matrix or array base operands - /// to be behind a pointer. - /// - /// (It would be simpler to always require the use of `AccessIndex` when - /// subscripting arrays and matrices that are not behind pointers, but to - /// accommodate existing front ends, Naga also permits `Access`, with a - /// restricted `index`.) - /// - /// [`Vector`]: TypeInner::Vector - /// [`Matrix`]: TypeInner::Matrix - /// [`Array`]: TypeInner::Array - /// [`Pointer`]: TypeInner::Pointer - /// [`ValuePointer`]: TypeInner::ValuePointer - /// [`Float`]: ScalarKind::Float Access { base: Handle, - index: Handle, + index: Handle, //int }, /// Array access with a known index. AccessIndex { @@ -916,9 +791,6 @@ pub enum Expression { right: Handle, }, /// Select between two values based on a condition. - /// - /// Note that, because expressions have no side effects, it is unobservable - /// whether the non-selected branch is evaluated. Select { /// Boolean expression condition: Handle, @@ -943,7 +815,7 @@ pub enum Expression { arg1: Option>, arg2: Option>, }, - /// Cast a simple type to another kind. + /// Cast a simply type to another kind. As { /// Source expression, which can only be a scalar or a vector. expr: Handle, @@ -989,10 +861,6 @@ pub struct SwitchCase { #[cfg_attr(feature = "deserialize", derive(Deserialize))] pub enum Statement { /// Emit a range of expressions, visible to all statements that follow in this block. - /// - /// See the [module-level documentation][emit] for details. - /// - /// [emit]: index.html#expression-evaluation-time-and-scope Emit(Range), /// A block containing more statements, to be executed sequentially. Block(Block), @@ -1095,8 +963,6 @@ pub struct Function { pub local_variables: Arena, /// Expressions used inside this function. pub expressions: Arena, - /// Map of expressions that have associated variable names - pub named_expressions: NamedExpressions, /// Block of instructions comprising the body of the function. pub body: Block, } @@ -1128,7 +994,7 @@ pub struct EntryPoint { /// To create a new module, use the `Default` implementation. /// Alternatively, you can load an existing shader using one of the [available front ends][front]. /// -/// When finished, you can export modules using one of the [available backends][back]. +/// When finished, you can export modules using one of the [available back ends][back]. #[derive(Debug, Default)] #[cfg_attr(feature = "serialize", derive(Serialize))] #[cfg_attr(feature = "deserialize", derive(Deserialize))] diff --git a/third_party/rust/naga/src/proc/interpolator.rs b/third_party/rust/naga/src/proc/interpolator.rs index 3b550782c91a..7f7296e50d3d 100644 --- a/third_party/rust/naga/src/proc/interpolator.rs +++ b/third_party/rust/naga/src/proc/interpolator.rs @@ -27,7 +27,7 @@ impl crate::Module { /// binding that can vary, and everything else either defaults to flat, or /// requires an explicit flat qualifier/attribute/what-have-you. /// - /// [`Binding`]: crate::Binding + /// [`Binding`]: super::Binding pub fn apply_common_default_interpolation(&mut self) { use crate::{Binding, ScalarKind, Type, TypeInner}; @@ -41,49 +41,36 @@ impl crate::Module { ty: Handle, types: &mut Arena, ) { - let inner = &mut types.get_mut(ty).inner; - if let TypeInner::Struct { - members: ref mut m, .. - } = *inner - { + match types.get_mut(ty).inner { // A struct. It's the individual members we care about, so recurse. + TypeInner::Struct { + members: ref mut m, .. + } => { + // To choose the right interpolations for `members`, we must consult other + // elements of `types`. But both `members` and the types it refers to are stored + // in `types`, and Rust won't let us mutate one element of the `Arena`'s `Vec` + // while reading others. + // + // So, temporarily swap the member list out its type, assign appropriate + // interpolations to its members, and then swap the list back in. + use std::mem::replace; + let mut members = replace(m, vec![]); - // To choose the right interpolations for `members`, we must consult other - // elements of `types`. But both `members` and the types it refers to are stored - // in `types`, and Rust won't let us mutate one element of the `Arena`'s `Vec` - // while reading others. - // - // So, temporarily swap the member list out its type, assign appropriate - // interpolations to its members, and then swap the list back in. - use std::mem; - let mut members = mem::take(m); + for member in &mut members { + default_binding_or_struct(&mut member.binding, member.ty, types); + } - for member in &mut members { - default_binding_or_struct(&mut member.binding, member.ty, types); + // Swap the member list back in. It's essential that we call `types.get_mut` + // afresh here, rather than just using `m`: it's only because `m` was dead that + // we were able to pass `types` to the recursive call. + match types.get_mut(ty).inner { + TypeInner::Struct { + members: ref mut m, .. + } => replace(m, members), + _ => unreachable!("ty must be a struct"), + }; } - // Swap the member list back in. It's essential that we call `types.get_mut` - // afresh here, rather than just using `m`: it's only because `m` was dead that - // we were able to pass `types` to the recursive call. - match types.get_mut(ty).inner { - TypeInner::Struct { - members: ref mut m, .. - } => mem::replace(m, members), - _ => unreachable!("ty must be a struct"), - }; - - return; - } - - // For all other types, a binding is required. Missing bindings will - // be caught during validation, but this processor is meant for use - // by front ends before validation, so just return for now. - let binding = match binding.as_mut() { - None => return, - Some(binding) => binding, - }; - - match *inner { // Some interpolatable type. // // GLSL has 64-bit floats, but it won't interpolate them. WGSL and MSL only have @@ -98,6 +85,9 @@ impl crate::Module { width: 4, .. } => { + // unwrap: all `EntryPoint` arguments or return values must either be structures + // or have a `Binding`. + let binding = binding.as_mut().unwrap(); if let Binding::Location { ref mut interpolation, ref mut sampling, @@ -116,6 +106,9 @@ impl crate::Module { // Some type that can't be interpolated. _ => { + // unwrap: all `EntryPoint` arguments or return values must either be structures + // or have a `Binding`. + let binding = binding.as_mut().unwrap(); if let Binding::Location { ref mut interpolation, ref mut sampling, diff --git a/third_party/rust/naga/src/proc/mod.rs b/third_party/rust/naga/src/proc/mod.rs index a524981f555c..49bab5528d77 100644 --- a/third_party/rust/naga/src/proc/mod.rs +++ b/third_party/rust/naga/src/proc/mod.rs @@ -1,12 +1,10 @@ //! Module processing functionality. mod interpolator; -mod layouter; mod namer; mod terminator; mod typifier; -pub use layouter::{Alignment, InvalidBaseType, Layouter, TypeLayout}; pub use namer::{EntryPointIndex, NameKey, Namer}; pub use terminator::ensure_block_returns; pub use typifier::{ResolveContext, ResolveError, TypeResolution}; @@ -183,28 +181,6 @@ impl crate::Expression { _ => false, } } - - /// Return true if this expression is a dynamic array index, for [`Access`]. - /// - /// This method returns true if this expression is a dynamically computed - /// index, and as such can only be used to index matrices and arrays when - /// they appear behind a pointer. See the documentation for [`Access`] for - /// details. - /// - /// Note, this does not check the _type_ of the given expression. It's up to - /// the caller to establish that the `Access` expression is well-typed - /// through other means, like [`ResolveContext`]. - /// - /// [`Access`]: crate::Expression::Access - /// [`ResolveContext`]: crate::proc::ResolveContext - pub fn is_dynamic_index(&self, module: &crate::Module) -> bool { - if let Self::Constant(handle) = *self { - let constant = &module.constants[handle]; - constant.specialization.is_some() - } else { - true - } - } } impl crate::SampleLevel { @@ -267,24 +243,3 @@ impl std::hash::Hash for crate::ScalarValue { } } } - -impl super::SwizzleComponent { - pub const XYZW: [Self; 4] = [Self::X, Self::Y, Self::Z, Self::W]; - - pub fn index(&self) -> u32 { - match *self { - Self::X => 0, - Self::Y => 1, - Self::Z => 2, - Self::W => 3, - } - } - pub fn from_index(idx: u32) -> Self { - match idx { - 0 => Self::X, - 1 => Self::Y, - 2 => Self::Z, - _ => Self::W, - } - } -} diff --git a/third_party/rust/naga/src/proc/namer.rs b/third_party/rust/naga/src/proc/namer.rs index b8f03b25ce36..5e8ba6132997 100644 --- a/third_party/rust/naga/src/proc/namer.rs +++ b/third_party/rust/naga/src/proc/namer.rs @@ -1,4 +1,4 @@ -use crate::{arena::Handle, FastHashMap, FastHashSet}; +use crate::{arena::Handle, FastHashMap}; use std::collections::hash_map::Entry; pub type EntryPointIndex = u16; @@ -21,10 +21,7 @@ pub enum NameKey { /// that may need identifiers in a textual backend. #[derive(Default)] pub struct Namer { - unique: FastHashMap<(String, u32), u32>, - keywords: FastHashSet, - /// Currently active namespace. - namespace_index: u32, + unique: FastHashMap, reserved_prefixes: Vec, } @@ -51,46 +48,17 @@ impl Namer { base } - /// Helper function that return unique name without cache update. - /// This function should be used **after** [`Namer`](crate::proc::Namer) initialization by [`reset`](Self::reset()) function. - pub fn call_unique(&mut self, string: &str) -> String { - let base = self.sanitize(string); - match self.unique.entry((base, self.namespace_index)) { - Entry::Occupied(mut e) => { - *e.get_mut() += 1; - format!("{}{}", e.key().0, e.get()) - } - Entry::Vacant(e) => { - let name = &e.key().0; - if self.keywords.contains(&e.key().0) { - let name = format!("{}1", name); - e.insert(1); - name - } else { - name.to_string() - } - } - } - } - pub fn call(&mut self, label_raw: &str) -> String { let base = self.sanitize(label_raw); - match self.unique.entry((base, self.namespace_index)) { + match self.unique.entry(base) { Entry::Occupied(mut e) => { *e.get_mut() += 1; - format!("{}{}", e.key().0, e.get()) + format!("{}{}", e.key(), e.get()) } Entry::Vacant(e) => { - let name = &e.key().0; - if self.keywords.contains(&e.key().0) { - let name = format!("{}1", name); - e.insert(1); - name - } else { - let name = name.to_string(); - e.insert(0); - name - } + let name = e.key().to_string(); + e.insert(0); + name } } } @@ -102,14 +70,6 @@ impl Namer { }) } - fn namespace(&mut self, f: impl FnOnce(&mut Self)) { - self.namespace_index += 1; - f(self); - let current_ns = self.namespace_index; - self.unique.retain(|&(_, ns), _| ns != current_ns); - self.namespace_index -= 1; - } - pub fn reset( &mut self, module: &crate::Module, @@ -122,9 +82,11 @@ impl Namer { .extend(reserved_prefixes.iter().map(|string| string.to_string())); self.unique.clear(); - self.keywords.clear(); - self.keywords - .extend(reserved_keywords.iter().map(|string| (string.to_string()))); + self.unique.extend( + reserved_keywords + .iter() + .map(|string| (string.to_string(), 0)), + ); let mut temp = String::new(); for (ty_handle, ty) in module.types.iter() { @@ -132,42 +94,10 @@ impl Namer { output.insert(NameKey::Type(ty_handle), ty_name); if let crate::TypeInner::Struct { ref members, .. } = ty.inner { - // struct members have their own namespace, because access is always prefixed - self.namespace(|namer| { - for (index, member) in members.iter().enumerate() { - let name = namer.call_or(&member.name, "member"); - output.insert(NameKey::StructMember(ty_handle, index as u32), name); - } - }) - } - } - - for (ep_index, ep) in module.entry_points.iter().enumerate() { - let ep_name = self.call(&ep.name); - output.insert(NameKey::EntryPoint(ep_index as _), ep_name); - for (index, arg) in ep.function.arguments.iter().enumerate() { - let name = self.call_or(&arg.name, "param"); - output.insert( - NameKey::EntryPointArgument(ep_index as _, index as u32), - name, - ); - } - for (handle, var) in ep.function.local_variables.iter() { - let name = self.call_or(&var.name, "local"); - output.insert(NameKey::EntryPointLocal(ep_index as _, handle), name); - } - } - - for (fun_handle, fun) in module.functions.iter() { - let fun_name = self.call_or(&fun.name, "function"); - output.insert(NameKey::Function(fun_handle), fun_name); - for (index, arg) in fun.arguments.iter().enumerate() { - let name = self.call_or(&arg.name, "param"); - output.insert(NameKey::FunctionArgument(fun_handle, index as u32), name); - } - for (handle, var) in fun.local_variables.iter() { - let name = self.call_or(&var.name, "local"); - output.insert(NameKey::FunctionLocal(fun_handle, handle), name); + for (index, member) in members.iter().enumerate() { + let name = self.call_or(&member.name, "member"); + output.insert(NameKey::StructMember(ty_handle, index as u32), name); + } } } @@ -226,5 +156,34 @@ impl Namer { let name = self.call(label); output.insert(NameKey::Constant(handle), name); } + + for (fun_handle, fun) in module.functions.iter() { + let fun_name = self.call_or(&fun.name, "function"); + output.insert(NameKey::Function(fun_handle), fun_name); + for (index, arg) in fun.arguments.iter().enumerate() { + let name = self.call_or(&arg.name, "param"); + output.insert(NameKey::FunctionArgument(fun_handle, index as u32), name); + } + for (handle, var) in fun.local_variables.iter() { + let name = self.call_or(&var.name, "local"); + output.insert(NameKey::FunctionLocal(fun_handle, handle), name); + } + } + + for (ep_index, ep) in module.entry_points.iter().enumerate() { + let ep_name = self.call(&ep.name); + output.insert(NameKey::EntryPoint(ep_index as _), ep_name); + for (index, arg) in ep.function.arguments.iter().enumerate() { + let name = self.call_or(&arg.name, "param"); + output.insert( + NameKey::EntryPointArgument(ep_index as _, index as u32), + name, + ); + } + for (handle, var) in ep.function.local_variables.iter() { + let name = self.call_or(&var.name, "local"); + output.insert(NameKey::EntryPointLocal(ep_index as _, handle), name); + } + } } } diff --git a/third_party/rust/naga/src/proc/typifier.rs b/third_party/rust/naga/src/proc/typifier.rs index c8d2b02df5a3..f39e65f1b4f8 100644 --- a/third_party/rust/naga/src/proc/typifier.rs +++ b/third_party/rust/naga/src/proc/typifier.rs @@ -111,7 +111,6 @@ pub enum ResolveError { pub struct ResolveContext<'a> { pub constants: &'a Arena, - pub types: &'a Arena, pub global_vars: &'a Arena, pub local_vars: &'a Arena, pub functions: &'a Arena, @@ -122,21 +121,13 @@ impl<'a> ResolveContext<'a> { pub fn resolve( &self, expr: &crate::Expression, + types: &'a Arena, past: impl Fn(Handle) -> &'a TypeResolution, ) -> Result { use crate::TypeInner as Ti; - let types = self.types; Ok(match *expr { crate::Expression::Access { base, .. } => match *past(base).inner_with(types) { - // Arrays and matrices can only be indexed dynamically behind a - // pointer, but that's a validation error, not a type error, so - // go ahead provide a type here. Ti::Array { base, .. } => TypeResolution::Handle(base), - Ti::Matrix { rows, width, .. } => TypeResolution::Value(Ti::Vector { - size: rows, - kind: crate::ScalarKind::Float, - width, - }), Ti::Vector { size: _, kind, @@ -153,39 +144,37 @@ impl<'a> ResolveContext<'a> { width, class, }), - Ti::Pointer { base, class } => { - TypeResolution::Value(match types[base].inner { - Ti::Array { base, .. } => Ti::Pointer { base, class }, - Ti::Vector { - size: _, - kind, - width, - } => Ti::ValuePointer { - size: None, - kind, - width, - class, - }, - // Matrices are only dynamically indexed behind a pointer - Ti::Matrix { - columns: _, - rows, - width, - } => Ti::ValuePointer { - kind: crate::ScalarKind::Float, - size: Some(rows), - width, - class, - }, - ref other => { - log::error!("Access sub-type {:?}", other); - return Err(ResolveError::InvalidSubAccess { - ty: base, - indexed: false, - }); - } - }) - } + Ti::Pointer { base, class } => TypeResolution::Value(match types[base].inner { + Ti::Array { base, .. } => Ti::Pointer { base, class }, + Ti::Vector { + size: _, + kind, + width, + } => Ti::ValuePointer { + size: None, + kind, + width, + class, + }, + // Matrices are only dynamically indexed behind a pointer + Ti::Matrix { + columns: _, + rows, + width, + } => Ti::ValuePointer { + kind: crate::ScalarKind::Float, + size: Some(rows), + width, + class, + }, + ref other => { + log::error!("Access sub-type {:?}", other); + return Err(ResolveError::InvalidSubAccess { + ty: base, + indexed: false, + }); + } + }), ref other => { log::error!("Access type {:?}", other); return Err(ResolveError::InvalidAccess { diff --git a/third_party/rust/naga/src/valid/analyzer.rs b/third_party/rust/naga/src/valid/analyzer.rs index aae5dcddefc6..494aabf9e148 100644 --- a/third_party/rust/naga/src/valid/analyzer.rs +++ b/third_party/rust/naga/src/valid/analyzer.rs @@ -299,6 +299,7 @@ impl FunctionInfo { expression: &crate::Expression, expression_arena: &Arena, other_functions: &[FunctionInfo], + type_arena: &Arena, resolve_context: &ResolveContext, ) -> Result<(), ExpressionError> { use crate::{Expression as E, SampleLevel as Sl}; @@ -512,7 +513,8 @@ impl FunctionInfo { }, }; - let ty = resolve_context.resolve(expression, |h| &self.expressions[h.index()].ty)?; + let ty = + resolve_context.resolve(expression, type_arena, |h| &self.expressions[h.index()].ty)?; self.expressions[handle.index()] = ExpressionInfo { uniformity, ref_count: 0, @@ -569,11 +571,7 @@ impl FunctionInfo { S::Break | S::Continue => FunctionUniformity::new(), S::Kill => FunctionUniformity { result: Uniformity::new(), - exit: if disruptor.is_some() { - ExitFlags::MAY_KILL - } else { - ExitFlags::empty() - }, + exit: ExitFlags::MAY_KILL, }, S::Barrier(_) => FunctionUniformity { result: Uniformity { @@ -637,11 +635,8 @@ impl FunctionInfo { non_uniform_result: value.and_then(|expr| self.add_ref(expr)), requirements: UniformityRequirements::empty(), }, - exit: if disruptor.is_some() { - ExitFlags::MAY_RETURN - } else { - ExitFlags::empty() - }, + //TODO: if we are in the uniform control flow, should this still be an exit flag? + exit: ExitFlags::MAY_RETURN, }, // Here and below, the used expressions are already emitted, // and their results do not affect the function return value, @@ -711,7 +706,6 @@ impl ModuleInfo { }; let resolve_context = ResolveContext { constants: &module.constants, - types: &module.types, global_vars: &module.global_variables, local_vars: &fun.local_variables, functions: &module.functions, @@ -724,6 +718,7 @@ impl ModuleInfo { expr, &fun.expressions, &self.functions, + &module.types, &resolve_context, ) { return Err(FunctionError::Expression { handle, error }); @@ -815,15 +810,21 @@ fn uniform_control_flow() { }; let resolve_context = ResolveContext { constants: &constant_arena, - types: &type_arena, global_vars: &global_var_arena, local_vars: &Arena::new(), functions: &Arena::new(), arguments: &[], }; for (handle, expression) in expressions.iter() { - info.process_expression(handle, expression, &expressions, &[], &resolve_context) - .unwrap(); + info.process_expression( + handle, + expression, + &expressions, + &[], + &type_arena, + &resolve_context, + ) + .unwrap(); } assert_eq!(info[non_uniform_global_expr].ref_count, 1); assert_eq!(info[uniform_global_expr].ref_count, 1); diff --git a/third_party/rust/naga/src/valid/expression.rs b/third_party/rust/naga/src/valid/expression.rs index 561a088a7e27..c87dedcc1d56 100644 --- a/third_party/rust/naga/src/valid/expression.rs +++ b/third_party/rust/naga/src/valid/expression.rs @@ -19,8 +19,6 @@ pub enum ExpressionError { InvalidIndexType(Handle), #[error("Accessing index {1} is out of {0:?} bounds")] IndexOutOfBounds(Handle, u32), - #[error("The expression {0:?} may only be indexed by a constant")] - IndexMustBeConstant(Handle), #[error("Function argument {0:?} doesn't exist")] FunctionArgumentDoesntExist(u32), #[error("Constant {0:?} doesn't exist")] @@ -144,16 +142,17 @@ impl super::Validator { let stages = match *expression { E::Access { base, index } => { - // See the documentation for `Expression::Access`. - let dynamic_indexing_restricted = match *resolver.resolve(base)? { - Ti::Vector { .. } => false, - Ti::Matrix { .. } | Ti::Array { .. } => true, - Ti::Pointer { .. } | Ti::ValuePointer { size: Some(_), .. } => false, + match *resolver.resolve(base)? { + Ti::Vector { .. } + | Ti::Matrix { .. } + | Ti::Array { .. } + | Ti::Pointer { .. } + | Ti::ValuePointer { size: Some(_), .. } => {} ref other => { log::error!("Indexing of {:?}", other); return Err(ExpressionError::InvalidBaseType(base)); } - }; + } match *resolver.resolve(index)? { //TODO: only allow one of these Ti::Scalar { @@ -169,11 +168,6 @@ impl super::Validator { return Err(ExpressionError::InvalidIndexType(index)); } } - if dynamic_indexing_restricted - && function.expressions[index].is_dynamic_index(module) - { - return Err(ExpressionError::IndexMustBeConstant(base)); - } ShaderStages::all() } E::AccessIndex { base, index } => { @@ -582,15 +576,7 @@ impl super::Validator { let left_inner = resolver.resolve(left)?; let right_inner = resolver.resolve(right)?; let good = match op { - Bo::Add | Bo::Subtract => match *left_inner { - Ti::Scalar { kind, .. } | Ti::Vector { kind, .. } => match kind { - Sk::Uint | Sk::Sint | Sk::Float => left_inner == right_inner, - Sk::Bool => false, - }, - Ti::Matrix { .. } => left_inner == right_inner, - _ => false, - }, - Bo::Divide | Bo::Modulo => match *left_inner { + Bo::Add | Bo::Subtract | Bo::Divide | Bo::Modulo => match *left_inner { Ti::Scalar { kind, .. } | Ti::Vector { kind, .. } => match kind { Sk::Uint | Sk::Sint | Sk::Float => left_inner == right_inner, Sk::Bool => false, diff --git a/third_party/rust/naga/src/valid/function.rs b/third_party/rust/naga/src/valid/function.rs index 60b4310524e6..f49bb70a1d45 100644 --- a/third_party/rust/naga/src/valid/function.rs +++ b/third_party/rust/naga/src/valid/function.rs @@ -558,7 +558,7 @@ impl super::Validator { for (index, argument) in fun.arguments.iter().enumerate() { if !self.types[argument.ty.index()] .flags - .contains(TypeFlags::DATA | TypeFlags::SIZED) + .contains(TypeFlags::DATA) { return Err(FunctionError::InvalidArgumentType { index, diff --git a/third_party/rust/naga/src/valid/interface.rs b/third_party/rust/naga/src/valid/interface.rs index d4050aea1d49..6a9949a652e5 100644 --- a/third_party/rust/naga/src/valid/interface.rs +++ b/third_party/rust/naga/src/valid/interface.rs @@ -41,18 +41,14 @@ pub enum VaryingError { InvalidInterpolation, #[error("Interpolation must be specified on vertex shader outputs and fragment shader inputs")] MissingInterpolation, - #[error("Built-in {0:?} is not available at this stage")] + #[error("BuiltIn {0:?} is not available at this stage")] InvalidBuiltInStage(crate::BuiltIn), - #[error("Built-in type for {0:?} is invalid")] + #[error("BuiltIn type for {0:?} is invalid")] InvalidBuiltInType(crate::BuiltIn), - #[error("Entry point arguments and return values must all have bindings")] - MissingBinding, #[error("Struct member {0} is missing a binding")] MemberMissingBinding(u32), #[error("Multiple bindings at location {location} are present")] BindingCollision { location: u32 }, - #[error("Built-in {0:?} is present more than once")] - DuplicateBuiltIn(crate::BuiltIn), } #[derive(Clone, Debug, thiserror::Error)] @@ -98,7 +94,6 @@ struct VaryingContext<'a> { output: bool, types: &'a Arena, location_mask: &'a mut BitSet, - built_in_mask: u32, } impl VaryingContext<'_> { @@ -110,12 +105,6 @@ impl VaryingContext<'_> { let ty_inner = &self.types[self.ty].inner; match *binding { crate::Binding::BuiltIn(built_in) => { - let bit = 1 << built_in as u32; - if self.built_in_mask & bit != 0 { - return Err(VaryingError::DuplicateBuiltIn(built_in)); - } - self.built_in_mask |= bit; - let width = 4; let (visible, type_good) = match built_in { Bi::BaseInstance | Bi::BaseVertex | Bi::InstanceIndex | Bi::VertexIndex => ( @@ -266,14 +255,14 @@ impl VaryingContext<'_> { Ok(()) } - fn validate(&mut self, binding: Option<&crate::Binding>) -> Result<(), VaryingError> { + fn validate(mut self, binding: Option<&crate::Binding>) -> Result<(), VaryingError> { match binding { Some(binding) => self.validate_impl(binding), None => { match self.types[self.ty].inner { //TODO: check the member types crate::TypeInner::Struct { - top_level: false, + level: crate::StructLevel::Normal { .. }, ref members, .. } => { @@ -287,7 +276,7 @@ impl VaryingContext<'_> { } } } - _ => return Err(VaryingError::MissingBinding), + _ => return Err(VaryingError::InvalidType(self.ty)), } Ok(()) } @@ -314,7 +303,7 @@ impl super::Validator { } ( crate::StorageAccess::all(), - TypeFlags::DATA | TypeFlags::HOST_SHARED | TypeFlags::TOP_LEVEL, + TypeFlags::DATA | TypeFlags::HOST_SHARED | TypeFlags::BLOCK, true, ) } @@ -326,10 +315,7 @@ impl super::Validator { } ( crate::StorageAccess::empty(), - TypeFlags::DATA - | TypeFlags::SIZED - | TypeFlags::HOST_SHARED - | TypeFlags::TOP_LEVEL, + TypeFlags::DATA | TypeFlags::SIZED | TypeFlags::HOST_SHARED | TypeFlags::BLOCK, true, ) } @@ -346,11 +332,9 @@ impl super::Validator { }; (access, TypeFlags::empty(), true) } - crate::StorageClass::Private | crate::StorageClass::WorkGroup => ( - crate::StorageAccess::empty(), - TypeFlags::DATA | TypeFlags::SIZED, - false, - ), + crate::StorageClass::Private | crate::StorageClass::WorkGroup => { + (crate::StorageAccess::empty(), TypeFlags::DATA, false) + } crate::StorageClass::PushConstant => { if !self.capabilities.contains(Capabilities::PUSH_CONSTANT) { return Err(GlobalVariableError::UnsupportedCapability( @@ -359,7 +343,7 @@ impl super::Validator { } ( crate::StorageAccess::LOAD, - TypeFlags::DATA | TypeFlags::HOST_SHARED | TypeFlags::SIZED, + TypeFlags::DATA | TypeFlags::HOST_SHARED, false, ) } @@ -413,37 +397,33 @@ impl super::Validator { crate::ShaderStage::Compute => ShaderStages::COMPUTE, }; - let info = self.validate_function(&ep.function, module, mod_info)?; + let info = self.validate_function(&ep.function, module, &mod_info)?; if !info.available_stages.contains(stage_bit) { return Err(EntryPointError::ForbiddenStageOperations); } self.location_mask.clear(); - let mut argument_built_ins = 0; for (index, fa) in ep.function.arguments.iter().enumerate() { - let mut ctx = VaryingContext { + let ctx = VaryingContext { ty: fa.ty, stage: ep.stage, output: false, types: &module.types, location_mask: &mut self.location_mask, - built_in_mask: argument_built_ins, }; ctx.validate(fa.binding.as_ref()) .map_err(|e| EntryPointError::Argument(index as u32, e))?; - argument_built_ins = ctx.built_in_mask; } self.location_mask.clear(); if let Some(ref fr) = ep.function.result { - let mut ctx = VaryingContext { + let ctx = VaryingContext { ty: fr.ty, stage: ep.stage, output: true, types: &module.types, location_mask: &mut self.location_mask, - built_in_mask: 0, }; ctx.validate(fr.binding.as_ref()) .map_err(EntryPointError::Result)?; diff --git a/third_party/rust/naga/src/valid/mod.rs b/third_party/rust/naga/src/valid/mod.rs index a6dc0f2c4e2c..c34eeebd2c18 100644 --- a/third_party/rust/naga/src/valid/mod.rs +++ b/third_party/rust/naga/src/valid/mod.rs @@ -7,7 +7,6 @@ mod r#type; use crate::{ arena::{Arena, Handle}, - proc::{InvalidBaseType, Layouter}, FastHashSet, }; use bit_set::BitSet; @@ -92,7 +91,6 @@ pub struct Validator { flags: ValidationFlags, capabilities: Capabilities, types: Vec, - layouter: Layouter, location_mask: BitSet, bind_group_masks: Vec, select_cases: FastHashSet, @@ -114,8 +112,6 @@ pub enum ConstantError { #[derive(Clone, Debug, thiserror::Error)] pub enum ValidationError { - #[error(transparent)] - Layouter(#[from] InvalidBaseType), #[error("Type {handle:?} '{name}' is invalid")] Type { handle: Handle, @@ -200,7 +196,6 @@ impl Validator { flags, capabilities, types: Vec::new(), - layouter: Layouter::default(), location_mask: BitSet::new(), bind_group_masks: Vec::new(), select_cases: FastHashSet::default(), @@ -251,7 +246,6 @@ impl Validator { /// Check the given module to be valid. pub fn validate(&mut self, module: &crate::Module) -> Result { self.reset_types(module.types.len()); - self.layouter.update(&module.types, &module.constants)?; if self.flags.contains(ValidationFlags::CONSTANTS) { for (handle, constant) in module.constants.iter() { @@ -264,6 +258,7 @@ impl Validator { } } + // doing after the globals, so that `type_flags` is ready for (handle, ty) in module.types.iter() { let ty_info = self .validate_type(handle, &module.types, &module.constants) diff --git a/third_party/rust/naga/src/valid/type.rs b/third_party/rust/naga/src/valid/type.rs index e9f888a63bc8..056af0508db5 100644 --- a/third_party/rust/naga/src/valid/type.rs +++ b/third_party/rust/naga/src/valid/type.rs @@ -1,8 +1,5 @@ use super::Capabilities; -use crate::{ - arena::{Arena, Handle}, - proc::Alignment, -}; +use crate::arena::{Arena, Handle}; bitflags::bitflags! { #[repr(transparent)] @@ -16,7 +13,7 @@ bitflags::bitflags! { /// Can be used for host-shareable structures. const HOST_SHARED = 0x8; /// This is a top-level host-shareable type. - const TOP_LEVEL = 0x10; + const BLOCK = 0x10; } } @@ -26,6 +23,12 @@ pub enum Disalignment { ArrayStride { stride: u32, alignment: u32 }, #[error("The struct span {span}, is not a multiple of the required alignment {alignment}")] StructSpan { span: u32, alignment: u32 }, + #[error("The struct span {alignment}, is not a multiple of the member[{member_index}] alignment {member_alignment}")] + StructAlignment { + alignment: u32, + member_index: u32, + member_alignment: u32, + }, #[error("The struct member[{index}] offset {offset} is not a multiple of the required alignment {alignment}")] MemberOffset { index: u32, @@ -57,34 +60,30 @@ pub enum TypeError { #[error("Structure member[{index}] at {offset} overlaps the previous member")] MemberOverlap { index: u32, offset: u32 }, #[error( - "Structure member[{index}] at {offset} and size {size} crosses the structure boundary of size {span}" + "Structure member[{index}] at {offset} and size {size} crosses the structure boundary" )] - MemberOutOfBounds { - index: u32, - offset: u32, - size: u32, - span: u32, - }, - #[error("The composite type contains a top-level structure")] - NestedTopLevel, + MemberOutOfBounds { index: u32, offset: u32, size: u32 }, + #[error("The composite type contains a block structure")] + NestedBlock, } // Only makes sense if `flags.contains(HOST_SHARED)` -type LayoutCompatibility = Result, (Handle, Disalignment)>; +type LayoutCompatibility = Result, (Handle, Disalignment)>; fn check_member_layout( accum: &mut LayoutCompatibility, member: &crate::StructMember, member_index: u32, member_layout: LayoutCompatibility, - parent_handle: Handle, + struct_level: crate::StructLevel, + ty_handle: Handle, ) { *accum = match (*accum, member_layout) { (Ok(cur_alignment), Ok(align)) => { let align = align.unwrap().get(); if member.offset % align != 0 { Err(( - parent_handle, + ty_handle, Disalignment::MemberOffset { index: member_index, offset: member.offset, @@ -92,14 +91,32 @@ fn check_member_layout( }, )) } else { - let combined_alignment = ((cur_alignment.unwrap().get() - 1) | (align - 1)) + 1; - Ok(Alignment::new(combined_alignment)) + match struct_level { + crate::StructLevel::Normal { alignment } if alignment.get() % align != 0 => { + Err(( + ty_handle, + Disalignment::StructAlignment { + alignment: alignment.get(), + member_index, + member_alignment: align, + }, + )) + } + _ => { + let combined_alignment = + ((cur_alignment.unwrap().get() - 1) | (align - 1)) + 1; + Ok(crate::Alignment::new(combined_alignment)) + } + } } } (Err(e), _) | (_, Err(e)) => Err(e), }; } +// For the uniform buffer alignment, array strides and struct sizes must be multiples of 16. +const UNIFORM_LAYOUT_ALIGNMENT_MASK: u32 = 0xF; + #[derive(Clone, Debug)] pub(super) struct TypeInfo { pub flags: TypeFlags, @@ -117,7 +134,7 @@ impl TypeInfo { } fn new(flags: TypeFlags, align: u32) -> Self { - let alignment = Alignment::new(align); + let alignment = crate::Alignment::new(align); TypeInfo { flags, uniform_layout: Ok(alignment), @@ -140,7 +157,6 @@ impl super::Validator { pub(super) fn reset_types(&mut self, size: usize) { self.types.clear(); self.types.resize(size, TypeInfo::dummy()); - self.layouter.clear(); } pub(super) fn validate_type( @@ -218,8 +234,8 @@ impl super::Validator { if !base_info.flags.contains(TypeFlags::DATA | TypeFlags::SIZED) { return Err(TypeError::InvalidArrayBaseType(base)); } - if base_info.flags.contains(TypeFlags::TOP_LEVEL) { - return Err(TypeError::NestedTopLevel); + if base_info.flags.contains(TypeFlags::BLOCK) { + return Err(TypeError::NestedBlock); } let base_size = types[base].inner.span(constants); @@ -227,12 +243,11 @@ impl super::Validator { return Err(TypeError::InsufficientArrayStride { stride, base_size }); } - let general_alignment = self.layouter[base].alignment; let uniform_layout = match base_info.uniform_layout { Ok(base_alignment) => { // combine the alignment requirements let align = ((base_alignment.unwrap().get() - 1) - | (general_alignment.get() - 1)) + | UNIFORM_LAYOUT_ALIGNMENT_MASK) + 1; if stride % align != 0 { Err(( @@ -243,16 +258,14 @@ impl super::Validator { }, )) } else { - Ok(Alignment::new(align)) + Ok(crate::Alignment::new(align)) } } Err(e) => Err(e), }; let storage_layout = match base_info.storage_layout { Ok(base_alignment) => { - let align = ((base_alignment.unwrap().get() - 1) - | (general_alignment.get() - 1)) - + 1; + let align = base_alignment.unwrap().get(); if stride % align != 0 { Err(( handle, @@ -262,7 +275,7 @@ impl super::Validator { }, )) } else { - Ok(Alignment::new(align)) + Ok(base_alignment) } } Err(e) => Err(e), @@ -299,12 +312,8 @@ impl super::Validator { TypeFlags::SIZED } - crate::ArraySize::Dynamic => { - // Non-SIZED types may only appear as the last element of a structure. - // This is enforced by checks for SIZED-ness for all compound types, - // and a special case for structs. - TypeFlags::empty() - } + //Note: this will be detected at the struct level + crate::ArraySize::Dynamic => TypeFlags::empty(), }; let base_mask = TypeFlags::HOST_SHARED | TypeFlags::INTERFACE; @@ -315,7 +324,7 @@ impl super::Validator { } } Ti::Struct { - top_level, + level, ref members, span, } => { @@ -327,7 +336,6 @@ impl super::Validator { 1, ); let mut min_offset = 0; - for (i, member) in members.iter().enumerate() { if member.ty >= handle { return Err(TypeError::UnresolvedBase(member.ty)); @@ -336,11 +344,13 @@ impl super::Validator { if !base_info.flags.contains(TypeFlags::DATA) { return Err(TypeError::InvalidData(member.ty)); } - if top_level && !base_info.flags.contains(TypeFlags::INTERFACE) { + if level == crate::StructLevel::Root + && !base_info.flags.contains(TypeFlags::INTERFACE) + { return Err(TypeError::InvalidBlockType(member.ty)); } - if base_info.flags.contains(TypeFlags::TOP_LEVEL) { - return Err(TypeError::NestedTopLevel); + if base_info.flags.contains(TypeFlags::BLOCK) { + return Err(TypeError::NestedBlock); } ti.flags &= base_info.flags; @@ -364,7 +374,6 @@ impl super::Validator { index: i as u32, offset: member.offset, size: base_size, - span, }); } @@ -373,6 +382,7 @@ impl super::Validator { member, i as u32, base_info.uniform_layout, + level, handle, ); check_member_layout( @@ -380,6 +390,7 @@ impl super::Validator { member, i as u32, base_info.storage_layout, + level, handle, ); @@ -395,16 +406,24 @@ impl super::Validator { } } } - if top_level { - ti.flags |= TypeFlags::TOP_LEVEL; + if let crate::StructLevel::Root = level { + ti.flags |= TypeFlags::BLOCK; } - let alignment = self.layouter[handle].alignment.get(); - if span % alignment != 0 { - ti.uniform_layout = Err((handle, Disalignment::StructSpan { span, alignment })); - ti.storage_layout = Err((handle, Disalignment::StructSpan { span, alignment })); + // disabled temporarily, see https://github.com/gpuweb/gpuweb/issues/1558 + const CHECK_STRUCT_SIZE: bool = false; + if CHECK_STRUCT_SIZE + && ti.uniform_layout.is_ok() + && span & UNIFORM_LAYOUT_ALIGNMENT_MASK != 0 + { + ti.uniform_layout = Err(( + handle, + Disalignment::StructSpan { + span, + alignment: UNIFORM_LAYOUT_ALIGNMENT_MASK + 1, + }, + )); } - ti } Ti::Image { .. } | Ti::Sampler { .. } => TypeInfo::new(TypeFlags::empty(), 0), diff --git a/third_party/rust/naga/tests/cases/glsl_constant_expression.vert b/third_party/rust/naga/tests/cases/glsl_constant_expression.vert new file mode 100644 index 000000000000..3edd419c1f2e --- /dev/null +++ b/third_party/rust/naga/tests/cases/glsl_constant_expression.vert @@ -0,0 +1,9 @@ +#version 450 core + +const int N = 5; + +float foo[2+N]; + +void main() { + gl_Position = vec4(1); +} \ No newline at end of file diff --git a/third_party/rust/naga/tests/cases/glsl_if_preprocessor.vert b/third_party/rust/naga/tests/cases/glsl_if_preprocessor.vert new file mode 100644 index 000000000000..752dde30887b --- /dev/null +++ b/third_party/rust/naga/tests/cases/glsl_if_preprocessor.vert @@ -0,0 +1,14 @@ +#version 460 core + +#define TEST 3 +#define TEST_EXPR 2 && 2 + +#if TEST_EXPR - 2 == 0 +#error 0 +#elif TEST_EXPR - 2 == 1 +#error 1 +#elif TEST_EXPR - 2 == 2 +#error 2 +#else +#error You shouldn't do that +#endif diff --git a/third_party/rust/naga/tests/cases/glsl_phong_lighting.frag b/third_party/rust/naga/tests/cases/glsl_phong_lighting.frag new file mode 100644 index 000000000000..6153f2cafe92 --- /dev/null +++ b/third_party/rust/naga/tests/cases/glsl_phong_lighting.frag @@ -0,0 +1,56 @@ +#version 450 core + +layout(location=0) in vec3 v_position; +layout(location=1) in vec3 v_color; +layout(location=2) in vec3 v_normal; + +layout(location=0) out vec4 f_color; + +layout(set=0, binding=0) +uniform Globals { + mat4 u_view_proj; + vec3 u_view_position; +}; + +layout(set = 1, binding = 0) uniform Light { + vec3 u_position; + vec3 u_color; +}; + +layout(set=2, binding=0) +uniform Locals { + mat4 u_transform; + vec2 u_min_max; +}; + +layout (set = 2, binding = 1) uniform texture2D t_color; +layout (set = 2, binding = 2) uniform sampler s_color; + +float invLerp(float from, float to, float value){ + return (value - from) / (to - from); +} + +void main() { + vec3 object_color = + texture(sampler2D(t_color,s_color), vec2(invLerp(u_min_max.x,u_min_max.y,length(v_position)),0.0)).xyz; + + float ambient_strength = 0.1; + vec3 ambient_color = u_color * ambient_strength; + + vec3 normal = normalize(v_normal); + vec3 light_dir = normalize(u_position - v_position); + + float diffuse_strength = max(dot(normal, light_dir), 0.0); + vec3 diffuse_color = u_color * diffuse_strength; + + vec3 view_dir = normalize(u_view_position - v_position); + vec3 half_dir = normalize(view_dir + light_dir); + + float specular_strength = pow(max(dot(normal, half_dir), 0.0), 32); + + vec3 specular_color = specular_strength * u_color; + + vec3 result = (ambient_color + diffuse_color + specular_color) * object_color; + + f_color = vec4(result, 1.0); +} diff --git a/third_party/rust/naga/tests/cases/glsl_preprocessor_abuse.vert b/third_party/rust/naga/tests/cases/glsl_preprocessor_abuse.vert new file mode 100644 index 000000000000..f83cc74c4f7f --- /dev/null +++ b/third_party/rust/naga/tests/cases/glsl_preprocessor_abuse.vert @@ -0,0 +1,11 @@ +#version 450 core + +#define MAIN void main() { +#define V_POSITION layout(location=0) in vec4 a_position; +#define ASSIGN_POSITION gl_Position = a_position; + +V_POSITION + +MAIN + ASSIGN_POSITION +} \ No newline at end of file diff --git a/third_party/rust/naga/tests/cases/glsl_vertex_test_shader.vert b/third_party/rust/naga/tests/cases/glsl_vertex_test_shader.vert new file mode 100644 index 000000000000..b82d34f8195e --- /dev/null +++ b/third_party/rust/naga/tests/cases/glsl_vertex_test_shader.vert @@ -0,0 +1,29 @@ +#version 450 core + +layout(location=0) in vec3 a_position; +layout(location=1) in vec3 a_color; +layout(location=2) in vec3 a_normal; + +layout(location=0) out vec3 v_position; +layout(location=1) out vec3 v_color; +layout(location=2) out vec3 v_normal; + +layout(set=0, binding=0) +uniform Globals { + mat4 u_view_proj; + vec3 u_view_position; +}; + +layout(set=2, binding=0) +uniform Locals { + mat4 u_transform; + vec2 U_min_max; +}; + +void main() { + v_color = a_color; + v_normal = a_normal; + + v_position = (u_transform * vec4(a_position, 1.0)).xyz; + gl_Position = u_view_proj * u_transform * vec4(a_position, 1.0); +} diff --git a/third_party/rust/naga/tests/in/access.param.ron b/third_party/rust/naga/tests/in/access.param.ron new file mode 100644 index 000000000000..e8c788d603c9 --- /dev/null +++ b/third_party/rust/naga/tests/in/access.param.ron @@ -0,0 +1,21 @@ +( + spv_version: (1, 1), + spv_capabilities: [ Shader, Image1D, Sampled1D ], + spv_debug: true, + spv_adjust_coordinate_space: false, + msl_custom: true, + msl: ( + lang_version: (2, 0), + binding_map: { + (stage: Vertex, group: 0, binding: 0): (buffer: Some(0), mutable: true), + }, + per_stage_map: ( + vs: ( + sizes_buffer: Some(24), + ), + ), + inline_samplers: [], + spirv_cross_compatibility: false, + fake_missing_bindings: false, + ), +) diff --git a/third_party/rust/naga/tests/in/access.wgsl b/third_party/rust/naga/tests/in/access.wgsl new file mode 100644 index 000000000000..b6ee08355743 --- /dev/null +++ b/third_party/rust/naga/tests/in/access.wgsl @@ -0,0 +1,23 @@ +// This snapshot tests accessing various containers, dereferencing pointers. + +[[block]] +struct Bar { + matrix: mat4x4; + data: [[stride(4)]] array; +}; + +[[group(0), binding(0)]] +var bar: [[access(read_write)]] Bar; + +[[stage(vertex)]] +fn foo([[builtin(vertex_index)]] vi: u32) -> [[builtin(position)]] vec4 { + let index = 3u; + let b = bar.matrix[index].x; + + let a = bar.data[arrayLength(&bar.data) - 1u]; + + let array = array(a, i32(b), 3, 4, 5); + let value = array[vi]; + + return vec4(vec4(value)); +} diff --git a/third_party/rust/naga/tests/in/boids.param.ron b/third_party/rust/naga/tests/in/boids.param.ron new file mode 100644 index 000000000000..16021ec3b1ab --- /dev/null +++ b/third_party/rust/naga/tests/in/boids.param.ron @@ -0,0 +1,23 @@ +( + spv_version: (1, 0), + spv_capabilities: [ Shader ], + spv_debug: true, + spv_adjust_coordinate_space: false, + msl_custom: true, + msl: ( + lang_version: (2, 0), + binding_map: { + (stage: Compute, group: 0, binding: 0): (buffer: Some(0), mutable: false), + (stage: Compute, group: 0, binding: 1): (buffer: Some(1), mutable: true), + (stage: Compute, group: 0, binding: 2): (buffer: Some(2), mutable: true), + }, + per_stage_map: ( + cs: ( + sizes_buffer: Some(3), + ) + ), + inline_samplers: [], + spirv_cross_compatibility: false, + fake_missing_bindings: false, + ), +) diff --git a/third_party/rust/naga/tests/in/boids.wgsl b/third_party/rust/naga/tests/in/boids.wgsl new file mode 100644 index 000000000000..dc5bd7ea39a3 --- /dev/null +++ b/third_party/rust/naga/tests/in/boids.wgsl @@ -0,0 +1,109 @@ +let NUM_PARTICLES: u32 = 1500u; + +struct Particle { + pos : vec2; + vel : vec2; +}; + +[[block]] +struct SimParams { + deltaT : f32; + rule1Distance : f32; + rule2Distance : f32; + rule3Distance : f32; + rule1Scale : f32; + rule2Scale : f32; + rule3Scale : f32; +}; + +[[block]] +struct Particles { + particles : [[stride(16)]] array; +}; + +[[group(0), binding(0)]] var params : SimParams; +[[group(0), binding(1)]] var particlesSrc : [[access(read)]] Particles; +[[group(0), binding(2)]] var particlesDst : [[access(read_write)]] Particles; + +// https://github.com/austinEng/Project6-Vulkan-Flocking/blob/master/data/shaders/computeparticles/particle.comp +[[stage(compute), workgroup_size(64)]] +fn main([[builtin(global_invocation_id)]] global_invocation_id : vec3) { + let index : u32 = global_invocation_id.x; + if (index >= NUM_PARTICLES) { + return; + } + + var vPos : vec2 = particlesSrc.particles[index].pos; + var vVel : vec2 = particlesSrc.particles[index].vel; + + var cMass : vec2 = vec2(0.0, 0.0); + var cVel : vec2 = vec2(0.0, 0.0); + var colVel : vec2 = vec2(0.0, 0.0); + var cMassCount : i32 = 0; + var cVelCount : i32 = 0; + + var pos : vec2; + var vel : vec2; + var i : u32 = 0u; + loop { + if (i >= NUM_PARTICLES) { + break; + } + if (i == index) { + continue; + } + + pos = particlesSrc.particles[i].pos; + vel = particlesSrc.particles[i].vel; + + if (distance(pos, vPos) < params.rule1Distance) { + cMass = cMass + pos; + cMassCount = cMassCount + 1; + } + if (distance(pos, vPos) < params.rule2Distance) { + colVel = colVel - (pos - vPos); + } + if (distance(pos, vPos) < params.rule3Distance) { + cVel = cVel + vel; + cVelCount = cVelCount + 1; + } + + continuing { + i = i + 1u; + } + } + if (cMassCount > 0) { + cMass = cMass / f32(cMassCount) - vPos; + } + if (cVelCount > 0) { + cVel = cVel / f32(cVelCount); + } + + vVel = vVel + (cMass * params.rule1Scale) + + (colVel * params.rule2Scale) + + (cVel * params.rule3Scale); + + // clamp velocity for a more pleasing simulation + vVel = normalize(vVel) * clamp(length(vVel), 0.0, 0.1); + + // kinematic update + vPos = vPos + (vVel * params.deltaT); + + // Wrap around boundary + if (vPos.x < -1.0) { + vPos.x = 1.0; + } + if (vPos.x > 1.0) { + vPos.x = -1.0; + } + if (vPos.y < -1.0) { + vPos.y = 1.0; + } + if (vPos.y > 1.0) { + vPos.y = -1.0; + } + + // Write back + particlesDst.particles[index].pos = vPos; + particlesDst.particles[index].vel = vVel; +} diff --git a/third_party/rust/naga/tests/in/collatz.param.ron b/third_party/rust/naga/tests/in/collatz.param.ron new file mode 100644 index 000000000000..42c8480e4c97 --- /dev/null +++ b/third_party/rust/naga/tests/in/collatz.param.ron @@ -0,0 +1,5 @@ +( + spv_version: (1, 0), + spv_capabilities: [ Shader ], + spv_debug: true, +) diff --git a/third_party/rust/naga/tests/in/collatz.wgsl b/third_party/rust/naga/tests/in/collatz.wgsl new file mode 100644 index 000000000000..4d2168f468bf --- /dev/null +++ b/third_party/rust/naga/tests/in/collatz.wgsl @@ -0,0 +1,36 @@ +[[block]] +struct PrimeIndices { + data: [[stride(4)]] array; +}; // this is used as both input and output for convenience + +[[group(0), binding(0)]] +var v_indices: [[access(read_write)]] PrimeIndices; + +// The Collatz Conjecture states that for any integer n: +// If n is even, n = n/2 +// If n is odd, n = 3n+1 +// And repeat this process for each new n, you will always eventually reach 1. +// Though the conjecture has not been proven, no counterexample has ever been found. +// This function returns how many times this recurrence needs to be applied to reach 1. +fn collatz_iterations(n_base: u32) -> u32 { + var n: u32 = n_base; + var i: u32 = 0u; + loop { + if (n <= 1u) { + break; + } + if (n % 2u == 0u) { + n = n / 2u; + } + else { + n = 3u * n + 1u; + } + i = i + 1u; + } + return i; +} + +[[stage(compute), workgroup_size(1)]] +fn main([[builtin(global_invocation_id)]] global_id: vec3) { + v_indices.data[global_id.x] = collatz_iterations(v_indices.data[global_id.x]); +} diff --git a/third_party/rust/naga/tests/in/control-flow.param.ron b/third_party/rust/naga/tests/in/control-flow.param.ron new file mode 100644 index 000000000000..d6269ab629b8 --- /dev/null +++ b/third_party/rust/naga/tests/in/control-flow.param.ron @@ -0,0 +1,4 @@ +( + spv_version: (1, 1), + spv_capabilities: [ Shader ], +) diff --git a/third_party/rust/naga/tests/in/control-flow.wgsl b/third_party/rust/naga/tests/in/control-flow.wgsl new file mode 100644 index 000000000000..893546e43077 --- /dev/null +++ b/third_party/rust/naga/tests/in/control-flow.wgsl @@ -0,0 +1,6 @@ +[[stage(compute), workgroup_size(1)]] +fn main([[builtin(global_invocation_id)]] global_id: vec3) { + //TODO: execution-only barrier? + storageBarrier(); + workgroupBarrier(); +} diff --git a/third_party/rust/naga/tests/in/empty.param.ron b/third_party/rust/naga/tests/in/empty.param.ron new file mode 100644 index 000000000000..d6269ab629b8 --- /dev/null +++ b/third_party/rust/naga/tests/in/empty.param.ron @@ -0,0 +1,4 @@ +( + spv_version: (1, 1), + spv_capabilities: [ Shader ], +) diff --git a/third_party/rust/naga/tests/in/empty.wgsl b/third_party/rust/naga/tests/in/empty.wgsl new file mode 100644 index 000000000000..9bd04d80cff6 --- /dev/null +++ b/third_party/rust/naga/tests/in/empty.wgsl @@ -0,0 +1,2 @@ +[[stage(compute), workgroup_size(1)]] +fn main() {} diff --git a/third_party/rust/naga/tests/in/extra.param.ron b/third_party/rust/naga/tests/in/extra.param.ron new file mode 100644 index 000000000000..1767cbe3f5a7 --- /dev/null +++ b/third_party/rust/naga/tests/in/extra.param.ron @@ -0,0 +1,5 @@ +( + god_mode: true, + spv_version: (1, 0), + spv_capabilities: [ Shader ], +) diff --git a/third_party/rust/naga/tests/in/extra.wgsl b/third_party/rust/naga/tests/in/extra.wgsl new file mode 100644 index 000000000000..11b8a2eb6410 --- /dev/null +++ b/third_party/rust/naga/tests/in/extra.wgsl @@ -0,0 +1,11 @@ +[[block]] +struct PushConstants { + index: u32; + double: vec2; +}; +var pc: PushConstants; + +[[stage(fragment)]] +fn main([[location(0)]] color: vec4) -> [[location(0)]] vec4 { + return color; +} diff --git a/third_party/rust/naga/tests/in/image.param.ron b/third_party/rust/naga/tests/in/image.param.ron new file mode 100644 index 000000000000..70311375597e --- /dev/null +++ b/third_party/rust/naga/tests/in/image.param.ron @@ -0,0 +1,5 @@ +( + spv_version: (1, 1), + spv_capabilities: [ Shader, ImageQuery, Image1D, Sampled1D ], + spv_debug: true, +) diff --git a/third_party/rust/naga/tests/in/image.wgsl b/third_party/rust/naga/tests/in/image.wgsl new file mode 100644 index 000000000000..440cd351d69d --- /dev/null +++ b/third_party/rust/naga/tests/in/image.wgsl @@ -0,0 +1,60 @@ +[[group(0), binding(1)]] +var image_src: [[access(read)]] texture_storage_2d; +[[group(0), binding(2)]] +var image_dst: [[access(write)]] texture_storage_1d; + +[[stage(compute), workgroup_size(16)]] +fn main( + [[builtin(local_invocation_id)]] local_id: vec3, + //TODO: https://github.com/gpuweb/gpuweb/issues/1590 + //[[builtin(workgroup_size)]] wg_size: vec3 +) { + let dim = textureDimensions(image_src); + let itc = dim * vec2(local_id.xy) % vec2(10, 20); + let value = textureLoad(image_src, itc); + textureStore(image_dst, itc.x, value); +} + +[[group(0), binding(0)]] +var image_1d: texture_1d; +[[group(0), binding(1)]] +var image_2d: texture_2d; +[[group(0), binding(2)]] +var image_2d_array: texture_2d_array; +[[group(0), binding(3)]] +var image_cube: texture_cube; +[[group(0), binding(4)]] +var image_cube_array: texture_cube_array; +[[group(0), binding(5)]] +var image_3d: texture_3d; +[[group(0), binding(6)]] +var image_aa: texture_multisampled_2d; + +[[stage(vertex)]] +fn queries() -> [[builtin(position)]] vec4 { + let dim_1d = textureDimensions(image_1d); + let dim_2d = textureDimensions(image_2d); + let num_levels_2d = textureNumLevels(image_2d); + let dim_2d_lod = textureDimensions(image_2d, 1); + let dim_2d_array = textureDimensions(image_2d_array); + let num_levels_2d_array = textureNumLevels(image_2d_array); + let dim_2d_array_lod = textureDimensions(image_2d_array, 1); + let num_layers_2d = textureNumLayers(image_2d_array); + let dim_cube = textureDimensions(image_cube); + let num_levels_cube = textureNumLevels(image_cube); + let dim_cube_lod = textureDimensions(image_cube, 1); + let dim_cube_array = textureDimensions(image_cube_array); + let num_levels_cube_array = textureNumLevels(image_cube_array); + let dim_cube_array_lod = textureDimensions(image_cube_array, 1); + let num_layers_cube = textureNumLayers(image_cube_array); + let dim_3d = textureDimensions(image_3d); + let num_levels_3d = textureNumLevels(image_3d); + let dim_3d_lod = textureDimensions(image_3d, 1); + let num_samples_aa = textureNumSamples(image_aa); + + let sum = dim_1d + dim_2d.y + dim_2d_lod.y + dim_2d_array.y + dim_2d_array_lod.y + + num_layers_2d + dim_cube.y + dim_cube_lod.y + dim_cube_array.y + dim_cube_array_lod.y + + num_layers_cube + dim_3d.z + dim_3d_lod.z + num_samples_aa + + num_levels_2d + num_levels_2d_array + num_levels_3d + num_levels_cube + num_levels_cube_array; + return vec4(f32(sum)); +} diff --git a/third_party/rust/naga/tests/in/interpolate.param.ron b/third_party/rust/naga/tests/in/interpolate.param.ron new file mode 100644 index 000000000000..f0c6a13d5691 --- /dev/null +++ b/third_party/rust/naga/tests/in/interpolate.param.ron @@ -0,0 +1,7 @@ +( + spv_version: (1, 0), + spv_capabilities: [ Shader, SampleRateShading ], + spv_debug: true, + spv_adjust_coordinate_space: true, + glsl_desktop_version: Some(400) +) diff --git a/third_party/rust/naga/tests/in/interpolate.wgsl b/third_party/rust/naga/tests/in/interpolate.wgsl new file mode 100644 index 000000000000..51eb4fde7466 --- /dev/null +++ b/third_party/rust/naga/tests/in/interpolate.wgsl @@ -0,0 +1,29 @@ +struct FragmentInput { + [[builtin(position)]] position: vec4; + [[location(0), interpolate(flat)]] flat : u32; + [[location(1), interpolate(linear)]] linear : f32; + [[location(2), interpolate(linear,centroid)]] linear_centroid : vec2; + [[location(3), interpolate(linear,sample)]] linear_sample : vec3; + [[location(4), interpolate(perspective)]] perspective : vec4; + [[location(5), interpolate(perspective,centroid)]] perspective_centroid : f32; + [[location(6), interpolate(perspective,sample)]] perspective_sample : f32; +}; + +[[stage(vertex)]] +fn main() -> FragmentInput { + var out: FragmentInput; + + out.position = vec4(2.0, 4.0, 5.0, 6.0); + out.flat = 8u32; + out.linear = 27.0; + out.linear_centroid = vec2(64.0, 125.0); + out.linear_sample = vec3(216.0, 343.0, 512.0); + out.perspective = vec4(729.0, 1000.0, 1331.0, 1728.0); + out.perspective_centroid = 2197.0; + out.perspective_sample = 2744.0; + + return out; +} + +[[stage(fragment)]] +fn main(val : FragmentInput) { } diff --git a/third_party/rust/naga/tests/in/operators.param.ron b/third_party/rust/naga/tests/in/operators.param.ron new file mode 100644 index 000000000000..553f5e75fb3e --- /dev/null +++ b/third_party/rust/naga/tests/in/operators.param.ron @@ -0,0 +1,4 @@ +( + spv_version: (1, 0), + spv_capabilities: [ Shader ], +) diff --git a/third_party/rust/naga/tests/in/operators.wgsl b/third_party/rust/naga/tests/in/operators.wgsl new file mode 100644 index 000000000000..3b7d1f33f9f2 --- /dev/null +++ b/third_party/rust/naga/tests/in/operators.wgsl @@ -0,0 +1,10 @@ +fn splat() -> vec4 { + let a = (1.0 + vec2(2.0) - 3.0) / 4.0; + let b = vec4(5) % 2; + return a.xyxy + vec4(b); +} + +fn unary() -> i32 { + let a = 1; + if (!true) { return a; } else { return ~a; }; +} diff --git a/third_party/rust/naga/tests/in/quad-glsl.glsl b/third_party/rust/naga/tests/in/quad-glsl.glsl new file mode 100644 index 000000000000..b8d653206660 --- /dev/null +++ b/third_party/rust/naga/tests/in/quad-glsl.glsl @@ -0,0 +1,28 @@ +#version 450 +// vertex +const float c_scale = 1.2; + +layout(location = 0) in vec2 a_pos; +layout(location = 1) in vec2 a_uv; +layout(location = 0) out vec2 v_uv; + +void vert_main() { + v_uv = a_uv; + gl_Position = vec4(c_scale * a_pos, 0.0, 1.0); +} + +// fragment +layout(location = 0) in vec2 v_uv; +#ifdef TEXTURE +layout(set = 0, binding = 0) uniform texture2D u_texture; +layout(set = 0, binding = 1) uniform sampler u_sampler; +#endif +layout(location = 0) out vec4 o_color; + +void frag_main() { +#ifdef TEXTURE + o_color = texture(sampler2D(u_texture, u_sampler), v_uv); +#else + o_color = vec4(1.0, 1.0, 1.0, 1.0); +#endif +} diff --git a/third_party/rust/naga/tests/in/quad-glsl.param.ron b/third_party/rust/naga/tests/in/quad-glsl.param.ron new file mode 100644 index 000000000000..c073cf1a5060 --- /dev/null +++ b/third_party/rust/naga/tests/in/quad-glsl.param.ron @@ -0,0 +1,6 @@ +( + spv_version: (1, 0), + spv_capabilities: [ Shader ], + spv_debug: true, + spv_adjust_coordinate_space: true, +) diff --git a/third_party/rust/naga/tests/in/quad-vert.spv b/third_party/rust/naga/tests/in/quad-vert.spv new file mode 100644 index 0000000000000000000000000000000000000000..eaf03d2fbe057ec0046f3fcaf1f351eec0d6f569 GIT binary patch literal 968 zcmYk3%}c{T5XIMiq_tIRt!@3NHR4ey9#jNT5rul_p;GW#f(8l1v@~fEPyT`aL0$yk zFLtXFCbRF&?9AKUZ04|F%#taYqS-gURy7$h#*~ed#%^!W>%_@(=j8O5iZzo>3#qA? z<#e6X|J4g308Ck4lQ-m9eHq11ZOEIPzQ0;9^|Rr0rjX{@>n*Tjs=_bfubvB0<6!t7d`ol948|;4Z}q)Fa4Ya zLoa9YhT~DJ*q!Tdlaztc4@Seic37S8nB~aB{jWnC96D?rYdG$$s|@|9V-{NM?3!5k z1J$sHbwevh(Umt zqBT8Q!t09E!?m^IEVc(ZoVgo{XdsU+FmC|IoJ~bnPG9DL>vD{GbaFQ~&&(}3Mm=~t zMIYD9+tZrfXgSlHaX*|1gZ~nfSG*L; + [[builtin(position)]] position : vec4; +}; + +[[stage(vertex)]] +fn main( + [[location(0)]] pos : vec2, + [[location(1)]] uv : vec2, +) -> VertexOutput { + return VertexOutput(uv, vec4(c_scale * pos, 0.0, 1.0)); +} + +// fragment +[[group(0), binding(0)]] var u_texture : texture_2d; +[[group(0), binding(1)]] var u_sampler : sampler; + +[[stage(fragment)]] +fn main([[location(0)]] uv : vec2) -> [[location(0)]] vec4 { + let color = textureSample(u_texture, u_sampler, uv); + if (color.a == 0.0) { + discard; + } + // forcing the expression here to be emitted in order to check the + // uniformity of the control flow a bit more strongly. + let premultiplied = color.a * color; + return premultiplied; +} diff --git a/third_party/rust/naga/tests/in/shadow.param.ron b/third_party/rust/naga/tests/in/shadow.param.ron new file mode 100644 index 000000000000..c6b43089b913 --- /dev/null +++ b/third_party/rust/naga/tests/in/shadow.param.ron @@ -0,0 +1,6 @@ +( + spv_version: (1, 2), + spv_capabilities: [ Shader ], + spv_debug: true, + spv_adjust_coordinate_space: true, +) diff --git a/third_party/rust/naga/tests/in/shadow.spv b/third_party/rust/naga/tests/in/shadow.spv new file mode 100644 index 0000000000000000000000000000000000000000..265cf65fc08335dfb5180801283e218430a0fd67 GIT binary patch literal 4764 zcmZve2X|Ig7KU$3v499-FPMmq9ebtQ!i;eQdk>!=5KZ#MB z55>5T-{xs5PQ@%rMm{Z>b;%w%2`{=GlcK0 z_xIGgOLi#UGp#Qi>@0f^^G&}y|8{2fY_|liwA;{~TU)*A*B0JvJAXY>V`nh8x@-K9 zDDyqbYQDDlE^}%_>Tu4CI7jK`RbqTLMpw87bbC#!aLyt(6FC1IWDA&G+KuITH=^4+ z&)#^K`|<2ID)pvPug?_^xX_LGuxO9?K~?+XnRTchCxUKvQ=fSK98o zC>Bpm+xc0;w&%clDgA3;tbD&?@`hAf3q5P@$j-Ib1jZ!RCt@UaD{xsydjv2(?X}Te zX^%p8@Aeskw2x@xEXRg!9ewMu+x3wf(|yzMnVUc@b!976HdkR8J27mZP1mNJ&=#dE zicP57if&HcjkY=3ZNTM>TOpm#b9c|XAzMKM$g`V>?y}xwq-X0HXip8>J+(2zbfmSc z{U@Zn^~d4sK+2i-XQXj*&ZX^hFQ@;o(2aKv=kj@V>7Ru(*81r`9No1M{!V0`|G4-K zB`3c*^UOikfV26WYg>Ch(%N#)rtNc`vn>eS`0VZb(`EkYNMo(7-5s|5GrkAirN0l^ z0G!Ky+Wv0j^z#}kbF~|6Rf9zvut>?}c+l{f~uS-OuB2F8xm;tNVE> z?CO4=4$k;zLa)yAESyXK%g9yl>lK|W{jVXb{az2d+V73vjDIuqYQMMO8i4+l;jixR z12~udkC4@VABSD-_epTZe;RtV-)9y7SLltve19L>eplr7;vz!g6J7Le3^oDQQvU_n9Jc(A*yiY0PA1Ry zx!*wF<=U;t@t_UlEZd?xqbs@X(B&GyOuB4^+#VRKFS#Ai{XY8MJ}=_7K{r-k*4qi) zdhTs0ah}_b!1?v9h}=J*o3Af>OhC7XSQ2qtqZ_L)d+dxZ?@DeLbUF92h_1UKcL&~2 zAGUMs8r;nIUhjczEpzo9P0uZmdjikW^UM1Cpt~=hx#ac>uDaKK;oPe}&&j@f1M~GA zAOAn!3*A1>px?TFBfV>V`>+$AJNaqAH63{8`y(gf^E)~TcqeNHHx)T0xV4c!D+eIW zQ+~%p2OrAK5VaP+kpKg$bq^7@vc3>Z4{2d&Hboo2bcQiN#IGeh&I=i;KwmI5LXYl)W9PnInrjyWfFALD; zf_b0|WXx&k8FM=NDZn^+&!8JQ8_4$n^ZXX~g4tpF4mbnU!DZxVpA3wV-8Is8ul4LJWIkn{foJ?B3g{jb0{dGq{U{{_e|269V~ zX94+hfVG_OTp(}EQsf1|eiwr5cM*E_TZVoiEJUjnk>yB7TlV4S>p*C8(l^4ACFH~KOleN}U*wUB25=hL6h z#r^2{TEND0J_O{S1-b9%f;aDF^p}ABUIE$fRrKul8v2XCIC=A4N4@~$ z-w5tac!e-hlM$ajGJXCV81 z9(>;G7ufFEIrRDc^Ine!`n-cB!5xc!9MCs~+nR)w^F4Pk()XM)=l&nMb1x>}d-^Ys p8wJeQCujZvNb?Uwn(qu>1AF-W9fSNPxMrmJUk3LxPmtfEUjed&gEasE literal 0 HcmV?d00001 diff --git a/third_party/rust/naga/tests/in/shadow.wgsl b/third_party/rust/naga/tests/in/shadow.wgsl new file mode 100644 index 000000000000..9cd5253bccab --- /dev/null +++ b/third_party/rust/naga/tests/in/shadow.wgsl @@ -0,0 +1,63 @@ +[[block]] +struct Globals { + num_lights: vec4; +}; + +[[group(0), binding(0)]] +var u_globals: Globals; + +struct Light { + proj: mat4x4; + pos: vec4; + color: vec4; +}; + +[[block]] +struct Lights { + data: [[stride(96)]] array; +}; + +[[group(0), binding(1)]] +var s_lights: [[access(read)]] Lights; +[[group(0), binding(2)]] +var t_shadow: texture_depth_2d_array; +[[group(0), binding(3)]] +var sampler_shadow: sampler_comparison; + +fn fetch_shadow(light_id: u32, homogeneous_coords: vec4) -> f32 { + if (homogeneous_coords.w <= 0.0) { + return 1.0; + } + let flip_correction = vec2(0.5, -0.5); + let light_local = homogeneous_coords.xy * flip_correction / homogeneous_coords.w + vec2(0.5, 0.5); + return textureSampleCompare(t_shadow, sampler_shadow, light_local, i32(light_id), homogeneous_coords.z / homogeneous_coords.w); +} + +let c_ambient: vec3 = vec3(0.05, 0.05, 0.05); +let c_max_lights: u32 = 10u; + +[[stage(fragment)]] +fn fs_main( + [[location(0)]] raw_normal: vec3, + [[location(1)]] position: vec4 +) -> [[location(0)]] vec4 { + let normal: vec3 = normalize(raw_normal); + // accumulate color + var color: vec3 = c_ambient; + var i: u32 = 0u; + loop { + if (i >= min(u_globals.num_lights.x, c_max_lights)) { + break; + } + let light = s_lights.data[i]; + let shadow = fetch_shadow(i, light.proj * position); + let light_dir = normalize(light.pos.xyz - position.xyz); + let diffuse = max(0.0, dot(normal, light_dir)); + color = color + shadow * diffuse * light.color.xyz; + continuing { + i = i + 1u; + } + } + // multiply the light by material color + return vec4(color, 1.0); +} diff --git a/third_party/rust/naga/tests/in/skybox.param.ron b/third_party/rust/naga/tests/in/skybox.param.ron new file mode 100644 index 000000000000..d4510ffcb090 --- /dev/null +++ b/third_party/rust/naga/tests/in/skybox.param.ron @@ -0,0 +1,33 @@ +( + spv_flow_dump_prefix: "", + spv_version: (1, 0), + spv_capabilities: [ Shader ], + spv_debug: false, + spv_adjust_coordinate_space: false, + msl_custom: true, + msl: ( + lang_version: (2, 1), + binding_map: { + (stage: Vertex, group: 0, binding: 0): (buffer: Some(0)), + (stage: Fragment, group: 0, binding: 1): (texture: Some(0)), + (stage: Fragment, group: 0, binding: 2): (sampler: Some(Inline(0))), + }, + per_stage_map: ( + ), + inline_samplers: [ + ( + coord: Normalized, + address: (ClampToEdge, ClampToEdge, ClampToEdge), + mag_filter: Linear, + min_filter: Linear, + mip_filter: None, + border_color: TransparentBlack, + compare_func: Never, + lod_clamp: Some((start: 0.5, end: 10.0)), + max_anisotropy: Some(8), + ), + ], + spirv_cross_compatibility: false, + fake_missing_bindings: false, + ) +) diff --git a/third_party/rust/naga/tests/in/skybox.wgsl b/third_party/rust/naga/tests/in/skybox.wgsl new file mode 100644 index 000000000000..96d0e1ceefbd --- /dev/null +++ b/third_party/rust/naga/tests/in/skybox.wgsl @@ -0,0 +1,39 @@ +struct VertexOutput { + [[builtin(position)]] position: vec4; + [[location(0)]] uv: vec3; +}; + +[[block]] +struct Data { + proj_inv: mat4x4; + view: mat4x4; +}; +[[group(0), binding(0)]] +var r_data: Data; + +[[stage(vertex)]] +fn vs_main([[builtin(vertex_index)]] vertex_index: u32) -> VertexOutput { + // hacky way to draw a large triangle + var tmp1: i32 = i32(vertex_index) / 2; + var tmp2: i32 = i32(vertex_index) & 1; + let pos = vec4( + f32(tmp1) * 4.0 - 1.0, + f32(tmp2) * 4.0 - 1.0, + 0.0, + 1.0, + ); + + let inv_model_view = transpose(mat3x3(r_data.view.x.xyz, r_data.view.y.xyz, r_data.view.z.xyz)); + let unprojected = r_data.proj_inv * pos; + return VertexOutput(pos, inv_model_view * unprojected.xyz); +} + +[[group(0), binding(1)]] +var r_texture: texture_cube; +[[group(0), binding(2)]] +var r_sampler: sampler; + +[[stage(fragment)]] +fn fs_main(in: VertexOutput) -> [[location(0)]] vec4 { + return textureSample(r_texture, r_sampler, in.uv); +} diff --git a/third_party/rust/naga/tests/out/access.msl b/third_party/rust/naga/tests/out/access.msl new file mode 100644 index 000000000000..36fd3577434c --- /dev/null +++ b/third_party/rust/naga/tests/out/access.msl @@ -0,0 +1,28 @@ +#include +#include + +struct _mslBufferSizes { + metal::uint size0; +}; + +typedef int type2[1]; +struct Bar { + metal::float4x4 matrix; + type2 data; +}; +struct type5 { + int inner[5]; +}; + +struct fooInput { +}; +struct fooOutput { + metal::float4 member [[position]]; +}; +vertex fooOutput foo( + metal::uint vi [[vertex_id]] +, device Bar& bar [[buffer(0)]] +, constant _mslBufferSizes& _buffer_sizes [[buffer(24)]] +) { + return fooOutput { static_cast(metal::int4(type5 {bar.data[(1 + (_buffer_sizes.size0 - 64 - 4) / 4) - 1u], static_cast(bar.matrix[3u].x), 3, 4, 5}.inner[vi])) }; +} diff --git a/third_party/rust/naga/tests/out/access.spvasm b/third_party/rust/naga/tests/out/access.spvasm new file mode 100644 index 000000000000..894968b5f05b --- /dev/null +++ b/third_party/rust/naga/tests/out/access.spvasm @@ -0,0 +1,81 @@ +; SPIR-V +; Version: 1.1 +; Generator: rspirv +; Bound: 49 +OpCapability Image1D +OpCapability Shader +OpCapability Sampled1D +OpExtension "SPV_KHR_storage_buffer_storage_class" +%1 = OpExtInstImport "GLSL.std.450" +OpMemoryModel Logical GLSL450 +OpEntryPoint Vertex %24 "foo" %19 %22 +OpSource GLSL 450 +OpName %14 "Bar" +OpMemberName %14 0 "matrix" +OpMemberName %14 1 "data" +OpName %16 "bar" +OpName %19 "vi" +OpName %24 "foo" +OpDecorate %13 ArrayStride 4 +OpDecorate %14 Block +OpMemberDecorate %14 0 Offset 0 +OpMemberDecorate %14 0 ColMajor +OpMemberDecorate %14 0 MatrixStride 16 +OpMemberDecorate %14 1 Offset 64 +OpDecorate %15 ArrayStride 4 +OpDecorate %16 DescriptorSet 0 +OpDecorate %16 Binding 0 +OpDecorate %19 BuiltIn VertexIndex +OpDecorate %22 BuiltIn Position +%2 = OpTypeVoid +%4 = OpTypeInt 32 0 +%3 = OpConstant %4 3 +%5 = OpConstant %4 1 +%7 = OpTypeInt 32 1 +%6 = OpConstant %7 5 +%8 = OpConstant %7 3 +%9 = OpConstant %7 4 +%12 = OpTypeFloat 32 +%11 = OpTypeVector %12 4 +%10 = OpTypeMatrix %11 4 +%13 = OpTypeRuntimeArray %7 +%14 = OpTypeStruct %10 %13 +%15 = OpTypeArray %7 %6 +%17 = OpTypePointer StorageBuffer %14 +%16 = OpVariable %17 StorageBuffer +%20 = OpTypePointer Input %4 +%19 = OpVariable %20 Input +%23 = OpTypePointer Output %11 +%22 = OpVariable %23 Output +%25 = OpTypeFunction %2 +%27 = OpTypePointer StorageBuffer %10 +%28 = OpTypePointer StorageBuffer %11 +%29 = OpConstant %4 0 +%33 = OpTypePointer StorageBuffer %13 +%36 = OpTypePointer StorageBuffer %7 +%41 = OpTypePointer Function %15 +%44 = OpTypePointer Function %7 +%46 = OpTypeVector %7 4 +%24 = OpFunction %2 None %25 +%18 = OpLabel +%42 = OpVariable %41 Function +%21 = OpLoad %4 %19 +OpBranch %26 +%26 = OpLabel +%30 = OpAccessChain %28 %16 %29 %3 +%31 = OpLoad %11 %30 +%32 = OpCompositeExtract %12 %31 0 +%34 = OpArrayLength %4 %16 1 +%35 = OpISub %4 %34 %5 +%37 = OpAccessChain %36 %16 %5 %35 +%38 = OpLoad %7 %37 +%39 = OpConvertFToS %7 %32 +%40 = OpCompositeConstruct %15 %38 %39 %8 %9 %6 +OpStore %42 %40 +%43 = OpAccessChain %44 %42 %21 +%45 = OpLoad %7 %43 +%47 = OpCompositeConstruct %46 %45 %45 %45 %45 +%48 = OpConvertSToF %11 %47 +OpStore %22 %48 +OpReturn +OpFunctionEnd \ No newline at end of file diff --git a/third_party/rust/naga/tests/out/boids.Compute.glsl b/third_party/rust/naga/tests/out/boids.Compute.glsl new file mode 100644 index 000000000000..343e25bc352f --- /dev/null +++ b/third_party/rust/naga/tests/out/boids.Compute.glsl @@ -0,0 +1,98 @@ +#version 310 es + +precision highp float; + +layout(local_size_x = 64, local_size_y = 1, local_size_z = 1) in; + +struct Particle { + vec2 pos; + vec2 vel; +}; + +uniform SimParams_block_0 { + float deltaT; + float rule1Distance; + float rule2Distance; + float rule3Distance; + float rule1Scale; + float rule2Scale; + float rule3Scale; +} _group_0_binding_0; + +readonly buffer Particles_block_1 { + Particle particles[]; +} _group_0_binding_1; + +buffer Particles_block_2 { + Particle particles[]; +} _group_0_binding_2; + + +void main() { + uvec3 global_invocation_id = gl_GlobalInvocationID; + vec2 vPos; + vec2 vVel; + vec2 cMass; + vec2 cVel; + vec2 colVel; + int cMassCount = 0; + int cVelCount = 0; + vec2 pos1; + vec2 vel1; + uint i = 0u; + if((global_invocation_id[0] >= 1500u)) { + return; + } + vPos = _group_0_binding_1.particles[global_invocation_id[0]].pos; + vVel = _group_0_binding_1.particles[global_invocation_id[0]].vel; + cMass = vec2(0.0, 0.0); + cVel = vec2(0.0, 0.0); + colVel = vec2(0.0, 0.0); + while(true) { + if((i >= 1500u)) { + break; + } + if((i == global_invocation_id[0])) { + continue; + } + pos1 = _group_0_binding_1.particles[i].pos; + vel1 = _group_0_binding_1.particles[i].vel; + if((distance(pos1, vPos) < _group_0_binding_0.rule1Distance)) { + cMass = (cMass + pos1); + cMassCount = (cMassCount + 1); + } + if((distance(pos1, vPos) < _group_0_binding_0.rule2Distance)) { + colVel = (colVel - (pos1 - vPos)); + } + if((distance(pos1, vPos) < _group_0_binding_0.rule3Distance)) { + cVel = (cVel + vel1); + cVelCount = (cVelCount + 1); + } + i = (i + 1u); + } + if((cMassCount > 0)) { + cMass = ((cMass / vec2(float(cMassCount))) - vPos); + } + if((cVelCount > 0)) { + cVel = (cVel / vec2(float(cVelCount))); + } + vVel = (((vVel + (cMass * _group_0_binding_0.rule1Scale)) + (colVel * _group_0_binding_0.rule2Scale)) + (cVel * _group_0_binding_0.rule3Scale)); + vVel = (normalize(vVel) * clamp(length(vVel), 0.0, 0.1)); + vPos = (vPos + (vVel * _group_0_binding_0.deltaT)); + if((vPos[0] < -1.0)) { + vPos[0] = 1.0; + } + if((vPos[0] > 1.0)) { + vPos[0] = -1.0; + } + if((vPos[1] < -1.0)) { + vPos[1] = 1.0; + } + if((vPos[1] > 1.0)) { + vPos[1] = -1.0; + } + _group_0_binding_2.particles[global_invocation_id[0]].pos = vPos; + _group_0_binding_2.particles[global_invocation_id[0]].vel = vVel; + return; +} + diff --git a/third_party/rust/naga/tests/out/boids.msl b/third_party/rust/naga/tests/out/boids.msl new file mode 100644 index 000000000000..05f0760f3520 --- /dev/null +++ b/third_party/rust/naga/tests/out/boids.msl @@ -0,0 +1,105 @@ +#include +#include + +struct _mslBufferSizes { + metal::uint size1; + metal::uint size2; +}; + +constexpr constant unsigned NUM_PARTICLES = 1500u; +struct Particle { + metal::float2 pos; + metal::float2 vel; +}; +struct SimParams { + float deltaT; + float rule1Distance; + float rule2Distance; + float rule3Distance; + float rule1Scale; + float rule2Scale; + float rule3Scale; +}; +typedef Particle type3[1]; +struct Particles { + type3 particles; +}; + +struct main1Input { +}; +kernel void main1( + metal::uint3 global_invocation_id [[thread_position_in_grid]] +, constant SimParams& params [[buffer(0)]] +, constant Particles& particlesSrc [[buffer(1)]] +, device Particles& particlesDst [[buffer(2)]] +, constant _mslBufferSizes& _buffer_sizes [[buffer(3)]] +) { + metal::float2 vPos; + metal::float2 vVel; + metal::float2 cMass; + metal::float2 cVel; + metal::float2 colVel; + int cMassCount = 0; + int cVelCount = 0; + metal::float2 pos1; + metal::float2 vel1; + metal::uint i = 0u; + if (global_invocation_id.x >= NUM_PARTICLES) { + return; + } + vPos = particlesSrc.particles[global_invocation_id.x].pos; + vVel = particlesSrc.particles[global_invocation_id.x].vel; + cMass = metal::float2(0.0, 0.0); + cVel = metal::float2(0.0, 0.0); + colVel = metal::float2(0.0, 0.0); + bool loop_init = true; + while(true) { + if (!loop_init) { + i = i + 1u; + } + loop_init = false; + if (i >= NUM_PARTICLES) { + break; + } + if (i == global_invocation_id.x) { + continue; + } + pos1 = particlesSrc.particles[i].pos; + vel1 = particlesSrc.particles[i].vel; + if (metal::distance(pos1, vPos) < params.rule1Distance) { + cMass = cMass + pos1; + cMassCount = cMassCount + 1; + } + if (metal::distance(pos1, vPos) < params.rule2Distance) { + colVel = colVel - (pos1 - vPos); + } + if (metal::distance(pos1, vPos) < params.rule3Distance) { + cVel = cVel + vel1; + cVelCount = cVelCount + 1; + } + } + if (cMassCount > 0) { + cMass = (cMass / metal::float2(static_cast(cMassCount))) - vPos; + } + if (cVelCount > 0) { + cVel = cVel / metal::float2(static_cast(cVelCount)); + } + vVel = ((vVel + (cMass * params.rule1Scale)) + (colVel * params.rule2Scale)) + (cVel * params.rule3Scale); + vVel = metal::normalize(vVel) * metal::clamp(metal::length(vVel), 0.0, 0.1); + vPos = vPos + (vVel * params.deltaT); + if (vPos.x < -1.0) { + vPos.x = 1.0; + } + if (vPos.x > 1.0) { + vPos.x = -1.0; + } + if (vPos.y < -1.0) { + vPos.y = 1.0; + } + if (vPos.y > 1.0) { + vPos.y = -1.0; + } + particlesDst.particles[global_invocation_id.x].pos = vPos; + particlesDst.particles[global_invocation_id.x].vel = vVel; + return; +} diff --git a/third_party/rust/naga/tests/out/boids.spvasm b/third_party/rust/naga/tests/out/boids.spvasm new file mode 100644 index 000000000000..a40311f79fc6 --- /dev/null +++ b/third_party/rust/naga/tests/out/boids.spvasm @@ -0,0 +1,330 @@ +; SPIR-V +; Version: 1.0 +; Generator: rspirv +; Bound: 203 +OpCapability Shader +OpExtension "SPV_KHR_storage_buffer_storage_class" +%1 = OpExtInstImport "GLSL.std.450" +OpMemoryModel Logical GLSL450 +OpEntryPoint GLCompute %43 "main" %40 +OpExecutionMode %43 LocalSize 64 1 1 +OpSource GLSL 450 +OpName %3 "NUM_PARTICLES" +OpName %16 "Particle" +OpMemberName %16 0 "pos" +OpMemberName %16 1 "vel" +OpName %17 "SimParams" +OpMemberName %17 0 "deltaT" +OpMemberName %17 1 "rule1Distance" +OpMemberName %17 2 "rule2Distance" +OpMemberName %17 3 "rule3Distance" +OpMemberName %17 4 "rule1Scale" +OpMemberName %17 5 "rule2Scale" +OpMemberName %17 6 "rule3Scale" +OpName %19 "Particles" +OpMemberName %19 0 "particles" +OpName %21 "params" +OpName %23 "particlesSrc" +OpName %25 "particlesDst" +OpName %26 "vPos" +OpName %28 "vVel" +OpName %29 "cMass" +OpName %30 "cVel" +OpName %31 "colVel" +OpName %32 "cMassCount" +OpName %34 "cVelCount" +OpName %35 "pos" +OpName %36 "vel" +OpName %37 "i" +OpName %40 "global_invocation_id" +OpName %43 "main" +OpMemberDecorate %16 0 Offset 0 +OpMemberDecorate %16 1 Offset 8 +OpDecorate %17 Block +OpMemberDecorate %17 0 Offset 0 +OpMemberDecorate %17 1 Offset 4 +OpMemberDecorate %17 2 Offset 8 +OpMemberDecorate %17 3 Offset 12 +OpMemberDecorate %17 4 Offset 16 +OpMemberDecorate %17 5 Offset 20 +OpMemberDecorate %17 6 Offset 24 +OpDecorate %18 ArrayStride 16 +OpDecorate %19 Block +OpMemberDecorate %19 0 Offset 0 +OpDecorate %21 DescriptorSet 0 +OpDecorate %21 Binding 0 +OpDecorate %23 NonWritable +OpDecorate %23 DescriptorSet 0 +OpDecorate %23 Binding 1 +OpDecorate %25 DescriptorSet 0 +OpDecorate %25 Binding 2 +OpDecorate %40 BuiltIn GlobalInvocationId +%2 = OpTypeVoid +%4 = OpTypeInt 32 0 +%3 = OpConstant %4 1500 +%6 = OpTypeFloat 32 +%5 = OpConstant %6 0.0 +%8 = OpTypeInt 32 1 +%7 = OpConstant %8 0 +%9 = OpConstant %4 0 +%10 = OpConstant %8 1 +%11 = OpConstant %4 1 +%12 = OpConstant %6 0.1 +%13 = OpConstant %6 -1.0 +%14 = OpConstant %6 1.0 +%15 = OpTypeVector %6 2 +%16 = OpTypeStruct %15 %15 +%17 = OpTypeStruct %6 %6 %6 %6 %6 %6 %6 +%18 = OpTypeRuntimeArray %16 +%19 = OpTypeStruct %18 +%20 = OpTypeVector %4 3 +%22 = OpTypePointer Uniform %17 +%21 = OpVariable %22 Uniform +%24 = OpTypePointer StorageBuffer %19 +%23 = OpVariable %24 StorageBuffer +%25 = OpVariable %24 StorageBuffer +%27 = OpTypePointer Function %15 +%33 = OpTypePointer Function %8 +%38 = OpTypePointer Function %4 +%41 = OpTypePointer Input %20 +%40 = OpVariable %41 Input +%44 = OpTypeFunction %2 +%47 = OpTypeBool +%51 = OpTypePointer StorageBuffer %18 +%52 = OpTypePointer StorageBuffer %16 +%53 = OpTypePointer StorageBuffer %15 +%82 = OpTypePointer Uniform %6 +%96 = OpConstant %4 2 +%110 = OpConstant %4 3 +%145 = OpConstant %4 4 +%151 = OpConstant %4 5 +%157 = OpConstant %4 6 +%179 = OpTypePointer Function %6 +%43 = OpFunction %2 None %44 +%39 = OpLabel +%37 = OpVariable %38 Function %9 +%34 = OpVariable %33 Function %7 +%30 = OpVariable %27 Function +%26 = OpVariable %27 Function +%35 = OpVariable %27 Function +%31 = OpVariable %27 Function +%28 = OpVariable %27 Function +%36 = OpVariable %27 Function +%32 = OpVariable %33 Function %7 +%29 = OpVariable %27 Function +%42 = OpLoad %20 %40 +OpBranch %45 +%45 = OpLabel +%46 = OpCompositeExtract %4 %42 0 +%48 = OpUGreaterThanEqual %47 %46 %3 +OpSelectionMerge %49 None +OpBranchConditional %48 %50 %49 +%50 = OpLabel +OpReturn +%49 = OpLabel +%54 = OpAccessChain %53 %23 %9 %46 %9 +%55 = OpLoad %15 %54 +OpStore %26 %55 +%56 = OpAccessChain %53 %23 %9 %46 %11 +%57 = OpLoad %15 %56 +OpStore %28 %57 +%58 = OpCompositeConstruct %15 %5 %5 +OpStore %29 %58 +%59 = OpCompositeConstruct %15 %5 %5 +OpStore %30 %59 +%60 = OpCompositeConstruct %15 %5 %5 +OpStore %31 %60 +OpBranch %61 +%61 = OpLabel +OpLoopMerge %62 %64 None +OpBranch %63 +%63 = OpLabel +%65 = OpLoad %4 %37 +%66 = OpUGreaterThanEqual %47 %65 %3 +OpSelectionMerge %67 None +OpBranchConditional %66 %68 %67 +%68 = OpLabel +OpBranch %62 +%67 = OpLabel +%69 = OpLoad %4 %37 +%70 = OpIEqual %47 %69 %46 +OpSelectionMerge %71 None +OpBranchConditional %70 %72 %71 +%72 = OpLabel +OpBranch %64 +%71 = OpLabel +%73 = OpLoad %4 %37 +%74 = OpAccessChain %53 %23 %9 %73 %9 +%75 = OpLoad %15 %74 +OpStore %35 %75 +%76 = OpLoad %4 %37 +%77 = OpAccessChain %53 %23 %9 %76 %11 +%78 = OpLoad %15 %77 +OpStore %36 %78 +%79 = OpLoad %15 %35 +%80 = OpLoad %15 %26 +%81 = OpExtInst %6 %1 Distance %79 %80 +%83 = OpAccessChain %82 %21 %11 +%84 = OpLoad %6 %83 +%85 = OpFOrdLessThan %47 %81 %84 +OpSelectionMerge %86 None +OpBranchConditional %85 %87 %86 +%87 = OpLabel +%88 = OpLoad %15 %29 +%89 = OpLoad %15 %35 +%90 = OpFAdd %15 %88 %89 +OpStore %29 %90 +%91 = OpLoad %8 %32 +%92 = OpIAdd %8 %91 %10 +OpStore %32 %92 +OpBranch %86 +%86 = OpLabel +%93 = OpLoad %15 %35 +%94 = OpLoad %15 %26 +%95 = OpExtInst %6 %1 Distance %93 %94 +%97 = OpAccessChain %82 %21 %96 +%98 = OpLoad %6 %97 +%99 = OpFOrdLessThan %47 %95 %98 +OpSelectionMerge %100 None +OpBranchConditional %99 %101 %100 +%101 = OpLabel +%102 = OpLoad %15 %31 +%103 = OpLoad %15 %35 +%104 = OpLoad %15 %26 +%105 = OpFSub %15 %103 %104 +%106 = OpFSub %15 %102 %105 +OpStore %31 %106 +OpBranch %100 +%100 = OpLabel +%107 = OpLoad %15 %35 +%108 = OpLoad %15 %26 +%109 = OpExtInst %6 %1 Distance %107 %108 +%111 = OpAccessChain %82 %21 %110 +%112 = OpLoad %6 %111 +%113 = OpFOrdLessThan %47 %109 %112 +OpSelectionMerge %114 None +OpBranchConditional %113 %115 %114 +%115 = OpLabel +%116 = OpLoad %15 %30 +%117 = OpLoad %15 %36 +%118 = OpFAdd %15 %116 %117 +OpStore %30 %118 +%119 = OpLoad %8 %34 +%120 = OpIAdd %8 %119 %10 +OpStore %34 %120 +OpBranch %114 +%114 = OpLabel +OpBranch %64 +%64 = OpLabel +%121 = OpLoad %4 %37 +%122 = OpIAdd %4 %121 %11 +OpStore %37 %122 +OpBranch %61 +%62 = OpLabel +%123 = OpLoad %8 %32 +%124 = OpSGreaterThan %47 %123 %7 +OpSelectionMerge %125 None +OpBranchConditional %124 %126 %125 +%126 = OpLabel +%127 = OpLoad %15 %29 +%128 = OpLoad %8 %32 +%129 = OpConvertSToF %6 %128 +%130 = OpCompositeConstruct %15 %129 %129 +%131 = OpFDiv %15 %127 %130 +%132 = OpLoad %15 %26 +%133 = OpFSub %15 %131 %132 +OpStore %29 %133 +OpBranch %125 +%125 = OpLabel +%134 = OpLoad %8 %34 +%135 = OpSGreaterThan %47 %134 %7 +OpSelectionMerge %136 None +OpBranchConditional %135 %137 %136 +%137 = OpLabel +%138 = OpLoad %15 %30 +%139 = OpLoad %8 %34 +%140 = OpConvertSToF %6 %139 +%141 = OpCompositeConstruct %15 %140 %140 +%142 = OpFDiv %15 %138 %141 +OpStore %30 %142 +OpBranch %136 +%136 = OpLabel +%143 = OpLoad %15 %28 +%144 = OpLoad %15 %29 +%146 = OpAccessChain %82 %21 %145 +%147 = OpLoad %6 %146 +%148 = OpVectorTimesScalar %15 %144 %147 +%149 = OpFAdd %15 %143 %148 +%150 = OpLoad %15 %31 +%152 = OpAccessChain %82 %21 %151 +%153 = OpLoad %6 %152 +%154 = OpVectorTimesScalar %15 %150 %153 +%155 = OpFAdd %15 %149 %154 +%156 = OpLoad %15 %30 +%158 = OpAccessChain %82 %21 %157 +%159 = OpLoad %6 %158 +%160 = OpVectorTimesScalar %15 %156 %159 +%161 = OpFAdd %15 %155 %160 +OpStore %28 %161 +%162 = OpLoad %15 %28 +%163 = OpExtInst %15 %1 Normalize %162 +%164 = OpLoad %15 %28 +%165 = OpExtInst %6 %1 Length %164 +%166 = OpExtInst %6 %1 FClamp %165 %5 %12 +%167 = OpVectorTimesScalar %15 %163 %166 +OpStore %28 %167 +%168 = OpLoad %15 %26 +%169 = OpLoad %15 %28 +%170 = OpAccessChain %82 %21 %9 +%171 = OpLoad %6 %170 +%172 = OpVectorTimesScalar %15 %169 %171 +%173 = OpFAdd %15 %168 %172 +OpStore %26 %173 +%174 = OpLoad %15 %26 +%175 = OpCompositeExtract %6 %174 0 +%176 = OpFOrdLessThan %47 %175 %13 +OpSelectionMerge %177 None +OpBranchConditional %176 %178 %177 +%178 = OpLabel +%180 = OpAccessChain %179 %26 %9 +OpStore %180 %14 +OpBranch %177 +%177 = OpLabel +%181 = OpLoad %15 %26 +%182 = OpCompositeExtract %6 %181 0 +%183 = OpFOrdGreaterThan %47 %182 %14 +OpSelectionMerge %184 None +OpBranchConditional %183 %185 %184 +%185 = OpLabel +%186 = OpAccessChain %179 %26 %9 +OpStore %186 %13 +OpBranch %184 +%184 = OpLabel +%187 = OpLoad %15 %26 +%188 = OpCompositeExtract %6 %187 1 +%189 = OpFOrdLessThan %47 %188 %13 +OpSelectionMerge %190 None +OpBranchConditional %189 %191 %190 +%191 = OpLabel +%192 = OpAccessChain %179 %26 %11 +OpStore %192 %14 +OpBranch %190 +%190 = OpLabel +%193 = OpLoad %15 %26 +%194 = OpCompositeExtract %6 %193 1 +%195 = OpFOrdGreaterThan %47 %194 %14 +OpSelectionMerge %196 None +OpBranchConditional %195 %197 %196 +%197 = OpLabel +%198 = OpAccessChain %179 %26 %11 +OpStore %198 %13 +OpBranch %196 +%196 = OpLabel +%199 = OpLoad %15 %26 +%200 = OpAccessChain %53 %25 %9 %46 %9 +OpStore %200 %199 +%201 = OpLoad %15 %28 +%202 = OpAccessChain %53 %25 %9 %46 %11 +OpStore %202 %201 +OpReturn +OpFunctionEnd \ No newline at end of file diff --git a/third_party/rust/naga/tests/out/collatz.info.ron b/third_party/rust/naga/tests/out/collatz.info.ron new file mode 100644 index 000000000000..edf6a5a5a437 --- /dev/null +++ b/third_party/rust/naga/tests/out/collatz.info.ron @@ -0,0 +1,497 @@ +( + functions: [ + ( + flags: ( + bits: 31, + ), + available_stages: ( + bits: 7, + ), + uniformity: ( + non_uniform_result: Some(5), + requirements: ( + bits: 0, + ), + ), + may_kill: false, + sampling_set: [], + global_uses: [ + ( + bits: 0, + ), + ], + expressions: [ + ( + uniformity: ( + non_uniform_result: Some(1), + requirements: ( + bits: 0, + ), + ), + ref_count: 0, + assignable_global: Some(1), + ty: Value(Pointer( + base: 3, + class: Storage, + )), + ), + ( + uniformity: ( + non_uniform_result: Some(2), + requirements: ( + bits: 0, + ), + ), + ref_count: 1, + assignable_global: None, + ty: Handle(1), + ), + ( + uniformity: ( + non_uniform_result: Some(3), + requirements: ( + bits: 0, + ), + ), + ref_count: 7, + assignable_global: None, + ty: Value(Pointer( + base: 1, + class: Function, + )), + ), + ( + uniformity: ( + non_uniform_result: None, + requirements: ( + bits: 0, + ), + ), + ref_count: 0, + assignable_global: None, + ty: Value(Scalar( + kind: Uint, + width: 4, + )), + ), + ( + uniformity: ( + non_uniform_result: Some(5), + requirements: ( + bits: 0, + ), + ), + ref_count: 3, + assignable_global: None, + ty: Value(Pointer( + base: 1, + class: Function, + )), + ), + ( + uniformity: ( + non_uniform_result: Some(3), + requirements: ( + bits: 0, + ), + ), + ref_count: 1, + assignable_global: None, + ty: Handle(1), + ), + ( + uniformity: ( + non_uniform_result: None, + requirements: ( + bits: 0, + ), + ), + ref_count: 1, + assignable_global: None, + ty: Value(Scalar( + kind: Uint, + width: 4, + )), + ), + ( + uniformity: ( + non_uniform_result: Some(3), + requirements: ( + bits: 0, + ), + ), + ref_count: 1, + assignable_global: None, + ty: Value(Scalar( + kind: Bool, + width: 1, + )), + ), + ( + uniformity: ( + non_uniform_result: Some(3), + requirements: ( + bits: 0, + ), + ), + ref_count: 1, + assignable_global: None, + ty: Handle(1), + ), + ( + uniformity: ( + non_uniform_result: None, + requirements: ( + bits: 0, + ), + ), + ref_count: 1, + assignable_global: None, + ty: Value(Scalar( + kind: Uint, + width: 4, + )), + ), + ( + uniformity: ( + non_uniform_result: Some(3), + requirements: ( + bits: 0, + ), + ), + ref_count: 1, + assignable_global: None, + ty: Handle(1), + ), + ( + uniformity: ( + non_uniform_result: None, + requirements: ( + bits: 0, + ), + ), + ref_count: 1, + assignable_global: None, + ty: Value(Scalar( + kind: Uint, + width: 4, + )), + ), + ( + uniformity: ( + non_uniform_result: Some(3), + requirements: ( + bits: 0, + ), + ), + ref_count: 1, + assignable_global: None, + ty: Value(Scalar( + kind: Bool, + width: 1, + )), + ), + ( + uniformity: ( + non_uniform_result: Some(3), + requirements: ( + bits: 0, + ), + ), + ref_count: 1, + assignable_global: None, + ty: Handle(1), + ), + ( + uniformity: ( + non_uniform_result: None, + requirements: ( + bits: 0, + ), + ), + ref_count: 1, + assignable_global: None, + ty: Value(Scalar( + kind: Uint, + width: 4, + )), + ), + ( + uniformity: ( + non_uniform_result: Some(3), + requirements: ( + bits: 0, + ), + ), + ref_count: 1, + assignable_global: None, + ty: Handle(1), + ), + ( + uniformity: ( + non_uniform_result: None, + requirements: ( + bits: 0, + ), + ), + ref_count: 1, + assignable_global: None, + ty: Value(Scalar( + kind: Uint, + width: 4, + )), + ), + ( + uniformity: ( + non_uniform_result: Some(3), + requirements: ( + bits: 0, + ), + ), + ref_count: 1, + assignable_global: None, + ty: Handle(1), + ), + ( + uniformity: ( + non_uniform_result: Some(3), + requirements: ( + bits: 0, + ), + ), + ref_count: 1, + assignable_global: None, + ty: Handle(1), + ), + ( + uniformity: ( + non_uniform_result: None, + requirements: ( + bits: 0, + ), + ), + ref_count: 1, + assignable_global: None, + ty: Value(Scalar( + kind: Uint, + width: 4, + )), + ), + ( + uniformity: ( + non_uniform_result: Some(3), + requirements: ( + bits: 0, + ), + ), + ref_count: 1, + assignable_global: None, + ty: Handle(1), + ), + ( + uniformity: ( + non_uniform_result: Some(5), + requirements: ( + bits: 0, + ), + ), + ref_count: 1, + assignable_global: None, + ty: Handle(1), + ), + ( + uniformity: ( + non_uniform_result: None, + requirements: ( + bits: 0, + ), + ), + ref_count: 1, + assignable_global: None, + ty: Value(Scalar( + kind: Uint, + width: 4, + )), + ), + ( + uniformity: ( + non_uniform_result: Some(5), + requirements: ( + bits: 0, + ), + ), + ref_count: 1, + assignable_global: None, + ty: Handle(1), + ), + ( + uniformity: ( + non_uniform_result: Some(5), + requirements: ( + bits: 0, + ), + ), + ref_count: 1, + assignable_global: None, + ty: Handle(1), + ), + ], + ), + ], + entry_points: [ + ( + flags: ( + bits: 31, + ), + available_stages: ( + bits: 7, + ), + uniformity: ( + non_uniform_result: Some(5), + requirements: ( + bits: 0, + ), + ), + may_kill: false, + sampling_set: [], + global_uses: [ + ( + bits: 3, + ), + ], + expressions: [ + ( + uniformity: ( + non_uniform_result: Some(1), + requirements: ( + bits: 0, + ), + ), + ref_count: 2, + assignable_global: Some(1), + ty: Value(Pointer( + base: 3, + class: Storage, + )), + ), + ( + uniformity: ( + non_uniform_result: Some(2), + requirements: ( + bits: 0, + ), + ), + ref_count: 2, + assignable_global: None, + ty: Handle(4), + ), + ( + uniformity: ( + non_uniform_result: Some(1), + requirements: ( + bits: 0, + ), + ), + ref_count: 1, + assignable_global: Some(1), + ty: Value(Pointer( + base: 2, + class: Storage, + )), + ), + ( + uniformity: ( + non_uniform_result: Some(2), + requirements: ( + bits: 0, + ), + ), + ref_count: 1, + assignable_global: None, + ty: Value(Scalar( + kind: Uint, + width: 4, + )), + ), + ( + uniformity: ( + non_uniform_result: Some(1), + requirements: ( + bits: 0, + ), + ), + ref_count: 1, + assignable_global: Some(1), + ty: Value(Pointer( + base: 1, + class: Storage, + )), + ), + ( + uniformity: ( + non_uniform_result: Some(1), + requirements: ( + bits: 0, + ), + ), + ref_count: 1, + assignable_global: Some(1), + ty: Value(Pointer( + base: 2, + class: Storage, + )), + ), + ( + uniformity: ( + non_uniform_result: Some(2), + requirements: ( + bits: 0, + ), + ), + ref_count: 1, + assignable_global: None, + ty: Value(Scalar( + kind: Uint, + width: 4, + )), + ), + ( + uniformity: ( + non_uniform_result: Some(1), + requirements: ( + bits: 0, + ), + ), + ref_count: 1, + assignable_global: Some(1), + ty: Value(Pointer( + base: 1, + class: Storage, + )), + ), + ( + uniformity: ( + non_uniform_result: Some(1), + requirements: ( + bits: 0, + ), + ), + ref_count: 1, + assignable_global: None, + ty: Handle(1), + ), + ( + uniformity: ( + non_uniform_result: Some(5), + requirements: ( + bits: 0, + ), + ), + ref_count: 1, + assignable_global: None, + ty: Handle(1), + ), + ], + ), + ], +) \ No newline at end of file diff --git a/third_party/rust/naga/tests/out/collatz.msl b/third_party/rust/naga/tests/out/collatz.msl new file mode 100644 index 000000000000..b33506022b73 --- /dev/null +++ b/third_party/rust/naga/tests/out/collatz.msl @@ -0,0 +1,42 @@ +#include +#include + +struct _mslBufferSizes { + metal::uint size0; +}; + +typedef metal::uint type1[1]; +struct PrimeIndices { + type1 data; +}; + +metal::uint collatz_iterations( + metal::uint n_base +) { + metal::uint n; + metal::uint i = 0u; + n = n_base; + while(true) { + if (n <= 1u) { + break; + } + if ((n % 2u) == 0u) { + n = n / 2u; + } else { + n = (3u * n) + 1u; + } + i = i + 1u; + } + return i; +} + +struct main1Input { +}; +kernel void main1( + metal::uint3 global_id [[thread_position_in_grid]] +, device PrimeIndices& v_indices [[user(fake0)]] +) { + metal::uint _e9 = collatz_iterations(v_indices.data[global_id.x]); + v_indices.data[global_id.x] = _e9; + return; +} diff --git a/third_party/rust/naga/tests/out/collatz.ron b/third_party/rust/naga/tests/out/collatz.ron new file mode 100644 index 000000000000..65f98a6e8fa0 --- /dev/null +++ b/third_party/rust/naga/tests/out/collatz.ron @@ -0,0 +1,346 @@ +( + types: [ + ( + name: None, + inner: Scalar( + kind: Uint, + width: 4, + ), + ), + ( + name: None, + inner: Array( + base: 1, + size: Dynamic, + stride: 4, + ), + ), + ( + name: Some("PrimeIndices"), + inner: Struct( + level: Root, + members: [ + ( + name: Some("data"), + ty: 2, + binding: None, + offset: 0, + ), + ], + span: 4, + ), + ), + ( + name: None, + inner: Vector( + size: Tri, + kind: Uint, + width: 4, + ), + ), + ], + constants: [ + ( + name: None, + specialization: None, + inner: Scalar( + width: 4, + value: Uint(0), + ), + ), + ( + name: None, + specialization: None, + inner: Scalar( + width: 4, + value: Uint(1), + ), + ), + ( + name: None, + specialization: None, + inner: Scalar( + width: 4, + value: Uint(2), + ), + ), + ( + name: None, + specialization: None, + inner: Scalar( + width: 4, + value: Uint(3), + ), + ), + ], + global_variables: [ + ( + name: Some("v_indices"), + class: Storage, + binding: Some(( + group: 0, + binding: 0, + )), + ty: 3, + init: None, + storage_access: ( + bits: 3, + ), + ), + ], + functions: [ + ( + name: Some("collatz_iterations"), + arguments: [ + ( + name: Some("n_base"), + ty: 1, + binding: None, + ), + ], + result: Some(( + ty: 1, + binding: None, + )), + local_variables: [ + ( + name: Some("n"), + ty: 1, + init: None, + ), + ( + name: Some("i"), + ty: 1, + init: Some(1), + ), + ], + expressions: [ + GlobalVariable(1), + FunctionArgument(0), + LocalVariable(1), + Constant(1), + LocalVariable(2), + Load( + pointer: 3, + ), + Constant(2), + Binary( + op: LessEqual, + left: 6, + right: 7, + ), + Load( + pointer: 3, + ), + Constant(3), + Binary( + op: Modulo, + left: 9, + right: 10, + ), + Constant(1), + Binary( + op: Equal, + left: 11, + right: 12, + ), + Load( + pointer: 3, + ), + Constant(3), + Binary( + op: Divide, + left: 14, + right: 15, + ), + Constant(4), + Load( + pointer: 3, + ), + Binary( + op: Multiply, + left: 17, + right: 18, + ), + Constant(2), + Binary( + op: Add, + left: 19, + right: 20, + ), + Load( + pointer: 5, + ), + Constant(2), + Binary( + op: Add, + left: 22, + right: 23, + ), + Load( + pointer: 5, + ), + ], + body: [ + Store( + pointer: 3, + value: 2, + ), + Loop( + body: [ + Emit(( + start: 5, + end: 6, + )), + Emit(( + start: 7, + end: 8, + )), + If( + condition: 8, + accept: [ + Break, + ], + reject: [], + ), + Emit(( + start: 8, + end: 9, + )), + Emit(( + start: 10, + end: 11, + )), + Emit(( + start: 12, + end: 13, + )), + If( + condition: 13, + accept: [ + Emit(( + start: 13, + end: 14, + )), + Emit(( + start: 15, + end: 16, + )), + Store( + pointer: 3, + value: 16, + ), + ], + reject: [ + Emit(( + start: 17, + end: 19, + )), + Emit(( + start: 20, + end: 21, + )), + Store( + pointer: 3, + value: 21, + ), + ], + ), + Emit(( + start: 21, + end: 22, + )), + Emit(( + start: 23, + end: 24, + )), + Store( + pointer: 5, + value: 24, + ), + ], + continuing: [], + ), + Emit(( + start: 24, + end: 25, + )), + Return( + value: Some(25), + ), + ], + ), + ], + entry_points: [ + ( + name: "main", + stage: Compute, + early_depth_test: None, + workgroup_size: (1, 1, 1), + function: ( + name: Some("main"), + arguments: [ + ( + name: Some("global_id"), + ty: 4, + binding: Some(BuiltIn(GlobalInvocationId)), + ), + ], + result: None, + local_variables: [], + expressions: [ + GlobalVariable(1), + FunctionArgument(0), + AccessIndex( + base: 1, + index: 0, + ), + AccessIndex( + base: 2, + index: 0, + ), + Access( + base: 3, + index: 4, + ), + AccessIndex( + base: 1, + index: 0, + ), + AccessIndex( + base: 2, + index: 0, + ), + Access( + base: 6, + index: 7, + ), + Load( + pointer: 8, + ), + Call(1), + ], + body: [ + Emit(( + start: 2, + end: 9, + )), + Call( + function: 1, + arguments: [ + 9, + ], + result: Some(10), + ), + Store( + pointer: 5, + value: 10, + ), + Return( + value: None, + ), + ], + ), + ), + ], +) \ No newline at end of file diff --git a/third_party/rust/naga/tests/out/collatz.spvasm b/third_party/rust/naga/tests/out/collatz.spvasm new file mode 100644 index 000000000000..ce1b3e5e263e --- /dev/null +++ b/third_party/rust/naga/tests/out/collatz.spvasm @@ -0,0 +1,105 @@ +; SPIR-V +; Version: 1.0 +; Generator: rspirv +; Bound: 59 +OpCapability Shader +OpExtension "SPV_KHR_storage_buffer_storage_class" +%1 = OpExtInstImport "GLSL.std.450" +OpMemoryModel Logical GLSL450 +OpEntryPoint GLCompute %48 "main" %45 +OpExecutionMode %48 LocalSize 1 1 1 +OpSource GLSL 450 +OpName %9 "PrimeIndices" +OpMemberName %9 0 "data" +OpName %11 "v_indices" +OpName %13 "n" +OpName %15 "i" +OpName %18 "collatz_iterations" +OpName %45 "global_id" +OpName %48 "main" +OpDecorate %8 ArrayStride 4 +OpDecorate %9 Block +OpMemberDecorate %9 0 Offset 0 +OpDecorate %11 DescriptorSet 0 +OpDecorate %11 Binding 0 +OpDecorate %45 BuiltIn GlobalInvocationId +%2 = OpTypeVoid +%4 = OpTypeInt 32 0 +%3 = OpConstant %4 0 +%5 = OpConstant %4 1 +%6 = OpConstant %4 2 +%7 = OpConstant %4 3 +%8 = OpTypeRuntimeArray %4 +%9 = OpTypeStruct %8 +%10 = OpTypeVector %4 3 +%12 = OpTypePointer StorageBuffer %9 +%11 = OpVariable %12 StorageBuffer +%14 = OpTypePointer Function %4 +%19 = OpTypeFunction %4 %4 +%26 = OpTypeBool +%46 = OpTypePointer Input %10 +%45 = OpVariable %46 Input +%49 = OpTypeFunction %2 +%51 = OpTypePointer StorageBuffer %8 +%53 = OpTypePointer StorageBuffer %4 +%18 = OpFunction %4 None %19 +%17 = OpFunctionParameter %4 +%16 = OpLabel +%13 = OpVariable %14 Function +%15 = OpVariable %14 Function %3 +OpBranch %20 +%20 = OpLabel +OpStore %13 %17 +OpBranch %21 +%21 = OpLabel +OpLoopMerge %22 %24 None +OpBranch %23 +%23 = OpLabel +%25 = OpLoad %4 %13 +%27 = OpULessThanEqual %26 %25 %5 +OpSelectionMerge %28 None +OpBranchConditional %27 %29 %28 +%29 = OpLabel +OpBranch %22 +%28 = OpLabel +%30 = OpLoad %4 %13 +%31 = OpUMod %4 %30 %6 +%32 = OpIEqual %26 %31 %3 +OpSelectionMerge %33 None +OpBranchConditional %32 %34 %35 +%34 = OpLabel +%36 = OpLoad %4 %13 +%37 = OpUDiv %4 %36 %6 +OpStore %13 %37 +OpBranch %33 +%35 = OpLabel +%38 = OpLoad %4 %13 +%39 = OpIMul %4 %7 %38 +%40 = OpIAdd %4 %39 %5 +OpStore %13 %40 +OpBranch %33 +%33 = OpLabel +%41 = OpLoad %4 %15 +%42 = OpIAdd %4 %41 %5 +OpStore %15 %42 +OpBranch %24 +%24 = OpLabel +OpBranch %21 +%22 = OpLabel +%43 = OpLoad %4 %15 +OpReturnValue %43 +OpFunctionEnd +%48 = OpFunction %2 None %49 +%44 = OpLabel +%47 = OpLoad %10 %45 +OpBranch %50 +%50 = OpLabel +%52 = OpCompositeExtract %4 %47 0 +%54 = OpCompositeExtract %4 %47 0 +%55 = OpAccessChain %53 %11 %3 %54 +%56 = OpLoad %4 %55 +%57 = OpFunctionCall %4 %18 %56 +%58 = OpAccessChain %53 %11 %3 %52 +OpStore %58 %57 +OpReturn +OpFunctionEnd \ No newline at end of file diff --git a/third_party/rust/naga/tests/out/control-flow.Compute.glsl b/third_party/rust/naga/tests/out/control-flow.Compute.glsl new file mode 100644 index 000000000000..2599163286a9 --- /dev/null +++ b/third_party/rust/naga/tests/out/control-flow.Compute.glsl @@ -0,0 +1,14 @@ +#version 310 es + +precision highp float; + +layout(local_size_x = 1, local_size_y = 1, local_size_z = 1) in; + + +void main() { + uvec3 global_id = gl_GlobalInvocationID; + groupMemoryBarrier(); + groupMemoryBarrier(); + return; +} + diff --git a/third_party/rust/naga/tests/out/control-flow.msl b/third_party/rust/naga/tests/out/control-flow.msl new file mode 100644 index 000000000000..d7afd08cff22 --- /dev/null +++ b/third_party/rust/naga/tests/out/control-flow.msl @@ -0,0 +1,13 @@ +#include +#include + + +struct main1Input { +}; +kernel void main1( + metal::uint3 global_id [[thread_position_in_grid]] +) { + metal::threadgroup_barrier(metal::mem_flags::mem_device); + metal::threadgroup_barrier(metal::mem_flags::mem_threadgroup); + return; +} diff --git a/third_party/rust/naga/tests/out/control-flow.spvasm b/third_party/rust/naga/tests/out/control-flow.spvasm new file mode 100644 index 000000000000..b46424b801b8 --- /dev/null +++ b/third_party/rust/naga/tests/out/control-flow.spvasm @@ -0,0 +1,29 @@ +; SPIR-V +; Version: 1.1 +; Generator: rspirv +; Bound: 16 +OpCapability Shader +%1 = OpExtInstImport "GLSL.std.450" +OpMemoryModel Logical GLSL450 +OpEntryPoint GLCompute %9 "main" %6 +OpExecutionMode %9 LocalSize 1 1 1 +OpDecorate %6 BuiltIn GlobalInvocationId +%2 = OpTypeVoid +%4 = OpTypeInt 32 0 +%3 = OpTypeVector %4 3 +%7 = OpTypePointer Input %3 +%6 = OpVariable %7 Input +%10 = OpTypeFunction %2 +%12 = OpConstant %4 2 +%13 = OpConstant %4 1 +%14 = OpConstant %4 72 +%15 = OpConstant %4 264 +%9 = OpFunction %2 None %10 +%5 = OpLabel +%8 = OpLoad %3 %6 +OpBranch %11 +%11 = OpLabel +OpControlBarrier %12 %13 %14 +OpControlBarrier %12 %12 %15 +OpReturn +OpFunctionEnd \ No newline at end of file diff --git a/third_party/rust/naga/tests/out/empty.Compute.glsl b/third_party/rust/naga/tests/out/empty.Compute.glsl new file mode 100644 index 000000000000..63328eab1894 --- /dev/null +++ b/third_party/rust/naga/tests/out/empty.Compute.glsl @@ -0,0 +1,11 @@ +#version 310 es + +precision highp float; + +layout(local_size_x = 1, local_size_y = 1, local_size_z = 1) in; + + +void main() { + return; +} + diff --git a/third_party/rust/naga/tests/out/empty.Compute.hlsl b/third_party/rust/naga/tests/out/empty.Compute.hlsl new file mode 100644 index 000000000000..2b52438678eb --- /dev/null +++ b/third_party/rust/naga/tests/out/empty.Compute.hlsl @@ -0,0 +1,6 @@ + +[numthreads(1, 1, 1)] +void main() +{ + return; +} diff --git a/third_party/rust/naga/tests/out/empty.hlsl b/third_party/rust/naga/tests/out/empty.hlsl new file mode 100644 index 000000000000..d1d3c9cb27a2 --- /dev/null +++ b/third_party/rust/naga/tests/out/empty.hlsl @@ -0,0 +1,5 @@ + +void main() +{ + return; +} diff --git a/third_party/rust/naga/tests/out/empty.msl b/third_party/rust/naga/tests/out/empty.msl new file mode 100644 index 000000000000..9b4ad48624ab --- /dev/null +++ b/third_party/rust/naga/tests/out/empty.msl @@ -0,0 +1,8 @@ +#include +#include + + +kernel void main1( +) { + return; +} diff --git a/third_party/rust/naga/tests/out/empty.spvasm b/third_party/rust/naga/tests/out/empty.spvasm new file mode 100644 index 000000000000..47d3c1756549 --- /dev/null +++ b/third_party/rust/naga/tests/out/empty.spvasm @@ -0,0 +1,17 @@ +; SPIR-V +; Version: 1.1 +; Generator: rspirv +; Bound: 7 +OpCapability Shader +%1 = OpExtInstImport "GLSL.std.450" +OpMemoryModel Logical GLSL450 +OpEntryPoint GLCompute %4 "main" +OpExecutionMode %4 LocalSize 1 1 1 +%2 = OpTypeVoid +%5 = OpTypeFunction %2 +%4 = OpFunction %2 None %5 +%3 = OpLabel +OpBranch %6 +%6 = OpLabel +OpReturn +OpFunctionEnd \ No newline at end of file diff --git a/third_party/rust/naga/tests/out/empty.wgsl b/third_party/rust/naga/tests/out/empty.wgsl new file mode 100644 index 000000000000..1ee73b580936 --- /dev/null +++ b/third_party/rust/naga/tests/out/empty.wgsl @@ -0,0 +1,4 @@ +[[stage(compute), workgroup_size(1, 1, 1)]] +fn main() { + return; +} diff --git a/third_party/rust/naga/tests/out/extra.msl b/third_party/rust/naga/tests/out/extra.msl new file mode 100644 index 000000000000..b2a2363d90fe --- /dev/null +++ b/third_party/rust/naga/tests/out/extra.msl @@ -0,0 +1,21 @@ +#include +#include + +struct PushConstants { + metal::uint index; + char _pad1[12]; + metal::float2 double1; +}; + +struct main1Input { + metal::float4 color [[user(loc0), center_perspective]]; +}; +struct main1Output { + metal::float4 member [[color(0)]]; +}; +fragment main1Output main1( + main1Input varyings [[stage_in]] +) { + const auto color = varyings.color; + return main1Output { color }; +} diff --git a/third_party/rust/naga/tests/out/extra.spvasm b/third_party/rust/naga/tests/out/extra.spvasm new file mode 100644 index 000000000000..ec05bdbdf104 --- /dev/null +++ b/third_party/rust/naga/tests/out/extra.spvasm @@ -0,0 +1,37 @@ +; SPIR-V +; Version: 1.0 +; Generator: rspirv +; Bound: 20 +OpCapability Shader +OpCapability Float64 +%1 = OpExtInstImport "GLSL.std.450" +OpMemoryModel Logical GLSL450 +OpEntryPoint Fragment %17 "main" %12 %15 +OpExecutionMode %17 OriginUpperLeft +OpDecorate %6 Block +OpMemberDecorate %6 0 Offset 0 +OpMemberDecorate %6 1 Offset 16 +OpDecorate %12 Location 0 +OpDecorate %15 Location 0 +%2 = OpTypeVoid +%3 = OpTypeInt 32 0 +%5 = OpTypeFloat 64 +%4 = OpTypeVector %5 2 +%6 = OpTypeStruct %3 %4 +%8 = OpTypeFloat 32 +%7 = OpTypeVector %8 4 +%10 = OpTypePointer PushConstant %6 +%9 = OpVariable %10 PushConstant +%13 = OpTypePointer Input %7 +%12 = OpVariable %13 Input +%16 = OpTypePointer Output %7 +%15 = OpVariable %16 Output +%18 = OpTypeFunction %2 +%17 = OpFunction %2 None %18 +%11 = OpLabel +%14 = OpLoad %7 %12 +OpBranch %19 +%19 = OpLabel +OpStore %15 %14 +OpReturn +OpFunctionEnd \ No newline at end of file diff --git a/third_party/rust/naga/tests/out/image.msl b/third_party/rust/naga/tests/out/image.msl new file mode 100644 index 000000000000..a8d37578eb63 --- /dev/null +++ b/third_party/rust/naga/tests/out/image.msl @@ -0,0 +1,32 @@ +#include +#include + + +struct main1Input { +}; +kernel void main1( + metal::uint3 local_id [[thread_position_in_threadgroup]] +, metal::texture2d image_src [[user(fake0)]] +, metal::texture1d image_dst [[user(fake0)]] +) { + metal::int2 _e10 = (int2(image_src.get_width(), image_src.get_height()) * static_cast(local_id.xy)) % metal::int2(10, 20); + metal::uint4 _e11 = image_src.read(metal::uint2(_e10)); + image_dst.write(_e11, metal::uint(_e10.x)); + return; +} + + +struct queriesOutput { + metal::float4 member1 [[position]]; +}; +vertex queriesOutput queries( + metal::texture1d image_1d [[user(fake0)]] +, metal::texture2d image_2d [[user(fake0)]] +, metal::texture2d_array image_2d_array [[user(fake0)]] +, metal::texturecube image_cube [[user(fake0)]] +, metal::texturecube_array image_cube_array [[user(fake0)]] +, metal::texture3d image_3d [[user(fake0)]] +, metal::texture2d_ms image_aa [[user(fake0)]] +) { + return queriesOutput { metal::float4(static_cast((((((((((((((((((int(image_1d.get_width()) + int2(image_2d.get_width(), image_2d.get_height()).y) + int2(image_2d.get_width(1), image_2d.get_height(1)).y) + int2(image_2d_array.get_width(), image_2d_array.get_height()).y) + int2(image_2d_array.get_width(1), image_2d_array.get_height(1)).y) + int(image_2d_array.get_array_size())) + int3(image_cube.get_width()).y) + int3(image_cube.get_width(1)).y) + int3(image_cube_array.get_width()).y) + int3(image_cube_array.get_width(1)).y) + int(image_cube_array.get_array_size())) + int3(image_3d.get_width(), image_3d.get_height(), image_3d.get_depth()).z) + int3(image_3d.get_width(1), image_3d.get_height(1), image_3d.get_depth(1)).z) + int(image_aa.get_num_samples())) + int(image_2d.get_num_mip_levels())) + int(image_2d_array.get_num_mip_levels())) + int(image_3d.get_num_mip_levels())) + int(image_cube.get_num_mip_levels())) + int(image_cube_array.get_num_mip_levels()))) }; +} diff --git a/third_party/rust/naga/tests/out/image.spvasm b/third_party/rust/naga/tests/out/image.spvasm new file mode 100644 index 000000000000..e0cac29d476f --- /dev/null +++ b/third_party/rust/naga/tests/out/image.spvasm @@ -0,0 +1,181 @@ +; SPIR-V +; Version: 1.1 +; Generator: rspirv +; Bound: 127 +OpCapability ImageQuery +OpCapability Image1D +OpCapability Shader +OpCapability Sampled1D +%1 = OpExtInstImport "GLSL.std.450" +OpMemoryModel Logical GLSL450 +OpEntryPoint GLCompute %43 "main" %40 +OpEntryPoint Vertex %61 "queries" %59 +OpExecutionMode %43 LocalSize 16 1 1 +OpSource GLSL 450 +OpName %21 "image_src" +OpName %23 "image_dst" +OpName %25 "image_1d" +OpName %27 "image_2d" +OpName %29 "image_2d_array" +OpName %31 "image_cube" +OpName %33 "image_cube_array" +OpName %35 "image_3d" +OpName %37 "image_aa" +OpName %40 "local_id" +OpName %43 "main" +OpName %61 "queries" +OpDecorate %21 NonWritable +OpDecorate %21 DescriptorSet 0 +OpDecorate %21 Binding 1 +OpDecorate %23 NonReadable +OpDecorate %23 DescriptorSet 0 +OpDecorate %23 Binding 2 +OpDecorate %25 DescriptorSet 0 +OpDecorate %25 Binding 0 +OpDecorate %27 DescriptorSet 0 +OpDecorate %27 Binding 1 +OpDecorate %29 DescriptorSet 0 +OpDecorate %29 Binding 2 +OpDecorate %31 DescriptorSet 0 +OpDecorate %31 Binding 3 +OpDecorate %33 DescriptorSet 0 +OpDecorate %33 Binding 4 +OpDecorate %35 DescriptorSet 0 +OpDecorate %35 Binding 5 +OpDecorate %37 DescriptorSet 0 +OpDecorate %37 Binding 6 +OpDecorate %40 BuiltIn LocalInvocationId +OpDecorate %59 BuiltIn Position +%2 = OpTypeVoid +%4 = OpTypeInt 32 1 +%3 = OpConstant %4 10 +%5 = OpConstant %4 20 +%6 = OpConstant %4 1 +%8 = OpTypeInt 32 0 +%7 = OpTypeImage %8 2D 0 0 0 2 Rgba8ui +%9 = OpTypeImage %8 1D 0 0 0 2 R32ui +%10 = OpTypeVector %8 3 +%11 = OpTypeVector %4 2 +%13 = OpTypeFloat 32 +%12 = OpTypeImage %13 1D 0 0 0 1 Unknown +%14 = OpTypeImage %13 2D 0 0 0 1 Unknown +%15 = OpTypeImage %13 2D 0 1 0 1 Unknown +%16 = OpTypeImage %13 Cube 0 0 0 1 Unknown +%17 = OpTypeImage %13 Cube 0 1 0 1 Unknown +%18 = OpTypeImage %13 3D 0 0 0 1 Unknown +%19 = OpTypeImage %13 2D 0 0 1 1 Unknown +%20 = OpTypeVector %13 4 +%22 = OpTypePointer UniformConstant %7 +%21 = OpVariable %22 UniformConstant +%24 = OpTypePointer UniformConstant %9 +%23 = OpVariable %24 UniformConstant +%26 = OpTypePointer UniformConstant %12 +%25 = OpVariable %26 UniformConstant +%28 = OpTypePointer UniformConstant %14 +%27 = OpVariable %28 UniformConstant +%30 = OpTypePointer UniformConstant %15 +%29 = OpVariable %30 UniformConstant +%32 = OpTypePointer UniformConstant %16 +%31 = OpVariable %32 UniformConstant +%34 = OpTypePointer UniformConstant %17 +%33 = OpVariable %34 UniformConstant +%36 = OpTypePointer UniformConstant %18 +%35 = OpVariable %36 UniformConstant +%38 = OpTypePointer UniformConstant %19 +%37 = OpVariable %38 UniformConstant +%41 = OpTypePointer Input %10 +%40 = OpVariable %41 Input +%44 = OpTypeFunction %2 +%49 = OpTypeVector %8 2 +%55 = OpTypeVector %8 4 +%60 = OpTypePointer Output %20 +%59 = OpVariable %60 Output +%70 = OpConstant %8 0 +%75 = OpTypeVector %4 3 +%43 = OpFunction %2 None %44 +%39 = OpLabel +%42 = OpLoad %10 %40 +%45 = OpLoad %7 %21 +%46 = OpLoad %9 %23 +OpBranch %47 +%47 = OpLabel +%48 = OpImageQuerySize %11 %45 +%50 = OpVectorShuffle %49 %42 %42 0 1 +%51 = OpBitcast %11 %50 +%52 = OpIMul %11 %48 %51 +%53 = OpCompositeConstruct %11 %3 %5 +%54 = OpSMod %11 %52 %53 +%56 = OpImageRead %55 %45 %54 +%57 = OpCompositeExtract %4 %54 0 +OpImageWrite %46 %57 %56 +OpReturn +OpFunctionEnd +%61 = OpFunction %2 None %44 +%58 = OpLabel +%62 = OpLoad %12 %25 +%63 = OpLoad %14 %27 +%64 = OpLoad %15 %29 +%65 = OpLoad %16 %31 +%66 = OpLoad %17 %33 +%67 = OpLoad %18 %35 +%68 = OpLoad %19 %37 +OpBranch %69 +%69 = OpLabel +%71 = OpImageQuerySizeLod %4 %62 %70 +%72 = OpImageQuerySizeLod %11 %63 %70 +%73 = OpImageQueryLevels %4 %63 +%74 = OpImageQuerySizeLod %11 %63 %6 +%76 = OpImageQuerySizeLod %75 %64 %70 +%77 = OpVectorShuffle %11 %76 %76 0 1 +%78 = OpImageQueryLevels %4 %64 +%79 = OpImageQuerySizeLod %75 %64 %6 +%80 = OpVectorShuffle %11 %79 %79 0 1 +%81 = OpImageQuerySizeLod %75 %64 %70 +%82 = OpCompositeExtract %4 %81 2 +%83 = OpImageQuerySizeLod %11 %65 %70 +%84 = OpVectorShuffle %75 %83 %83 0 0 0 +%85 = OpImageQueryLevels %4 %65 +%86 = OpImageQuerySizeLod %11 %65 %6 +%87 = OpVectorShuffle %75 %86 %86 0 0 0 +%88 = OpImageQuerySizeLod %75 %66 %70 +%89 = OpImageQueryLevels %4 %66 +%90 = OpImageQuerySizeLod %75 %66 %6 +%91 = OpImageQuerySizeLod %75 %66 %70 +%92 = OpCompositeExtract %4 %91 2 +%93 = OpImageQuerySizeLod %75 %67 %70 +%94 = OpImageQueryLevels %4 %67 +%95 = OpImageQuerySizeLod %75 %67 %6 +%96 = OpImageQuerySamples %4 %68 +%97 = OpCompositeExtract %4 %72 1 +%98 = OpIAdd %4 %71 %97 +%99 = OpCompositeExtract %4 %74 1 +%100 = OpIAdd %4 %98 %99 +%101 = OpCompositeExtract %4 %77 1 +%102 = OpIAdd %4 %100 %101 +%103 = OpCompositeExtract %4 %80 1 +%104 = OpIAdd %4 %102 %103 +%105 = OpIAdd %4 %104 %82 +%106 = OpCompositeExtract %4 %84 1 +%107 = OpIAdd %4 %105 %106 +%108 = OpCompositeExtract %4 %87 1 +%109 = OpIAdd %4 %107 %108 +%110 = OpCompositeExtract %4 %88 1 +%111 = OpIAdd %4 %109 %110 +%112 = OpCompositeExtract %4 %90 1 +%113 = OpIAdd %4 %111 %112 +%114 = OpIAdd %4 %113 %92 +%115 = OpCompositeExtract %4 %93 2 +%116 = OpIAdd %4 %114 %115 +%117 = OpCompositeExtract %4 %95 2 +%118 = OpIAdd %4 %116 %117 +%119 = OpIAdd %4 %118 %96 +%120 = OpIAdd %4 %119 %73 +%121 = OpIAdd %4 %120 %78 +%122 = OpIAdd %4 %121 %94 +%123 = OpIAdd %4 %122 %85 +%124 = OpIAdd %4 %123 %89 +%125 = OpConvertSToF %13 %124 +%126 = OpCompositeConstruct %20 %125 %125 %125 %125 +OpStore %59 %126 +OpReturn +OpFunctionEnd \ No newline at end of file diff --git a/third_party/rust/naga/tests/out/interpolate.Fragment.glsl b/third_party/rust/naga/tests/out/interpolate.Fragment.glsl new file mode 100644 index 000000000000..b415997304b5 --- /dev/null +++ b/third_party/rust/naga/tests/out/interpolate.Fragment.glsl @@ -0,0 +1,25 @@ +#version 400 core +struct FragmentInput { + vec4 position; + uint flat1; + float linear; + vec2 linear_centroid; + vec3 linear_sample; + vec4 perspective; + float perspective_centroid; + float perspective_sample; +}; + +flat in uint _vs2fs_location0; +noperspective in float _vs2fs_location1; +noperspective centroid in vec2 _vs2fs_location2; +noperspective sample in vec3 _vs2fs_location3; +smooth in vec4 _vs2fs_location4; +smooth centroid in float _vs2fs_location5; +smooth sample in float _vs2fs_location6; + +void main() { + FragmentInput val = FragmentInput(gl_FragCoord, _vs2fs_location0, _vs2fs_location1, _vs2fs_location2, _vs2fs_location3, _vs2fs_location4, _vs2fs_location5, _vs2fs_location6); + return; +} + diff --git a/third_party/rust/naga/tests/out/interpolate.Vertex.glsl b/third_party/rust/naga/tests/out/interpolate.Vertex.glsl new file mode 100644 index 000000000000..e287089a15e9 --- /dev/null +++ b/third_party/rust/naga/tests/out/interpolate.Vertex.glsl @@ -0,0 +1,41 @@ +#version 400 core +struct FragmentInput { + vec4 position; + uint flat1; + float linear; + vec2 linear_centroid; + vec3 linear_sample; + vec4 perspective; + float perspective_centroid; + float perspective_sample; +}; + +flat out uint _vs2fs_location0; +noperspective out float _vs2fs_location1; +noperspective centroid out vec2 _vs2fs_location2; +noperspective sample out vec3 _vs2fs_location3; +smooth out vec4 _vs2fs_location4; +smooth centroid out float _vs2fs_location5; +smooth sample out float _vs2fs_location6; + +void main() { + FragmentInput out1; + out1.position = vec4(2.0, 4.0, 5.0, 6.0); + out1.flat1 = 8u; + out1.linear = 27.0; + out1.linear_centroid = vec2(64.0, 125.0); + out1.linear_sample = vec3(216.0, 343.0, 512.0); + out1.perspective = vec4(729.0, 1000.0, 1331.0, 1728.0); + out1.perspective_centroid = 2197.0; + out1.perspective_sample = 2744.0; + gl_Position = out1.position; + _vs2fs_location0 = out1.flat1; + _vs2fs_location1 = out1.linear; + _vs2fs_location2 = out1.linear_centroid; + _vs2fs_location3 = out1.linear_sample; + _vs2fs_location4 = out1.perspective; + _vs2fs_location5 = out1.perspective_centroid; + _vs2fs_location6 = out1.perspective_sample; + return; +} + diff --git a/third_party/rust/naga/tests/out/interpolate.msl b/third_party/rust/naga/tests/out/interpolate.msl new file mode 100644 index 000000000000..963e78b95ca0 --- /dev/null +++ b/third_party/rust/naga/tests/out/interpolate.msl @@ -0,0 +1,56 @@ +#include +#include + +struct FragmentInput { + metal::float4 position; + metal::uint flat; + float linear; + metal::float2 linear_centroid; + metal::float3 linear_sample; + metal::float4 perspective; + float perspective_centroid; + float perspective_sample; +}; + +struct main1Output { + metal::float4 position [[position]]; + metal::uint flat [[user(loc0), flat]]; + float linear [[user(loc1), center_no_perspective]]; + metal::float2 linear_centroid [[user(loc2), centroid_no_perspective]]; + metal::float3 linear_sample [[user(loc3), sample_no_perspective]]; + metal::float4 perspective [[user(loc4), center_perspective]]; + float perspective_centroid [[user(loc5), centroid_perspective]]; + float perspective_sample [[user(loc6), sample_perspective]]; +}; +vertex main1Output main1( +) { + FragmentInput out; + out.position = metal::float4(2.0, 4.0, 5.0, 6.0); + out.flat = 8u; + out.linear = 27.0; + out.linear_centroid = metal::float2(64.0, 125.0); + out.linear_sample = metal::float3(216.0, 343.0, 512.0); + out.perspective = metal::float4(729.0, 1000.0, 1331.0, 1728.0); + out.perspective_centroid = 2197.0; + out.perspective_sample = 2744.0; + const auto _tmp = out; + return main1Output { _tmp.position, _tmp.flat, _tmp.linear, _tmp.linear_centroid, _tmp.linear_sample, _tmp.perspective, _tmp.perspective_centroid, _tmp.perspective_sample }; +} + + +struct main2Input { + metal::uint flat [[user(loc0), flat]]; + float linear [[user(loc1), center_no_perspective]]; + metal::float2 linear_centroid [[user(loc2), centroid_no_perspective]]; + metal::float3 linear_sample [[user(loc3), sample_no_perspective]]; + metal::float4 perspective [[user(loc4), center_perspective]]; + float perspective_centroid [[user(loc5), centroid_perspective]]; + float perspective_sample [[user(loc6), sample_perspective]]; +}; +fragment void main2( + main2Input varyings1 [[stage_in]] +, metal::float4 position [[position]] +) { + const FragmentInput val = { position, varyings1.flat, varyings1.linear, varyings1.linear_centroid, varyings1.linear_sample, varyings1.perspective, varyings1.perspective_centroid, varyings1.perspective_sample }; + return; +} diff --git a/third_party/rust/naga/tests/out/interpolate.spvasm b/third_party/rust/naga/tests/out/interpolate.spvasm new file mode 100644 index 000000000000..80f62b6a4014 --- /dev/null +++ b/third_party/rust/naga/tests/out/interpolate.spvasm @@ -0,0 +1,209 @@ +; SPIR-V +; Version: 1.0 +; Generator: rspirv +; Bound: 108 +OpCapability Shader +OpCapability SampleRateShading +%1 = OpExtInstImport "GLSL.std.450" +OpMemoryModel Logical GLSL450 +OpEntryPoint Vertex %42 "main" %29 %31 %33 %35 %37 %39 %40 %41 +OpEntryPoint Fragment %106 "main" %85 %88 %91 %94 %97 %100 %102 %104 +OpExecutionMode %106 OriginUpperLeft +OpSource GLSL 450 +OpName %25 "FragmentInput" +OpMemberName %25 0 "position" +OpMemberName %25 1 "flat" +OpMemberName %25 2 "linear" +OpMemberName %25 3 "linear_centroid" +OpMemberName %25 4 "linear_sample" +OpMemberName %25 5 "perspective" +OpMemberName %25 6 "perspective_centroid" +OpMemberName %25 7 "perspective_sample" +OpName %26 "out" +OpName %29 "position" +OpName %31 "flat" +OpName %33 "linear" +OpName %35 "linear_centroid" +OpName %37 "linear_sample" +OpName %39 "perspective" +OpName %40 "perspective_centroid" +OpName %41 "perspective_sample" +OpName %42 "main" +OpName %85 "position" +OpName %88 "flat" +OpName %91 "linear" +OpName %94 "linear_centroid" +OpName %97 "linear_sample" +OpName %100 "perspective" +OpName %102 "perspective_centroid" +OpName %104 "perspective_sample" +OpName %106 "main" +OpMemberDecorate %25 0 Offset 0 +OpMemberDecorate %25 1 Offset 16 +OpMemberDecorate %25 2 Offset 20 +OpMemberDecorate %25 3 Offset 24 +OpMemberDecorate %25 4 Offset 32 +OpMemberDecorate %25 5 Offset 48 +OpMemberDecorate %25 6 Offset 64 +OpMemberDecorate %25 7 Offset 68 +OpDecorate %29 BuiltIn Position +OpDecorate %31 Location 0 +OpDecorate %31 Flat +OpDecorate %33 Location 1 +OpDecorate %33 NoPerspective +OpDecorate %35 Location 2 +OpDecorate %35 NoPerspective +OpDecorate %35 Centroid +OpDecorate %37 Location 3 +OpDecorate %37 NoPerspective +OpDecorate %37 Sample +OpDecorate %39 Location 4 +OpDecorate %40 Location 5 +OpDecorate %40 Centroid +OpDecorate %41 Location 6 +OpDecorate %41 Sample +OpDecorate %85 BuiltIn FragCoord +OpDecorate %88 Location 0 +OpDecorate %88 Flat +OpDecorate %91 Location 1 +OpDecorate %91 NoPerspective +OpDecorate %94 Location 2 +OpDecorate %94 NoPerspective +OpDecorate %94 Centroid +OpDecorate %97 Location 3 +OpDecorate %97 NoPerspective +OpDecorate %97 Sample +OpDecorate %100 Location 4 +OpDecorate %102 Location 5 +OpDecorate %102 Centroid +OpDecorate %104 Location 6 +OpDecorate %104 Sample +%2 = OpTypeVoid +%4 = OpTypeFloat 32 +%3 = OpConstant %4 2.0 +%5 = OpConstant %4 4.0 +%6 = OpConstant %4 5.0 +%7 = OpConstant %4 6.0 +%9 = OpTypeInt 32 0 +%8 = OpConstant %9 8 +%10 = OpConstant %4 27.0 +%11 = OpConstant %4 64.0 +%12 = OpConstant %4 125.0 +%13 = OpConstant %4 216.0 +%14 = OpConstant %4 343.0 +%15 = OpConstant %4 512.0 +%16 = OpConstant %4 729.0 +%17 = OpConstant %4 1000.0 +%18 = OpConstant %4 1331.0 +%19 = OpConstant %4 1728.0 +%20 = OpConstant %4 2197.0 +%21 = OpConstant %4 2744.0 +%22 = OpTypeVector %4 4 +%23 = OpTypeVector %4 2 +%24 = OpTypeVector %4 3 +%25 = OpTypeStruct %22 %9 %4 %23 %24 %22 %4 %4 +%27 = OpTypePointer Function %25 +%30 = OpTypePointer Output %22 +%29 = OpVariable %30 Output +%32 = OpTypePointer Output %9 +%31 = OpVariable %32 Output +%34 = OpTypePointer Output %4 +%33 = OpVariable %34 Output +%36 = OpTypePointer Output %23 +%35 = OpVariable %36 Output +%38 = OpTypePointer Output %24 +%37 = OpVariable %38 Output +%39 = OpVariable %30 Output +%40 = OpVariable %34 Output +%41 = OpVariable %34 Output +%43 = OpTypeFunction %2 +%45 = OpTypePointer Function %22 +%47 = OpConstant %9 0 +%49 = OpTypePointer Function %9 +%50 = OpConstant %9 1 +%52 = OpTypePointer Function %4 +%53 = OpConstant %9 2 +%55 = OpTypePointer Function %23 +%57 = OpConstant %9 3 +%59 = OpTypePointer Function %24 +%61 = OpConstant %9 4 +%64 = OpConstant %9 5 +%66 = OpConstant %9 6 +%68 = OpConstant %9 7 +%73 = OpTypePointer Output %4 +%86 = OpTypePointer Input %22 +%85 = OpVariable %86 Input +%89 = OpTypePointer Input %9 +%88 = OpVariable %89 Input +%92 = OpTypePointer Input %4 +%91 = OpVariable %92 Input +%95 = OpTypePointer Input %23 +%94 = OpVariable %95 Input +%98 = OpTypePointer Input %24 +%97 = OpVariable %98 Input +%100 = OpVariable %86 Input +%102 = OpVariable %92 Input +%104 = OpVariable %92 Input +%42 = OpFunction %2 None %43 +%28 = OpLabel +%26 = OpVariable %27 Function +OpBranch %44 +%44 = OpLabel +%46 = OpCompositeConstruct %22 %3 %5 %6 %7 +%48 = OpAccessChain %45 %26 %47 +OpStore %48 %46 +%51 = OpAccessChain %49 %26 %50 +OpStore %51 %8 +%54 = OpAccessChain %52 %26 %53 +OpStore %54 %10 +%56 = OpCompositeConstruct %23 %11 %12 +%58 = OpAccessChain %55 %26 %57 +OpStore %58 %56 +%60 = OpCompositeConstruct %24 %13 %14 %15 +%62 = OpAccessChain %59 %26 %61 +OpStore %62 %60 +%63 = OpCompositeConstruct %22 %16 %17 %18 %19 +%65 = OpAccessChain %45 %26 %64 +OpStore %65 %63 +%67 = OpAccessChain %52 %26 %66 +OpStore %67 %20 +%69 = OpAccessChain %52 %26 %68 +OpStore %69 %21 +%70 = OpLoad %25 %26 +%71 = OpCompositeExtract %22 %70 0 +OpStore %29 %71 +%72 = OpAccessChain %73 %29 %50 +%74 = OpLoad %4 %72 +%75 = OpFNegate %4 %74 +OpStore %72 %75 +%76 = OpCompositeExtract %9 %70 1 +OpStore %31 %76 +%77 = OpCompositeExtract %4 %70 2 +OpStore %33 %77 +%78 = OpCompositeExtract %23 %70 3 +OpStore %35 %78 +%79 = OpCompositeExtract %24 %70 4 +OpStore %37 %79 +%80 = OpCompositeExtract %22 %70 5 +OpStore %39 %80 +%81 = OpCompositeExtract %4 %70 6 +OpStore %40 %81 +%82 = OpCompositeExtract %4 %70 7 +OpStore %41 %82 +OpReturn +OpFunctionEnd +%106 = OpFunction %2 None %43 +%83 = OpLabel +%87 = OpLoad %22 %85 +%90 = OpLoad %9 %88 +%93 = OpLoad %4 %91 +%96 = OpLoad %23 %94 +%99 = OpLoad %24 %97 +%101 = OpLoad %22 %100 +%103 = OpLoad %4 %102 +%105 = OpLoad %4 %104 +%84 = OpCompositeConstruct %25 %87 %90 %93 %96 %99 %101 %103 %105 +OpBranch %107 +%107 = OpLabel +OpReturn +OpFunctionEnd \ No newline at end of file diff --git a/third_party/rust/naga/tests/out/operators.Vertex.glsl b/third_party/rust/naga/tests/out/operators.Vertex.glsl new file mode 100644 index 000000000000..88898e6efa92 --- /dev/null +++ b/third_party/rust/naga/tests/out/operators.Vertex.glsl @@ -0,0 +1,10 @@ +#version 310 es + +precision highp float; + + +void main() { + gl_Position = ((((vec2(1.0) + vec2(2.0)) - vec2(3.0)) / vec2(4.0)).xyxy + vec4((ivec4(5) % ivec4(2)))); + return; +} + diff --git a/third_party/rust/naga/tests/out/operators.msl b/third_party/rust/naga/tests/out/operators.msl new file mode 100644 index 000000000000..48c82bbcaa8b --- /dev/null +++ b/third_party/rust/naga/tests/out/operators.msl @@ -0,0 +1,17 @@ +#include +#include + + +metal::float4 splat( +) { + return (((metal::float2(1.0) + metal::float2(2.0)) - metal::float2(3.0)) / metal::float2(4.0)).xyxy + static_cast(metal::int4(5) % metal::int4(2)); +} + +int unary( +) { + if (!true) { + return 1; + } else { + return !1; + } +} diff --git a/third_party/rust/naga/tests/out/operators.spvasm b/third_party/rust/naga/tests/out/operators.spvasm new file mode 100644 index 000000000000..a6380e59c11b --- /dev/null +++ b/third_party/rust/naga/tests/out/operators.spvasm @@ -0,0 +1,60 @@ +; SPIR-V +; Version: 1.0 +; Generator: rspirv +; Bound: 44 +OpCapability Shader +OpCapability Linkage +%1 = OpExtInstImport "GLSL.std.450" +OpMemoryModel Logical GLSL450 +%2 = OpTypeVoid +%4 = OpTypeFloat 32 +%3 = OpConstant %4 1.0 +%5 = OpConstant %4 2.0 +%6 = OpConstant %4 3.0 +%7 = OpConstant %4 4.0 +%9 = OpTypeInt 32 1 +%8 = OpConstant %9 5 +%10 = OpConstant %9 2 +%11 = OpConstant %9 1 +%13 = OpTypeBool +%12 = OpConstantTrue %13 +%14 = OpTypeVector %4 4 +%17 = OpTypeFunction %14 +%19 = OpTypeVector %4 2 +%27 = OpTypeVector %9 4 +%36 = OpTypeFunction %9 +%43 = OpConstantNull %9 +%16 = OpFunction %14 None %17 +%15 = OpLabel +OpBranch %18 +%18 = OpLabel +%20 = OpCompositeConstruct %19 %5 %5 +%21 = OpCompositeConstruct %19 %3 %3 +%22 = OpFAdd %19 %21 %20 +%23 = OpCompositeConstruct %19 %6 %6 +%24 = OpFSub %19 %22 %23 +%25 = OpCompositeConstruct %19 %7 %7 +%26 = OpFDiv %19 %24 %25 +%28 = OpCompositeConstruct %27 %8 %8 %8 %8 +%29 = OpCompositeConstruct %27 %10 %10 %10 %10 +%30 = OpSMod %27 %28 %29 +%31 = OpVectorShuffle %14 %26 %26 0 1 0 1 +%32 = OpConvertSToF %14 %30 +%33 = OpFAdd %14 %31 %32 +OpReturnValue %33 +OpFunctionEnd +%35 = OpFunction %9 None %36 +%34 = OpLabel +OpBranch %37 +%37 = OpLabel +%38 = OpLogicalNot %13 %12 +OpSelectionMerge %39 None +OpBranchConditional %38 %40 %41 +%40 = OpLabel +OpReturnValue %11 +%41 = OpLabel +%42 = OpNot %9 %11 +OpReturnValue %42 +%39 = OpLabel +OpReturnValue %43 +OpFunctionEnd \ No newline at end of file diff --git a/third_party/rust/naga/tests/out/quad-vert.Vertex.glsl b/third_party/rust/naga/tests/out/quad-vert.Vertex.glsl new file mode 100644 index 000000000000..3f3bb13ad645 --- /dev/null +++ b/third_party/rust/naga/tests/out/quad-vert.Vertex.glsl @@ -0,0 +1,48 @@ +#version 310 es + +precision highp float; + +struct type10 { + vec2 member; + vec4 gen_gl_Position1; + float gen_gl_PointSize1; + float gen_gl_ClipDistance1[1]; + float gen_gl_CullDistance1[1]; +}; + +vec2 v_uv = vec2(0, 0); + +vec2 a_uv = vec2(0, 0); + +struct gen_gl_PerVertex_block_0 { + vec4 gen_gl_Position; + float gen_gl_PointSize; + float gen_gl_ClipDistance[1]; + float gen_gl_CullDistance[1]; +} perVertexStruct; + +vec2 a_pos = vec2(0, 0); + +layout(location = 1) in vec2 _p2vs_location1; +layout(location = 0) in vec2 _p2vs_location0; +smooth layout(location = 0) out vec2 _vs2fs_location0; + +void main1() { + v_uv = a_uv; + vec2 _expr13 = a_pos; + perVertexStruct.gen_gl_Position = vec4(_expr13[0], _expr13[1], 0.0, 1.0); + return; +} + +void main() { + vec2 a_uv1 = _p2vs_location1; + vec2 a_pos1 = _p2vs_location0; + a_uv = a_uv1; + a_pos = a_pos1; + main1(); + type10 _tmp_return = type10(v_uv, perVertexStruct.gen_gl_Position, perVertexStruct.gen_gl_PointSize, perVertexStruct.gen_gl_ClipDistance, perVertexStruct.gen_gl_CullDistance); + _vs2fs_location0 = _tmp_return.member; + gl_Position = _tmp_return.gen_gl_Position1; + return; +} + diff --git a/third_party/rust/naga/tests/out/quad-vert.msl b/third_party/rust/naga/tests/out/quad-vert.msl new file mode 100644 index 000000000000..16f2fbc83f8e --- /dev/null +++ b/third_party/rust/naga/tests/out/quad-vert.msl @@ -0,0 +1,60 @@ +#include +#include + +struct type6 { + float inner[1u]; +}; +struct gl_PerVertex { + metal::float4 gl_Position; + float gl_PointSize; + type6 gl_ClipDistance; + type6 gl_CullDistance; +}; +struct type10 { + metal::float2 member; + metal::float4 gl_Position1; + float gl_PointSize1; + type6 gl_ClipDistance1; + type6 gl_CullDistance1; +}; +constant metal::float4 const_type4_ = {0.0, 0.0, 0.0, 1.0}; +constant type6 const_type6_ = {0.0}; +constant gl_PerVertex const_gl_PerVertex = {const_type4_, 1.0, const_type6_, const_type6_}; + +void main1( + thread metal::float2& v_uv, + thread metal::float2 const& a_uv, + thread gl_PerVertex& perVertexStruct, + thread metal::float2 const& a_pos +) { + v_uv = a_uv; + metal::float2 _e13 = a_pos; + perVertexStruct.gl_Position = metal::float4(_e13.x, _e13.y, 0.0, 1.0); + return; +} + +struct main2Input { + metal::float2 a_uv1 [[attribute(1)]]; + metal::float2 a_pos1 [[attribute(0)]]; +}; +struct main2Output { + metal::float2 member [[user(loc0), center_perspective]]; + metal::float4 gl_Position1 [[position]]; + float gl_PointSize1 [[point_size]]; + float gl_ClipDistance1 [[clip_distance]] [1]; +}; +vertex main2Output main2( + main2Input varyings [[stage_in]] +) { + metal::float2 v_uv = {}; + metal::float2 a_uv = {}; + gl_PerVertex perVertexStruct = const_gl_PerVertex; + metal::float2 a_pos = {}; + const auto a_uv1 = varyings.a_uv1; + const auto a_pos1 = varyings.a_pos1; + a_uv = a_uv1; + a_pos = a_pos1; + main1(v_uv, a_uv, perVertexStruct, a_pos); + const auto _tmp = type10 {v_uv, perVertexStruct.gl_Position, perVertexStruct.gl_PointSize, perVertexStruct.gl_ClipDistance, perVertexStruct.gl_CullDistance}; + return main2Output { _tmp.member, _tmp.gl_Position1, _tmp.gl_PointSize1, {_tmp.gl_ClipDistance1.inner[0]} }; +} diff --git a/third_party/rust/naga/tests/out/quad-vert.wgsl b/third_party/rust/naga/tests/out/quad-vert.wgsl new file mode 100644 index 000000000000..66a173b4cfa6 --- /dev/null +++ b/third_party/rust/naga/tests/out/quad-vert.wgsl @@ -0,0 +1,29 @@ +[[block]] +struct gl_PerVertex { + [[builtin(position)]] gl_Position: vec4; +}; + +struct type10 { + [[location(0), interpolate(perspective)]] member: vec2; + [[builtin(position)]] gl_Position1: vec4; +}; + +var v_uv: vec2; +var a_uv: vec2; +var perVertexStruct: gl_PerVertex; +var a_pos: vec2; + +fn main() { + v_uv = a_uv; + let _e13: vec2 = a_pos; + perVertexStruct.gl_Position = vec4(_e13[0], _e13[1], 0.0, 1.0); + return; +} + +[[stage(vertex)]] +fn main1([[location(1)]] a_uv1: vec2, [[location(0)]] a_pos1: vec2) -> type10 { + a_uv = a_uv1; + a_pos = a_pos1; + main(); + return type10(v_uv, perVertexStruct.gl_Position); +} diff --git a/third_party/rust/naga/tests/out/quad.Fragment.glsl b/third_party/rust/naga/tests/out/quad.Fragment.glsl new file mode 100644 index 000000000000..8062e3d20010 --- /dev/null +++ b/third_party/rust/naga/tests/out/quad.Fragment.glsl @@ -0,0 +1,24 @@ +#version 310 es + +precision highp float; + +struct VertexOutput { + vec2 uv; + vec4 position; +}; + +uniform highp sampler2D _group_0_binding_0; + +smooth layout(location = 0) in vec2 _vs2fs_location0; +layout(location = 0) out vec4 _fs2p_location0; + +void main() { + vec2 uv2 = _vs2fs_location0; + vec4 _expr4 = texture(_group_0_binding_0, vec2(uv2)); + if((_expr4[3] == 0.0)) { + discard; + } + _fs2p_location0 = (_expr4[3] * _expr4); + return; +} + diff --git a/third_party/rust/naga/tests/out/quad.Vertex.glsl b/third_party/rust/naga/tests/out/quad.Vertex.glsl new file mode 100644 index 000000000000..95ce41427a6a --- /dev/null +++ b/third_party/rust/naga/tests/out/quad.Vertex.glsl @@ -0,0 +1,22 @@ +#version 310 es + +precision highp float; + +struct VertexOutput { + vec2 uv; + vec4 position; +}; + +layout(location = 0) in vec2 _p2vs_location0; +layout(location = 1) in vec2 _p2vs_location1; +smooth layout(location = 0) out vec2 _vs2fs_location0; + +void main() { + vec2 pos = _p2vs_location0; + vec2 uv1 = _p2vs_location1; + VertexOutput _tmp_return = VertexOutput(uv1, vec4((1.2 * pos), 0.0, 1.0)); + _vs2fs_location0 = _tmp_return.uv; + gl_Position = _tmp_return.position; + return; +} + diff --git a/third_party/rust/naga/tests/out/quad.dot b/third_party/rust/naga/tests/out/quad.dot new file mode 100644 index 000000000000..60587fc8b17d --- /dev/null +++ b/third_party/rust/naga/tests/out/quad.dot @@ -0,0 +1,85 @@ +digraph Module { + subgraph cluster_globals { + label="Globals" + g0 [ shape=hexagon label="[1] Handle/'u_texture'" ] + g1 [ shape=hexagon label="[2] Handle/'u_sampler'" ] + } + subgraph cluster_ep0 { + label="Vertex/'main'" + node [ style=filled ] + ep0_e0 [ fillcolor="#ffffb3" label="[1] Constant" ] + ep0_e1 [ color="#8dd3c7" label="[2] Argument[0]" ] + ep0_e2 [ color="#8dd3c7" label="[3] Argument[1]" ] + ep0_e3 [ color="#fdb462" label="[4] Multiply" ] + ep0_e1 -> ep0_e3 [ label="right" ] + ep0_e0 -> ep0_e3 [ label="left" ] + ep0_e4 [ fillcolor="#ffffb3" label="[5] Constant" ] + ep0_e5 [ fillcolor="#ffffb3" label="[6] Constant" ] + ep0_e6 [ color="#bebada" label="[7] Compose" ] + { ep0_e3 ep0_e4 ep0_e5 } -> ep0_e6 + ep0_e7 [ color="#bebada" label="[8] Compose" ] + { ep0_e2 ep0_e6 } -> ep0_e7 + ep0_s0 [ shape=square label="Root" ] + ep0_s1 [ shape=square label="Emit" ] + ep0_s2 [ shape=square label="Emit" ] + ep0_s3 [ shape=square label="Return" ] + ep0_s0 -> ep0_s1 [ arrowhead=tee label="" ] + ep0_s1 -> ep0_s2 [ arrowhead=tee label="" ] + ep0_s2 -> ep0_s3 [ arrowhead=tee label="" ] + ep0_e7 -> ep0_s3 [ label="value" ] + ep0_s1 -> ep0_e3 [ style=dotted ] + ep0_s2 -> ep0_e6 [ style=dotted ] + ep0_s2 -> ep0_e7 [ style=dotted ] + } + subgraph cluster_ep1 { + label="Fragment/'main'" + node [ style=filled ] + ep1_e0 [ fillcolor="#ffffb3" label="[1] Constant" ] + ep1_e1 [ fillcolor="#ffffb3" label="[2] Global" ] + g1 -> ep1_e1 [fillcolor=gray] + ep1_e2 [ fillcolor="#ffffb3" label="[3] Global" ] + g0 -> ep1_e2 [fillcolor=gray] + ep1_e3 [ color="#8dd3c7" label="[4] Argument[0]" ] + ep1_e4 [ color="#80b1d3" label="[5] ImageSample" ] + ep1_e1 -> ep1_e4 [ label="sampler" ] + ep1_e2 -> ep1_e4 [ label="image" ] + ep1_e3 -> ep1_e4 [ label="coordinate" ] + ep1_e5 [ color="#8dd3c7" label="[6] AccessIndex[3]" ] + ep1_e4 -> ep1_e5 [ label="base" ] + ep1_e6 [ fillcolor="#ffffb3" label="[7] Constant" ] + ep1_e7 [ color="#fdb462" label="[8] Equal" ] + ep1_e6 -> ep1_e7 [ label="right" ] + ep1_e5 -> ep1_e7 [ label="left" ] + ep1_e8 [ color="#8dd3c7" label="[9] AccessIndex[3]" ] + ep1_e4 -> ep1_e8 [ label="base" ] + ep1_e9 [ color="#fdb462" label="[10] Multiply" ] + ep1_e4 -> ep1_e9 [ label="right" ] + ep1_e8 -> ep1_e9 [ label="left" ] + ep1_s0 [ shape=square label="Root" ] + ep1_s1 [ shape=square label="Emit" ] + ep1_s2 [ shape=square label="Emit" ] + ep1_s3 [ shape=square label="Emit" ] + ep1_s4 [ shape=square label="If" ] + ep1_s5 [ shape=square label="Node" ] + ep1_s6 [ shape=square label="Kill" ] + ep1_s7 [ shape=square label="Node" ] + ep1_s8 [ shape=square label="Emit" ] + ep1_s9 [ shape=square label="Return" ] + ep1_s0 -> ep1_s1 [ arrowhead=tee label="" ] + ep1_s1 -> ep1_s2 [ arrowhead=tee label="" ] + ep1_s2 -> ep1_s3 [ arrowhead=tee label="" ] + ep1_s3 -> ep1_s4 [ arrowhead=tee label="" ] + ep1_s5 -> ep1_s6 [ arrowhead=tee label="" ] + ep1_s4 -> ep1_s5 [ arrowhead=tee label="accept" ] + ep1_s4 -> ep1_s7 [ arrowhead=tee label="reject" ] + ep1_s7 -> ep1_s8 [ arrowhead=tee label="" ] + ep1_s8 -> ep1_s9 [ arrowhead=tee label="" ] + ep1_e7 -> ep1_s4 [ label="condition" ] + ep1_e9 -> ep1_s9 [ label="value" ] + ep1_s1 -> ep1_e4 [ style=dotted ] + ep1_s2 -> ep1_e5 [ style=dotted ] + ep1_s3 -> ep1_e7 [ style=dotted ] + ep1_s8 -> ep1_e8 [ style=dotted ] + ep1_s8 -> ep1_e9 [ style=dotted ] + } +} diff --git a/third_party/rust/naga/tests/out/quad.msl b/third_party/rust/naga/tests/out/quad.msl new file mode 100644 index 000000000000..47238348b3f2 --- /dev/null +++ b/third_party/rust/naga/tests/out/quad.msl @@ -0,0 +1,45 @@ +#include +#include + +constexpr constant float c_scale = 1.2; +struct VertexOutput { + metal::float2 uv; + metal::float4 position; +}; + +struct main1Input { + metal::float2 pos [[attribute(0)]]; + metal::float2 uv1 [[attribute(1)]]; +}; +struct main1Output { + metal::float2 uv [[user(loc0), center_perspective]]; + metal::float4 position [[position]]; +}; +vertex main1Output main1( + main1Input varyings [[stage_in]] +) { + const auto pos = varyings.pos; + const auto uv1 = varyings.uv1; + const auto _tmp = VertexOutput {uv1, metal::float4(c_scale * pos, 0.0, 1.0)}; + return main1Output { _tmp.uv, _tmp.position }; +} + + +struct main2Input { + metal::float2 uv2 [[user(loc0), center_perspective]]; +}; +struct main2Output { + metal::float4 member1 [[color(0)]]; +}; +fragment main2Output main2( + main2Input varyings1 [[stage_in]] +, metal::texture2d u_texture [[user(fake0)]] +, metal::sampler u_sampler [[user(fake0)]] +) { + const auto uv2 = varyings1.uv2; + metal::float4 _e4 = u_texture.sample(u_sampler, uv2); + if (_e4.w == 0.0) { + metal::discard_fragment(); + } + return main2Output { _e4.w * _e4 }; +} diff --git a/third_party/rust/naga/tests/out/quad.spvasm b/third_party/rust/naga/tests/out/quad.spvasm new file mode 100644 index 000000000000..02c31384b201 --- /dev/null +++ b/third_party/rust/naga/tests/out/quad.spvasm @@ -0,0 +1,105 @@ +; SPIR-V +; Version: 1.0 +; Generator: rspirv +; Bound: 58 +OpCapability Shader +%1 = OpExtInstImport "GLSL.std.450" +OpMemoryModel Logical GLSL450 +OpEntryPoint Vertex %26 "main" %17 %20 %22 %24 +OpEntryPoint Fragment %44 "main" %41 %43 +OpExecutionMode %44 OriginUpperLeft +OpSource GLSL 450 +OpName %3 "c_scale" +OpName %9 "VertexOutput" +OpMemberName %9 0 "uv" +OpMemberName %9 1 "position" +OpName %12 "u_texture" +OpName %14 "u_sampler" +OpName %17 "pos" +OpName %20 "uv" +OpName %22 "uv" +OpName %24 "position" +OpName %26 "main" +OpName %41 "uv" +OpName %44 "main" +OpMemberDecorate %9 0 Offset 0 +OpMemberDecorate %9 1 Offset 16 +OpDecorate %12 DescriptorSet 0 +OpDecorate %12 Binding 0 +OpDecorate %14 DescriptorSet 0 +OpDecorate %14 Binding 1 +OpDecorate %17 Location 0 +OpDecorate %20 Location 1 +OpDecorate %22 Location 0 +OpDecorate %24 BuiltIn Position +OpDecorate %41 Location 0 +OpDecorate %43 Location 0 +%2 = OpTypeVoid +%4 = OpTypeFloat 32 +%3 = OpConstant %4 1.2 +%5 = OpConstant %4 0.0 +%6 = OpConstant %4 1.0 +%7 = OpTypeVector %4 2 +%8 = OpTypeVector %4 4 +%9 = OpTypeStruct %7 %8 +%10 = OpTypeImage %4 2D 0 0 0 1 Unknown +%11 = OpTypeSampler +%13 = OpTypePointer UniformConstant %10 +%12 = OpVariable %13 UniformConstant +%15 = OpTypePointer UniformConstant %11 +%14 = OpVariable %15 UniformConstant +%18 = OpTypePointer Input %7 +%17 = OpVariable %18 Input +%20 = OpVariable %18 Input +%23 = OpTypePointer Output %7 +%22 = OpVariable %23 Output +%25 = OpTypePointer Output %8 +%24 = OpVariable %25 Output +%27 = OpTypeFunction %2 +%35 = OpTypePointer Output %4 +%37 = OpTypeInt 32 0 +%36 = OpConstant %37 1 +%41 = OpVariable %18 Input +%43 = OpVariable %25 Output +%48 = OpTypeSampledImage %10 +%52 = OpTypeBool +%26 = OpFunction %2 None %27 +%16 = OpLabel +%19 = OpLoad %7 %17 +%21 = OpLoad %7 %20 +OpBranch %28 +%28 = OpLabel +%29 = OpVectorTimesScalar %7 %19 %3 +%30 = OpCompositeConstruct %8 %29 %5 %6 +%31 = OpCompositeConstruct %9 %21 %30 +%32 = OpCompositeExtract %7 %31 0 +OpStore %22 %32 +%33 = OpCompositeExtract %8 %31 1 +OpStore %24 %33 +%34 = OpAccessChain %35 %24 %36 +%38 = OpLoad %4 %34 +%39 = OpFNegate %4 %38 +OpStore %34 %39 +OpReturn +OpFunctionEnd +%44 = OpFunction %2 None %27 +%40 = OpLabel +%42 = OpLoad %7 %41 +%45 = OpLoad %10 %12 +%46 = OpLoad %11 %14 +OpBranch %47 +%47 = OpLabel +%49 = OpSampledImage %48 %45 %46 +%50 = OpImageSampleImplicitLod %8 %49 %42 +%51 = OpCompositeExtract %4 %50 3 +%53 = OpFOrdEqual %52 %51 %5 +OpSelectionMerge %54 None +OpBranchConditional %53 %55 %54 +%55 = OpLabel +OpKill +%54 = OpLabel +%56 = OpCompositeExtract %4 %50 3 +%57 = OpVectorTimesScalar %8 %50 %56 +OpStore %43 %57 +OpReturn +OpFunctionEnd \ No newline at end of file diff --git a/third_party/rust/naga/tests/out/quad.wgsl b/third_party/rust/naga/tests/out/quad.wgsl new file mode 100644 index 000000000000..fe924dc76507 --- /dev/null +++ b/third_party/rust/naga/tests/out/quad.wgsl @@ -0,0 +1,25 @@ +struct VertexOutput { + [[location(0), interpolate(perspective)]] uv: vec2; + [[builtin(position)]] position: vec4; +}; + +let c_scale: f32 = 1.2; + +[[group(0), binding(0)]] +var u_texture: texture_2d; +[[group(0), binding(1)]] +var u_sampler: sampler; + +[[stage(vertex)]] +fn main([[location(0)]] pos: vec2, [[location(1)]] uv1: vec2) -> VertexOutput { + return VertexOutput(uv1, vec4(c_scale * pos, 0.0, 1.0)); +} + +[[stage(fragment)]] +fn main1([[location(0), interpolate(perspective)]] uv2: vec2) -> [[location(0)]] vec4 { + let _e4: vec4 = textureSample(u_texture, u_sampler, uv2); + if (_e4[3] == 0.0) { + discard; + } + return _e4[3] * _e4; +} diff --git a/third_party/rust/naga/tests/out/shadow.Fragment.glsl b/third_party/rust/naga/tests/out/shadow.Fragment.glsl new file mode 100644 index 000000000000..e1310ca34f17 --- /dev/null +++ b/third_party/rust/naga/tests/out/shadow.Fragment.glsl @@ -0,0 +1,50 @@ +#version 310 es + +precision highp float; + +struct Light { + mat4x4 proj; + vec4 pos; + vec4 color; +}; + +uniform Globals_block_0 { + uvec4 num_lights; +} _group_0_binding_0; + +readonly buffer Lights_block_1 { + Light data[]; +} _group_0_binding_1; + +uniform highp sampler2DArrayShadow _group_0_binding_2; + +smooth layout(location = 0) in vec3 _vs2fs_location0; +smooth layout(location = 1) in vec4 _vs2fs_location1; +layout(location = 0) out vec4 _fs2p_location0; + +float fetch_shadow(uint light_id, vec4 homogeneous_coords) { + if((homogeneous_coords[3] <= 0.0)) { + return 1.0; + } + float _expr26 = textureGrad(_group_0_binding_2, vec4((((homogeneous_coords.xy * vec2(0.5, -0.5)) / vec2(homogeneous_coords[3])) + vec2(0.5, 0.5)), int(light_id), (homogeneous_coords[2] / homogeneous_coords[3])), vec2(0, 0), vec2(0,0)); + return _expr26; +} + +void main() { + vec3 raw_normal = _vs2fs_location0; + vec4 position = _vs2fs_location1; + vec3 color1 = vec3(0.05, 0.05, 0.05); + uint i = 0u; + while(true) { + if((i >= min(_group_0_binding_0.num_lights[0], 10u))) { + break; + } + Light _expr21 = _group_0_binding_1.data[i]; + float _expr25 = fetch_shadow(i, (_expr21.proj * position)); + color1 = (color1 + ((_expr25 * max(0.0, dot(normalize(raw_normal), normalize((_expr21.pos.xyz - position.xyz))))) * _expr21.color.xyz)); + i = (i + 1u); + } + _fs2p_location0 = vec4(color1, 1.0); + return; +} + diff --git a/third_party/rust/naga/tests/out/shadow.info.ron b/third_party/rust/naga/tests/out/shadow.info.ron new file mode 100644 index 000000000000..6d59c22e6885 --- /dev/null +++ b/third_party/rust/naga/tests/out/shadow.info.ron @@ -0,0 +1,2876 @@ +( + functions: [ + ( + flags: ( + bits: 31, + ), + available_stages: ( + bits: 7, + ), + uniformity: ( + non_uniform_result: Some(48), + requirements: ( + bits: 0, + ), + ), + may_kill: false, + sampling_set: [ + ( + image: 1, + sampler: 2, + ), + ], + global_uses: [ + ( + bits: 1, + ), + ( + bits: 1, + ), + ( + bits: 0, + ), + ( + bits: 0, + ), + ( + bits: 0, + ), + ( + bits: 0, + ), + ( + bits: 0, + ), + ], + expressions: [ + ( + uniformity: ( + non_uniform_result: None, + requirements: ( + bits: 0, + ), + ), + ref_count: 0, + assignable_global: Some(3), + ty: Value(Pointer( + base: 14, + class: Uniform, + )), + ), + ( + uniformity: ( + non_uniform_result: Some(2), + requirements: ( + bits: 0, + ), + ), + ref_count: 0, + assignable_global: Some(6), + ty: Value(Pointer( + base: 2, + class: Private, + )), + ), + ( + uniformity: ( + non_uniform_result: Some(3), + requirements: ( + bits: 0, + ), + ), + ref_count: 0, + assignable_global: Some(5), + ty: Value(Pointer( + base: 4, + class: Private, + )), + ), + ( + uniformity: ( + non_uniform_result: None, + requirements: ( + bits: 0, + ), + ), + ref_count: 1, + assignable_global: Some(1), + ty: Handle(56), + ), + ( + uniformity: ( + non_uniform_result: None, + requirements: ( + bits: 0, + ), + ), + ref_count: 1, + assignable_global: Some(2), + ty: Handle(57), + ), + ( + uniformity: ( + non_uniform_result: None, + requirements: ( + bits: 0, + ), + ), + ref_count: 0, + assignable_global: Some(4), + ty: Value(Pointer( + base: 21, + class: Storage, + )), + ), + ( + uniformity: ( + non_uniform_result: Some(7), + requirements: ( + bits: 0, + ), + ), + ref_count: 0, + assignable_global: Some(7), + ty: Value(Pointer( + base: 4, + class: Private, + )), + ), + ( + uniformity: ( + non_uniform_result: None, + requirements: ( + bits: 0, + ), + ), + ref_count: 0, + assignable_global: None, + ty: Value(Scalar( + kind: Sint, + width: 4, + )), + ), + ( + uniformity: ( + non_uniform_result: None, + requirements: ( + bits: 0, + ), + ), + ref_count: 0, + assignable_global: None, + ty: Value(Scalar( + kind: Sint, + width: 4, + )), + ), + ( + uniformity: ( + non_uniform_result: None, + requirements: ( + bits: 0, + ), + ), + ref_count: 0, + assignable_global: None, + ty: Value(Scalar( + kind: Sint, + width: 4, + )), + ), + ( + uniformity: ( + non_uniform_result: None, + requirements: ( + bits: 0, + ), + ), + ref_count: 0, + assignable_global: None, + ty: Value(Scalar( + kind: Sint, + width: 4, + )), + ), + ( + uniformity: ( + non_uniform_result: None, + requirements: ( + bits: 0, + ), + ), + ref_count: 0, + assignable_global: None, + ty: Value(Scalar( + kind: Sint, + width: 4, + )), + ), + ( + uniformity: ( + non_uniform_result: None, + requirements: ( + bits: 0, + ), + ), + ref_count: 2, + assignable_global: None, + ty: Value(Scalar( + kind: Float, + width: 4, + )), + ), + ( + uniformity: ( + non_uniform_result: None, + requirements: ( + bits: 0, + ), + ), + ref_count: 0, + assignable_global: None, + ty: Value(Scalar( + kind: Sint, + width: 4, + )), + ), + ( + uniformity: ( + non_uniform_result: None, + requirements: ( + bits: 0, + ), + ), + ref_count: 0, + assignable_global: None, + ty: Value(Scalar( + kind: Sint, + width: 4, + )), + ), + ( + uniformity: ( + non_uniform_result: None, + requirements: ( + bits: 0, + ), + ), + ref_count: 0, + assignable_global: None, + ty: Value(Scalar( + kind: Sint, + width: 4, + )), + ), + ( + uniformity: ( + non_uniform_result: None, + requirements: ( + bits: 0, + ), + ), + ref_count: 0, + assignable_global: None, + ty: Value(Scalar( + kind: Sint, + width: 4, + )), + ), + ( + uniformity: ( + non_uniform_result: None, + requirements: ( + bits: 0, + ), + ), + ref_count: 0, + assignable_global: None, + ty: Value(Scalar( + kind: Sint, + width: 4, + )), + ), + ( + uniformity: ( + non_uniform_result: None, + requirements: ( + bits: 0, + ), + ), + ref_count: 0, + assignable_global: None, + ty: Value(Scalar( + kind: Sint, + width: 4, + )), + ), + ( + uniformity: ( + non_uniform_result: None, + requirements: ( + bits: 0, + ), + ), + ref_count: 0, + assignable_global: None, + ty: Value(Scalar( + kind: Uint, + width: 4, + )), + ), + ( + uniformity: ( + non_uniform_result: None, + requirements: ( + bits: 0, + ), + ), + ref_count: 0, + assignable_global: None, + ty: Value(Scalar( + kind: Sint, + width: 4, + )), + ), + ( + uniformity: ( + non_uniform_result: None, + requirements: ( + bits: 0, + ), + ), + ref_count: 1, + assignable_global: None, + ty: Value(Scalar( + kind: Float, + width: 4, + )), + ), + ( + uniformity: ( + non_uniform_result: None, + requirements: ( + bits: 0, + ), + ), + ref_count: 0, + assignable_global: None, + ty: Value(Scalar( + kind: Sint, + width: 4, + )), + ), + ( + uniformity: ( + non_uniform_result: None, + requirements: ( + bits: 0, + ), + ), + ref_count: 0, + assignable_global: None, + ty: Value(Scalar( + kind: Sint, + width: 4, + )), + ), + ( + uniformity: ( + non_uniform_result: None, + requirements: ( + bits: 0, + ), + ), + ref_count: 1, + assignable_global: None, + ty: Value(Scalar( + kind: Float, + width: 4, + )), + ), + ( + uniformity: ( + non_uniform_result: None, + requirements: ( + bits: 0, + ), + ), + ref_count: 0, + assignable_global: None, + ty: Value(Scalar( + kind: Sint, + width: 4, + )), + ), + ( + uniformity: ( + non_uniform_result: None, + requirements: ( + bits: 0, + ), + ), + ref_count: 0, + assignable_global: None, + ty: Value(Scalar( + kind: Sint, + width: 4, + )), + ), + ( + uniformity: ( + non_uniform_result: None, + requirements: ( + bits: 0, + ), + ), + ref_count: 0, + assignable_global: None, + ty: Value(Scalar( + kind: Sint, + width: 4, + )), + ), + ( + uniformity: ( + non_uniform_result: None, + requirements: ( + bits: 0, + ), + ), + ref_count: 0, + assignable_global: None, + ty: Value(Scalar( + kind: Sint, + width: 4, + )), + ), + ( + uniformity: ( + non_uniform_result: None, + requirements: ( + bits: 0, + ), + ), + ref_count: 0, + assignable_global: None, + ty: Value(Scalar( + kind: Sint, + width: 4, + )), + ), + ( + uniformity: ( + non_uniform_result: None, + requirements: ( + bits: 0, + ), + ), + ref_count: 0, + assignable_global: None, + ty: Value(Scalar( + kind: Uint, + width: 4, + )), + ), + ( + uniformity: ( + non_uniform_result: None, + requirements: ( + bits: 0, + ), + ), + ref_count: 0, + assignable_global: None, + ty: Value(Scalar( + kind: Uint, + width: 4, + )), + ), + ( + uniformity: ( + non_uniform_result: None, + requirements: ( + bits: 0, + ), + ), + ref_count: 0, + assignable_global: None, + ty: Value(Scalar( + kind: Float, + width: 4, + )), + ), + ( + uniformity: ( + non_uniform_result: None, + requirements: ( + bits: 0, + ), + ), + ref_count: 3, + assignable_global: None, + ty: Value(Scalar( + kind: Float, + width: 4, + )), + ), + ( + uniformity: ( + non_uniform_result: None, + requirements: ( + bits: 0, + ), + ), + ref_count: 0, + assignable_global: None, + ty: Value(Scalar( + kind: Sint, + width: 4, + )), + ), + ( + uniformity: ( + non_uniform_result: None, + requirements: ( + bits: 0, + ), + ), + ref_count: 0, + assignable_global: None, + ty: Value(Scalar( + kind: Sint, + width: 4, + )), + ), + ( + uniformity: ( + non_uniform_result: None, + requirements: ( + bits: 0, + ), + ), + ref_count: 0, + assignable_global: None, + ty: Value(Scalar( + kind: Sint, + width: 4, + )), + ), + ( + uniformity: ( + non_uniform_result: None, + requirements: ( + bits: 0, + ), + ), + ref_count: 0, + assignable_global: None, + ty: Value(Scalar( + kind: Sint, + width: 4, + )), + ), + ( + uniformity: ( + non_uniform_result: None, + requirements: ( + bits: 0, + ), + ), + ref_count: 0, + assignable_global: None, + ty: Value(Scalar( + kind: Sint, + width: 4, + )), + ), + ( + uniformity: ( + non_uniform_result: None, + requirements: ( + bits: 0, + ), + ), + ref_count: 0, + assignable_global: None, + ty: Value(Scalar( + kind: Sint, + width: 4, + )), + ), + ( + uniformity: ( + non_uniform_result: None, + requirements: ( + bits: 0, + ), + ), + ref_count: 0, + assignable_global: None, + ty: Value(Scalar( + kind: Sint, + width: 4, + )), + ), + ( + uniformity: ( + non_uniform_result: None, + requirements: ( + bits: 0, + ), + ), + ref_count: 0, + assignable_global: None, + ty: Value(Scalar( + kind: Sint, + width: 4, + )), + ), + ( + uniformity: ( + non_uniform_result: None, + requirements: ( + bits: 0, + ), + ), + ref_count: 0, + assignable_global: None, + ty: Value(Scalar( + kind: Sint, + width: 4, + )), + ), + ( + uniformity: ( + non_uniform_result: None, + requirements: ( + bits: 0, + ), + ), + ref_count: 0, + assignable_global: None, + ty: Value(Scalar( + kind: Sint, + width: 4, + )), + ), + ( + uniformity: ( + non_uniform_result: None, + requirements: ( + bits: 0, + ), + ), + ref_count: 0, + assignable_global: None, + ty: Handle(2), + ), + ( + uniformity: ( + non_uniform_result: None, + requirements: ( + bits: 0, + ), + ), + ref_count: 1, + assignable_global: None, + ty: Value(Scalar( + kind: Float, + width: 4, + )), + ), + ( + uniformity: ( + non_uniform_result: Some(47), + requirements: ( + bits: 0, + ), + ), + ref_count: 1, + assignable_global: None, + ty: Handle(3), + ), + ( + uniformity: ( + non_uniform_result: Some(48), + requirements: ( + bits: 0, + ), + ), + ref_count: 6, + assignable_global: None, + ty: Handle(4), + ), + ( + uniformity: ( + non_uniform_result: Some(48), + requirements: ( + bits: 0, + ), + ), + ref_count: 1, + assignable_global: None, + ty: Value(Scalar( + kind: Float, + width: 4, + )), + ), + ( + uniformity: ( + non_uniform_result: Some(48), + requirements: ( + bits: 0, + ), + ), + ref_count: 1, + assignable_global: None, + ty: Value(Scalar( + kind: Bool, + width: 1, + )), + ), + ( + uniformity: ( + non_uniform_result: Some(48), + requirements: ( + bits: 0, + ), + ), + ref_count: 1, + assignable_global: None, + ty: Value(Scalar( + kind: Float, + width: 4, + )), + ), + ( + uniformity: ( + non_uniform_result: Some(48), + requirements: ( + bits: 0, + ), + ), + ref_count: 1, + assignable_global: None, + ty: Value(Scalar( + kind: Float, + width: 4, + )), + ), + ( + uniformity: ( + non_uniform_result: Some(48), + requirements: ( + bits: 0, + ), + ), + ref_count: 1, + assignable_global: None, + ty: Handle(9), + ), + ( + uniformity: ( + non_uniform_result: None, + requirements: ( + bits: 0, + ), + ), + ref_count: 1, + assignable_global: None, + ty: Handle(9), + ), + ( + uniformity: ( + non_uniform_result: Some(48), + requirements: ( + bits: 0, + ), + ), + ref_count: 1, + assignable_global: None, + ty: Handle(9), + ), + ( + uniformity: ( + non_uniform_result: Some(48), + requirements: ( + bits: 0, + ), + ), + ref_count: 1, + assignable_global: None, + ty: Value(Scalar( + kind: Float, + width: 4, + )), + ), + ( + uniformity: ( + non_uniform_result: Some(48), + requirements: ( + bits: 0, + ), + ), + ref_count: 1, + assignable_global: None, + ty: Value(Scalar( + kind: Float, + width: 4, + )), + ), + ( + uniformity: ( + non_uniform_result: Some(48), + requirements: ( + bits: 0, + ), + ), + ref_count: 1, + assignable_global: None, + ty: Handle(9), + ), + ( + uniformity: ( + non_uniform_result: None, + requirements: ( + bits: 0, + ), + ), + ref_count: 1, + assignable_global: None, + ty: Value(Vector( + size: Bi, + kind: Float, + width: 4, + )), + ), + ( + uniformity: ( + non_uniform_result: Some(48), + requirements: ( + bits: 0, + ), + ), + ref_count: 2, + assignable_global: None, + ty: Handle(9), + ), + ( + uniformity: ( + non_uniform_result: Some(48), + requirements: ( + bits: 0, + ), + ), + ref_count: 1, + assignable_global: None, + ty: Value(Scalar( + kind: Float, + width: 4, + )), + ), + ( + uniformity: ( + non_uniform_result: Some(48), + requirements: ( + bits: 0, + ), + ), + ref_count: 1, + assignable_global: None, + ty: Value(Scalar( + kind: Float, + width: 4, + )), + ), + ( + uniformity: ( + non_uniform_result: Some(47), + requirements: ( + bits: 0, + ), + ), + ref_count: 1, + assignable_global: None, + ty: Value(Scalar( + kind: Sint, + width: 4, + )), + ), + ( + uniformity: ( + non_uniform_result: Some(47), + requirements: ( + bits: 0, + ), + ), + ref_count: 1, + assignable_global: None, + ty: Value(Scalar( + kind: Float, + width: 4, + )), + ), + ( + uniformity: ( + non_uniform_result: Some(48), + requirements: ( + bits: 0, + ), + ), + ref_count: 3, + assignable_global: None, + ty: Handle(2), + ), + ( + uniformity: ( + non_uniform_result: Some(48), + requirements: ( + bits: 0, + ), + ), + ref_count: 1, + assignable_global: None, + ty: Value(Scalar( + kind: Float, + width: 4, + )), + ), + ( + uniformity: ( + non_uniform_result: Some(48), + requirements: ( + bits: 0, + ), + ), + ref_count: 1, + assignable_global: None, + ty: Value(Scalar( + kind: Float, + width: 4, + )), + ), + ( + uniformity: ( + non_uniform_result: Some(48), + requirements: ( + bits: 0, + ), + ), + ref_count: 1, + assignable_global: None, + ty: Value(Scalar( + kind: Float, + width: 4, + )), + ), + ( + uniformity: ( + non_uniform_result: Some(48), + requirements: ( + bits: 0, + ), + ), + ref_count: 1, + assignable_global: None, + ty: Value(Scalar( + kind: Float, + width: 4, + )), + ), + ( + uniformity: ( + non_uniform_result: Some(48), + requirements: ( + bits: 0, + ), + ), + ref_count: 1, + assignable_global: None, + ty: Value(Scalar( + kind: Float, + width: 4, + )), + ), + ( + uniformity: ( + non_uniform_result: Some(48), + requirements: ( + bits: 0, + ), + ), + ref_count: 1, + assignable_global: None, + ty: Value(Scalar( + kind: Float, + width: 4, + )), + ), + ( + uniformity: ( + non_uniform_result: Some(48), + requirements: ( + bits: 0, + ), + ), + ref_count: 1, + assignable_global: None, + ty: Handle(6), + ), + ( + uniformity: ( + non_uniform_result: Some(48), + requirements: ( + bits: 0, + ), + ), + ref_count: 1, + assignable_global: None, + ty: Value(Scalar( + kind: Float, + width: 4, + )), + ), + ( + uniformity: ( + non_uniform_result: Some(48), + requirements: ( + bits: 0, + ), + ), + ref_count: 1, + assignable_global: None, + ty: Value(Scalar( + kind: Sint, + width: 4, + )), + ), + ( + uniformity: ( + non_uniform_result: Some(48), + requirements: ( + bits: 0, + ), + ), + ref_count: 1, + assignable_global: None, + ty: Value(Scalar( + kind: Float, + width: 4, + )), + ), + ], + ), + ( + flags: ( + bits: 31, + ), + available_stages: ( + bits: 7, + ), + uniformity: ( + non_uniform_result: Some(48), + requirements: ( + bits: 0, + ), + ), + may_kill: false, + sampling_set: [ + ( + image: 1, + sampler: 2, + ), + ], + global_uses: [ + ( + bits: 1, + ), + ( + bits: 1, + ), + ( + bits: 1, + ), + ( + bits: 1, + ), + ( + bits: 1, + ), + ( + bits: 1, + ), + ( + bits: 2, + ), + ], + expressions: [ + ( + uniformity: ( + non_uniform_result: None, + requirements: ( + bits: 0, + ), + ), + ref_count: 1, + assignable_global: Some(3), + ty: Value(Pointer( + base: 14, + class: Uniform, + )), + ), + ( + uniformity: ( + non_uniform_result: Some(2), + requirements: ( + bits: 0, + ), + ), + ref_count: 1, + assignable_global: Some(6), + ty: Value(Pointer( + base: 2, + class: Private, + )), + ), + ( + uniformity: ( + non_uniform_result: Some(3), + requirements: ( + bits: 0, + ), + ), + ref_count: 4, + assignable_global: Some(5), + ty: Value(Pointer( + base: 4, + class: Private, + )), + ), + ( + uniformity: ( + non_uniform_result: None, + requirements: ( + bits: 0, + ), + ), + ref_count: 0, + assignable_global: Some(1), + ty: Handle(56), + ), + ( + uniformity: ( + non_uniform_result: None, + requirements: ( + bits: 0, + ), + ), + ref_count: 0, + assignable_global: Some(2), + ty: Handle(57), + ), + ( + uniformity: ( + non_uniform_result: None, + requirements: ( + bits: 0, + ), + ), + ref_count: 7, + assignable_global: Some(4), + ty: Value(Pointer( + base: 21, + class: Storage, + )), + ), + ( + uniformity: ( + non_uniform_result: Some(7), + requirements: ( + bits: 0, + ), + ), + ref_count: 1, + assignable_global: Some(7), + ty: Value(Pointer( + base: 4, + class: Private, + )), + ), + ( + uniformity: ( + non_uniform_result: None, + requirements: ( + bits: 0, + ), + ), + ref_count: 0, + assignable_global: None, + ty: Value(Scalar( + kind: Sint, + width: 4, + )), + ), + ( + uniformity: ( + non_uniform_result: None, + requirements: ( + bits: 0, + ), + ), + ref_count: 0, + assignable_global: None, + ty: Value(Scalar( + kind: Sint, + width: 4, + )), + ), + ( + uniformity: ( + non_uniform_result: None, + requirements: ( + bits: 0, + ), + ), + ref_count: 0, + assignable_global: None, + ty: Value(Scalar( + kind: Sint, + width: 4, + )), + ), + ( + uniformity: ( + non_uniform_result: None, + requirements: ( + bits: 0, + ), + ), + ref_count: 0, + assignable_global: None, + ty: Value(Scalar( + kind: Sint, + width: 4, + )), + ), + ( + uniformity: ( + non_uniform_result: None, + requirements: ( + bits: 0, + ), + ), + ref_count: 0, + assignable_global: None, + ty: Value(Scalar( + kind: Sint, + width: 4, + )), + ), + ( + uniformity: ( + non_uniform_result: None, + requirements: ( + bits: 0, + ), + ), + ref_count: 0, + assignable_global: None, + ty: Value(Scalar( + kind: Float, + width: 4, + )), + ), + ( + uniformity: ( + non_uniform_result: None, + requirements: ( + bits: 0, + ), + ), + ref_count: 1, + assignable_global: None, + ty: Value(Scalar( + kind: Sint, + width: 4, + )), + ), + ( + uniformity: ( + non_uniform_result: None, + requirements: ( + bits: 0, + ), + ), + ref_count: 0, + assignable_global: None, + ty: Value(Scalar( + kind: Sint, + width: 4, + )), + ), + ( + uniformity: ( + non_uniform_result: None, + requirements: ( + bits: 0, + ), + ), + ref_count: 1, + assignable_global: None, + ty: Value(Scalar( + kind: Sint, + width: 4, + )), + ), + ( + uniformity: ( + non_uniform_result: None, + requirements: ( + bits: 0, + ), + ), + ref_count: 1, + assignable_global: None, + ty: Value(Scalar( + kind: Sint, + width: 4, + )), + ), + ( + uniformity: ( + non_uniform_result: None, + requirements: ( + bits: 0, + ), + ), + ref_count: 0, + assignable_global: None, + ty: Value(Scalar( + kind: Sint, + width: 4, + )), + ), + ( + uniformity: ( + non_uniform_result: None, + requirements: ( + bits: 0, + ), + ), + ref_count: 0, + assignable_global: None, + ty: Value(Scalar( + kind: Sint, + width: 4, + )), + ), + ( + uniformity: ( + non_uniform_result: None, + requirements: ( + bits: 0, + ), + ), + ref_count: 0, + assignable_global: None, + ty: Value(Scalar( + kind: Uint, + width: 4, + )), + ), + ( + uniformity: ( + non_uniform_result: None, + requirements: ( + bits: 0, + ), + ), + ref_count: 0, + assignable_global: None, + ty: Value(Scalar( + kind: Sint, + width: 4, + )), + ), + ( + uniformity: ( + non_uniform_result: None, + requirements: ( + bits: 0, + ), + ), + ref_count: 0, + assignable_global: None, + ty: Value(Scalar( + kind: Float, + width: 4, + )), + ), + ( + uniformity: ( + non_uniform_result: None, + requirements: ( + bits: 0, + ), + ), + ref_count: 1, + assignable_global: None, + ty: Value(Scalar( + kind: Sint, + width: 4, + )), + ), + ( + uniformity: ( + non_uniform_result: None, + requirements: ( + bits: 0, + ), + ), + ref_count: 0, + assignable_global: None, + ty: Value(Scalar( + kind: Sint, + width: 4, + )), + ), + ( + uniformity: ( + non_uniform_result: None, + requirements: ( + bits: 0, + ), + ), + ref_count: 0, + assignable_global: None, + ty: Value(Scalar( + kind: Float, + width: 4, + )), + ), + ( + uniformity: ( + non_uniform_result: None, + requirements: ( + bits: 0, + ), + ), + ref_count: 0, + assignable_global: None, + ty: Value(Scalar( + kind: Sint, + width: 4, + )), + ), + ( + uniformity: ( + non_uniform_result: None, + requirements: ( + bits: 0, + ), + ), + ref_count: 1, + assignable_global: None, + ty: Value(Scalar( + kind: Sint, + width: 4, + )), + ), + ( + uniformity: ( + non_uniform_result: None, + requirements: ( + bits: 0, + ), + ), + ref_count: 0, + assignable_global: None, + ty: Value(Scalar( + kind: Sint, + width: 4, + )), + ), + ( + uniformity: ( + non_uniform_result: None, + requirements: ( + bits: 0, + ), + ), + ref_count: 0, + assignable_global: None, + ty: Value(Scalar( + kind: Sint, + width: 4, + )), + ), + ( + uniformity: ( + non_uniform_result: None, + requirements: ( + bits: 0, + ), + ), + ref_count: 1, + assignable_global: None, + ty: Value(Scalar( + kind: Sint, + width: 4, + )), + ), + ( + uniformity: ( + non_uniform_result: None, + requirements: ( + bits: 0, + ), + ), + ref_count: 1, + assignable_global: None, + ty: Value(Scalar( + kind: Uint, + width: 4, + )), + ), + ( + uniformity: ( + non_uniform_result: None, + requirements: ( + bits: 0, + ), + ), + ref_count: 1, + assignable_global: None, + ty: Value(Scalar( + kind: Uint, + width: 4, + )), + ), + ( + uniformity: ( + non_uniform_result: None, + requirements: ( + bits: 0, + ), + ), + ref_count: 0, + assignable_global: None, + ty: Value(Scalar( + kind: Float, + width: 4, + )), + ), + ( + uniformity: ( + non_uniform_result: None, + requirements: ( + bits: 0, + ), + ), + ref_count: 1, + assignable_global: None, + ty: Value(Scalar( + kind: Float, + width: 4, + )), + ), + ( + uniformity: ( + non_uniform_result: None, + requirements: ( + bits: 0, + ), + ), + ref_count: 1, + assignable_global: None, + ty: Value(Scalar( + kind: Sint, + width: 4, + )), + ), + ( + uniformity: ( + non_uniform_result: None, + requirements: ( + bits: 0, + ), + ), + ref_count: 0, + assignable_global: None, + ty: Value(Scalar( + kind: Sint, + width: 4, + )), + ), + ( + uniformity: ( + non_uniform_result: None, + requirements: ( + bits: 0, + ), + ), + ref_count: 0, + assignable_global: None, + ty: Value(Scalar( + kind: Sint, + width: 4, + )), + ), + ( + uniformity: ( + non_uniform_result: None, + requirements: ( + bits: 0, + ), + ), + ref_count: 0, + assignable_global: None, + ty: Value(Scalar( + kind: Sint, + width: 4, + )), + ), + ( + uniformity: ( + non_uniform_result: None, + requirements: ( + bits: 0, + ), + ), + ref_count: 0, + assignable_global: None, + ty: Value(Scalar( + kind: Sint, + width: 4, + )), + ), + ( + uniformity: ( + non_uniform_result: None, + requirements: ( + bits: 0, + ), + ), + ref_count: 1, + assignable_global: None, + ty: Value(Scalar( + kind: Sint, + width: 4, + )), + ), + ( + uniformity: ( + non_uniform_result: None, + requirements: ( + bits: 0, + ), + ), + ref_count: 1, + assignable_global: None, + ty: Value(Scalar( + kind: Sint, + width: 4, + )), + ), + ( + uniformity: ( + non_uniform_result: None, + requirements: ( + bits: 0, + ), + ), + ref_count: 1, + assignable_global: None, + ty: Value(Scalar( + kind: Sint, + width: 4, + )), + ), + ( + uniformity: ( + non_uniform_result: None, + requirements: ( + bits: 0, + ), + ), + ref_count: 0, + assignable_global: None, + ty: Value(Scalar( + kind: Sint, + width: 4, + )), + ), + ( + uniformity: ( + non_uniform_result: None, + requirements: ( + bits: 0, + ), + ), + ref_count: 0, + assignable_global: None, + ty: Value(Scalar( + kind: Sint, + width: 4, + )), + ), + ( + uniformity: ( + non_uniform_result: None, + requirements: ( + bits: 0, + ), + ), + ref_count: 0, + assignable_global: None, + ty: Handle(2), + ), + ( + uniformity: ( + non_uniform_result: None, + requirements: ( + bits: 0, + ), + ), + ref_count: 1, + assignable_global: None, + ty: Value(Scalar( + kind: Float, + width: 4, + )), + ), + ( + uniformity: ( + non_uniform_result: Some(47), + requirements: ( + bits: 0, + ), + ), + ref_count: 3, + assignable_global: None, + ty: Value(Pointer( + base: 2, + class: Function, + )), + ), + ( + uniformity: ( + non_uniform_result: Some(48), + requirements: ( + bits: 0, + ), + ), + ref_count: 11, + assignable_global: None, + ty: Value(Pointer( + base: 3, + class: Function, + )), + ), + ( + uniformity: ( + non_uniform_result: Some(48), + requirements: ( + bits: 0, + ), + ), + ref_count: 1, + assignable_global: None, + ty: Handle(3), + ), + ( + uniformity: ( + non_uniform_result: None, + requirements: ( + bits: 0, + ), + ), + ref_count: 1, + assignable_global: Some(3), + ty: Value(Pointer( + base: 13, + class: Uniform, + )), + ), + ( + uniformity: ( + non_uniform_result: None, + requirements: ( + bits: 0, + ), + ), + ref_count: 1, + assignable_global: Some(3), + ty: Value(ValuePointer( + size: None, + kind: Uint, + width: 4, + class: Uniform, + )), + ), + ( + uniformity: ( + non_uniform_result: None, + requirements: ( + bits: 0, + ), + ), + ref_count: 1, + assignable_global: None, + ty: Value(Scalar( + kind: Uint, + width: 4, + )), + ), + ( + uniformity: ( + non_uniform_result: None, + requirements: ( + bits: 0, + ), + ), + ref_count: 1, + assignable_global: None, + ty: Value(Scalar( + kind: Uint, + width: 4, + )), + ), + ( + uniformity: ( + non_uniform_result: Some(48), + requirements: ( + bits: 0, + ), + ), + ref_count: 1, + assignable_global: None, + ty: Value(Scalar( + kind: Bool, + width: 1, + )), + ), + ( + uniformity: ( + non_uniform_result: Some(47), + requirements: ( + bits: 0, + ), + ), + ref_count: 1, + assignable_global: None, + ty: Handle(2), + ), + ( + uniformity: ( + non_uniform_result: Some(48), + requirements: ( + bits: 0, + ), + ), + ref_count: 1, + assignable_global: None, + ty: Handle(3), + ), + ( + uniformity: ( + non_uniform_result: None, + requirements: ( + bits: 0, + ), + ), + ref_count: 1, + assignable_global: Some(4), + ty: Value(Pointer( + base: 20, + class: Storage, + )), + ), + ( + uniformity: ( + non_uniform_result: Some(48), + requirements: ( + bits: 0, + ), + ), + ref_count: 1, + assignable_global: None, + ty: Handle(3), + ), + ( + uniformity: ( + non_uniform_result: Some(48), + requirements: ( + bits: 0, + ), + ), + ref_count: 1, + assignable_global: Some(4), + ty: Value(Pointer( + base: 19, + class: Storage, + )), + ), + ( + uniformity: ( + non_uniform_result: Some(48), + requirements: ( + bits: 0, + ), + ), + ref_count: 1, + assignable_global: Some(4), + ty: Value(Pointer( + base: 18, + class: Storage, + )), + ), + ( + uniformity: ( + non_uniform_result: Some(48), + requirements: ( + bits: 0, + ), + ), + ref_count: 1, + assignable_global: None, + ty: Handle(18), + ), + ( + uniformity: ( + non_uniform_result: Some(3), + requirements: ( + bits: 0, + ), + ), + ref_count: 1, + assignable_global: None, + ty: Handle(4), + ), + ( + uniformity: ( + non_uniform_result: Some(48), + requirements: ( + bits: 0, + ), + ), + ref_count: 1, + assignable_global: None, + ty: Value(Vector( + size: Quad, + kind: Float, + width: 4, + )), + ), + ( + uniformity: ( + non_uniform_result: Some(48), + requirements: ( + bits: 0, + ), + ), + ref_count: 1, + assignable_global: None, + ty: Handle(1), + ), + ( + uniformity: ( + non_uniform_result: Some(2), + requirements: ( + bits: 0, + ), + ), + ref_count: 1, + assignable_global: None, + ty: Handle(2), + ), + ( + uniformity: ( + non_uniform_result: Some(2), + requirements: ( + bits: 0, + ), + ), + ref_count: 1, + assignable_global: None, + ty: Handle(2), + ), + ( + uniformity: ( + non_uniform_result: None, + requirements: ( + bits: 0, + ), + ), + ref_count: 1, + assignable_global: Some(4), + ty: Value(Pointer( + base: 20, + class: Storage, + )), + ), + ( + uniformity: ( + non_uniform_result: Some(48), + requirements: ( + bits: 0, + ), + ), + ref_count: 1, + assignable_global: None, + ty: Handle(3), + ), + ( + uniformity: ( + non_uniform_result: Some(48), + requirements: ( + bits: 0, + ), + ), + ref_count: 1, + assignable_global: Some(4), + ty: Value(Pointer( + base: 19, + class: Storage, + )), + ), + ( + uniformity: ( + non_uniform_result: Some(48), + requirements: ( + bits: 0, + ), + ), + ref_count: 1, + assignable_global: Some(4), + ty: Value(Pointer( + base: 4, + class: Storage, + )), + ), + ( + uniformity: ( + non_uniform_result: Some(48), + requirements: ( + bits: 0, + ), + ), + ref_count: 1, + assignable_global: Some(4), + ty: Value(ValuePointer( + size: None, + kind: Float, + width: 4, + class: Storage, + )), + ), + ( + uniformity: ( + non_uniform_result: Some(48), + requirements: ( + bits: 0, + ), + ), + ref_count: 1, + assignable_global: None, + ty: Value(Scalar( + kind: Float, + width: 4, + )), + ), + ( + uniformity: ( + non_uniform_result: None, + requirements: ( + bits: 0, + ), + ), + ref_count: 1, + assignable_global: Some(4), + ty: Value(Pointer( + base: 20, + class: Storage, + )), + ), + ( + uniformity: ( + non_uniform_result: Some(48), + requirements: ( + bits: 0, + ), + ), + ref_count: 1, + assignable_global: None, + ty: Handle(3), + ), + ( + uniformity: ( + non_uniform_result: Some(48), + requirements: ( + bits: 0, + ), + ), + ref_count: 1, + assignable_global: Some(4), + ty: Value(Pointer( + base: 19, + class: Storage, + )), + ), + ( + uniformity: ( + non_uniform_result: Some(48), + requirements: ( + bits: 0, + ), + ), + ref_count: 1, + assignable_global: Some(4), + ty: Value(Pointer( + base: 4, + class: Storage, + )), + ), + ( + uniformity: ( + non_uniform_result: Some(48), + requirements: ( + bits: 0, + ), + ), + ref_count: 1, + assignable_global: Some(4), + ty: Value(ValuePointer( + size: None, + kind: Float, + width: 4, + class: Storage, + )), + ), + ( + uniformity: ( + non_uniform_result: Some(48), + requirements: ( + bits: 0, + ), + ), + ref_count: 1, + assignable_global: None, + ty: Value(Scalar( + kind: Float, + width: 4, + )), + ), + ( + uniformity: ( + non_uniform_result: None, + requirements: ( + bits: 0, + ), + ), + ref_count: 1, + assignable_global: Some(4), + ty: Value(Pointer( + base: 20, + class: Storage, + )), + ), + ( + uniformity: ( + non_uniform_result: Some(48), + requirements: ( + bits: 0, + ), + ), + ref_count: 1, + assignable_global: None, + ty: Handle(3), + ), + ( + uniformity: ( + non_uniform_result: Some(48), + requirements: ( + bits: 0, + ), + ), + ref_count: 1, + assignable_global: Some(4), + ty: Value(Pointer( + base: 19, + class: Storage, + )), + ), + ( + uniformity: ( + non_uniform_result: Some(48), + requirements: ( + bits: 0, + ), + ), + ref_count: 1, + assignable_global: Some(4), + ty: Value(Pointer( + base: 4, + class: Storage, + )), + ), + ( + uniformity: ( + non_uniform_result: Some(48), + requirements: ( + bits: 0, + ), + ), + ref_count: 1, + assignable_global: Some(4), + ty: Value(ValuePointer( + size: None, + kind: Float, + width: 4, + class: Storage, + )), + ), + ( + uniformity: ( + non_uniform_result: Some(48), + requirements: ( + bits: 0, + ), + ), + ref_count: 1, + assignable_global: None, + ty: Value(Scalar( + kind: Float, + width: 4, + )), + ), + ( + uniformity: ( + non_uniform_result: Some(48), + requirements: ( + bits: 0, + ), + ), + ref_count: 1, + assignable_global: None, + ty: Handle(2), + ), + ( + uniformity: ( + non_uniform_result: Some(3), + requirements: ( + bits: 0, + ), + ), + ref_count: 1, + assignable_global: Some(5), + ty: Value(ValuePointer( + size: None, + kind: Float, + width: 4, + class: Private, + )), + ), + ( + uniformity: ( + non_uniform_result: Some(3), + requirements: ( + bits: 0, + ), + ), + ref_count: 1, + assignable_global: None, + ty: Value(Scalar( + kind: Float, + width: 4, + )), + ), + ( + uniformity: ( + non_uniform_result: Some(3), + requirements: ( + bits: 0, + ), + ), + ref_count: 1, + assignable_global: Some(5), + ty: Value(ValuePointer( + size: None, + kind: Float, + width: 4, + class: Private, + )), + ), + ( + uniformity: ( + non_uniform_result: Some(3), + requirements: ( + bits: 0, + ), + ), + ref_count: 1, + assignable_global: None, + ty: Value(Scalar( + kind: Float, + width: 4, + )), + ), + ( + uniformity: ( + non_uniform_result: Some(3), + requirements: ( + bits: 0, + ), + ), + ref_count: 1, + assignable_global: Some(5), + ty: Value(ValuePointer( + size: None, + kind: Float, + width: 4, + class: Private, + )), + ), + ( + uniformity: ( + non_uniform_result: Some(3), + requirements: ( + bits: 0, + ), + ), + ref_count: 1, + assignable_global: None, + ty: Value(Scalar( + kind: Float, + width: 4, + )), + ), + ( + uniformity: ( + non_uniform_result: Some(3), + requirements: ( + bits: 0, + ), + ), + ref_count: 1, + assignable_global: None, + ty: Handle(2), + ), + ( + uniformity: ( + non_uniform_result: Some(48), + requirements: ( + bits: 0, + ), + ), + ref_count: 1, + assignable_global: None, + ty: Handle(2), + ), + ( + uniformity: ( + non_uniform_result: Some(48), + requirements: ( + bits: 0, + ), + ), + ref_count: 1, + assignable_global: None, + ty: Handle(2), + ), + ( + uniformity: ( + non_uniform_result: Some(2), + requirements: ( + bits: 0, + ), + ), + ref_count: 1, + assignable_global: None, + ty: Value(Scalar( + kind: Float, + width: 4, + )), + ), + ( + uniformity: ( + non_uniform_result: Some(2), + requirements: ( + bits: 0, + ), + ), + ref_count: 1, + assignable_global: None, + ty: Value(Scalar( + kind: Float, + width: 4, + )), + ), + ( + uniformity: ( + non_uniform_result: Some(48), + requirements: ( + bits: 0, + ), + ), + ref_count: 1, + assignable_global: None, + ty: Value(Scalar( + kind: Float, + width: 4, + )), + ), + ( + uniformity: ( + non_uniform_result: None, + requirements: ( + bits: 0, + ), + ), + ref_count: 1, + assignable_global: Some(4), + ty: Value(Pointer( + base: 20, + class: Storage, + )), + ), + ( + uniformity: ( + non_uniform_result: Some(48), + requirements: ( + bits: 0, + ), + ), + ref_count: 1, + assignable_global: None, + ty: Handle(3), + ), + ( + uniformity: ( + non_uniform_result: Some(48), + requirements: ( + bits: 0, + ), + ), + ref_count: 1, + assignable_global: Some(4), + ty: Value(Pointer( + base: 19, + class: Storage, + )), + ), + ( + uniformity: ( + non_uniform_result: Some(48), + requirements: ( + bits: 0, + ), + ), + ref_count: 1, + assignable_global: Some(4), + ty: Value(Pointer( + base: 4, + class: Storage, + )), + ), + ( + uniformity: ( + non_uniform_result: Some(48), + requirements: ( + bits: 0, + ), + ), + ref_count: 1, + assignable_global: Some(4), + ty: Value(ValuePointer( + size: None, + kind: Float, + width: 4, + class: Storage, + )), + ), + ( + uniformity: ( + non_uniform_result: Some(48), + requirements: ( + bits: 0, + ), + ), + ref_count: 1, + assignable_global: None, + ty: Value(Scalar( + kind: Float, + width: 4, + )), + ), + ( + uniformity: ( + non_uniform_result: None, + requirements: ( + bits: 0, + ), + ), + ref_count: 1, + assignable_global: Some(4), + ty: Value(Pointer( + base: 20, + class: Storage, + )), + ), + ( + uniformity: ( + non_uniform_result: Some(48), + requirements: ( + bits: 0, + ), + ), + ref_count: 1, + assignable_global: None, + ty: Handle(3), + ), + ( + uniformity: ( + non_uniform_result: Some(48), + requirements: ( + bits: 0, + ), + ), + ref_count: 1, + assignable_global: Some(4), + ty: Value(Pointer( + base: 19, + class: Storage, + )), + ), + ( + uniformity: ( + non_uniform_result: Some(48), + requirements: ( + bits: 0, + ), + ), + ref_count: 1, + assignable_global: Some(4), + ty: Value(Pointer( + base: 4, + class: Storage, + )), + ), + ( + uniformity: ( + non_uniform_result: Some(48), + requirements: ( + bits: 0, + ), + ), + ref_count: 1, + assignable_global: Some(4), + ty: Value(ValuePointer( + size: None, + kind: Float, + width: 4, + class: Storage, + )), + ), + ( + uniformity: ( + non_uniform_result: Some(48), + requirements: ( + bits: 0, + ), + ), + ref_count: 1, + assignable_global: None, + ty: Value(Scalar( + kind: Float, + width: 4, + )), + ), + ( + uniformity: ( + non_uniform_result: None, + requirements: ( + bits: 0, + ), + ), + ref_count: 1, + assignable_global: Some(4), + ty: Value(Pointer( + base: 20, + class: Storage, + )), + ), + ( + uniformity: ( + non_uniform_result: Some(48), + requirements: ( + bits: 0, + ), + ), + ref_count: 1, + assignable_global: None, + ty: Handle(3), + ), + ( + uniformity: ( + non_uniform_result: Some(48), + requirements: ( + bits: 0, + ), + ), + ref_count: 1, + assignable_global: Some(4), + ty: Value(Pointer( + base: 19, + class: Storage, + )), + ), + ( + uniformity: ( + non_uniform_result: Some(48), + requirements: ( + bits: 0, + ), + ), + ref_count: 1, + assignable_global: Some(4), + ty: Value(Pointer( + base: 4, + class: Storage, + )), + ), + ( + uniformity: ( + non_uniform_result: Some(48), + requirements: ( + bits: 0, + ), + ), + ref_count: 1, + assignable_global: Some(4), + ty: Value(ValuePointer( + size: None, + kind: Float, + width: 4, + class: Storage, + )), + ), + ( + uniformity: ( + non_uniform_result: Some(48), + requirements: ( + bits: 0, + ), + ), + ref_count: 1, + assignable_global: None, + ty: Value(Scalar( + kind: Float, + width: 4, + )), + ), + ( + uniformity: ( + non_uniform_result: Some(48), + requirements: ( + bits: 0, + ), + ), + ref_count: 1, + assignable_global: None, + ty: Handle(2), + ), + ( + uniformity: ( + non_uniform_result: Some(48), + requirements: ( + bits: 0, + ), + ), + ref_count: 1, + assignable_global: None, + ty: Handle(2), + ), + ( + uniformity: ( + non_uniform_result: Some(47), + requirements: ( + bits: 0, + ), + ), + ref_count: 1, + assignable_global: None, + ty: Handle(2), + ), + ( + uniformity: ( + non_uniform_result: Some(48), + requirements: ( + bits: 0, + ), + ), + ref_count: 1, + assignable_global: None, + ty: Handle(3), + ), + ( + uniformity: ( + non_uniform_result: Some(48), + requirements: ( + bits: 0, + ), + ), + ref_count: 1, + assignable_global: None, + ty: Handle(3), + ), + ( + uniformity: ( + non_uniform_result: Some(47), + requirements: ( + bits: 0, + ), + ), + ref_count: 1, + assignable_global: None, + ty: Handle(2), + ), + ( + uniformity: ( + non_uniform_result: Some(47), + requirements: ( + bits: 0, + ), + ), + ref_count: 1, + assignable_global: None, + ty: Handle(4), + ), + ], + ), + ], + entry_points: [ + ( + flags: ( + bits: 31, + ), + available_stages: ( + bits: 7, + ), + uniformity: ( + non_uniform_result: Some(48), + requirements: ( + bits: 0, + ), + ), + may_kill: false, + sampling_set: [ + ( + image: 1, + sampler: 2, + ), + ], + global_uses: [ + ( + bits: 1, + ), + ( + bits: 1, + ), + ( + bits: 1, + ), + ( + bits: 1, + ), + ( + bits: 3, + ), + ( + bits: 3, + ), + ( + bits: 3, + ), + ], + expressions: [ + ( + uniformity: ( + non_uniform_result: Some(1), + requirements: ( + bits: 0, + ), + ), + ref_count: 1, + assignable_global: None, + ty: Handle(2), + ), + ( + uniformity: ( + non_uniform_result: Some(2), + requirements: ( + bits: 0, + ), + ), + ref_count: 1, + assignable_global: Some(6), + ty: Value(Pointer( + base: 2, + class: Private, + )), + ), + ( + uniformity: ( + non_uniform_result: Some(3), + requirements: ( + bits: 0, + ), + ), + ref_count: 1, + assignable_global: None, + ty: Handle(4), + ), + ( + uniformity: ( + non_uniform_result: Some(4), + requirements: ( + bits: 0, + ), + ), + ref_count: 1, + assignable_global: Some(5), + ty: Value(Pointer( + base: 4, + class: Private, + )), + ), + ( + uniformity: ( + non_uniform_result: Some(5), + requirements: ( + bits: 0, + ), + ), + ref_count: 1, + assignable_global: Some(7), + ty: Value(Pointer( + base: 4, + class: Private, + )), + ), + ( + uniformity: ( + non_uniform_result: Some(5), + requirements: ( + bits: 0, + ), + ), + ref_count: 1, + assignable_global: None, + ty: Handle(4), + ), + ], + ), + ], +) \ No newline at end of file diff --git a/third_party/rust/naga/tests/out/shadow.msl b/third_party/rust/naga/tests/out/shadow.msl new file mode 100644 index 000000000000..ed9b5bc55a13 --- /dev/null +++ b/third_party/rust/naga/tests/out/shadow.msl @@ -0,0 +1,68 @@ +#include +#include + +struct _mslBufferSizes { + metal::uint size1; +}; + +constexpr constant unsigned c_max_lights = 10u; +struct Globals { + metal::uint4 num_lights; +}; +struct Light { + metal::float4x4 proj; + metal::float4 pos; + metal::float4 color; +}; +typedef Light type3[1]; +struct Lights { + type3 data; +}; +constant metal::float3 c_ambient = {0.05, 0.05, 0.05}; + +float fetch_shadow( + metal::uint light_id, + metal::float4 homogeneous_coords, + metal::depth2d_array t_shadow, + metal::sampler sampler_shadow +) { + if (homogeneous_coords.w <= 0.0) { + return 1.0; + } + float _e26 = t_shadow.sample_compare(sampler_shadow, ((homogeneous_coords.xy * metal::float2(0.5, -0.5)) / metal::float2(homogeneous_coords.w)) + metal::float2(0.5, 0.5), static_cast(light_id), homogeneous_coords.z / homogeneous_coords.w); + return _e26; +} + +struct fs_mainInput { + metal::float3 raw_normal [[user(loc0), center_perspective]]; + metal::float4 position [[user(loc1), center_perspective]]; +}; +struct fs_mainOutput { + metal::float4 member [[color(0)]]; +}; +fragment fs_mainOutput fs_main( + fs_mainInput varyings [[stage_in]] +, constant Globals& u_globals [[user(fake0)]] +, constant Lights& s_lights [[user(fake0)]] +, metal::depth2d_array t_shadow [[user(fake0)]] +, metal::sampler sampler_shadow [[user(fake0)]] +) { + const auto raw_normal = varyings.raw_normal; + const auto position = varyings.position; + metal::float3 color1 = c_ambient; + metal::uint i = 0u; + bool loop_init = true; + while(true) { + if (!loop_init) { + i = i + 1u; + } + loop_init = false; + if (i >= metal::min(u_globals.num_lights.x, c_max_lights)) { + break; + } + Light _e21 = s_lights.data[i]; + float _e25 = fetch_shadow(i, _e21.proj * position, t_shadow, sampler_shadow); + color1 = color1 + ((_e25 * metal::max(0.0, metal::dot(metal::normalize(raw_normal), metal::normalize(_e21.pos.xyz - position.xyz)))) * _e21.color.xyz); + } + return fs_mainOutput { metal::float4(color1, 1.0) }; +} diff --git a/third_party/rust/naga/tests/out/shadow.ron b/third_party/rust/naga/tests/out/shadow.ron new file mode 100644 index 000000000000..6d55e1bd7c6c --- /dev/null +++ b/third_party/rust/naga/tests/out/shadow.ron @@ -0,0 +1,1580 @@ +( + types: [ + ( + name: None, + inner: Scalar( + kind: Float, + width: 4, + ), + ), + ( + name: None, + inner: Vector( + size: Tri, + kind: Float, + width: 4, + ), + ), + ( + name: None, + inner: Scalar( + kind: Uint, + width: 4, + ), + ), + ( + name: None, + inner: Vector( + size: Quad, + kind: Float, + width: 4, + ), + ), + ( + name: None, + inner: Scalar( + kind: Bool, + width: 1, + ), + ), + ( + name: None, + inner: Vector( + size: Bi, + kind: Float, + width: 4, + ), + ), + ( + name: None, + inner: Image( + dim: D2, + arrayed: true, + class: Sampled( + kind: Float, + multi: false, + ), + ), + ), + ( + name: None, + inner: Sampler( + comparison: false, + ), + ), + ( + name: None, + inner: Vector( + size: Bi, + kind: Float, + width: 4, + ), + ), + ( + name: None, + inner: Scalar( + kind: Sint, + width: 4, + ), + ), + ( + name: None, + inner: Pointer( + base: 2, + class: Function, + ), + ), + ( + name: None, + inner: Pointer( + base: 3, + class: Function, + ), + ), + ( + name: None, + inner: Vector( + size: Quad, + kind: Uint, + width: 4, + ), + ), + ( + name: Some("Globals"), + inner: Struct( + level: Root, + members: [ + ( + name: Some("num_lights"), + ty: 13, + binding: None, + offset: 0, + ), + ], + span: 16, + ), + ), + ( + name: None, + inner: Pointer( + base: 14, + class: Uniform, + ), + ), + ( + name: None, + inner: Pointer( + base: 13, + class: Uniform, + ), + ), + ( + name: None, + inner: Pointer( + base: 3, + class: Uniform, + ), + ), + ( + name: None, + inner: Matrix( + columns: Quad, + rows: Quad, + width: 4, + ), + ), + ( + name: Some("Light"), + inner: Struct( + level: Normal( + alignment: 16, + ), + members: [ + ( + name: Some("proj"), + ty: 18, + binding: None, + offset: 0, + ), + ( + name: Some("pos"), + ty: 4, + binding: None, + offset: 64, + ), + ( + name: Some("color"), + ty: 4, + binding: None, + offset: 80, + ), + ], + span: 96, + ), + ), + ( + name: None, + inner: Array( + base: 19, + size: Dynamic, + stride: 96, + ), + ), + ( + name: Some("Lights"), + inner: Struct( + level: Root, + members: [ + ( + name: Some("data"), + ty: 20, + binding: None, + offset: 0, + ), + ], + span: 96, + ), + ), + ( + name: None, + inner: Pointer( + base: 21, + class: Storage, + ), + ), + ( + name: None, + inner: Pointer( + base: 20, + class: Uniform, + ), + ), + ( + name: None, + inner: Pointer( + base: 19, + class: Uniform, + ), + ), + ( + name: None, + inner: Pointer( + base: 18, + class: Uniform, + ), + ), + ( + name: None, + inner: Pointer( + base: 4, + class: Private, + ), + ), + ( + name: None, + inner: Pointer( + base: 2, + class: Private, + ), + ), + ( + name: None, + inner: Pointer( + base: 20, + class: Uniform, + ), + ), + ( + name: None, + inner: Pointer( + base: 19, + class: Uniform, + ), + ), + ( + name: None, + inner: Pointer( + base: 4, + class: Uniform, + ), + ), + ( + name: None, + inner: Pointer( + base: 1, + class: Uniform, + ), + ), + ( + name: None, + inner: Pointer( + base: 20, + class: Uniform, + ), + ), + ( + name: None, + inner: Pointer( + base: 19, + class: Uniform, + ), + ), + ( + name: None, + inner: Pointer( + base: 4, + class: Uniform, + ), + ), + ( + name: None, + inner: Pointer( + base: 1, + class: Uniform, + ), + ), + ( + name: None, + inner: Pointer( + base: 20, + class: Uniform, + ), + ), + ( + name: None, + inner: Pointer( + base: 19, + class: Uniform, + ), + ), + ( + name: None, + inner: Pointer( + base: 4, + class: Uniform, + ), + ), + ( + name: None, + inner: Pointer( + base: 1, + class: Uniform, + ), + ), + ( + name: None, + inner: Pointer( + base: 1, + class: Private, + ), + ), + ( + name: None, + inner: Pointer( + base: 1, + class: Private, + ), + ), + ( + name: None, + inner: Pointer( + base: 1, + class: Private, + ), + ), + ( + name: None, + inner: Pointer( + base: 20, + class: Uniform, + ), + ), + ( + name: None, + inner: Pointer( + base: 19, + class: Uniform, + ), + ), + ( + name: None, + inner: Pointer( + base: 4, + class: Uniform, + ), + ), + ( + name: None, + inner: Pointer( + base: 1, + class: Uniform, + ), + ), + ( + name: None, + inner: Pointer( + base: 20, + class: Uniform, + ), + ), + ( + name: None, + inner: Pointer( + base: 19, + class: Uniform, + ), + ), + ( + name: None, + inner: Pointer( + base: 4, + class: Uniform, + ), + ), + ( + name: None, + inner: Pointer( + base: 1, + class: Uniform, + ), + ), + ( + name: None, + inner: Pointer( + base: 20, + class: Uniform, + ), + ), + ( + name: None, + inner: Pointer( + base: 19, + class: Uniform, + ), + ), + ( + name: None, + inner: Pointer( + base: 4, + class: Uniform, + ), + ), + ( + name: None, + inner: Pointer( + base: 1, + class: Uniform, + ), + ), + ( + name: None, + inner: Pointer( + base: 4, + class: Private, + ), + ), + ( + name: None, + inner: Image( + dim: D2, + arrayed: true, + class: Depth, + ), + ), + ( + name: None, + inner: Sampler( + comparison: true, + ), + ), + ], + constants: [ + ( + name: None, + specialization: None, + inner: Scalar( + width: 4, + value: Sint(0), + ), + ), + ( + name: None, + specialization: None, + inner: Scalar( + width: 4, + value: Sint(1), + ), + ), + ( + name: None, + specialization: None, + inner: Scalar( + width: 4, + value: Sint(2), + ), + ), + ( + name: None, + specialization: None, + inner: Scalar( + width: 4, + value: Sint(3), + ), + ), + ( + name: None, + specialization: None, + inner: Scalar( + width: 4, + value: Float(0), + ), + ), + ( + name: None, + specialization: None, + inner: Scalar( + width: 4, + value: Float(1), + ), + ), + ( + name: None, + specialization: None, + inner: Scalar( + width: 4, + value: Float(0.5), + ), + ), + ( + name: None, + specialization: None, + inner: Scalar( + width: 4, + value: Float(-0.5), + ), + ), + ( + name: None, + specialization: None, + inner: Scalar( + width: 4, + value: Float(0.05000000074505806), + ), + ), + ( + name: None, + specialization: None, + inner: Composite( + ty: 2, + components: [ + 9, + 9, + 9, + ], + ), + ), + ( + name: None, + specialization: None, + inner: Scalar( + width: 4, + value: Uint(10), + ), + ), + ( + name: None, + specialization: None, + inner: Scalar( + width: 4, + value: Uint(0), + ), + ), + ( + name: None, + specialization: None, + inner: Scalar( + width: 4, + value: Uint(1), + ), + ), + ( + name: None, + specialization: None, + inner: Scalar( + width: 4, + value: Float(0), + ), + ), + ( + name: None, + specialization: None, + inner: Scalar( + width: 4, + value: Sint(0), + ), + ), + ( + name: None, + specialization: None, + inner: Scalar( + width: 4, + value: Sint(0), + ), + ), + ( + name: None, + specialization: None, + inner: Scalar( + width: 4, + value: Sint(0), + ), + ), + ( + name: None, + specialization: None, + inner: Scalar( + width: 4, + value: Sint(0), + ), + ), + ( + name: None, + specialization: None, + inner: Scalar( + width: 4, + value: Sint(0), + ), + ), + ( + name: None, + specialization: None, + inner: Scalar( + width: 4, + value: Sint(1), + ), + ), + ( + name: None, + specialization: None, + inner: Scalar( + width: 4, + value: Sint(0), + ), + ), + ( + name: None, + specialization: None, + inner: Scalar( + width: 4, + value: Sint(0), + ), + ), + ( + name: None, + specialization: None, + inner: Scalar( + width: 4, + value: Sint(1), + ), + ), + ( + name: None, + specialization: None, + inner: Scalar( + width: 4, + value: Sint(1), + ), + ), + ( + name: None, + specialization: None, + inner: Scalar( + width: 4, + value: Sint(0), + ), + ), + ( + name: None, + specialization: None, + inner: Scalar( + width: 4, + value: Sint(1), + ), + ), + ( + name: None, + specialization: None, + inner: Scalar( + width: 4, + value: Sint(2), + ), + ), + ( + name: None, + specialization: None, + inner: Scalar( + width: 4, + value: Sint(0), + ), + ), + ( + name: None, + specialization: None, + inner: Scalar( + width: 4, + value: Sint(1), + ), + ), + ( + name: None, + specialization: None, + inner: Scalar( + width: 4, + value: Sint(2), + ), + ), + ( + name: None, + specialization: None, + inner: Scalar( + width: 4, + value: Sint(0), + ), + ), + ( + name: None, + specialization: None, + inner: Scalar( + width: 4, + value: Sint(2), + ), + ), + ( + name: None, + specialization: None, + inner: Scalar( + width: 4, + value: Sint(0), + ), + ), + ( + name: None, + specialization: None, + inner: Scalar( + width: 4, + value: Sint(0), + ), + ), + ( + name: None, + specialization: None, + inner: Scalar( + width: 4, + value: Sint(2), + ), + ), + ( + name: None, + specialization: None, + inner: Scalar( + width: 4, + value: Sint(1), + ), + ), + ( + name: None, + specialization: None, + inner: Scalar( + width: 4, + value: Sint(0), + ), + ), + ( + name: None, + specialization: None, + inner: Scalar( + width: 4, + value: Sint(2), + ), + ), + ( + name: None, + specialization: None, + inner: Scalar( + width: 4, + value: Sint(2), + ), + ), + ], + global_variables: [ + ( + name: Some("t_shadow"), + class: Handle, + binding: Some(( + group: 0, + binding: 2, + )), + ty: 56, + init: None, + storage_access: ( + bits: 0, + ), + ), + ( + name: Some("sampler_shadow"), + class: Handle, + binding: Some(( + group: 0, + binding: 3, + )), + ty: 57, + init: None, + storage_access: ( + bits: 0, + ), + ), + ( + name: Some("u_globals"), + class: Uniform, + binding: Some(( + group: 0, + binding: 0, + )), + ty: 14, + init: None, + storage_access: ( + bits: 0, + ), + ), + ( + name: Some("s_lights"), + class: Storage, + binding: Some(( + group: 0, + binding: 1, + )), + ty: 21, + init: None, + storage_access: ( + bits: 1, + ), + ), + ( + name: Some("in_position_fs"), + class: Private, + binding: None, + ty: 4, + init: None, + storage_access: ( + bits: 0, + ), + ), + ( + name: Some("in_normal_fs"), + class: Private, + binding: None, + ty: 2, + init: None, + storage_access: ( + bits: 0, + ), + ), + ( + name: Some("out_color_fs"), + class: Private, + binding: None, + ty: 4, + init: None, + storage_access: ( + bits: 0, + ), + ), + ], + functions: [ + ( + name: None, + arguments: [ + ( + name: None, + ty: 3, + binding: None, + ), + ( + name: None, + ty: 4, + binding: None, + ), + ], + result: Some(( + ty: 1, + binding: None, + )), + local_variables: [], + expressions: [ + GlobalVariable(3), + GlobalVariable(6), + GlobalVariable(5), + GlobalVariable(1), + GlobalVariable(2), + GlobalVariable(4), + GlobalVariable(7), + Constant(1), + Constant(2), + Constant(3), + Constant(4), + Constant(20), + Constant(7), + Constant(33), + Constant(31), + Constant(29), + Constant(27), + Constant(25), + Constant(15), + Constant(12), + Constant(23), + Constant(8), + Constant(36), + Constant(34), + Constant(14), + Constant(32), + Constant(30), + Constant(17), + Constant(26), + Constant(39), + Constant(13), + Constant(11), + Constant(9), + Constant(6), + Constant(21), + Constant(35), + Constant(19), + Constant(37), + Constant(18), + Constant(28), + Constant(16), + Constant(24), + Constant(38), + Constant(22), + Constant(10), + Constant(5), + FunctionArgument(0), + FunctionArgument(1), + AccessIndex( + base: 48, + index: 3, + ), + Binary( + op: LessEqual, + left: 49, + right: 46, + ), + AccessIndex( + base: 48, + index: 0, + ), + AccessIndex( + base: 48, + index: 1, + ), + Compose( + ty: 9, + components: [ + 51, + 52, + ], + ), + Compose( + ty: 9, + components: [ + 13, + 22, + ], + ), + Binary( + op: Multiply, + left: 53, + right: 54, + ), + AccessIndex( + base: 48, + index: 3, + ), + Binary( + op: Divide, + left: 34, + right: 56, + ), + Binary( + op: Multiply, + left: 55, + right: 57, + ), + Splat( + size: Bi, + value: 13, + ), + Binary( + op: Add, + left: 58, + right: 59, + ), + AccessIndex( + base: 60, + index: 0, + ), + AccessIndex( + base: 60, + index: 1, + ), + As( + expr: 47, + kind: Sint, + convert: None, + ), + As( + expr: 63, + kind: Float, + convert: Some(4), + ), + Compose( + ty: 2, + components: [ + 61, + 62, + 64, + ], + ), + AccessIndex( + base: 48, + index: 2, + ), + AccessIndex( + base: 48, + index: 3, + ), + Binary( + op: Divide, + left: 34, + right: 67, + ), + Binary( + op: Multiply, + left: 66, + right: 68, + ), + AccessIndex( + base: 65, + index: 0, + ), + AccessIndex( + base: 65, + index: 1, + ), + Compose( + ty: 6, + components: [ + 70, + 71, + ], + ), + AccessIndex( + base: 65, + index: 2, + ), + As( + expr: 73, + kind: Sint, + convert: Some(4), + ), + ImageSample( + image: 4, + sampler: 5, + coordinate: 72, + array_index: Some(74), + offset: None, + level: Exact(25), + depth_ref: Some(69), + ), + ], + body: [ + Emit(( + start: 48, + end: 50, + )), + If( + condition: 50, + accept: [ + Return( + value: Some(34), + ), + ], + reject: [], + ), + Emit(( + start: 50, + end: 75, + )), + Return( + value: Some(75), + ), + ], + ), + ( + name: Some("fs_main"), + arguments: [], + result: None, + local_variables: [ + ( + name: Some("color"), + ty: 2, + init: Some(10), + ), + ( + name: Some("i"), + ty: 3, + init: Some(12), + ), + ], + expressions: [ + GlobalVariable(3), + GlobalVariable(6), + GlobalVariable(5), + GlobalVariable(1), + GlobalVariable(2), + GlobalVariable(4), + GlobalVariable(7), + Constant(1), + Constant(2), + Constant(3), + Constant(4), + Constant(20), + Constant(7), + Constant(33), + Constant(31), + Constant(29), + Constant(27), + Constant(25), + Constant(15), + Constant(12), + Constant(23), + Constant(8), + Constant(36), + Constant(34), + Constant(14), + Constant(32), + Constant(30), + Constant(17), + Constant(26), + Constant(39), + Constant(13), + Constant(11), + Constant(9), + Constant(6), + Constant(21), + Constant(35), + Constant(19), + Constant(37), + Constant(18), + Constant(28), + Constant(16), + Constant(24), + Constant(38), + Constant(22), + Constant(10), + Constant(5), + LocalVariable(1), + LocalVariable(2), + Load( + pointer: 48, + ), + AccessIndex( + base: 1, + index: 0, + ), + Access( + base: 50, + index: 41, + ), + Load( + pointer: 51, + ), + Math( + fun: Min, + arg: 52, + arg1: Some(32), + arg2: None, + ), + Binary( + op: GreaterEqual, + left: 49, + right: 53, + ), + Load( + pointer: 47, + ), + Load( + pointer: 48, + ), + AccessIndex( + base: 6, + index: 0, + ), + Load( + pointer: 48, + ), + Access( + base: 57, + index: 58, + ), + AccessIndex( + base: 59, + index: 0, + ), + Load( + pointer: 60, + ), + Load( + pointer: 3, + ), + Binary( + op: Multiply, + left: 61, + right: 62, + ), + Call(1), + Load( + pointer: 2, + ), + Math( + fun: Normalize, + arg: 65, + arg1: None, + arg2: None, + ), + AccessIndex( + base: 6, + index: 0, + ), + Load( + pointer: 48, + ), + Access( + base: 67, + index: 68, + ), + AccessIndex( + base: 69, + index: 1, + ), + Access( + base: 70, + index: 35, + ), + Load( + pointer: 71, + ), + AccessIndex( + base: 6, + index: 0, + ), + Load( + pointer: 48, + ), + Access( + base: 73, + index: 74, + ), + AccessIndex( + base: 75, + index: 1, + ), + Access( + base: 76, + index: 42, + ), + Load( + pointer: 77, + ), + AccessIndex( + base: 6, + index: 0, + ), + Load( + pointer: 48, + ), + Access( + base: 79, + index: 80, + ), + AccessIndex( + base: 81, + index: 1, + ), + Access( + base: 82, + index: 17, + ), + Load( + pointer: 83, + ), + Compose( + ty: 2, + components: [ + 72, + 78, + 84, + ], + ), + Access( + base: 3, + index: 40, + ), + Load( + pointer: 86, + ), + Access( + base: 3, + index: 16, + ), + Load( + pointer: 88, + ), + Access( + base: 3, + index: 27, + ), + Load( + pointer: 90, + ), + Compose( + ty: 2, + components: [ + 87, + 89, + 91, + ], + ), + Binary( + op: Subtract, + left: 85, + right: 92, + ), + Math( + fun: Normalize, + arg: 93, + arg1: None, + arg2: None, + ), + Math( + fun: Dot, + arg: 66, + arg1: Some(94), + arg2: None, + ), + Math( + fun: Max, + arg: 46, + arg1: Some(95), + arg2: None, + ), + Binary( + op: Multiply, + left: 64, + right: 96, + ), + AccessIndex( + base: 6, + index: 0, + ), + Load( + pointer: 48, + ), + Access( + base: 98, + index: 99, + ), + AccessIndex( + base: 100, + index: 2, + ), + Access( + base: 101, + index: 14, + ), + Load( + pointer: 102, + ), + AccessIndex( + base: 6, + index: 0, + ), + Load( + pointer: 48, + ), + Access( + base: 104, + index: 105, + ), + AccessIndex( + base: 106, + index: 2, + ), + Access( + base: 107, + index: 23, + ), + Load( + pointer: 108, + ), + AccessIndex( + base: 6, + index: 0, + ), + Load( + pointer: 48, + ), + Access( + base: 110, + index: 111, + ), + AccessIndex( + base: 112, + index: 2, + ), + Access( + base: 113, + index: 30, + ), + Load( + pointer: 114, + ), + Compose( + ty: 2, + components: [ + 103, + 109, + 115, + ], + ), + Binary( + op: Multiply, + left: 116, + right: 97, + ), + Binary( + op: Add, + left: 55, + right: 117, + ), + Load( + pointer: 48, + ), + Binary( + op: Add, + left: 119, + right: 31, + ), + Load( + pointer: 47, + ), + Compose( + ty: 4, + components: [ + 121, + 34, + ], + ), + ], + body: [ + Loop( + body: [ + Emit(( + start: 48, + end: 54, + )), + If( + condition: 54, + accept: [ + Break, + ], + reject: [], + ), + Emit(( + start: 54, + end: 63, + )), + Call( + function: 1, + arguments: [ + 56, + 63, + ], + result: Some(64), + ), + Emit(( + start: 64, + end: 118, + )), + Store( + pointer: 47, + value: 118, + ), + ], + continuing: [ + Emit(( + start: 118, + end: 120, + )), + Store( + pointer: 48, + value: 120, + ), + ], + ), + Emit(( + start: 120, + end: 122, + )), + Store( + pointer: 7, + value: 122, + ), + Return( + value: None, + ), + ], + ), + ], + entry_points: [ + ( + name: "fs_main", + stage: Fragment, + early_depth_test: None, + workgroup_size: (0, 0, 0), + function: ( + name: Some("fs_main_wrap"), + arguments: [ + ( + name: Some("in_normal_fs"), + ty: 2, + binding: Some(Location( + location: 0, + interpolation: Some(Perspective), + sampling: Some(Center), + )), + ), + ( + name: Some("in_position_fs"), + ty: 4, + binding: Some(Location( + location: 1, + interpolation: Some(Perspective), + sampling: Some(Center), + )), + ), + ], + result: Some(( + ty: 4, + binding: Some(Location( + location: 0, + interpolation: None, + sampling: None, + )), + )), + local_variables: [], + expressions: [ + FunctionArgument(0), + GlobalVariable(6), + FunctionArgument(1), + GlobalVariable(5), + GlobalVariable(7), + Load( + pointer: 5, + ), + ], + body: [ + Store( + pointer: 2, + value: 1, + ), + Store( + pointer: 4, + value: 3, + ), + Call( + function: 2, + arguments: [], + result: None, + ), + Emit(( + start: 5, + end: 6, + )), + Return( + value: Some(6), + ), + ], + ), + ), + ], +) \ No newline at end of file diff --git a/third_party/rust/naga/tests/out/shadow.spvasm b/third_party/rust/naga/tests/out/shadow.spvasm new file mode 100644 index 000000000000..6f3697e3acc0 --- /dev/null +++ b/third_party/rust/naga/tests/out/shadow.spvasm @@ -0,0 +1,195 @@ +; SPIR-V +; Version: 1.2 +; Generator: rspirv +; Bound: 124 +OpCapability Shader +OpExtension "SPV_KHR_storage_buffer_storage_class" +%1 = OpExtInstImport "GLSL.std.450" +OpMemoryModel Logical GLSL450 +OpEntryPoint Fragment %79 "fs_main" %71 %74 %77 +OpExecutionMode %79 OriginUpperLeft +OpSource GLSL 450 +OpName %9 "c_max_lights" +OpName %14 "Globals" +OpMemberName %14 0 "num_lights" +OpName %17 "Light" +OpMemberName %17 0 "proj" +OpMemberName %17 1 "pos" +OpMemberName %17 2 "color" +OpName %19 "Lights" +OpMemberName %19 0 "data" +OpName %24 "c_ambient" +OpName %25 "u_globals" +OpName %27 "s_lights" +OpName %29 "t_shadow" +OpName %31 "sampler_shadow" +OpName %36 "fetch_shadow" +OpName %66 "color" +OpName %68 "i" +OpName %71 "raw_normal" +OpName %74 "position" +OpName %79 "fs_main" +OpDecorate %14 Block +OpMemberDecorate %14 0 Offset 0 +OpMemberDecorate %17 0 Offset 0 +OpMemberDecorate %17 0 ColMajor +OpMemberDecorate %17 0 MatrixStride 16 +OpMemberDecorate %17 1 Offset 64 +OpMemberDecorate %17 2 Offset 80 +OpDecorate %18 ArrayStride 96 +OpDecorate %19 Block +OpMemberDecorate %19 0 Offset 0 +OpDecorate %25 DescriptorSet 0 +OpDecorate %25 Binding 0 +OpDecorate %27 NonWritable +OpDecorate %27 DescriptorSet 0 +OpDecorate %27 Binding 1 +OpDecorate %29 DescriptorSet 0 +OpDecorate %29 Binding 2 +OpDecorate %31 DescriptorSet 0 +OpDecorate %31 Binding 3 +OpDecorate %71 Location 0 +OpDecorate %74 Location 1 +OpDecorate %77 Location 0 +%2 = OpTypeVoid +%4 = OpTypeFloat 32 +%3 = OpConstant %4 0.0 +%5 = OpConstant %4 1.0 +%6 = OpConstant %4 0.5 +%7 = OpConstant %4 -0.5 +%8 = OpConstant %4 0.05 +%10 = OpTypeInt 32 0 +%9 = OpConstant %10 10 +%11 = OpConstant %10 0 +%12 = OpConstant %10 1 +%13 = OpTypeVector %10 4 +%14 = OpTypeStruct %13 +%16 = OpTypeVector %4 4 +%15 = OpTypeMatrix %16 4 +%17 = OpTypeStruct %15 %16 %16 +%18 = OpTypeRuntimeArray %17 +%19 = OpTypeStruct %18 +%20 = OpTypeImage %4 2D 1 1 0 1 Unknown +%21 = OpTypeSampler +%22 = OpTypeVector %4 2 +%23 = OpTypeVector %4 3 +%24 = OpConstantComposite %23 %8 %8 %8 +%26 = OpTypePointer Uniform %14 +%25 = OpVariable %26 Uniform +%28 = OpTypePointer StorageBuffer %19 +%27 = OpVariable %28 StorageBuffer +%30 = OpTypePointer UniformConstant %20 +%29 = OpVariable %30 UniformConstant +%32 = OpTypePointer UniformConstant %21 +%31 = OpVariable %32 UniformConstant +%37 = OpTypeFunction %4 %10 %16 +%42 = OpTypeBool +%54 = OpTypeInt 32 1 +%59 = OpTypeSampledImage %20 +%67 = OpTypePointer Function %23 +%69 = OpTypePointer Function %10 +%72 = OpTypePointer Input %23 +%71 = OpVariable %72 Input +%75 = OpTypePointer Input %16 +%74 = OpVariable %75 Input +%78 = OpTypePointer Output %16 +%77 = OpVariable %78 Output +%80 = OpTypeFunction %2 +%90 = OpTypePointer Uniform %13 +%98 = OpTypePointer StorageBuffer %18 +%100 = OpTypePointer StorageBuffer %17 +%36 = OpFunction %4 None %37 +%34 = OpFunctionParameter %10 +%35 = OpFunctionParameter %16 +%33 = OpLabel +%38 = OpLoad %20 %29 +%39 = OpLoad %21 %31 +OpBranch %40 +%40 = OpLabel +%41 = OpCompositeExtract %4 %35 3 +%43 = OpFOrdLessThanEqual %42 %41 %3 +OpSelectionMerge %44 None +OpBranchConditional %43 %45 %44 +%45 = OpLabel +OpReturnValue %5 +%44 = OpLabel +%46 = OpCompositeConstruct %22 %6 %7 +%47 = OpVectorShuffle %22 %35 %35 0 1 +%48 = OpFMul %22 %47 %46 +%49 = OpCompositeExtract %4 %35 3 +%50 = OpCompositeConstruct %22 %49 %49 +%51 = OpFDiv %22 %48 %50 +%52 = OpCompositeConstruct %22 %6 %6 +%53 = OpFAdd %22 %51 %52 +%55 = OpBitcast %54 %34 +%56 = OpCompositeExtract %4 %35 2 +%57 = OpCompositeExtract %4 %35 3 +%58 = OpFDiv %4 %56 %57 +%60 = OpCompositeExtract %4 %53 0 +%61 = OpCompositeExtract %4 %53 1 +%62 = OpConvertUToF %4 %55 +%63 = OpCompositeConstruct %23 %60 %61 %62 +%64 = OpSampledImage %59 %38 %39 +%65 = OpImageSampleDrefExplicitLod %4 %64 %63 %58 Lod %3 +OpReturnValue %65 +OpFunctionEnd +%79 = OpFunction %2 None %80 +%70 = OpLabel +%66 = OpVariable %67 Function %24 +%68 = OpVariable %69 Function %11 +%73 = OpLoad %23 %71 +%76 = OpLoad %16 %74 +%81 = OpLoad %20 %29 +%82 = OpLoad %21 %31 +OpBranch %83 +%83 = OpLabel +%84 = OpExtInst %23 %1 Normalize %73 +OpBranch %85 +%85 = OpLabel +OpLoopMerge %86 %88 None +OpBranch %87 +%87 = OpLabel +%89 = OpLoad %10 %68 +%91 = OpAccessChain %90 %25 %11 +%92 = OpLoad %13 %91 +%93 = OpCompositeExtract %10 %92 0 +%94 = OpExtInst %10 %1 UMin %93 %9 +%95 = OpUGreaterThanEqual %42 %89 %94 +OpSelectionMerge %96 None +OpBranchConditional %95 %97 %96 +%97 = OpLabel +OpBranch %86 +%96 = OpLabel +%99 = OpLoad %10 %68 +%101 = OpAccessChain %100 %27 %11 %99 +%102 = OpLoad %17 %101 +%103 = OpLoad %10 %68 +%104 = OpCompositeExtract %15 %102 0 +%105 = OpMatrixTimesVector %16 %104 %76 +%106 = OpFunctionCall %4 %36 %103 %105 +%107 = OpCompositeExtract %16 %102 1 +%108 = OpVectorShuffle %23 %107 %107 0 1 2 +%109 = OpVectorShuffle %23 %76 %76 0 1 2 +%110 = OpFSub %23 %108 %109 +%111 = OpExtInst %23 %1 Normalize %110 +%112 = OpDot %4 %84 %111 +%113 = OpExtInst %4 %1 FMax %3 %112 +%114 = OpLoad %23 %66 +%115 = OpFMul %4 %106 %113 +%116 = OpCompositeExtract %16 %102 2 +%117 = OpVectorShuffle %23 %116 %116 0 1 2 +%118 = OpVectorTimesScalar %23 %117 %115 +%119 = OpFAdd %23 %114 %118 +OpStore %66 %119 +OpBranch %88 +%88 = OpLabel +%120 = OpLoad %10 %68 +%121 = OpIAdd %10 %120 %12 +OpStore %68 %121 +OpBranch %85 +%86 = OpLabel +%122 = OpLoad %23 %66 +%123 = OpCompositeConstruct %16 %122 %5 +OpStore %77 %123 +OpReturn +OpFunctionEnd \ No newline at end of file diff --git a/third_party/rust/naga/tests/out/skybox.Fragment.glsl b/third_party/rust/naga/tests/out/skybox.Fragment.glsl new file mode 100644 index 000000000000..c8a25b4488c1 --- /dev/null +++ b/third_party/rust/naga/tests/out/skybox.Fragment.glsl @@ -0,0 +1,21 @@ +#version 310 es + +precision highp float; + +struct VertexOutput { + vec4 position; + vec3 uv; +}; + +uniform highp samplerCube _group_0_binding_1; + +smooth layout(location = 0) in vec3 _vs2fs_location0; +layout(location = 0) out vec4 _fs2p_location0; + +void main() { + VertexOutput in1 = VertexOutput(gl_FragCoord, _vs2fs_location0); + vec4 _expr5 = texture(_group_0_binding_1, vec3(in1.uv)); + _fs2p_location0 = _expr5; + return; +} + diff --git a/third_party/rust/naga/tests/out/skybox.Vertex.glsl b/third_party/rust/naga/tests/out/skybox.Vertex.glsl new file mode 100644 index 000000000000..870a4ee26ef9 --- /dev/null +++ b/third_party/rust/naga/tests/out/skybox.Vertex.glsl @@ -0,0 +1,29 @@ +#version 310 es + +precision highp float; + +struct VertexOutput { + vec4 position; + vec3 uv; +}; + +uniform Data_block_0 { + mat4x4 proj_inv; + mat4x4 view; +} _group_0_binding_0; + +smooth layout(location = 0) out vec3 _vs2fs_location0; + +void main() { + uint vertex_index = uint(gl_VertexID); + int tmp1_; + int tmp2_; + tmp1_ = (int(vertex_index) / 2); + tmp2_ = (int(vertex_index) & 1); + vec4 _expr24 = vec4(((float(tmp1_) * 4.0) - 1.0), ((float(tmp2_) * 4.0) - 1.0), 0.0, 1.0); + VertexOutput _tmp_return = VertexOutput(_expr24, (transpose(mat3x3(_group_0_binding_0.view[0].xyz, _group_0_binding_0.view[1].xyz, _group_0_binding_0.view[2].xyz)) * (_group_0_binding_0.proj_inv * _expr24).xyz)); + gl_Position = _tmp_return.position; + _vs2fs_location0 = _tmp_return.uv; + return; +} + diff --git a/third_party/rust/naga/tests/out/skybox.msl b/third_party/rust/naga/tests/out/skybox.msl new file mode 100644 index 000000000000..c3ba8121374d --- /dev/null +++ b/third_party/rust/naga/tests/out/skybox.msl @@ -0,0 +1,55 @@ +#include +#include + +struct VertexOutput { + metal::float4 position; + metal::float3 uv; +}; +struct Data { + metal::float4x4 proj_inv; + metal::float4x4 view; +}; + +struct vs_mainInput { +}; +struct vs_mainOutput { + metal::float4 position [[position]]; + metal::float3 uv [[user(loc0), center_perspective]]; +}; +vertex vs_mainOutput vs_main( + metal::uint vertex_index [[vertex_id]] +, constant Data& r_data [[buffer(0)]] +) { + int tmp1_; + int tmp2_; + tmp1_ = static_cast(vertex_index) / 2; + tmp2_ = static_cast(vertex_index) & 1; + metal::float4 _e24 = metal::float4((static_cast(tmp1_) * 4.0) - 1.0, (static_cast(tmp2_) * 4.0) - 1.0, 0.0, 1.0); + const auto _tmp = VertexOutput {_e24, metal::transpose(metal::float3x3(r_data.view[0].xyz, r_data.view[1].xyz, r_data.view[2].xyz)) * (r_data.proj_inv * _e24).xyz}; + return vs_mainOutput { _tmp.position, _tmp.uv }; +} + + +struct fs_mainInput { + metal::float3 uv [[user(loc0), center_perspective]]; +}; +struct fs_mainOutput { + metal::float4 member1 [[color(0)]]; +}; +fragment fs_mainOutput fs_main( + fs_mainInput varyings1 [[stage_in]] +, metal::float4 position [[position]] +, metal::texturecube r_texture [[texture(0)]] +) { + constexpr metal::sampler r_sampler( + metal::s_address::clamp_to_edge, + metal::t_address::clamp_to_edge, + metal::r_address::clamp_to_edge, + metal::mag_filter::linear, + metal::min_filter::linear, + metal::coord::normalized + ); + const VertexOutput in = { position, varyings1.uv }; + metal::float4 _e5 = r_texture.sample(r_sampler, in.uv); + return fs_mainOutput { _e5 }; +} diff --git a/third_party/rust/naga/tests/out/skybox.spvasm b/third_party/rust/naga/tests/out/skybox.spvasm new file mode 100644 index 000000000000..16e597cb89e9 --- /dev/null +++ b/third_party/rust/naga/tests/out/skybox.spvasm @@ -0,0 +1,133 @@ +; SPIR-V +; Version: 1.0 +; Generator: rspirv +; Bound: 93 +OpCapability Shader +%1 = OpExtInstImport "GLSL.std.450" +OpMemoryModel Logical GLSL450 +OpEntryPoint Vertex %36 "vs_main" %29 %32 %34 +OpEntryPoint Fragment %85 "fs_main" %78 %81 %84 +OpExecutionMode %85 OriginUpperLeft +OpMemberDecorate %12 0 Offset 0 +OpMemberDecorate %12 1 Offset 16 +OpDecorate %14 Block +OpMemberDecorate %14 0 Offset 0 +OpMemberDecorate %14 0 ColMajor +OpMemberDecorate %14 0 MatrixStride 16 +OpMemberDecorate %14 1 Offset 64 +OpMemberDecorate %14 1 ColMajor +OpMemberDecorate %14 1 MatrixStride 16 +OpDecorate %19 DescriptorSet 0 +OpDecorate %19 Binding 0 +OpDecorate %21 DescriptorSet 0 +OpDecorate %21 Binding 1 +OpDecorate %23 DescriptorSet 0 +OpDecorate %23 Binding 2 +OpDecorate %29 BuiltIn VertexIndex +OpDecorate %32 BuiltIn Position +OpDecorate %34 Location 0 +OpDecorate %78 BuiltIn FragCoord +OpDecorate %81 Location 0 +OpDecorate %84 Location 0 +%2 = OpTypeVoid +%4 = OpTypeInt 32 1 +%3 = OpConstant %4 2 +%5 = OpConstant %4 1 +%7 = OpTypeFloat 32 +%6 = OpConstant %7 4.0 +%8 = OpConstant %7 1.0 +%9 = OpConstant %7 0.0 +%10 = OpTypeVector %7 4 +%11 = OpTypeVector %7 3 +%12 = OpTypeStruct %10 %11 +%13 = OpTypeMatrix %10 4 +%14 = OpTypeStruct %13 %13 +%15 = OpTypeInt 32 0 +%16 = OpTypeMatrix %11 3 +%17 = OpTypeImage %7 Cube 0 0 0 1 Unknown +%18 = OpTypeSampler +%20 = OpTypePointer Uniform %14 +%19 = OpVariable %20 Uniform +%22 = OpTypePointer UniformConstant %17 +%21 = OpVariable %22 UniformConstant +%24 = OpTypePointer UniformConstant %18 +%23 = OpVariable %24 UniformConstant +%26 = OpTypePointer Function %4 +%30 = OpTypePointer Input %15 +%29 = OpVariable %30 Input +%33 = OpTypePointer Output %10 +%32 = OpVariable %33 Output +%35 = OpTypePointer Output %11 +%34 = OpVariable %35 Output +%37 = OpTypeFunction %2 +%52 = OpTypePointer Uniform %13 +%53 = OpTypePointer Uniform %10 +%54 = OpConstant %15 0 +%55 = OpConstant %15 1 +%62 = OpConstant %15 2 +%79 = OpTypePointer Input %10 +%78 = OpVariable %79 Input +%82 = OpTypePointer Input %11 +%81 = OpVariable %82 Input +%84 = OpVariable %33 Output +%90 = OpTypeSampledImage %17 +%36 = OpFunction %2 None %37 +%28 = OpLabel +%25 = OpVariable %26 Function +%27 = OpVariable %26 Function +%31 = OpLoad %15 %29 +OpBranch %38 +%38 = OpLabel +%39 = OpBitcast %4 %31 +%40 = OpSDiv %4 %39 %3 +OpStore %25 %40 +%41 = OpBitcast %4 %31 +%42 = OpBitwiseAnd %4 %41 %5 +OpStore %27 %42 +%43 = OpLoad %4 %25 +%44 = OpConvertSToF %7 %43 +%45 = OpFMul %7 %44 %6 +%46 = OpFSub %7 %45 %8 +%47 = OpLoad %4 %27 +%48 = OpConvertSToF %7 %47 +%49 = OpFMul %7 %48 %6 +%50 = OpFSub %7 %49 %8 +%51 = OpCompositeConstruct %10 %46 %50 %9 %8 +%56 = OpAccessChain %53 %19 %55 %54 +%57 = OpLoad %10 %56 +%58 = OpVectorShuffle %11 %57 %57 0 1 2 +%59 = OpAccessChain %53 %19 %55 %55 +%60 = OpLoad %10 %59 +%61 = OpVectorShuffle %11 %60 %60 0 1 2 +%63 = OpAccessChain %53 %19 %55 %62 +%64 = OpLoad %10 %63 +%65 = OpVectorShuffle %11 %64 %64 0 1 2 +%66 = OpCompositeConstruct %16 %58 %61 %65 +%67 = OpTranspose %16 %66 +%68 = OpAccessChain %52 %19 %54 +%69 = OpLoad %13 %68 +%70 = OpMatrixTimesVector %10 %69 %51 +%71 = OpVectorShuffle %11 %70 %70 0 1 2 +%72 = OpMatrixTimesVector %11 %67 %71 +%73 = OpCompositeConstruct %12 %51 %72 +%74 = OpCompositeExtract %10 %73 0 +OpStore %32 %74 +%75 = OpCompositeExtract %11 %73 1 +OpStore %34 %75 +OpReturn +OpFunctionEnd +%85 = OpFunction %2 None %37 +%76 = OpLabel +%80 = OpLoad %10 %78 +%83 = OpLoad %11 %81 +%77 = OpCompositeConstruct %12 %80 %83 +%86 = OpLoad %17 %21 +%87 = OpLoad %18 %23 +OpBranch %88 +%88 = OpLabel +%89 = OpCompositeExtract %11 %77 1 +%91 = OpSampledImage %90 %86 %87 +%92 = OpImageSampleImplicitLod %10 %91 %89 +OpStore %84 %92 +OpReturn +OpFunctionEnd \ No newline at end of file diff --git a/third_party/rust/naga/tests/snapshots.rs b/third_party/rust/naga/tests/snapshots.rs new file mode 100644 index 000000000000..8a85ee32cfcf --- /dev/null +++ b/third_party/rust/naga/tests/snapshots.rs @@ -0,0 +1,346 @@ +//TODO: move this to a binary target once Rust supports +// binary-specific dependencies. + +use std::{fs, path::PathBuf}; + +const DIR_IN: &str = "tests/in"; +const DIR_OUT: &str = "tests/out"; + +bitflags::bitflags! { + struct Targets: u32 { + const IR = 0x1; + const ANALYSIS = 0x2; + const SPIRV = 0x4; + const METAL = 0x8; + const GLSL = 0x10; + const DOT = 0x20; + const HLSL = 0x40; + const WGSL = 0x80; + } +} + +#[derive(Default, serde::Deserialize)] +struct Parameters { + #[serde(default)] + god_mode: bool, + #[cfg_attr(not(feature = "spv-out"), allow(dead_code))] + spv_version: (u8, u8), + #[cfg_attr(not(feature = "spv-out"), allow(dead_code))] + spv_capabilities: naga::FastHashSet, + #[cfg_attr(not(feature = "spv-out"), allow(dead_code))] + #[serde(default)] + spv_debug: bool, + #[cfg_attr(not(feature = "spv-out"), allow(dead_code))] + #[serde(default)] + spv_adjust_coordinate_space: bool, + #[cfg(all(feature = "deserialize", feature = "msl-out"))] + #[serde(default)] + msl: naga::back::msl::Options, + #[cfg(all(not(feature = "deserialize"), feature = "msl-out"))] + #[serde(default)] + msl_custom: bool, + #[cfg_attr(not(feature = "glsl-out"), allow(dead_code))] + #[serde(default)] + glsl_desktop_version: Option, +} + +#[allow(dead_code, unused_variables)] +fn check_targets(module: &naga::Module, name: &str, targets: Targets) { + let root = env!("CARGO_MANIFEST_DIR"); + let params = match fs::read_to_string(format!("{}/{}/{}.param.ron", root, DIR_IN, name)) { + Ok(string) => ron::de::from_str(&string).expect("Couldn't find param file"), + Err(_) => Parameters::default(), + }; + let capabilities = if params.god_mode { + naga::valid::Capabilities::all() + } else { + naga::valid::Capabilities::empty() + }; + let info = naga::valid::Validator::new(naga::valid::ValidationFlags::all(), capabilities) + .validate(module) + .unwrap(); + + let dest = PathBuf::from(root).join(DIR_OUT).join(name); + + #[cfg(feature = "serialize")] + { + if targets.contains(Targets::IR) { + let config = ron::ser::PrettyConfig::default().with_new_line("\n".to_string()); + let string = ron::ser::to_string_pretty(module, config).unwrap(); + fs::write(dest.with_extension("ron"), string).unwrap(); + } + if targets.contains(Targets::ANALYSIS) { + let config = ron::ser::PrettyConfig::default().with_new_line("\n".to_string()); + let string = ron::ser::to_string_pretty(&info, config).unwrap(); + fs::write(dest.with_extension("info.ron"), string).unwrap(); + } + } + + #[cfg(feature = "spv-out")] + { + if targets.contains(Targets::SPIRV) { + check_output_spv(module, &info, &dest, ¶ms); + } + } + #[cfg(feature = "msl-out")] + { + if targets.contains(Targets::METAL) { + check_output_msl(module, &info, &dest, ¶ms); + } + } + #[cfg(feature = "glsl-out")] + { + if targets.contains(Targets::GLSL) { + for ep in module.entry_points.iter() { + check_output_glsl(module, &info, &dest, ep.stage, &ep.name, ¶ms); + } + } + } + #[cfg(feature = "dot-out")] + { + if targets.contains(Targets::DOT) { + let string = naga::back::dot::write(module, Some(&info)).unwrap(); + fs::write(dest.with_extension("dot"), string).unwrap(); + } + } + #[cfg(feature = "hlsl-out")] + { + if targets.contains(Targets::HLSL) { + for ep in module.entry_points.iter() { + check_output_hlsl(module, &dest, ep.stage); + } + } + } + #[cfg(feature = "wgsl-out")] + { + if targets.contains(Targets::WGSL) { + check_output_wgsl(module, &info, &dest); + } + } +} + +#[cfg(feature = "spv-out")] +fn check_output_spv( + module: &naga::Module, + info: &naga::valid::ModuleInfo, + destination: &PathBuf, + params: &Parameters, +) { + use naga::back::spv; + use rspirv::binary::Disassemble; + + let mut flags = spv::WriterFlags::empty(); + if params.spv_debug { + flags |= spv::WriterFlags::DEBUG; + } + if params.spv_adjust_coordinate_space { + flags |= spv::WriterFlags::ADJUST_COORDINATE_SPACE; + } + let options = spv::Options { + lang_version: params.spv_version, + flags, + capabilities: Some(params.spv_capabilities.clone()), + }; + + let spv = spv::write_vec(module, info, &options).unwrap(); + + let dis = rspirv::dr::load_words(spv) + .expect("Produced invalid SPIR-V") + .disassemble(); + + fs::write(destination.with_extension("spvasm"), dis).unwrap(); +} + +#[cfg(feature = "msl-out")] +fn check_output_msl( + module: &naga::Module, + info: &naga::valid::ModuleInfo, + destination: &PathBuf, + params: &Parameters, +) { + use naga::back::msl; + + #[cfg_attr(feature = "deserialize", allow(unused_variables))] + let default_options = msl::Options::default(); + #[cfg(feature = "deserialize")] + let options = ¶ms.msl; + #[cfg(not(feature = "deserialize"))] + let options = if params.msl_custom { + println!("Skipping {}", destination.display()); + return; + } else { + &default_options + }; + + let pipeline_options = msl::PipelineOptions { + allow_point_size: true, + }; + + let (string, tr_info) = msl::write_string(module, info, options, &pipeline_options).unwrap(); + + for (ep, result) in module.entry_points.iter().zip(tr_info.entry_point_names) { + if let Err(error) = result { + panic!("Failed to translate '{}': {}", ep.name, error); + } + } + + fs::write(destination.with_extension("msl"), string).unwrap(); +} + +#[cfg(feature = "glsl-out")] +fn check_output_glsl( + module: &naga::Module, + info: &naga::valid::ModuleInfo, + destination: &PathBuf, + stage: naga::ShaderStage, + ep_name: &str, + params: &Parameters, +) { + use naga::back::glsl; + + let options = glsl::Options { + version: match params.glsl_desktop_version { + Some(v) => glsl::Version::Desktop(v), + None => glsl::Version::Embedded(310), + }, + shader_stage: stage, + entry_point: ep_name.to_string(), + }; + + let mut buffer = String::new(); + let mut writer = glsl::Writer::new(&mut buffer, module, info, &options).unwrap(); + writer.write().unwrap(); + + let ext = format!("{:?}.glsl", stage); + fs::write(destination.with_extension(&ext), buffer).unwrap(); +} + +#[cfg(feature = "hlsl-out")] +fn check_output_hlsl(module: &naga::Module, destination: &PathBuf, stage: naga::ShaderStage) { + use naga::back::hlsl; + + let string = hlsl::write_string(module).unwrap(); + + let ext = format!("{:?}.hlsl", stage); + fs::write(destination.with_extension(&ext), string).unwrap(); +} + +#[cfg(feature = "wgsl-out")] +fn check_output_wgsl(module: &naga::Module, info: &naga::valid::ModuleInfo, destination: &PathBuf) { + use naga::back::wgsl; + + let string = wgsl::write_string(module, info).unwrap(); + + fs::write(destination.with_extension("wgsl"), string).unwrap(); +} + +#[cfg(feature = "wgsl-in")] +#[test] +fn convert_wgsl() { + let root = env!("CARGO_MANIFEST_DIR"); + let inputs = [ + ( + "empty", + Targets::SPIRV | Targets::METAL | Targets::GLSL | Targets::HLSL | Targets::WGSL, + ), + ( + "quad", + Targets::SPIRV | Targets::METAL | Targets::GLSL | Targets::DOT | Targets::WGSL, + ), + ("boids", Targets::SPIRV | Targets::METAL | Targets::GLSL), + ("skybox", Targets::SPIRV | Targets::METAL | Targets::GLSL), + ( + "collatz", + Targets::SPIRV | Targets::METAL | Targets::IR | Targets::ANALYSIS, + ), + ("shadow", Targets::SPIRV | Targets::METAL | Targets::GLSL), + ("image", Targets::SPIRV | Targets::METAL), + ("extra", Targets::SPIRV | Targets::METAL), + ("operators", Targets::SPIRV | Targets::METAL | Targets::GLSL), + ( + "interpolate", + Targets::SPIRV | Targets::METAL | Targets::GLSL, + ), + ("access", Targets::SPIRV | Targets::METAL), + ( + "control-flow", + Targets::SPIRV | Targets::METAL | Targets::GLSL, + ), + ]; + + for &(name, targets) in inputs.iter() { + println!("Processing '{}'", name); + let file = fs::read_to_string(format!("{}/{}/{}.wgsl", root, DIR_IN, name)) + .expect("Couldn't find wgsl file"); + match naga::front::wgsl::parse_str(&file) { + Ok(module) => check_targets(&module, name, targets), + Err(e) => panic!("{}", e), + } + } +} + +#[cfg(feature = "spv-in")] +fn convert_spv(name: &str, adjust_coordinate_space: bool, targets: Targets) { + let root = env!("CARGO_MANIFEST_DIR"); + let module = naga::front::spv::parse_u8_slice( + &fs::read(format!("{}/{}/{}.spv", root, DIR_IN, name)).expect("Couldn't find spv file"), + &naga::front::spv::Options { + adjust_coordinate_space, + strict_capabilities: false, + flow_graph_dump_prefix: None, + }, + ) + .unwrap(); + check_targets(&module, name, targets); + naga::valid::Validator::new( + naga::valid::ValidationFlags::all(), + naga::valid::Capabilities::empty(), + ) + .validate(&module) + .unwrap(); +} + +#[cfg(feature = "spv-in")] +#[test] +fn convert_spv_quad_vert() { + convert_spv( + "quad-vert", + false, + Targets::METAL | Targets::GLSL | Targets::WGSL, + ); +} + +#[cfg(feature = "spv-in")] +#[test] +fn convert_spv_shadow() { + convert_spv("shadow", true, Targets::IR | Targets::ANALYSIS); +} + +#[cfg(feature = "glsl-in")] +fn convert_glsl( + name: &str, + entry_points: naga::FastHashMap, + _targets: Targets, +) { + let root = env!("CARGO_MANIFEST_DIR"); + let _module = naga::front::glsl::parse_str( + &fs::read_to_string(format!("{}/{}/{}.glsl", root, DIR_IN, name)) + .expect("Couldn't find glsl file"), + &naga::front::glsl::Options { + entry_points, + defines: Default::default(), + }, + ) + .unwrap(); + //TODO + //check_targets(&module, name, targets); +} + +#[cfg(feature = "glsl-in")] +#[test] +fn convert_glsl_quad() { + let mut entry_points = naga::FastHashMap::default(); + entry_points.insert("vert_main".to_string(), naga::ShaderStage::Vertex); + entry_points.insert("frag_main".to_string(), naga::ShaderStage::Fragment); + convert_glsl("quad-glsl", entry_points, Targets::SPIRV | Targets::IR); +} diff --git a/third_party/rust/naga/tests/wgsl-errors.rs b/third_party/rust/naga/tests/wgsl-errors.rs new file mode 100644 index 000000000000..5a03f47857c3 --- /dev/null +++ b/third_party/rust/naga/tests/wgsl-errors.rs @@ -0,0 +1,177 @@ +//! Tests for the WGSL front end. +#![cfg(feature = "wgsl-in")] + +fn check(input: &str, snapshot: &str) { + let output = naga::front::wgsl::parse_str(input) + .expect_err("expected parser error") + .emit_to_string(input); + if output != snapshot { + for diff in diff::lines(&output, snapshot) { + match diff { + diff::Result::Left(l) => println!("-{}", l), + diff::Result::Both(l, _) => println!(" {}", l), + diff::Result::Right(r) => println!("+{}", r), + } + } + panic!("Error snapshot failed"); + } +} + +#[test] +fn function_without_identifier() { + check( + "fn () {}", + r###"error: expected identifier, found '(' + ┌─ wgsl:1:4 + │ +1 │ fn () {} + │ ^ expected identifier + +"###, + ); +} + +#[test] +fn invalid_integer() { + check( + "fn foo([location(1.)] x: i32) {}", + r###"error: expected identifier, found '[' + ┌─ wgsl:1:8 + │ +1 │ fn foo([location(1.)] x: i32) {} + │ ^ expected identifier + +"###, + ); +} + +#[test] +fn invalid_float() { + check( + "let scale: f32 = 1.1.;", + r###"error: expected floating-point literal, found `1.1.` + ┌─ wgsl:1:18 + │ +1 │ let scale: f32 = 1.1.; + │ ^^^^ expected floating-point literal + +"###, + ); +} + +#[test] +fn invalid_scalar_width() { + check( + "let scale: f32 = 1.1f1000;", + r###"error: invalid width of `1000` for literal + ┌─ wgsl:1:18 + │ +1 │ let scale: f32 = 1.1f1000; + │ ^^^^^^^^ invalid width + │ + = note: valid width is 32 + +"###, + ); +} + +macro_rules! check_validation_error { + ( $( $source:literal ),* : $pattern:pat ) => { + $( + let error = validation_error($source); + if ! matches!(error, $pattern) { + eprintln!("validation error does not match pattern:\n\ + {:?}\n\ + \n\ + expected match for pattern:\n\ + {}", + error, + stringify!($pattern)); + panic!("validation error does not match pattern"); + } + )* + } +} + +fn validation_error(source: &str) -> Result { + let module = naga::front::wgsl::parse_str(source).expect("expected WGSL parse to succeed"); + naga::valid::Validator::new( + naga::valid::ValidationFlags::all(), + naga::valid::Capabilities::empty(), + ) + .validate(&module) +} + +#[test] +fn invalid_arrays() { + check_validation_error! { + "type Bad = array, 4>;", + "type Bad = array;", + "type Bad = array, 4>;": + Err(naga::valid::ValidationError::Type { + error: naga::valid::TypeError::InvalidArrayBaseType(_), + .. + }) + } + + check_validation_error! { + r#" + [[block]] struct Block { value: f32; }; + type Bad = array; + "#: + Err(naga::valid::ValidationError::Type { + error: naga::valid::TypeError::NestedBlock, + .. + }) + } + + check_validation_error! { + r#" + type Bad = [[stride(2)]] array; + "#: + Err(naga::valid::ValidationError::Type { + error: naga::valid::TypeError::InsufficientArrayStride { stride: 2, base_size: 4 }, + .. + }) + } + + check_validation_error! { + "type Bad = array;", + r#" + let length: f32 = 2.718; + type Bad = array; + "#: + Err(naga::valid::ValidationError::Type { + error: naga::valid::TypeError::InvalidArraySizeConstant(_), + .. + }) + } +} + +#[test] +fn invalid_structs() { + check_validation_error! { + "struct Bad { data: sampler; };", + "struct Bad { data: texture_2d; };": + Err(naga::valid::ValidationError::Type { + error: naga::valid::TypeError::InvalidData(_), + .. + }) + } + + check_validation_error! { + "[[block]] struct Bad { data: ptr; };": + Err(naga::valid::ValidationError::Type { + error: naga::valid::TypeError::InvalidBlockType(_), + .. + }) + } + + check_validation_error! { + "struct Bad { data: array; other: f32; };": + Err(naga::valid::ValidationError::Type { + error: naga::valid::TypeError::InvalidDynamicArray(_, _), + .. + }) + } +} diff --git a/third_party/rust/range-alloc/.cargo-checksum.json b/third_party/rust/range-alloc/.cargo-checksum.json index 1ab9a7c09499..defc8572613d 100644 --- a/third_party/rust/range-alloc/.cargo-checksum.json +++ b/third_party/rust/range-alloc/.cargo-checksum.json @@ -1 +1 @@ -{"files":{"Cargo.toml":"1ee0fb024b32b4b1ab797cca38578108cf6c50b19e9a8fe6c04472c013d6b93f","src/lib.rs":"5f43d044c9dd063a3a2053fb386927c48ee612a49d1106a329b3c76a009548aa"},"package":"63e935c45e09cc6dcf00d2f0b2d630a58f4095320223d47fc68918722f0538b6"} \ No newline at end of file +{"files":{"Cargo.toml":"11becea98301abc20d7996a8f05d4238d2e46386a3eeffe82de933d9999d7dc8","src/lib.rs":"5f43d044c9dd063a3a2053fb386927c48ee612a49d1106a329b3c76a009548aa"},"package":null} \ No newline at end of file diff --git a/third_party/rust/range-alloc/Cargo.toml b/third_party/rust/range-alloc/Cargo.toml index 258a312ba0fc..d3a2c5648cdf 100644 --- a/third_party/rust/range-alloc/Cargo.toml +++ b/third_party/rust/range-alloc/Cargo.toml @@ -1,27 +1,16 @@ -# THIS FILE IS AUTOMATICALLY GENERATED BY CARGO -# -# When uploading crates to the registry Cargo will automatically -# "normalize" Cargo.toml files for maximal compatibility -# with all versions of Cargo and also rewrite `path` dependencies -# to registry (e.g., crates.io) dependencies -# -# If you believe there's an error in this file please file an -# issue against the rust-lang/cargo repository. If you're -# editing this file be aware that the upstream Cargo.toml -# will likely look very different (and much more reasonable) - [package] -edition = "2018" name = "range-alloc" version = "0.1.2" -authors = ["The Gfx-rs Developers"] description = "Generic range allocator used by gfx-rs backends" homepage = "https://github.com/gfx-rs/gfx" -documentation = "https://docs.rs/range-alloc" -keywords = ["allocator"] -categories = ["memory-management"] -license = "MIT OR Apache-2.0" repository = "https://github.com/gfx-rs/gfx" +keywords = ["allocator"] +license = "MIT OR Apache-2.0" +authors = ["The Gfx-rs Developers"] +documentation = "https://docs.rs/range-alloc" +categories = ["memory-management"] +workspace = "../../../" +edition = "2018" [lib] name = "range_alloc"