├── reftests ├── data │ ├── buffer.raw │ ├── image.raw │ ├── fill-8-bytes.raw │ ├── fill-10-bytes.raw │ ├── vertex-offset.raw │ ├── passthrough.frag │ ├── vertex-offset.frag │ ├── fill.comp │ ├── passthrough.vert │ └── vertex-offset.vert ├── ci.ron ├── scenes │ ├── compute.ron │ ├── basic.ron │ ├── transfer.ron │ └── vertex-offset.ron └── local.ron ├── rustfmt.toml ├── info ├── logo.png ├── dx_depth_coordinates.png ├── gl_depth_coordinates.png ├── dx_texture_coordinates.png ├── gl_render_coordinates.png ├── gl_texture_coordinates.png ├── vk_render_coordinates.png ├── README.md ├── getting_started.md ├── hal.svg └── research.md ├── src ├── backend │ ├── gl │ │ ├── src │ │ │ ├── window │ │ │ │ ├── mod.rs │ │ │ │ └── glutin.rs │ │ │ ├── pool.rs │ │ │ └── conv.rs │ │ ├── Cargo.toml │ │ └── README.md │ ├── metal │ │ ├── shaders │ │ │ ├── macros.h │ │ │ ├── fill.metal │ │ │ ├── clear.metal │ │ │ └── blit.metal │ │ ├── README.md │ │ ├── Cargo.toml │ │ └── build.rs │ ├── vulkan │ │ ├── src │ │ │ ├── info.rs │ │ │ ├── pool.rs │ │ │ ├── result.rs │ │ │ └── native.rs │ │ ├── README.md │ │ └── Cargo.toml │ ├── dx12 │ │ ├── README.md │ │ ├── shaders │ │ │ └── blit.hlsl │ │ ├── Cargo.toml │ │ └── src │ │ │ ├── pool.rs │ │ │ ├── descriptors_cpu.rs │ │ │ └── window.rs │ ├── dx11 │ │ ├── README.md │ │ ├── shaders │ │ │ ├── clear.hlsl │ │ │ └── blit.hlsl │ │ ├── Cargo.toml │ │ └── src │ │ │ ├── debug.rs │ │ │ └── dxgi.rs │ └── empty │ │ └── Cargo.toml ├── macros │ ├── tests │ │ ├── derive.rs │ │ └── macros.rs │ └── src │ │ └── lib.rs ├── warden │ ├── src │ │ ├── lib.rs │ │ └── raw.rs │ ├── examples │ │ └── basic.rs │ ├── Cargo.toml │ └── README.md ├── auxil │ └── range-alloc │ │ └── Cargo.toml └── hal │ ├── Cargo.toml │ └── src │ ├── pso │ ├── compute.rs │ └── input_assembler.rs │ ├── range.rs │ ├── backend.rs │ ├── command │ ├── compute.rs │ ├── transfer.rs │ └── mod.rs │ ├── mapping.rs │ ├── error.rs │ ├── queue │ ├── capability.rs │ ├── family.rs │ └── mod.rs │ ├── query.rs │ ├── pool.rs │ ├── buffer.rs │ ├── memory.rs │ ├── pass.rs │ └── adapter.rs ├── .github ├── ISSUE_TEMPLATE.md ├── PULL_REQUEST_TEMPLATE.md └── CONTRIBUTING.md ├── examples ├── quad │ └── data │ │ ├── logo.png │ │ ├── quad.frag │ │ └── quad.vert ├── colour-uniform │ └── data │ │ ├── logo.png │ │ ├── quad.vert │ │ └── quad.frag ├── README.md ├── compute │ └── shader │ │ └── collatz.comp └── Cargo.toml ├── bors.toml ├── .gitignore ├── Cargo.toml ├── CHANGELOG.md ├── LICENSE-MIT ├── Makefile ├── .travis.yml └── README.md /reftests/data/buffer.raw: -------------------------------------------------------------------------------- 1 | HALP -------------------------------------------------------------------------------- /reftests/data/image.raw: -------------------------------------------------------------------------------- 1 | 01234567 -------------------------------------------------------------------------------- /reftests/data/fill-8-bytes.raw: -------------------------------------------------------------------------------- 1 | 8 BYTES! -------------------------------------------------------------------------------- /reftests/data/fill-10-bytes.raw: -------------------------------------------------------------------------------- 1 | 10 BYTES?! -------------------------------------------------------------------------------- /rustfmt.toml: -------------------------------------------------------------------------------- 1 | newline_style = "Native" 2 | -------------------------------------------------------------------------------- /info/logo.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/tmahmood/gfx/master/info/logo.png -------------------------------------------------------------------------------- /src/backend/gl/src/window/mod.rs: -------------------------------------------------------------------------------- 1 | #[cfg(feature = "glutin")] 2 | pub mod glutin; 3 | -------------------------------------------------------------------------------- /.github/ISSUE_TEMPLATE.md: -------------------------------------------------------------------------------- 1 | Short info header: 2 | - GFX version: 3 | - OS: 4 | - GPU: 5 | -------------------------------------------------------------------------------- /reftests/data/vertex-offset.raw: -------------------------------------------------------------------------------- 1 |  2 |  3 |  4 |  -------------------------------------------------------------------------------- /examples/quad/data/logo.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/tmahmood/gfx/master/examples/quad/data/logo.png -------------------------------------------------------------------------------- /info/dx_depth_coordinates.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/tmahmood/gfx/master/info/dx_depth_coordinates.png -------------------------------------------------------------------------------- /info/gl_depth_coordinates.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/tmahmood/gfx/master/info/gl_depth_coordinates.png -------------------------------------------------------------------------------- /info/dx_texture_coordinates.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/tmahmood/gfx/master/info/dx_texture_coordinates.png -------------------------------------------------------------------------------- /info/gl_render_coordinates.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/tmahmood/gfx/master/info/gl_render_coordinates.png -------------------------------------------------------------------------------- /info/gl_texture_coordinates.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/tmahmood/gfx/master/info/gl_texture_coordinates.png -------------------------------------------------------------------------------- /info/vk_render_coordinates.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/tmahmood/gfx/master/info/vk_render_coordinates.png -------------------------------------------------------------------------------- /bors.toml: -------------------------------------------------------------------------------- 1 | status = [ 2 | "continuous-integration/travis-ci/push" 3 | ] 4 | 5 | timeout_sec = 18000 # 5 hours 6 | -------------------------------------------------------------------------------- /examples/colour-uniform/data/logo.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/tmahmood/gfx/master/examples/colour-uniform/data/logo.png -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | # Junk 2 | .DS_Store 3 | .vscode/ 4 | .#* 5 | *.iml 6 | .idea 7 | .fuse_hidden* 8 | 9 | # Compiled 10 | /doc 11 | target/ 12 | 13 | # Service 14 | Cargo.lock 15 | *.swp 16 | -------------------------------------------------------------------------------- /src/backend/metal/shaders/macros.h: -------------------------------------------------------------------------------- 1 | #ifdef __METAL_MACOS__ 2 | # define GFX_RENDER_TARGET_ARRAY_INDEX [[render_target_array_index]] 3 | #else 4 | # define GFX_RENDER_TARGET_ARRAY_INDEX 5 | #endif 6 | -------------------------------------------------------------------------------- /info/README.md: -------------------------------------------------------------------------------- 1 | ## Navigation 2 | 3 | * [Getting started](getting_started.md) 4 | * [Research on Data-Oriented Programming](research.md) 5 | * [Contribution guidelines](../.github/CONTRIBUTING.md) 6 | 7 | -------------------------------------------------------------------------------- /src/backend/vulkan/src/info.rs: -------------------------------------------------------------------------------- 1 | pub mod intel { 2 | pub const VENDOR: u32 = 0x8086; 3 | pub const DEVICE_KABY_LAKE_MASK: u32 = 0x5900; 4 | pub const DEVICE_SKY_LAKE_MASK: u32 = 0x1900; 5 | } 6 | -------------------------------------------------------------------------------- /reftests/data/passthrough.frag: -------------------------------------------------------------------------------- 1 | #version 450 2 | #extension GL_ARB_separate_shader_objects : enable 3 | 4 | layout(location = 0) out vec4 o_Color; 5 | 6 | void main() { 7 | o_Color = vec4(0.0, 1.0, 0.0, 1.0); 8 | } 9 | -------------------------------------------------------------------------------- /.github/PULL_REQUEST_TEMPLATE.md: -------------------------------------------------------------------------------- 1 | Fixes #issue 2 | PR checklist: 3 | - [ ] `make` succeeds (on *nix) 4 | - [ ] `make reftests` succeeds 5 | - [ ] tested examples with the following backends: 6 | - [ ] `rustfmt` run on changed code 7 | -------------------------------------------------------------------------------- /reftests/data/vertex-offset.frag: -------------------------------------------------------------------------------- 1 | #version 450 2 | #extension GL_ARB_separate_shader_objects : enable 3 | 4 | layout(location = 0) in flat uvec4 v_color; 5 | layout(location = 0) out uvec4 o_Color; 6 | 7 | void main() { 8 | o_Color = v_color; 9 | } 10 | -------------------------------------------------------------------------------- /src/macros/tests/derive.rs: -------------------------------------------------------------------------------- 1 | extern crate gfx; 2 | #[macro_use] 3 | extern crate gfx_macros; 4 | 5 | #[derive(VertexData)] 6 | struct Vertex { 7 | pos: [u8; 4], 8 | } 9 | 10 | #[derive(ConstantBuffer)] 11 | struct Constant { 12 | transform: [[f32; 4]; 4], 13 | } 14 | -------------------------------------------------------------------------------- /reftests/data/fill.comp: -------------------------------------------------------------------------------- 1 | #version 450 2 | 3 | layout(local_size_x = 1, local_size_y = 1) in; 4 | layout(std430, set = 0, binding = 0) buffer b_Output 5 | { 6 | uint data[]; 7 | }; 8 | 9 | 10 | void main() { 11 | uint index = gl_GlobalInvocationID.x; 12 | data[index] = 1; 13 | } 14 | -------------------------------------------------------------------------------- /Cargo.toml: -------------------------------------------------------------------------------- 1 | [workspace] 2 | 3 | members = [ 4 | "src/auxil/range-alloc", 5 | "src/backend/dx11", 6 | "src/backend/dx12", 7 | "src/backend/empty", 8 | "src/backend/gl", 9 | "src/backend/metal", 10 | "src/backend/vulkan", 11 | "src/hal", 12 | "src/warden", 13 | "examples", 14 | ] 15 | -------------------------------------------------------------------------------- /reftests/ci.ron: -------------------------------------------------------------------------------- 1 | { 2 | "basic": { 3 | "render-pass-clear": ( 4 | features: (bits: 0), 5 | jobs: ["empty"], 6 | expect: ImageRow("image.color", 0, [204,204,204,255]), 7 | ), 8 | "pass-through": ( 9 | features: (bits: 0), 10 | jobs: ["pass-through"], 11 | expect: ImageRow("image.color", 0, [0,255,0,255]), 12 | ), 13 | }, 14 | } 15 | -------------------------------------------------------------------------------- /reftests/data/passthrough.vert: -------------------------------------------------------------------------------- 1 | #version 450 2 | #extension GL_ARB_separate_shader_objects : enable 3 | 4 | void main() { 5 | vec2 pos = vec2(0.0); 6 | if (gl_VertexIndex==0) pos = vec2(-1.0, -3.0); 7 | if (gl_VertexIndex==1) pos = vec2(3.0, 1.0); 8 | if (gl_VertexIndex==2) pos = vec2(-1.0, 1.0); 9 | gl_Position = vec4(pos, 0.0, 1.0); 10 | } 11 | -------------------------------------------------------------------------------- /src/warden/src/lib.rs: -------------------------------------------------------------------------------- 1 | //! Data-driven reference test framework for warding 2 | //! against breaking changes. 3 | 4 | extern crate gfx_hal as hal; 5 | #[macro_use] 6 | extern crate log; 7 | #[macro_use] 8 | extern crate serde; 9 | extern crate failure; 10 | #[cfg(feature = "glsl-to-spirv")] 11 | extern crate glsl_to_spirv; 12 | 13 | pub mod gpu; 14 | pub mod raw; 15 | -------------------------------------------------------------------------------- /examples/quad/data/quad.frag: -------------------------------------------------------------------------------- 1 | #version 450 2 | #extension GL_ARB_separate_shader_objects : enable 3 | 4 | layout(location = 0) in vec2 v_uv; 5 | layout(location = 0) out vec4 target0; 6 | 7 | layout(set = 0, binding = 0) uniform texture2D u_texture; 8 | layout(set = 0, binding = 1) uniform sampler u_sampler; 9 | 10 | void main() { 11 | target0 = texture(sampler2D(u_texture, u_sampler), v_uv); 12 | } 13 | -------------------------------------------------------------------------------- /src/backend/dx12/README.md: -------------------------------------------------------------------------------- 1 | # gfx-backend-dx12 2 | 3 | DX12 backend for gfx. 4 | 5 | ## Normalized Coordinates 6 | 7 | Render | Depth | Texture 8 | -------|-------|-------- 9 | ![render_coordinates](../../../info/gl_render_coordinates.png) | ![depth_coordinates](../../../info/dx_depth_coordinates.png) | ![texture_coordinates](../../../info/dx_texture_coordinates.png) 10 | 11 | ## Mirroring 12 | 13 | TODO 14 | -------------------------------------------------------------------------------- /examples/quad/data/quad.vert: -------------------------------------------------------------------------------- 1 | #version 450 2 | #extension GL_ARB_separate_shader_objects : enable 3 | 4 | layout(constant_id = 0) const float scale = 1.2f; 5 | 6 | layout(location = 0) in vec2 a_pos; 7 | layout(location = 1) in vec2 a_uv; 8 | layout(location = 0) out vec2 v_uv; 9 | 10 | out gl_PerVertex { 11 | vec4 gl_Position; 12 | }; 13 | 14 | void main() { 15 | v_uv = a_uv; 16 | gl_Position = vec4(scale * a_pos, 0.0, 1.0); 17 | } 18 | -------------------------------------------------------------------------------- /src/backend/metal/README.md: -------------------------------------------------------------------------------- 1 | # gfx-backend-metal 2 | 3 | [Metal](https://developer.apple.com/metal/) backend for gfx-rs. 4 | 5 | ## Normalized Coordinates 6 | 7 | Render | Depth | Texture 8 | -------|-------|-------- 9 | ![render_coordinates](../../../info/gl_render_coordinates.png) | ![depth_coordinates](../../../info/dx_depth_coordinates.png) | ![texture_coordinates](../../../info/dx_texture_coordinates.png) 10 | 11 | ## Mirroring 12 | 13 | TODO 14 | -------------------------------------------------------------------------------- /examples/colour-uniform/data/quad.vert: -------------------------------------------------------------------------------- 1 | #version 450 2 | #extension GL_ARB_separate_shader_objects : enable 3 | 4 | layout(constant_id = 0) const float scale = 1.2f; 5 | 6 | layout(location = 0) in vec2 a_pos; 7 | layout(location = 1) in vec2 a_uv; 8 | layout(location = 0) out vec2 v_uv; 9 | 10 | out gl_PerVertex { 11 | vec4 gl_Position; 12 | }; 13 | 14 | void main() { 15 | v_uv = a_uv; 16 | gl_Position = vec4(scale * a_pos, 0.0, 1.0); 17 | } 18 | -------------------------------------------------------------------------------- /reftests/data/vertex-offset.vert: -------------------------------------------------------------------------------- 1 | #version 450 2 | #extension GL_ARB_separate_shader_objects : enable 3 | 4 | layout(location = 0) in uvec4 a_color; 5 | layout(location = 0) out flat uvec4 v_color; 6 | 7 | void main() { 8 | vec2 pos = vec2(0.0); 9 | if (gl_VertexIndex==0) pos = vec2(-1.0, -3.0); 10 | if (gl_VertexIndex==1) pos = vec2(3.0, 1.0); 11 | if (gl_VertexIndex==2) pos = vec2(-1.0, 1.0); 12 | gl_Position = vec4(pos, 0.0, 1.0); 13 | v_color = a_color; 14 | } 15 | -------------------------------------------------------------------------------- /src/backend/dx11/README.md: -------------------------------------------------------------------------------- 1 | # gfx_device_dx11 2 | 3 | [DX11](https://msdn.microsoft.com/en-us/library/windows/desktop/ff476080(v=vs.85).aspx) backend for gfx. 4 | 5 | ## Normalized Coordinates 6 | 7 | Render | Depth | Texture 8 | -------|-------|-------- 9 | ![render_coordinates](../../../info/gl_render_coordinates.png) | ![depth_coordinates](../../../info/dx_depth_coordinates.png) | ![texture_coordinates](../../../info/dx_texture_coordinates.png) 10 | 11 | ## Mirroring 12 | 13 | TODO 14 | -------------------------------------------------------------------------------- /src/backend/empty/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "gfx-backend-empty" 3 | version = "0.1.0" 4 | description = "Empty backend for gfx-rs" 5 | license = "MIT OR Apache-2.0" 6 | authors = ["The Gfx-rs Developers"] 7 | documentation = "https://docs.rs/gfx-backend-empty" 8 | workspace = "../../.." 9 | edition = "2018" 10 | 11 | [lib] 12 | name = "gfx_backend_empty" 13 | 14 | [dependencies] 15 | gfx-hal = { path = "../../hal", version = "0.1" } 16 | winit = { version = "0.18", optional = true } 17 | -------------------------------------------------------------------------------- /src/backend/vulkan/README.md: -------------------------------------------------------------------------------- 1 | # gfx-backend-vulkan 2 | 3 | [Vulkan](https://www.khronos.org/vulkan/) backend for gfx-rs. 4 | 5 | ## Normalized Coordinates 6 | 7 | Render | Depth | Texture 8 | -------|-------|-------- 9 | ![render_coordinates](../../../info/vk_render_coordinates.png) | ![depth_coordinates](../../../info/dx_depth_coordinates.png) | ![texture_coordinates](../../../info/dx_texture_coordinates.png) 10 | 11 | ## Mirroring 12 | 13 | HAL is modelled after Vulkan, so everything should be 1:1. 14 | -------------------------------------------------------------------------------- /examples/colour-uniform/data/quad.frag: -------------------------------------------------------------------------------- 1 | #version 450 2 | #extension GL_ARB_separate_shader_objects : enable 3 | 4 | layout(location = 0) in vec2 v_uv; 5 | layout(location = 0) out vec4 target0; 6 | 7 | layout(set = 0, binding = 0) uniform texture2D u_texture; 8 | layout(set = 0, binding = 1) uniform sampler u_sampler; 9 | 10 | layout(set = 1, binding = 0) uniform UBOCol { 11 | vec4 color; 12 | } color_dat; 13 | 14 | void main() { 15 | target0 = texture(sampler2D(u_texture, u_sampler), v_uv) * color_dat.color; 16 | } 17 | -------------------------------------------------------------------------------- /src/auxil/range-alloc/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "range-alloc" 3 | version = "0.1.0" 4 | description = "Generic range allocator used by gfx-rs backends" 5 | homepage = "https://github.com/gfx-rs/gfx" 6 | repository = "https://github.com/gfx-rs/gfx" 7 | keywords = ["allocator"] 8 | license = "MIT OR Apache-2.0" 9 | authors = ["The Gfx-rs Developers"] 10 | documentation = "https://docs.rs/range-alloc" 11 | categories = ["memory-management"] 12 | workspace = "../../../" 13 | edition = "2018" 14 | 15 | [lib] 16 | name = "range_alloc" 17 | -------------------------------------------------------------------------------- /examples/README.md: -------------------------------------------------------------------------------- 1 | # Examples 2 | 3 | This directory contains a collection of examples which use the various gfx APIs. 4 | 5 | The examples are split across three directories, each pertaining to the gfx API they are using. 6 | 7 | To run the examples, set your working directory to the examples directory and execute 8 | `cargo run --bin --features=`, where `` is the example you want to run and `` is the backend you would like to use (`vulkan`, `dx12`, `metal`, or `gl`). 9 | 10 | For example, to run the `quad` example on the `vulkan` backend, try: 11 | 12 | cd examples 13 | cargo run --bin quad --features=vulkan 14 | 15 | If you run the examples for the first time, it may take some time because all dependencies must be compiled too. 16 | -------------------------------------------------------------------------------- /src/backend/gl/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "gfx-backend-gl" 3 | version = "0.1.0" 4 | description = "OpenGL backend for gfx-rs" 5 | homepage = "https://github.com/gfx-rs/gfx" 6 | repository = "https://github.com/gfx-rs/gfx" 7 | keywords = ["graphics", "gamedev"] 8 | license = "MIT OR Apache-2.0" 9 | authors = ["The Gfx-rs Developers"] 10 | readme = "README.md" 11 | documentation = "https://docs.rs/gfx-backend-gl" 12 | workspace = "../../.." 13 | edition = "2018" 14 | 15 | [lib] 16 | name = "gfx_backend_gl" 17 | 18 | [features] 19 | default = ["glutin"] 20 | 21 | [dependencies] 22 | bitflags = "1" 23 | log = { version = "0.4" } 24 | gfx_gl = "0.5" 25 | gfx-hal = { path = "../../hal", version = "0.1" } 26 | smallvec = "0.6" 27 | glutin = { version = "0.19", optional = true } 28 | spirv_cross = "0.12.1" 29 | -------------------------------------------------------------------------------- /.github/CONTRIBUTING.md: -------------------------------------------------------------------------------- 1 | ## Contributing to gfx-rs 2 | 3 | ### Communication 4 | 5 | General-purpose chat is on [Gitter](https://gitter.im/gfx-rs/gfx-rs). It has all the project activity displayed on the right side for your convenience. Note that it embeds images and gists automatically, so post bare links with discretion. 6 | 7 | If you've got the code, making a pull request to discuss things on the way may be more efficient than posting it to the chat. 8 | 9 | There is also a [Waffle board](https://waffle.io/gfx-rs/gfx-rs), which you can use conveniently to track the whole picture. 10 | 11 | Finally, feel free to hop on [#rust-gamedev](http://chat.mibbit.com/?server=irc.mozilla.org&channel=%23rust-gamedev). 12 | 13 | ### Code 14 | 15 | gfx-rs adheres to [Rust Coding Guidelines](http://aturon.github.io/). 16 | -------------------------------------------------------------------------------- /src/hal/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "gfx-hal" 3 | version = "0.1.0" 4 | description = "gfx-rs hardware abstraction layer" 5 | homepage = "https://github.com/gfx-rs/gfx" 6 | repository = "https://github.com/gfx-rs/gfx" 7 | keywords = ["graphics"] 8 | license = "MIT OR Apache-2.0" 9 | authors = ["The Gfx-rs Developers"] 10 | documentation = "https://docs.rs/gfx-hal" 11 | workspace = "../.." 12 | edition = "2018" 13 | 14 | [features] 15 | unstable = [] 16 | 17 | [lib] 18 | name = "gfx_hal" 19 | path = "src/lib.rs" 20 | 21 | [dependencies] 22 | bitflags = "1.0" 23 | mint = { version = "0.5", optional = true } 24 | failure = "0.1" 25 | serde = { version = "1", features = ["serde_derive"], optional = true } 26 | fxhash = "0.2.1" 27 | 28 | [dev-dependencies] 29 | gfx-backend-empty = { path = "../backend/empty", version = "0.1" } 30 | -------------------------------------------------------------------------------- /src/backend/dx12/shaders/blit.hlsl: -------------------------------------------------------------------------------- 1 | 2 | Texture2DArray BlitSource : register(t0); 3 | SamplerState BlitSampler : register(s0); 4 | 5 | cbuffer Region : register(b0) { 6 | float2 offset; 7 | float2 extent; 8 | float z; 9 | float level; 10 | }; 11 | 12 | struct VsOutput { 13 | float4 pos: SV_POSITION; 14 | float4 uv: TEXCOORD0; 15 | }; 16 | 17 | // Create a screen filling triangle 18 | VsOutput vs_blit_2d(uint id: SV_VertexID) { 19 | float2 coord = float2((id << 1) & 2, id & 2); 20 | VsOutput output = { 21 | float4(float2(-1.0, 1.0) + coord * float2(2.0, -2.0), 0.0, 1.0), 22 | float4(offset + coord * extent, z, level) 23 | }; 24 | return output; 25 | } 26 | 27 | float4 ps_blit_2d(VsOutput input) : SV_TARGET { 28 | return BlitSource.SampleLevel(BlitSampler, input.uv.xyz, input.uv.w); 29 | } 30 | -------------------------------------------------------------------------------- /src/backend/dx11/shaders/clear.hlsl: -------------------------------------------------------------------------------- 1 | cbuffer ClearColorF32 : register(b0) { float4 ClearF32; }; 2 | cbuffer ClearColorU32 : register(b0) { uint4 ClearU32; }; 3 | cbuffer ClearColorI32 : register(b0) { int4 ClearI32; }; 4 | cbuffer ClearColorDepth : register(b0) { float ClearDepth; }; 5 | 6 | // fullscreen triangle 7 | float4 vs_partial_clear(uint id : SV_VertexID) : SV_Position 8 | { 9 | return float4( 10 | float(id / 2) * 4.0 - 1.0, 11 | float(id % 2) * 4.0 - 1.0, 12 | 0.0, 13 | 1.0 14 | ); 15 | } 16 | 17 | // TODO: send constants through VS as flat attributes 18 | float4 ps_partial_clear_float() : SV_Target0 { return ClearF32; } 19 | uint4 ps_partial_clear_uint() : SV_Target0 { return ClearU32; } 20 | int4 ps_partial_clear_int() : SV_Target0 { return ClearI32; } 21 | float ps_partial_clear_depth() : SV_Depth { return ClearDepth; } 22 | void ps_partial_clear_stencil() { } 23 | -------------------------------------------------------------------------------- /src/backend/metal/shaders/fill.metal: -------------------------------------------------------------------------------- 1 | #include 2 | using namespace metal; 3 | 4 | typedef struct { 5 | uint value; 6 | uint length; 7 | } FillBufferValue; 8 | 9 | kernel void cs_fill_buffer( 10 | device uint *buffer [[ buffer(0) ]], 11 | constant FillBufferValue &fill [[ buffer(1) ]], 12 | uint index [[ thread_position_in_grid ]] 13 | ) { 14 | if (index < fill.length) { 15 | buffer[index] = fill.value; 16 | } 17 | } 18 | 19 | typedef struct { 20 | uint size; 21 | uint offsets; 22 | } CopyBufferRange; 23 | 24 | kernel void cs_copy_buffer( 25 | device uchar *dest [[ buffer(0) ]], 26 | device uchar *source [[ buffer(1) ]], 27 | constant CopyBufferRange &range [[ buffer(2) ]], 28 | uint index [[ thread_position_in_grid ]] 29 | ) { 30 | if (index < range.size) { 31 | dest[(range.offsets>>16) + index] = source[(range.offsets & 0xFFFF) + index]; 32 | } 33 | } 34 | -------------------------------------------------------------------------------- /CHANGELOG.md: -------------------------------------------------------------------------------- 1 | # Change Log 2 | 3 | ### backend-dx11-0.1.1 (05-03-2019) 4 | - fixed buffer bind flags 5 | - synchronization of disjoint CB across copy operations 6 | - depth texture views 7 | 8 | ### backend-dx12-0.1.2 (04-03-2019) 9 | - typeless formats for textures 10 | - fixed vertex buffer binding 11 | - fixed non-array views of array textures 12 | 13 | ### backend-metal-0.1.1 (21-02-2019) 14 | - secondary command buffers 15 | - multiple iOS fixes 16 | - fixed surface dimensions 17 | 18 | ### backend-dx12-0.1.1 (04-02-2019) 19 | - `get_fence_status` 20 | 21 | ### backend-empty-0.1.0 (04-02-2019) 22 | - dummy surface creation 23 | 24 | ## hal-0.1.0 (27-12-2018) 25 | - `gfx-hal`: graphics hardware abstraction layer 26 | - `gfx-backend-*`: Vulkan, D3D12, D3D11, Metal, and GL 27 | - `range-alloc`: helper struct to manage ranges 28 | - unsafe qualifiers on all the API methods 29 | - non-clonable command buffers and resources -------------------------------------------------------------------------------- /examples/compute/shader/collatz.comp: -------------------------------------------------------------------------------- 1 | #version 450 2 | layout(local_size_x = 1) in; 3 | 4 | layout(set = 0, binding = 0) buffer PrimeIndices { 5 | uint[] indices; 6 | }; // this is used as both input and output for convenience 7 | 8 | // The Collatz Conjecture states that for any integer n: 9 | // If n is even, n = n/2 10 | // If n is odd, n = 3n+1 11 | // And repeat this process for each new n, you will always eventually reach 1. 12 | // Though the conjecture has not been proven, no counterexample has ever been found. 13 | // This function returns how many times this recurrence needs to be applied to reach 1. 14 | uint collatz_iterations(uint n) { 15 | uint i = 0; 16 | while(n != 1) { 17 | if (mod(n, 2) == 0) { 18 | n = n / 2; 19 | } 20 | else { 21 | n = (3 * n) + 1; 22 | } 23 | i++; 24 | } 25 | return i; 26 | } 27 | 28 | void main() { 29 | uint index = gl_GlobalInvocationID.x; 30 | indices[index] = collatz_iterations(indices[index]); 31 | } 32 | -------------------------------------------------------------------------------- /src/backend/gl/README.md: -------------------------------------------------------------------------------- 1 | # gfx-backend-gl 2 | 3 | [OpenGL](https://www.khronos.org/opengl/) backend for gfx. 4 | 5 | ## Normalized Coordinates 6 | 7 | Render | Depth | Texture 8 | -------|-------|-------- 9 | ![render_coordinates](../../../info/gl_render_coordinates.png) | ![depth_coordinates](../../../info/gl_depth_coordinates.png) | ![texture_coordinates](../../../info/gl_texture_coordinates.png) 10 | 11 | ## GLSL Mirroring 12 | 13 | Texture Kind | GLSL sampler 14 | -------------|------------- 15 | `D1` | *g*sampler1D, sampler1DShadow 16 | `D1Array` | *g*sampler1DArray, sampler1DArrayShadow 17 | `D2` | *g*sampler2D, *g*sampler2DMS, sampler2DShadow 18 | `D2Array` | *g*sampler2DArray, *g*sampler2DMSArray, sampler2DArrayShadow 19 | `D3` | *g*sampler3D 20 | `Cube` | *g*samplerCube, samplerCubeShadow 21 | `CubeArray` | *g*samplerCubeArray, samplerCubeArrayShadow 22 | 23 | Buffer resource views are seen as *g*samplerBuffer. 24 | 25 | Rust basic type | GLSL (1.3 and above) 26 | ----------------|--------------------- 27 | i32 | int 28 | u32 | uint 29 | f32 | float 30 | f64 | double 31 | -------------------------------------------------------------------------------- /src/hal/src/pso/compute.rs: -------------------------------------------------------------------------------- 1 | //! Compute pipeline descriptor. 2 | 3 | use super::{BasePipeline, EntryPoint, PipelineCreationFlags}; 4 | use crate::Backend; 5 | 6 | /// A description of the data needed to construct a compute pipeline. 7 | #[derive(Debug)] 8 | pub struct ComputePipelineDesc<'a, B: Backend> { 9 | /// The shader entry point that performs the computation. 10 | pub shader: EntryPoint<'a, B>, 11 | /// Pipeline layout. 12 | pub layout: &'a B::PipelineLayout, 13 | /// Any flags necessary for the pipeline creation. 14 | pub flags: PipelineCreationFlags, 15 | /// The parent pipeline to this one, if any. 16 | pub parent: BasePipeline<'a, B::ComputePipeline>, 17 | } 18 | 19 | impl<'a, B: Backend> ComputePipelineDesc<'a, B> { 20 | /// Create a new empty PSO descriptor. 21 | pub fn new(shader: EntryPoint<'a, B>, layout: &'a B::PipelineLayout) -> Self { 22 | ComputePipelineDesc { 23 | shader, 24 | layout, 25 | flags: PipelineCreationFlags::empty(), 26 | parent: BasePipeline::None, 27 | } 28 | } 29 | } 30 | -------------------------------------------------------------------------------- /LICENSE-MIT: -------------------------------------------------------------------------------- 1 | Permission is hereby granted, free of charge, to any 2 | person obtaining a copy of this software and associated 3 | documentation files (the "Software"), to deal in the 4 | Software without restriction, including without 5 | limitation the rights to use, copy, modify, merge, 6 | publish, distribute, sublicense, and/or sell copies of 7 | the Software, and to permit persons to whom the Software 8 | is furnished to do so, subject to the following 9 | conditions: 10 | 11 | The above copyright notice and this permission notice 12 | shall be included in all copies or substantial portions 13 | of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF 16 | ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED 17 | TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A 18 | PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT 19 | SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY 20 | CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION 21 | OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR 22 | IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER 23 | DEALINGS IN THE SOFTWARE. 24 | -------------------------------------------------------------------------------- /reftests/scenes/compute.ron: -------------------------------------------------------------------------------- 1 | ( 2 | resources: { 3 | "buffer.output": Buffer( 4 | size: 4, 5 | usage: (bits: 0x20), //STORAGE 6 | ), 7 | "desc-layout": DescriptorSetLayout( 8 | bindings: [ 9 | ( 10 | binding: 0, 11 | ty: StorageBuffer, 12 | count: 1, 13 | stage_flags: (bits: 0x20), //COMPUTE 14 | immutable_samplers: false, 15 | ), 16 | ], 17 | ), 18 | "desc-pool": DescriptorPool( 19 | capacity: 1, 20 | ranges: [ 21 | ( 22 | ty: StorageBuffer, 23 | count: 1, 24 | ), 25 | ], 26 | ), 27 | "desc": DescriptorSet( 28 | layout: "desc-layout", 29 | pool: "desc-pool", 30 | data: [ 31 | Buffers(["buffer.output"]), 32 | ], 33 | ), 34 | "pipe-layout": PipelineLayout( 35 | set_layouts: ["desc-layout"], 36 | push_constant_ranges: [], 37 | ), 38 | "shader": Shader("fill.comp"), 39 | "pipe": ComputePipeline( 40 | shader: "shader", 41 | layout: "pipe-layout", 42 | ), 43 | }, 44 | jobs: { 45 | "fill": Compute( 46 | pipeline: "pipe", 47 | descriptor_sets: ["desc"], 48 | dispatch: (1, 1, 1), 49 | ), 50 | } 51 | ) 52 | -------------------------------------------------------------------------------- /src/backend/dx12/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "gfx-backend-dx12" 3 | version = "0.1.0" 4 | description = "DirectX-12 API backend for gfx-rs" 5 | homepage = "https://github.com/gfx-rs/gfx" 6 | repository = "https://github.com/gfx-rs/gfx" 7 | keywords = ["graphics", "gamedev"] 8 | license = "MIT OR Apache-2.0" 9 | authors = ["The Gfx-rs Developers"] 10 | readme = "README.md" 11 | documentation = "https://docs.rs/gfx-backend-dx12" 12 | workspace = "../../.." 13 | 14 | [features] 15 | default = ["winit"] 16 | 17 | [lib] 18 | name = "gfx_backend_dx12" 19 | 20 | [dependencies] 21 | gfx-hal = { path = "../../hal", version = "0.1" } 22 | range-alloc = { path = "../../auxil/range-alloc", version = "0.1" } 23 | bitflags = "1" 24 | derivative = "1" 25 | d3d12 = "0.1" 26 | log = { version = "0.4" } 27 | smallvec = "0.6" 28 | spirv_cross = "0.12.1" 29 | winapi = { version = "0.3", features = ["basetsd","d3d12","d3d12sdklayers","d3d12shader","d3dcommon","d3dcompiler","dxgi1_2","dxgi1_3","dxgi1_4","dxgi1_6","dxgidebug","dxgiformat","dxgitype","handleapi","minwindef","synchapi","unknwnbase","winbase","windef","winerror","winnt","winuser"] } 30 | winit = { version = "0.18", optional = true } 31 | -------------------------------------------------------------------------------- /src/backend/dx11/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "gfx-backend-dx11" 3 | version = "0.1.1" 4 | description = "DirectX-11 API backend for gfx-rs" 5 | homepage = "https://github.com/gfx-rs/gfx" 6 | repository = "https://github.com/gfx-rs/gfx" 7 | keywords = ["graphics", "gamedev"] 8 | license = "MIT OR Apache-2.0" 9 | authors = ["The Gfx-rs Developers"] 10 | readme = "README.md" 11 | documentation = "https://docs.rs/gfx-backend-dx11" 12 | workspace = "../../.." 13 | 14 | [features] 15 | default = ["winit"] 16 | 17 | [lib] 18 | name = "gfx_backend_dx11" 19 | 20 | [dependencies] 21 | gfx-hal = { path = "../../hal", version = "0.1" } 22 | range-alloc = { path = "../../auxil/range-alloc", version = "0.1" } 23 | bitflags = "1" 24 | derivative = "1" 25 | log = { version = "0.4" } 26 | smallvec = "0.6" 27 | spirv_cross = "0.12.1" 28 | parking_lot = "0.7" 29 | winapi = { version = "0.3", features = ["basetsd","d3d11", "d3d11sdklayers", "d3dcommon","d3dcompiler","dxgi1_2","dxgi1_3","dxgi1_4", "dxgi1_5", "dxgiformat","dxgitype","handleapi","minwindef","synchapi","unknwnbase","winbase","windef","winerror","winnt","winuser"] } 30 | winit = { version = "0.18", optional = true } 31 | wio = "0.2" 32 | -------------------------------------------------------------------------------- /src/backend/metal/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "gfx-backend-metal" 3 | version = "0.1.0" 4 | description = "Metal API backend for gfx-rs" 5 | homepage = "https://github.com/gfx-rs/gfx" 6 | repository = "https://github.com/gfx-rs/gfx" 7 | keywords = ["graphics", "gamedev"] 8 | license = "MIT OR Apache-2.0" 9 | authors = ["The Gfx-rs Developers"] 10 | readme = "README.md" 11 | documentation = "https://docs.rs/gfx-backend-metal" 12 | workspace = "../../.." 13 | edition = "2018" 14 | 15 | [features] 16 | default = ["winit"] 17 | auto-capture = [] 18 | 19 | [lib] 20 | name = "gfx_backend_metal" 21 | 22 | [dependencies] 23 | hal = { path = "../../hal", version = "0.1", package = "gfx-hal" } 24 | range-alloc = { path = "../../auxil/range-alloc", version = "0.1" } 25 | arrayvec = "0.4" 26 | bitflags = "1.0" 27 | copyless = "0.1" 28 | log = { version = "0.4" } 29 | winit = { version = "0.18", optional = true } 30 | dispatch = { version = "0.1", optional = true } 31 | metal = { version = "0.14.0", features = ["private"] } 32 | foreign-types = "0.3" 33 | objc = "0.2.5" 34 | block = "0.1" 35 | cocoa = "0.18" 36 | core-graphics = "0.17" 37 | smallvec = "0.6" 38 | spirv_cross = "0.12.1" 39 | parking_lot = "0.6.3" 40 | storage-map = "0.1.2" 41 | -------------------------------------------------------------------------------- /src/backend/vulkan/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "gfx-backend-vulkan" 3 | version = "0.1.0" 4 | description = "Vulkan API backend for gfx-rs" 5 | homepage = "https://github.com/gfx-rs/gfx" 6 | repository = "https://github.com/gfx-rs/gfx" 7 | keywords = ["graphics", "gamedev"] 8 | license = "MIT OR Apache-2.0" 9 | authors = ["The Gfx-rs Developers"] 10 | readme = "README.md" 11 | documentation = "https://docs.rs/gfx-backend-vulkan" 12 | workspace = "../../.." 13 | 14 | [features] 15 | default = ["winit"] 16 | use-rtld-next = ["shared_library"] 17 | 18 | [lib] 19 | name = "gfx_backend_vulkan" 20 | 21 | [dependencies] 22 | byteorder = "1" 23 | log = { version = "0.4" } 24 | lazy_static = "1" 25 | shared_library = { version = "0.1.9", optional = true } 26 | ash = "0.28.0" 27 | gfx-hal = { path = "../../hal", version = "0.1" } 28 | smallvec = "0.6" 29 | winit = { version = "0.18", optional = true } 30 | 31 | [target.'cfg(windows)'.dependencies] 32 | winapi = { version = "0.3", features = ["libloaderapi", "windef", "winuser"] } 33 | 34 | [target.'cfg(all(unix, not(target_os = "macos"), not(target_os = "ios"), not(target_os = "android")))'.dependencies] 35 | x11 = { version = "2.15", features = ["xlib"]} 36 | xcb = { version = "0.8" } 37 | -------------------------------------------------------------------------------- /info/getting_started.md: -------------------------------------------------------------------------------- 1 | # Getting Started 2 | 3 | ## macOS Dependencies 4 | 5 | Install the newest XCode from the App Store. This installs the required `metal` developer tools. 6 | 7 | ## Vulkan Dependencies 8 | 9 | First, install the x11 and Vulkan dev libraries. 10 | 11 | ```bash 12 | # Fedora 13 | sudo dnf install -y libX11-devel vulkan 14 | # Ubuntu 15 | sudo apt install -y libx11-dev libvulkan-dev libxcb1-dev xorg-dev 16 | ``` 17 | 18 | For Linux, a Vulkan compatible driver must also be installed. For example, the open source `mesa-vulkan-drivers` for Intel or Radeon gpu's. The proprietary Nvidia drivers support Vulkan out of the box but, as of time of writing, Nouveau users are currenty limited to OpenGL. 19 | 20 | ## Usage 21 | 22 | The gfx repository contains a number of examples. Those examples are automatically downloaded when the repository is cloned. 23 | 24 | To run an example, simply use `cargo run` and specify the backend with `--features {backend}` (where `{backend}` is one of `vulkan`, `dx12`, `metal`, or `gl`). For example: 25 | 26 | ```bash 27 | git clone https://github.com/gfx-rs/gfx 28 | cd gfx/examples 29 | # macOS 30 | cargo run --bin quad --features metal 31 | # vulkan 32 | cargo run --bin quad --features vulkan 33 | # Windows 34 | cargo run --bin compute --features dx12 1 2 3 4 35 | ``` 36 | 37 | This would run the `quad` example using the Vulkan backend, and then the `compute` example using the Direct3D 12 backend. 38 | -------------------------------------------------------------------------------- /src/hal/src/range.rs: -------------------------------------------------------------------------------- 1 | //! Generic range type abstraction that allows 2 | //! ranges to be handled a little more generically. 3 | 4 | use std::ops::{Range, RangeFrom, RangeFull, RangeTo}; 5 | 6 | /// Abstracts the std range types. 7 | /// 8 | /// Based upon the nightly `RangeArgument` trait. 9 | pub trait RangeArg { 10 | /// Start index bound. 11 | fn start(&self) -> Option<&T>; 12 | /// End index bound. 13 | fn end(&self) -> Option<&T>; 14 | } 15 | 16 | impl RangeArg for Range { 17 | fn start(&self) -> Option<&T> { 18 | Some(&self.start) 19 | } 20 | fn end(&self) -> Option<&T> { 21 | Some(&self.end) 22 | } 23 | } 24 | 25 | impl RangeArg for RangeTo { 26 | fn start(&self) -> Option<&T> { 27 | None 28 | } 29 | fn end(&self) -> Option<&T> { 30 | Some(&self.end) 31 | } 32 | } 33 | 34 | impl RangeArg for RangeFrom { 35 | fn start(&self) -> Option<&T> { 36 | Some(&self.start) 37 | } 38 | fn end(&self) -> Option<&T> { 39 | None 40 | } 41 | } 42 | 43 | impl RangeArg for RangeFull { 44 | fn start(&self) -> Option<&T> { 45 | None 46 | } 47 | fn end(&self) -> Option<&T> { 48 | None 49 | } 50 | } 51 | 52 | impl RangeArg for (Option, Option) { 53 | fn start(&self) -> Option<&T> { 54 | self.0.as_ref() 55 | } 56 | fn end(&self) -> Option<&T> { 57 | self.1.as_ref() 58 | } 59 | } 60 | -------------------------------------------------------------------------------- /src/hal/src/backend.rs: -------------------------------------------------------------------------------- 1 | //! Functionality only required for backend implementations. 2 | 3 | use crate::queue::{QueueFamily, Queues}; 4 | use crate::Backend; 5 | 6 | use std::collections::HashMap; 7 | use std::hash::BuildHasherDefault; 8 | 9 | use fxhash::FxHasher; 10 | 11 | /// Bare-metal queue group. 12 | /// 13 | /// Denotes all queues created from one queue family. 14 | pub struct RawQueueGroup { 15 | pub family: B::QueueFamily, 16 | pub queues: Vec, 17 | } 18 | 19 | impl RawQueueGroup { 20 | /// Create a new, empty queue group for a queue family. 21 | pub fn new(family: B::QueueFamily) -> Self { 22 | RawQueueGroup { 23 | family, 24 | queues: Vec::new(), 25 | } 26 | } 27 | 28 | /// Add a command queue to the group. 29 | /// 30 | /// The queue needs to be created from this queue family. 31 | /// 32 | /// # Panics 33 | /// 34 | /// Panics if more command queues are added than exposed by the queue family. 35 | pub fn add_queue(&mut self, queue: B::CommandQueue) { 36 | assert!(self.queues.len() < self.family.max_queues()); 37 | self.queues.push(queue); 38 | } 39 | } 40 | 41 | impl Queues { 42 | /// Create a new collection of queues. 43 | pub fn new(queues: Vec>) -> Self { 44 | Queues(queues) 45 | } 46 | } 47 | 48 | /// Fast hash map used internally. 49 | pub type FastHashMap = HashMap>; 50 | -------------------------------------------------------------------------------- /examples/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "hal-examples" 3 | version = "0.1.0" 4 | publish = false 5 | workspace = ".." 6 | edition = "2018" 7 | 8 | [features] 9 | default = [] 10 | metal = ["gfx-backend-metal"] 11 | gl = ["gfx-backend-gl"] 12 | dx11 = ["gfx-backend-dx11"] 13 | dx12 = ["gfx-backend-dx12"] 14 | vulkan = ["gfx-backend-vulkan"] 15 | unstable = [] 16 | 17 | [[bin]] 18 | name = "colour-uniform" 19 | path = "colour-uniform/main.rs" 20 | 21 | [[bin]] 22 | name = "quad" 23 | path = "quad/main.rs" 24 | 25 | [[bin]] 26 | name = "compute" 27 | path = "compute/main.rs" 28 | 29 | [dependencies] 30 | env_logger = "0.5" 31 | image = "0.19" 32 | log = "0.4" 33 | winit = "0.18" 34 | glsl-to-spirv = "0.1.4" 35 | gfx-hal = { path = "../src/hal", version = "0.1" } 36 | gfx-backend-empty = { path = "../src/backend/empty", version = "0.1" } 37 | 38 | [dependencies.gfx-backend-gl] 39 | path = "../src/backend/gl" 40 | version = "0.1" 41 | features = ["glutin"] 42 | optional = true 43 | 44 | [dependencies.gfx-backend-vulkan] 45 | path = "../src/backend/vulkan" 46 | version = "0.1" 47 | optional = true 48 | 49 | [target.'cfg(any(target_os = "macos", all(target_os = "ios", target_arch = "aarch64")))'.dependencies.gfx-backend-metal] 50 | path = "../src/backend/metal" 51 | version = "0.1" 52 | optional = true 53 | 54 | [target.'cfg(windows)'.dependencies.gfx-backend-dx11] 55 | path = "../src/backend/dx11" 56 | version = "0.1" 57 | optional = true 58 | 59 | [target.'cfg(windows)'.dependencies.gfx-backend-dx12] 60 | path = "../src/backend/dx12" 61 | version = "0.1" 62 | optional = true 63 | -------------------------------------------------------------------------------- /src/warden/examples/basic.rs: -------------------------------------------------------------------------------- 1 | extern crate gfx_backend_gl as gl; 2 | extern crate gfx_hal as hal; 3 | extern crate gfx_warden as warden; 4 | extern crate ron; 5 | extern crate serde; 6 | 7 | use std::fs::File; 8 | use std::io::Read; 9 | use std::path::PathBuf; 10 | 11 | use hal::Instance; 12 | use ron::de::Deserializer; 13 | use serde::de::Deserialize; 14 | 15 | fn main() { 16 | let base_path = PathBuf::from(concat!(env!("CARGO_MANIFEST_DIR"), "/../../reftests",)); 17 | 18 | let raw_scene = { 19 | let mut raw_data = Vec::new(); 20 | File::open(base_path.join("scenes/basic.ron")) 21 | .unwrap() 22 | .read_to_end(&mut raw_data) 23 | .unwrap(); 24 | let mut deserializer = Deserializer::from_bytes(&raw_data).unwrap(); 25 | warden::raw::Scene::deserialize(&mut deserializer).unwrap() 26 | }; 27 | 28 | let events_loop = gl::glutin::EventsLoop::new(); 29 | let window = gl::glutin::GlWindow::new( 30 | gl::glutin::WindowBuilder::new(), 31 | gl::glutin::ContextBuilder::new().with_gl_profile(gl::glutin::GlProfile::Core), 32 | &events_loop, 33 | ) 34 | .unwrap(); 35 | let instance = gl::Surface::from_window(window); 36 | 37 | let adapter = instance.enumerate_adapters().swap_remove(0); 38 | let mut scene = 39 | warden::gpu::Scene::::new(adapter, &raw_scene, base_path.join("data")) 40 | .unwrap(); 41 | scene.run(Some("empty")); 42 | let guard = scene.fetch_image("image.color"); 43 | println!("row: {:?}", guard.row(0)); 44 | } 45 | -------------------------------------------------------------------------------- /src/backend/dx11/shaders/blit.hlsl: -------------------------------------------------------------------------------- 1 | cbuffer Region : register(b0) { 2 | float2 offset; 3 | float2 extent; 4 | float z; 5 | float level; 6 | }; 7 | 8 | struct VsOutput { 9 | float4 pos: SV_POSITION; 10 | float4 uv: TEXCOORD0; 11 | }; 12 | 13 | // Create a screen filling triangle 14 | VsOutput vs_blit_2d(uint id: SV_VertexID) { 15 | float2 coord = float2((id << 1) & 2, id & 2); 16 | VsOutput output = { 17 | float4(float2(-1.0, 1.0) + coord * float2(2.0, -2.0), 0.0, 1.0), 18 | float4(offset + coord * extent, z, level) 19 | }; 20 | return output; 21 | } 22 | 23 | SamplerState BlitSampler : register(s0); 24 | 25 | Texture2DArray BlitSrc_Uint : register(t0); 26 | Texture2DArray BlitSrc_Sint : register(t0); 27 | Texture2DArray BlitSrc_Float : register(t0); 28 | 29 | // TODO: get rid of GetDimensions call 30 | uint4 Nearest_Uint(float4 uv) 31 | { 32 | float4 size; 33 | BlitSrc_Uint.GetDimensions(0, size.x, size.y, size.z, size.w); 34 | 35 | float2 pix = uv.xy * size.xy; 36 | 37 | return BlitSrc_Uint.Load(int4(int2(pix), uv.zw)); 38 | } 39 | 40 | int4 Nearest_Sint(float4 uv) 41 | { 42 | float4 size; 43 | BlitSrc_Sint.GetDimensions(0, size.x, size.y, size.z, size.w); 44 | 45 | float2 pix = uv.xy * size.xy; 46 | 47 | return BlitSrc_Sint.Load(int4(int2(pix), uv.zw)); 48 | } 49 | 50 | uint4 ps_blit_2d_uint(VsOutput input) : SV_Target 51 | { 52 | return Nearest_Uint(input.uv); 53 | } 54 | 55 | int4 ps_blit_2d_int(VsOutput input) : SV_Target 56 | { 57 | return Nearest_Sint(input.uv); 58 | } 59 | 60 | float4 ps_blit_2d_float(VsOutput input) : SV_Target 61 | { 62 | return BlitSrc_Float.SampleLevel(BlitSampler, input.uv.xyz, input.uv.w); 63 | } 64 | -------------------------------------------------------------------------------- /src/warden/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "gfx-warden" 3 | version = "0.1.0" 4 | description = "gfx-rs reftest framework" 5 | homepage = "https://github.com/gfx-rs/gfx" 6 | repository = "https://github.com/gfx-rs/gfx" 7 | keywords = ["graphics", "gamedev"] 8 | license = "MIT OR Apache-2.0" 9 | authors = ["The Gfx-rs Developers"] 10 | readme = "../../README.md" 11 | documentation = "https://docs.rs/gfx-render" 12 | categories = ["rendering::graphics-api"] 13 | workspace = "../.." 14 | edition = "2018" 15 | 16 | [lib] 17 | name = "gfx_warden" 18 | 19 | [features] 20 | default = ["glsl-to-spirv"] 21 | vulkan = ["gfx-backend-vulkan"] 22 | dx12 = ["gfx-backend-dx12"] 23 | metal = ["gfx-backend-metal"] 24 | gl = ["gfx-backend-gl"] 25 | gl-headless = ["gfx-backend-gl"] # "glsl-to-spirv" 26 | 27 | #TODO: keep Warden backend-agnostic? 28 | 29 | [dependencies] 30 | failure = "0.1" 31 | gfx-hal = { path = "../hal", version = "0.1", features = ["serde"] } 32 | log = "0.4" 33 | ron = "0.2.1" 34 | serde = { version = "1", features = ["serde_derive"] } 35 | env_logger = { version = "0.5", optional = true } 36 | glsl-to-spirv = { version = "0.1", optional = true } 37 | 38 | [dependencies.gfx-backend-vulkan] 39 | path = "../../src/backend/vulkan" 40 | version = "0.1" 41 | optional = true 42 | 43 | [target.'cfg(windows)'.dependencies.gfx-backend-dx12] 44 | path = "../../src/backend/dx12" 45 | version = "0.1" 46 | optional = true 47 | 48 | [target.'cfg(any(target_os = "macos", all(target_os = "ios", target_arch = "aarch64")))'.dependencies.gfx-backend-metal] 49 | path = "../../src/backend/metal" 50 | version = "0.1" 51 | features = ["auto-capture"] 52 | optional = true 53 | 54 | [dependencies.gfx-backend-gl] 55 | path = "../../src/backend/gl" 56 | version = "0.1" 57 | features = ["glutin"] 58 | optional = true 59 | 60 | [[example]] 61 | name = "basic" 62 | required-features = ["gl", "glsl-to-spirv"] 63 | -------------------------------------------------------------------------------- /src/backend/vulkan/src/pool.rs: -------------------------------------------------------------------------------- 1 | use ash::version::DeviceV1_0; 2 | use ash::vk; 3 | use smallvec::SmallVec; 4 | use std::ptr; 5 | use std::sync::Arc; 6 | 7 | use command::CommandBuffer; 8 | use conv; 9 | use hal::{command, pool}; 10 | use {Backend, RawDevice}; 11 | 12 | pub struct RawCommandPool { 13 | pub(crate) raw: vk::CommandPool, 14 | pub(crate) device: Arc, 15 | } 16 | 17 | impl pool::RawCommandPool for RawCommandPool { 18 | unsafe fn reset(&mut self) { 19 | assert_eq!( 20 | Ok(()), 21 | self.device 22 | .0 23 | .reset_command_pool(self.raw, vk::CommandPoolResetFlags::empty()) 24 | ); 25 | } 26 | 27 | fn allocate_vec(&mut self, num: usize, level: command::RawLevel) -> Vec { 28 | let info = vk::CommandBufferAllocateInfo { 29 | s_type: vk::StructureType::COMMAND_BUFFER_ALLOCATE_INFO, 30 | p_next: ptr::null(), 31 | command_pool: self.raw, 32 | level: conv::map_command_buffer_level(level), 33 | command_buffer_count: num as u32, 34 | }; 35 | 36 | let device = &self.device; 37 | let cbufs_raw = unsafe { device.0.allocate_command_buffers(&info) } 38 | .expect("Error on command buffer allocation"); 39 | 40 | cbufs_raw 41 | .into_iter() 42 | .map(|buffer| CommandBuffer { 43 | raw: buffer, 44 | device: device.clone(), 45 | }) 46 | .collect() 47 | } 48 | 49 | unsafe fn free(&mut self, cbufs: I) 50 | where 51 | I: IntoIterator, 52 | { 53 | let buffers: SmallVec<[vk::CommandBuffer; 16]> = 54 | cbufs.into_iter().map(|buffer| buffer.raw).collect(); 55 | self.device.0.free_command_buffers(self.raw, &buffers); 56 | } 57 | } 58 | -------------------------------------------------------------------------------- /src/hal/src/command/compute.rs: -------------------------------------------------------------------------------- 1 | //! `CommandBuffer` methods for compute operations. 2 | 3 | use std::borrow::Borrow; 4 | 5 | use super::{CommandBuffer, DescriptorSetOffset, Level, RawCommandBuffer, Shot}; 6 | use crate::buffer::Offset; 7 | use crate::queue::capability::{Compute, Supports}; 8 | use crate::{Backend, WorkGroupCount}; 9 | 10 | impl, S: Shot, L: Level> CommandBuffer { 11 | /// Identical to the `RawCommandBuffer` method of the same name. 12 | pub unsafe fn bind_compute_pipeline(&mut self, pipeline: &B::ComputePipeline) { 13 | self.raw.bind_compute_pipeline(pipeline) 14 | } 15 | 16 | /// Identical to the `RawCommandBuffer` method of the same name. 17 | pub unsafe fn bind_compute_descriptor_sets( 18 | &mut self, 19 | layout: &B::PipelineLayout, 20 | first_set: usize, 21 | sets: I, 22 | offsets: J, 23 | ) where 24 | I: IntoIterator, 25 | I::Item: Borrow, 26 | J: IntoIterator, 27 | J::Item: Borrow, 28 | { 29 | self.raw 30 | .bind_compute_descriptor_sets(layout, first_set, sets, offsets) 31 | } 32 | 33 | /// Identical to the `RawCommandBuffer` method of the same name. 34 | pub unsafe fn dispatch(&mut self, count: WorkGroupCount) { 35 | self.raw.dispatch(count) 36 | } 37 | 38 | /// Identical to the `RawCommandBuffer` method of the same name. 39 | pub unsafe fn dispatch_indirect(&mut self, buffer: &B::Buffer, offset: Offset) { 40 | self.raw.dispatch_indirect(buffer, offset) 41 | } 42 | 43 | /// Identical to the `RawCommandBuffer` method of the same name. 44 | pub unsafe fn push_compute_constants( 45 | &mut self, 46 | layout: &B::PipelineLayout, 47 | offset: u32, 48 | constants: &[u32], 49 | ) { 50 | self.raw.push_compute_constants(layout, offset, constants); 51 | } 52 | } 53 | -------------------------------------------------------------------------------- /info/hal.svg: -------------------------------------------------------------------------------- 1 | 4 | 5 | 6 | 7 | 8 | Hardware Abstraction Layer (HAL) 9 | 10 | 11 | 12 | 13 | 14 | Vulkan 15 | 16 | 17 | 18 | DirectX 12 19 | DirectX 11 20 | 21 | 22 | 23 | Metal 24 | 25 | 26 | 27 | OpenGL 28 | OpenGL ES 29 | WebGL 30 | 31 | 32 | 33 | 34 | -------------------------------------------------------------------------------- /src/backend/metal/shaders/clear.metal: -------------------------------------------------------------------------------- 1 | #include "macros.h" 2 | #include 3 | using namespace metal; 4 | 5 | //TODO: simplified code path for Metal 2.0? 6 | //> Starting in Metal 2.0, the [[color(n)]] and [[raster_order_group(index)]] indices can 7 | //> also be a function constant. The function constant specified as indices for color and 8 | //> raster order group attributes must be a scalar integer type. 9 | 10 | typedef struct { 11 | float4 coords [[attribute(0)]]; 12 | } ClearAttributes; 13 | 14 | typedef struct { 15 | float4 position [[position]]; 16 | uint layer GFX_RENDER_TARGET_ARRAY_INDEX; 17 | } ClearVertexData; 18 | 19 | vertex ClearVertexData vs_clear(ClearAttributes in [[stage_in]]) { 20 | float4 pos = { 0.0, 0.0, in.coords.z, 1.0f }; 21 | pos.xy = in.coords.xy * 2.0 - 1.0; 22 | return ClearVertexData { pos, uint(in.coords.w) }; 23 | } 24 | 25 | 26 | fragment float4 ps_clear0_float( 27 | ClearVertexData in [[stage_in]], 28 | constant float4 &value [[ buffer(0) ]] 29 | ) { 30 | return value; 31 | } 32 | 33 | fragment int4 ps_clear0_int( 34 | ClearVertexData in [[stage_in]], 35 | constant int4 &value [[ buffer(0) ]] 36 | ) { 37 | return value; 38 | } 39 | 40 | fragment uint4 ps_clear0_uint( 41 | ClearVertexData in [[stage_in]], 42 | constant uint4 &value [[ buffer(0) ]] 43 | ) { 44 | return value; 45 | } 46 | 47 | 48 | typedef struct { 49 | float4 color [[color(1)]]; 50 | } Clear1FloatFragment; 51 | 52 | fragment Clear1FloatFragment ps_clear1_float( 53 | ClearVertexData in [[stage_in]], 54 | constant float4 &value [[ buffer(0) ]] 55 | ) { 56 | return Clear1FloatFragment { value }; 57 | } 58 | 59 | typedef struct { 60 | int4 color [[color(1)]]; 61 | } Clear1IntFragment; 62 | 63 | fragment Clear1IntFragment ps_clear1_int( 64 | ClearVertexData in [[stage_in]], 65 | constant int4 &value [[ buffer(0) ]] 66 | ) { 67 | return Clear1IntFragment { value }; 68 | } 69 | 70 | typedef struct { 71 | uint4 color [[color(1)]]; 72 | } Clear1UintFragment; 73 | 74 | fragment Clear1UintFragment ps_clear1_uint( 75 | ClearVertexData in [[stage_in]], 76 | constant uint4 &value [[ buffer(0) ]] 77 | ) { 78 | return Clear1UintFragment { value }; 79 | } 80 | -------------------------------------------------------------------------------- /src/warden/README.md: -------------------------------------------------------------------------------- 1 | # Warden 2 | 3 | Warden is the data-driven reference test framework for gfx-rs Hardware Abstraction Layer (`gfx-hal`), heavily inspired by the Wrench component of [WebRender](https://github.com/servo/webrender/). Warden's main purpose is to run a suite of GPU workloads on all native backends supported by the host platform, then match the results against provided expectations. Both the workloads and expectations are backend-agnostic. The backend discovery and initialization is done by the `reftest` binary. All that needs to be done by a developer is typing `make reftests` from the project root and ensuring that every test passes. 4 | 5 | Warden has two types of definitions: scene and suite. Both are written in [Ron](https://github.com/ron-rs/ron) format, but technically the code should work with any `serde`-enabled format given minimal tweaking. 6 | 7 | ## Scene definition 8 | 9 | A scene consists of a number of resources and jobs that can be run on them. Resources are buffers, images, render passes, and so on. Jobs are sets of either transfer, compute, or graphics operations. The latter is contained within a single render pass. Please refer to [raw.rs](src/raw.rs) for the formal definition of the scene format. Actual reference scenes can be found in [reftests](../../reftests/scenes). 10 | 11 | ### Resource states 12 | 13 | Internally, a scene has a command buffer to fill up all the initial data for resources. This command buffer needs to change the resource access and image layouts, so we establish a convention here by which every resource has an associated "stable" state that the user (and the reftest framework) promises to deliver at the end of each job. 14 | 15 | For images with no source data, the stable layout is `ColorAttachmentOptimal` or `DepthStencilAttachmentOptimal` depending on the format. For sourced images, it's `ShaderReadOnlyOptimal`. 16 | 17 | ## Test suite 18 | 19 | A test suite is just a set of scenes, each with multiple tests. A test is defined as a sequence of jobs being run on the scene and an expectation result. The central suite file can be found in [reftests](../../reftests/suite.ron), and the serialization structures are in [reftest.rs](src/bin/reftest.rs). 20 | 21 | ## Warning 22 | 23 | This gfx-rs component is heavy WIP, provided under no warranty! There is a lot of logic missing, especially with regards to error reporting. 24 | -------------------------------------------------------------------------------- /src/hal/src/mapping.rs: -------------------------------------------------------------------------------- 1 | #![deny(missing_docs, missing_copy_implementations)] 2 | 3 | //! Memory mapping 4 | use crate::device; 5 | use std::ops::{self, Range}; 6 | use crate::Backend; 7 | 8 | // TODO 9 | /// Error accessing a mapping. 10 | #[derive(Clone, Copy, Debug, Eq, PartialEq, Fail)] 11 | pub enum Error { 12 | /// Out of either host or device memory. 13 | #[fail(display = "{}", _0)] 14 | OutOfMemory(device::OutOfMemory), 15 | /// The requested mapping access did not match the expected usage. 16 | #[fail(display = "The requested mapping access did not match the expected usage")] 17 | InvalidAccess, 18 | /// The requested mapping range is outside of the resource. 19 | #[fail(display = "The requested mapping range is outside of the resource")] 20 | OutOfBounds, 21 | /// Failed to map memory range. 22 | #[fail(display = "Unable to allocate an appropriately sized contiguous virtual address")] 23 | MappingFailed, 24 | } 25 | 26 | impl From for Error { 27 | fn from(error: device::OutOfMemory) -> Self { 28 | Error::OutOfMemory(error) 29 | } 30 | } 31 | 32 | /// Mapping reader 33 | pub struct Reader<'a, B: Backend, T: 'a> { 34 | pub(crate) slice: &'a [T], 35 | pub(crate) memory: &'a B::Memory, 36 | pub(crate) released: bool, 37 | } 38 | 39 | impl<'a, B: Backend, T: 'a> Drop for Reader<'a, B, T> { 40 | fn drop(&mut self) { 41 | assert!(self.released, "a mapping reader was not released"); 42 | } 43 | } 44 | 45 | impl<'a, B: Backend, T: 'a> ops::Deref for Reader<'a, B, T> { 46 | type Target = [T]; 47 | fn deref(&self) -> &[T] { 48 | self.slice 49 | } 50 | } 51 | 52 | /// Mapping writer. 53 | /// Currently is not possible to make write-only slice so while it is technically possible 54 | /// to read from Writer, it will lead to an undefined behavior. Please do not read from it. 55 | pub struct Writer<'a, B: Backend, T: 'a> { 56 | pub(crate) slice: &'a mut [T], 57 | pub(crate) memory: &'a B::Memory, 58 | pub(crate) range: Range, 59 | pub(crate) released: bool, 60 | } 61 | 62 | impl<'a, B: Backend, T: 'a> Drop for Writer<'a, B, T> { 63 | fn drop(&mut self) { 64 | assert!(self.released, "a mapping writer was not released"); 65 | } 66 | } 67 | 68 | impl<'a, B: Backend, T: 'a> ops::Deref for Writer<'a, B, T> { 69 | type Target = [T]; 70 | fn deref(&self) -> &[T] { 71 | self.slice 72 | } 73 | } 74 | 75 | impl<'a, B: Backend, T: 'a> ops::DerefMut for Writer<'a, B, T> { 76 | fn deref_mut(&mut self) -> &mut [T] { 77 | self.slice 78 | } 79 | } 80 | -------------------------------------------------------------------------------- /src/macros/tests/macros.rs: -------------------------------------------------------------------------------- 1 | #[macro_use] 2 | extern crate gfx; 3 | 4 | pub use gfx::format as fm; 5 | 6 | #[derive(Clone, Debug, PartialEq)] 7 | pub struct Rg16; 8 | gfx_format!(Rg16: R16_G16 = Vec2); 9 | 10 | gfx_defines!{ 11 | vertex Vertex { 12 | _x: i8 = "x", 13 | _y: f32 = "y", 14 | } 15 | 16 | vertex Instance { 17 | pos: [f32; 2] = "pos", 18 | color: [f32; 3] = "color", 19 | } 20 | 21 | constant Local { 22 | pos: [u32; 4] = "pos", 23 | } 24 | 25 | constant LocalMeta { 26 | pos: [u32; 4] = "pos_meta", 27 | } 28 | 29 | pipeline testpipe { 30 | vertex: gfx::VertexBuffer = (), 31 | instance: gfx::InstanceBuffer = (), 32 | const_locals: gfx::ConstantBuffer = "Locals", 33 | global: gfx::Global<[f32; 4]> = "Global", 34 | tex_diffuse: gfx::ShaderResource<[f32; 4]> = "Diffuse", 35 | sampler_linear: gfx::Sampler = "Linear", 36 | buf_frequency: gfx::UnorderedAccess<[f32; 4]> = "Frequency", 37 | pixel_color: gfx::RenderTarget = "Color", 38 | blend_target: gfx::BlendTarget = 39 | ("o_Color1", gfx::state::MASK_ALL, gfx::preset::blend::ADD), 40 | depth: gfx::DepthTarget = 41 | gfx::preset::depth::LESS_EQUAL_TEST, 42 | blend_ref: gfx::BlendRef = (), 43 | scissor: gfx::Scissor = (), 44 | } 45 | } 46 | 47 | fn _test_pso(device: &mut D) -> gfx::PipelineState 48 | where R: gfx::Resources, 49 | D: gfx::traits::DeviceExt 50 | { 51 | device.create_pipeline_simple(&[], &[], testpipe::new()).unwrap() 52 | } 53 | 54 | 55 | gfx_pipeline_base!( testraw { 56 | vertex: gfx::RawVertexBuffer, 57 | cbuf: gfx::RawConstantBuffer, 58 | tex: gfx::RawShaderResource, 59 | target: gfx::RawRenderTarget, 60 | }); 61 | 62 | fn _test_raw(device: &mut D) -> gfx::PipelineState 63 | where R: gfx::Resources, 64 | D: gfx::traits::DeviceExt 65 | { 66 | let special = gfx::pso::buffer::Element { 67 | format: fm::Format(fm::SurfaceType::R32, fm::ChannelType::Float), 68 | offset: 0, 69 | }; 70 | let init = testraw::Init { 71 | vertex: (&[("a_Special", special)], 12, 0), 72 | cbuf: "Locals", 73 | tex: "Specular", 74 | target: ("o_Color2", 75 | fm::Format(fm::SurfaceType::R8_G8_B8_A8, fm::ChannelType::Unorm), 76 | gfx::state::MASK_ALL, 77 | None), 78 | }; 79 | device.create_pipeline_simple(&[], &[], init).unwrap() 80 | } 81 | -------------------------------------------------------------------------------- /reftests/scenes/basic.ron: -------------------------------------------------------------------------------- 1 | ( 2 | resources: { 3 | "image.color": Image( 4 | kind: D2(1, 1, 1, 1), 5 | num_levels: 1, 6 | format: Rgba8Unorm, 7 | usage: (bits: 0x14), //COLOR_ATTACHMENT | SAMPLED (temporary for GL) 8 | ), 9 | "pass": RenderPass( 10 | attachments: { 11 | "c": ( 12 | format: Some(Rgba8Unorm), 13 | samples: 1, 14 | ops: (load: Clear, store: Store), 15 | layouts: (start: General, end: General), 16 | ), 17 | }, 18 | subpasses: { 19 | "main": ( 20 | colors: [("c", General)], 21 | depth_stencil: None, 22 | ) 23 | }, 24 | dependencies: [], 25 | ), 26 | "image.color.view": ImageView( 27 | image: "image.color", 28 | kind: D2, 29 | format: Rgba8Unorm, 30 | range: ( 31 | aspects: (bits: 1), 32 | levels: (start: 0, end: 1), 33 | layers: (start: 0, end: 1), 34 | ), 35 | ), 36 | "fbo": Framebuffer( 37 | pass: "pass", 38 | views: { 39 | "c": "image.color.view" 40 | }, 41 | extent: ( 42 | width: 1, 43 | height: 1, 44 | depth: 1, 45 | ), 46 | ), 47 | "pipe-layout": PipelineLayout( 48 | set_layouts: [], 49 | push_constant_ranges: [], 50 | ), 51 | "shader.passthrough.vs": Shader("passthrough.vert"), 52 | "shader.passthrough.fs": Shader("passthrough.frag"), 53 | "pipe.passthrough": GraphicsPipeline( 54 | shaders: ( 55 | vertex: "shader.passthrough.vs", 56 | fragment: "shader.passthrough.fs", 57 | ), 58 | rasterizer: ( 59 | polygon_mode: Fill, 60 | cull_face: (bits: 0), 61 | front_face: Clockwise, 62 | depth_clamping: false, 63 | depth_bias: None, 64 | conservative: false, 65 | ), 66 | input_assembler: ( 67 | primitive: TriangleList, 68 | primitive_restart: Disabled, 69 | ), 70 | blender: ( 71 | alpha_coverage: false, 72 | logic_op: None, 73 | targets: [ 74 | ((bits: 15), Off), 75 | ], 76 | ), 77 | layout: "pipe-layout", 78 | subpass: ( 79 | parent: "pass", 80 | index: 0, 81 | ), 82 | ), 83 | }, 84 | jobs: { 85 | "empty": Graphics( 86 | framebuffer: "fbo", 87 | clear_values: [ 88 | Color(Float((0.8, 0.8, 0.8, 1.0))), 89 | ], 90 | pass: ("pass", { 91 | "main": (commands: [ 92 | ]), 93 | }), 94 | ), 95 | "pass-through": Graphics( 96 | framebuffer: "fbo", 97 | clear_values: [ 98 | Color(Float((0.8, 0.8, 0.8, 1.0))), 99 | ], 100 | pass: ("pass", { 101 | "main": (commands: [ 102 | BindPipeline("pipe.passthrough"), 103 | Draw( 104 | vertices: (start: 0, end: 3), 105 | ), 106 | ]), 107 | }), 108 | ), 109 | }, 110 | ) 111 | -------------------------------------------------------------------------------- /src/hal/src/error.rs: -------------------------------------------------------------------------------- 1 | //! Return values from function calls. 2 | 3 | /// Device creation errors during `open`. 4 | #[derive(Fail, Debug, Clone, PartialEq, Eq)] 5 | pub enum DeviceCreationError { 6 | /// Memory allocation on the host side failed. 7 | /// This could be caused by a lack of memory. 8 | #[fail(display = "Host memory allocation failed.")] 9 | OutOfHostMemory, 10 | /// Memory allocation on the device side failed. 11 | /// This could be caused by a lack of memory. 12 | #[fail(display = "Device memory allocation failed.")] 13 | OutOfDeviceMemory, 14 | /// Device initialization failed due to implementation specific errors. 15 | #[fail(display = "Device initialization failed.")] 16 | InitializationFailed, 17 | /// At least one of the user requested extensions if not supported by the 18 | /// physical device. 19 | #[fail(display = "One or multiple extensions are not supported.")] 20 | MissingExtension, 21 | /// At least one of the user requested features if not supported by the 22 | /// physical device. 23 | /// 24 | /// Use [`features`](trait.PhysicalDevice.html#tymethod.features) 25 | /// for checking the supported features. 26 | #[fail(display = "One or multiple features are not supported.")] 27 | MissingFeature, 28 | /// Too many logical devices have been created from this physical device. 29 | /// 30 | /// The implementation may only support one logical device for each physical 31 | /// device or lacks resources to allocate a new device. 32 | #[fail(display = "Too many device objects have been created.")] 33 | TooManyObjects, 34 | /// The logical or physical device are lost during the device creation 35 | /// process. 36 | /// 37 | /// This may be caused by hardware failure, physical device removal, 38 | /// power outage, etc. 39 | #[fail(display = "Physical or logical device lost.")] 40 | DeviceLost, 41 | } 42 | 43 | /// Errors during execution of operations on the host side. 44 | #[derive(Fail, Debug, Clone, PartialEq, Eq)] 45 | pub enum HostExecutionError { 46 | /// Memory allocation on the host side failed. 47 | /// This could be caused by a lack of memory. 48 | #[fail(display = "Host memory allocation failed.")] 49 | OutOfHostMemory, 50 | /// Memory allocation on the device side failed. 51 | /// This could be caused by a lack of memory. 52 | #[fail(display = "Device memory allocation failed.")] 53 | OutOfDeviceMemory, 54 | /// The logical or physical device are lost during the device creation 55 | /// process. 56 | /// 57 | /// This may be caused by hardware failure, physical device removal, 58 | /// power outage, etc. 59 | #[fail(display = "Physical or logical device lost.")] 60 | DeviceLost, 61 | } 62 | -------------------------------------------------------------------------------- /reftests/local.ron: -------------------------------------------------------------------------------- 1 | { 2 | "transfer": { 3 | "copy-buf": ( 4 | features: (bits: 0), 5 | jobs: ["copy-buf"], 6 | expect: Buffer("buffer.output", [72, 65, 76, 80]), 7 | ), 8 | "copy-buf-cut": ( 9 | features: (bits: 0), 10 | jobs: ["copy-buf-cut"], 11 | expect: Buffer("buffer.large", [48, 49, 50, 51, 72, 65, 54, 55]), 12 | ), 13 | "copy-image": ( 14 | features: (bits: 0), 15 | jobs: ["copy-image"], 16 | expect: ImageRow("image.output", 0, [48, 49, 50, 51]), 17 | ), 18 | "copy-buf-image": ( 19 | features: (bits: 0), 20 | jobs: ["copy-buf-image"], 21 | expect: ImageRow("image.output", 0, [72, 65, 76, 80]), 22 | ), 23 | "copy-image-buf": ( 24 | features: (bits: 0), 25 | jobs: ["copy-image-buf"], 26 | expect: Buffer("buffer.output", [52, 53, 54, 55]), 27 | ), 28 | "clear-image": ( 29 | features: (bits: 0), 30 | jobs: ["clear-image"], 31 | expect: ImageRow("image.output", 0, [128, 128, 128, 128]), 32 | ), 33 | "blit-image": ( 34 | features: (bits: 0), 35 | jobs: ["blit-image"], 36 | expect: ImageRow("image.output", 0, [50, 51, 52, 53]), 37 | ), 38 | "fill-whole": ( 39 | features: (bits: 0), 40 | jobs: ["fill-whole"], 41 | expect: Buffer("buffer.fill-8-bytes", [0, 0, 255, 0, 0, 0, 255, 0]), 42 | ), 43 | "fill-first": ( 44 | features: (bits: 0), 45 | jobs: ["fill-first"], 46 | expect: Buffer("buffer.fill-8-bytes", [0, 255, 0, 0, 84, 69, 83, 33]), 47 | ), 48 | "fill-last": ( 49 | features: (bits: 0), 50 | jobs: ["fill-last"], 51 | expect: Buffer("buffer.fill-8-bytes", [56, 32, 66, 89, 255, 0, 0, 0]), 52 | ), 53 | /* 54 | Diabled temporarily because of Vulkan driver support 55 | "fill-whole-nearest-multiple": ( 56 | features: (bits: 0), 57 | jobs: ["fill-whole-nearest-multiple"], 58 | expect: Buffer("buffer.fill-10-bytes", [255, 0, 0, 0, 255, 0, 0, 0, 63, 33]), 59 | ),*/ 60 | }, 61 | "basic": { 62 | "render-pass-clear": ( 63 | features: (bits: 0), 64 | jobs: ["empty"], 65 | expect: ImageRow("image.color", 0, [204,204,204,255]), 66 | ), 67 | "pass-through": ( 68 | features: (bits: 0), 69 | jobs: ["pass-through"], 70 | expect: ImageRow("image.color", 0, [0,255,0,255]), 71 | ), 72 | }, 73 | "compute": { 74 | "fill": ( 75 | features: (bits: 0), 76 | jobs: ["fill"], 77 | expect: Buffer("buffer.output", [1, 0, 0, 0]), 78 | ), 79 | }, 80 | "vertex-offset": { 81 | "offset-aligned": ( 82 | features: (bits: 0), 83 | jobs: ["offset-aligned"], 84 | expect: ImageRow("image.color", 0, [0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15]), 85 | ), 86 | "offset-overlap": ( 87 | features: (bits: 0), 88 | jobs: ["offset-overlap"], 89 | expect: ImageRow("image.color", 0, [8, 9, 10, 11, 12, 13, 14, 15, 0, 1, 2, 3, 4, 5, 6, 7]), 90 | ), 91 | }, 92 | } 93 | -------------------------------------------------------------------------------- /src/backend/dx11/src/debug.rs: -------------------------------------------------------------------------------- 1 | use winapi::um::d3d11; 2 | 3 | use wio::com::ComPtr; 4 | use wio::wide::ToWide; 5 | 6 | use std::ffi::OsStr; 7 | use std::{env, fmt}; 8 | 9 | // TODO: replace with new winapi version when available 10 | #[allow(bad_style, unused)] 11 | mod temp { 12 | use winapi::shared::minwindef::{BOOL, INT}; 13 | use winapi::um::unknwnbase::{IUnknown, IUnknownVtbl}; 14 | use winapi::um::winnt::LPCWSTR; 15 | 16 | RIDL! {#[uuid(0xb2daad8b, 0x03d4, 0x4dbf, 0x95, 0xeb, 0x32, 0xab, 0x4b, 0x63, 0xd0, 0xab)] 17 | interface ID3DUserDefinedAnnotation(ID3DUserDefinedAnnotationVtbl): 18 | IUnknown(IUnknownVtbl) { 19 | fn BeginEvent( 20 | Name: LPCWSTR, 21 | ) -> INT, 22 | fn EndEvent() -> INT, 23 | fn SetMarker( 24 | Name: LPCWSTR, 25 | ) -> (), 26 | fn GetStatus() -> BOOL, 27 | }} 28 | } 29 | 30 | #[must_use] 31 | #[cfg(debug_assertions)] 32 | pub struct DebugScope { 33 | annotation: ComPtr, 34 | } 35 | 36 | #[cfg(debug_assertions)] 37 | impl DebugScope { 38 | pub fn with_name( 39 | context: &ComPtr, 40 | args: fmt::Arguments, 41 | ) -> Option { 42 | let name = format!("{}", args); 43 | 44 | // debugging with visual studio and its ilk *really* doesn't like calling this on a 45 | // deferred context when replaying a capture, compared to renderdoc 46 | if unsafe { context.GetType() } == d3d11::D3D11_DEVICE_CONTEXT_DEFERRED { 47 | // TODO: find a better way to detect either if RD or VS is active debugger 48 | if env::var("GFX_NO_RENDERDOC").is_ok() { 49 | return None; 50 | } 51 | } 52 | 53 | let annotation = context.cast::().unwrap(); 54 | let msg: &OsStr = name.as_ref(); 55 | let msg: Vec = msg.to_wide_null(); 56 | 57 | unsafe { 58 | annotation.BeginEvent(msg.as_ptr() as _); 59 | } 60 | 61 | Some(DebugScope { annotation }) 62 | } 63 | } 64 | 65 | #[cfg(debug_assertions)] 66 | impl Drop for DebugScope { 67 | fn drop(&mut self) { 68 | unsafe { 69 | self.annotation.EndEvent(); 70 | } 71 | } 72 | } 73 | 74 | #[cfg(debug_assertions)] 75 | pub fn debug_marker(context: &ComPtr, args: fmt::Arguments) { 76 | let name = format!("{}", args); 77 | 78 | // same here 79 | if unsafe { context.GetType() } == d3d11::D3D11_DEVICE_CONTEXT_DEFERRED { 80 | if env::var("GFX_NO_RENDERDOC").is_ok() { 81 | return; 82 | } 83 | } 84 | 85 | let annotation = context.cast::().unwrap(); 86 | let msg: &OsStr = name.as_ref(); 87 | let msg: Vec = msg.to_wide_null(); 88 | 89 | unsafe { 90 | annotation.SetMarker(msg.as_ptr() as _); 91 | } 92 | } 93 | -------------------------------------------------------------------------------- /Makefile: -------------------------------------------------------------------------------- 1 | RUST_BACKTRACE:=1 2 | EXCLUDES:= 3 | FEATURES_EXTRA:=mint serialize 4 | FEATURES_HAL:= 5 | FEATURES_HAL2:= 6 | 7 | SDL2_DEST=$(HOME)/deps 8 | SDL2_CONFIG=$(SDL2_DEST)/usr/bin/sdl2-config 9 | SDL2_PPA=http://ppa.launchpad.net/zoogie/sdl2-snapshots/ubuntu/pool/main/libs/libsdl2 10 | 11 | ifeq (,$(TARGET)) 12 | CHECK_TARGET_FLAG= 13 | else 14 | CHECK_TARGET_FLAG=--target $(TARGET) 15 | endif 16 | 17 | ifeq ($(OS),Windows_NT) 18 | EXCLUDES+= --exclude gfx-backend-metal 19 | FEATURES_HAL=vulkan 20 | ifeq ($(TARGET),x86_64-pc-windows-gnu) 21 | # No d3d12 support on GNU windows ATM 22 | # context: https://github.com/gfx-rs/gfx/pull/1417 23 | EXCLUDES+= --exclude gfx-backend-dx12 24 | EXCLUDES+= --exclude gfx-backend-dx11 25 | else 26 | FEATURES_HAL2=dx12 27 | endif 28 | else 29 | UNAME_S:=$(shell uname -s) 30 | EXCLUDES+= --exclude gfx-backend-dx12 31 | EXCLUDES+= --exclude gfx-backend-dx11 32 | ifeq ($(UNAME_S),Linux) 33 | EXCLUDES+= --exclude gfx-backend-metal 34 | FEATURES_HAL=vulkan 35 | endif 36 | ifeq ($(UNAME_S),Darwin) 37 | EXCLUDES+= --exclude gfx-backend-vulkan 38 | FEATURES_HAL=metal 39 | endif 40 | endif 41 | 42 | 43 | .PHONY: all check quad test doc reftests travis-sdl2 44 | 45 | all: check test 46 | 47 | help: 48 | @echo "Supported backends: gl $(FEATURES_HAL) $(FEATURES_HAL2)" 49 | 50 | check: 51 | @echo "Note: excluding \`warden\` here, since it depends on serialization" 52 | cargo check --all $(CHECK_TARGET_FLAG) $(EXCLUDES) --exclude gfx-warden 53 | cd examples && cargo check $(CHECK_TARGET_FLAG) --features "gl" 54 | cd examples && cargo check $(CHECK_TARGET_FLAG) --features "$(FEATURES_HAL)" 55 | cd examples && cargo check $(CHECK_TARGET_FLAG) --features "$(FEATURES_HAL2)" 56 | cd src/warden && cargo check $(CHECK_TARGET_FLAG) --no-default-features 57 | cd src/warden && cargo check $(CHECK_TARGET_FLAG) --features "env_logger gl gl-headless $(FEATURES_HAL) $(FEATURES_HAL2)" 58 | 59 | test: 60 | cargo test --all $(EXCLUDES) 61 | 62 | doc: 63 | cargo doc --all $(EXCLUDES) 64 | 65 | reftests: 66 | cd src/warden && cargo run --features "$(FEATURES_HAL) $(FEATURES_HAL2)" -- local #TODO: gl 67 | 68 | reftests-ci: 69 | cd src/warden && cargo test --features "gl" 70 | cd src/warden && cargo run --features "gl" -- ci #TODO: "gl-headless" 71 | 72 | quad: 73 | cd examples && cargo run --bin quad --features ${FEATURES_HAL} 74 | 75 | travis-sdl2: 76 | #TODO 77 | #if [ -e $(SDL2_CONFIG) ]; then exit 1; fi 78 | #mkdir -p $(SDL2_DEST) 79 | #test -f $(SDL2_DEST)/dev.deb || curl -sLo $(SDL2_DEST)/dev.deb $(SDL2_PPA)/libsdl2-dev_2.0.3+z4~20140315-8621-1ppa1precise1_amd64.deb 80 | #test -f $(SDL2_DEST)/bin.deb || curl -sLo $(SDL2_DEST)/bin.deb $(SDL2_PPA)/libsdl2_2.0.3+z4~20140315-8621-1ppa1precise1_amd64.deb 81 | #dpkg-deb -x $(SDL2_DEST)/bin.deb . 82 | #dpkg-deb -x $(SDL2_DEST)/dev.deb . 83 | #sed -e s,/usr,$(SDL2_DEST),g $(SDL2_CONFIG) > $(SDL2_CONFIG).new 84 | #mv $(SDL2_CONFIG).new $(SDL2_CONFIG) 85 | #chmod a+x $(SDL2_CONFIG) 86 | -------------------------------------------------------------------------------- /src/macros/src/lib.rs: -------------------------------------------------------------------------------- 1 | #![recursion_limit = "192"] 2 | 3 | extern crate proc_macro; 4 | extern crate syn; 5 | #[macro_use] extern crate quote; 6 | 7 | use proc_macro::TokenStream; 8 | 9 | 10 | #[proc_macro_derive(VertexData)] 11 | pub fn vertex(input: TokenStream) -> TokenStream { 12 | let s = input.to_string(); 13 | let ast = syn::parse_macro_input(&s).unwrap(); 14 | let gen = structure(ast, quote!(gfx::format::Formatted), quote!(gfx::format::Format)); 15 | gen.parse().unwrap() 16 | } 17 | 18 | #[proc_macro_derive(ConstantBuffer)] 19 | pub fn constant(input: TokenStream) -> TokenStream { 20 | let s = input.to_string(); 21 | let ast = syn::parse_macro_input(&s).unwrap(); 22 | let gen = structure(ast, quote!(gfx::shade::Formatted), quote!(gfx::shade::ConstFormat)); 23 | gen.parse().unwrap() 24 | } 25 | 26 | fn structure(ast: syn::DeriveInput, ty_compile: quote::Tokens, ty_run: quote::Tokens) 27 | -> quote::Tokens { 28 | let name = &ast.ident; 29 | let fields = match ast.body { 30 | syn::Body::Struct(syn::VariantData::Struct(ref fields)) => fields, 31 | _ => panic!("gfx-rs custom derives can only be casted on structs"), 32 | }; 33 | let match_name = fields.iter().map(|field| { 34 | let ident = &field.ident; 35 | let ty = &field.ty; 36 | quote! { 37 | stringify!(#ident) => Some(Element { 38 | format: <#ty as #ty_compile>::get_format(), 39 | offset: ((&tmp.#ident as *const _ as usize) - base) as ElemOffset + big_offset, 40 | }), 41 | } 42 | }); 43 | quote! { 44 | unsafe impl gfx::traits::Pod for #name {} 45 | 46 | impl gfx::pso::buffer::Structure<#ty_run> for #name { 47 | fn query(field_name: &str) -> Option> { 48 | use std::mem::{size_of, transmute}; 49 | use gfx::pso::buffer::{Element, ElemOffset}; 50 | // using an address of 1 as a simplest non-zero pointer to avoid UB 51 | let tmp: &#name = unsafe { transmute(1usize) }; 52 | let base = tmp as *const _ as usize; 53 | let (sub_name, big_offset) = { 54 | let mut split = field_name.split(|c| c == '[' || c == ']'); 55 | let _ = split.next().unwrap(); 56 | match split.next() { 57 | Some(s) => { 58 | let array_id: ElemOffset = s.parse().unwrap(); 59 | let sub_name = match split.next() { 60 | Some(s) if s.starts_with('.') => &s[1..], 61 | _ => field_name, 62 | }; 63 | (sub_name, array_id * (size_of::<#name>() as ElemOffset)) 64 | }, 65 | None => (field_name, 0), 66 | } 67 | }; 68 | match sub_name { 69 | #(#match_name)* 70 | _ => None, 71 | } 72 | } 73 | } 74 | } 75 | } 76 | -------------------------------------------------------------------------------- /.travis.yml: -------------------------------------------------------------------------------- 1 | sudo: false 2 | language: rust 3 | cache: 4 | directories: 5 | - $HOME/.cargo 6 | before-cache: 7 | - rm -rf $HOME/.cargo/registry 8 | 9 | matrix: 10 | include: 11 | # Linux 64bit 12 | - os: linux 13 | rust: stable 14 | compiler: gcc 15 | - os: linux 16 | rust: nightly 17 | compiler: gcc 18 | 19 | # macOS 64bit 20 | - env: MACOSX_DEPLOYMENT_TARGET=10.9 21 | os: osx 22 | rust: stable 23 | osx_image: xcode9 24 | compiler: clang 25 | - env: MACOSX_DEPLOYMENT_TARGET=10.9 26 | os: osx 27 | rust: nightly 28 | osx_image: xcode9 29 | compiler: clang 30 | 31 | # iPhoneOS 64bit 32 | - env: TARGET=aarch64-apple-ios 33 | os: osx 34 | osx_image: xcode9 35 | rust: nightly 36 | 37 | # Windows 64bit 38 | - os: windows 39 | rust: stable 40 | 41 | branches: 42 | except: 43 | - staging.tmp 44 | 45 | notifications: 46 | webhooks: 47 | urls: 48 | - https://webhooks.gitter.im/e/7479b6691b7e5e40716a 49 | on_success: always 50 | on_failure: always 51 | on_start: false 52 | 53 | before_install: 54 | # Do not run bors builds against the nightly compiler. 55 | # We want to find out about nightly bugs, so they're done in master, but we don't block on them. 56 | - if [[ $TRAVIS_RUST_VERSION == "nightly" && $TRAVIS_BRANCH == "staging" ]]; then exit; fi 57 | # Extract SDL2 .deb into a cached directory (see cache section above and LIBRARY_PATH exports below) 58 | # Will no longer be needed when Trusty build environment goes out of beta at Travis CI 59 | # (assuming it will have libsdl2-dev and Rust by then) 60 | # see https://docs.travis-ci.com/user/trusty-ci-environment/ 61 | - if [[ $TRAVIS_OS_NAME == "linux" ]]; then export DISPLAY=:99.0 && sh -e /etc/init.d/xvfb start && make travis-sdl2 && export CXX=g++-5; fi 62 | - if [[ $TRAVIS_OS_NAME == "osx" ]]; then brew update && brew install sdl2; fi 63 | - if [[ $TRAVIS_OS_NAME == "windows" ]]; then choco install make; fi 64 | - rustup self update 65 | - rustup target add $TARGET; true 66 | 67 | addons: 68 | apt: 69 | sources: 70 | # install a newer cmake since at this time Travis only has version 2.8.7 71 | - george-edison55-precise-backports 72 | - llvm-toolchain-precise-3.8 73 | - ubuntu-toolchain-r-test 74 | #- ppa:xorg-edgers/ppa # for libosmesa6-dev 75 | packages: 76 | - xdotool 77 | - cmake 78 | - cmake-data 79 | - libxxf86vm-dev 80 | - libxinerama-dev 81 | - libxinerama1 82 | - libxcursor-dev 83 | - libxcursor1 84 | - libglfw-dev 85 | - libosmesa6-dev 86 | - libxi-dev 87 | - libxrandr-dev 88 | - g++-5 89 | - gcc 90 | 91 | script: 92 | - if [[ $TRAVIS_RUST_VERSION == "nightly" && $TRAVIS_BRANCH == "staging" ]]; then exit; fi 93 | - export PATH=$PATH:$HOME/deps/bin 94 | - if [[ $TRAVIS_OS_NAME == "linux" ]]; then export LIBRARY_PATH=$HOME/deps/usr/lib/x86_64-linux-gnu; fi 95 | - if [[ $TRAVIS_OS_NAME == "linux" ]]; then export LD_LIBRARY_PATH=$LIBRARY_PATH; fi 96 | - if [[ $TARGET != "aarch64-apple-ios" ]]; then make all; else make check; fi 97 | #- if [[ $TRAVIS_OS_NAME == "linux" ]]; then make reftests-ci; fi 98 | -------------------------------------------------------------------------------- /src/backend/metal/build.rs: -------------------------------------------------------------------------------- 1 | // Compiles the shaders used internally by some commands 2 | 3 | use std::env; 4 | use std::ffi::OsStr; 5 | use std::fs; 6 | use std::path::{Path, PathBuf}; 7 | use std::process::Command; 8 | 9 | fn main() { 10 | let pd = env::var("CARGO_MANIFEST_DIR").unwrap(); 11 | let target = env::var("TARGET").unwrap(); 12 | let os = if target.ends_with("ios") { 13 | "ios" 14 | } else if target.ends_with("darwin") { 15 | "darwin" 16 | } else { 17 | panic!("unsupported target {}", target) 18 | }; 19 | let arch = &target[..target.chars().position(|c| c == '-').unwrap()]; 20 | 21 | let (sdk_name, platform_args): (_, &[_]) = match (os, arch) { 22 | ("ios", "aarch64") => ("iphoneos", &["-mios-version-min=8.0"]), 23 | ("ios", "armv7s") | ("ios", "armv7") => panic!("32-bit iOS does not have metal support"), 24 | ("ios", "i386") | ("ios", "x86_64") => panic!("iOS simulator does not have metal support"), 25 | ("darwin", _) => ("macosx", &["-mmacosx-version-min=10.11"]), 26 | _ => panic!("unsupported target {}", target), 27 | }; 28 | 29 | let project_dir = Path::new(&pd); 30 | let shader_dir = project_dir.join("shaders"); 31 | println!("cargo:rerun-if-changed={}", shader_dir.to_str().unwrap()); 32 | 33 | let od = env::var("OUT_DIR").unwrap(); 34 | let out_dir = Path::new(&od); 35 | let out_lib = out_dir.join("gfx_shaders.metallib"); 36 | 37 | // Find all .metal files _at the top level only_ 38 | let shader_files = fs::read_dir(&shader_dir) 39 | .expect("could not open shader directory") 40 | .filter_map(|entry| { 41 | let entry = entry.expect("error reading shader directory entry"); 42 | let path = entry.path(); 43 | match path.extension().and_then(OsStr::to_str) { 44 | Some("metal") => Some(path), 45 | _ => None, 46 | } 47 | }); 48 | 49 | // Compile all the metal files into OUT_DIR 50 | let mut compiled_shader_files: Vec = Vec::new(); 51 | for shader_path in shader_files.into_iter() { 52 | println!("cargo:rerun-if-changed={}", shader_path.to_str().unwrap()); 53 | 54 | let mut out_path = out_dir.join(shader_path.file_name().unwrap()); 55 | out_path.set_extension("air"); 56 | 57 | let status = Command::new("xcrun") 58 | .args(&["-sdk", sdk_name, "metal", "-c"]) 59 | .arg(shader_path.as_os_str()) 60 | .arg("-o") 61 | .arg(out_path.as_os_str()) 62 | .args(platform_args) 63 | .status() 64 | .expect("failed to execute metal compiler"); 65 | 66 | if !status.success() { 67 | // stdout is linked to parent, so more detailed message will have been output from `metal` 68 | panic!("shader compilation failed"); 69 | } 70 | 71 | compiled_shader_files.push(out_path); 72 | } 73 | 74 | // Link all the compiled files into a single library 75 | let status = Command::new("xcrun") 76 | .args(&["-sdk", sdk_name, "metallib"]) 77 | .args(compiled_shader_files.iter().map(|p| p.as_os_str())) 78 | .arg("-o") 79 | .arg(out_lib.as_os_str()) 80 | .status() 81 | .expect("failed to execute metal library builder"); 82 | 83 | if !status.success() { 84 | panic!("shader library build failed"); 85 | } 86 | } 87 | -------------------------------------------------------------------------------- /info/research.md: -------------------------------------------------------------------------------- 1 | # Research 2 | 3 | ## Data-Oriented Design 4 | 5 | - [Data-Oriented Design - Links and Thoughts](http://www.asawicki.info/news_1422_data-oriented_design_-_links_and_thoughts.html) (Adam Sawicki) 6 | - [Pitfalls of Object Oriented Programming [PDF]](http://research.scee.net/files/presentations/gcapaustralia09/Pitfalls_of_Object_Oriented_Programming_GCAP_09.pdf) (Sony) 7 | - [Data-Oriented Design [Youtube]](http://www.youtube.com/watch?v=16ZF9XqkfRY) (Sony) 8 | - [Practical Examples in Data Oriented Design [PDF]](https://docs.google.com/presentation/d/17Bzle0w6jz-1ndabrvC5MXUIQ5jme0M8xBF71oz-0Js/present) (BitSquid) 9 | - [Introduction to Data Oriented Design [SlideShare]](http://www.slideshare.net/DICEStudio/a-step-towards-data-orientation) (DICE) 10 | - [A Step Towards Data Orientation [SlideShare]](http://www.slideshare.net/DICEStudio/introduction-to-data-oriented-design) (DICE) 11 | - [Data-Oriented Design (Or Why You Might Be Shooting Yourself in The Foot With OOP)](http://gamesfromwithin.com/data-oriented-design) (Noel Llopis) 12 | - [The Always-Evolving Coding Style](http://gamesfromwithin.com/the-always-evolving-coding-style) (Noel Llopis) 13 | - [Managing Data Relationships](http://gamesfromwithin.com/managing-data-relationships) (Noel Llopis) 14 | - [Data-Oriented Design Now And In The Future](http://gamesfromwithin.com/data-oriented-design-now-and-in-the-future) (Noel Llopis) 15 | - [Data Oriented Design : Starting Up](http://www.randygaul.net/2013/05/05/data-oriented-design-starting-up/) (Randy Gaul) 16 | - [Adventures in data-oriented design – Part 1: Mesh data](http://molecularmusings.wordpress.com/2011/11/03/adventures-in-data-oriented-design-part-1-mesh-data-3/) (Molecular Musings) 17 | - [Adventures in data-oriented design – Part 2: Hierarchical data](http://molecularmusings.wordpress.com/2013/02/22/adventures-in-data-oriented-design-part-2-hierarchical-data/) (Molecular Musings) 18 | - [Adventures in data-oriented design – Part 3a: Ownership](http://molecularmusings.wordpress.com/2013/05/02/adventures-in-data-oriented-design-part-3a-ownership/) (Molecular Musings) 19 | - [Adventures in data-oriented design – Part 3b: Internal References](http://molecularmusings.wordpress.com/2013/05/17/adventures-in-data-oriented-design-part-3b-internal-references/) (Molecular Musings) 20 | - [Adventures in data-oriented design – Part 3c: External References](http://molecularmusings.wordpress.com/2013/07/24/adventures-in-data-oriented-design-part-3c-external-references/) (Molecular Musings) 21 | - [Adventures in data-oriented design – Part 4: Skinning it to 11](http://molecularmusings.wordpress.com/2013/08/22/adventures-in-data-oriented-design-part-4-skinning-it-to-11/) (Molecular Musings) 22 | - [Implementing a semi-automatic structure-of-arrays data container](http://molecularmusings.wordpress.com/2013/10/22/implementing-a-semi-automatic-structure-of-arrays-data-container/) (Molecular Musings) 23 | - [Understanding cache-friendly, data-oriented objects and handles](http://stackoverflow.com/questions/19385853/understanding-cache-friendly-data-oriented-objects-and-handles) (StackOverflow) 24 | - [Ogre 2.0 forum thread](http://www.ogre3d.org/forums/viewtopic.php?f=25&t=75459) 25 | - [Slides](http://www.mediafire.com/view/?7k0q4guxgm74y2g) 26 | - [Addendum](http://www.mediafire.com/view/?575z0dfnrk0377v) 27 | 28 | ## Draw Call Bucketing 29 | 30 | - [Order your graphics draw calls around!](http://realtimecollisiondetection.net/blog/?p=86) (realtimecollisiondetection.net) 31 | -------------------------------------------------------------------------------- /src/backend/metal/shaders/blit.metal: -------------------------------------------------------------------------------- 1 | #include "macros.h" 2 | #include 3 | using namespace metal; 4 | 5 | typedef struct { 6 | float4 src_coords [[attribute(0)]]; 7 | float4 dst_coords [[attribute(1)]]; 8 | } BlitAttributes; 9 | 10 | typedef struct { 11 | float4 position [[position]]; 12 | float4 uv; 13 | uint layer GFX_RENDER_TARGET_ARRAY_INDEX; 14 | } BlitVertexData; 15 | 16 | typedef struct { 17 | float depth [[depth(any)]]; 18 | } BlitDepthFragment; 19 | 20 | 21 | vertex BlitVertexData vs_blit(BlitAttributes in [[stage_in]]) { 22 | float4 pos = { 0.0, 0.0, in.dst_coords.z, 1.0f }; 23 | pos.xy = in.dst_coords.xy * 2.0 - 1.0; 24 | return BlitVertexData { pos, in.src_coords, uint(in.dst_coords.w) }; 25 | } 26 | 27 | fragment float4 ps_blit_1d_float( 28 | BlitVertexData in [[stage_in]], 29 | texture1d tex1D [[ texture(0) ]], 30 | sampler sampler2D [[ sampler(0) ]] 31 | ) { 32 | return tex1D.sample(sampler2D, in.uv.x); 33 | } 34 | 35 | 36 | fragment float4 ps_blit_1d_array_float( 37 | BlitVertexData in [[stage_in]], 38 | texture1d_array tex1DArray [[ texture(0) ]], 39 | sampler sampler2D [[ sampler(0) ]] 40 | ) { 41 | return tex1DArray.sample(sampler2D, in.uv.x, uint(in.uv.z)); 42 | } 43 | 44 | 45 | fragment float4 ps_blit_2d_float( 46 | BlitVertexData in [[stage_in]], 47 | texture2d tex2D [[ texture(0) ]], 48 | sampler sampler2D [[ sampler(0) ]] 49 | ) { 50 | return tex2D.sample(sampler2D, in.uv.xy, level(in.uv.w)); 51 | } 52 | 53 | fragment uint4 ps_blit_2d_uint( 54 | BlitVertexData in [[stage_in]], 55 | texture2d tex2D [[ texture(0) ]], 56 | sampler sampler2D [[ sampler(0) ]] 57 | ) { 58 | return tex2D.sample(sampler2D, in.uv.xy, level(in.uv.w)); 59 | } 60 | 61 | fragment int4 ps_blit_2d_int( 62 | BlitVertexData in [[stage_in]], 63 | texture2d tex2D [[ texture(0) ]], 64 | sampler sampler2D [[ sampler(0) ]] 65 | ) { 66 | return tex2D.sample(sampler2D, in.uv.xy, level(in.uv.w)); 67 | } 68 | 69 | fragment BlitDepthFragment ps_blit_2d_depth( 70 | BlitVertexData in [[stage_in]], 71 | depth2d tex2D [[ texture(0) ]], 72 | sampler sampler2D [[ sampler(0) ]] 73 | ) { 74 | float depth = tex2D.sample(sampler2D, in.uv.xy, level(in.uv.w)); 75 | return BlitDepthFragment { depth }; 76 | } 77 | 78 | 79 | fragment float4 ps_blit_2d_array_float( 80 | BlitVertexData in [[stage_in]], 81 | texture2d_array tex2DArray [[ texture(0) ]], 82 | sampler sampler2D [[ sampler(0) ]] 83 | ) { 84 | return tex2DArray.sample(sampler2D, in.uv.xy, uint(in.uv.z), level(in.uv.w)); 85 | } 86 | 87 | fragment uint4 ps_blit_2d_array_uint( 88 | BlitVertexData in [[stage_in]], 89 | texture2d_array tex2DArray [[ texture(0) ]], 90 | sampler sampler2D [[ sampler(0) ]] 91 | ) { 92 | return tex2DArray.sample(sampler2D, in.uv.xy, uint(in.uv.z), level(in.uv.w)); 93 | } 94 | 95 | fragment int4 ps_blit_2d_array_int( 96 | BlitVertexData in [[stage_in]], 97 | texture2d_array tex2DArray [[ texture(0) ]], 98 | sampler sampler2D [[ sampler(0) ]] 99 | ) { 100 | return tex2DArray.sample(sampler2D, in.uv.xy, uint(in.uv.z), level(in.uv.w)); 101 | } 102 | 103 | 104 | fragment float4 ps_blit_3d_float( 105 | BlitVertexData in [[stage_in]], 106 | texture3d tex3D [[ texture(0) ]], 107 | sampler sampler2D [[ sampler(0) ]] 108 | ) { 109 | return tex3D.sample(sampler2D, in.uv.xyz, level(in.uv.w)); 110 | } 111 | -------------------------------------------------------------------------------- /src/backend/vulkan/src/result.rs: -------------------------------------------------------------------------------- 1 | use ash::vk; 2 | 3 | use hal::error::{DeviceCreationError, HostExecutionError}; 4 | 5 | // Generic error codes from Vulkan 6 | #[derive(Debug)] 7 | pub(crate) enum Error { 8 | OutOfHostMemory, 9 | OutOfDeviceMemory, 10 | InitializationFailed, 11 | DeviceLost, 12 | MemoryMapFailed, 13 | LayerNotPresent, 14 | ExtensionNotPresent, 15 | FeatureNotPresent, 16 | IncompatibleDriver, 17 | TooManyObjects, 18 | FormatNotSupported, 19 | FragmentedPool, 20 | SurfaceLostKhr, 21 | NativeWindowInUseKhr, 22 | OutOfDateKhr, 23 | IncompatibleDisplayKhr, 24 | ValidationFailedExt, 25 | // Not an actual vulkan error, but handle the case where an implementation 26 | // might return an unkown error. 27 | Unknown, 28 | } 29 | 30 | impl From for Error { 31 | fn from(result: vk::Result) -> Self { 32 | match result { 33 | vk::Result::ERROR_OUT_OF_HOST_MEMORY => Error::OutOfHostMemory, 34 | vk::Result::ERROR_OUT_OF_DEVICE_MEMORY => Error::OutOfDeviceMemory, 35 | vk::Result::ERROR_INITIALIZATION_FAILED => Error::InitializationFailed, 36 | vk::Result::ERROR_DEVICE_LOST => Error::DeviceLost, 37 | vk::Result::ERROR_MEMORY_MAP_FAILED => Error::MemoryMapFailed, 38 | vk::Result::ERROR_LAYER_NOT_PRESENT => Error::LayerNotPresent, 39 | vk::Result::ERROR_EXTENSION_NOT_PRESENT => Error::ExtensionNotPresent, 40 | vk::Result::ERROR_FEATURE_NOT_PRESENT => Error::FeatureNotPresent, 41 | vk::Result::ERROR_INCOMPATIBLE_DRIVER => Error::IncompatibleDriver, 42 | vk::Result::ERROR_TOO_MANY_OBJECTS => Error::TooManyObjects, 43 | vk::Result::ERROR_FORMAT_NOT_SUPPORTED => Error::FormatNotSupported, 44 | vk::Result::ERROR_FRAGMENTED_POOL => Error::FragmentedPool, 45 | vk::Result::ERROR_SURFACE_LOST_KHR => Error::SurfaceLostKhr, 46 | vk::Result::ERROR_NATIVE_WINDOW_IN_USE_KHR => Error::NativeWindowInUseKhr, 47 | vk::Result::ERROR_OUT_OF_DATE_KHR => Error::OutOfDateKhr, 48 | vk::Result::ERROR_INCOMPATIBLE_DISPLAY_KHR => Error::IncompatibleDisplayKhr, 49 | vk::Result::ERROR_VALIDATION_FAILED_EXT => Error::ValidationFailedExt, 50 | _ => Error::Unknown, 51 | } 52 | } 53 | } 54 | 55 | // Impl `From` for various HAL error types. 56 | // 57 | // Syntax: 58 | // #HalError { 59 | // #VulkanError => #HalErrorVariant, 60 | // } 61 | macro_rules! from_error { 62 | { $($name:ident { $($base_error:ident => $err:ident,)* },)* } => { 63 | $( 64 | impl From for $name { 65 | fn from(err: Error) -> Self { 66 | match err { 67 | $( 68 | Error::$base_error => $name::$err, 69 | )* 70 | _ => unreachable!("Unexpected error code ({:?}). Non specification conformant driver.", err), 71 | } 72 | } 73 | } 74 | )* 75 | } 76 | } 77 | 78 | from_error! { 79 | DeviceCreationError { 80 | OutOfHostMemory => OutOfHostMemory, 81 | OutOfDeviceMemory => OutOfDeviceMemory, 82 | InitializationFailed => InitializationFailed, 83 | ExtensionNotPresent => MissingExtension, 84 | FeatureNotPresent => MissingFeature, 85 | TooManyObjects => TooManyObjects, 86 | DeviceLost => DeviceLost, 87 | }, 88 | } 89 | 90 | from_error! { 91 | HostExecutionError { 92 | OutOfHostMemory => OutOfHostMemory, 93 | OutOfDeviceMemory => OutOfDeviceMemory, 94 | DeviceLost => DeviceLost, 95 | }, 96 | } 97 | -------------------------------------------------------------------------------- /src/hal/src/queue/capability.rs: -------------------------------------------------------------------------------- 1 | //! Type system encoded queue capabilities. 2 | use crate::queue::QueueType; 3 | 4 | /// General capability, supporting graphics, compute and transfer operations. 5 | pub enum General {} 6 | /// Graphics capability, supporting graphics and transfer operations. 7 | pub enum Graphics {} 8 | /// Compute capability, supporting compute and transfer operations. 9 | pub enum Compute {} 10 | /// Transfer capability, supporting only transfer operations. 11 | pub enum Transfer {} 12 | 13 | /// Graphics or compute capability. 14 | pub enum GraphicsOrCompute {} 15 | 16 | /// A Capability is an object that specifies what kind of operations 17 | /// a queue type performs, allowing what types support what queue operations 18 | /// to be described at runtime by the type system. 19 | pub trait Capability { 20 | /// Return true if this type level capability is supported by 21 | /// a run-time queue type. 22 | fn supported_by(qt: QueueType) -> bool; 23 | } 24 | impl Capability for General { 25 | fn supported_by(qt: QueueType) -> bool { 26 | match qt { 27 | QueueType::General => true, 28 | _ => false, 29 | } 30 | } 31 | } 32 | impl Capability for Graphics { 33 | fn supported_by(qt: QueueType) -> bool { 34 | match qt { 35 | QueueType::General | QueueType::Graphics => true, 36 | _ => false, 37 | } 38 | } 39 | } 40 | impl Capability for Compute { 41 | fn supported_by(qt: QueueType) -> bool { 42 | match qt { 43 | QueueType::General | QueueType::Compute => true, 44 | _ => false, 45 | } 46 | } 47 | } 48 | impl Capability for Transfer { 49 | fn supported_by(qt: QueueType) -> bool { 50 | match qt { 51 | QueueType::General | QueueType::Compute | QueueType::Graphics | QueueType::Transfer => { 52 | true 53 | } 54 | } 55 | } 56 | } 57 | 58 | /// A trait that indicates that a particular type of queue supports 59 | /// a particular `Capability`. 60 | pub trait Supports {} 61 | impl Supports for T {} 62 | impl Supports for General {} 63 | impl Supports for General {} 64 | impl Supports for General {} 65 | impl Supports for Graphics {} 66 | impl Supports for Compute {} 67 | 68 | impl Supports for General {} 69 | impl Supports for Graphics {} 70 | impl Supports for Compute {} 71 | 72 | /// Encoding the minimal capability to support a combination of other capabilities. 73 | pub trait Upper { 74 | /// Resulting minimal required capability. 75 | type Result; 76 | } 77 | 78 | impl Upper for (T, T) { 79 | type Result = T; 80 | } 81 | impl Upper for (General, Graphics) { 82 | type Result = General; 83 | } 84 | impl Upper for (General, Compute) { 85 | type Result = General; 86 | } 87 | impl Upper for (General, Transfer) { 88 | type Result = General; 89 | } 90 | impl Upper for (Graphics, General) { 91 | type Result = General; 92 | } 93 | impl Upper for (Graphics, Compute) { 94 | type Result = General; 95 | } 96 | impl Upper for (Graphics, Transfer) { 97 | type Result = Graphics; 98 | } 99 | impl Upper for (Compute, General) { 100 | type Result = General; 101 | } 102 | impl Upper for (Compute, Graphics) { 103 | type Result = General; 104 | } 105 | impl Upper for (Compute, Transfer) { 106 | type Result = Compute; 107 | } 108 | impl Upper for (Transfer, General) { 109 | type Result = General; 110 | } 111 | impl Upper for (Transfer, Graphics) { 112 | type Result = Graphics; 113 | } 114 | impl Upper for (Transfer, Compute) { 115 | type Result = Compute; 116 | } 117 | -------------------------------------------------------------------------------- /src/backend/gl/src/pool.rs: -------------------------------------------------------------------------------- 1 | use crate::command::{self, Command, RawCommandBuffer}; 2 | use crate::hal::backend::FastHashMap; 3 | use crate::hal::{self, pool}; 4 | use crate::native as n; 5 | use crate::Backend; 6 | 7 | use std::sync::{Arc, Mutex}; 8 | 9 | pub struct OwnedBuffer { 10 | pub(crate) commands: Vec, 11 | pub(crate) data: Vec, 12 | } 13 | 14 | impl OwnedBuffer { 15 | pub fn new() -> Self { 16 | OwnedBuffer { 17 | commands: Vec::new(), 18 | data: Vec::new(), 19 | } 20 | } 21 | 22 | fn clear(&mut self) { 23 | self.commands.clear(); 24 | self.data.clear(); 25 | } 26 | } 27 | 28 | // Storage of command buffer memory. 29 | // Depends on the reset model chosen when creating the command pool. 30 | pub enum BufferMemory { 31 | // Storing all recorded commands and data in the pool in a linear 32 | // piece of memory shared by all associated command buffers. 33 | // 34 | // # Safety! 35 | // 36 | // This implementation heavily relays on the fact that the user **must** 37 | // ensure that only **one** associated command buffer from each pool 38 | // is recorded at the same time. Additionally, we only allow to reset the 39 | // whole command pool. This allows us to avoid fragmentation of the memory 40 | // and saves us additional bookkeeping overhead for keeping track of all 41 | // allocated buffers. 42 | // 43 | // Resetting the pool will free all data and commands recorded. Therefore it's 44 | // crucial that all submits have been finished **before** calling `reset`. 45 | Linear(OwnedBuffer), 46 | // Storing the memory for each command buffer separately to allow individual 47 | // command buffer resets. 48 | Individual { 49 | storage: FastHashMap, 50 | next_buffer_id: u64, 51 | }, 52 | } 53 | 54 | pub struct RawCommandPool { 55 | pub(crate) fbo: Option, 56 | pub(crate) limits: command::Limits, 57 | pub(crate) memory: Arc>, 58 | } 59 | 60 | impl pool::RawCommandPool for RawCommandPool { 61 | unsafe fn reset(&mut self) { 62 | let mut memory = self 63 | .memory 64 | .try_lock() 65 | .expect("Trying to reset command pool, while memory is still in-use."); 66 | 67 | match *memory { 68 | BufferMemory::Linear(ref mut buffer) => { 69 | buffer.clear(); 70 | } 71 | BufferMemory::Individual { 72 | ref mut storage, .. 73 | } => { 74 | for (_, ref mut buffer) in storage { 75 | buffer.clear(); 76 | } 77 | } 78 | } 79 | } 80 | 81 | fn allocate_one(&mut self, _level: hal::command::RawLevel) -> RawCommandBuffer { 82 | // TODO: Implement secondary buffers 83 | RawCommandBuffer::new(self.fbo, self.limits, self.memory.clone()) 84 | } 85 | 86 | unsafe fn free(&mut self, buffers: I) 87 | where 88 | I: IntoIterator, 89 | { 90 | let mut memory = self 91 | .memory 92 | .try_lock() 93 | .expect("Trying to free command buffers, while memory is still in-use."); 94 | 95 | if let BufferMemory::Individual { 96 | ref mut storage, .. 97 | } = *memory 98 | { 99 | // Expecting that the buffers actually are allocated from this pool. 100 | for buffer in buffers { 101 | storage.remove(&buffer.id); 102 | } 103 | } 104 | // Linear: Freeing doesn't really matter here as everything is backed by 105 | // only one Vec. 106 | } 107 | } 108 | -------------------------------------------------------------------------------- /src/hal/src/queue/family.rs: -------------------------------------------------------------------------------- 1 | //! Queue family and groups. 2 | 3 | use crate::backend::RawQueueGroup; 4 | use crate::queue::capability::{Capability, Compute, Graphics, Transfer}; 5 | use crate::queue::{CommandQueue, QueueType}; 6 | use crate::Backend; 7 | 8 | use std::any::Any; 9 | use std::fmt::Debug; 10 | 11 | /// General information about a queue family, available upon adapter discovery. 12 | /// 13 | /// Note that a backend can expose multiple queue families with the same properties. 14 | pub trait QueueFamily: Debug + Any + Send + Sync { 15 | /// Returns the type of queues. 16 | fn queue_type(&self) -> QueueType; 17 | /// Returns maximum number of queues created from this family. 18 | fn max_queues(&self) -> usize; 19 | /// Returns true if the queue supports graphics operations. 20 | fn supports_graphics(&self) -> bool { 21 | Graphics::supported_by(self.queue_type()) 22 | } 23 | /// Returns true if the queue supports compute operations. 24 | fn supports_compute(&self) -> bool { 25 | Compute::supported_by(self.queue_type()) 26 | } 27 | /// Returns true if the queue supports transfer operations. 28 | fn supports_transfer(&self) -> bool { 29 | Transfer::supported_by(self.queue_type()) 30 | } 31 | /// Returns the queue family ID. 32 | fn id(&self) -> QueueFamilyId; 33 | } 34 | 35 | /// Identifier for a queue family of a physical device. 36 | #[derive(Debug, Copy, Clone, PartialEq, Eq, Hash)] 37 | #[cfg_attr(feature = "serde", derive(Serialize, Deserialize))] 38 | pub struct QueueFamilyId(pub usize); 39 | 40 | /// Strong-typed group of queues of the same queue family. 41 | pub struct QueueGroup { 42 | family: QueueFamilyId, 43 | /// Command queues created in this family. 44 | pub queues: Vec>, 45 | } 46 | 47 | impl QueueGroup { 48 | /// Return the associated queue family id. 49 | pub fn family(&self) -> QueueFamilyId { 50 | self.family 51 | } 52 | } 53 | 54 | impl QueueGroup { 55 | /// Create a new strongly typed queue group from a raw one. 56 | /// 57 | /// # Panics 58 | /// 59 | /// Panics if the family doesn't expose required queue capabilities. 60 | fn new(raw: RawQueueGroup) -> Self { 61 | assert!(C::supported_by(raw.family.queue_type())); 62 | QueueGroup { 63 | family: raw.family.id(), 64 | queues: raw 65 | .queues 66 | .into_iter() 67 | .map(|q| unsafe { CommandQueue::new(q) }) 68 | .collect(), 69 | } 70 | } 71 | } 72 | 73 | /// Contains a list of all instantiated queues. Conceptually structured as a collection of 74 | /// `QueueGroup`s, one for each queue family. 75 | pub struct Queues(pub(crate) Vec>); 76 | 77 | impl Queues { 78 | /// Removes the queue family with the passed id from the queue list and 79 | /// returns the queue group. 80 | /// 81 | /// # Panics 82 | /// 83 | /// Panics if the family doesn't expose required queue capabilities. 84 | pub fn take(&mut self, id: QueueFamilyId) -> Option> { 85 | self.0 86 | .iter() 87 | .position(|raw| raw.family.id() == id) 88 | .map(|index| QueueGroup::new(self.0.swap_remove(index))) 89 | } 90 | 91 | /// Removes the queue family with the passed id from the queue list and 92 | /// returns the command queues. 93 | pub fn take_raw(&mut self, id: QueueFamilyId) -> Option> { 94 | self.0 95 | .iter() 96 | .position(|raw| raw.family.id() == id) 97 | .map(|index| self.0.swap_remove(index).queues) 98 | } 99 | } 100 | -------------------------------------------------------------------------------- /src/backend/dx12/src/pool.rs: -------------------------------------------------------------------------------- 1 | use std::sync::Arc; 2 | 3 | use winapi::shared::winerror::SUCCEEDED; 4 | 5 | use command::CommandBuffer; 6 | use hal::{command, pool}; 7 | use native::command_list::CmdListType; 8 | use {native, Backend, Shared}; 9 | 10 | pub enum CommandPoolAllocator { 11 | Shared(native::CommandAllocator), 12 | Individual(Vec), 13 | } 14 | 15 | pub struct RawCommandPool { 16 | pub(crate) allocator: CommandPoolAllocator, 17 | pub(crate) device: native::Device, 18 | pub(crate) list_type: CmdListType, 19 | pub(crate) shared: Arc, 20 | pub(crate) create_flags: pool::CommandPoolCreateFlags, 21 | } 22 | 23 | impl RawCommandPool { 24 | fn create_command_list(&mut self) -> (native::GraphicsCommandList, native::CommandAllocator) { 25 | let command_allocator = match self.allocator { 26 | CommandPoolAllocator::Shared(ref allocator) => allocator.clone(), 27 | CommandPoolAllocator::Individual(ref mut allocators) => { 28 | let (command_allocator, hr) = self.device.create_command_allocator(self.list_type); 29 | 30 | // TODO: error handling 31 | if !SUCCEEDED(hr) { 32 | error!("error on command allocator creation: {:x}", hr); 33 | } 34 | 35 | allocators.push(command_allocator); 36 | command_allocator 37 | } 38 | }; 39 | 40 | // allocate command lists 41 | let (command_list, hr) = self.device.create_graphics_command_list( 42 | self.list_type, 43 | command_allocator, 44 | native::PipelineState::null(), 45 | 0, 46 | ); 47 | 48 | if !SUCCEEDED(hr) { 49 | error!("error on command list creation: {:x}", hr); 50 | } 51 | 52 | // Close command list as they are initiated as recording. 53 | // But only one command list can be recording for each allocator 54 | let _hr = command_list.close(); 55 | 56 | (command_list, command_allocator) 57 | } 58 | 59 | pub(crate) fn destroy(self) { 60 | match self.allocator { 61 | CommandPoolAllocator::Shared(ref allocator) => unsafe { 62 | allocator.destroy(); 63 | }, 64 | CommandPoolAllocator::Individual(ref allocators) => { 65 | for allocator in allocators.iter() { 66 | unsafe { 67 | allocator.destroy(); 68 | } 69 | } 70 | } 71 | } 72 | } 73 | } 74 | 75 | unsafe impl Send for RawCommandPool {} 76 | unsafe impl Sync for RawCommandPool {} 77 | 78 | impl pool::RawCommandPool for RawCommandPool { 79 | unsafe fn reset(&mut self) { 80 | match self.allocator { 81 | CommandPoolAllocator::Shared(ref allocator) => { 82 | allocator.Reset(); 83 | } 84 | CommandPoolAllocator::Individual(ref mut allocators) => { 85 | for allocator in allocators.iter_mut() { 86 | allocator.Reset(); 87 | } 88 | } 89 | } 90 | } 91 | 92 | fn allocate_one(&mut self, level: command::RawLevel) -> CommandBuffer { 93 | // TODO: Implement secondary buffers 94 | assert_eq!(level, command::RawLevel::Primary); 95 | let (command_list, command_allocator) = self.create_command_list(); 96 | CommandBuffer::new(command_list, command_allocator, self.shared.clone(), self.create_flags) 97 | } 98 | 99 | unsafe fn free(&mut self, cbufs: I) 100 | where 101 | I: IntoIterator, 102 | { 103 | for mut cbuf in cbufs { 104 | cbuf.destroy(); 105 | } 106 | } 107 | } 108 | -------------------------------------------------------------------------------- /src/hal/src/query.rs: -------------------------------------------------------------------------------- 1 | //! Queries are commands that can be submitted to a command buffer to record statistics or 2 | //! other useful values as the command buffer is running. They are often intended for profiling 3 | //! or other introspection, providing a mechanism for the command buffer to record data about its 4 | //! operation as it is running. 5 | 6 | use crate::device::OutOfMemory; 7 | use crate::Backend; 8 | 9 | /// A query identifier. 10 | pub type Id = u32; 11 | 12 | /// Query creation error. 13 | #[derive(Clone, Copy, Debug, Fail, PartialEq, Eq)] 14 | pub enum CreationError { 15 | /// Out of either host or device memory. 16 | #[fail(display = "{}", _0)] 17 | OutOfMemory(OutOfMemory), 18 | 19 | /// Query type unsupported. 20 | #[fail(display = "Query type ({:?}) unsupported", _0)] 21 | Unsupported(Type), 22 | } 23 | 24 | impl From for CreationError { 25 | fn from(error: OutOfMemory) -> Self { 26 | CreationError::OutOfMemory(error) 27 | } 28 | } 29 | 30 | /// A `Query` object has a particular identifier and saves its results to a given `QueryPool`. 31 | /// It is passed as a parameter to the command buffer's query methods. 32 | #[derive(Debug)] 33 | pub struct Query<'a, B: Backend> { 34 | /// 35 | pub pool: &'a B::QueryPool, 36 | /// 37 | pub id: Id, 38 | } 39 | 40 | bitflags!( 41 | /// Query control flags. 42 | #[cfg_attr(feature = "serde", derive(Serialize, Deserialize))] 43 | pub struct ControlFlags: u32 { 44 | /// Occlusion queries **must** return the exact sampler number. 45 | /// 46 | /// Requires `precise_occlusion_query` device feature. 47 | const PRECISE = 0x1; 48 | } 49 | ); 50 | 51 | bitflags!( 52 | /// Query result flags. 53 | #[cfg_attr(feature = "serde", derive(Serialize, Deserialize))] 54 | pub struct ResultFlags: u32 { 55 | /// Results will be written as an array of 64-bit unsigned integer values. 56 | const BITS_64 = 0x1; 57 | /// Wait for each query’s status to become available before retrieving its results. 58 | const WAIT = 0x2; 59 | /// Availability status accompanies the results. 60 | const WITH_AVAILABILITY = 0x4; 61 | /// Returning partial results is acceptable. 62 | const PARTIAL = 0x8; 63 | } 64 | ); 65 | 66 | /// Type of queries in a query pool. 67 | #[derive(Copy, Clone, Debug, Hash, Eq, PartialEq)] 68 | pub enum Type { 69 | /// Occlusion query. Count the number of drawn samples between 70 | /// the start and end of the query command. 71 | Occlusion, 72 | /// Pipeline statistic query. Counts the number of pipeline stage 73 | /// invocations of the given types between the start and end of 74 | /// the query command. 75 | PipelineStatistics(PipelineStatistic), 76 | /// Timestamp query. Timestamps can be recorded to the 77 | /// query pool by calling `write_timestamp()`. 78 | Timestamp, 79 | } 80 | 81 | bitflags!( 82 | /// Pipeline statistic flags 83 | #[cfg_attr(feature = "serde", derive(Serialize, Deserialize))] 84 | pub struct PipelineStatistic: u32 { 85 | /// 86 | const INPUT_ASSEMBLY_VERTICES = 0x1; 87 | /// 88 | const INPUT_ASSEMBLY_PRIMITIVES = 0x2; 89 | /// 90 | const VERTEX_SHADER_INVOCATIONS = 0x4; 91 | /// 92 | const GEOMETRY_SHADER_INVOCATIONS = 0x8; 93 | /// 94 | const GEOMETRY_SHADER_PRIMITIVES = 0x10; 95 | /// 96 | const CLIPPING_INVOCATIONS = 0x20; 97 | /// 98 | const CLIPPING_PRIMITIVES = 0x40; 99 | /// 100 | const FRAGMENT_SHADER_INVOCATIONS = 0x80; 101 | /// 102 | const HULL_SHADER_PATCHES = 0x100; 103 | /// 104 | const DOMAIN_SHADER_INVOCATIONS = 0x200; 105 | /// 106 | const COMPUTE_SHADER_INVOCATIONS = 0x400; 107 | } 108 | ); 109 | -------------------------------------------------------------------------------- /src/backend/dx12/src/descriptors_cpu.rs: -------------------------------------------------------------------------------- 1 | use native; 2 | use native::descriptor::{CpuDescriptor, HeapFlags, HeapType}; 3 | use std::collections::HashSet; 4 | 5 | // Linear stack allocator for CPU descriptor heaps. 6 | pub struct HeapLinear { 7 | handle_size: usize, 8 | num: usize, 9 | size: usize, 10 | start: CpuDescriptor, 11 | raw: native::DescriptorHeap, 12 | } 13 | 14 | impl HeapLinear { 15 | pub fn new(device: native::Device, ty: HeapType, size: usize) -> Self { 16 | let (heap, _hr) = device.create_descriptor_heap(size as _, ty, HeapFlags::empty(), 0); 17 | 18 | HeapLinear { 19 | handle_size: device.get_descriptor_increment_size(ty) as _, 20 | num: 0, 21 | size, 22 | start: heap.start_cpu_descriptor(), 23 | raw: heap, 24 | } 25 | } 26 | 27 | pub fn alloc_handle(&mut self) -> CpuDescriptor { 28 | assert!(!self.is_full()); 29 | 30 | let slot = self.num; 31 | self.num += 1; 32 | 33 | CpuDescriptor { 34 | ptr: self.start.ptr + self.handle_size * slot, 35 | } 36 | } 37 | 38 | pub fn is_full(&self) -> bool { 39 | self.num >= self.size 40 | } 41 | 42 | pub fn clear(&mut self) { 43 | self.num = 0; 44 | } 45 | 46 | pub unsafe fn destroy(&self) { 47 | self.raw.destroy(); 48 | } 49 | } 50 | 51 | const HEAP_SIZE_FIXED: usize = 64; 52 | 53 | // Fixed-size free-list allocator for CPU descriptors. 54 | struct Heap { 55 | // Bit flag representation of available handles in the heap. 56 | // 57 | // 0 - Occupied 58 | // 1 - free 59 | availability: u64, 60 | handle_size: usize, 61 | start: CpuDescriptor, 62 | raw: native::DescriptorHeap, 63 | } 64 | 65 | impl Heap { 66 | pub fn new(device: native::Device, ty: HeapType) -> Self { 67 | let (heap, _hr) = 68 | device.create_descriptor_heap(HEAP_SIZE_FIXED as _, ty, HeapFlags::empty(), 0); 69 | 70 | Heap { 71 | handle_size: device.get_descriptor_increment_size(ty) as _, 72 | availability: !0, // all free! 73 | start: heap.start_cpu_descriptor(), 74 | raw: heap, 75 | } 76 | } 77 | 78 | pub fn alloc_handle(&mut self) -> CpuDescriptor { 79 | // Find first free slot. 80 | let slot = self.availability.trailing_zeros() as usize; 81 | assert!(slot < HEAP_SIZE_FIXED); 82 | // Set the slot as occupied. 83 | self.availability ^= 1 << slot; 84 | 85 | CpuDescriptor { 86 | ptr: self.start.ptr + self.handle_size * slot, 87 | } 88 | } 89 | 90 | pub fn is_full(&self) -> bool { 91 | self.availability == 0 92 | } 93 | 94 | pub unsafe fn destroy(&self) { 95 | self.raw.destroy(); 96 | } 97 | } 98 | 99 | pub struct DescriptorCpuPool { 100 | device: native::Device, 101 | ty: HeapType, 102 | heaps: Vec, 103 | free_list: HashSet, 104 | } 105 | 106 | impl DescriptorCpuPool { 107 | pub fn new(device: native::Device, ty: HeapType) -> Self { 108 | DescriptorCpuPool { 109 | device, 110 | ty, 111 | heaps: Vec::new(), 112 | free_list: HashSet::new(), 113 | } 114 | } 115 | 116 | pub fn alloc_handle(&mut self) -> CpuDescriptor { 117 | let heap_id = self.free_list.iter().cloned().next().unwrap_or_else(|| { 118 | // Allocate a new heap 119 | let id = self.heaps.len(); 120 | self.heaps.push(Heap::new(self.device, self.ty)); 121 | self.free_list.insert(id); 122 | id 123 | }); 124 | 125 | let heap = &mut self.heaps[heap_id]; 126 | let handle = heap.alloc_handle(); 127 | if heap.is_full() { 128 | self.free_list.remove(&heap_id); 129 | } 130 | 131 | handle 132 | } 133 | 134 | // TODO: free handles 135 | 136 | pub unsafe fn destroy(&self) { 137 | for heap in &self.heaps { 138 | heap.destroy(); 139 | } 140 | } 141 | } 142 | -------------------------------------------------------------------------------- /src/hal/src/pool.rs: -------------------------------------------------------------------------------- 1 | //! Command pools 2 | 3 | use crate::command::{ 4 | CommandBuffer, IntoRawCommandBuffer, RawLevel, SecondaryCommandBuffer, Shot, 5 | SubpassCommandBuffer, 6 | }; 7 | use crate::queue::capability::{Graphics, Supports}; 8 | use crate::Backend; 9 | 10 | use std::any::Any; 11 | use std::marker::PhantomData; 12 | 13 | bitflags!( 14 | /// Command pool creation flags. 15 | #[cfg_attr(feature = "serde", derive(Serialize, Deserialize))] 16 | pub struct CommandPoolCreateFlags: u8 { 17 | /// Indicates short-lived command buffers. 18 | /// Memory optimization hint for implementations. 19 | const TRANSIENT = 0x1; 20 | /// Allow command buffers to be reset individually. 21 | const RESET_INDIVIDUAL = 0x2; 22 | } 23 | ); 24 | 25 | /// The allocated command buffers are associated with the creating command queue. 26 | pub trait RawCommandPool: Any + Send + Sync { 27 | /// Reset the command pool and the corresponding command buffers. 28 | /// 29 | /// # Synchronization: You may _not_ free the pool if a command buffer is still in use (pool memory still in use) 30 | unsafe fn reset(&mut self); 31 | 32 | /// Allocate a single command buffers from the pool. 33 | fn allocate_one(&mut self, level: RawLevel) -> B::CommandBuffer { 34 | self.allocate_vec(1, level).pop().unwrap() 35 | } 36 | 37 | /// Allocate new command buffers from the pool. 38 | fn allocate_vec(&mut self, num: usize, level: RawLevel) -> Vec { 39 | (0..num).map(|_| self.allocate_one(level)).collect() 40 | } 41 | 42 | /// Free command buffers which are allocated from this pool. 43 | unsafe fn free(&mut self, buffers: I) 44 | where 45 | I: IntoIterator; 46 | } 47 | 48 | /// Strong-typed command pool. 49 | pub struct CommandPool { 50 | raw: B::CommandPool, 51 | _capability: PhantomData, 52 | } 53 | 54 | impl CommandPool { 55 | /// Create typed command pool from raw. 56 | /// 57 | /// # Safety 58 | /// 59 | /// `::supported_by(queue_type)` must return true 60 | /// for `queue_type` being the type of queues from family this `raw` pool is associated with. 61 | /// 62 | pub unsafe fn new(raw: B::CommandPool) -> Self { 63 | CommandPool { 64 | raw, 65 | _capability: PhantomData, 66 | } 67 | } 68 | 69 | /// Reset the command pool and the corresponding command buffers. 70 | /// 71 | /// # Synchronization: You may _not_ free the pool if a command buffer is still in use (pool memory still in use) 72 | pub unsafe fn reset(&mut self) { 73 | self.raw.reset(); 74 | } 75 | 76 | /// Allocates a new primary command buffer from the pool. 77 | pub fn acquire_command_buffer(&mut self) -> CommandBuffer { 78 | let buffer = self.raw.allocate_one(RawLevel::Primary); 79 | unsafe { CommandBuffer::new(buffer) } 80 | } 81 | 82 | /// Allocates a new secondary command buffer from the pool. 83 | pub fn acquire_secondary_command_buffer(&mut self) -> SecondaryCommandBuffer { 84 | let buffer = self.raw.allocate_one(RawLevel::Secondary); 85 | unsafe { SecondaryCommandBuffer::new(buffer) } 86 | } 87 | 88 | /// Free the given iterator of command buffers from the pool. 89 | pub unsafe fn free(&mut self, cmd_buffers: I) 90 | where 91 | I: IntoIterator, 92 | I::Item: IntoRawCommandBuffer, 93 | { 94 | self.raw 95 | .free(cmd_buffers.into_iter().map(|cmb| cmb.into_raw())) 96 | } 97 | 98 | /// Downgrade a typed command pool to untyped one. 99 | pub fn into_raw(self) -> B::CommandPool { 100 | self.raw 101 | } 102 | } 103 | 104 | impl> CommandPool { 105 | /// Allocates a new subpass command buffer from the pool. 106 | pub fn acquire_subpass_command_buffer(&mut self) -> SubpassCommandBuffer { 107 | let buffer = self.raw.allocate_one(RawLevel::Secondary); 108 | unsafe { SubpassCommandBuffer::new(buffer) } 109 | } 110 | } 111 | -------------------------------------------------------------------------------- /src/backend/gl/src/conv.rs: -------------------------------------------------------------------------------- 1 | use crate::gl::{self, types as t}; 2 | use crate::hal::format::Format; 3 | use crate::hal::{buffer, image as i, Primitive}; 4 | use crate::native::VertexAttribFunction; 5 | 6 | /* 7 | pub fn _image_kind_to_gl(kind: i::Kind) -> t::GLenum { 8 | match kind { 9 | i::Kind::D1(_) => gl::TEXTURE_1D, 10 | i::Kind::D1Array(_, _) => gl::TEXTURE_1D_ARRAY, 11 | i::Kind::D2(_, _, i::AaMode::Single) => gl::TEXTURE_2D, 12 | i::Kind::D2(_, _, _) => gl::TEXTURE_2D_MULTISAMPLE, 13 | i::Kind::D2Array(_, _, _, i::AaMode::Single) => gl::TEXTURE_2D_ARRAY, 14 | i::Kind::D2Array(_, _, _, _) => gl::TEXTURE_2D_MULTISAMPLE_ARRAY, 15 | i::Kind::D3(_, _, _) => gl::TEXTURE_3D, 16 | i::Kind::Cube(_) => gl::TEXTURE_CUBE_MAP, 17 | i::Kind::CubeArray(_, _) => gl::TEXTURE_CUBE_MAP_ARRAY, 18 | } 19 | }*/ 20 | 21 | pub fn filter_to_gl(mag: i::Filter, min: i::Filter, mip: i::Filter) -> (t::GLenum, t::GLenum) { 22 | use crate::hal::image::Filter::*; 23 | 24 | let mag_filter = match mag { 25 | Nearest => gl::NEAREST, 26 | Linear => gl::LINEAR, 27 | }; 28 | 29 | let min_filter = match (min, mip) { 30 | (Nearest, Nearest) => gl::NEAREST_MIPMAP_NEAREST, 31 | (Nearest, Linear) => gl::NEAREST_MIPMAP_LINEAR, 32 | (Linear, Nearest) => gl::LINEAR_MIPMAP_NEAREST, 33 | (Linear, Linear) => gl::LINEAR_MIPMAP_LINEAR, 34 | }; 35 | 36 | (min_filter, mag_filter) 37 | } 38 | 39 | pub fn wrap_to_gl(w: i::WrapMode) -> t::GLenum { 40 | match w { 41 | i::WrapMode::Tile => gl::REPEAT, 42 | i::WrapMode::Mirror => gl::MIRRORED_REPEAT, 43 | i::WrapMode::Clamp => gl::CLAMP_TO_EDGE, 44 | i::WrapMode::Border => gl::CLAMP_TO_BORDER, 45 | } 46 | } 47 | 48 | pub fn buffer_usage_to_gl_target(usage: buffer::Usage) -> Option { 49 | use self::buffer::Usage; 50 | match usage & (Usage::UNIFORM | Usage::INDEX | Usage::VERTEX | Usage::INDIRECT) { 51 | Usage::UNIFORM => Some(gl::UNIFORM_BUFFER), 52 | Usage::INDEX => Some(gl::ELEMENT_ARRAY_BUFFER), 53 | Usage::VERTEX => Some(gl::ARRAY_BUFFER), 54 | Usage::INDIRECT => unimplemented!(), 55 | _ => None, 56 | } 57 | } 58 | 59 | pub fn primitive_to_gl_primitive(primitive: Primitive) -> t::GLenum { 60 | match primitive { 61 | Primitive::PointList => gl::POINTS, 62 | Primitive::LineList => gl::LINES, 63 | Primitive::LineStrip => gl::LINE_STRIP, 64 | Primitive::TriangleList => gl::TRIANGLES, 65 | Primitive::TriangleStrip => gl::TRIANGLE_STRIP, 66 | Primitive::LineListAdjacency => gl::LINES_ADJACENCY, 67 | Primitive::LineStripAdjacency => gl::LINE_STRIP_ADJACENCY, 68 | Primitive::TriangleListAdjacency => gl::TRIANGLES_ADJACENCY, 69 | Primitive::TriangleStripAdjacency => gl::TRIANGLE_STRIP_ADJACENCY, 70 | Primitive::PatchList(_) => gl::PATCHES, 71 | } 72 | } 73 | 74 | pub fn format_to_gl_format( 75 | format: Format, 76 | ) -> Option<(gl::types::GLint, gl::types::GLenum, VertexAttribFunction)> { 77 | use crate::gl::*; 78 | use crate::hal::format::Format::*; 79 | use crate::native::VertexAttribFunction::*; 80 | let _ = Double; //mark as used 81 | // TODO: Add more formats and error handling for `None` 82 | let format = match format { 83 | R8Uint => (1, UNSIGNED_BYTE, Integer), 84 | R8Sint => (1, BYTE, Integer), 85 | Rg8Uint => (2, UNSIGNED_BYTE, Integer), 86 | Rg8Sint => (2, BYTE, Integer), 87 | Rgba8Uint => (4, UNSIGNED_BYTE, Integer), 88 | Rgba8Sint => (4, BYTE, Integer), 89 | R16Uint => (1, UNSIGNED_SHORT, Integer), 90 | R16Sint => (1, SHORT, Integer), 91 | R16Sfloat => (1, HALF_FLOAT, Float), 92 | Rg16Uint => (2, UNSIGNED_SHORT, Integer), 93 | Rg16Sint => (2, SHORT, Integer), 94 | Rg16Sfloat => (2, HALF_FLOAT, Float), 95 | Rgba16Uint => (4, UNSIGNED_SHORT, Integer), 96 | Rgba16Sint => (4, SHORT, Integer), 97 | Rgba16Sfloat => (4, HALF_FLOAT, Float), 98 | R32Uint => (1, UNSIGNED_INT, Integer), 99 | R32Sint => (1, INT, Integer), 100 | R32Sfloat => (1, FLOAT, Float), 101 | Rg32Uint => (2, UNSIGNED_INT, Integer), 102 | Rg32Sint => (2, INT, Integer), 103 | Rg32Sfloat => (2, FLOAT, Float), 104 | Rgb32Uint => (3, UNSIGNED_INT, Integer), 105 | Rgb32Sint => (3, INT, Integer), 106 | Rgb32Sfloat => (3, FLOAT, Float), 107 | Rgba32Uint => (4, UNSIGNED_INT, Integer), 108 | Rgba32Sint => (4, INT, Integer), 109 | Rgba32Sfloat => (4, FLOAT, Float), 110 | 111 | _ => return None, 112 | }; 113 | 114 | Some(format) 115 | } 116 | -------------------------------------------------------------------------------- /src/hal/src/buffer.rs: -------------------------------------------------------------------------------- 1 | //! Memory buffers. 2 | //! 3 | //! # Buffer 4 | //! 5 | //! Buffers interpret memory slices as linear contiguous data array. 6 | //! They can be used as shader resources, vertex buffers, index buffers or for 7 | //! specifying the action commands for indirect execution. 8 | 9 | use crate::{device, format, Backend, IndexType}; 10 | 11 | /// An offset inside a buffer, in bytes. 12 | pub type Offset = u64; 13 | 14 | /// Buffer state. 15 | pub type State = Access; 16 | 17 | /// Error creating a buffer. 18 | #[derive(Fail, Debug, Clone, PartialEq, Eq)] 19 | pub enum CreationError { 20 | /// Out of either host or device memory. 21 | #[fail(display = "{}", _0)] 22 | OutOfMemory(device::OutOfMemory), 23 | 24 | /// Requested buffer usage is not supported. 25 | /// 26 | /// Older GL version don't support constant buffers or multiple usage flags. 27 | #[fail(display = "Buffer usage unsupported ({:?}).", usage)] 28 | UnsupportedUsage { 29 | /// Unsupported usage passed on buffer creation. 30 | usage: Usage, 31 | }, 32 | } 33 | 34 | impl From for CreationError { 35 | fn from(error: device::OutOfMemory) -> Self { 36 | CreationError::OutOfMemory(error) 37 | } 38 | } 39 | 40 | /// Error creating a buffer view. 41 | #[derive(Fail, Debug, Clone, PartialEq, Eq)] 42 | pub enum ViewCreationError { 43 | /// Out of either host or device memory. 44 | #[fail(display = "{}", _0)] 45 | OutOfMemory(device::OutOfMemory), 46 | 47 | /// Buffer view format is not supported. 48 | #[fail(display = "Buffer view format unsupported ({:?}).", format)] 49 | UnsupportedFormat { 50 | /// Unsupported format passed on view creation. 51 | format: Option, 52 | }, 53 | } 54 | 55 | impl From for ViewCreationError { 56 | fn from(error: device::OutOfMemory) -> Self { 57 | ViewCreationError::OutOfMemory(error) 58 | } 59 | } 60 | 61 | bitflags!( 62 | /// Buffer usage flags. 63 | #[cfg_attr(feature = "serde", derive(Serialize, Deserialize))] 64 | pub struct Usage: u32 { 65 | /// 66 | const TRANSFER_SRC = 0x1; 67 | /// 68 | const TRANSFER_DST = 0x2; 69 | /// 70 | const UNIFORM_TEXEL = 0x4; 71 | /// 72 | const STORAGE_TEXEL = 0x8; 73 | /// 74 | const UNIFORM = 0x10; 75 | /// 76 | const STORAGE = 0x20; 77 | /// 78 | const INDEX = 0x40; 79 | /// 80 | const VERTEX = 0x80; 81 | /// 82 | const INDIRECT = 0x100; 83 | } 84 | ); 85 | 86 | impl Usage { 87 | /// Returns if the buffer can be used in transfer operations. 88 | pub fn can_transfer(&self) -> bool { 89 | self.intersects(Usage::TRANSFER_SRC | Usage::TRANSFER_DST) 90 | } 91 | } 92 | 93 | bitflags!( 94 | /// Buffer access flags. 95 | /// 96 | /// Access of buffers by the pipeline or shaders. 97 | #[cfg_attr(feature = "serde", derive(Serialize, Deserialize))] 98 | pub struct Access: u32 { 99 | /// Read commands instruction for indirect execution. 100 | const INDIRECT_COMMAND_READ = 0x1; 101 | /// Read index values for indexed draw commands. 102 | /// 103 | /// See [`draw_indexed`](../command/trait.RawCommandBuffer.html#tymethod.draw_indexed) 104 | /// and [`draw_indexed_indirect`](../command/trait.RawCommandBuffer.html#tymethod.draw_indexed_indirect). 105 | const INDEX_BUFFER_READ = 0x2; 106 | /// Read vertices from vertex buffer for draw commands in the [`VERTEX_INPUT`]( 107 | /// ../pso/struct.PipelineStage.html#associatedconstant.VERTEX_INPUT) stage. 108 | const VERTEX_BUFFER_READ = 0x4; 109 | /// 110 | const CONSTANT_BUFFER_READ = 0x8; 111 | /// 112 | const SHADER_READ = 0x20; 113 | /// 114 | const SHADER_WRITE = 0x40; 115 | /// 116 | const TRANSFER_READ = 0x800; 117 | /// 118 | const TRANSFER_WRITE = 0x1000; 119 | /// 120 | const HOST_READ = 0x2000; 121 | /// 122 | const HOST_WRITE = 0x4000; 123 | /// 124 | const MEMORY_READ = 0x8000; 125 | /// 126 | const MEMORY_WRITE = 0x10000; 127 | } 128 | ); 129 | 130 | /// Index buffer view for `bind_index_buffer`. 131 | /// 132 | /// Defines a buffer slice used for acquiring the indices on draw commands. 133 | /// Indices are used to lookup vertex indices in the vertex buffers. 134 | pub struct IndexBufferView<'a, B: Backend> { 135 | /// The buffer to bind. 136 | pub buffer: &'a B::Buffer, 137 | /// The offset into the buffer to start at. 138 | pub offset: u64, 139 | /// The type of the table elements (`u16` or `u32`). 140 | pub index_type: IndexType, 141 | } 142 | -------------------------------------------------------------------------------- /src/backend/dx12/src/window.rs: -------------------------------------------------------------------------------- 1 | use std::collections::VecDeque; 2 | use std::mem; 3 | 4 | #[cfg(feature = "winit")] 5 | use winit; 6 | 7 | use winapi::shared::dxgi1_4; 8 | use winapi::shared::windef::{HWND, RECT}; 9 | use winapi::um::winuser::GetClientRect; 10 | 11 | use hal::{self, format as f, image as i, CompositeAlpha}; 12 | use {native, resource as r, Backend, Instance, PhysicalDevice, QueueFamily}; 13 | 14 | use std::os::raw::c_void; 15 | 16 | impl Instance { 17 | pub fn create_surface_from_hwnd(&self, hwnd: *mut c_void) -> Surface { 18 | Surface { 19 | factory: self.factory, 20 | wnd_handle: hwnd as *mut _, 21 | } 22 | } 23 | 24 | #[cfg(feature = "winit")] 25 | pub fn create_surface(&self, window: &winit::Window) -> Surface { 26 | use winit::os::windows::WindowExt; 27 | self.create_surface_from_hwnd(window.get_hwnd() as *mut _) 28 | } 29 | } 30 | 31 | pub struct Surface { 32 | pub(crate) factory: native::WeakPtr, 33 | pub(crate) wnd_handle: HWND, 34 | } 35 | 36 | unsafe impl Send for Surface {} 37 | unsafe impl Sync for Surface {} 38 | 39 | impl Surface { 40 | fn get_extent(&self) -> (u32, u32) { 41 | unsafe { 42 | let mut rect: RECT = mem::zeroed(); 43 | if GetClientRect(self.wnd_handle as *mut _, &mut rect as *mut RECT) == 0 { 44 | panic!("GetClientRect failed"); 45 | } 46 | ( 47 | (rect.right - rect.left) as u32, 48 | (rect.bottom - rect.top) as u32, 49 | ) 50 | } 51 | } 52 | } 53 | 54 | impl hal::Surface for Surface { 55 | fn supports_queue_family(&self, queue_family: &QueueFamily) -> bool { 56 | match queue_family { 57 | &QueueFamily::Present => true, 58 | _ => false, 59 | } 60 | } 61 | 62 | fn kind(&self) -> i::Kind { 63 | let (width, height) = self.get_extent(); 64 | i::Kind::D2(width, height, 1, 1) 65 | } 66 | 67 | fn compatibility( 68 | &self, 69 | _: &PhysicalDevice, 70 | ) -> ( 71 | hal::SurfaceCapabilities, 72 | Option>, 73 | Vec, 74 | ) { 75 | let (width, height) = self.get_extent(); 76 | let extent = hal::window::Extent2D { width, height }; 77 | 78 | let capabilities = hal::SurfaceCapabilities { 79 | image_count: 2..16, // we currently use a flip effect which supports 2..16 buffers 80 | current_extent: Some(extent), 81 | extents: extent..extent, 82 | max_image_layers: 1, 83 | usage: i::Usage::COLOR_ATTACHMENT | i::Usage::TRANSFER_SRC | i::Usage::TRANSFER_DST, 84 | composite_alpha: CompositeAlpha::OPAQUE, //TODO 85 | }; 86 | 87 | // Sticking to FLIP swap effects for the moment. 88 | // We also expose sRGB buffers but they are handled internally as UNORM. 89 | // Roughly ordered by popularity.. 90 | let formats = vec![ 91 | f::Format::Bgra8Srgb, 92 | f::Format::Bgra8Unorm, 93 | f::Format::Rgba8Srgb, 94 | f::Format::Rgba8Unorm, 95 | f::Format::A2b10g10r10Unorm, 96 | f::Format::Rgba16Sfloat, 97 | ]; 98 | 99 | let present_modes = vec![ 100 | hal::PresentMode::Fifo, //TODO 101 | ]; 102 | 103 | (capabilities, Some(formats), present_modes) 104 | } 105 | } 106 | 107 | pub struct Swapchain { 108 | pub(crate) inner: native::WeakPtr, 109 | pub(crate) next_frame: usize, 110 | pub(crate) frame_queue: VecDeque, 111 | #[allow(dead_code)] 112 | pub(crate) rtv_heap: r::DescriptorHeap, 113 | // need to associate raw image pointers with the swapchain so they can be properly released 114 | // when the swapchain is destroyed 115 | pub(crate) resources: Vec, 116 | } 117 | 118 | impl hal::Swapchain for Swapchain { 119 | unsafe fn acquire_image( 120 | &mut self, 121 | _timout_ns: u64, 122 | _semaphore: Option<&r::Semaphore>, 123 | _fence: Option<&r::Fence>, 124 | ) -> Result { 125 | // TODO: sync 126 | 127 | if false { 128 | // TODO: we need to block this at some point? (running out of backbuffers) 129 | //let num_images = self.images.len(); 130 | let num_images = 1; 131 | let index = self.next_frame; 132 | self.frame_queue.push_back(index); 133 | self.next_frame = (self.next_frame + 1) % num_images; 134 | } 135 | 136 | // TODO: 137 | Ok(self.inner.GetCurrentBackBufferIndex()) 138 | } 139 | } 140 | 141 | unsafe impl Send for Swapchain {} 142 | unsafe impl Sync for Swapchain {} 143 | -------------------------------------------------------------------------------- /src/hal/src/pso/input_assembler.rs: -------------------------------------------------------------------------------- 1 | //! Input Assembler (IA) stage description. 2 | //! The input assembler collects raw vertex and index data. 3 | 4 | use crate::format; 5 | use crate::Primitive; 6 | 7 | /// Shader binding location. 8 | pub type Location = u32; 9 | /// Index of a vertex buffer. 10 | pub type BufferIndex = u32; 11 | /// Offset of an attribute from the start of the buffer, in bytes 12 | pub type ElemOffset = u32; 13 | /// Offset between attribute values, in bytes 14 | pub type ElemStride = u32; 15 | /// Number of instances between each advancement of the vertex buffer. 16 | pub type InstanceRate = u8; 17 | 18 | /// The rate at which to advance input data to shaders for the given buffer 19 | #[derive(Clone, Copy, Debug, Eq, Hash, PartialEq, PartialOrd, Ord)] 20 | #[cfg_attr(feature = "serde", derive(Serialize, Deserialize))] 21 | pub enum VertexInputRate { 22 | /// Advance the buffer after every vertex 23 | Vertex, 24 | /// Advance the buffer after every instance 25 | Instance(InstanceRate), 26 | } 27 | 28 | impl VertexInputRate { 29 | /// Get the numeric representation of the rate 30 | pub fn as_uint(&self) -> u8 { 31 | match *self { 32 | VertexInputRate::Vertex => 0, 33 | VertexInputRate::Instance(divisor) => divisor, 34 | } 35 | } 36 | } 37 | 38 | /// A struct element descriptor. 39 | #[derive(Clone, Copy, Debug, Eq, Hash, PartialEq, PartialOrd, Ord)] 40 | #[cfg_attr(feature = "serde", derive(Serialize, Deserialize))] 41 | pub struct Element { 42 | /// Element format 43 | pub format: F, 44 | /// Offset from the beginning of the container, in bytes 45 | pub offset: ElemOffset, 46 | } 47 | 48 | /// Vertex buffer description. Notably, completely separate from resource `Descriptor`s 49 | /// used in `DescriptorSet`s. 50 | #[derive(Clone, Copy, Debug, Eq, Hash, PartialEq, PartialOrd, Ord)] 51 | #[cfg_attr(feature = "serde", derive(Serialize, Deserialize))] 52 | pub struct VertexBufferDesc { 53 | /// Binding number of this vertex buffer. This binding number is 54 | /// used only for vertex buffers, and is completely separate from 55 | /// `Descriptor` and `DescriptorSet` bind points. 56 | pub binding: BufferIndex, 57 | /// Total container size, in bytes. 58 | /// Specifies the byte distance between two consecutive elements. 59 | pub stride: ElemStride, 60 | /// The rate at which to advance data for the given buffer 61 | /// 62 | /// i.e. the rate at which data passed to shaders will get advanced by 63 | /// `stride` bytes 64 | pub rate: VertexInputRate, 65 | } 66 | 67 | /// Vertex attribute description. Notably, completely separate from resource `Descriptor`s 68 | /// used in `DescriptorSet`s. 69 | #[derive(Clone, Copy, Debug, Eq, Hash, PartialEq, PartialOrd, Ord)] 70 | #[cfg_attr(feature = "serde", derive(Serialize, Deserialize))] 71 | pub struct AttributeDesc { 72 | /// Attribute binding location in the shader. Attribute locations are 73 | /// shared between all vertex buffers in a pipeline, meaning that even if the 74 | /// data for this attribute comes from a different vertex buffer, it still cannot 75 | /// share the same location with another attribute. 76 | pub location: Location, 77 | /// Binding number of the associated vertex buffer. 78 | pub binding: BufferIndex, 79 | /// Attribute element description. 80 | pub element: Element, 81 | } 82 | 83 | /// Describes whether or not primitive restart is supported for 84 | /// an input assembler. Primitive restart is a feature that 85 | /// allows a mark to be placed in an index buffer where it is 86 | /// is "broken" into multiple pieces of geometry. 87 | /// 88 | /// See 89 | /// for more detail. 90 | #[derive(Clone, Copy, Debug, Eq, Hash, PartialEq, PartialOrd, Ord)] 91 | #[cfg_attr(feature = "serde", derive(Serialize, Deserialize))] 92 | pub enum PrimitiveRestart { 93 | /// No primitive restart. 94 | Disabled, 95 | /// Primitive restart using a 16-bit index value (`std::u16::MAX`). 96 | U16, 97 | /// Primitive restart using a 32-bit index value (`std::u32::MAX`) 98 | U32, 99 | } 100 | 101 | /// All the information needed to create an input assembler. 102 | #[derive(Clone, Debug, Eq, PartialEq, PartialOrd, Ord)] 103 | #[cfg_attr(feature = "serde", derive(Serialize, Deserialize))] 104 | pub struct InputAssemblerDesc { 105 | /// Type of the primitive 106 | pub primitive: Primitive, 107 | /// The primitive restart specification. 108 | pub primitive_restart: PrimitiveRestart, 109 | } 110 | 111 | impl InputAssemblerDesc { 112 | /// Create a new IA descriptor without primitive restart 113 | pub fn new(primitive: Primitive) -> Self { 114 | InputAssemblerDesc { 115 | primitive, 116 | primitive_restart: PrimitiveRestart::Disabled, 117 | } 118 | } 119 | } 120 | -------------------------------------------------------------------------------- /src/hal/src/command/transfer.rs: -------------------------------------------------------------------------------- 1 | //! `CommandBuffer` methods for transfer operations. 2 | use std::borrow::Borrow; 3 | use std::ops::Range; 4 | 5 | use super::{CommandBuffer, Level, RawCommandBuffer, Shot}; 6 | use crate::memory::{Barrier, Dependencies}; 7 | use crate::pso::PipelineStage; 8 | use crate::queue::capability::{Supports, Transfer}; 9 | use crate::range::RangeArg; 10 | use crate::Backend; 11 | use crate::{buffer, image}; 12 | /// Specifies a source region and a destination 13 | /// region in a buffer for copying. All values 14 | /// are in units of bytes. 15 | #[derive(Clone, Copy, Debug)] 16 | #[cfg_attr(feature = "serde", derive(Serialize, Deserialize))] 17 | pub struct BufferCopy { 18 | /// Buffer region source offset. 19 | pub src: buffer::Offset, 20 | /// Buffer region destination offset. 21 | pub dst: buffer::Offset, 22 | /// Region size. 23 | pub size: buffer::Offset, 24 | } 25 | 26 | /// Bundles together all the parameters needed to copy data from one `Image` 27 | /// to another. 28 | #[derive(Clone, Debug)] 29 | #[cfg_attr(feature = "serde", derive(Serialize, Deserialize))] 30 | pub struct ImageCopy { 31 | /// The image subresource to copy from. 32 | pub src_subresource: image::SubresourceLayers, 33 | /// The source offset. 34 | pub src_offset: image::Offset, 35 | /// The image subresource to copy to. 36 | pub dst_subresource: image::SubresourceLayers, 37 | /// The destination offset. 38 | pub dst_offset: image::Offset, 39 | /// The extent of the region to copy. 40 | pub extent: image::Extent, 41 | } 42 | 43 | /// Bundles together all the parameters needed to copy a buffer 44 | /// to an image or vice-versa. 45 | #[derive(Clone, Debug)] 46 | #[cfg_attr(feature = "serde", derive(Serialize, Deserialize))] 47 | pub struct BufferImageCopy { 48 | /// Buffer offset in bytes. 49 | pub buffer_offset: buffer::Offset, 50 | /// Width of a buffer 'row' in texels. 51 | pub buffer_width: u32, 52 | /// Height of a buffer 'image slice' in texels. 53 | pub buffer_height: u32, 54 | /// The image subresource. 55 | pub image_layers: image::SubresourceLayers, 56 | /// The offset of the portion of the image to copy. 57 | pub image_offset: image::Offset, 58 | /// Size of the portion of the image to copy. 59 | pub image_extent: image::Extent, 60 | } 61 | 62 | impl, S: Shot, L: Level> CommandBuffer { 63 | /// Identical to the `RawCommandBuffer` method of the same name. 64 | pub unsafe fn pipeline_barrier<'i, T>( 65 | &mut self, 66 | stages: Range, 67 | dependencies: Dependencies, 68 | barriers: T, 69 | ) where 70 | T: IntoIterator, 71 | T::Item: Borrow>, 72 | { 73 | self.raw.pipeline_barrier(stages, dependencies, barriers) 74 | } 75 | 76 | /// Identical to the `RawCommandBuffer` method of the same name. 77 | pub unsafe fn fill_buffer(&mut self, buffer: &B::Buffer, range: R, data: u32) 78 | where 79 | R: RangeArg, 80 | { 81 | self.raw.fill_buffer(buffer, range, data) 82 | } 83 | 84 | /// Identical to the `RawCommandBuffer` method of the same name. 85 | pub unsafe fn copy_buffer(&mut self, src: &B::Buffer, dst: &B::Buffer, regions: T) 86 | where 87 | T: IntoIterator, 88 | T::Item: Borrow, 89 | { 90 | self.raw.copy_buffer(src, dst, regions) 91 | } 92 | 93 | /// Identical to the `RawCommandBuffer` method of the same name. 94 | pub unsafe fn update_buffer( 95 | &mut self, 96 | buffer: &B::Buffer, 97 | offset: buffer::Offset, 98 | data: &[u8], 99 | ) { 100 | self.raw.update_buffer(buffer, offset, data) 101 | } 102 | 103 | /// Identical to the `RawCommandBuffer` method of the same name. 104 | pub unsafe fn copy_image( 105 | &mut self, 106 | src: &B::Image, 107 | src_layout: image::Layout, 108 | dst: &B::Image, 109 | dst_layout: image::Layout, 110 | regions: T, 111 | ) where 112 | T: IntoIterator, 113 | T::Item: Borrow, 114 | { 115 | self.raw 116 | .copy_image(src, src_layout, dst, dst_layout, regions) 117 | } 118 | 119 | /// Identical to the `RawCommandBuffer` method of the same name. 120 | pub unsafe fn copy_buffer_to_image( 121 | &mut self, 122 | src: &B::Buffer, 123 | dst: &B::Image, 124 | dst_layout: image::Layout, 125 | regions: T, 126 | ) where 127 | T: IntoIterator, 128 | T::Item: Borrow, 129 | { 130 | self.raw.copy_buffer_to_image(src, dst, dst_layout, regions) 131 | } 132 | 133 | /// Identical to the `RawCommandBuffer` method of the same name. 134 | pub unsafe fn copy_image_to_buffer( 135 | &mut self, 136 | src: &B::Image, 137 | src_layout: image::Layout, 138 | dst: &B::Buffer, 139 | regions: T, 140 | ) where 141 | T: IntoIterator, 142 | T::Item: Borrow, 143 | { 144 | self.raw.copy_image_to_buffer(src, src_layout, dst, regions) 145 | } 146 | } 147 | -------------------------------------------------------------------------------- /reftests/scenes/transfer.ron: -------------------------------------------------------------------------------- 1 | ( 2 | resources: { 3 | "buffer.input": Buffer( 4 | size: 4, 5 | usage: (bits: 0x3), //TRANSFER_SRC | TRANSFER_DST 6 | data: "buffer.raw", 7 | ), 8 | "buffer.output": Buffer( 9 | size: 4, 10 | usage: (bits: 0x2), //TRANSFER_DST 11 | ), 12 | "buffer.large": Buffer( 13 | size: 8, 14 | usage: (bits: 0x2), //TRANSFER_DST 15 | data: "image.raw", 16 | ), 17 | "image.input": Image( 18 | kind: D2(2, 1, 1, 1), 19 | num_levels: 1, 20 | format: Rgba8Unorm, 21 | usage: (bits: 0x1), //TRANSFER_SRC 22 | data: "image.raw", 23 | ), 24 | "image.output": Image( 25 | kind: D2(1, 1, 1, 1), 26 | num_levels: 1, 27 | format: Rgba8Unorm, 28 | usage: (bits: 0x3), //TRANSFER_DST | TRANSFER_SRC 29 | ), 30 | "buffer.fill-8-bytes": Buffer( 31 | size: 8, 32 | usage: (bits: 0x3), //TRANSFER_SRC | TRANSFER_DST 33 | data: "fill-8-bytes.raw", 34 | ), 35 | "buffer.fill-10-bytes": Buffer( 36 | size: 10, 37 | usage: (bits: 0x3), //TRANSFER_SRC | TRANSFER_DST 38 | data: "fill-10-bytes.raw", 39 | ), 40 | }, 41 | jobs: { 42 | "copy-buf": Transfer( 43 | CopyBuffer( 44 | src: "buffer.input", 45 | dst: "buffer.output", 46 | regions: [ 47 | ( 48 | src: 0, 49 | dst: 0, 50 | size: 4, 51 | ), 52 | ], 53 | ), 54 | ), 55 | "copy-buf-cut": Transfer( 56 | CopyBuffer( 57 | src: "buffer.input", 58 | dst: "buffer.large", 59 | regions: [ 60 | ( 61 | src: 0, 62 | dst: 4, 63 | size: 2, 64 | ), 65 | ], 66 | ), 67 | ), 68 | "copy-image": Transfer( 69 | CopyImage( 70 | src: "image.input", 71 | dst: "image.output", 72 | regions: [ 73 | ( 74 | src_subresource: ( 75 | aspects: (bits: 0x1), //COLOR 76 | level: 0, 77 | layers: (start: 0, end: 1), 78 | ), 79 | src_offset: (x: 0, y: 0, z: 0), 80 | dst_subresource: ( 81 | aspects: (bits: 0x1), //COLOR 82 | level: 0, 83 | layers: (start: 0, end: 1), 84 | ), 85 | dst_offset: (x: 0, y: 0, z: 0), 86 | extent: ( 87 | width: 1, 88 | height: 1, 89 | depth: 1, 90 | ), 91 | ), 92 | ], 93 | ), 94 | ), 95 | "copy-buf-image": Transfer( 96 | CopyBufferToImage( 97 | src: "buffer.input", 98 | dst: "image.output", 99 | regions: [ 100 | ( 101 | buffer_offset: 0, 102 | buffer_width: 1, 103 | buffer_height: 1, 104 | image_layers: ( 105 | aspects: (bits: 0x1), //COLOR 106 | level: 0, 107 | layers: (start: 0, end: 1), 108 | ), 109 | image_offset: (x: 0, y: 0, z: 0), 110 | image_extent: ( 111 | width: 1, 112 | height: 1, 113 | depth: 1, 114 | ), 115 | ), 116 | ], 117 | ), 118 | ), 119 | "copy-image-buf": Transfer( 120 | CopyImageToBuffer( 121 | src: "image.input", 122 | dst: "buffer.output", 123 | regions: [ 124 | ( 125 | buffer_offset: 0, 126 | buffer_width: 1, 127 | buffer_height: 1, 128 | image_layers: ( 129 | aspects: (bits: 0x1), //COLOR 130 | level: 0, 131 | layers: (start: 0, end: 1), 132 | ), 133 | image_offset: (x: 1, y: 0, z: 0), 134 | image_extent: ( 135 | width: 1, 136 | height: 1, 137 | depth: 1, 138 | ), 139 | ), 140 | ], 141 | ), 142 | ), 143 | "clear-image": Transfer( 144 | ClearImage( 145 | image: "image.output", 146 | color: Float((0.501, 0.501, 0.501, 0.501)), 147 | depth_stencil: (0.0, 0), 148 | ranges: [ 149 | ( 150 | aspects: (bits: 0x1), //COLOR 151 | levels: (start: 0, end: 1), 152 | layers: (start: 0, end: 1), 153 | ), 154 | ], 155 | ), 156 | ), 157 | "blit-image": Transfer( 158 | BlitImage( 159 | src: "image.input", 160 | dst: "image.output", 161 | filter: Linear, 162 | regions: [ 163 | ( 164 | src_subresource: ( 165 | aspects: (bits: 0x1), //COLOR 166 | level: 0, 167 | layers: (start: 0, end: 1), 168 | ), 169 | src_bounds: ( 170 | start: (x: 0, y: 0, z: 0), 171 | end: (x: 2, y: 1, z: 1), 172 | ), 173 | dst_subresource: ( 174 | aspects: (bits: 0x1), //COLOR 175 | level: 0, 176 | layers: (start: 0, end: 1), 177 | ), 178 | dst_bounds: ( 179 | start: (x: 0, y: 0, z: 0), 180 | end: (x: 1, y: 1, z: 1), 181 | ), 182 | ), 183 | ], 184 | ), 185 | ), 186 | "fill-whole": Transfer( 187 | FillBuffer( 188 | buffer: "buffer.fill-8-bytes", 189 | start: Some(0), 190 | end: None, 191 | data: 0xFF0000, 192 | ), 193 | ), 194 | "fill-first": Transfer( 195 | FillBuffer( 196 | buffer: "buffer.fill-8-bytes", 197 | start: Some(0), 198 | end: Some(4), 199 | data: 0xFF00, 200 | ), 201 | ), 202 | "fill-last": Transfer( 203 | FillBuffer( 204 | buffer: "buffer.fill-8-bytes", 205 | start: Some(4), 206 | end: Some(8), 207 | data: 0xFF, 208 | ), 209 | ), 210 | "fill-whole-nearest-multiple": Transfer( 211 | FillBuffer( 212 | buffer: "buffer.fill-10-bytes", 213 | start: Some(0), 214 | end: None, 215 | data: 0xFF, 216 | ), 217 | ), 218 | } 219 | ) 220 | -------------------------------------------------------------------------------- /src/backend/vulkan/src/native.rs: -------------------------------------------------------------------------------- 1 | use ash::version::DeviceV1_0; 2 | use ash::vk; 3 | use hal::image::SubresourceRange; 4 | use hal::pso; 5 | use std::borrow::Borrow; 6 | use std::sync::Arc; 7 | use {Backend, RawDevice}; 8 | 9 | #[derive(Debug, Hash)] 10 | pub struct Semaphore(pub vk::Semaphore); 11 | 12 | #[derive(Debug, Hash, PartialEq, Eq)] 13 | pub struct Fence(pub vk::Fence); 14 | 15 | #[derive(Debug, Hash)] 16 | pub struct GraphicsPipeline(pub vk::Pipeline); 17 | 18 | #[derive(Debug, Hash)] 19 | pub struct ComputePipeline(pub vk::Pipeline); 20 | 21 | #[derive(Debug, Hash)] 22 | pub struct Memory { 23 | pub(crate) raw: vk::DeviceMemory, 24 | } 25 | 26 | #[derive(Copy, Clone, Debug, PartialEq, Eq, Hash)] 27 | pub struct Buffer { 28 | pub(crate) raw: vk::Buffer, 29 | } 30 | 31 | unsafe impl Sync for Buffer {} 32 | unsafe impl Send for Buffer {} 33 | 34 | #[derive(Clone, Debug, Eq, Hash, PartialEq)] 35 | pub struct BufferView { 36 | pub(crate) raw: vk::BufferView, 37 | } 38 | 39 | #[derive(Debug, Hash, PartialEq, Eq)] 40 | pub struct Image { 41 | pub(crate) raw: vk::Image, 42 | pub(crate) ty: vk::ImageType, 43 | pub(crate) flags: vk::ImageCreateFlags, 44 | pub(crate) extent: vk::Extent3D, 45 | } 46 | 47 | #[derive(Debug, Hash, PartialEq, Eq)] 48 | pub struct ImageView { 49 | pub(crate) image: vk::Image, 50 | pub(crate) view: vk::ImageView, 51 | pub(crate) range: SubresourceRange, 52 | } 53 | 54 | #[derive(Debug, Hash)] 55 | pub struct Sampler(pub vk::Sampler); 56 | 57 | #[derive(Debug, Hash)] 58 | pub struct RenderPass { 59 | pub raw: vk::RenderPass, 60 | pub clear_attachments_mask: u64, 61 | } 62 | 63 | #[derive(Debug, Hash)] 64 | pub struct Framebuffer { 65 | pub(crate) raw: vk::Framebuffer, 66 | } 67 | 68 | #[derive(Debug)] 69 | pub struct DescriptorSetLayout { 70 | pub(crate) raw: vk::DescriptorSetLayout, 71 | pub(crate) bindings: Arc>, 72 | } 73 | 74 | #[derive(Debug)] 75 | pub struct DescriptorSet { 76 | pub(crate) raw: vk::DescriptorSet, 77 | pub(crate) bindings: Arc>, 78 | } 79 | 80 | #[derive(Debug, Hash)] 81 | pub struct PipelineLayout { 82 | pub(crate) raw: vk::PipelineLayout, 83 | } 84 | 85 | #[derive(Debug)] 86 | pub struct PipelineCache { 87 | pub(crate) raw: vk::PipelineCache, 88 | } 89 | 90 | #[derive(Debug, Eq, Hash, PartialEq)] 91 | pub struct ShaderModule { 92 | pub(crate) raw: vk::ShaderModule, 93 | } 94 | 95 | #[derive(Debug)] 96 | pub struct DescriptorPool { 97 | pub(crate) raw: vk::DescriptorPool, 98 | pub(crate) device: Arc, 99 | /// This vec only exists to re-use allocations when `DescriptorSet`s are freed. 100 | pub(crate) set_free_vec: Vec, 101 | } 102 | 103 | impl pso::DescriptorPool for DescriptorPool { 104 | unsafe fn allocate_sets( 105 | &mut self, 106 | layout_iter: I, 107 | output: &mut Vec, 108 | ) -> Result<(), pso::AllocationError> 109 | where 110 | I: IntoIterator, 111 | I::Item: Borrow, 112 | { 113 | use std::ptr; 114 | 115 | let mut raw_layouts = Vec::new(); 116 | let mut layout_bindings = Vec::new(); 117 | for layout in layout_iter { 118 | raw_layouts.push(layout.borrow().raw); 119 | layout_bindings.push(layout.borrow().bindings.clone()); 120 | } 121 | 122 | let info = vk::DescriptorSetAllocateInfo { 123 | s_type: vk::StructureType::DESCRIPTOR_SET_ALLOCATE_INFO, 124 | p_next: ptr::null(), 125 | descriptor_pool: self.raw, 126 | descriptor_set_count: raw_layouts.len() as u32, 127 | p_set_layouts: raw_layouts.as_ptr(), 128 | }; 129 | 130 | self.device 131 | .0 132 | .allocate_descriptor_sets(&info) 133 | .map(|sets| { 134 | output.extend( 135 | sets.into_iter() 136 | .zip(layout_bindings) 137 | .map(|(raw, bindings)| DescriptorSet { raw, bindings }), 138 | ) 139 | }) 140 | .map_err(|err| match err { 141 | vk::Result::ERROR_OUT_OF_HOST_MEMORY => pso::AllocationError::OutOfHostMemory, 142 | vk::Result::ERROR_OUT_OF_DEVICE_MEMORY => pso::AllocationError::OutOfDeviceMemory, 143 | vk::Result::ERROR_OUT_OF_POOL_MEMORY => pso::AllocationError::OutOfPoolMemory, 144 | _ => pso::AllocationError::FragmentedPool, 145 | }) 146 | } 147 | 148 | unsafe fn free_sets(&mut self, descriptor_sets: I) 149 | where 150 | I: IntoIterator, 151 | { 152 | self.set_free_vec.clear(); 153 | self.set_free_vec 154 | .extend(descriptor_sets.into_iter().map(|d| d.raw)); 155 | self.device 156 | .0 157 | .free_descriptor_sets(self.raw, &self.set_free_vec); 158 | } 159 | 160 | unsafe fn reset(&mut self) { 161 | assert_eq!( 162 | Ok(()), 163 | self.device 164 | .0 165 | .reset_descriptor_pool(self.raw, vk::DescriptorPoolResetFlags::empty()) 166 | ); 167 | } 168 | } 169 | 170 | #[derive(Debug, Hash)] 171 | pub struct QueryPool(pub vk::QueryPool); 172 | -------------------------------------------------------------------------------- /src/hal/src/memory.rs: -------------------------------------------------------------------------------- 1 | //! Types to describe the properties of memory allocated for gfx resources. 2 | 3 | use std::mem; 4 | use std::ops::Range; 5 | use crate::Backend; 6 | use crate::{buffer, image, queue}; 7 | 8 | /// A trait for plain-old-data types. 9 | /// 10 | /// A POD type does not have invalid bit patterns and can be safely 11 | /// created from arbitrary bit pattern. 12 | /// The `Pod` trait is implemented for standard integer and floating point numbers as well as 13 | /// common arrays of them (for example `[f32; 2]`). 14 | pub unsafe trait Pod: Copy {} 15 | 16 | macro_rules! impl_pod { 17 | ( ty = $($ty:ty)* ) => { $( unsafe impl Pod for $ty {} )* }; 18 | ( ar = $($tt:expr)* ) => { $( unsafe impl Pod for [T; $tt] {} )* }; 19 | } 20 | 21 | impl_pod! { ty = isize usize i8 u8 i16 u16 i32 u32 i64 u64 f32 f64 } 22 | impl_pod! { ar = 23 | 0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 24 | } 25 | 26 | unsafe impl Pod for (T, U) {} 27 | 28 | /// Cast a slice from one POD type to another. 29 | pub fn cast_slice(slice: &[A]) -> &[B] { 30 | use std::slice; 31 | 32 | let raw_len = mem::size_of::().wrapping_mul(slice.len()); 33 | let len = raw_len / mem::size_of::(); 34 | assert_eq!(raw_len, mem::size_of::().wrapping_mul(len)); 35 | unsafe { slice::from_raw_parts(slice.as_ptr() as *const B, len) } 36 | } 37 | 38 | bitflags!( 39 | /// Memory property flags. 40 | #[cfg_attr(feature = "serde", derive(Serialize, Deserialize))] 41 | pub struct Properties: u16 { 42 | /// Device local memory on the GPU. 43 | const DEVICE_LOCAL = 0x1; 44 | 45 | /// Host visible memory can be accessed by the CPU. 46 | /// 47 | /// Backends must provide at least one cpu visible memory. 48 | const CPU_VISIBLE = 0x2; 49 | 50 | /// CPU-GPU coherent. 51 | /// 52 | /// Non-coherent memory requires explicit flushing. 53 | const COHERENT = 0x4; 54 | 55 | /// Cached memory by the CPU 56 | const CPU_CACHED = 0x8; 57 | 58 | /// Memory that may be lazily allocated as needed on the GPU 59 | /// and *must not* be visible to the CPU. 60 | const LAZILY_ALLOCATED = 0x10; 61 | } 62 | ); 63 | 64 | bitflags!( 65 | /// Barrier dependency flags. 66 | #[cfg_attr(feature = "serde", derive(Serialize, Deserialize))] 67 | pub struct Dependencies: u32 { 68 | /// Specifies the memory dependency to be framebuffer-local. 69 | const BY_REGION = 0x1; 70 | //const VIEW_LOCAL = 0x2; 71 | //const DEVICE_GROUP = 0x4; 72 | } 73 | ); 74 | 75 | // DOC TODO: Could be better, but I don't know how to do this without 76 | // trying to explain the whole synchronization model. 77 | /// A [memory barrier](https://www.khronos.org/registry/vulkan/specs/1.0/html/vkspec.html#synchronization-memory-barriers) 78 | /// type for either buffers or images. 79 | #[allow(missing_docs)] 80 | #[derive(Clone, Debug)] 81 | pub enum Barrier<'a, B: Backend> { 82 | /// Applies the given access flags to all buffers in the range. 83 | AllBuffers(Range), 84 | /// Applies the given access flags to all images in the range. 85 | AllImages(Range), 86 | /// A memory barrier that defines access to a buffer. 87 | Buffer { 88 | /// The access flags controlling the buffer. 89 | states: Range, 90 | /// The buffer the barrier controls. 91 | target: &'a B::Buffer, 92 | /// The source and destination Queue family IDs, for a [queue family ownership transfer](https://www.khronos.org/registry/vulkan/specs/1.0/html/vkspec.html#synchronization-queue-transfers) 93 | /// Can be `None` to indicate no ownership transfer. 94 | families: Option>, 95 | /// Range of the buffer the barrier applies to. 96 | range: Range>, 97 | }, 98 | /// A memory barrier that defines access to (a subset of) an image. 99 | Image { 100 | /// The access flags controlling the image. 101 | states: Range, 102 | /// The image the barrier controls. 103 | target: &'a B::Image, 104 | /// The source and destination Queue family IDs, for a [queue family ownership transfer](https://www.khronos.org/registry/vulkan/specs/1.0/html/vkspec.html#synchronization-queue-transfers) 105 | /// Can be `None` to indicate no ownership transfer. 106 | families: Option>, 107 | /// A `SubresourceRange` that defines which section of an image the barrier applies to. 108 | range: image::SubresourceRange, 109 | }, 110 | } 111 | 112 | impl<'a, B: Backend> Barrier<'a, B> { 113 | /// Create a barrier for the whole buffer between the given states. 114 | pub fn whole_buffer(target: &'a B::Buffer, states: Range) -> Self { 115 | Barrier::Buffer { 116 | states, 117 | target, 118 | families: None, 119 | range: None..None, 120 | } 121 | } 122 | } 123 | 124 | /// Memory requirements for a certain resource (buffer/image). 125 | #[derive(Clone, Copy, Debug)] 126 | pub struct Requirements { 127 | /// Size in the memory. 128 | pub size: u64, 129 | /// Memory alignment. 130 | pub alignment: u64, 131 | /// Supported memory types. 132 | pub type_mask: u64, 133 | } 134 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 |

2 | 3 |

4 |
20 | 21 | # gfx-rs 22 | 23 | **PLEASE BE AWARE: this is home to the gfx-hal crate/API. The Old gfx Crate Is Now Deprecated.** 24 | 25 | gfx-rs is a low-level, cross-platform graphics abstraction library in Rust. It consists of the following components: 26 | 27 | * `gfx-hal` which is gfx's hardware abstraction layer: a Vulkan-ic mostly unsafe API which translates to native graphics backends. 28 | * `gfx-backend-*` which contains graphics backends for various platforms: 29 | * [Vulkan](src/backend/vulkan) (confirmed to run on Linux and Windows) 30 | * [DirectX 12](src/backend/dx12) and [11](src/backend/dx11) 31 | * [Metal](src/backend/metal) (confirmed to run on macOS and iOS) 32 | * [OpenGL 2.1+/ES2+](src/backend/gl) 33 | * `gfx-warden` which is a data-driven reference test framework, used to verify consistency across all graphics backends. 34 | 35 | ## The `gfx` crate 36 | 37 | This repository was originally home to the [`gfx`](https://crates.io/crates/gfx) crate, which is now deprecated. You can find the latest versions of the code for that crate in the [`pre-ll`](https://github.com/gfx-rs/gfx/tree/pre-ll) branch of this repository. 38 | 39 | The master branch of this repository is now focused on developing [`gfx-hal`](https://crates.io/crates/gfx-hal) and its associated backend and helper libraries, as described above. `gfx-hal` is a complete rewrite of `gfx`, but it is not necessarily the direct successor to `gfx`. Instead, it serves a different purpose than the original `gfx` crate, by being "lower level" than the original. Hence, the name of `gfx-hal` was originally `ll`, which stands for "lower level", and the original `gfx` is now referred to as `pre-ll`. 40 | 41 | The spiritual successor to the original `gfx` is actually [`wgpu`](https://github.com/gfx-rs/wgpu), which stands on a similar level of abstraction to the old `gfx` crate, but with a modernized API that is more fit for being used over Vulkan/DX12/Metal. If you want something similar to the old `gfx` crate that is being actively developed, `wgpu` is probably what you're looking for, rather than `gfx-hal`. 42 | 43 | ## Example 44 | 45 | To run an example, simply `cd` to the `examples` directory, then use `cargo run` with `--features {backend}` to specify the backend (where `{backend}` is either `vulkan`, `dx12`, `dx11`, `metal`, or `gl`). For example: 46 | 47 | ```bash 48 | # Clone the gfx repository 49 | git clone https://github.com/gfx-rs/gfx 50 | # Change directory to `examples` 51 | cd gfx/examples 52 | # Vulkan 53 | cargo run --bin quad --features vulkan 54 | # Metal (macOS/iOS) 55 | cargo run --bin quad --features metal 56 | # DirectX12 (Windows) 57 | cargo run --bin compute --features dx12 1 2 3 4 58 | ``` 59 | 60 | This runs the `quad` example using the Vulkan backend, and then the `compute` example using the DirectX 12 backend. 61 | 62 | These examples assume that necessary dependencies for the graphics backend are already installed. For more information about installation and usage, refer to the [Getting Started](info/getting_started.md) guide. 63 | 64 | ## Hardware Abstraction Layer 65 | 66 | The Hardware Abstraction Layer (HAL), is a thin, low-level graphics layer which translates API calls to various graphics backends, which allows for cross-platform support. The API of this layer is based on the Vulkan API, adapted to be more Rust-friendly. 67 | 68 |

Hardware Abstraction Layer (HAL)

69 | 70 | Currently HAL has backends for Vulkan, DirectX 12/11, Metal, and OpenGL/OpenGL ES/WebGL. 71 | 72 | The HAL layer is consumed directly by user applications or libraries. HAL is also used in efforts such as [gfx-portability](https://github.com/gfx-rs/portability). 73 | 74 | ## Previously Released Crates and API (pre-LL) 75 | 76 | The code in `master` is a complete low-level rewrite of gfx based on HAL as described above. 77 | 78 | The previously released crates (`gfx_core`, `gfx`, `gfx_device_*`, `gfx_window_`) are still being developed and published from the [pre-ll](https://github.com/gfx-rs/gfx/tree/pre-ll) (pre-low level rewrite) branch. 79 | 80 | ## License 81 | 82 | [license]: #license 83 | 84 | This repository is licensed under either of 85 | 86 | * Apache License, Version 2.0, ([LICENSE-APACHE](LICENSE-APACHE) or http://www.apache.org/licenses/LICENSE-2.0) 87 | * MIT license ([LICENSE-MIT](LICENSE-MIT) or http://opensource.org/licenses/MIT) 88 | 89 | at your option. 90 | 91 | ## Contributions 92 | 93 | Unless you explicitly state otherwise, any contribution intentionally submitted for inclusion in the work by you, as defined in the Apache-2.0 license, shall be dual licensed as above, without any additional terms or conditions. 94 | -------------------------------------------------------------------------------- /reftests/scenes/vertex-offset.ron: -------------------------------------------------------------------------------- 1 | ( 2 | resources: { 3 | "buffer.vertex": Buffer( 4 | size: 48, 5 | usage: (bits: 0x80), // VERTEX 6 | data: "vertex-offset.raw", 7 | ), 8 | "image.color": Image( 9 | kind: D2(1, 1, 1, 1), 10 | num_levels: 1, 11 | format: Rgba32Uint, 12 | usage: (bits: 0x14), //COLOR_ATTACHMENT | SAMPLED (temporary for GL) 13 | ), 14 | "pass": RenderPass( 15 | attachments: { 16 | "c": ( 17 | format: Some(Rgba32Uint), 18 | samples: 1, 19 | ops: (load: Clear, store: Store), 20 | layouts: (start: General, end: General), 21 | ), 22 | }, 23 | subpasses: { 24 | "main": ( 25 | colors: [("c", General)], 26 | depth_stencil: None, 27 | ) 28 | }, 29 | dependencies: [], 30 | ), 31 | "image.color.view": ImageView( 32 | image: "image.color", 33 | kind: D2, 34 | format: Rgba32Uint, 35 | range: ( 36 | aspects: (bits: 1), 37 | levels: (start: 0, end: 1), 38 | layers: (start: 0, end: 1), 39 | ), 40 | ), 41 | "fbo": Framebuffer( 42 | pass: "pass", 43 | views: { 44 | "c": "image.color.view" 45 | }, 46 | extent: ( 47 | width: 1, 48 | height: 1, 49 | depth: 1, 50 | ), 51 | ), 52 | "pipe-layout": PipelineLayout( 53 | set_layouts: [], 54 | push_constant_ranges: [], 55 | ), 56 | "shader.vertex-offset.vs": Shader("vertex-offset.vert"), 57 | "shader.vertex-offset.fs": Shader("vertex-offset.frag"), 58 | "pipe.vertex-offset": GraphicsPipeline( 59 | shaders: ( 60 | vertex: "shader.vertex-offset.vs", 61 | fragment: "shader.vertex-offset.fs", 62 | ), 63 | rasterizer: ( 64 | polygon_mode: Fill, 65 | cull_face: (bits: 0), 66 | front_face: Clockwise, 67 | depth_clamping: false, 68 | depth_bias: None, 69 | conservative: false, 70 | ), 71 | vertex_buffers: [ 72 | VertexBufferDesc( 73 | binding: 0, 74 | stride: 16, 75 | rate: Vertex, 76 | ), 77 | ], 78 | attributes: [ 79 | AttributeDesc( 80 | location: 0, 81 | binding: 0, 82 | element: Element( 83 | offset: 32, 84 | format: Rgba32Uint, 85 | ), 86 | ), 87 | ], 88 | input_assembler: ( 89 | primitive: TriangleList, 90 | primitive_restart: Disabled, 91 | ), 92 | blender: ( 93 | alpha_coverage: false, 94 | logic_op: None, 95 | targets: [ 96 | ((bits: 15), Off), 97 | ], 98 | ), 99 | layout: "pipe-layout", 100 | subpass: ( 101 | parent: "pass", 102 | index: 0, 103 | ), 104 | ), 105 | "pipe.vertex-offset-overlap": GraphicsPipeline( 106 | shaders: ( 107 | vertex: "shader.vertex-offset.vs", 108 | fragment: "shader.vertex-offset.fs", 109 | ), 110 | rasterizer: ( 111 | polygon_mode: Fill, 112 | cull_face: (bits: 0), 113 | front_face: Clockwise, 114 | depth_clamping: false, 115 | depth_bias: None, 116 | conservative: false, 117 | ), 118 | vertex_buffers: [ 119 | VertexBufferDesc( 120 | binding: 0, 121 | stride: 16, 122 | rate: Vertex, 123 | ), 124 | ], 125 | attributes: [ 126 | AttributeDesc( 127 | location: 0, 128 | binding: 0, 129 | element: Element( 130 | offset: 8, 131 | format: Rgba32Uint, 132 | ), 133 | ), 134 | ], 135 | input_assembler: ( 136 | primitive: TriangleList, 137 | primitive_restart: Disabled, 138 | ), 139 | blender: ( 140 | alpha_coverage: false, 141 | logic_op: None, 142 | targets: [ 143 | ((bits: 15), Off), 144 | ], 145 | ), 146 | layout: "pipe-layout", 147 | subpass: ( 148 | parent: "pass", 149 | index: 0, 150 | ), 151 | ), 152 | }, 153 | jobs: { 154 | "offset-aligned": Graphics( 155 | framebuffer: "fbo", 156 | clear_values: [ 157 | Color(Float((0.0, 0.0, 0.0, 0.0))), 158 | ], 159 | pass: ("pass", { 160 | "main": (commands: [ 161 | BindPipeline("pipe.vertex-offset"), 162 | BindVertexBuffers([("buffer.vertex", 0)]), 163 | Draw( 164 | vertices: (start: 0, end: 3), 165 | ), 166 | ]), 167 | }), 168 | ), 169 | "offset-overlap": Graphics( 170 | framebuffer: "fbo", 171 | clear_values: [ 172 | Color(Float((0.0, 0.0, 0.0, 0.0))), 173 | ], 174 | pass: ("pass", { 175 | "main": (commands: [ 176 | BindPipeline("pipe.vertex-offset-overlap"), 177 | BindVertexBuffers([("buffer.vertex", 0)]), 178 | Draw( 179 | vertices: (start: 0, end: 3), 180 | ), 181 | ]), 182 | }), 183 | ), 184 | }, 185 | ) 186 | -------------------------------------------------------------------------------- /src/warden/src/raw.rs: -------------------------------------------------------------------------------- 1 | use std::collections::HashMap; 2 | use std::ops::Range; 3 | 4 | use crate::hal; 5 | 6 | #[derive(Debug, Deserialize)] 7 | pub struct AttachmentRef(pub String, pub hal::pass::AttachmentLayout); 8 | 9 | #[derive(Debug, Deserialize)] 10 | pub struct Subpass { 11 | pub colors: Vec, 12 | pub depth_stencil: Option, 13 | #[serde(default)] 14 | pub inputs: Vec, 15 | #[serde(default)] 16 | pub preserves: Vec, 17 | #[serde(default)] 18 | pub resolves: Vec, 19 | } 20 | 21 | #[derive(Debug, Deserialize)] 22 | pub struct SubpassDependency { 23 | pub passes: Range, 24 | pub stages: Range, 25 | pub accesses: Range, 26 | } 27 | 28 | #[derive(Debug, Deserialize)] 29 | pub struct GraphicsShaderSet { 30 | pub vertex: String, 31 | #[serde(default)] 32 | pub hull: String, 33 | #[serde(default)] 34 | pub domain: String, 35 | #[serde(default)] 36 | pub geometry: String, 37 | #[serde(default)] 38 | pub fragment: String, 39 | } 40 | 41 | #[derive(Debug, Deserialize)] 42 | pub struct SubpassRef { 43 | pub parent: String, 44 | pub index: usize, 45 | } 46 | 47 | #[derive(Debug, Deserialize)] 48 | pub enum Resource { 49 | Buffer { 50 | size: usize, 51 | usage: hal::buffer::Usage, 52 | #[serde(default)] 53 | data: String, 54 | }, 55 | Image { 56 | kind: hal::image::Kind, 57 | num_levels: hal::image::Level, 58 | format: hal::format::Format, 59 | usage: hal::image::Usage, 60 | #[serde(default)] 61 | data: String, 62 | }, 63 | ImageView { 64 | image: String, 65 | kind: hal::image::ViewKind, 66 | format: hal::format::Format, 67 | #[serde(default)] 68 | swizzle: hal::format::Swizzle, 69 | range: hal::image::SubresourceRange, 70 | }, 71 | RenderPass { 72 | attachments: HashMap, 73 | subpasses: HashMap, 74 | dependencies: Vec, 75 | }, 76 | Shader(String), 77 | DescriptorSetLayout { 78 | bindings: Vec, 79 | #[serde(default)] 80 | immutable_samplers: Vec, 81 | }, 82 | DescriptorPool { 83 | capacity: usize, 84 | ranges: Vec, 85 | }, 86 | DescriptorSet { 87 | pool: String, 88 | layout: String, 89 | data: Vec, 90 | }, 91 | PipelineLayout { 92 | set_layouts: Vec, 93 | push_constant_ranges: Vec<(hal::pso::ShaderStageFlags, Range)>, 94 | }, 95 | GraphicsPipeline { 96 | shaders: GraphicsShaderSet, 97 | rasterizer: hal::pso::Rasterizer, 98 | #[serde(default)] 99 | vertex_buffers: Vec, 100 | #[serde(default)] 101 | attributes: Vec, 102 | input_assembler: hal::pso::InputAssemblerDesc, 103 | blender: hal::pso::BlendDesc, 104 | #[serde(default)] 105 | depth_stencil: hal::pso::DepthStencilDesc, 106 | layout: String, 107 | subpass: SubpassRef, 108 | }, 109 | ComputePipeline { 110 | shader: String, 111 | layout: String, 112 | }, 113 | Framebuffer { 114 | pass: String, 115 | views: HashMap, 116 | extent: hal::image::Extent, 117 | }, 118 | } 119 | 120 | #[derive(Debug, Deserialize)] 121 | pub enum TransferCommand { 122 | CopyBuffer { 123 | src: String, 124 | dst: String, 125 | regions: Vec, 126 | }, 127 | CopyImage { 128 | src: String, 129 | dst: String, 130 | regions: Vec, 131 | }, 132 | CopyBufferToImage { 133 | src: String, 134 | dst: String, 135 | regions: Vec, 136 | }, 137 | CopyImageToBuffer { 138 | src: String, 139 | dst: String, 140 | regions: Vec, 141 | }, 142 | ClearImage { 143 | image: String, 144 | color: hal::command::ClearColor, 145 | depth_stencil: hal::command::ClearDepthStencil, 146 | ranges: Vec, 147 | }, 148 | BlitImage { 149 | src: String, 150 | dst: String, 151 | filter: hal::image::Filter, 152 | regions: Vec, 153 | }, 154 | FillBuffer { 155 | buffer: String, 156 | start: Option, 157 | end: Option, 158 | data: u32, 159 | }, 160 | } 161 | 162 | #[derive(Clone, Debug, Deserialize)] 163 | pub enum DescriptorRange { 164 | Buffers(Vec), 165 | Images(Vec), 166 | } 167 | 168 | fn default_instance_range() -> Range { 169 | 0..1 170 | } 171 | 172 | #[derive(Debug, Deserialize)] 173 | pub enum DrawCommand { 174 | BindIndexBuffer { 175 | buffer: String, 176 | offset: hal::buffer::Offset, 177 | index_type: hal::IndexType, 178 | }, 179 | BindVertexBuffers(Vec<(String, hal::buffer::Offset)>), 180 | BindPipeline(String), 181 | BindDescriptorSets { 182 | layout: String, 183 | first: usize, 184 | sets: Vec, 185 | }, 186 | Draw { 187 | vertices: Range, 188 | #[serde(default = "default_instance_range")] 189 | instances: Range, 190 | }, 191 | DrawIndexed { 192 | indices: Range, 193 | base_vertex: hal::VertexOffset, 194 | instances: Range, 195 | }, 196 | SetViewports(Vec), 197 | SetScissors(Vec), 198 | } 199 | 200 | #[derive(Debug, Deserialize)] 201 | pub struct DrawPass { 202 | pub commands: Vec, 203 | } 204 | 205 | #[derive(Debug, Deserialize)] 206 | pub enum Job { 207 | Transfer(TransferCommand), 208 | Graphics { 209 | framebuffer: String, 210 | clear_values: Vec, 211 | pass: (String, HashMap), 212 | }, 213 | Compute { 214 | pipeline: String, 215 | descriptor_sets: Vec, 216 | dispatch: hal::WorkGroupCount, 217 | }, 218 | } 219 | 220 | #[derive(Debug, Deserialize)] 221 | pub struct Scene { 222 | pub resources: HashMap, 223 | pub jobs: HashMap, 224 | } 225 | -------------------------------------------------------------------------------- /src/backend/dx11/src/dxgi.rs: -------------------------------------------------------------------------------- 1 | use hal::adapter::DeviceType; 2 | use hal::AdapterInfo; 3 | 4 | use winapi::shared::guiddef::GUID; 5 | use winapi::shared::{dxgi, dxgi1_2, dxgi1_3, dxgi1_4, dxgi1_5, winerror}; 6 | use winapi::um::unknwnbase::IUnknown; 7 | use winapi::Interface; 8 | 9 | use wio::com::ComPtr; 10 | 11 | use std::ffi::OsString; 12 | use std::mem; 13 | use std::os::windows::ffi::OsStringExt; 14 | use std::ptr; 15 | 16 | #[derive(Debug, Copy, Clone)] 17 | pub(crate) enum DxgiVersion { 18 | /// Capable of the following interfaces: 19 | /// * IDXGIObject 20 | /// * IDXGIDeviceSubObject 21 | /// * IDXGIResource 22 | /// * IDXGIKeyedMutex 23 | /// * IDXGISurface 24 | /// * IDXGISurface1 25 | /// * IDXGIOutput 26 | /// * IDXGISwapChain 27 | /// * IDXGIFactory 28 | /// * IDXGIDevice 29 | /// * IDXGIFactory1 30 | /// * IDXGIAdapter1 31 | /// * IDXGIDevice1 32 | Dxgi1_0, 33 | 34 | /// Capable of the following interfaces: 35 | /// * IDXGIDisplayControl 36 | /// * IDXGIOutputDuplication 37 | /// * IDXGISurface2 38 | /// * IDXGIResource1 39 | /// * IDXGIDevice2 40 | /// * IDXGISwapChain1 41 | /// * IDXGIFactory2 42 | /// * IDXGIAdapter2 43 | /// * IDXGIOutput1 44 | Dxgi1_2, 45 | 46 | /// Capable of the following interfaces: 47 | /// * IDXGIDevice3 48 | /// * IDXGISwapChain2 49 | /// * IDXGIOutput2 50 | /// * IDXGIDecodeSwapChain 51 | /// * IDXGIFactoryMedia 52 | /// * IDXGISwapChainMedia 53 | /// * IDXGIOutput3 54 | Dxgi1_3, 55 | 56 | /// Capable of the following interfaces: 57 | /// * IDXGISwapChain3 58 | /// * IDXGIOutput4 59 | /// * IDXGIFactory4 60 | /// * IDXGIAdapter3 61 | Dxgi1_4, 62 | 63 | /// Capable of the following interfaces: 64 | /// * IDXGIOutput5 65 | /// * IDXGISwapChain4 66 | /// * IDXGIDevice4 67 | /// * IDXGIFactory5 68 | Dxgi1_5, 69 | } 70 | 71 | fn create_dxgi_factory1(guid: &GUID) -> Result, winerror::HRESULT> { 72 | let mut factory: *mut IUnknown = ptr::null_mut(); 73 | 74 | let hr = unsafe { dxgi::CreateDXGIFactory1(guid, &mut factory as *mut *mut _ as *mut *mut _) }; 75 | 76 | if winerror::SUCCEEDED(hr) { 77 | Ok(unsafe { ComPtr::from_raw(factory as *mut _) }) 78 | } else { 79 | Err(hr) 80 | } 81 | } 82 | 83 | pub(crate) fn get_dxgi_factory( 84 | ) -> Result<(ComPtr, DxgiVersion), winerror::HRESULT> { 85 | // TODO: do we even need `create_dxgi_factory2`? 86 | if let Ok(factory) = create_dxgi_factory1(&dxgi1_5::IDXGIFactory5::uuidof()) { 87 | return Ok((factory, DxgiVersion::Dxgi1_5)); 88 | } 89 | 90 | if let Ok(factory) = create_dxgi_factory1(&dxgi1_4::IDXGIFactory4::uuidof()) { 91 | return Ok((factory, DxgiVersion::Dxgi1_4)); 92 | } 93 | 94 | if let Ok(factory) = create_dxgi_factory1(&dxgi1_3::IDXGIFactory3::uuidof()) { 95 | return Ok((factory, DxgiVersion::Dxgi1_3)); 96 | } 97 | 98 | if let Ok(factory) = create_dxgi_factory1(&dxgi1_2::IDXGIFactory2::uuidof()) { 99 | return Ok((factory, DxgiVersion::Dxgi1_2)); 100 | } 101 | 102 | if let Ok(factory) = create_dxgi_factory1(&dxgi::IDXGIFactory1::uuidof()) { 103 | return Ok((factory, DxgiVersion::Dxgi1_0)); 104 | } 105 | 106 | // TODO: any reason why above would fail and this wouldnt? 107 | match create_dxgi_factory1(&dxgi::IDXGIFactory::uuidof()) { 108 | Ok(factory) => Ok((factory, DxgiVersion::Dxgi1_0)), 109 | Err(hr) => Err(hr), 110 | } 111 | } 112 | 113 | fn enum_adapters1( 114 | idx: u32, 115 | factory: *mut dxgi::IDXGIFactory, 116 | ) -> Result, winerror::HRESULT> { 117 | let mut adapter: *mut dxgi::IDXGIAdapter = ptr::null_mut(); 118 | 119 | let hr = unsafe { 120 | (*(factory as *mut dxgi::IDXGIFactory1)) 121 | .EnumAdapters1(idx, &mut adapter as *mut *mut _ as *mut *mut _) 122 | }; 123 | 124 | if winerror::SUCCEEDED(hr) { 125 | Ok(unsafe { ComPtr::from_raw(adapter) }) 126 | } else { 127 | Err(hr) 128 | } 129 | } 130 | 131 | fn get_adapter_desc(adapter: *mut dxgi::IDXGIAdapter, version: DxgiVersion) -> AdapterInfo { 132 | match version { 133 | DxgiVersion::Dxgi1_0 => { 134 | let mut desc: dxgi::DXGI_ADAPTER_DESC1 = unsafe { mem::zeroed() }; 135 | unsafe { 136 | (*(adapter as *mut dxgi::IDXGIAdapter1)).GetDesc1(&mut desc); 137 | } 138 | 139 | let device_name = { 140 | let len = desc.Description.iter().take_while(|&&c| c != 0).count(); 141 | let name = ::from_wide(&desc.Description[..len]); 142 | name.to_string_lossy().into_owned() 143 | }; 144 | 145 | AdapterInfo { 146 | name: device_name, 147 | vendor: desc.VendorId as usize, 148 | device: desc.DeviceId as usize, 149 | device_type: if (desc.Flags & dxgi::DXGI_ADAPTER_FLAG_SOFTWARE) != 0 { 150 | DeviceType::VirtualGpu 151 | } else { 152 | DeviceType::DiscreteGpu 153 | }, 154 | } 155 | } 156 | DxgiVersion::Dxgi1_2 157 | | DxgiVersion::Dxgi1_3 158 | | DxgiVersion::Dxgi1_4 159 | | DxgiVersion::Dxgi1_5 => { 160 | let mut desc: dxgi1_2::DXGI_ADAPTER_DESC2 = unsafe { mem::zeroed() }; 161 | unsafe { 162 | (*(adapter as *mut dxgi1_2::IDXGIAdapter2)).GetDesc2(&mut desc); 163 | } 164 | 165 | let device_name = { 166 | let len = desc.Description.iter().take_while(|&&c| c != 0).count(); 167 | let name = ::from_wide(&desc.Description[..len]); 168 | name.to_string_lossy().into_owned() 169 | }; 170 | 171 | AdapterInfo { 172 | name: device_name, 173 | vendor: desc.VendorId as usize, 174 | device: desc.DeviceId as usize, 175 | device_type: if (desc.Flags & dxgi::DXGI_ADAPTER_FLAG_SOFTWARE) != 0 { 176 | DeviceType::VirtualGpu 177 | } else { 178 | DeviceType::DiscreteGpu 179 | }, 180 | } 181 | } 182 | } 183 | } 184 | 185 | pub(crate) fn get_adapter( 186 | idx: u32, 187 | factory: *mut dxgi::IDXGIFactory, 188 | version: DxgiVersion, 189 | ) -> Result<(ComPtr, AdapterInfo), winerror::HRESULT> { 190 | let adapter = match version { 191 | DxgiVersion::Dxgi1_0 192 | | DxgiVersion::Dxgi1_2 193 | | DxgiVersion::Dxgi1_3 194 | | DxgiVersion::Dxgi1_4 195 | | DxgiVersion::Dxgi1_5 => enum_adapters1(idx, factory)?, 196 | }; 197 | 198 | let desc = get_adapter_desc(adapter.as_raw(), version); 199 | 200 | Ok((adapter, desc)) 201 | } 202 | -------------------------------------------------------------------------------- /src/hal/src/queue/mod.rs: -------------------------------------------------------------------------------- 1 | //! Command queues. 2 | //! 3 | //! Queues are the execution paths of the graphical processing units. These process 4 | //! submitted commands buffers. 5 | //! 6 | //! There are different types of queues, which can only handle associated command buffers. 7 | //! `CommandQueue` has the capability defined by `C`: graphics, compute and transfer. 8 | 9 | pub mod capability; 10 | pub mod family; 11 | 12 | use std::any::Any; 13 | use std::borrow::Borrow; 14 | use std::iter; 15 | use std::marker::PhantomData; 16 | 17 | use crate::command::{Primary, Submittable}; 18 | use crate::error::HostExecutionError; 19 | use crate::pso; 20 | use crate::window::SwapImageIndex; 21 | use crate::Backend; 22 | 23 | pub use self::capability::{Capability, Compute, General, Graphics, Supports, Transfer}; 24 | pub use self::family::{QueueFamily, QueueFamilyId, QueueGroup, Queues}; 25 | 26 | /// The type of the queue, an enum encompassing `queue::Capability` 27 | #[derive(Debug, Copy, Clone, PartialEq, Eq)] 28 | #[cfg_attr(feature = "serde", derive(Serialize, Deserialize))] 29 | pub enum QueueType { 30 | /// Supports all operations. 31 | General, 32 | /// Only supports graphics and transfer operations. 33 | Graphics, 34 | /// Only supports compute and transfer operations. 35 | Compute, 36 | /// Only supports transfer operations. 37 | Transfer, 38 | } 39 | 40 | /// Submission information for a command queue. 41 | pub struct Submission { 42 | /// Command buffers to submit. 43 | pub command_buffers: Ic, 44 | /// Semaphores to wait being signalled before submission. 45 | pub wait_semaphores: Iw, 46 | /// Semaphores to signal after all command buffers in the submission have finished execution. 47 | pub signal_semaphores: Is, 48 | } 49 | 50 | /// `RawCommandQueue` are abstractions to the internal GPU execution engines. 51 | /// Commands are executed on the the device by submitting command buffers to queues. 52 | pub trait RawCommandQueue: Any + Send + Sync { 53 | /// Submit command buffers to queue for execution. 54 | /// `fence` must be in unsignalled state, and will be signalled after all command buffers in the submission have 55 | /// finished execution. 56 | /// 57 | /// Unsafe because it's not checked that the queue can process the submitted command buffers. 58 | /// Trying to submit compute commands to a graphics queue will result in undefined behavior. 59 | /// Each queue implements safe wrappers according to their supported functionalities! 60 | unsafe fn submit<'a, T, Ic, S, Iw, Is>( 61 | &mut self, 62 | submission: Submission, 63 | fence: Option<&B::Fence>, 64 | ) where 65 | T: 'a + Borrow, 66 | Ic: IntoIterator, 67 | S: 'a + Borrow, 68 | Iw: IntoIterator, 69 | Is: IntoIterator; 70 | 71 | /// Presents the result of the queue to the given swapchains, after waiting on all the 72 | /// semaphores given in `wait_semaphores`. A given swapchain must not appear in this 73 | /// list more than once. 74 | /// 75 | /// Unsafe for the same reasons as `submit()`. 76 | unsafe fn present<'a, W, Is, S, Iw>( 77 | &mut self, 78 | swapchains: Is, 79 | wait_semaphores: Iw, 80 | ) -> Result<(), ()> 81 | where 82 | Self: Sized, 83 | W: 'a + Borrow, 84 | Is: IntoIterator, 85 | S: 'a + Borrow, 86 | Iw: IntoIterator; 87 | 88 | /// Wait for the queue to idle. 89 | fn wait_idle(&self) -> Result<(), HostExecutionError>; 90 | } 91 | 92 | /// Stronger-typed and safer `CommandQueue` wraps around `RawCommandQueue`. 93 | pub struct CommandQueue(B::CommandQueue, PhantomData); 94 | 95 | impl CommandQueue { 96 | /// Create typed command queue from raw. 97 | /// 98 | /// # Safety 99 | /// 100 | /// `::supported_by(queue_type)` must return true 101 | /// for `queue_type` being the type this `raw` queue. 102 | pub unsafe fn new(raw: B::CommandQueue) -> Self { 103 | CommandQueue(raw, PhantomData) 104 | } 105 | 106 | /// Get a reference to the raw command queue 107 | pub fn as_raw(&self) -> &B::CommandQueue { 108 | &self.0 109 | } 110 | 111 | /// Get a mutable reference to the raw command queue 112 | pub unsafe fn as_raw_mut(&mut self) -> &mut B::CommandQueue { 113 | &mut self.0 114 | } 115 | 116 | /// Downgrade a typed command queue to untyped one. 117 | pub fn into_raw(self) -> B::CommandQueue { 118 | self.0 119 | } 120 | 121 | /// Submit command buffers to queue for execution. 122 | /// `fence` must be in unsignalled state, and will be signalled after all command buffers in the submission have 123 | /// finished execution. 124 | pub unsafe fn submit<'a, T, Ic, S, Iw, Is>( 125 | &mut self, 126 | submission: Submission, 127 | fence: Option<&B::Fence>, 128 | ) where 129 | T: 'a + Submittable, 130 | Ic: IntoIterator, 131 | S: 'a + Borrow, 132 | Iw: IntoIterator, 133 | Is: IntoIterator, 134 | { 135 | self.0.submit(submission, fence) 136 | } 137 | 138 | /// Submit command buffers without any semaphore waits or signals. 139 | pub unsafe fn submit_nosemaphores<'a, T, I>( 140 | &mut self, 141 | command_buffers: I, 142 | fence: Option<&B::Fence>, 143 | ) where 144 | T: 'a + Submittable, 145 | I: IntoIterator, 146 | { 147 | let submission = Submission { 148 | command_buffers, 149 | wait_semaphores: iter::empty(), 150 | signal_semaphores: iter::empty(), 151 | }; 152 | self.submit::<_, _, B::Semaphore, _, _>(submission, fence) 153 | } 154 | 155 | /// Presents the result of the queue to the given swapchains, after waiting on all the 156 | /// semaphores given in `wait_semaphores`. A given swapchain must not appear in this 157 | /// list more than once. 158 | pub unsafe fn present<'a, W, Is, S, Iw>( 159 | &mut self, 160 | swapchains: Is, 161 | wait_semaphores: Iw, 162 | ) -> Result<(), ()> 163 | where 164 | W: 'a + Borrow, 165 | Is: IntoIterator, 166 | S: 'a + Borrow, 167 | Iw: IntoIterator, 168 | { 169 | self.0.present(swapchains, wait_semaphores) 170 | } 171 | 172 | /// Wait for the queue to idle. 173 | pub fn wait_idle(&self) -> Result<(), HostExecutionError> { 174 | self.0.wait_idle() 175 | } 176 | 177 | /// Downgrade a command queue to a lesser capability type. 178 | pub unsafe fn downgrade(&mut self) -> &mut CommandQueue 179 | where 180 | C: Supports, 181 | { 182 | ::std::mem::transmute(self) 183 | } 184 | } 185 | -------------------------------------------------------------------------------- /src/hal/src/pass.rs: -------------------------------------------------------------------------------- 1 | //! RenderPass handling. 2 | 3 | use crate::format::Format; 4 | use crate::image; 5 | use crate::pso::PipelineStage; 6 | use std::ops::Range; 7 | use crate::Backend; 8 | 9 | /// Specifies the operation which will be applied at the beginning of a subpass. 10 | #[derive(Copy, Clone, Debug, Hash, Eq, PartialEq)] 11 | #[cfg_attr(feature = "serde", derive(Serialize, Deserialize))] 12 | pub enum AttachmentLoadOp { 13 | /// Preserve existing content in the attachment. 14 | Load, 15 | /// Clear the attachment. 16 | Clear, 17 | /// Attachment content will be undefined. 18 | DontCare, 19 | } 20 | 21 | /// 22 | #[derive(Copy, Clone, Debug, Hash, Eq, PartialEq)] 23 | #[cfg_attr(feature = "serde", derive(Serialize, Deserialize))] 24 | pub enum AttachmentStoreOp { 25 | /// Content written to the attachment will be preserved. 26 | Store, 27 | /// Attachment content will be undefined. 28 | DontCare, 29 | } 30 | 31 | /// Image layout of an attachment. 32 | pub type AttachmentLayout = image::Layout; 33 | 34 | /// Attachment operations. 35 | #[derive(Copy, Clone, Debug, Hash, Eq, PartialEq)] 36 | #[cfg_attr(feature = "serde", derive(Serialize, Deserialize))] 37 | pub struct AttachmentOps { 38 | /// Indicates how the data of the attachment will be loaded at first usage at 39 | /// the beginning of the subpass. 40 | pub load: AttachmentLoadOp, 41 | /// Whether or not data from the store operation will be preserved after the subpass. 42 | pub store: AttachmentStoreOp, 43 | } 44 | 45 | impl AttachmentOps { 46 | /// Specifies `DontCare` for both load and store op. 47 | pub const DONT_CARE: Self = AttachmentOps { 48 | load: AttachmentLoadOp::DontCare, 49 | store: AttachmentStoreOp::DontCare, 50 | }; 51 | 52 | /// Specifies `Load` for load op and `Store` for store op. 53 | pub const PRESERVE: Self = AttachmentOps { 54 | load: AttachmentLoadOp::Load, 55 | store: AttachmentStoreOp::Store, 56 | }; 57 | 58 | /// Convenience function to create a new `AttachmentOps`. 59 | pub fn new(load: AttachmentLoadOp, store: AttachmentStoreOp) -> Self { 60 | AttachmentOps { load, store } 61 | } 62 | 63 | /// A method to provide `AttachmentOps::DONT_CARE` to things that expect 64 | /// a default function rather than a value. 65 | #[cfg(feature = "serde")] 66 | fn whatever() -> Self { 67 | Self::DONT_CARE 68 | } 69 | } 70 | 71 | /// An `Attachment` is a description of a resource provided to a render subpass. 72 | /// It includes things such as render targets, images that were produced from 73 | /// previous subpasses, etc. 74 | #[derive(Clone, Debug, Hash, PartialEq)] 75 | #[cfg_attr(feature = "serde", derive(Serialize, Deserialize))] 76 | pub struct Attachment { 77 | /// Attachment format 78 | /// 79 | /// In the most cases `format` is not `None`. It should be only used for 80 | /// creating dummy renderpasses, which are used as placeholder for compatible 81 | /// renderpasses. 82 | pub format: Option, 83 | /// Number of samples. 84 | pub samples: image::NumSamples, 85 | /// Load and store operations of the attachment 86 | pub ops: AttachmentOps, 87 | /// Load and store operations of the stencil aspect, if any 88 | #[cfg_attr(feature = "serde", serde(default = "AttachmentOps::whatever"))] 89 | pub stencil_ops: AttachmentOps, 90 | /// Initial and final image layouts of the renderpass. 91 | pub layouts: Range, 92 | } 93 | 94 | impl Attachment { 95 | /// Returns true if this attachment has some clear operations. This is useful 96 | /// when starting a render pass, since there has to be a clear value provided. 97 | pub fn has_clears(&self) -> bool { 98 | self.ops.load == AttachmentLoadOp::Clear || self.stencil_ops.load == AttachmentLoadOp::Clear 99 | } 100 | } 101 | 102 | /// Index of an attachment within a framebuffer/renderpass, 103 | pub type AttachmentId = usize; 104 | /// Reference to an attachment by index and expected image layout. 105 | pub type AttachmentRef = (AttachmentId, AttachmentLayout); 106 | 107 | /// Which other subpasses a particular subpass depends on. 108 | #[derive(Copy, Clone, Debug, Hash, PartialEq)] 109 | #[cfg_attr(feature = "serde", derive(Serialize, Deserialize))] 110 | pub enum SubpassRef { 111 | /// The subpass depends on something that was submitted to the 112 | /// queue before or after the render pass began. 113 | External, 114 | /// The subpass depends on another subpass with the given index, 115 | /// which must be less than or equal to the index of the current 116 | /// subpass. The index here refers to the corresponding 117 | /// `SubpassId` of a `Subpass`. 118 | Pass(usize), 119 | } 120 | 121 | /// Expresses a dependency between multiple subpasses. This is used 122 | /// both to describe a source or destination subpass; data either 123 | /// explicitly passes from this subpass to the next or from another 124 | /// subpass into this one. 125 | #[derive(Clone, Debug, Hash)] 126 | #[cfg_attr(feature = "serde", derive(Serialize, Deserialize))] 127 | pub struct SubpassDependency { 128 | /// Other subpasses this one depends on. 129 | pub passes: Range, 130 | /// Other pipeline stages this subpass depends on. 131 | pub stages: Range, 132 | /// Resource accesses this subpass depends on. 133 | pub accesses: Range, 134 | } 135 | 136 | /// Description of a subpass for renderpass creation. 137 | pub struct SubpassDesc<'a> { 138 | /// Which attachments will be used as color buffers. 139 | pub colors: &'a [AttachmentRef], 140 | /// Which attachments will be used as depth/stencil buffers. 141 | pub depth_stencil: Option<&'a AttachmentRef>, 142 | /// Which attachments will be used as input attachments. 143 | pub inputs: &'a [AttachmentRef], 144 | /// Which attachments will be used as resolve destinations. 145 | /// 146 | /// The number of resolve attachments may be zero or equal to the number of color attachments. 147 | /// At the end of a subpass the color attachment will be resolved to the corresponding 148 | /// resolve attachment. The resolve attachment must not be multisampled. 149 | pub resolves: &'a [AttachmentRef], 150 | /// Attachments that are not used by the subpass but must be preserved to be 151 | /// passed on to subsequent passes. 152 | pub preserves: &'a [AttachmentId], 153 | } 154 | 155 | /// Index of a subpass. 156 | pub type SubpassId = usize; 157 | 158 | /// A sub-pass borrow of a pass. 159 | #[derive(Debug)] 160 | pub struct Subpass<'a, B: Backend> { 161 | /// Index of the subpass 162 | pub index: SubpassId, 163 | /// Main pass borrow. 164 | pub main_pass: &'a B::RenderPass, 165 | } 166 | 167 | impl<'a, B: Backend> Clone for Subpass<'a, B> { 168 | fn clone(&self) -> Self { 169 | Subpass { 170 | index: self.index, 171 | main_pass: self.main_pass, 172 | } 173 | } 174 | } 175 | 176 | impl<'a, B: Backend> PartialEq for Subpass<'a, B> { 177 | fn eq(&self, other: &Self) -> bool { 178 | self.index == other.index && self.main_pass as *const _ == other.main_pass as *const _ 179 | } 180 | } 181 | 182 | impl<'a, B: Backend> Copy for Subpass<'a, B> {} 183 | impl<'a, B: Backend> Eq for Subpass<'a, B> {} 184 | -------------------------------------------------------------------------------- /src/backend/gl/src/window/glutin.rs: -------------------------------------------------------------------------------- 1 | //! Window creation using glutin for gfx. 2 | //! 3 | //! # Examples 4 | //! 5 | //! The following code creates a `gfx::Surface` using glutin. 6 | //! 7 | //! ```no_run 8 | //! extern crate glutin; 9 | //! extern crate gfx_backend_gl; 10 | //! 11 | //! fn main() { 12 | //! use gfx_backend_gl::Surface; 13 | //! use glutin::{EventsLoop, WindowBuilder, ContextBuilder, GlWindow}; 14 | //! 15 | //! // First create a window using glutin. 16 | //! let mut events_loop = EventsLoop::new(); 17 | //! let wb = WindowBuilder::new(); 18 | //! let cb = ContextBuilder::new().with_vsync(true); 19 | //! let glutin_window = GlWindow::new(wb, cb, &events_loop).unwrap(); 20 | //! 21 | //! // Then use the glutin window to create a gfx surface. 22 | //! let surface = Surface::from_window(glutin_window); 23 | //! } 24 | //! ``` 25 | //! 26 | //! Headless initialization without a window. 27 | //! 28 | //! ```no_run 29 | //! extern crate glutin; 30 | //! extern crate gfx_backend_gl; 31 | //! extern crate gfx_hal; 32 | //! 33 | //! use gfx_hal::Instance; 34 | //! use gfx_backend_gl::Headless; 35 | //! use glutin::{Context, ContextBuilder, EventsLoop}; 36 | //! 37 | //! fn main() { 38 | //! let events_loop = EventsLoop::new(); 39 | //! let context = Context::new(&events_loop, ContextBuilder::new(), false) 40 | //! .expect("Failed to build headless context"); 41 | //! let headless = Headless(context); 42 | //! let _adapters = headless.enumerate_adapters(); 43 | //! } 44 | //! ``` 45 | 46 | use hal::{ 47 | self, 48 | format as f, image, 49 | CompositeAlpha, 50 | }; 51 | 52 | use crate::{ 53 | native, 54 | Backend as B, Device, PhysicalDevice, QueueFamily, Starc 55 | }; 56 | 57 | use glutin::{self, GlContext}; 58 | 59 | fn get_window_extent(window: &glutin::GlWindow) -> image::Extent { 60 | let px = window 61 | .get_inner_size() 62 | .unwrap() 63 | .to_physical(window.get_hidpi_factor()); 64 | image::Extent { 65 | width: px.width as image::Size, 66 | height: px.height as image::Size, 67 | depth: 1, 68 | } 69 | } 70 | 71 | pub struct Swapchain { 72 | // Underlying window, required for presentation 73 | pub(crate) window: Starc, 74 | } 75 | 76 | impl hal::Swapchain for Swapchain { 77 | unsafe fn acquire_image( 78 | &mut self, 79 | _timeout_ns: u64, 80 | _semaphore: Option<&native::Semaphore>, 81 | _fence: Option<&native::Fence>, 82 | ) -> Result { 83 | // TODO: sync 84 | Ok(0) 85 | } 86 | } 87 | 88 | //TODO: if we make `Surface` a `WindowBuilder` instead of `GlWindow`, 89 | // we could spawn window + GL context when a swapchain is requested 90 | // and actually respect the swapchain configuration provided by the user. 91 | pub struct Surface { 92 | window: Starc, 93 | } 94 | 95 | impl Surface { 96 | pub fn from_window(window: glutin::GlWindow) -> Self { 97 | Surface { 98 | window: Starc::new(window), 99 | } 100 | } 101 | 102 | pub fn get_window(&self) -> &glutin::GlWindow { 103 | &*self.window 104 | } 105 | 106 | pub fn window(&self) -> &glutin::GlWindow { 107 | &self.window 108 | } 109 | 110 | fn swapchain_formats(&self) -> Vec { 111 | let pixel_format = self.window.get_pixel_format(); 112 | let color_bits = pixel_format.color_bits; 113 | let alpha_bits = pixel_format.alpha_bits; 114 | let srgb = pixel_format.srgb; 115 | 116 | // TODO: expose more formats 117 | match (color_bits, alpha_bits, srgb) { 118 | (24, 8, true) => vec![f::Format::Rgba8Srgb, f::Format::Bgra8Srgb], 119 | (24, 8, false) => vec![f::Format::Rgba8Unorm, f::Format::Bgra8Unorm], 120 | _ => vec![], 121 | } 122 | } 123 | } 124 | 125 | impl hal::Surface for Surface { 126 | fn kind(&self) -> hal::image::Kind { 127 | let ex = get_window_extent(&self.window); 128 | let samples = self.window.get_pixel_format().multisampling.unwrap_or(1); 129 | hal::image::Kind::D2(ex.width, ex.height, 1, samples as _) 130 | } 131 | 132 | fn compatibility( 133 | &self, 134 | _: &PhysicalDevice, 135 | ) -> ( 136 | hal::SurfaceCapabilities, 137 | Option>, 138 | Vec, 139 | ) { 140 | let ex = get_window_extent(&self.window); 141 | let extent = hal::window::Extent2D::from(ex); 142 | 143 | let caps = hal::SurfaceCapabilities { 144 | image_count: if self.window.get_pixel_format().double_buffer { 145 | 2..3 146 | } else { 147 | 1..2 148 | }, 149 | current_extent: Some(extent), 150 | extents: extent..hal::window::Extent2D { 151 | width: ex.width + 1, 152 | height: ex.height + 1, 153 | }, 154 | max_image_layers: 1, 155 | usage: image::Usage::COLOR_ATTACHMENT | image::Usage::TRANSFER_SRC, 156 | composite_alpha: CompositeAlpha::OPAQUE, //TODO 157 | }; 158 | let present_modes = vec![ 159 | hal::PresentMode::Fifo, //TODO 160 | ]; 161 | 162 | (caps, Some(self.swapchain_formats()), present_modes) 163 | } 164 | 165 | fn supports_queue_family(&self, _: &QueueFamily) -> bool { 166 | true 167 | } 168 | } 169 | 170 | impl Device { 171 | pub(crate) fn create_swapchain_impl( 172 | &self, 173 | surface: &mut Surface, 174 | _config: hal::SwapchainConfig, 175 | ) -> (Swapchain, hal::Backbuffer) { 176 | let swapchain = Swapchain { 177 | window: surface.window.clone(), 178 | }; 179 | let backbuffer = hal::Backbuffer::Framebuffer(0); 180 | (swapchain, backbuffer) 181 | } 182 | } 183 | 184 | impl hal::Instance for Surface { 185 | type Backend = B; 186 | fn enumerate_adapters(&self) -> Vec> { 187 | unsafe { self.window.make_current().unwrap() }; 188 | let adapter = PhysicalDevice::new_adapter(|s| self.window.get_proc_address(s) as *const _); 189 | vec![adapter] 190 | } 191 | } 192 | 193 | pub fn config_context( 194 | builder: glutin::ContextBuilder, 195 | color_format: f::Format, 196 | ds_format: Option, 197 | ) -> glutin::ContextBuilder { 198 | let color_base = color_format.base_format(); 199 | let color_bits = color_base.0.describe_bits(); 200 | let depth_bits = match ds_format { 201 | Some(fm) => fm.base_format().0.describe_bits(), 202 | None => f::BITS_ZERO, 203 | }; 204 | builder 205 | .with_depth_buffer(depth_bits.depth) 206 | .with_stencil_buffer(depth_bits.stencil) 207 | .with_pixel_format(color_bits.color, color_bits.alpha) 208 | .with_srgb(color_base.1 == f::ChannelType::Srgb) 209 | } 210 | 211 | pub struct Headless(pub glutin::Context); 212 | 213 | unsafe impl Send for Headless {} 214 | unsafe impl Sync for Headless {} 215 | 216 | impl hal::Instance for Headless { 217 | type Backend = B; 218 | fn enumerate_adapters(&self) -> Vec> { 219 | unsafe { self.0.make_current().unwrap() }; 220 | let adapter = PhysicalDevice::new_adapter(|s| self.0.get_proc_address(s) as *const _); 221 | vec![adapter] 222 | } 223 | } 224 | -------------------------------------------------------------------------------- /src/hal/src/command/mod.rs: -------------------------------------------------------------------------------- 1 | //! Command buffers. 2 | //! 3 | //! A command buffer collects a list of commands to be submitted to the device. 4 | //! Each command buffer has specific capabilities for graphics, compute or transfer operations, 5 | //! and can be either a "primary" command buffer or a "secondary" command buffer. Operations 6 | //! always start from a primary command buffer, but a primary command buffer can contain calls 7 | //! to secondary command buffers that contain snippets of commands that do specific things, similar 8 | //! to function calls. 9 | //! 10 | //! All the possible commands are implemented in the `RawCommandBuffer` trait, and then the `CommandBuffer` 11 | //! and related types make a generic, strongly-typed wrapper around it that only expose the methods that 12 | //! are valid for the capabilities it provides. 13 | 14 | // TODO: Document pipelines and subpasses better. 15 | 16 | use crate::queue::capability::{Capability, Supports}; 17 | use crate::Backend; 18 | 19 | use std::borrow::Borrow; 20 | use std::marker::PhantomData; 21 | 22 | mod compute; 23 | mod graphics; 24 | mod raw; 25 | mod render_pass; 26 | mod transfer; 27 | 28 | pub use self::graphics::*; 29 | pub use self::raw::{ 30 | ClearColorRaw, ClearDepthStencilRaw, ClearValueRaw, CommandBufferFlags, 31 | CommandBufferInheritanceInfo, DescriptorSetOffset, IntoRawCommandBuffer, Level as RawLevel, 32 | RawCommandBuffer, 33 | }; 34 | pub use self::render_pass::*; 35 | pub use self::transfer::*; 36 | 37 | /// Trait indicating how many times a Submit object can be submitted to a command buffer. 38 | pub trait Shot {} 39 | /// Indicates a Submit that can only be submitted once. 40 | pub enum OneShot {} 41 | impl Shot for OneShot {} 42 | 43 | /// Indicates a Submit that can be submitted multiple times. 44 | pub enum MultiShot {} 45 | impl Shot for MultiShot {} 46 | 47 | /// A trait indicating the level of a command buffer. 48 | pub trait Level {} 49 | 50 | /// Indicates a primary command buffer. 51 | 52 | /// Vulkan describes a primary command buffer as one which can be directly submitted 53 | /// to a queue, and can execute `Secondary` command buffers. 54 | pub enum Primary {} 55 | impl Level for Primary {} 56 | 57 | /// Indicates a secondary command buffer. 58 | /// 59 | /// Vulkan describes a secondary command buffer as one which cannot be directly submitted 60 | /// to a queue, but can be executed by a primary command buffer. This allows 61 | /// multiple secondary command buffers to be constructed which do specific 62 | /// things and can then be composed together into primary command buffers. 63 | pub enum Secondary {} 64 | impl Level for Secondary {} 65 | 66 | /// A property of a command buffer to be submitted to a queue with specific capability. 67 | pub trait Submittable: Borrow {} 68 | 69 | /// A convenience alias for not typing out the full signature of a secondary command buffer. 70 | pub type SecondaryCommandBuffer = CommandBuffer; 71 | 72 | /// A strongly-typed command buffer that will only implement methods that are valid for the operations 73 | /// it supports. 74 | pub struct CommandBuffer::CommandBuffer> 75 | { 76 | pub(crate) raw: R, 77 | pub(crate) _marker: PhantomData<(B, C, S, L)>, 78 | } 79 | 80 | impl Borrow for CommandBuffer 81 | where 82 | R: RawCommandBuffer, 83 | B: Backend, 84 | { 85 | fn borrow(&self) -> &B::CommandBuffer { 86 | &self.raw 87 | } 88 | } 89 | 90 | impl, S, L: Level> Submittable 91 | for CommandBuffer 92 | { 93 | } 94 | 95 | impl IntoRawCommandBuffer for CommandBuffer { 96 | fn into_raw(self) -> B::CommandBuffer { 97 | self.raw 98 | } 99 | } 100 | 101 | impl CommandBuffer { 102 | /// Begin recording a one-shot primary command buffer. 103 | pub unsafe fn begin(&mut self) { 104 | let flags = CommandBufferFlags::ONE_TIME_SUBMIT; 105 | self.raw 106 | .begin(flags, CommandBufferInheritanceInfo::default()); 107 | } 108 | } 109 | 110 | impl CommandBuffer { 111 | /// Begin recording a multi-shot primary command buffer. 112 | pub unsafe fn begin(&mut self, allow_pending_resubmit: bool) { 113 | let flags = if allow_pending_resubmit { 114 | CommandBufferFlags::SIMULTANEOUS_USE 115 | } else { 116 | CommandBufferFlags::empty() 117 | }; 118 | self.raw 119 | .begin(flags, CommandBufferInheritanceInfo::default()); 120 | } 121 | } 122 | 123 | impl CommandBuffer { 124 | /// Begin recording a one-shot secondary command buffer. 125 | pub unsafe fn begin(&mut self, inheritance: CommandBufferInheritanceInfo) { 126 | let flags = CommandBufferFlags::ONE_TIME_SUBMIT; 127 | self.raw.begin(flags, inheritance); 128 | } 129 | } 130 | 131 | impl CommandBuffer { 132 | /// Begin recording a multi-shot secondary command buffer. 133 | pub unsafe fn begin( 134 | &mut self, 135 | allow_pending_resubmit: bool, 136 | inheritance: CommandBufferInheritanceInfo, 137 | ) { 138 | let flags = if allow_pending_resubmit { 139 | CommandBufferFlags::SIMULTANEOUS_USE 140 | } else { 141 | CommandBufferFlags::empty() 142 | }; 143 | self.raw.begin(flags, inheritance); 144 | } 145 | } 146 | 147 | impl CommandBuffer { 148 | /// Create a new typed command buffer from a raw command pool. 149 | pub unsafe fn new(raw: B::CommandBuffer) -> Self { 150 | CommandBuffer { 151 | raw, 152 | _marker: PhantomData, 153 | } 154 | } 155 | 156 | /// Finish recording commands to the command buffers. 157 | /// 158 | /// The command buffer must be reset to able to re-record commands. 159 | pub unsafe fn finish(&mut self) { 160 | self.raw.finish(); 161 | } 162 | 163 | /// Empties the command buffer, optionally releasing all resources from the 164 | /// commands that have been submitted. The command buffer is moved back to 165 | /// the "initial" state. 166 | /// 167 | /// The command buffer must not be in the "pending" state. Additionally, the 168 | /// command pool must have been created with the RESET_INDIVIDUAL flag to be 169 | /// able to reset individual buffers. 170 | pub unsafe fn reset(&mut self, release_resources: bool) { 171 | self.raw.reset(release_resources); 172 | } 173 | 174 | /* 175 | /// Get a reference to the raw command buffer 176 | pub fn as_raw(&self) -> &B::CommandBuffer { 177 | &self.raw 178 | } 179 | 180 | /// Get a mutable reference to the raw command buffer 181 | pub fn as_raw_mut(&mut self) -> &mut B::CommandBuffer { 182 | &mut self.raw 183 | }*/ 184 | 185 | /// Downgrade a command buffer to a lesser capability type. 186 | pub unsafe fn downgrade(&mut self) -> &mut CommandBuffer 187 | where 188 | C: Supports, 189 | { 190 | ::std::mem::transmute(self) 191 | } 192 | } 193 | 194 | impl CommandBuffer { 195 | /// Identical to the `RawCommandBuffer` method of the same name. 196 | pub unsafe fn execute_commands<'a, I, T, K>(&mut self, cmd_buffers: I) 197 | where 198 | K: Capability, 199 | T: 'a + Submittable, 200 | I: IntoIterator, 201 | C: Supports, 202 | { 203 | self.raw 204 | .execute_commands(cmd_buffers.into_iter().map(|cmb| cmb.borrow())); 205 | } 206 | } 207 | -------------------------------------------------------------------------------- /src/hal/src/adapter.rs: -------------------------------------------------------------------------------- 1 | //! Physical devices and adapters. 2 | //! 3 | //! The `PhysicalDevice` trait specifies the API a backend must provide for dealing with 4 | //! and querying a physical device, such as a particular GPU. An `Adapter` is a struct 5 | //! containing a `PhysicalDevice` and metadata for a particular GPU, generally created 6 | //! from an `Instance` of that backend. `adapter.open_with(...)` will return a `Device` 7 | //! that has the properties specified. 8 | 9 | use std::any::Any; 10 | 11 | use crate::error::DeviceCreationError; 12 | use crate::queue::{Capability, QueueGroup}; 13 | use crate::{format, image, memory, Backend, Features, Gpu, Limits}; 14 | 15 | /// Scheduling hint for devices about the priority of a queue. Values range from `0.0` (low) to 16 | /// `1.0` (high). 17 | pub type QueuePriority = f32; 18 | 19 | /// A strongly-typed index to a particular `MemoryType`. 20 | #[derive(Copy, Clone, Debug, PartialEq, Eq, PartialOrd, Ord, Hash)] 21 | #[cfg_attr(feature = "serde", derive(Serialize, Deserialize))] 22 | pub struct MemoryTypeId(pub usize); 23 | 24 | impl From for MemoryTypeId { 25 | fn from(id: usize) -> Self { 26 | MemoryTypeId(id) 27 | } 28 | } 29 | 30 | /// A description for a single chunk of memory in a heap. 31 | #[derive(Copy, Clone, Debug, PartialEq, Eq)] 32 | #[cfg_attr(feature = "serde", derive(Serialize, Deserialize))] 33 | pub struct MemoryType { 34 | /// Properties of the associated memory, such as synchronization 35 | /// properties or whether it's on the CPU or GPU. 36 | pub properties: memory::Properties, 37 | /// Index to the underlying memory heap in `Gpu::memory_heaps` 38 | pub heap_index: usize, 39 | } 40 | 41 | /// Types of memory supported by this adapter and available memory. 42 | #[derive(Clone, Debug, Eq, PartialEq)] 43 | #[cfg_attr(feature = "serde", derive(Serialize, Deserialize))] 44 | pub struct MemoryProperties { 45 | /// Each memory type is associated with one heap of `memory_heaps`. 46 | /// Multiple types can point to the same heap. 47 | pub memory_types: Vec, 48 | /// Memory heaps with their size in bytes. 49 | pub memory_heaps: Vec, 50 | } 51 | 52 | /// Represents a physical device (such as a GPU) capable of supporting the given backend. 53 | pub trait PhysicalDevice: Any + Send + Sync { 54 | /// Create a new logical device with the requested features. If `requested_features` is 55 | /// empty (e.g. through `Features::empty()`) then only the core features are supported. 56 | /// 57 | /// # Errors 58 | /// 59 | /// - Returns `TooManyObjects` if the implementation can't create a new logical device. 60 | /// - Returns `MissingFeature` if the implementation does not support a requested feature. 61 | /// 62 | /// # Examples 63 | /// 64 | /// ```no_run 65 | /// # extern crate gfx_backend_empty as empty; 66 | /// # extern crate gfx_hal; 67 | /// # fn main() { 68 | /// use gfx_hal::{PhysicalDevice, Features}; 69 | /// 70 | /// # let physical_device: empty::PhysicalDevice = return; 71 | /// # let family: empty::QueueFamily = return; 72 | /// # unsafe { 73 | /// let gpu = physical_device.open(&[(&family, &[1.0; 1])], Features::empty()); 74 | /// # }} 75 | /// ``` 76 | unsafe fn open( 77 | &self, 78 | families: &[(&B::QueueFamily, &[QueuePriority])], 79 | requested_features: Features, 80 | ) -> Result, DeviceCreationError>; 81 | 82 | /// Fetch details for a particular format. 83 | fn format_properties(&self, format: Option) -> format::Properties; 84 | 85 | /// Fetch details for a particular image format. 86 | fn image_format_properties( 87 | &self, 88 | format: format::Format, 89 | dimensions: u8, 90 | tiling: image::Tiling, 91 | usage: image::Usage, 92 | view_caps: image::ViewCapabilities, 93 | ) -> Option; 94 | 95 | /// Fetch details for the memory regions provided by the device. 96 | fn memory_properties(&self) -> MemoryProperties; 97 | 98 | /// Returns the features of this `Device`. This usually depends on the graphics API being 99 | /// used. 100 | fn features(&self) -> Features; 101 | 102 | /// Returns the resource limits of this `Device`. 103 | fn limits(&self) -> Limits; 104 | 105 | /// Check cache compatibility with the `Device`. 106 | fn is_valid_cache(&self, _cache: &[u8]) -> bool { false } 107 | } 108 | 109 | /// Supported physical device types 110 | #[derive(Clone, PartialEq, Eq, Debug)] 111 | #[cfg_attr(feature = "serde", derive(Serialize, Deserialize))] 112 | pub enum DeviceType { 113 | /// Other 114 | Other = 0, 115 | /// Integrated 116 | IntegratedGpu = 1, 117 | /// Discrete 118 | DiscreteGpu = 2, 119 | /// Virtual / Hosted 120 | VirtualGpu = 3, 121 | /// Cpu / Software Rendering 122 | Cpu = 4, 123 | } 124 | 125 | /// Metadata about a backend adapter. 126 | #[derive(Clone, Debug, Eq, PartialEq)] 127 | #[cfg_attr(feature = "serde", derive(Serialize, Deserialize))] 128 | pub struct AdapterInfo { 129 | /// Adapter name 130 | pub name: String, 131 | /// Vendor PCI id of the adapter 132 | pub vendor: usize, 133 | /// PCI id of the adapter 134 | pub device: usize, 135 | /// Type of device 136 | pub device_type: DeviceType, 137 | } 138 | 139 | /// The list of `Adapter` instances is obtained by calling `Instance::enumerate_adapters()`. 140 | /// 141 | /// Given an `Adapter` a `Gpu` can be constructed by calling `PhysicalDevice::open()` on its 142 | /// `physical_device` field. However, if only a single queue family is needed or if no 143 | /// additional device features are required, then the `Adapter::open_with` convenience method 144 | /// can be used instead. 145 | pub struct Adapter { 146 | /// General information about this adapter. 147 | pub info: AdapterInfo, 148 | /// Actual physical device. 149 | pub physical_device: B::PhysicalDevice, 150 | /// Queue families supported by this adapter. 151 | pub queue_families: Vec, 152 | } 153 | 154 | impl Adapter { 155 | /// Open the physical device with `count` queues from some active queue family. The family is 156 | /// the first that both provides the capability `C`, supports at least `count` queues, and for 157 | /// which `selector` returns true. 158 | /// 159 | /// # Examples 160 | /// 161 | /// ```no_run 162 | /// # extern crate gfx_backend_empty as empty; 163 | /// # extern crate gfx_hal as hal; 164 | /// use hal::General; 165 | /// # fn main() { 166 | /// 167 | /// # let mut adapter: hal::Adapter = return; 168 | /// let (device, queues) = adapter.open_with::<_, General>(1, |_| true).unwrap(); 169 | /// # } 170 | /// ``` 171 | /// 172 | /// # Return 173 | /// 174 | /// Returns the same errors as `open` and `InitializationFailed` if no suitable 175 | /// queue family could be found. 176 | pub fn open_with( 177 | &self, 178 | count: usize, 179 | selector: F, 180 | ) -> Result<(B::Device, QueueGroup), DeviceCreationError> 181 | where 182 | F: Fn(&B::QueueFamily) -> bool, 183 | C: Capability, 184 | { 185 | use crate::queue::QueueFamily; 186 | 187 | let requested_family = self 188 | .queue_families 189 | .iter() 190 | .find(|family| { 191 | C::supported_by(family.queue_type()) 192 | && selector(family) 193 | && count <= family.max_queues() 194 | }); 195 | 196 | let priorities = vec![1.0; count]; 197 | let (id, families) = match requested_family { 198 | Some(family) => (family.id(), [(family, priorities.as_slice())]), 199 | _ => return Err(DeviceCreationError::InitializationFailed), 200 | }; 201 | 202 | let Gpu { device, mut queues } = 203 | unsafe { self.physical_device.open(&families, Features::empty()) }?; 204 | Ok((device, queues.take(id).unwrap())) 205 | } 206 | } 207 | --------------------------------------------------------------------------------

5 | 6 | Travis Build Status 7 | 8 | 9 | gfx-hal on crates.io 10 | 11 | 12 | Gitter Chat 13 | 14 | 15 | Zulip Chat 16 | 17 |
18 | Getting Started | Documentation | Blog 19 |