Compare commits
1 commit
main
...
main-refac
Author | SHA1 | Date | |
---|---|---|---|
42db1a33a0 |
54 changed files with 1404 additions and 4581 deletions
10
.cursor/mcp.json
Normal file
10
.cursor/mcp.json
Normal file
|
@ -0,0 +1,10 @@
|
|||
{
|
||||
"mcpServers": {
|
||||
"run-program": {
|
||||
"command": "cargo",
|
||||
"args": [
|
||||
"run"
|
||||
]
|
||||
}
|
||||
}
|
||||
}
|
1975
Cargo.lock
generated
1975
Cargo.lock
generated
File diff suppressed because it is too large
Load diff
17
Cargo.toml
17
Cargo.toml
|
@ -7,24 +7,17 @@ publish = false
|
|||
|
||||
[dependencies]
|
||||
anyhow = "1.0"
|
||||
thiserror = "2.0"
|
||||
winit = { version = "0.30", features = ["rwh_06"] }
|
||||
|
||||
vulkano = "0.35"
|
||||
vulkano-shaders = "0.35"
|
||||
vulkano-util = "0.35"
|
||||
egui_winit_vulkano = { version = "0.28" }
|
||||
|
||||
image = { version = "0.25", features = ["png", "jpeg"] }
|
||||
|
||||
# Math
|
||||
glam = { version = "0.30" }
|
||||
|
||||
# Log and tracing
|
||||
tracing = "0.1"
|
||||
tracing-subscriber = { version = "0.3", features = ["env-filter", "fmt"] }
|
||||
tracing-log = "0.2"
|
||||
tracing-tracy = "0.11"
|
||||
# ECS
|
||||
apecs = "0.8"
|
||||
|
||||
# Random
|
||||
rand = "0.9"
|
||||
# Log and tracing
|
||||
log = "0.4"
|
||||
env_logger = "0.11.5"
|
||||
|
|
17
README.md
17
README.md
|
@ -1,17 +0,0 @@
|
|||
# Project
|
||||
|
||||
## Notes
|
||||
|
||||
1. Run renderdoc on wayland:
|
||||
|
||||
```console
|
||||
WAYLAND_DISPLAY= QT_QPA_PLATFORM=xcb qrenderdoc
|
||||
```
|
||||
> Not supported yet https://github.com/baldurk/renderdoc/issues/853
|
||||
|
||||
2. [Difference Between OpenGL and Vulkan](./docs/OPENGL_VULKAN_DIFF.md)
|
||||
|
||||
## Usefull links
|
||||
|
||||
- https://vulkan-tutorial.com/fr/Introduction
|
||||
- https://github.com/bwasty/vulkan-tutorial-rs
|
|
@ -1,11 +0,0 @@
|
|||
# Difference between Vulkan and OpenGL
|
||||
|
||||
Viewport:
|
||||
|
||||
- Y axis is flipped like D3D
|
||||
- Clipped Z axis is not [-1; 1] but [0; 1]
|
||||
|
||||

|
||||

|
||||
|
||||
See: [Vulkan Tutorial (Vertex step)](https://vulkan-tutorial.com/Drawing_a_triangle/Graphics_pipeline_basics/Shader_modules) and [VK_KHR_maintenance1 (Allow negative height)](https://registry.khronos.org/vulkan/specs/latest/man/html/VK_KHR_maintenance1.html#_description)
|
Binary file not shown.
Before Width: | Height: | Size: 24 KiB |
|
@ -1,219 +0,0 @@
|
|||
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
|
||||
<!-- Created with Inkscape (http://www.inkscape.org/) -->
|
||||
|
||||
<svg
|
||||
xmlns:dc="http://purl.org/dc/elements/1.1/"
|
||||
xmlns:cc="http://creativecommons.org/ns#"
|
||||
xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
|
||||
xmlns:svg="http://www.w3.org/2000/svg"
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
|
||||
xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
|
||||
width="168.23553mm"
|
||||
height="76.127022mm"
|
||||
viewBox="0 0 596.11016 269.74141"
|
||||
id="svg2"
|
||||
version="1.1"
|
||||
inkscape:version="0.91 r13725"
|
||||
sodipodi:docname="clip_coordinates.svg">
|
||||
<defs
|
||||
id="defs4" />
|
||||
<sodipodi:namedview
|
||||
id="base"
|
||||
pagecolor="#ffffff"
|
||||
bordercolor="#666666"
|
||||
borderopacity="1.0"
|
||||
inkscape:pageopacity="0.0"
|
||||
inkscape:pageshadow="2"
|
||||
inkscape:zoom="0.98994949"
|
||||
inkscape:cx="140.75091"
|
||||
inkscape:cy="-3.0732866"
|
||||
inkscape:document-units="px"
|
||||
inkscape:current-layer="layer1"
|
||||
showgrid="false"
|
||||
inkscape:window-width="1600"
|
||||
inkscape:window-height="837"
|
||||
inkscape:window-x="-8"
|
||||
inkscape:window-y="-8"
|
||||
inkscape:window-maximized="1"
|
||||
fit-margin-top="10"
|
||||
fit-margin-left="10"
|
||||
fit-margin-right="10"
|
||||
fit-margin-bottom="10" />
|
||||
<metadata
|
||||
id="metadata7">
|
||||
<rdf:RDF>
|
||||
<cc:Work
|
||||
rdf:about="">
|
||||
<dc:format>image/svg+xml</dc:format>
|
||||
<dc:type
|
||||
rdf:resource="http://purl.org/dc/dcmitype/StillImage" />
|
||||
<dc:title></dc:title>
|
||||
</cc:Work>
|
||||
</rdf:RDF>
|
||||
</metadata>
|
||||
<g
|
||||
inkscape:label="Layer 1"
|
||||
inkscape:groupmode="layer"
|
||||
id="layer1"
|
||||
transform="translate(-68.169789,-67.73013)">
|
||||
<rect
|
||||
style="fill:none;fill-opacity:1;stroke:#000000;stroke-width:2.37949777;stroke-opacity:1"
|
||||
id="rect4136"
|
||||
width="185.26089"
|
||||
height="129.17273"
|
||||
x="127.66544"
|
||||
y="152.46893" />
|
||||
<text
|
||||
xml:space="preserve"
|
||||
style="font-style:normal;font-weight:normal;font-size:12.5px;line-height:125%;font-family:sans-serif;letter-spacing:0px;word-spacing:0px;fill:#000000;fill-opacity:1;stroke:none;stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1"
|
||||
x="142.5"
|
||||
y="114.50506"
|
||||
id="text4153"
|
||||
sodipodi:linespacing="125%"><tspan
|
||||
sodipodi:role="line"
|
||||
id="tspan4155"
|
||||
x="142.5"
|
||||
y="114.50506">Framebuffer coordinates</tspan></text>
|
||||
<text
|
||||
xml:space="preserve"
|
||||
style="font-style:normal;font-weight:normal;font-size:12.5px;line-height:125%;font-family:sans-serif;letter-spacing:0px;word-spacing:0px;fill:#4d4d4d;fill-opacity:1;stroke:none;stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1"
|
||||
x="108.08633"
|
||||
y="144.23506"
|
||||
id="text4157"
|
||||
sodipodi:linespacing="125%"><tspan
|
||||
sodipodi:role="line"
|
||||
id="tspan4159"
|
||||
x="108.08633"
|
||||
y="144.23506">(0, 0)</tspan></text>
|
||||
<text
|
||||
xml:space="preserve"
|
||||
style="font-style:normal;font-weight:normal;font-size:12.5px;line-height:125%;font-family:sans-serif;letter-spacing:0px;word-spacing:0px;fill:#4d4d4d;fill-opacity:1;stroke:none;stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1"
|
||||
x="289.4823"
|
||||
y="143.68567"
|
||||
id="text4157-1"
|
||||
sodipodi:linespacing="125%"><tspan
|
||||
sodipodi:role="line"
|
||||
id="tspan4159-7"
|
||||
x="289.4823"
|
||||
y="143.68567">(1920, 0)</tspan></text>
|
||||
<text
|
||||
xml:space="preserve"
|
||||
style="font-style:normal;font-weight:normal;font-size:12.5px;line-height:125%;font-family:sans-serif;letter-spacing:0px;word-spacing:0px;fill:#4d4d4d;fill-opacity:1;stroke:none;stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1"
|
||||
x="102.49812"
|
||||
y="299.52383"
|
||||
id="text4157-0"
|
||||
sodipodi:linespacing="125%"><tspan
|
||||
sodipodi:role="line"
|
||||
id="tspan4159-3"
|
||||
x="102.49812"
|
||||
y="299.52383">(0, 1080)</tspan></text>
|
||||
<text
|
||||
xml:space="preserve"
|
||||
style="font-style:normal;font-weight:normal;font-size:12.5px;line-height:125%;font-family:sans-serif;letter-spacing:0px;word-spacing:0px;fill:#4d4d4d;fill-opacity:1;stroke:none;stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1"
|
||||
x="277.83316"
|
||||
y="298.46939"
|
||||
id="text4157-1-3"
|
||||
sodipodi:linespacing="125%"><tspan
|
||||
sodipodi:role="line"
|
||||
id="tspan4159-7-2"
|
||||
x="277.83316"
|
||||
y="298.46939">(1920, 1080)</tspan></text>
|
||||
<circle
|
||||
style="fill:#000000;fill-opacity:1;stroke:none;stroke-opacity:1"
|
||||
id="path4229"
|
||||
cx="220.46579"
|
||||
cy="218.48128"
|
||||
r="1.767767" />
|
||||
<text
|
||||
xml:space="preserve"
|
||||
style="font-style:normal;font-weight:normal;font-size:12.5px;line-height:125%;font-family:sans-serif;letter-spacing:0px;word-spacing:0px;fill:#4d4d4d;fill-opacity:1;stroke:none;stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1"
|
||||
x="187.29964"
|
||||
y="232.99626"
|
||||
id="text4157-1-3-3"
|
||||
sodipodi:linespacing="125%"><tspan
|
||||
sodipodi:role="line"
|
||||
id="tspan4159-7-2-3"
|
||||
x="187.29964"
|
||||
y="232.99626">(960, 540)</tspan></text>
|
||||
<rect
|
||||
style="fill:none;fill-opacity:1;stroke:#000000;stroke-width:2.37949777;stroke-opacity:1"
|
||||
id="rect4136-0"
|
||||
width="185.26089"
|
||||
height="129.17273"
|
||||
x="426.228"
|
||||
y="150.62413" />
|
||||
<text
|
||||
xml:space="preserve"
|
||||
style="font-style:normal;font-weight:normal;font-size:12.5px;line-height:125%;font-family:sans-serif;letter-spacing:0px;word-spacing:0px;fill:#000000;fill-opacity:1;stroke:none;stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1"
|
||||
x="465.34827"
|
||||
y="112.66027"
|
||||
id="text4153-2"
|
||||
sodipodi:linespacing="125%"><tspan
|
||||
sodipodi:role="line"
|
||||
id="tspan4155-2"
|
||||
x="435.34827"
|
||||
y="112.66027">Normalized device coordinates</tspan></text>
|
||||
<text
|
||||
xml:space="preserve"
|
||||
style="font-style:normal;font-weight:normal;font-size:12.5px;line-height:125%;font-family:sans-serif;letter-spacing:0px;word-spacing:0px;fill:#4d4d4d;fill-opacity:1;stroke:none;stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1"
|
||||
x="406.6489"
|
||||
y="142.39026"
|
||||
id="text4157-9"
|
||||
sodipodi:linespacing="125%"><tspan
|
||||
sodipodi:role="line"
|
||||
id="tspan4159-0"
|
||||
x="406.6489"
|
||||
y="142.39026">(-1, -1)</tspan></text>
|
||||
<text
|
||||
xml:space="preserve"
|
||||
style="font-style:normal;font-weight:normal;font-size:12.5px;line-height:125%;font-family:sans-serif;letter-spacing:0px;word-spacing:0px;fill:#4d4d4d;fill-opacity:1;stroke:none;stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1"
|
||||
x="588.04486"
|
||||
y="141.84087"
|
||||
id="text4157-1-4"
|
||||
sodipodi:linespacing="125%"><tspan
|
||||
sodipodi:role="line"
|
||||
id="tspan4159-7-21"
|
||||
x="588.04486"
|
||||
y="141.84087">(1, -1)</tspan></text>
|
||||
<text
|
||||
xml:space="preserve"
|
||||
style="font-style:normal;font-weight:normal;font-size:12.5px;line-height:125%;font-family:sans-serif;letter-spacing:0px;word-spacing:0px;fill:#4d4d4d;fill-opacity:1;stroke:none;stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1"
|
||||
x="401.0607"
|
||||
y="297.67902"
|
||||
id="text4157-0-6"
|
||||
sodipodi:linespacing="125%"><tspan
|
||||
sodipodi:role="line"
|
||||
id="tspan4159-3-5"
|
||||
x="401.0607"
|
||||
y="297.67902">(-1, 1)</tspan></text>
|
||||
<text
|
||||
xml:space="preserve"
|
||||
style="font-style:normal;font-weight:normal;font-size:12.5px;line-height:125%;font-family:sans-serif;letter-spacing:0px;word-spacing:0px;fill:#4d4d4d;fill-opacity:1;stroke:none;stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1"
|
||||
x="592.82428"
|
||||
y="296.62457"
|
||||
id="text4157-1-3-7"
|
||||
sodipodi:linespacing="125%"><tspan
|
||||
sodipodi:role="line"
|
||||
id="tspan4159-7-2-6"
|
||||
x="592.82428"
|
||||
y="296.62457">(1, 1)</tspan></text>
|
||||
<circle
|
||||
style="fill:#000000;fill-opacity:1;stroke:none;stroke-opacity:1"
|
||||
id="path4229-5"
|
||||
cx="519.02832"
|
||||
cy="216.63647"
|
||||
r="1.767767" />
|
||||
<text
|
||||
xml:space="preserve"
|
||||
style="font-style:normal;font-weight:normal;font-size:12.5px;line-height:125%;font-family:sans-serif;letter-spacing:0px;word-spacing:0px;fill:#4d4d4d;fill-opacity:1;stroke:none;stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1"
|
||||
x="500.14792"
|
||||
y="231.15146"
|
||||
id="text4157-1-3-3-8"
|
||||
sodipodi:linespacing="125%"><tspan
|
||||
sodipodi:role="line"
|
||||
id="tspan4159-7-2-3-0"
|
||||
x="500.14792"
|
||||
y="231.15146">(0, 0)</tspan></text>
|
||||
</g>
|
||||
</svg>
|
Before Width: | Height: | Size: 8.8 KiB |
12
flake.lock
generated
12
flake.lock
generated
|
@ -44,11 +44,11 @@
|
|||
},
|
||||
"nixpkgs": {
|
||||
"locked": {
|
||||
"lastModified": 1747312588,
|
||||
"narHash": "sha256-MmJvj6mlWzeRwKGLcwmZpKaOPZ5nJb/6al5CXqJsgjo=",
|
||||
"lastModified": 1742546557,
|
||||
"narHash": "sha256-QyhimDBaDBtMfRc7kyL28vo+HTwXRPq3hz+BgSJDotw=",
|
||||
"owner": "nixos",
|
||||
"repo": "nixpkgs",
|
||||
"rev": "b1bebd0fe266bbd1820019612ead889e96a8fa2d",
|
||||
"rev": "bfa9810ff7104a17555ab68ebdeafb6705f129b1",
|
||||
"type": "github"
|
||||
},
|
||||
"original": {
|
||||
|
@ -73,11 +73,11 @@
|
|||
]
|
||||
},
|
||||
"locked": {
|
||||
"lastModified": 1747363019,
|
||||
"narHash": "sha256-N4dwkRBmpOosa4gfFkFf/LTD8oOcNkAyvZ07JvRDEf0=",
|
||||
"lastModified": 1742524367,
|
||||
"narHash": "sha256-KzTwk/5ETJavJZYV1DEWdCx05M4duFCxCpRbQSKWpng=",
|
||||
"owner": "oxalica",
|
||||
"repo": "rust-overlay",
|
||||
"rev": "0e624f2b1972a34be1a9b35290ed18ea4b419b6f",
|
||||
"rev": "70bf752d176b2ce07417e346d85486acea9040ef",
|
||||
"type": "github"
|
||||
},
|
||||
"original": {
|
||||
|
|
19
flake.nix
19
flake.nix
|
@ -31,15 +31,18 @@
|
|||
cargo = rust;
|
||||
});
|
||||
|
||||
buildInputs = with pkgs; [ vulkan-headers vulkan-loader vulkan-validation-layers renderdoc tracy ]
|
||||
++ pkgs.lib.optionals pkgs.stdenv.hostPlatform.isLinux (with pkgs; [
|
||||
stdenv.cc.cc.lib
|
||||
renderdoc = pkgs.renderdoc.overrideAttrs (oldAttrs: {
|
||||
cmakeFlags = oldAttrs.cmakeFlags ++ [
|
||||
(pkgs.lib.cmakeBool "ENABLE_UNSUPPORTED_EXPERIMENTAL_POSSIBLY_BROKEN_WAYLAND" true)
|
||||
];
|
||||
});
|
||||
|
||||
buildInputs = with pkgs; [ vulkan-headers vulkan-loader vulkan-validation-layers renderdoc ]
|
||||
++ pkgs.lib.optionals pkgs.stdenv.hostPlatform.isLinux (with pkgs; [
|
||||
# Wayland
|
||||
libxkbcommon
|
||||
wayland
|
||||
libGL
|
||||
|
||||
# Xorg
|
||||
xorg.libX11
|
||||
xorg.libXcursor
|
||||
|
@ -57,7 +60,7 @@
|
|||
|
||||
mkCustomShell = { packages ? [ ] }: pkgs.mkShell {
|
||||
nativeBuildInputs = [
|
||||
pkgs.renderdoc
|
||||
renderdoc
|
||||
(rust.override { extensions = [ "rust-src" "rust-analyzer" ]; })
|
||||
] ++ nativeBuildInputs;
|
||||
|
||||
|
@ -65,8 +68,8 @@
|
|||
++ packages;
|
||||
|
||||
LD_LIBRARY_PATH = pkgs.lib.makeLibraryPath buildInputs;
|
||||
VK_LAYER_PATH = "${pkgs.vulkan-validation-layers}/share/vulkan/explicit_layer.d";
|
||||
RUST_LOG = "debug,rust_vulkan_test=trace";
|
||||
VK_LAYER_PATH = "${pkgs.vulkan-validation-layers}/share/vulkan/explicit_layer.d:${renderdoc}/share/vulkan/implicit_layer.d";
|
||||
RUST_LOG = "info,rust_vulkan_test=trace";
|
||||
};
|
||||
in
|
||||
{
|
||||
|
@ -77,7 +80,7 @@
|
|||
|
||||
packages = {
|
||||
default = rustPlatform.buildRustPackage {
|
||||
pname = "vulkan_test";
|
||||
pname = "rust_ash_test";
|
||||
version = "0.1.0";
|
||||
|
||||
src = self;
|
||||
|
|
|
@ -1,12 +1,9 @@
|
|||
#version 450
|
||||
|
||||
layout (location = 0) in vec2 tex_coords;
|
||||
layout (location = 0) in vec3 color;
|
||||
|
||||
layout (location = 0) out vec4 f_color;
|
||||
|
||||
layout(set = 1, binding = 0) uniform sampler mySampler;
|
||||
layout(set = 1, binding = 1) uniform texture2D myTexture;
|
||||
|
||||
void main() {
|
||||
f_color = texture(sampler2D(myTexture, mySampler), tex_coords);
|
||||
}
|
||||
f_color = vec4(color, 1.0);
|
||||
}
|
|
@ -1,20 +1 @@
|
|||
#version 450
|
||||
|
||||
layout (location = 0) in vec3 position;
|
||||
layout (location = 1) in vec2 uv;
|
||||
layout (location = 2) in mat4 model;
|
||||
|
||||
layout (location = 0) out vec2 fragUv;
|
||||
|
||||
layout (set = 0, binding = 0) uniform MVP {
|
||||
mat4 world;
|
||||
mat4 view;
|
||||
mat4 projection;
|
||||
} uniforms;
|
||||
|
||||
void main() {
|
||||
mat4 worldview = uniforms.view * uniforms.world;
|
||||
vec4 modelPosition = model * vec4(position, 1.0);
|
||||
gl_Position = uniforms.projection * worldview * modelPosition;
|
||||
fragUv = uv;
|
||||
}
|
||||
#version 450
layout (location = 0) in vec2 position;
layout (location = 1) in vec3 color;
layout (location = 0) out vec3 fragColor;
layout (set = 0, binding = 0) uniform MVPData {
mat4 world;
mat4 view;
mat4 projection;
} uniforms;
void main() {
mat4 worldview = uniforms.view * uniforms.world;
gl_Position = uniforms.projection * worldview * vec4(position, 0.0, 1.0);
fragColor = color;
}
|
Binary file not shown.
Before Width: | Height: | Size: 49 KiB |
|
@ -1,2 +1,2 @@
|
|||
[toolchain]
|
||||
channel = "1.87.0"
|
||||
channel = "1.85.1"
|
||||
|
|
|
@ -1,186 +0,0 @@
|
|||
use std::{
|
||||
cell::RefCell,
|
||||
rc::Rc,
|
||||
sync::{Arc, RwLock},
|
||||
};
|
||||
|
||||
use egui_winit_vulkano::Gui;
|
||||
use vulkano::{
|
||||
command_buffer::allocator::StandardCommandBufferAllocator,
|
||||
descriptor_set::allocator::StandardDescriptorSetAllocator,
|
||||
device::{Device, Queue},
|
||||
instance::Instance,
|
||||
memory::allocator::StandardMemoryAllocator,
|
||||
};
|
||||
use vulkano_util::{renderer::VulkanoWindowRenderer, window::VulkanoWindows};
|
||||
use winit::{event_loop::EventLoopProxy, monitor::MonitorHandle, window::WindowId};
|
||||
|
||||
use crate::core::{input::InputManager, render::vulkan_context::VulkanContext, timer::Timer};
|
||||
|
||||
use super::user_event::UserEvent;
|
||||
|
||||
/// Contexte d'application unifié avec Arc<Mutex<>> pour la mutabilité partagée
|
||||
#[derive(Clone)]
|
||||
pub struct WindowContext {
|
||||
// Données Vulkan (immutables)
|
||||
pub vulkan_context: Arc<VulkanContext>,
|
||||
pub device: Arc<Device>,
|
||||
pub instance: Arc<Instance>,
|
||||
pub graphics_queue: Arc<Queue>,
|
||||
pub compute_queue: Arc<Queue>,
|
||||
pub transfer_queue: Option<Arc<Queue>>,
|
||||
pub memory_allocator: Arc<StandardMemoryAllocator>,
|
||||
pub command_buffer_allocator: Arc<StandardCommandBufferAllocator>,
|
||||
pub descriptor_set_allocator: Arc<StandardDescriptorSetAllocator>,
|
||||
pub event_loop_proxy: EventLoopProxy<UserEvent>,
|
||||
pub window_id: WindowId,
|
||||
|
||||
// Données mutables partagées avec Arc<Mutex<>>
|
||||
pub vulkano_windows: Rc<RefCell<VulkanoWindows>>,
|
||||
pub input_manager: Arc<RwLock<InputManager>>,
|
||||
pub timer: Arc<RwLock<Timer>>,
|
||||
pub gui: Rc<RefCell<Gui>>,
|
||||
}
|
||||
|
||||
impl WindowContext {
|
||||
pub fn new(
|
||||
vulkan_context: Arc<VulkanContext>,
|
||||
vulkano_windows: Rc<RefCell<VulkanoWindows>>,
|
||||
input_manager: Arc<RwLock<InputManager>>,
|
||||
timer: Arc<RwLock<Timer>>,
|
||||
gui: Rc<RefCell<Gui>>,
|
||||
event_loop_proxy: EventLoopProxy<UserEvent>,
|
||||
window_id: WindowId,
|
||||
) -> Self {
|
||||
let vulkano_context_inner = vulkan_context.vulkano_context();
|
||||
|
||||
Self {
|
||||
// Données Vulkan
|
||||
vulkan_context: vulkan_context.clone(),
|
||||
device: vulkano_context_inner.device().clone(),
|
||||
instance: vulkano_context_inner.instance().clone(),
|
||||
graphics_queue: vulkano_context_inner.graphics_queue().clone(),
|
||||
compute_queue: vulkano_context_inner.compute_queue().clone(),
|
||||
transfer_queue: vulkano_context_inner.transfer_queue().cloned(),
|
||||
memory_allocator: vulkano_context_inner.memory_allocator().clone(),
|
||||
command_buffer_allocator: vulkan_context.command_buffer_allocator().clone(),
|
||||
descriptor_set_allocator: vulkan_context.descriptor_set_allocator().clone(),
|
||||
event_loop_proxy,
|
||||
window_id,
|
||||
|
||||
// Données mutables partagées
|
||||
vulkano_windows,
|
||||
input_manager,
|
||||
timer,
|
||||
gui,
|
||||
}
|
||||
}
|
||||
|
||||
/// Extrait les résolutions d'un moniteur donné
|
||||
fn extract_resolutions_from_monitor(monitor: MonitorHandle) -> Vec<(u32, u32)> {
|
||||
let video_modes: Vec<_> = monitor.video_modes().collect();
|
||||
|
||||
let resolutions: Vec<(u32, u32)> = video_modes
|
||||
.into_iter()
|
||||
.map(|mode| {
|
||||
let size = mode.size();
|
||||
(size.width, size.height)
|
||||
})
|
||||
.collect();
|
||||
|
||||
tracing::trace!(
|
||||
"Modes vidéo trouvés pour {:?}: {:?}",
|
||||
monitor.name(),
|
||||
resolutions
|
||||
);
|
||||
resolutions
|
||||
}
|
||||
|
||||
/// Récupère les résolutions disponibles
|
||||
pub fn get_available_resolutions(&self) -> Vec<(u32, u32)> {
|
||||
self.with_renderer(|renderer| {
|
||||
renderer
|
||||
.window()
|
||||
.current_monitor()
|
||||
.map(Self::extract_resolutions_from_monitor)
|
||||
.unwrap_or_default()
|
||||
})
|
||||
}
|
||||
|
||||
/// Récupère le delta time actuel depuis le timer
|
||||
pub fn get_delta_time(&self) -> f32 {
|
||||
self.with_timer(|timer| timer.delta_time())
|
||||
}
|
||||
|
||||
/// Récupère la taille de la fenêtre depuis le renderer
|
||||
pub fn get_window_size(&self) -> [f32; 2] {
|
||||
self.with_renderer(|renderer| renderer.window_size())
|
||||
}
|
||||
|
||||
/// Récupère l'aspect ratio depuis le renderer
|
||||
pub fn get_aspect_ratio(&self) -> f32 {
|
||||
self.with_renderer(|renderer| renderer.aspect_ratio())
|
||||
}
|
||||
|
||||
pub fn with_renderer<T, F>(&self, f: F) -> T
|
||||
where
|
||||
F: FnOnce(&VulkanoWindowRenderer) -> T,
|
||||
{
|
||||
let vulkano_windows = self.vulkano_windows.borrow_mut();
|
||||
let renderer = vulkano_windows
|
||||
.get_renderer(self.window_id)
|
||||
.expect("Failed to get renderer");
|
||||
f(renderer)
|
||||
}
|
||||
|
||||
/// Méthode utilitaire pour accéder au renderer de manière thread-safe
|
||||
pub fn with_renderer_mut<T, F>(&mut self, f: F) -> T
|
||||
where
|
||||
F: FnOnce(&mut VulkanoWindowRenderer) -> T,
|
||||
{
|
||||
let mut vulkano_windows = self.vulkano_windows.borrow_mut();
|
||||
let renderer = vulkano_windows
|
||||
.get_renderer_mut(self.window_id)
|
||||
.expect("Failed to get renderer");
|
||||
f(renderer)
|
||||
}
|
||||
|
||||
/// Méthode utilitaire pour accéder au gui de manière thread-safe
|
||||
pub fn with_gui<T, F>(&self, f: F) -> T
|
||||
where
|
||||
F: FnOnce(&Gui) -> T,
|
||||
{
|
||||
let gui = self.gui.borrow();
|
||||
f(&gui)
|
||||
}
|
||||
|
||||
/// Méthode utilitaire pour accéder au gui de manière thread-safe
|
||||
pub fn with_gui_mut<T, F>(&mut self, f: F) -> T
|
||||
where
|
||||
F: FnOnce(&mut Gui) -> T,
|
||||
{
|
||||
let mut gui = self.gui.borrow_mut();
|
||||
f(&mut gui)
|
||||
}
|
||||
|
||||
/// Méthode utilitaire pour accéder à l'input manager de manière thread-safe
|
||||
pub fn with_input_manager<T, F>(&self, f: F) -> T
|
||||
where
|
||||
F: FnOnce(&InputManager) -> T,
|
||||
{
|
||||
let input_manager = self
|
||||
.input_manager
|
||||
.read()
|
||||
.expect("Failed to lock input_manager");
|
||||
f(&input_manager)
|
||||
}
|
||||
|
||||
/// Méthode utilitaire pour accéder au timer de manière thread-safe
|
||||
pub fn with_timer<T, F>(&self, f: F) -> T
|
||||
where
|
||||
F: FnOnce(&Timer) -> T,
|
||||
{
|
||||
let timer = self.timer.read().expect("Failed to lock timer");
|
||||
f(&timer)
|
||||
}
|
||||
}
|
|
@ -1,260 +0,0 @@
|
|||
use std::cell::RefCell;
|
||||
use std::collections::HashMap;
|
||||
use std::rc::Rc;
|
||||
use std::sync::{Arc, RwLock};
|
||||
|
||||
use super::render::vulkan_context::VulkanContext;
|
||||
use crate::core::input::InputManager;
|
||||
use crate::core::scene::manager::SceneManager;
|
||||
use crate::core::timer::Timer;
|
||||
use crate::game::scenes::main_scene::MainScene;
|
||||
use egui_winit_vulkano::{Gui, GuiConfig};
|
||||
use user_event::UserEvent;
|
||||
use vulkano::format::Format;
|
||||
use vulkano::image::ImageUsage;
|
||||
use vulkano::swapchain::PresentMode;
|
||||
use vulkano_util::context::VulkanoContext;
|
||||
use vulkano_util::window::{VulkanoWindows, WindowDescriptor};
|
||||
use winit::application::ApplicationHandler;
|
||||
use winit::event::WindowEvent;
|
||||
use winit::event_loop::{ActiveEventLoop, EventLoopProxy};
|
||||
use winit::window::WindowId;
|
||||
|
||||
use self::context::WindowContext;
|
||||
|
||||
pub mod context;
|
||||
pub mod user_event;
|
||||
|
||||
pub const DEPTH_IMAGE_ID: usize = 0;
|
||||
|
||||
pub struct App {
|
||||
vulkan_context: Arc<VulkanContext>,
|
||||
vulkano_windows: Rc<RefCell<VulkanoWindows>>,
|
||||
gui: HashMap<WindowId, Rc<RefCell<Gui>>>,
|
||||
scene_manager: HashMap<WindowId, SceneManager>,
|
||||
input_manager: Arc<RwLock<InputManager>>,
|
||||
timer: Arc<RwLock<Timer>>,
|
||||
event_loop_proxy: EventLoopProxy<UserEvent>,
|
||||
|
||||
// Context d'application partagé par fenêtre - architecture unifiée
|
||||
app_contexts: HashMap<WindowId, Rc<RefCell<WindowContext>>>,
|
||||
}
|
||||
|
||||
impl App {
|
||||
pub fn new(
|
||||
vulkano_context: VulkanoContext,
|
||||
input_manager: InputManager,
|
||||
event_loop_proxy: EventLoopProxy<UserEvent>,
|
||||
) -> Self {
|
||||
Self {
|
||||
vulkan_context: Arc::new(VulkanContext::new(vulkano_context)),
|
||||
vulkano_windows: Rc::new(RefCell::new(VulkanoWindows::default())),
|
||||
gui: HashMap::new(),
|
||||
input_manager: Arc::new(RwLock::new(input_manager)),
|
||||
scene_manager: HashMap::new(),
|
||||
timer: Arc::new(RwLock::new(Timer::new())),
|
||||
event_loop_proxy,
|
||||
app_contexts: HashMap::new(),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl ApplicationHandler<UserEvent> for App {
|
||||
fn resumed(&mut self, event_loop: &ActiveEventLoop) {
|
||||
let mut vulkano_windows = self.vulkano_windows.borrow_mut();
|
||||
let window_id = vulkano_windows.create_window(
|
||||
event_loop,
|
||||
self.vulkan_context.vulkano_context(),
|
||||
&WindowDescriptor {
|
||||
title: "Rust ASH Test".to_string(),
|
||||
width: 800.0,
|
||||
height: 600.0,
|
||||
present_mode: PresentMode::Fifo,
|
||||
cursor_visible: false,
|
||||
cursor_locked: true,
|
||||
..Default::default()
|
||||
},
|
||||
|_| {},
|
||||
);
|
||||
|
||||
let renderer = vulkano_windows.get_renderer_mut(window_id).unwrap();
|
||||
renderer.add_additional_image_view(
|
||||
DEPTH_IMAGE_ID,
|
||||
Format::D16_UNORM,
|
||||
ImageUsage::DEPTH_STENCIL_ATTACHMENT,
|
||||
);
|
||||
|
||||
let gui = {
|
||||
Gui::new(
|
||||
event_loop,
|
||||
renderer.surface(),
|
||||
renderer.graphics_queue(),
|
||||
renderer.swapchain_format(),
|
||||
GuiConfig {
|
||||
is_overlay: true,
|
||||
allow_srgb_render_target: true,
|
||||
..Default::default()
|
||||
},
|
||||
)
|
||||
};
|
||||
self.gui.insert(window_id, Rc::new(RefCell::new(gui)));
|
||||
|
||||
let mut scene_manager = SceneManager::new();
|
||||
scene_manager.load_scene(Box::new(MainScene::default()));
|
||||
|
||||
self.scene_manager.insert(window_id, scene_manager);
|
||||
|
||||
let app_context = Rc::new(RefCell::new(WindowContext::new(
|
||||
self.vulkan_context.clone(),
|
||||
self.vulkano_windows.clone(),
|
||||
self.input_manager.clone(),
|
||||
self.timer.clone(),
|
||||
self.gui.get(&window_id).unwrap().clone(),
|
||||
self.event_loop_proxy.clone(),
|
||||
window_id,
|
||||
)));
|
||||
self.app_contexts.insert(window_id, app_context);
|
||||
}
|
||||
|
||||
fn device_event(
|
||||
&mut self,
|
||||
_event_loop: &ActiveEventLoop,
|
||||
_device_id: winit::event::DeviceId,
|
||||
event: winit::event::DeviceEvent,
|
||||
) {
|
||||
let mut input_manager = self.input_manager.write().unwrap();
|
||||
input_manager.process_device_event(&event);
|
||||
}
|
||||
|
||||
fn window_event(&mut self, event_loop: &ActiveEventLoop, id: WindowId, event: WindowEvent) {
|
||||
{
|
||||
let gui = self.gui.get_mut(&id).unwrap();
|
||||
let mut gui = gui.borrow_mut();
|
||||
if !gui.update(&event) {
|
||||
let mut input_manager = self.input_manager.write().unwrap();
|
||||
input_manager.process_window_event(&event);
|
||||
}
|
||||
}
|
||||
|
||||
match event {
|
||||
WindowEvent::CloseRequested => {
|
||||
tracing::debug!("The close button was pressed; stopping");
|
||||
event_loop.exit();
|
||||
}
|
||||
WindowEvent::Resized(_) | WindowEvent::ScaleFactorChanged { .. } => {
|
||||
let mut vulkano_windows = self.vulkano_windows.borrow_mut();
|
||||
vulkano_windows.get_renderer_mut(id).unwrap().resize();
|
||||
}
|
||||
WindowEvent::RedrawRequested => {
|
||||
let _frame_span = tracing::info_span!("frame").entered();
|
||||
|
||||
{
|
||||
let _input_span = tracing::debug_span!("input_update").entered();
|
||||
let mut input_manager = self
|
||||
.input_manager
|
||||
.write()
|
||||
.expect("Failed to lock input manager");
|
||||
input_manager.update();
|
||||
}
|
||||
{
|
||||
let _timer_span = tracing::debug_span!("timer_update").entered();
|
||||
let mut timer = self.timer.write().expect("Failed to lock timer");
|
||||
timer.update();
|
||||
}
|
||||
|
||||
// Créer ou mettre à jour le contexte d'application
|
||||
let window_context = self.app_contexts.get(&id).unwrap().clone();
|
||||
let scene_manager = self.scene_manager.get_mut(&id).unwrap();
|
||||
|
||||
// Utiliser le contexte partagé pour les scènes
|
||||
{
|
||||
let mut context = window_context.borrow_mut();
|
||||
|
||||
{
|
||||
let _scene_span =
|
||||
tracing::info_span!("scene_loading_if_not_loaded").entered();
|
||||
scene_manager
|
||||
.load_scene_if_not_loaded(&mut context)
|
||||
.unwrap();
|
||||
}
|
||||
|
||||
if let Some(scene) = scene_manager.current_scene_mut() {
|
||||
{
|
||||
let _update_span = tracing::debug_span!("scene_update").entered();
|
||||
scene.update(&mut context).unwrap();
|
||||
}
|
||||
|
||||
let acquire_future = {
|
||||
let _acquire_span = tracing::debug_span!("acquire_swapchain").entered();
|
||||
context.with_renderer_mut(|renderer| {
|
||||
renderer.acquire(None, |_| {}).unwrap()
|
||||
})
|
||||
};
|
||||
|
||||
let acquire_future = {
|
||||
let _render_span = tracing::debug_span!("scene_render").entered();
|
||||
scene.render(acquire_future, &mut context).unwrap()
|
||||
};
|
||||
|
||||
{
|
||||
let _present_span = tracing::debug_span!("present_frame").entered();
|
||||
context.with_renderer_mut(|renderer| {
|
||||
renderer.present(acquire_future, true);
|
||||
});
|
||||
}
|
||||
} else {
|
||||
tracing::warn!("No current scene found for update!");
|
||||
}
|
||||
}
|
||||
|
||||
{
|
||||
let _gui_span = tracing::debug_span!("request_redraw").entered();
|
||||
let mut window_context = window_context.borrow_mut();
|
||||
window_context.with_renderer_mut(|renderer| {
|
||||
renderer.window().request_redraw();
|
||||
});
|
||||
}
|
||||
}
|
||||
_ => {}
|
||||
}
|
||||
}
|
||||
|
||||
fn user_event(&mut self, event_loop: &ActiveEventLoop, event: UserEvent) {
|
||||
match event {
|
||||
UserEvent::CursorGrabMode(window_id, grab) => {
|
||||
let vulkano_windows = self.vulkano_windows.borrow();
|
||||
let window = vulkano_windows.get_window(window_id).unwrap();
|
||||
if let Err(e) = window.set_cursor_grab(grab) {
|
||||
tracing::error!("Failed to set cursor grab: {}", e);
|
||||
}
|
||||
}
|
||||
UserEvent::CursorVisible(window_id, visible) => {
|
||||
let vulkano_windows = self.vulkano_windows.borrow();
|
||||
let window = vulkano_windows.get_window(window_id).unwrap();
|
||||
window.set_cursor_visible(visible);
|
||||
}
|
||||
UserEvent::ChangeScene(window_id, scene) => {
|
||||
if let Some(scene_manager) = self.scene_manager.get_mut(&window_id) {
|
||||
scene_manager.load_scene(scene);
|
||||
}
|
||||
}
|
||||
UserEvent::ChangeResolution(window_id, width, height) => {
|
||||
let mut vulkano_windows = self.vulkano_windows.borrow_mut();
|
||||
let window = vulkano_windows.get_window(window_id).unwrap();
|
||||
let _ = window.request_inner_size(winit::dpi::LogicalSize::new(width, height));
|
||||
let renderer = vulkano_windows.get_renderer_mut(window_id).unwrap();
|
||||
renderer.resize();
|
||||
tracing::trace!(
|
||||
"Resolution changed to {}x{} for window {:?}",
|
||||
width,
|
||||
height,
|
||||
window_id
|
||||
);
|
||||
}
|
||||
UserEvent::Exit(window_id) => {
|
||||
tracing::trace!("Exit requested for window {:?}", window_id);
|
||||
event_loop.exit();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
|
@ -1,11 +0,0 @@
|
|||
use winit::window::{CursorGrabMode, WindowId};
|
||||
|
||||
use crate::core::scene::Scene;
|
||||
|
||||
pub enum UserEvent {
|
||||
CursorGrabMode(WindowId, CursorGrabMode),
|
||||
CursorVisible(WindowId, bool),
|
||||
ChangeScene(WindowId, Box<dyn Scene>),
|
||||
ChangeResolution(WindowId, f32, f32),
|
||||
Exit(WindowId),
|
||||
}
|
|
@ -1,86 +0,0 @@
|
|||
use std::{
|
||||
collections::HashMap,
|
||||
hash::Hash,
|
||||
ops::{Add, AddAssign, Sub},
|
||||
};
|
||||
|
||||
use winit::event::ElementState;
|
||||
|
||||
pub struct CachedElementState<K: Eq + Hash> {
|
||||
cache: HashMap<K, ElementState>,
|
||||
}
|
||||
|
||||
impl<K: Eq + Hash> Default for CachedElementState<K> {
|
||||
fn default() -> Self {
|
||||
Self {
|
||||
cache: HashMap::new(),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl<K: Eq + Hash> CachedElementState<K> {
|
||||
pub fn set_key_state(&mut self, key: K, state: ElementState) -> Option<ElementState> {
|
||||
let key_state = self.cache.get(&key);
|
||||
let new_key_state = match key_state {
|
||||
Some(old) => match state {
|
||||
ElementState::Pressed => match old {
|
||||
ElementState::Released => Some(ElementState::Pressed),
|
||||
ElementState::Pressed => None,
|
||||
},
|
||||
ElementState::Released => match old {
|
||||
ElementState::Released => None,
|
||||
ElementState::Pressed => Some(ElementState::Released),
|
||||
},
|
||||
},
|
||||
None => match state {
|
||||
ElementState::Pressed => Some(ElementState::Pressed),
|
||||
ElementState::Released => Some(ElementState::Released),
|
||||
},
|
||||
};
|
||||
if let Some(new_key_state) = new_key_state {
|
||||
self.cache.insert(key, new_key_state);
|
||||
}
|
||||
new_key_state
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Default)]
|
||||
pub struct CachedMovement<T>
|
||||
where
|
||||
T: Sub<Output = T> + Add<Output = T> + Default + Copy,
|
||||
{
|
||||
pub old_value: Option<T>,
|
||||
pub value: T,
|
||||
}
|
||||
|
||||
impl<T> CachedMovement<T>
|
||||
where
|
||||
T: Sub<Output = T> + Add<Output = T> + Default + Copy,
|
||||
{
|
||||
pub fn set_value(&mut self, value: T) {
|
||||
self.value = value;
|
||||
}
|
||||
|
||||
pub fn reset(&mut self) -> T {
|
||||
match self.old_value.as_ref() {
|
||||
Some(old_value) => {
|
||||
let diff = self.value - *old_value;
|
||||
self.old_value = Some(self.value);
|
||||
diff
|
||||
}
|
||||
None => {
|
||||
self.old_value = Some(self.value);
|
||||
T::default()
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl<T> AddAssign<T> for CachedMovement<T>
|
||||
where
|
||||
T: Add<Output = T> + Sub<Output = T> + Default + Copy,
|
||||
{
|
||||
fn add_assign(&mut self, rhs: T) {
|
||||
self.value = self.value + rhs;
|
||||
}
|
||||
}
|
|
@ -1,97 +0,0 @@
|
|||
use std::collections::HashMap;
|
||||
|
||||
use cache::{CachedElementState, CachedMovement};
|
||||
use virtual_input::VirtualInput;
|
||||
use winit::{
|
||||
event::{DeviceEvent, MouseButton, MouseScrollDelta, WindowEvent},
|
||||
keyboard::PhysicalKey,
|
||||
};
|
||||
|
||||
mod cache;
|
||||
mod virtual_binding;
|
||||
mod virtual_input;
|
||||
mod virtual_state;
|
||||
pub use virtual_binding::{AxisDirection, VirtualBinding};
|
||||
|
||||
#[derive(Default)]
|
||||
pub struct InputManager {
|
||||
keys_state: CachedElementState<PhysicalKey>,
|
||||
mouse_buttons_state: CachedElementState<MouseButton>,
|
||||
mouse_position_delta: CachedMovement<glam::Vec2>,
|
||||
mouse_wheel_delta: CachedMovement<glam::Vec2>,
|
||||
virtual_input: VirtualInput,
|
||||
}
|
||||
|
||||
impl std::fmt::Debug for InputManager {
|
||||
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
|
||||
f.debug_struct("InputManager")
|
||||
.field("virtual_input", &self.virtual_input)
|
||||
.finish()
|
||||
}
|
||||
}
|
||||
|
||||
impl InputManager {
|
||||
pub fn new(input_mapping: HashMap<String, Vec<VirtualBinding>>) -> Self {
|
||||
let mut input_manager = InputManager::default();
|
||||
for (value_name, bindings) in input_mapping {
|
||||
input_manager.add_virtual_bindings(value_name, bindings);
|
||||
}
|
||||
input_manager
|
||||
}
|
||||
|
||||
pub fn process_device_event(&mut self, event: &DeviceEvent) {
|
||||
if let DeviceEvent::MouseMotion { delta, .. } = event {
|
||||
self.mouse_position_delta += glam::Vec2::new(delta.0 as f32, delta.1 as f32);
|
||||
}
|
||||
}
|
||||
|
||||
pub fn process_window_event(&mut self, event: &WindowEvent) {
|
||||
match event {
|
||||
WindowEvent::AxisMotion { axis, value, .. } => {
|
||||
self.virtual_input.update_axis_binding(*axis, *value as f32);
|
||||
}
|
||||
WindowEvent::KeyboardInput { event, .. } => {
|
||||
let new_key_state = self
|
||||
.keys_state
|
||||
.set_key_state(event.physical_key, event.state);
|
||||
if let Some(new_key_state) = new_key_state {
|
||||
self.virtual_input
|
||||
.update_key_binding(event.physical_key, new_key_state);
|
||||
}
|
||||
}
|
||||
WindowEvent::MouseInput { button, state, .. } => {
|
||||
let new_mouse_button_state =
|
||||
self.mouse_buttons_state.set_key_state(*button, *state);
|
||||
if let Some(new_mouse_button_state) = new_mouse_button_state {
|
||||
self.virtual_input
|
||||
.update_mouse_button_binding(*button, new_mouse_button_state);
|
||||
}
|
||||
}
|
||||
WindowEvent::MouseWheel { delta, .. } => {
|
||||
self.mouse_wheel_delta += match delta {
|
||||
MouseScrollDelta::PixelDelta(position) => {
|
||||
glam::Vec2::new(position.x as f32, position.y as f32)
|
||||
}
|
||||
MouseScrollDelta::LineDelta(x, y) => glam::Vec2::new(*x, *y),
|
||||
};
|
||||
}
|
||||
_ => {}
|
||||
}
|
||||
}
|
||||
|
||||
/// Updates deltas before running update
|
||||
pub fn update(&mut self) {
|
||||
self.virtual_input
|
||||
.update_mouse_move_binding(&self.mouse_position_delta.reset());
|
||||
self.virtual_input
|
||||
.update_mouse_wheel_binding(&self.mouse_wheel_delta.reset());
|
||||
}
|
||||
|
||||
pub fn get_virtual_input_state(&self, value_name: &str) -> f32 {
|
||||
self.virtual_input.get_state(value_name)
|
||||
}
|
||||
|
||||
fn add_virtual_bindings(&mut self, value_name: String, bindings: Vec<VirtualBinding>) {
|
||||
self.virtual_input.add_bindings(value_name, bindings);
|
||||
}
|
||||
}
|
|
@ -1,30 +0,0 @@
|
|||
use winit::{
|
||||
event::{AxisId, MouseButton},
|
||||
keyboard::PhysicalKey,
|
||||
};
|
||||
|
||||
#[derive(Clone)]
|
||||
pub enum AxisDirection {
|
||||
Normal,
|
||||
Invert,
|
||||
}
|
||||
|
||||
impl From<&AxisDirection> for f32 {
|
||||
fn from(direction: &AxisDirection) -> Self {
|
||||
match direction {
|
||||
AxisDirection::Normal => 1.0,
|
||||
AxisDirection::Invert => -1.0,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Clone)]
|
||||
pub enum VirtualBinding {
|
||||
Keyboard(PhysicalKey, AxisDirection),
|
||||
Axis(AxisId, AxisDirection, f32), // f32 deadzone
|
||||
MouseX(AxisDirection),
|
||||
MouseY(AxisDirection),
|
||||
MouseWheelX(AxisDirection),
|
||||
MouseWheelY(AxisDirection),
|
||||
MouseButton(MouseButton, AxisDirection),
|
||||
}
|
|
@ -1,150 +0,0 @@
|
|||
use std::{
|
||||
collections::HashMap,
|
||||
sync::{Arc, RwLock},
|
||||
};
|
||||
|
||||
use winit::{
|
||||
event::{AxisId, ElementState, MouseButton},
|
||||
keyboard::PhysicalKey,
|
||||
};
|
||||
|
||||
use super::{
|
||||
virtual_binding::VirtualBinding,
|
||||
virtual_state::{VirtualBindingState, VirtualInputState},
|
||||
};
|
||||
|
||||
#[derive(Default)]
|
||||
pub struct VirtualInput {
|
||||
// Global states
|
||||
states: HashMap<String, Arc<RwLock<VirtualInputState>>>,
|
||||
|
||||
// Per kind of input states to keep complexity low during state updates
|
||||
states_by_key: HashMap<PhysicalKey, Vec<Arc<RwLock<VirtualInputState>>>>,
|
||||
mouse_move_states: Vec<Arc<RwLock<VirtualInputState>>>,
|
||||
mouse_wheel_states: Vec<Arc<RwLock<VirtualInputState>>>,
|
||||
mouse_button_states: HashMap<MouseButton, Vec<Arc<RwLock<VirtualInputState>>>>,
|
||||
axis_states: HashMap<AxisId, Vec<Arc<RwLock<VirtualInputState>>>>,
|
||||
}
|
||||
|
||||
impl std::fmt::Debug for VirtualInput {
|
||||
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
|
||||
let mut debug = f.debug_struct("VirtualInput");
|
||||
|
||||
for (name, state) in &self.states {
|
||||
let value = state.read().expect("Poisoned lock for debug").value;
|
||||
debug.field(name, &value);
|
||||
}
|
||||
|
||||
debug.finish()
|
||||
}
|
||||
}
|
||||
|
||||
impl VirtualInput {
|
||||
pub fn get_state(&self, value_name: &str) -> f32 {
|
||||
self.states
|
||||
.get(value_name)
|
||||
.map(|state| state.read().expect("Poisoned lock for get state").value)
|
||||
.unwrap_or(0.0)
|
||||
}
|
||||
|
||||
pub fn add_bindings(&mut self, value_name: String, new_bindings: Vec<VirtualBinding>) {
|
||||
let state = self
|
||||
.states
|
||||
.entry(value_name)
|
||||
.or_insert(Arc::new(RwLock::new(VirtualInputState {
|
||||
value: 0.0,
|
||||
bindings: Vec::new(),
|
||||
})));
|
||||
|
||||
for binding in &new_bindings {
|
||||
match binding {
|
||||
VirtualBinding::Keyboard(key, _) => {
|
||||
self.states_by_key
|
||||
.entry(*key)
|
||||
.or_default()
|
||||
.push(state.clone());
|
||||
}
|
||||
VirtualBinding::MouseX(_) | VirtualBinding::MouseY(_) => {
|
||||
self.mouse_move_states.push(state.clone());
|
||||
}
|
||||
VirtualBinding::MouseButton(button, _) => {
|
||||
self.mouse_button_states
|
||||
.entry(*button)
|
||||
.or_default()
|
||||
.push(state.clone());
|
||||
}
|
||||
VirtualBinding::MouseWheelX(_) | VirtualBinding::MouseWheelY(_) => {
|
||||
self.mouse_wheel_states.push(state.clone());
|
||||
}
|
||||
VirtualBinding::Axis(axis, _, _) => {
|
||||
self.axis_states
|
||||
.entry(*axis)
|
||||
.or_default()
|
||||
.push(state.clone());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
state
|
||||
.write()
|
||||
.expect("Poisoned lock for add bindings")
|
||||
.bindings
|
||||
.extend(new_bindings.iter().map(|b| VirtualBindingState {
|
||||
value: 0.0,
|
||||
binding: b.clone(),
|
||||
}));
|
||||
}
|
||||
|
||||
pub(super) fn update_key_binding(&mut self, key: PhysicalKey, key_state: ElementState) {
|
||||
let states = self.states_by_key.get_mut(&key);
|
||||
|
||||
if let Some(states) = states {
|
||||
for state in states {
|
||||
let mut state = state.write().expect("Poisoned lock for key update");
|
||||
state.update_from_key(key, key_state);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub(super) fn update_mouse_move_binding(&mut self, delta: &glam::Vec2) {
|
||||
for state in &mut self.mouse_move_states {
|
||||
let mut state = state.write().expect("Poisoned lock for mouse move update");
|
||||
state.update_from_mouse(delta);
|
||||
}
|
||||
}
|
||||
|
||||
pub(super) fn update_mouse_wheel_binding(&mut self, delta: &glam::Vec2) {
|
||||
for state in &mut self.mouse_wheel_states {
|
||||
let mut state = state.write().expect("Poisoned lock for mouse wheel update");
|
||||
state.update_from_mouse_wheel(delta);
|
||||
}
|
||||
}
|
||||
|
||||
pub(super) fn update_mouse_button_binding(
|
||||
&mut self,
|
||||
button: MouseButton,
|
||||
button_state: ElementState,
|
||||
) {
|
||||
let states = self.mouse_button_states.get_mut(&button);
|
||||
|
||||
if let Some(states) = states {
|
||||
for state in states {
|
||||
let mut state = state
|
||||
.write()
|
||||
.expect("Poisoned lock for mouse button update");
|
||||
state.update_from_mouse_button(button, button_state);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub(super) fn update_axis_binding(&mut self, axis: AxisId, axis_state: f32) {
|
||||
let states = self.axis_states.get_mut(&axis);
|
||||
|
||||
if let Some(states) = states {
|
||||
for state in states {
|
||||
let mut state = state.write().expect("Poisoned lock for axis update");
|
||||
state.update_from_axis(axis, axis_state);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
|
@ -1,105 +0,0 @@
|
|||
use winit::{
|
||||
event::{AxisId, ElementState, MouseButton},
|
||||
keyboard::PhysicalKey,
|
||||
};
|
||||
|
||||
use super::virtual_binding::VirtualBinding;
|
||||
|
||||
pub struct VirtualBindingState {
|
||||
pub value: f32,
|
||||
pub binding: VirtualBinding,
|
||||
}
|
||||
|
||||
pub struct VirtualInputState {
|
||||
pub value: f32,
|
||||
pub bindings: Vec<VirtualBindingState>,
|
||||
}
|
||||
|
||||
impl VirtualInputState {
|
||||
pub fn update_from_key(&mut self, key: PhysicalKey, key_state: ElementState) {
|
||||
let mut new_value = 0.0;
|
||||
for binding in &mut self.bindings {
|
||||
if let VirtualBinding::Keyboard(binding_key, direction) = &binding.binding {
|
||||
if binding_key == &key {
|
||||
if key_state == ElementState::Pressed {
|
||||
binding.value += f32::from(direction);
|
||||
} else {
|
||||
binding.value = 0.0;
|
||||
}
|
||||
}
|
||||
}
|
||||
new_value += binding.value;
|
||||
}
|
||||
self.value = new_value;
|
||||
}
|
||||
|
||||
pub fn update_from_mouse(&mut self, delta: &glam::Vec2) {
|
||||
let mut new_value = 0.0;
|
||||
for binding in &mut self.bindings {
|
||||
match &binding.binding {
|
||||
VirtualBinding::MouseX(direction) => {
|
||||
binding.value = f32::from(direction) * delta.x;
|
||||
}
|
||||
VirtualBinding::MouseY(direction) => {
|
||||
binding.value = f32::from(direction) * delta.y;
|
||||
}
|
||||
_ => {}
|
||||
}
|
||||
new_value += binding.value;
|
||||
}
|
||||
self.value = new_value;
|
||||
}
|
||||
|
||||
pub fn update_from_mouse_wheel(&mut self, delta: &glam::Vec2) {
|
||||
let mut new_value = 0.0;
|
||||
for binding in &mut self.bindings {
|
||||
match &binding.binding {
|
||||
VirtualBinding::MouseWheelX(direction) => {
|
||||
binding.value = f32::from(direction) * delta.x;
|
||||
}
|
||||
VirtualBinding::MouseWheelY(direction) => {
|
||||
binding.value = f32::from(direction) * delta.y;
|
||||
}
|
||||
_ => {}
|
||||
}
|
||||
new_value += binding.value;
|
||||
}
|
||||
self.value = new_value;
|
||||
}
|
||||
|
||||
pub fn update_from_mouse_button(&mut self, button: MouseButton, button_state: ElementState) {
|
||||
let mut new_value = 0.0;
|
||||
for binding in &mut self.bindings {
|
||||
if let VirtualBinding::MouseButton(binding_button, direction) = &binding.binding {
|
||||
if binding_button == &button {
|
||||
if button_state == ElementState::Pressed {
|
||||
binding.value = f32::from(direction);
|
||||
} else {
|
||||
binding.value = 0.0;
|
||||
}
|
||||
}
|
||||
}
|
||||
new_value += binding.value;
|
||||
}
|
||||
self.value = new_value;
|
||||
}
|
||||
|
||||
pub fn update_from_axis(&mut self, axis: AxisId, axis_state: f32) {
|
||||
let mut new_value = 0.0;
|
||||
for binding in &mut self.bindings {
|
||||
if let VirtualBinding::Axis(binding_axis, direction, deadzone) = &binding.binding {
|
||||
if binding_axis == &axis {
|
||||
binding.value =
|
||||
f32::from(direction) * process_axis_deadzone(axis_state, *deadzone);
|
||||
}
|
||||
}
|
||||
new_value += binding.value;
|
||||
}
|
||||
self.value = new_value;
|
||||
}
|
||||
}
|
||||
|
||||
#[inline]
|
||||
fn process_axis_deadzone(value: f32, deadzone: f32) -> f32 {
|
||||
if value.abs() < deadzone { 0.0 } else { value }
|
||||
}
|
|
@ -1,5 +0,0 @@
|
|||
pub mod app;
|
||||
pub mod input;
|
||||
pub mod render;
|
||||
pub mod scene;
|
||||
pub mod timer;
|
|
@ -1,246 +0,0 @@
|
|||
use std::{
|
||||
any::TypeId,
|
||||
error::Error,
|
||||
sync::{Arc, RwLock, RwLockReadGuard},
|
||||
};
|
||||
|
||||
use vulkano::{device::Device, format::Format, memory::allocator::StandardMemoryAllocator};
|
||||
|
||||
pub trait Pipeline {
|
||||
fn load(
|
||||
&mut self,
|
||||
device: &Device,
|
||||
memory_allocator: &StandardMemoryAllocator,
|
||||
swapchain_format: Format,
|
||||
depth_format: Format,
|
||||
) -> Result<(), Box<dyn Error>>;
|
||||
}
|
||||
|
||||
pub trait Material {
|
||||
fn pipeline_type_id() -> TypeId
|
||||
where
|
||||
Self: Sized;
|
||||
|
||||
fn load(
|
||||
&mut self,
|
||||
device: &Device,
|
||||
memory_allocator: &StandardMemoryAllocator,
|
||||
) -> Result<(), Box<dyn Error>>;
|
||||
}
|
||||
|
||||
pub enum MaterialState {
|
||||
Loading,
|
||||
Loaded,
|
||||
}
|
||||
|
||||
pub enum MaterialError {
|
||||
PipelineNotFound,
|
||||
}
|
||||
|
||||
pub struct MaterialManager {
|
||||
device: Arc<Device>,
|
||||
memory_allocator: Arc<StandardMemoryAllocator>,
|
||||
swapchain_format: Format,
|
||||
depth_format: Format,
|
||||
|
||||
pipelines_id: Arc<RwLock<Vec<TypeId>>>,
|
||||
pipelines_state: Arc<RwLock<Vec<Arc<RwLock<MaterialState>>>>>,
|
||||
pipelines: Arc<RwLock<Vec<Arc<RwLock<dyn Pipeline>>>>>,
|
||||
|
||||
materials_id: Arc<RwLock<Vec<TypeId>>>,
|
||||
materials_pipeline_id: Arc<RwLock<Vec<TypeId>>>,
|
||||
materials_pipeline: Arc<RwLock<Vec<Arc<RwLock<dyn Pipeline>>>>>,
|
||||
materials_pipeline_state: Arc<RwLock<Vec<Arc<RwLock<MaterialState>>>>>,
|
||||
materials_state: Arc<RwLock<Vec<Arc<RwLock<MaterialState>>>>>,
|
||||
materials: Arc<RwLock<Vec<Arc<RwLock<dyn Material>>>>>,
|
||||
}
|
||||
|
||||
impl MaterialManager {
|
||||
pub fn new(
|
||||
device: Arc<Device>,
|
||||
memory_allocator: Arc<StandardMemoryAllocator>,
|
||||
swapchain_format: Format,
|
||||
depth_format: Format,
|
||||
) -> Self {
|
||||
Self {
|
||||
device,
|
||||
memory_allocator,
|
||||
swapchain_format,
|
||||
depth_format,
|
||||
pipelines_id: Arc::new(RwLock::new(Vec::new())),
|
||||
pipelines_state: Arc::new(RwLock::new(Vec::new())),
|
||||
pipelines: Arc::new(RwLock::new(Vec::new())),
|
||||
materials_id: Arc::new(RwLock::new(Vec::new())),
|
||||
materials_pipeline_id: Arc::new(RwLock::new(Vec::new())),
|
||||
materials_pipeline: Arc::new(RwLock::new(Vec::new())),
|
||||
materials_pipeline_state: Arc::new(RwLock::new(Vec::new())),
|
||||
materials_state: Arc::new(RwLock::new(Vec::new())),
|
||||
materials: Arc::new(RwLock::new(Vec::new())),
|
||||
}
|
||||
}
|
||||
|
||||
pub fn add_pipeline<P: Pipeline + Default + 'static>(&self) {
|
||||
let type_id = TypeId::of::<P>();
|
||||
let pipeline = Arc::new(RwLock::new(P::default()));
|
||||
|
||||
let mut pipelines_id = self.pipelines_id.write().unwrap();
|
||||
let mut pipelines_state = self.pipelines_state.write().unwrap();
|
||||
let mut pipelines = self.pipelines.write().unwrap();
|
||||
|
||||
pipelines_id.push(type_id);
|
||||
pipelines_state.push(Arc::new(RwLock::new(MaterialState::Loading)));
|
||||
pipelines.push(pipeline.clone());
|
||||
}
|
||||
|
||||
pub fn add_material<M: Material + Default + 'static>(&self) -> Result<(), MaterialError> {
|
||||
let pipeline_id = M::pipeline_type_id();
|
||||
|
||||
let pipeline_result = {
|
||||
let pipelines_id = self.pipelines_id.read().unwrap();
|
||||
let pipelines_state = self.pipelines_state.read().unwrap();
|
||||
let pipelines = self.pipelines.read().unwrap();
|
||||
|
||||
pipelines_id
|
||||
.iter()
|
||||
.zip(pipelines.iter())
|
||||
.zip(pipelines_state.iter())
|
||||
.find(|((id, _), _)| *id == &pipeline_id)
|
||||
.map(|((_, pipeline), state)| (pipeline.clone(), state.clone()))
|
||||
};
|
||||
|
||||
let (pipeline, pipeline_state) = match pipeline_result {
|
||||
Some(pipeline) => pipeline,
|
||||
None => {
|
||||
tracing::error!(
|
||||
"Pipeline with id {pipeline_id:?} not found, please add it before adding a material"
|
||||
);
|
||||
return Err(MaterialError::PipelineNotFound);
|
||||
}
|
||||
};
|
||||
|
||||
let type_id = TypeId::of::<M>();
|
||||
|
||||
let mut materials_id = self.materials_id.write().unwrap();
|
||||
let mut materials_pipeline_id = self.materials_pipeline_id.write().unwrap();
|
||||
let mut materials_pipeline = self.materials_pipeline.write().unwrap();
|
||||
let mut materials_pipeline_state = self.materials_pipeline_state.write().unwrap();
|
||||
let mut materials_state = self.materials_state.write().unwrap();
|
||||
let mut materials = self.materials.write().unwrap();
|
||||
|
||||
materials_id.push(type_id);
|
||||
materials_pipeline_id.push(pipeline_id);
|
||||
materials_pipeline.push(pipeline);
|
||||
materials_pipeline_state.push(pipeline_state);
|
||||
materials_state.push(Arc::new(RwLock::new(MaterialState::Loading)));
|
||||
materials.push(Arc::new(RwLock::new(M::default())));
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
pub fn update_swapchain_format(&mut self, swapchain_format: Format) {
|
||||
if self.swapchain_format == swapchain_format {
|
||||
return;
|
||||
}
|
||||
|
||||
self.swapchain_format = swapchain_format;
|
||||
self.mark_all_pipelines_as_loading();
|
||||
}
|
||||
|
||||
fn mark_all_pipelines_as_loading(&self) {
|
||||
let pipelines_state = self.pipelines_state.write().unwrap();
|
||||
|
||||
for state in pipelines_state.iter() {
|
||||
let mut state = state.write().unwrap();
|
||||
*state = MaterialState::Loading;
|
||||
}
|
||||
}
|
||||
|
||||
fn load_pipelines(&self) {
|
||||
let pipelines_state = self.pipelines_state.read().unwrap();
|
||||
let pipelines = self.pipelines.read().unwrap();
|
||||
|
||||
let iter = pipelines_state
|
||||
.iter()
|
||||
.zip(pipelines.iter())
|
||||
.filter(|(state, _)| {
|
||||
let state = state.read().unwrap();
|
||||
matches!(*state, MaterialState::Loading)
|
||||
});
|
||||
|
||||
for (state, pipeline) in iter {
|
||||
let mut pipeline = pipeline.write().unwrap();
|
||||
let result = pipeline.load(
|
||||
&self.device,
|
||||
&self.memory_allocator,
|
||||
self.swapchain_format,
|
||||
self.depth_format,
|
||||
);
|
||||
|
||||
match result {
|
||||
Ok(_) => {
|
||||
let mut state = state.write().unwrap();
|
||||
*state = MaterialState::Loaded;
|
||||
}
|
||||
Err(e) => {
|
||||
tracing::error!("Failed to load pipeline: {e}");
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn load_materials(&self) {
|
||||
let materials_state = self.materials_state.read().unwrap();
|
||||
let materials = self.materials.read().unwrap();
|
||||
|
||||
let iter = materials_state
|
||||
.iter()
|
||||
.zip(materials.iter())
|
||||
.filter(|(state, _)| {
|
||||
let state = state.read().unwrap();
|
||||
matches!(*state, MaterialState::Loading)
|
||||
});
|
||||
|
||||
for (state, material) in iter {
|
||||
let mut material = material.write().unwrap();
|
||||
let result = material.load(&self.device, &self.memory_allocator);
|
||||
|
||||
match result {
|
||||
Ok(_) => {
|
||||
let mut state = state.write().unwrap();
|
||||
*state = MaterialState::Loaded;
|
||||
}
|
||||
Err(e) => {
|
||||
tracing::error!("Failed to load material: {e}");
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn render_materials<F>(&self, f: F)
|
||||
where
|
||||
F: Fn(RwLockReadGuard<'_, dyn Material>, RwLockReadGuard<'_, dyn Pipeline>),
|
||||
{
|
||||
let materials = self.materials.read().unwrap();
|
||||
let materials_state = self.materials_state.read().unwrap();
|
||||
let materials_pipeline = self.materials_pipeline.read().unwrap();
|
||||
let materials_pipeline_state = self.materials_pipeline_state.read().unwrap();
|
||||
|
||||
materials
|
||||
.iter()
|
||||
.zip(materials_state.iter())
|
||||
.zip(materials_pipeline.iter())
|
||||
.zip(materials_pipeline_state.iter())
|
||||
.filter(|(((_, material_state), _), pipeline_state)| {
|
||||
let material_state = material_state.read().unwrap();
|
||||
let pipeline_state = pipeline_state.read().unwrap();
|
||||
matches!(*material_state, MaterialState::Loaded)
|
||||
&& matches!(*pipeline_state, MaterialState::Loaded)
|
||||
})
|
||||
.for_each(|(((material, _), pipeline), _)| {
|
||||
let material = material.read().unwrap();
|
||||
let pipeline = pipeline.read().unwrap();
|
||||
|
||||
f(material, pipeline);
|
||||
});
|
||||
}
|
||||
}
|
|
@ -1,5 +0,0 @@
|
|||
pub mod material_manager;
|
||||
pub mod primitives;
|
||||
pub mod render_pass_manager;
|
||||
pub mod texture;
|
||||
pub mod vulkan_context;
|
|
@ -1,122 +0,0 @@
|
|||
use std::{f32::consts::FRAC_PI_2, sync::Arc};
|
||||
|
||||
use glam::{Mat4, Vec3, Vec4};
|
||||
use vulkano::{
|
||||
Validated,
|
||||
buffer::{AllocateBufferError, Subbuffer},
|
||||
memory::allocator::StandardMemoryAllocator,
|
||||
};
|
||||
|
||||
use crate::core::{input::InputManager, timer::Timer};
|
||||
|
||||
use super::mvp::Mvp;
|
||||
|
||||
// See docs/OPENGL_VULKAN_DIFF.md
|
||||
const OPENGL_TO_VULKAN_Y_AXIS_FLIP: Mat4 = Mat4 {
|
||||
x_axis: Vec4::new(1.0, 0.0, 0.0, 0.0),
|
||||
y_axis: Vec4::new(0.0, -1.0, 0.0, 0.0),
|
||||
z_axis: Vec4::new(0.0, 0.0, 1.0, 0.0),
|
||||
w_axis: Vec4::new(0.0, 0.0, 0.0, 1.0),
|
||||
};
|
||||
|
||||
#[derive(Default)]
|
||||
pub struct Camera3D {
|
||||
projection: Mat4,
|
||||
|
||||
position: Vec3,
|
||||
rotation: Vec3,
|
||||
aspect_ratio: f32,
|
||||
fov: f32,
|
||||
near: f32,
|
||||
far: f32,
|
||||
}
|
||||
|
||||
impl Camera3D {
|
||||
pub fn new(aspect_ratio: f32, fov: f32, near: f32, far: f32) -> Self {
|
||||
Self {
|
||||
projection: Mat4::perspective_rh(fov, aspect_ratio, near, far),
|
||||
position: Vec3::ZERO,
|
||||
rotation: Vec3::ZERO,
|
||||
aspect_ratio,
|
||||
fov,
|
||||
near,
|
||||
far,
|
||||
}
|
||||
}
|
||||
|
||||
pub fn update(
|
||||
&mut self,
|
||||
input_manager: &InputManager,
|
||||
timer: &Timer,
|
||||
movement_speed: f32,
|
||||
camera_sensitivity: f32,
|
||||
window_aspect_ratio: f32,
|
||||
) {
|
||||
// Process camera rotation
|
||||
let camera_delta = camera_sensitivity * timer.delta_time();
|
||||
self.rotation += Vec3::new(
|
||||
(input_manager.get_virtual_input_state("mouse_y") * camera_delta).to_radians(),
|
||||
(input_manager.get_virtual_input_state("mouse_x") * camera_delta).to_radians(),
|
||||
0.0,
|
||||
);
|
||||
|
||||
if self.rotation.x > FRAC_PI_2 {
|
||||
self.rotation = Vec3::new(FRAC_PI_2, self.rotation.y, 0.0);
|
||||
}
|
||||
|
||||
if self.rotation.x < -FRAC_PI_2 {
|
||||
self.rotation = Vec3::new(-FRAC_PI_2, self.rotation.y, 0.0);
|
||||
}
|
||||
|
||||
let movement_delta = movement_speed * timer.delta_time();
|
||||
|
||||
let (yaw_sin, yaw_cos) = self.rotation.y.sin_cos();
|
||||
let forward = Vec3::new(yaw_cos, 0.0, yaw_sin).normalize();
|
||||
let right = Vec3::new(-yaw_sin, 0.0, yaw_cos).normalize();
|
||||
|
||||
let tx = input_manager.get_virtual_input_state("move_right") * movement_delta;
|
||||
self.position += tx * right;
|
||||
|
||||
let tz = input_manager.get_virtual_input_state("move_forward") * movement_delta;
|
||||
self.position += tz * forward;
|
||||
|
||||
if self.aspect_ratio != window_aspect_ratio {
|
||||
self.aspect_ratio = window_aspect_ratio;
|
||||
self.projection =
|
||||
Mat4::perspective_rh(self.fov, self.aspect_ratio, self.near, self.far);
|
||||
}
|
||||
}
|
||||
|
||||
pub fn set_projection(&mut self, projection: Mat4) {
|
||||
self.projection = projection;
|
||||
}
|
||||
|
||||
pub fn get_position(&self) -> Vec3 {
|
||||
self.position
|
||||
}
|
||||
|
||||
pub fn get_rotation(&self) -> Vec3 {
|
||||
self.rotation
|
||||
}
|
||||
|
||||
pub fn create_buffer(
|
||||
&self,
|
||||
memory_allocator: &Arc<StandardMemoryAllocator>,
|
||||
) -> Result<Subbuffer<[Mvp]>, Validated<AllocateBufferError>> {
|
||||
let (sin_pitch, cos_pitch) = self.rotation.x.sin_cos();
|
||||
let (sin_yaw, cos_yaw) = self.rotation.y.sin_cos();
|
||||
|
||||
let view_matrix = Mat4::look_to_rh(
|
||||
self.position,
|
||||
Vec3::new(cos_pitch * cos_yaw, sin_pitch, cos_pitch * sin_yaw).normalize(),
|
||||
Vec3::Y,
|
||||
);
|
||||
|
||||
Mvp {
|
||||
model: OPENGL_TO_VULKAN_Y_AXIS_FLIP.to_cols_array_2d(),
|
||||
view: view_matrix.to_cols_array_2d(),
|
||||
projection: self.projection.to_cols_array_2d(),
|
||||
}
|
||||
.into_buffer(memory_allocator)
|
||||
}
|
||||
}
|
|
@ -1,25 +0,0 @@
|
|||
use std::{collections::BTreeMap, sync::Arc};
|
||||
|
||||
use vulkano::{
|
||||
Validated, VulkanError,
|
||||
descriptor_set::{
|
||||
DescriptorSet,
|
||||
allocator::StandardDescriptorSetAllocator,
|
||||
layout::{DescriptorSetLayout, DescriptorSetLayoutBinding},
|
||||
},
|
||||
};
|
||||
|
||||
pub mod camera;
|
||||
pub mod mvp;
|
||||
pub mod transform;
|
||||
pub mod vertex;
|
||||
|
||||
pub trait AsBindableDescriptorSet<T> {
|
||||
fn as_descriptor_set_layout_bindings() -> BTreeMap<u32, DescriptorSetLayoutBinding>;
|
||||
|
||||
fn as_descriptor_set(
|
||||
descriptor_set_allocator: &Arc<StandardDescriptorSetAllocator>,
|
||||
layout: &Arc<DescriptorSetLayout>,
|
||||
data: &T,
|
||||
) -> Result<Arc<DescriptorSet>, Validated<VulkanError>>;
|
||||
}
|
|
@ -1,70 +0,0 @@
|
|||
use std::collections::BTreeMap;
|
||||
use std::sync::Arc;
|
||||
|
||||
use vulkano::buffer::{
|
||||
AllocateBufferError, Buffer, BufferContents, BufferCreateInfo, BufferUsage, Subbuffer,
|
||||
};
|
||||
use vulkano::descriptor_set::allocator::StandardDescriptorSetAllocator;
|
||||
use vulkano::descriptor_set::layout::{
|
||||
DescriptorSetLayout, DescriptorSetLayoutBinding, DescriptorType,
|
||||
};
|
||||
use vulkano::descriptor_set::{DescriptorSet, WriteDescriptorSet};
|
||||
use vulkano::memory::allocator::{AllocationCreateInfo, MemoryTypeFilter, StandardMemoryAllocator};
|
||||
use vulkano::shader::ShaderStages;
|
||||
use vulkano::{Validated, VulkanError};
|
||||
|
||||
use crate::core::render::primitives::AsBindableDescriptorSet;
|
||||
|
||||
#[derive(BufferContents, Clone, Copy)]
|
||||
#[repr(C)]
|
||||
pub struct Mvp {
|
||||
pub model: [[f32; 4]; 4],
|
||||
pub view: [[f32; 4]; 4],
|
||||
pub projection: [[f32; 4]; 4],
|
||||
}
|
||||
|
||||
impl Mvp {
|
||||
pub fn into_buffer(
|
||||
self,
|
||||
memory_allocator: &Arc<StandardMemoryAllocator>,
|
||||
) -> Result<Subbuffer<[Mvp]>, Validated<AllocateBufferError>> {
|
||||
Buffer::from_iter(
|
||||
memory_allocator.clone(),
|
||||
BufferCreateInfo {
|
||||
usage: BufferUsage::UNIFORM_BUFFER,
|
||||
..Default::default()
|
||||
},
|
||||
AllocationCreateInfo {
|
||||
memory_type_filter: MemoryTypeFilter::PREFER_DEVICE
|
||||
| MemoryTypeFilter::HOST_SEQUENTIAL_WRITE,
|
||||
..Default::default()
|
||||
},
|
||||
[self],
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
impl AsBindableDescriptorSet<Subbuffer<[Mvp]>> for Mvp {
|
||||
fn as_descriptor_set_layout_bindings() -> BTreeMap<u32, DescriptorSetLayoutBinding> {
|
||||
BTreeMap::<u32, DescriptorSetLayoutBinding>::from_iter([(
|
||||
0,
|
||||
DescriptorSetLayoutBinding {
|
||||
stages: ShaderStages::VERTEX,
|
||||
..DescriptorSetLayoutBinding::descriptor_type(DescriptorType::UniformBuffer)
|
||||
},
|
||||
)])
|
||||
}
|
||||
|
||||
fn as_descriptor_set(
|
||||
descriptor_set_allocator: &Arc<StandardDescriptorSetAllocator>,
|
||||
layout: &Arc<DescriptorSetLayout>,
|
||||
data: &Subbuffer<[Mvp]>,
|
||||
) -> Result<Arc<DescriptorSet>, Validated<VulkanError>> {
|
||||
DescriptorSet::new(
|
||||
descriptor_set_allocator.clone(),
|
||||
layout.clone(),
|
||||
[WriteDescriptorSet::buffer(0, data.clone())],
|
||||
[],
|
||||
)
|
||||
}
|
||||
}
|
|
@ -1,81 +0,0 @@
|
|||
use std::sync::Arc;
|
||||
|
||||
use glam::{Mat4, Quat, Vec3};
|
||||
use vulkano::{
|
||||
Validated,
|
||||
buffer::{
|
||||
AllocateBufferError, Buffer, BufferContents, BufferCreateInfo, BufferUsage, Subbuffer,
|
||||
},
|
||||
memory::allocator::{AllocationCreateInfo, MemoryTypeFilter, StandardMemoryAllocator},
|
||||
pipeline::graphics::vertex_input::Vertex,
|
||||
};
|
||||
|
||||
pub struct Transform {
|
||||
pub position: Vec3,
|
||||
pub rotation: Quat,
|
||||
pub scale: Vec3,
|
||||
}
|
||||
|
||||
#[derive(BufferContents, Vertex)]
|
||||
#[repr(C)]
|
||||
pub struct TransformRaw {
|
||||
#[format(R32G32B32A32_SFLOAT)]
|
||||
pub model: [[f32; 4]; 4],
|
||||
}
|
||||
|
||||
impl Default for Transform {
|
||||
fn default() -> Self {
|
||||
Self {
|
||||
position: Vec3::default(),
|
||||
rotation: Quat::default(),
|
||||
scale: Vec3::ONE,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl Transform {
|
||||
pub fn rotate(&mut self, rotation: Quat) {
|
||||
self.rotation *= rotation;
|
||||
}
|
||||
|
||||
pub fn translate(&mut self, translation: Vec3) {
|
||||
self.position += translation;
|
||||
}
|
||||
|
||||
pub fn scale(&mut self, scale: Vec3) {
|
||||
self.scale *= scale;
|
||||
}
|
||||
|
||||
pub fn to_raw_tranform(&self) -> TransformRaw {
|
||||
TransformRaw {
|
||||
model: (Mat4::from_translation(self.position)
|
||||
* Mat4::from_quat(self.rotation)
|
||||
* Mat4::from_scale(self.scale))
|
||||
.to_cols_array_2d(),
|
||||
}
|
||||
}
|
||||
|
||||
pub fn create_buffer(
|
||||
memory_allocator: &Arc<StandardMemoryAllocator>,
|
||||
transforms: &[Transform],
|
||||
) -> Result<Subbuffer<[TransformRaw]>, Validated<AllocateBufferError>> {
|
||||
let transform_raws: Vec<TransformRaw> =
|
||||
transforms.iter().map(|t| t.to_raw_tranform()).collect();
|
||||
|
||||
let buffer = Buffer::from_iter(
|
||||
memory_allocator.clone(),
|
||||
BufferCreateInfo {
|
||||
usage: BufferUsage::VERTEX_BUFFER,
|
||||
..Default::default()
|
||||
},
|
||||
AllocationCreateInfo {
|
||||
memory_type_filter: MemoryTypeFilter::PREFER_DEVICE
|
||||
| MemoryTypeFilter::HOST_SEQUENTIAL_WRITE,
|
||||
..Default::default()
|
||||
},
|
||||
transform_raws,
|
||||
)?;
|
||||
|
||||
Ok(buffer)
|
||||
}
|
||||
}
|
|
@ -1,22 +0,0 @@
|
|||
use vulkano::buffer::BufferContents;
|
||||
use vulkano::pipeline::graphics::vertex_input::Vertex;
|
||||
|
||||
#[derive(BufferContents, Vertex)]
|
||||
#[repr(C)]
|
||||
pub struct Vertex2D {
|
||||
#[format(R32G32_SFLOAT)]
|
||||
pub position: [f32; 2],
|
||||
|
||||
#[format(R32G32_SFLOAT)]
|
||||
pub uv: [f32; 2],
|
||||
}
|
||||
|
||||
#[derive(BufferContents, Vertex)]
|
||||
#[repr(C)]
|
||||
pub struct Vertex3D {
|
||||
#[format(R32G32B32_SFLOAT)]
|
||||
pub position: [f32; 3],
|
||||
|
||||
#[format(R32G32_SFLOAT)]
|
||||
pub uv: [f32; 2],
|
||||
}
|
|
@ -1,113 +0,0 @@
|
|||
use std::sync::Arc;
|
||||
use vulkano::{
|
||||
command_buffer::{AutoCommandBufferBuilder, RenderingAttachmentInfo, RenderingInfo},
|
||||
image::view::ImageView,
|
||||
pipeline::graphics::viewport::Viewport,
|
||||
render_pass::{AttachmentLoadOp, AttachmentStoreOp},
|
||||
};
|
||||
|
||||
/// Types de render passes disponibles
|
||||
#[derive(Debug, Clone)]
|
||||
pub enum RenderPassType {
|
||||
Standard,
|
||||
ShadowMap,
|
||||
PostProcess,
|
||||
}
|
||||
|
||||
/// Configuration pour un render pass
|
||||
#[derive(Debug, Clone)]
|
||||
pub struct RenderPassConfig {
|
||||
pub pass_type: RenderPassType,
|
||||
pub clear_color: Option<[f32; 4]>,
|
||||
pub clear_depth: Option<f32>,
|
||||
pub load_op: AttachmentLoadOp,
|
||||
pub store_op: AttachmentStoreOp,
|
||||
}
|
||||
|
||||
impl Default for RenderPassConfig {
|
||||
fn default() -> Self {
|
||||
Self {
|
||||
pass_type: RenderPassType::Standard,
|
||||
clear_color: Some([0.0, 0.0, 0.0, 1.0]),
|
||||
clear_depth: Some(1.0),
|
||||
load_op: AttachmentLoadOp::Clear,
|
||||
store_op: AttachmentStoreOp::Store,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// Gestionnaire de render passes réutilisable
|
||||
pub struct RenderPassManager;
|
||||
|
||||
impl RenderPassManager {
|
||||
/// Commence un render pass standard avec les paramètres donnés
|
||||
pub fn begin_standard_rendering(
|
||||
builder: &mut AutoCommandBufferBuilder<vulkano::command_buffer::PrimaryAutoCommandBuffer>,
|
||||
config: &RenderPassConfig,
|
||||
color_attachment: Arc<ImageView>,
|
||||
depth_attachment: Option<Arc<ImageView>>,
|
||||
window_size: [f32; 2],
|
||||
) -> Result<(), Box<dyn std::error::Error>> {
|
||||
let viewport = Viewport {
|
||||
offset: [0.0, 0.0],
|
||||
extent: window_size,
|
||||
depth_range: 0.0..=1.0,
|
||||
};
|
||||
|
||||
let mut rendering_info = RenderingInfo {
|
||||
color_attachments: vec![Some(RenderingAttachmentInfo {
|
||||
load_op: config.load_op,
|
||||
store_op: config.store_op,
|
||||
clear_value: config.clear_color.map(|c| c.into()),
|
||||
..RenderingAttachmentInfo::image_view(color_attachment)
|
||||
})],
|
||||
depth_attachment: None,
|
||||
..Default::default()
|
||||
};
|
||||
|
||||
if let Some(depth_view) = depth_attachment {
|
||||
rendering_info.depth_attachment = Some(RenderingAttachmentInfo {
|
||||
load_op: AttachmentLoadOp::Clear,
|
||||
store_op: AttachmentStoreOp::DontCare,
|
||||
clear_value: config.clear_depth.map(|d| [d].into()),
|
||||
..RenderingAttachmentInfo::image_view(depth_view)
|
||||
});
|
||||
}
|
||||
|
||||
builder
|
||||
.begin_rendering(rendering_info)?
|
||||
.set_viewport(0, [viewport].into_iter().collect())?;
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
/// Termine le render pass actuel
|
||||
pub fn end_rendering(
|
||||
builder: &mut AutoCommandBufferBuilder<vulkano::command_buffer::PrimaryAutoCommandBuffer>,
|
||||
) -> Result<(), Box<dyn std::error::Error>> {
|
||||
builder.end_rendering()?;
|
||||
Ok(())
|
||||
}
|
||||
|
||||
/// Crée une configuration pour un render pass shadow map
|
||||
pub fn shadow_map_config() -> RenderPassConfig {
|
||||
RenderPassConfig {
|
||||
pass_type: RenderPassType::ShadowMap,
|
||||
clear_color: None,
|
||||
clear_depth: Some(1.0),
|
||||
load_op: AttachmentLoadOp::Clear,
|
||||
store_op: AttachmentStoreOp::Store,
|
||||
}
|
||||
}
|
||||
|
||||
/// Crée une configuration pour un render pass de post-processing
|
||||
pub fn post_process_config() -> RenderPassConfig {
|
||||
RenderPassConfig {
|
||||
pass_type: RenderPassType::PostProcess,
|
||||
clear_color: Some([0.0, 0.0, 0.0, 1.0]),
|
||||
clear_depth: None,
|
||||
load_op: AttachmentLoadOp::Load,
|
||||
store_op: AttachmentStoreOp::Store,
|
||||
}
|
||||
}
|
||||
}
|
|
@ -1,168 +0,0 @@
|
|||
use std::{collections::BTreeMap, error::Error, sync::Arc};
|
||||
|
||||
use image::DynamicImage;
|
||||
use vulkano::{
|
||||
Validated, VulkanError,
|
||||
buffer::{Buffer, BufferCreateInfo, BufferUsage},
|
||||
command_buffer::{AutoCommandBufferBuilder, CopyBufferToImageInfo, PrimaryAutoCommandBuffer},
|
||||
descriptor_set::{
|
||||
DescriptorSet, WriteDescriptorSet,
|
||||
allocator::StandardDescriptorSetAllocator,
|
||||
layout::{DescriptorSetLayout, DescriptorSetLayoutBinding, DescriptorType},
|
||||
},
|
||||
device::Device,
|
||||
format::Format,
|
||||
image::{
|
||||
Image, ImageCreateInfo, ImageType, ImageUsage,
|
||||
sampler::{Filter, Sampler, SamplerAddressMode, SamplerCreateInfo},
|
||||
view::ImageView,
|
||||
},
|
||||
memory::allocator::{AllocationCreateInfo, MemoryTypeFilter, StandardMemoryAllocator},
|
||||
shader::ShaderStages,
|
||||
};
|
||||
|
||||
use crate::core::render::primitives::AsBindableDescriptorSet;
|
||||
|
||||
pub struct Texture {
|
||||
texture: Arc<ImageView>,
|
||||
sampler: Arc<Sampler>,
|
||||
}
|
||||
|
||||
impl Texture {
|
||||
fn new(texture: Arc<ImageView>, sampler: Arc<Sampler>) -> Self {
|
||||
Self { texture, sampler }
|
||||
}
|
||||
|
||||
pub fn from_file(
|
||||
device: &Arc<Device>,
|
||||
memory_allocator: &Arc<StandardMemoryAllocator>,
|
||||
builder: &mut AutoCommandBufferBuilder<PrimaryAutoCommandBuffer>,
|
||||
path: &str,
|
||||
) -> Result<Self, Box<dyn Error>> {
|
||||
let _span = tracing::info_span!("texture_load_from_file", path = path);
|
||||
|
||||
let bytes = std::fs::read(path)?;
|
||||
Self::from_bytes(device, memory_allocator, builder, &bytes)
|
||||
}
|
||||
|
||||
pub fn from_bytes(
|
||||
device: &Arc<Device>,
|
||||
memory_allocator: &Arc<StandardMemoryAllocator>,
|
||||
builder: &mut AutoCommandBufferBuilder<PrimaryAutoCommandBuffer>,
|
||||
bytes: &[u8],
|
||||
) -> Result<Self, Box<dyn Error>> {
|
||||
let image = image::load_from_memory(bytes)?;
|
||||
Self::from_dynamic_image(device, memory_allocator, builder, image)
|
||||
}
|
||||
|
||||
pub fn from_dynamic_image(
|
||||
device: &Arc<Device>,
|
||||
memory_allocator: &Arc<StandardMemoryAllocator>,
|
||||
builder: &mut AutoCommandBufferBuilder<PrimaryAutoCommandBuffer>,
|
||||
image: DynamicImage,
|
||||
) -> Result<Self, Box<dyn Error>> {
|
||||
let _span = tracing::info_span!("texture_from_dynamic_image");
|
||||
|
||||
let image_data = image.to_rgba8();
|
||||
let image_dimensions = image_data.dimensions();
|
||||
let image_data = image_data.into_raw();
|
||||
|
||||
let upload_buffer = Buffer::new_slice(
|
||||
memory_allocator.clone(),
|
||||
BufferCreateInfo {
|
||||
usage: BufferUsage::TRANSFER_SRC,
|
||||
..Default::default()
|
||||
},
|
||||
AllocationCreateInfo {
|
||||
memory_type_filter: MemoryTypeFilter::PREFER_HOST
|
||||
| MemoryTypeFilter::HOST_SEQUENTIAL_WRITE,
|
||||
..Default::default()
|
||||
},
|
||||
image_data.len() as u64,
|
||||
)?;
|
||||
|
||||
{
|
||||
let buffer_data = &mut *upload_buffer.write()?;
|
||||
buffer_data.copy_from_slice(&image_data);
|
||||
}
|
||||
|
||||
let image = Image::new(
|
||||
memory_allocator.clone(),
|
||||
ImageCreateInfo {
|
||||
image_type: ImageType::Dim2d,
|
||||
format: Format::R8G8B8A8_SRGB,
|
||||
extent: [image_dimensions.0, image_dimensions.1, 1],
|
||||
array_layers: 1,
|
||||
usage: ImageUsage::TRANSFER_DST | ImageUsage::SAMPLED,
|
||||
..Default::default()
|
||||
},
|
||||
AllocationCreateInfo::default(),
|
||||
)?;
|
||||
|
||||
builder.copy_buffer_to_image(CopyBufferToImageInfo::buffer_image(
|
||||
upload_buffer,
|
||||
image.clone(),
|
||||
))?;
|
||||
|
||||
let sampler = Sampler::new(
|
||||
device.clone(),
|
||||
SamplerCreateInfo {
|
||||
mag_filter: Filter::Linear,
|
||||
min_filter: Filter::Linear,
|
||||
address_mode: [SamplerAddressMode::Repeat; 3],
|
||||
..Default::default()
|
||||
},
|
||||
)?;
|
||||
|
||||
let image_view = ImageView::new_default(image)?;
|
||||
|
||||
tracing::trace!("Texture loaded with dimensions {:?}", image_dimensions);
|
||||
|
||||
Ok(Self::new(image_view, sampler))
|
||||
}
|
||||
|
||||
pub fn get_texture(&self) -> &Arc<ImageView> {
|
||||
&self.texture
|
||||
}
|
||||
|
||||
pub fn get_sampler(&self) -> &Arc<Sampler> {
|
||||
&self.sampler
|
||||
}
|
||||
}
|
||||
|
||||
impl AsBindableDescriptorSet<Texture> for Texture {
|
||||
fn as_descriptor_set_layout_bindings() -> BTreeMap<u32, DescriptorSetLayoutBinding> {
|
||||
BTreeMap::<u32, DescriptorSetLayoutBinding>::from_iter([
|
||||
(
|
||||
0,
|
||||
DescriptorSetLayoutBinding {
|
||||
stages: ShaderStages::FRAGMENT,
|
||||
..DescriptorSetLayoutBinding::descriptor_type(DescriptorType::Sampler)
|
||||
},
|
||||
),
|
||||
(
|
||||
1,
|
||||
DescriptorSetLayoutBinding {
|
||||
stages: ShaderStages::FRAGMENT,
|
||||
..DescriptorSetLayoutBinding::descriptor_type(DescriptorType::SampledImage)
|
||||
},
|
||||
),
|
||||
])
|
||||
}
|
||||
|
||||
fn as_descriptor_set(
|
||||
descriptor_set_allocator: &Arc<StandardDescriptorSetAllocator>,
|
||||
layout: &Arc<DescriptorSetLayout>,
|
||||
data: &Texture,
|
||||
) -> Result<Arc<DescriptorSet>, Validated<VulkanError>> {
|
||||
DescriptorSet::new(
|
||||
descriptor_set_allocator.clone(),
|
||||
layout.clone(),
|
||||
[
|
||||
WriteDescriptorSet::sampler(0, data.sampler.clone()),
|
||||
WriteDescriptorSet::image_view(1, data.texture.clone()),
|
||||
],
|
||||
[],
|
||||
)
|
||||
}
|
||||
}
|
|
@ -1,45 +0,0 @@
|
|||
use std::sync::Arc;
|
||||
|
||||
use vulkano::{
|
||||
command_buffer::allocator::StandardCommandBufferAllocator,
|
||||
descriptor_set::allocator::StandardDescriptorSetAllocator,
|
||||
};
|
||||
use vulkano_util::context::VulkanoContext;
|
||||
|
||||
pub struct VulkanContext {
|
||||
vulkano_context: VulkanoContext,
|
||||
command_buffer_allocator: Arc<StandardCommandBufferAllocator>,
|
||||
descriptor_set_allocator: Arc<StandardDescriptorSetAllocator>,
|
||||
}
|
||||
|
||||
impl VulkanContext {
|
||||
pub fn new(vulkano_context: VulkanoContext) -> Self {
|
||||
let command_buffer_allocator = Arc::new(StandardCommandBufferAllocator::new(
|
||||
vulkano_context.device().clone(),
|
||||
Default::default(),
|
||||
));
|
||||
|
||||
let descriptor_set_allocator = Arc::new(StandardDescriptorSetAllocator::new(
|
||||
vulkano_context.device().clone(),
|
||||
Default::default(),
|
||||
));
|
||||
|
||||
Self {
|
||||
vulkano_context,
|
||||
command_buffer_allocator,
|
||||
descriptor_set_allocator,
|
||||
}
|
||||
}
|
||||
|
||||
pub fn vulkano_context(&self) -> &VulkanoContext {
|
||||
&self.vulkano_context
|
||||
}
|
||||
|
||||
pub fn command_buffer_allocator(&self) -> &Arc<StandardCommandBufferAllocator> {
|
||||
&self.command_buffer_allocator
|
||||
}
|
||||
|
||||
pub fn descriptor_set_allocator(&self) -> &Arc<StandardDescriptorSetAllocator> {
|
||||
&self.descriptor_set_allocator
|
||||
}
|
||||
}
|
|
@ -1,65 +0,0 @@
|
|||
use std::error::Error;
|
||||
|
||||
use crate::core::app::context::WindowContext;
|
||||
|
||||
use super::Scene;
|
||||
|
||||
pub struct SceneManager {
|
||||
scenes: Vec<Box<dyn Scene>>,
|
||||
current_scene_index: Option<usize>,
|
||||
}
|
||||
|
||||
impl SceneManager {
|
||||
pub fn new() -> Self {
|
||||
Self {
|
||||
scenes: Vec::new(),
|
||||
current_scene_index: None,
|
||||
}
|
||||
}
|
||||
|
||||
pub fn load_scene(&mut self, scene: Box<dyn Scene>) {
|
||||
self.scenes.push(scene);
|
||||
self.current_scene_index = Some(self.scenes.len() - 1);
|
||||
}
|
||||
|
||||
pub fn replace_current_scene(&mut self, scene: Box<dyn Scene>) {
|
||||
if let Some(index) = self.current_scene_index {
|
||||
if index < self.scenes.len() {
|
||||
self.scenes[index].unload();
|
||||
self.scenes[index] = scene;
|
||||
}
|
||||
} else {
|
||||
self.load_scene(scene);
|
||||
}
|
||||
}
|
||||
|
||||
pub fn current_scene(&self) -> Option<&Box<dyn Scene>> {
|
||||
if let Some(index) = self.current_scene_index {
|
||||
self.scenes.get(index)
|
||||
} else {
|
||||
None
|
||||
}
|
||||
}
|
||||
|
||||
pub fn current_scene_mut(&mut self) -> Option<&mut Box<dyn Scene>> {
|
||||
if let Some(index) = self.current_scene_index {
|
||||
self.scenes.get_mut(index)
|
||||
} else {
|
||||
None
|
||||
}
|
||||
}
|
||||
|
||||
pub fn load_scene_if_not_loaded(
|
||||
&mut self,
|
||||
app_context: &mut WindowContext,
|
||||
) -> Result<(), Box<dyn Error>> {
|
||||
if let Some(scene) = self.current_scene_mut() {
|
||||
if !scene.loaded() {
|
||||
scene.load(app_context)?;
|
||||
}
|
||||
} else {
|
||||
tracing::warn!("No scene found in SceneManager!");
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
}
|
|
@ -1,19 +0,0 @@
|
|||
use std::error::Error;
|
||||
|
||||
use vulkano::sync::GpuFuture;
|
||||
|
||||
use crate::core::app::context::WindowContext;
|
||||
|
||||
pub mod manager;
|
||||
|
||||
pub trait Scene {
|
||||
fn loaded(&self) -> bool;
|
||||
fn load(&mut self, app_context: &mut WindowContext) -> Result<(), Box<dyn Error>>;
|
||||
fn update(&mut self, app_context: &mut WindowContext) -> Result<(), Box<dyn Error>>;
|
||||
fn render(
|
||||
&mut self,
|
||||
acquire_future: Box<dyn GpuFuture>,
|
||||
app_context: &mut WindowContext,
|
||||
) -> Result<Box<dyn GpuFuture>, Box<dyn Error>>;
|
||||
fn unload(&mut self);
|
||||
}
|
|
@ -1,34 +0,0 @@
|
|||
pub struct Timer {
|
||||
start_time: std::time::Instant,
|
||||
last_time: std::time::Instant,
|
||||
current_time: std::time::Instant,
|
||||
delta_time: f32,
|
||||
}
|
||||
|
||||
impl Timer {
|
||||
pub fn new() -> Self {
|
||||
Self {
|
||||
start_time: std::time::Instant::now(),
|
||||
last_time: std::time::Instant::now(),
|
||||
current_time: std::time::Instant::now(),
|
||||
delta_time: 0.0,
|
||||
}
|
||||
}
|
||||
|
||||
pub fn update(&mut self) {
|
||||
self.current_time = std::time::Instant::now();
|
||||
self.delta_time = self
|
||||
.current_time
|
||||
.duration_since(self.last_time)
|
||||
.as_secs_f32();
|
||||
self.last_time = self.current_time;
|
||||
}
|
||||
|
||||
pub fn delta_time(&self) -> f32 {
|
||||
self.delta_time
|
||||
}
|
||||
|
||||
pub fn start_time(&self) -> std::time::Instant {
|
||||
self.start_time
|
||||
}
|
||||
}
|
|
@ -1 +0,0 @@
|
|||
pub mod square;
|
|
@ -1,231 +0,0 @@
|
|||
use std::{collections::BTreeMap, error::Error, sync::Arc};
|
||||
|
||||
use vulkano::{
|
||||
buffer::{Buffer, BufferCreateInfo, BufferUsage, Subbuffer},
|
||||
command_buffer::{AutoCommandBufferBuilder, PrimaryAutoCommandBuffer},
|
||||
descriptor_set::{
|
||||
DescriptorSet, WriteDescriptorSet,
|
||||
allocator::StandardDescriptorSetAllocator,
|
||||
layout::{DescriptorSetLayoutBinding, DescriptorSetLayoutCreateInfo, DescriptorType},
|
||||
},
|
||||
device::Device,
|
||||
format::Format,
|
||||
memory::allocator::{AllocationCreateInfo, MemoryTypeFilter, StandardMemoryAllocator},
|
||||
pipeline::{
|
||||
DynamicState, GraphicsPipeline, Pipeline, PipelineBindPoint, PipelineLayout,
|
||||
PipelineShaderStageCreateInfo,
|
||||
graphics::{
|
||||
GraphicsPipelineCreateInfo,
|
||||
color_blend::{ColorBlendAttachmentState, ColorBlendState},
|
||||
depth_stencil::{DepthState, DepthStencilState},
|
||||
input_assembly::InputAssemblyState,
|
||||
multisample::MultisampleState,
|
||||
rasterization::RasterizationState,
|
||||
subpass::PipelineRenderingCreateInfo,
|
||||
vertex_input::{Vertex, VertexDefinition},
|
||||
viewport::ViewportState,
|
||||
},
|
||||
layout::{PipelineDescriptorSetLayoutCreateInfo, PipelineLayoutCreateFlags},
|
||||
},
|
||||
shader::ShaderStages,
|
||||
};
|
||||
|
||||
use crate::core::render::{
|
||||
primitives::{AsBindableDescriptorSet, mvp::Mvp, transform::TransformRaw, vertex::Vertex3D},
|
||||
texture::Texture,
|
||||
};
|
||||
|
||||
const VERTICES: [Vertex3D; 4] = [
|
||||
Vertex3D {
|
||||
position: [-0.5, -0.5, 0.0],
|
||||
uv: [0.0, 0.0],
|
||||
},
|
||||
Vertex3D {
|
||||
position: [-0.5, 0.5, 0.0],
|
||||
uv: [0.0, 1.0],
|
||||
},
|
||||
Vertex3D {
|
||||
position: [0.5, -0.5, 0.0],
|
||||
uv: [1.0, 0.0],
|
||||
},
|
||||
Vertex3D {
|
||||
position: [0.5, 0.5, 0.0],
|
||||
uv: [1.0, 1.0],
|
||||
},
|
||||
];
|
||||
|
||||
const INDICES: [u32; 6] = [0, 2, 1, 2, 3, 1];
|
||||
|
||||
pub mod shaders {
|
||||
pub mod vs {
|
||||
vulkano_shaders::shader! {
|
||||
ty: "vertex",
|
||||
path: r"res/shaders/vertex.vert",
|
||||
generate_structs: false,
|
||||
}
|
||||
}
|
||||
|
||||
pub mod fs {
|
||||
vulkano_shaders::shader! {
|
||||
ty: "fragment",
|
||||
path: r"res/shaders/vertex.frag",
|
||||
generate_structs: false,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub struct Square {
|
||||
vertex_buffer: Subbuffer<[Vertex3D]>,
|
||||
index_buffer: Subbuffer<[u32]>,
|
||||
pipeline: Arc<GraphicsPipeline>,
|
||||
}
|
||||
|
||||
impl Square {
|
||||
pub fn new(
|
||||
device: &Arc<Device>,
|
||||
memory_allocator: &Arc<StandardMemoryAllocator>,
|
||||
swapchain_format: Format,
|
||||
depth_format: Format,
|
||||
) -> Result<Self, Box<dyn Error>> {
|
||||
let vertex_buffer = Buffer::from_iter(
|
||||
memory_allocator.clone(),
|
||||
BufferCreateInfo {
|
||||
usage: BufferUsage::VERTEX_BUFFER,
|
||||
..Default::default()
|
||||
},
|
||||
AllocationCreateInfo {
|
||||
memory_type_filter: MemoryTypeFilter::PREFER_DEVICE
|
||||
| MemoryTypeFilter::HOST_SEQUENTIAL_WRITE,
|
||||
..Default::default()
|
||||
},
|
||||
Vec::from_iter(VERTICES),
|
||||
)?;
|
||||
let index_buffer = Buffer::from_iter(
|
||||
memory_allocator.clone(),
|
||||
BufferCreateInfo {
|
||||
usage: BufferUsage::INDEX_BUFFER,
|
||||
..Default::default()
|
||||
},
|
||||
AllocationCreateInfo {
|
||||
memory_type_filter: MemoryTypeFilter::PREFER_DEVICE
|
||||
| MemoryTypeFilter::HOST_SEQUENTIAL_WRITE,
|
||||
..Default::default()
|
||||
},
|
||||
Vec::from_iter(INDICES),
|
||||
)?;
|
||||
|
||||
let vs = shaders::vs::load(device.clone())?
|
||||
.entry_point("main")
|
||||
.ok_or("Failed find main entry point of vertex shader".to_string())?;
|
||||
|
||||
let fs = shaders::fs::load(device.clone())?
|
||||
.entry_point("main")
|
||||
.ok_or("Failed find main entry point of fragment shader".to_string())?;
|
||||
|
||||
let vertex_input_state =
|
||||
[Vertex3D::per_vertex(), TransformRaw::per_instance()].definition(&vs)?;
|
||||
|
||||
let stages = [
|
||||
PipelineShaderStageCreateInfo::new(vs),
|
||||
PipelineShaderStageCreateInfo::new(fs),
|
||||
];
|
||||
|
||||
let vertex_bindings = Mvp::as_descriptor_set_layout_bindings();
|
||||
let texture_bindings = Texture::as_descriptor_set_layout_bindings();
|
||||
|
||||
let vertex_descriptor_set_layout = DescriptorSetLayoutCreateInfo {
|
||||
bindings: vertex_bindings,
|
||||
..Default::default()
|
||||
};
|
||||
|
||||
let fragment_descriptor_set_layout = DescriptorSetLayoutCreateInfo {
|
||||
bindings: texture_bindings,
|
||||
..Default::default()
|
||||
};
|
||||
|
||||
let create_info = PipelineDescriptorSetLayoutCreateInfo {
|
||||
set_layouts: vec![vertex_descriptor_set_layout, fragment_descriptor_set_layout],
|
||||
flags: PipelineLayoutCreateFlags::default(),
|
||||
push_constant_ranges: vec![],
|
||||
}
|
||||
.into_pipeline_layout_create_info(device.clone())?;
|
||||
|
||||
let layout = PipelineLayout::new(device.clone(), create_info)?;
|
||||
|
||||
let subpass = PipelineRenderingCreateInfo {
|
||||
color_attachment_formats: vec![Some(swapchain_format)],
|
||||
depth_attachment_format: Some(depth_format),
|
||||
..Default::default()
|
||||
};
|
||||
|
||||
let pipeline = GraphicsPipeline::new(
|
||||
device.clone(),
|
||||
None,
|
||||
GraphicsPipelineCreateInfo {
|
||||
stages: stages.into_iter().collect(),
|
||||
vertex_input_state: Some(vertex_input_state),
|
||||
input_assembly_state: Some(InputAssemblyState::default()),
|
||||
viewport_state: Some(ViewportState::default()),
|
||||
rasterization_state: Some(RasterizationState::default()),
|
||||
multisample_state: Some(MultisampleState::default()),
|
||||
color_blend_state: Some(ColorBlendState::with_attachment_states(
|
||||
subpass.color_attachment_formats.len() as u32,
|
||||
ColorBlendAttachmentState::default(),
|
||||
)),
|
||||
depth_stencil_state: Some(DepthStencilState {
|
||||
depth: Some(DepthState::simple()),
|
||||
..Default::default()
|
||||
}),
|
||||
dynamic_state: [DynamicState::Viewport].into_iter().collect(),
|
||||
subpass: Some(subpass.into()),
|
||||
..GraphicsPipelineCreateInfo::layout(layout)
|
||||
},
|
||||
)?;
|
||||
|
||||
Ok(Self {
|
||||
vertex_buffer,
|
||||
index_buffer,
|
||||
pipeline,
|
||||
})
|
||||
}
|
||||
|
||||
pub fn render(
|
||||
&self,
|
||||
command_buffer: &mut AutoCommandBufferBuilder<PrimaryAutoCommandBuffer>,
|
||||
descriptor_set_allocator: &Arc<StandardDescriptorSetAllocator>,
|
||||
mvp_uniform: &Subbuffer<[Mvp]>,
|
||||
transform_uniform: &Subbuffer<[TransformRaw]>,
|
||||
texture: &Texture,
|
||||
) -> Result<(), Box<dyn Error>> {
|
||||
let layouts = self.pipeline.layout().set_layouts();
|
||||
|
||||
let uniform_descriptor_set =
|
||||
Mvp::as_descriptor_set(descriptor_set_allocator, &layouts[0], mvp_uniform)?;
|
||||
|
||||
let texture_descriptor_set =
|
||||
Texture::as_descriptor_set(descriptor_set_allocator, &layouts[1], texture)?;
|
||||
|
||||
command_buffer.bind_pipeline_graphics(self.pipeline.clone())?;
|
||||
command_buffer.bind_descriptor_sets(
|
||||
PipelineBindPoint::Graphics,
|
||||
self.pipeline.layout().clone(),
|
||||
0,
|
||||
vec![uniform_descriptor_set, texture_descriptor_set],
|
||||
)?;
|
||||
command_buffer
|
||||
.bind_vertex_buffers(0, (self.vertex_buffer.clone(), transform_uniform.clone()))?;
|
||||
command_buffer.bind_index_buffer(self.index_buffer.clone())?;
|
||||
|
||||
unsafe {
|
||||
command_buffer.draw_indexed(
|
||||
INDICES.len() as u32,
|
||||
transform_uniform.len() as u32,
|
||||
0,
|
||||
0,
|
||||
0,
|
||||
)?;
|
||||
}
|
||||
|
||||
Ok(())
|
||||
}
|
||||
}
|
|
@ -1,2 +0,0 @@
|
|||
pub mod assets;
|
||||
pub mod scenes;
|
|
@ -1,285 +0,0 @@
|
|||
use std::error::Error;
|
||||
|
||||
use super::settings_scene::SettingsScene;
|
||||
use crate::core::app::DEPTH_IMAGE_ID;
|
||||
use crate::core::app::context::WindowContext;
|
||||
use crate::core::app::user_event::UserEvent;
|
||||
use crate::core::render::primitives::camera::Camera3D;
|
||||
use crate::core::render::primitives::transform::Transform;
|
||||
use crate::core::render::render_pass_manager::{RenderPassConfig, RenderPassManager};
|
||||
use crate::core::render::texture::Texture;
|
||||
use crate::core::scene::Scene;
|
||||
use crate::game::assets::square::Square;
|
||||
use egui_winit_vulkano::egui;
|
||||
use glam::EulerRot;
|
||||
use glam::Quat;
|
||||
use glam::Vec3;
|
||||
use vulkano::{
|
||||
command_buffer::{AutoCommandBufferBuilder, CommandBufferUsage, PrimaryCommandBufferAbstract},
|
||||
sync::GpuFuture,
|
||||
};
|
||||
use winit::window::CursorGrabMode;
|
||||
|
||||
pub struct MainSceneState {
|
||||
square: Square,
|
||||
instances: Vec<Transform>,
|
||||
camera: Camera3D,
|
||||
texture: Texture,
|
||||
speed: f32,
|
||||
}
|
||||
|
||||
#[derive(Default)]
|
||||
pub struct MainScene {
|
||||
state: Option<MainSceneState>,
|
||||
}
|
||||
|
||||
impl Scene for MainScene {
|
||||
fn loaded(&self) -> bool {
|
||||
self.state.is_some()
|
||||
}
|
||||
|
||||
fn load(&mut self, app_context: &mut WindowContext) -> Result<(), Box<dyn std::error::Error>> {
|
||||
let depth_image_view = app_context.with_renderer_mut(|renderer| {
|
||||
renderer.get_additional_image_view(DEPTH_IMAGE_ID).clone()
|
||||
});
|
||||
|
||||
let swapchain_image_view =
|
||||
app_context.with_renderer(|renderer| renderer.swapchain_image_view().clone());
|
||||
|
||||
let square = Square::new(
|
||||
&app_context.device,
|
||||
&app_context.memory_allocator,
|
||||
swapchain_image_view.format(),
|
||||
depth_image_view.format(),
|
||||
)?;
|
||||
|
||||
let num_instances = 100;
|
||||
let instance_size = 10.0;
|
||||
let instance_spacing = 10.0;
|
||||
let num_instances_per_row = (num_instances as f32 / instance_spacing).ceil() as u32;
|
||||
let instances: Vec<Transform> = (0..num_instances)
|
||||
.map(|i| Transform {
|
||||
position: Vec3::new(
|
||||
(i % num_instances_per_row) as f32 * (instance_spacing + instance_size),
|
||||
0.0,
|
||||
(i / num_instances_per_row) as f32 * (instance_spacing + instance_size),
|
||||
),
|
||||
rotation: Quat::from_euler(
|
||||
EulerRot::XYZ,
|
||||
0.0,
|
||||
rand::random_range(0.0..=360.0),
|
||||
0.0,
|
||||
),
|
||||
scale: Vec3::new(instance_size, instance_size, instance_size),
|
||||
})
|
||||
.collect();
|
||||
|
||||
let texture = {
|
||||
let mut uploads = AutoCommandBufferBuilder::primary(
|
||||
app_context.command_buffer_allocator.clone(),
|
||||
app_context.graphics_queue.queue_family_index(),
|
||||
CommandBufferUsage::OneTimeSubmit,
|
||||
)?;
|
||||
|
||||
let texture = Texture::from_file(
|
||||
&app_context.device,
|
||||
&app_context.memory_allocator,
|
||||
&mut uploads,
|
||||
"res/textures/wooden-crate.jpg",
|
||||
)?;
|
||||
|
||||
let _ = uploads
|
||||
.build()?
|
||||
.execute(app_context.graphics_queue.clone())?;
|
||||
|
||||
texture
|
||||
};
|
||||
|
||||
let camera = app_context.with_renderer(|renderer| {
|
||||
Camera3D::new(
|
||||
renderer.aspect_ratio(),
|
||||
std::f32::consts::FRAC_PI_2,
|
||||
0.01,
|
||||
1000.0,
|
||||
)
|
||||
});
|
||||
|
||||
self.state = Some(MainSceneState {
|
||||
square,
|
||||
instances,
|
||||
camera,
|
||||
texture,
|
||||
speed: 50.0,
|
||||
});
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn update(&mut self, app_context: &mut WindowContext) -> Result<(), Box<dyn Error>> {
|
||||
let state = self.state.as_mut().unwrap();
|
||||
app_context.with_input_manager(|input_manager| {
|
||||
app_context.with_timer(|timer| {
|
||||
state.camera.update(
|
||||
input_manager,
|
||||
timer,
|
||||
state.speed,
|
||||
10.0,
|
||||
app_context.get_aspect_ratio(),
|
||||
);
|
||||
});
|
||||
});
|
||||
|
||||
if app_context
|
||||
.with_input_manager(|input_manager| input_manager.get_virtual_input_state("mouse_left"))
|
||||
> 0.0
|
||||
{
|
||||
let _ = app_context
|
||||
.event_loop_proxy
|
||||
.send_event(UserEvent::CursorVisible(app_context.window_id, false));
|
||||
let _ = app_context
|
||||
.event_loop_proxy
|
||||
.send_event(UserEvent::CursorGrabMode(
|
||||
app_context.window_id,
|
||||
CursorGrabMode::Locked,
|
||||
));
|
||||
}
|
||||
|
||||
if app_context.with_input_manager(|input_manager| {
|
||||
input_manager.get_virtual_input_state("mouse_right")
|
||||
}) > 0.0
|
||||
{
|
||||
let _ = app_context
|
||||
.event_loop_proxy
|
||||
.send_event(UserEvent::CursorVisible(app_context.window_id, true));
|
||||
let _ = app_context
|
||||
.event_loop_proxy
|
||||
.send_event(UserEvent::CursorGrabMode(
|
||||
app_context.window_id,
|
||||
CursorGrabMode::None,
|
||||
));
|
||||
}
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn render(
|
||||
&mut self,
|
||||
before_future: Box<dyn GpuFuture>,
|
||||
app_context: &mut WindowContext,
|
||||
) -> Result<Box<dyn GpuFuture>, Box<dyn Error>> {
|
||||
let state = self.state.as_ref().ok_or("State not loaded")?;
|
||||
|
||||
let mut builder = AutoCommandBufferBuilder::primary(
|
||||
app_context.command_buffer_allocator.clone(),
|
||||
app_context.graphics_queue.queue_family_index(),
|
||||
CommandBufferUsage::OneTimeSubmit,
|
||||
)?;
|
||||
|
||||
{
|
||||
let swapchain_image_view =
|
||||
app_context.with_renderer(|renderer| renderer.swapchain_image_view().clone());
|
||||
let depth_image_view = app_context.with_renderer_mut(|renderer| {
|
||||
renderer.get_additional_image_view(DEPTH_IMAGE_ID).clone()
|
||||
});
|
||||
let config = RenderPassConfig::default();
|
||||
RenderPassManager::begin_standard_rendering(
|
||||
&mut builder,
|
||||
&config,
|
||||
swapchain_image_view,
|
||||
Some(depth_image_view),
|
||||
app_context.get_window_size(),
|
||||
)?;
|
||||
}
|
||||
|
||||
// Create camera uniform using the actual camera
|
||||
let camera_uniform = state.camera.create_buffer(&app_context.memory_allocator)?;
|
||||
|
||||
let transform_uniform =
|
||||
Transform::create_buffer(&app_context.memory_allocator, &state.instances)?;
|
||||
|
||||
state
|
||||
.square
|
||||
.render(
|
||||
&mut builder,
|
||||
&app_context.descriptor_set_allocator,
|
||||
&camera_uniform,
|
||||
&transform_uniform,
|
||||
&state.texture,
|
||||
)
|
||||
.unwrap();
|
||||
|
||||
RenderPassManager::end_rendering(&mut builder)?;
|
||||
|
||||
let command_buffer = builder.build()?;
|
||||
|
||||
let render_future =
|
||||
before_future.then_execute(app_context.graphics_queue.clone(), command_buffer)?;
|
||||
|
||||
let swapchain_image_view =
|
||||
app_context.with_renderer(|renderer| renderer.swapchain_image_view().clone());
|
||||
let input_manager_status =
|
||||
app_context.with_input_manager(|input_manager| format!("{:#?}", input_manager));
|
||||
let event_loop_proxy = app_context.event_loop_proxy.clone();
|
||||
let delta_time = app_context.get_delta_time();
|
||||
let window_id = app_context.window_id;
|
||||
let window_size = app_context.get_window_size();
|
||||
|
||||
let render_future = app_context.with_gui_mut(|gui| {
|
||||
gui.immediate_ui(|gui| {
|
||||
let ctx = gui.context();
|
||||
egui::TopBottomPanel::top("top_panel").show(&ctx, |ui| {
|
||||
ui.horizontal(|ui| {
|
||||
ui.heading("Vulkan Test - Moteur 3D");
|
||||
ui.with_layout(egui::Layout::right_to_left(egui::Align::Center), |ui| {
|
||||
if ui.button("Paramètres").clicked() {
|
||||
let _ = event_loop_proxy.send_event(UserEvent::ChangeScene(
|
||||
window_id,
|
||||
Box::new(SettingsScene::default()),
|
||||
));
|
||||
}
|
||||
if ui.button("Quitter").clicked() {
|
||||
let _ = event_loop_proxy.send_event(UserEvent::Exit(window_id));
|
||||
}
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
egui::SidePanel::left("side_panel").show(&ctx, |ui| {
|
||||
ui.heading("Informations");
|
||||
|
||||
ui.separator();
|
||||
|
||||
ui.label(format!("Résolution: {:?}", window_size));
|
||||
ui.label(format!("Delta Time: {:.2}ms", delta_time * 1000.0));
|
||||
|
||||
ui.separator();
|
||||
|
||||
ui.label("Position caméra:");
|
||||
let position = state.camera.get_position();
|
||||
ui.label(format!(" X: {:.2}", position[0]));
|
||||
ui.label(format!(" Y: {:.2}", position[1]));
|
||||
ui.label(format!(" Z: {:.2}", position[2]));
|
||||
|
||||
ui.separator();
|
||||
|
||||
ui.label("Rotation caméra:");
|
||||
let rotation = state.camera.get_rotation();
|
||||
ui.label(format!(" Yaw: {:.2}°", rotation.y.to_degrees()));
|
||||
ui.label(format!(" Pitch: {:.2}°", rotation.x.to_degrees()));
|
||||
|
||||
ui.separator();
|
||||
|
||||
ui.label(input_manager_status);
|
||||
});
|
||||
});
|
||||
|
||||
gui.draw_on_image(render_future, swapchain_image_view.clone())
|
||||
});
|
||||
|
||||
Ok(render_future)
|
||||
}
|
||||
|
||||
fn unload(&mut self) {
|
||||
self.state = None;
|
||||
}
|
||||
}
|
|
@ -1,2 +0,0 @@
|
|||
pub mod main_scene;
|
||||
pub mod settings_scene;
|
|
@ -1,136 +0,0 @@
|
|||
use std::error::Error;
|
||||
|
||||
use crate::core::app::DEPTH_IMAGE_ID;
|
||||
use crate::core::app::context::WindowContext;
|
||||
use crate::core::app::user_event::UserEvent;
|
||||
use crate::core::render::render_pass_manager::{RenderPassConfig, RenderPassManager};
|
||||
use crate::core::scene::Scene;
|
||||
use egui_winit_vulkano::egui;
|
||||
use vulkano::{
|
||||
command_buffer::AutoCommandBufferBuilder, command_buffer::CommandBufferUsage, sync::GpuFuture,
|
||||
};
|
||||
|
||||
use super::main_scene::MainScene;
|
||||
|
||||
pub struct SettingsSceneState {
|
||||
current_resolution: [f32; 2],
|
||||
available_resolutions: Vec<(u32, u32)>,
|
||||
}
|
||||
|
||||
#[derive(Default)]
|
||||
pub struct SettingsScene {
|
||||
state: Option<SettingsSceneState>,
|
||||
}
|
||||
|
||||
impl Scene for SettingsScene {
|
||||
fn loaded(&self) -> bool {
|
||||
self.state.is_some()
|
||||
}
|
||||
|
||||
fn load(&mut self, app_context: &mut WindowContext) -> Result<(), Box<dyn Error>> {
|
||||
let current_resolution = app_context.get_window_size();
|
||||
let available_resolutions = app_context.get_available_resolutions();
|
||||
|
||||
self.state = Some(SettingsSceneState {
|
||||
current_resolution,
|
||||
available_resolutions,
|
||||
});
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn update(&mut self, _app_context: &mut WindowContext) -> Result<(), Box<dyn Error>> {
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn render(
|
||||
&mut self,
|
||||
before_future: Box<dyn GpuFuture>,
|
||||
app_context: &mut WindowContext,
|
||||
) -> Result<Box<dyn GpuFuture>, Box<dyn Error>> {
|
||||
let state = self.state.as_ref().ok_or("State not found")?;
|
||||
|
||||
let mut builder = AutoCommandBufferBuilder::primary(
|
||||
app_context.command_buffer_allocator.clone(),
|
||||
app_context.graphics_queue.queue_family_index(),
|
||||
CommandBufferUsage::OneTimeSubmit,
|
||||
)?;
|
||||
|
||||
// Utiliser le RenderPassManager
|
||||
{
|
||||
let swapchain_image_view =
|
||||
app_context.with_renderer(|renderer| renderer.swapchain_image_view().clone());
|
||||
let depth_stencil_image_view = app_context.with_renderer_mut(|renderer| {
|
||||
renderer.get_additional_image_view(DEPTH_IMAGE_ID).clone()
|
||||
});
|
||||
|
||||
let config = RenderPassConfig::default();
|
||||
RenderPassManager::begin_standard_rendering(
|
||||
&mut builder,
|
||||
&config,
|
||||
swapchain_image_view,
|
||||
Some(depth_stencil_image_view),
|
||||
app_context.get_window_size(),
|
||||
)?;
|
||||
}
|
||||
|
||||
// Pas de géométrie dans cette scène - juste un écran de paramètres
|
||||
RenderPassManager::end_rendering(&mut builder)?;
|
||||
|
||||
let command_buffer = builder.build()?;
|
||||
|
||||
let render_future =
|
||||
before_future.then_execute(app_context.graphics_queue.clone(), command_buffer)?;
|
||||
|
||||
let swapchain_image_view =
|
||||
app_context.with_renderer(|renderer| renderer.swapchain_image_view().clone());
|
||||
|
||||
let event_loop_proxy = app_context.event_loop_proxy.clone();
|
||||
let window_id = app_context.window_id;
|
||||
|
||||
let render_future = app_context.with_gui_mut(|gui| {
|
||||
gui.immediate_ui(|gui| {
|
||||
let ctx = gui.context();
|
||||
|
||||
egui::CentralPanel::default().show(&ctx, |ui| {
|
||||
ui.heading("Paramètres");
|
||||
|
||||
ui.separator();
|
||||
|
||||
ui.label(format!(
|
||||
"Résolution actuelle: {:?}",
|
||||
state.current_resolution
|
||||
));
|
||||
|
||||
ui.separator();
|
||||
ui.label("Changer la résolution:");
|
||||
|
||||
for &(width, height) in &state.available_resolutions {
|
||||
if ui.button(format!("{}x{}", width, height)).clicked() {
|
||||
let _ = event_loop_proxy.send_event(UserEvent::ChangeResolution(
|
||||
window_id,
|
||||
width as f32,
|
||||
height as f32,
|
||||
));
|
||||
}
|
||||
}
|
||||
|
||||
ui.separator();
|
||||
|
||||
if ui.button("Retour au jeu").clicked() {
|
||||
let _ = event_loop_proxy.send_event(UserEvent::ChangeScene(
|
||||
window_id,
|
||||
Box::new(MainScene::default()),
|
||||
));
|
||||
}
|
||||
});
|
||||
});
|
||||
|
||||
gui.draw_on_image(render_future, swapchain_image_view.clone())
|
||||
});
|
||||
|
||||
Ok(render_future)
|
||||
}
|
||||
|
||||
fn unload(&mut self) {}
|
||||
}
|
112
src/main.rs
112
src/main.rs
|
@ -1,108 +1,14 @@
|
|||
use core::input::{AxisDirection, InputManager, VirtualBinding};
|
||||
use std::collections::HashMap;
|
||||
use winit::event_loop::EventLoop;
|
||||
|
||||
use vulkano::device::{DeviceExtensions, DeviceFeatures};
|
||||
use vulkano_util::context::{VulkanoConfig, VulkanoContext};
|
||||
use winit::{
|
||||
event::MouseButton,
|
||||
event_loop::{ControlFlow, EventLoop},
|
||||
keyboard::{KeyCode, PhysicalKey},
|
||||
};
|
||||
mod renderer;
|
||||
mod vulkan;
|
||||
|
||||
mod core;
|
||||
mod game;
|
||||
use renderer::app::App;
|
||||
use vulkan::context::VulkanContext;
|
||||
|
||||
fn main() {
|
||||
use tracing_subscriber::layer::SubscriberExt;
|
||||
use tracing_subscriber::util::SubscriberInitExt;
|
||||
|
||||
tracing_subscriber::registry()
|
||||
.with(
|
||||
tracing_subscriber::fmt::layer()
|
||||
.with_target(true)
|
||||
.with_thread_ids(true)
|
||||
.with_file(true)
|
||||
.with_line_number(true),
|
||||
)
|
||||
.with(tracing_tracy::TracyLayer::default())
|
||||
.with(
|
||||
tracing_subscriber::EnvFilter::try_from_default_env()
|
||||
.unwrap_or_else(|_| tracing_subscriber::EnvFilter::new("info")),
|
||||
)
|
||||
.init();
|
||||
|
||||
let input_manager = InputManager::new(HashMap::from([
|
||||
(
|
||||
"move_forward".to_string(),
|
||||
vec![
|
||||
VirtualBinding::Keyboard(PhysicalKey::Code(KeyCode::KeyW), AxisDirection::Normal),
|
||||
VirtualBinding::Keyboard(PhysicalKey::Code(KeyCode::KeyS), AxisDirection::Invert),
|
||||
],
|
||||
),
|
||||
(
|
||||
"move_right".to_string(),
|
||||
vec![
|
||||
VirtualBinding::Keyboard(PhysicalKey::Code(KeyCode::KeyD), AxisDirection::Normal),
|
||||
VirtualBinding::Keyboard(PhysicalKey::Code(KeyCode::KeyA), AxisDirection::Invert),
|
||||
],
|
||||
),
|
||||
(
|
||||
"mouse_x".to_string(),
|
||||
vec![VirtualBinding::MouseX(AxisDirection::Normal)],
|
||||
),
|
||||
(
|
||||
"mouse_y".to_string(),
|
||||
vec![VirtualBinding::MouseY(AxisDirection::Normal)],
|
||||
),
|
||||
(
|
||||
"mouse_wheel".to_string(),
|
||||
vec![VirtualBinding::MouseWheelY(AxisDirection::Normal)],
|
||||
),
|
||||
(
|
||||
"mouse_left".to_string(),
|
||||
vec![VirtualBinding::MouseButton(
|
||||
MouseButton::Left,
|
||||
AxisDirection::Normal,
|
||||
)],
|
||||
),
|
||||
(
|
||||
"mouse_right".to_string(),
|
||||
vec![VirtualBinding::MouseButton(
|
||||
MouseButton::Right,
|
||||
AxisDirection::Normal,
|
||||
)],
|
||||
),
|
||||
]));
|
||||
|
||||
let device_extensions = DeviceExtensions {
|
||||
khr_swapchain: true,
|
||||
..Default::default()
|
||||
};
|
||||
|
||||
let device_features = DeviceFeatures {
|
||||
dynamic_rendering: true,
|
||||
..Default::default()
|
||||
};
|
||||
|
||||
let vulkano_config = VulkanoConfig {
|
||||
print_device_name: true,
|
||||
device_extensions,
|
||||
device_features,
|
||||
..Default::default()
|
||||
};
|
||||
|
||||
let vulkano_context = VulkanoContext::new(vulkano_config);
|
||||
|
||||
let event_loop = EventLoop::with_user_event().build().unwrap();
|
||||
event_loop.set_control_flow(ControlFlow::Poll);
|
||||
let proxy = event_loop.create_proxy();
|
||||
|
||||
let mut app = core::app::App::new(vulkano_context, input_manager, proxy);
|
||||
|
||||
match event_loop.run_app(&mut app) {
|
||||
Ok(_) => {}
|
||||
Err(e) => {
|
||||
tracing::error!("Error running old app: {e}");
|
||||
}
|
||||
}
|
||||
let event_loop = EventLoop::new().unwrap();
|
||||
let vulkan_context = VulkanContext::new(&event_loop).unwrap();
|
||||
let mut app = App::new(vulkan_context);
|
||||
event_loop.run_app(&mut app).unwrap();
|
||||
}
|
||||
|
|
262
src/renderer/app.rs
Normal file
262
src/renderer/app.rs
Normal file
|
@ -0,0 +1,262 @@
|
|||
use crate::renderer::components::{Entity, Material, Mesh, Transform};
|
||||
use crate::vulkan::context::VulkanContext;
|
||||
use crate::vulkan::pipeline::{Pipeline, triangle::TrianglePipeline};
|
||||
use crate::vulkan::renderer::VulkanRenderer;
|
||||
use crate::vulkan::resources::vertex::{MVPData, Vertex2D};
|
||||
use std::error::Error;
|
||||
use std::sync::Arc;
|
||||
use vulkano::VulkanError;
|
||||
use vulkano::buffer::{Buffer, BufferCreateInfo, BufferUsage};
|
||||
use vulkano::command_buffer::{
|
||||
AutoCommandBufferBuilder, CommandBufferUsage, PrimaryAutoCommandBuffer,
|
||||
RenderingAttachmentInfo, RenderingInfo,
|
||||
};
|
||||
use vulkano::descriptor_set::{DescriptorSet, WriteDescriptorSet};
|
||||
use vulkano::memory::allocator::{AllocationCreateInfo, MemoryTypeFilter};
|
||||
use vulkano::pipeline::{Pipeline as VulkanPipeline, PipelineBindPoint};
|
||||
use vulkano::render_pass::{AttachmentLoadOp, AttachmentStoreOp};
|
||||
use vulkano::swapchain::Surface;
|
||||
use winit::application::ApplicationHandler;
|
||||
use winit::event::WindowEvent;
|
||||
use winit::event_loop::ActiveEventLoop;
|
||||
use winit::window::WindowId;
|
||||
|
||||
pub struct App {
|
||||
pub vulkan_context: VulkanContext,
|
||||
pub renderer: Option<VulkanRenderer>,
|
||||
pub entities: Vec<Entity>,
|
||||
}
|
||||
|
||||
impl App {
|
||||
pub fn new(vulkan_context: VulkanContext) -> Self {
|
||||
Self {
|
||||
vulkan_context,
|
||||
renderer: None,
|
||||
entities: Vec::new(),
|
||||
}
|
||||
}
|
||||
|
||||
pub fn setup_test_entities(&mut self) -> Result<(), Box<dyn Error>> {
|
||||
// Créer un pipeline de test
|
||||
let pipeline = TrianglePipeline::new(&self.vulkan_context.device);
|
||||
|
||||
// Créer un buffer de vertex pour un triangle
|
||||
let vertices = [
|
||||
Vertex2D {
|
||||
position: [-0.5, -0.5],
|
||||
color: [1.0, 0.0, 0.0], // Rouge
|
||||
},
|
||||
Vertex2D {
|
||||
position: [0.5, -0.5],
|
||||
color: [0.0, 1.0, 0.0], // Vert
|
||||
},
|
||||
Vertex2D {
|
||||
position: [0.0, 0.5],
|
||||
color: [0.0, 0.0, 1.0], // Bleu
|
||||
},
|
||||
];
|
||||
|
||||
let vertex_buffer =
|
||||
Vertex2D::create_buffer(vertices.to_vec(), &self.vulkan_context.memory_allocator)
|
||||
.unwrap();
|
||||
|
||||
// Créer un buffer uniform pour les matrices MVP
|
||||
let mvp_data = MVPData {
|
||||
world: [
|
||||
[1.0, 0.0, 0.0, 0.0],
|
||||
[0.0, 1.0, 0.0, 0.0],
|
||||
[0.0, 0.0, 1.0, 0.0],
|
||||
[0.0, 0.0, 0.0, 1.0],
|
||||
],
|
||||
view: [
|
||||
[1.0, 0.0, 0.0, 0.0],
|
||||
[0.0, 1.0, 0.0, 0.0],
|
||||
[0.0, 0.0, 1.0, 0.0],
|
||||
[0.0, 0.0, 0.0, 1.0],
|
||||
],
|
||||
projection: [
|
||||
[1.0, 0.0, 0.0, 0.0],
|
||||
[0.0, 1.0, 0.0, 0.0],
|
||||
[0.0, 0.0, 1.0, 0.0],
|
||||
[0.0, 0.0, 0.0, 1.0],
|
||||
],
|
||||
};
|
||||
|
||||
let uniform_buffer = Buffer::from_data(
|
||||
self.vulkan_context.memory_allocator.clone(),
|
||||
BufferCreateInfo {
|
||||
usage: BufferUsage::UNIFORM_BUFFER,
|
||||
..Default::default()
|
||||
},
|
||||
AllocationCreateInfo {
|
||||
memory_type_filter: MemoryTypeFilter::PREFER_DEVICE
|
||||
| MemoryTypeFilter::HOST_SEQUENTIAL_WRITE,
|
||||
..Default::default()
|
||||
},
|
||||
mvp_data,
|
||||
)
|
||||
.unwrap();
|
||||
|
||||
// Créer un descriptor set de test
|
||||
let descriptor_set = DescriptorSet::new(
|
||||
self.vulkan_context.descriptor_set_allocator.clone(),
|
||||
pipeline
|
||||
.get_pipeline()
|
||||
.layout()
|
||||
.set_layouts()
|
||||
.get(0)
|
||||
.unwrap()
|
||||
.clone(),
|
||||
[WriteDescriptorSet::buffer(0, uniform_buffer)],
|
||||
[],
|
||||
)?;
|
||||
|
||||
let material = Material {
|
||||
pipeline: pipeline.get_pipeline().clone(),
|
||||
descriptor_set,
|
||||
};
|
||||
|
||||
// Créer quelques entités de test
|
||||
let mut entities = Vec::new();
|
||||
for i in 0..3 {
|
||||
entities.push(Entity {
|
||||
mesh: Mesh {
|
||||
vertex_buffer: vertex_buffer.clone(),
|
||||
vertex_count: 3,
|
||||
instance_count: 1,
|
||||
},
|
||||
material: material.clone(),
|
||||
transform: Transform {
|
||||
position: [i as f32 * 0.5 - 0.5, 0.0, 0.0],
|
||||
rotation: [0.0, 0.0, 0.0],
|
||||
scale: [1.0, 1.0, 1.0],
|
||||
},
|
||||
});
|
||||
}
|
||||
self.entities = entities;
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
pub fn render(
|
||||
&self,
|
||||
command_buffer: &mut AutoCommandBufferBuilder<PrimaryAutoCommandBuffer>,
|
||||
) -> Result<(), Box<dyn Error>> {
|
||||
for entity in &self.entities {
|
||||
command_buffer
|
||||
.bind_pipeline_graphics(entity.material.pipeline.clone())
|
||||
.unwrap()
|
||||
.bind_descriptor_sets(
|
||||
PipelineBindPoint::Graphics,
|
||||
entity.material.pipeline.layout().clone(),
|
||||
0,
|
||||
entity.material.descriptor_set.clone(),
|
||||
)
|
||||
.unwrap()
|
||||
.bind_vertex_buffers(0, entity.mesh.vertex_buffer.clone())
|
||||
.unwrap();
|
||||
unsafe {
|
||||
command_buffer
|
||||
.draw(entity.mesh.vertex_count, 1, 0, 0)
|
||||
.unwrap();
|
||||
}
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
||||
impl ApplicationHandler for App {
|
||||
fn resumed(&mut self, event_loop: &ActiveEventLoop) {
|
||||
let window_attributes = winit::window::Window::default_attributes()
|
||||
.with_title("Rust ASH Test")
|
||||
.with_inner_size(winit::dpi::PhysicalSize::new(
|
||||
f64::from(800),
|
||||
f64::from(600),
|
||||
));
|
||||
|
||||
let window = Arc::new(event_loop.create_window(window_attributes).unwrap());
|
||||
|
||||
let surface =
|
||||
Surface::from_window(self.vulkan_context.instance.clone(), window.clone()).unwrap();
|
||||
|
||||
self.renderer = Some(VulkanRenderer::new(
|
||||
window,
|
||||
surface,
|
||||
self.vulkan_context.device.clone(),
|
||||
self.vulkan_context.queue.clone(),
|
||||
));
|
||||
|
||||
self.setup_test_entities().unwrap();
|
||||
}
|
||||
|
||||
fn window_event(&mut self, event_loop: &ActiveEventLoop, _id: WindowId, event: WindowEvent) {
|
||||
match event {
|
||||
WindowEvent::CloseRequested => {
|
||||
log::debug!("The close button was pressed; stopping");
|
||||
event_loop.exit();
|
||||
}
|
||||
WindowEvent::Resized(_) => {
|
||||
let renderer = self.renderer.as_mut().unwrap();
|
||||
renderer.recreate_swapchain = true;
|
||||
}
|
||||
WindowEvent::RedrawRequested => {
|
||||
let (image_index, acquire_future) =
|
||||
match self.renderer.as_mut().unwrap().begin_frame() {
|
||||
Ok(r) => r,
|
||||
Err(VulkanError::OutOfDate) => return,
|
||||
Err(e) => panic!("failed to acquire next image: {e}"),
|
||||
};
|
||||
|
||||
let mut builder = AutoCommandBufferBuilder::primary(
|
||||
self.vulkan_context.command_buffer_allocator.clone(),
|
||||
self.vulkan_context.queue.queue_family_index(),
|
||||
CommandBufferUsage::OneTimeSubmit,
|
||||
)
|
||||
.unwrap();
|
||||
|
||||
{
|
||||
builder
|
||||
.begin_rendering(RenderingInfo {
|
||||
color_attachments: vec![Some(RenderingAttachmentInfo {
|
||||
load_op: AttachmentLoadOp::Clear,
|
||||
store_op: AttachmentStoreOp::Store,
|
||||
clear_value: Some([0.0, 0.0, 0.0, 1.0].into()),
|
||||
..RenderingAttachmentInfo::image_view(
|
||||
self.renderer.as_ref().unwrap().attachment_image_views
|
||||
[image_index as usize]
|
||||
.clone(),
|
||||
)
|
||||
})],
|
||||
..Default::default()
|
||||
})
|
||||
.unwrap()
|
||||
.set_viewport(
|
||||
0,
|
||||
[self.renderer.as_ref().unwrap().viewport.clone()]
|
||||
.into_iter()
|
||||
.collect(),
|
||||
)
|
||||
.unwrap();
|
||||
}
|
||||
|
||||
self.render(&mut builder).unwrap();
|
||||
|
||||
{
|
||||
builder.end_rendering().unwrap();
|
||||
}
|
||||
|
||||
self.renderer
|
||||
.as_mut()
|
||||
.unwrap()
|
||||
.end_frame(image_index, acquire_future, builder)
|
||||
.unwrap();
|
||||
}
|
||||
_ => {}
|
||||
}
|
||||
}
|
||||
|
||||
fn about_to_wait(&mut self, _event_loop: &ActiveEventLoop) {
|
||||
let renderer = self.renderer.as_mut().unwrap();
|
||||
renderer.window.request_redraw();
|
||||
}
|
||||
}
|
71
src/renderer/components.rs
Normal file
71
src/renderer/components.rs
Normal file
|
@ -0,0 +1,71 @@
|
|||
use std::sync::Arc;
|
||||
use vulkano::buffer::Subbuffer;
|
||||
use vulkano::buffer::allocator::SubbufferAllocator;
|
||||
use vulkano::command_buffer::allocator::StandardCommandBufferAllocator;
|
||||
use vulkano::command_buffer::{AutoCommandBufferBuilder, PrimaryAutoCommandBuffer};
|
||||
use vulkano::descriptor_set::DescriptorSet;
|
||||
use vulkano::descriptor_set::allocator::StandardDescriptorSetAllocator;
|
||||
use vulkano::device::Device;
|
||||
use vulkano::memory::allocator::StandardMemoryAllocator;
|
||||
use vulkano::pipeline::GraphicsPipeline;
|
||||
use vulkano::pipeline::Pipeline;
|
||||
|
||||
use crate::vulkan::resources::vertex::Vertex2D;
|
||||
|
||||
#[derive(Clone)]
|
||||
pub struct Mesh {
|
||||
pub vertex_buffer: Subbuffer<[Vertex2D]>,
|
||||
pub vertex_count: u32,
|
||||
pub instance_count: u32,
|
||||
}
|
||||
|
||||
#[derive(Clone)]
|
||||
pub struct Material {
|
||||
pub pipeline: Arc<GraphicsPipeline>,
|
||||
pub descriptor_set: Arc<DescriptorSet>,
|
||||
}
|
||||
|
||||
#[derive(Clone)]
|
||||
pub struct Transform {
|
||||
pub position: [f32; 3],
|
||||
pub rotation: [f32; 3],
|
||||
pub scale: [f32; 3],
|
||||
}
|
||||
|
||||
#[derive(Clone)]
|
||||
pub struct RenderResources {
|
||||
pub device: Arc<Device>,
|
||||
pub memory_allocator: Arc<StandardMemoryAllocator>,
|
||||
pub command_buffer_allocator: Arc<StandardCommandBufferAllocator>,
|
||||
pub uniform_buffer_allocator: Arc<SubbufferAllocator>,
|
||||
pub descriptor_set_allocator: Arc<StandardDescriptorSetAllocator>,
|
||||
}
|
||||
|
||||
pub struct Entity {
|
||||
pub mesh: Mesh,
|
||||
pub material: Material,
|
||||
pub transform: Transform,
|
||||
}
|
||||
|
||||
pub fn render_system(
|
||||
_resources: &RenderResources,
|
||||
builder: &mut AutoCommandBufferBuilder<PrimaryAutoCommandBuffer>,
|
||||
entities: &[Entity],
|
||||
) -> Result<(), Box<dyn std::error::Error>> {
|
||||
for entity in entities {
|
||||
unsafe {
|
||||
builder
|
||||
.bind_pipeline_graphics(entity.material.pipeline.clone())?
|
||||
.bind_descriptor_sets(
|
||||
vulkano::pipeline::PipelineBindPoint::Graphics,
|
||||
entity.material.pipeline.layout().clone(),
|
||||
0,
|
||||
entity.material.descriptor_set.clone(),
|
||||
)?
|
||||
.bind_vertex_buffers(0, entity.mesh.vertex_buffer.clone())?
|
||||
.draw(entity.mesh.vertex_count, entity.mesh.instance_count, 0, 0)?;
|
||||
}
|
||||
}
|
||||
|
||||
Ok(())
|
||||
}
|
2
src/renderer/mod.rs
Normal file
2
src/renderer/mod.rs
Normal file
|
@ -0,0 +1,2 @@
|
|||
pub mod app;
|
||||
pub mod components;
|
135
src/vulkan/context.rs
Normal file
135
src/vulkan/context.rs
Normal file
|
@ -0,0 +1,135 @@
|
|||
use std::sync::Arc;
|
||||
use vulkano::buffer::BufferUsage;
|
||||
use vulkano::buffer::allocator::{SubbufferAllocator, SubbufferAllocatorCreateInfo};
|
||||
use vulkano::command_buffer::allocator::StandardCommandBufferAllocator;
|
||||
use vulkano::descriptor_set::allocator::StandardDescriptorSetAllocator;
|
||||
use vulkano::device::physical::PhysicalDeviceType;
|
||||
use vulkano::device::{
|
||||
Device, DeviceCreateInfo, DeviceExtensions, DeviceFeatures, Queue, QueueCreateInfo, QueueFlags,
|
||||
};
|
||||
use vulkano::instance::{Instance, InstanceCreateFlags, InstanceCreateInfo};
|
||||
use vulkano::memory::allocator::{MemoryTypeFilter, StandardMemoryAllocator};
|
||||
use vulkano::swapchain::Surface;
|
||||
use vulkano::{Version, VulkanLibrary};
|
||||
use winit::event_loop::EventLoop;
|
||||
|
||||
pub struct VulkanContext {
|
||||
pub instance: Arc<Instance>,
|
||||
pub device: Arc<Device>,
|
||||
pub queue: Arc<Queue>,
|
||||
pub memory_allocator: Arc<StandardMemoryAllocator>,
|
||||
pub command_buffer_allocator: Arc<StandardCommandBufferAllocator>,
|
||||
pub uniform_buffer_allocator: Arc<SubbufferAllocator>,
|
||||
pub descriptor_set_allocator: Arc<StandardDescriptorSetAllocator>,
|
||||
}
|
||||
|
||||
impl VulkanContext {
|
||||
pub fn new(event_loop: &EventLoop<()>) -> Result<Self, Box<dyn std::error::Error>> {
|
||||
let library = VulkanLibrary::new().unwrap();
|
||||
for layer in library.layer_properties().unwrap() {
|
||||
log::debug!("Available layer: {}", layer.name());
|
||||
}
|
||||
|
||||
let required_extensions = Surface::required_extensions(event_loop).unwrap();
|
||||
let instance = Instance::new(
|
||||
library,
|
||||
InstanceCreateInfo {
|
||||
flags: InstanceCreateFlags::ENUMERATE_PORTABILITY,
|
||||
enabled_extensions: required_extensions,
|
||||
enabled_layers: vec![String::from("VK_LAYER_KHRONOS_validation")],
|
||||
..Default::default()
|
||||
},
|
||||
)
|
||||
.unwrap();
|
||||
|
||||
let mut device_extensions = DeviceExtensions {
|
||||
khr_swapchain: true,
|
||||
..DeviceExtensions::empty()
|
||||
};
|
||||
|
||||
let (physical_device, queue_family_index) = instance
|
||||
.enumerate_physical_devices()
|
||||
.unwrap()
|
||||
.filter(|p| {
|
||||
p.api_version() >= Version::V1_3 || p.supported_extensions().khr_dynamic_rendering
|
||||
})
|
||||
.filter(|p| p.supported_extensions().contains(&device_extensions))
|
||||
.filter_map(|p| {
|
||||
p.queue_family_properties()
|
||||
.iter()
|
||||
.enumerate()
|
||||
.position(|(_i, q)| q.queue_flags.intersects(QueueFlags::GRAPHICS))
|
||||
.map(|i| (p, i as u32))
|
||||
})
|
||||
.min_by_key(|(p, _)| match p.properties().device_type {
|
||||
PhysicalDeviceType::DiscreteGpu => 0,
|
||||
PhysicalDeviceType::IntegratedGpu => 1,
|
||||
PhysicalDeviceType::VirtualGpu => 2,
|
||||
PhysicalDeviceType::Cpu => 3,
|
||||
PhysicalDeviceType::Other => 4,
|
||||
_ => 5,
|
||||
})
|
||||
.expect("no suitable physical device found");
|
||||
|
||||
log::debug!(
|
||||
"Using device: {} (type: {:?})",
|
||||
physical_device.properties().device_name,
|
||||
physical_device.properties().device_type,
|
||||
);
|
||||
|
||||
if physical_device.api_version() < Version::V1_3 {
|
||||
device_extensions.khr_dynamic_rendering = true;
|
||||
}
|
||||
|
||||
log::debug!("Using device extensions: {:#?}", device_extensions);
|
||||
|
||||
let (device, mut queues) = Device::new(
|
||||
physical_device,
|
||||
DeviceCreateInfo {
|
||||
queue_create_infos: vec![QueueCreateInfo {
|
||||
queue_family_index,
|
||||
..Default::default()
|
||||
}],
|
||||
enabled_extensions: device_extensions,
|
||||
enabled_features: DeviceFeatures {
|
||||
dynamic_rendering: true,
|
||||
..DeviceFeatures::empty()
|
||||
},
|
||||
..Default::default()
|
||||
},
|
||||
)
|
||||
.unwrap();
|
||||
|
||||
let queue = queues.next().unwrap();
|
||||
let memory_allocator = Arc::new(StandardMemoryAllocator::new_default(device.clone()));
|
||||
let command_buffer_allocator = Arc::new(StandardCommandBufferAllocator::new(
|
||||
device.clone(),
|
||||
Default::default(),
|
||||
));
|
||||
|
||||
let uniform_buffer_allocator = Arc::new(SubbufferAllocator::new(
|
||||
memory_allocator.clone(),
|
||||
SubbufferAllocatorCreateInfo {
|
||||
buffer_usage: BufferUsage::UNIFORM_BUFFER,
|
||||
memory_type_filter: MemoryTypeFilter::PREFER_DEVICE
|
||||
| MemoryTypeFilter::HOST_SEQUENTIAL_WRITE,
|
||||
..Default::default()
|
||||
},
|
||||
));
|
||||
|
||||
let descriptor_set_allocator = Arc::new(StandardDescriptorSetAllocator::new(
|
||||
device.clone(),
|
||||
Default::default(),
|
||||
));
|
||||
|
||||
Ok(Self {
|
||||
instance,
|
||||
device,
|
||||
queue,
|
||||
memory_allocator,
|
||||
command_buffer_allocator,
|
||||
uniform_buffer_allocator,
|
||||
descriptor_set_allocator,
|
||||
})
|
||||
}
|
||||
}
|
4
src/vulkan/mod.rs
Normal file
4
src/vulkan/mod.rs
Normal file
|
@ -0,0 +1,4 @@
|
|||
pub mod context;
|
||||
pub mod pipeline;
|
||||
pub mod renderer;
|
||||
pub mod resources;
|
30
src/vulkan/pipeline/mod.rs
Normal file
30
src/vulkan/pipeline/mod.rs
Normal file
|
@ -0,0 +1,30 @@
|
|||
pub mod triangle;
|
||||
|
||||
use std::sync::Arc;
|
||||
use vulkano::device::Device;
|
||||
use vulkano::pipeline::GraphicsPipeline;
|
||||
|
||||
pub trait Pipeline {
|
||||
fn create_pipeline(device: &Arc<Device>) -> Arc<GraphicsPipeline>;
|
||||
fn get_pipeline(&self) -> &Arc<GraphicsPipeline>;
|
||||
}
|
||||
|
||||
pub struct PipelineManager {
|
||||
pipelines: std::collections::HashMap<String, Arc<GraphicsPipeline>>,
|
||||
}
|
||||
|
||||
impl PipelineManager {
|
||||
pub fn new() -> Self {
|
||||
Self {
|
||||
pipelines: std::collections::HashMap::new(),
|
||||
}
|
||||
}
|
||||
|
||||
pub fn register_pipeline(&mut self, name: String, pipeline: Arc<GraphicsPipeline>) {
|
||||
self.pipelines.insert(name, pipeline);
|
||||
}
|
||||
|
||||
pub fn get_pipeline(&self, name: &str) -> Option<&Arc<GraphicsPipeline>> {
|
||||
self.pipelines.get(name)
|
||||
}
|
||||
}
|
127
src/vulkan/pipeline/triangle.rs
Normal file
127
src/vulkan/pipeline/triangle.rs
Normal file
|
@ -0,0 +1,127 @@
|
|||
use std::collections::BTreeMap;
|
||||
use std::error::Error;
|
||||
use std::sync::Arc;
|
||||
use vulkano::descriptor_set::layout::{
|
||||
DescriptorSetLayoutBinding, DescriptorSetLayoutCreateInfo, DescriptorType,
|
||||
};
|
||||
use vulkano::device::Device;
|
||||
use vulkano::pipeline::graphics::GraphicsPipelineCreateInfo;
|
||||
use vulkano::pipeline::graphics::color_blend::{ColorBlendAttachmentState, ColorBlendState};
|
||||
use vulkano::pipeline::graphics::input_assembly::InputAssemblyState;
|
||||
use vulkano::pipeline::graphics::multisample::MultisampleState;
|
||||
use vulkano::pipeline::graphics::rasterization::RasterizationState;
|
||||
use vulkano::pipeline::graphics::subpass::PipelineRenderingCreateInfo;
|
||||
use vulkano::pipeline::graphics::vertex_input::{Vertex, VertexDefinition};
|
||||
use vulkano::pipeline::graphics::viewport::ViewportState;
|
||||
use vulkano::pipeline::layout::{PipelineDescriptorSetLayoutCreateInfo, PipelineLayoutCreateFlags};
|
||||
use vulkano::pipeline::{
|
||||
DynamicState, GraphicsPipeline, PipelineLayout, PipelineShaderStageCreateInfo,
|
||||
};
|
||||
use vulkano::shader::{EntryPoint, ShaderStages};
|
||||
|
||||
use crate::vulkan::resources::vertex::Vertex2D;
|
||||
|
||||
use super::Pipeline;
|
||||
|
||||
mod shaders {
|
||||
pub mod vs {
|
||||
vulkano_shaders::shader! {
|
||||
ty: "vertex",
|
||||
path: r"res/shaders/vertex.vert",
|
||||
}
|
||||
}
|
||||
|
||||
pub mod fs {
|
||||
vulkano_shaders::shader! {
|
||||
ty: "fragment",
|
||||
path: r"res/shaders/vertex.frag",
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub struct TrianglePipeline {
|
||||
pipeline: Arc<GraphicsPipeline>,
|
||||
}
|
||||
|
||||
impl super::Pipeline for TrianglePipeline {
|
||||
fn create_pipeline(device: &Arc<Device>) -> Arc<GraphicsPipeline> {
|
||||
let (vs, fs) = load_shaders(device).unwrap();
|
||||
let vertex_input_state = Vertex2D::per_vertex().definition(&vs).unwrap();
|
||||
|
||||
let stages = [
|
||||
PipelineShaderStageCreateInfo::new(vs),
|
||||
PipelineShaderStageCreateInfo::new(fs),
|
||||
];
|
||||
|
||||
let mut bindings = BTreeMap::<u32, DescriptorSetLayoutBinding>::new();
|
||||
let mut descriptor_set_layout_binding =
|
||||
DescriptorSetLayoutBinding::descriptor_type(DescriptorType::UniformBuffer);
|
||||
descriptor_set_layout_binding.stages = ShaderStages::VERTEX;
|
||||
bindings.insert(0, descriptor_set_layout_binding);
|
||||
|
||||
let descriptor_set_layout = DescriptorSetLayoutCreateInfo {
|
||||
bindings,
|
||||
..Default::default()
|
||||
};
|
||||
|
||||
let create_info = PipelineDescriptorSetLayoutCreateInfo {
|
||||
set_layouts: vec![descriptor_set_layout],
|
||||
flags: PipelineLayoutCreateFlags::default(),
|
||||
push_constant_ranges: vec![],
|
||||
}
|
||||
.into_pipeline_layout_create_info(device.clone())
|
||||
.unwrap();
|
||||
|
||||
let layout = PipelineLayout::new(device.clone(), create_info).unwrap();
|
||||
|
||||
let subpass = PipelineRenderingCreateInfo {
|
||||
color_attachment_formats: vec![Some(vulkano::format::Format::B8G8R8A8_UNORM)],
|
||||
..Default::default()
|
||||
};
|
||||
|
||||
GraphicsPipeline::new(
|
||||
device.clone(),
|
||||
None,
|
||||
GraphicsPipelineCreateInfo {
|
||||
stages: stages.into_iter().collect(),
|
||||
vertex_input_state: Some(vertex_input_state),
|
||||
input_assembly_state: Some(InputAssemblyState::default()),
|
||||
viewport_state: Some(ViewportState::default()),
|
||||
rasterization_state: Some(RasterizationState::default()),
|
||||
multisample_state: Some(MultisampleState::default()),
|
||||
color_blend_state: Some(ColorBlendState::with_attachment_states(
|
||||
subpass.color_attachment_formats.len() as u32,
|
||||
ColorBlendAttachmentState::default(),
|
||||
)),
|
||||
dynamic_state: [DynamicState::Viewport].into_iter().collect(),
|
||||
subpass: Some(subpass.into()),
|
||||
..GraphicsPipelineCreateInfo::layout(layout)
|
||||
},
|
||||
)
|
||||
.unwrap()
|
||||
}
|
||||
|
||||
fn get_pipeline(&self) -> &Arc<GraphicsPipeline> {
|
||||
&self.pipeline
|
||||
}
|
||||
}
|
||||
|
||||
impl TrianglePipeline {
|
||||
pub fn new(device: &Arc<Device>) -> Self {
|
||||
Self {
|
||||
pipeline: Self::create_pipeline(device),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn load_shaders(device: &Arc<Device>) -> Result<(EntryPoint, EntryPoint), Box<dyn Error>> {
|
||||
let vs = shaders::vs::load(device.clone())?
|
||||
.entry_point("main")
|
||||
.ok_or("Failed find main entry point of vertex shader".to_string())?;
|
||||
|
||||
let fs = shaders::fs::load(device.clone())?
|
||||
.entry_point("main")
|
||||
.ok_or("Failed find main entry point of fragment shader".to_string())?;
|
||||
|
||||
Ok((vs, fs))
|
||||
}
|
167
src/vulkan/renderer.rs
Normal file
167
src/vulkan/renderer.rs
Normal file
|
@ -0,0 +1,167 @@
|
|||
use std::sync::Arc;
|
||||
use vulkano::command_buffer::{AutoCommandBufferBuilder, PrimaryAutoCommandBuffer};
|
||||
use vulkano::device::Queue;
|
||||
use vulkano::image::view::ImageView;
|
||||
use vulkano::pipeline::graphics::viewport::Viewport;
|
||||
use vulkano::swapchain::{Surface, Swapchain, SwapchainCreateInfo, SwapchainPresentInfo};
|
||||
use vulkano::sync::{self, GpuFuture};
|
||||
use vulkano::{Validated, VulkanError};
|
||||
use winit::window::Window;
|
||||
|
||||
pub struct VulkanRenderer {
|
||||
pub window: Arc<Window>,
|
||||
pub surface: Arc<Surface>,
|
||||
pub swapchain: Arc<Swapchain>,
|
||||
pub queue: Arc<Queue>,
|
||||
pub attachment_image_views: Vec<Arc<ImageView>>,
|
||||
pub previous_frame_end: Option<Box<dyn GpuFuture>>,
|
||||
pub recreate_swapchain: bool,
|
||||
pub viewport: Viewport,
|
||||
}
|
||||
|
||||
impl VulkanRenderer {
|
||||
pub fn new(
|
||||
window: Arc<Window>,
|
||||
surface: Arc<Surface>,
|
||||
device: Arc<vulkano::device::Device>,
|
||||
queue: Arc<Queue>,
|
||||
) -> Self {
|
||||
let window_size = window.inner_size();
|
||||
let surface_formats = device
|
||||
.physical_device()
|
||||
.surface_formats(&surface, Default::default())
|
||||
.unwrap();
|
||||
let surface_format = surface_formats[0];
|
||||
|
||||
let (swapchain, images) = Swapchain::new(
|
||||
device.clone(),
|
||||
surface.clone(),
|
||||
SwapchainCreateInfo {
|
||||
image_extent: window_size.into(),
|
||||
image_usage: vulkano::image::ImageUsage::COLOR_ATTACHMENT,
|
||||
image_format: surface_format.0,
|
||||
..Default::default()
|
||||
},
|
||||
)
|
||||
.unwrap();
|
||||
|
||||
let attachment_image_views = images
|
||||
.into_iter()
|
||||
.map(|image| ImageView::new_default(image).unwrap())
|
||||
.collect();
|
||||
|
||||
let viewport = Viewport {
|
||||
offset: [0.0, 0.0],
|
||||
extent: window_size.into(),
|
||||
depth_range: 0.0..=1.0,
|
||||
};
|
||||
|
||||
Self {
|
||||
window,
|
||||
surface,
|
||||
swapchain,
|
||||
queue,
|
||||
attachment_image_views,
|
||||
previous_frame_end: Some(sync::now(device).boxed()),
|
||||
recreate_swapchain: false,
|
||||
viewport,
|
||||
}
|
||||
}
|
||||
|
||||
pub fn begin_frame(&mut self) -> Result<(u32, Box<dyn GpuFuture>), VulkanError> {
|
||||
self.previous_frame_end.as_mut().unwrap().cleanup_finished();
|
||||
|
||||
if self.recreate_swapchain {
|
||||
self.recreate_swapchain();
|
||||
self.recreate_swapchain = false;
|
||||
}
|
||||
|
||||
let (image_index, suboptimal, acquire_future) =
|
||||
match vulkano::swapchain::acquire_next_image(self.swapchain.clone(), None)
|
||||
.map_err(Validated::unwrap)
|
||||
{
|
||||
Ok(r) => r,
|
||||
Err(VulkanError::OutOfDate) => {
|
||||
self.recreate_swapchain = true;
|
||||
return Err(VulkanError::OutOfDate);
|
||||
}
|
||||
Err(e) => panic!("failed to acquire next image: {e}"),
|
||||
};
|
||||
|
||||
if suboptimal {
|
||||
self.recreate_swapchain = true;
|
||||
}
|
||||
|
||||
Ok((image_index, acquire_future.boxed()))
|
||||
}
|
||||
|
||||
pub fn end_frame(
|
||||
&mut self,
|
||||
image_index: u32,
|
||||
acquire_future: Box<dyn GpuFuture>,
|
||||
command_buffer: AutoCommandBufferBuilder<PrimaryAutoCommandBuffer>,
|
||||
) -> Result<(), VulkanError> {
|
||||
let command_buffer = command_buffer.build().unwrap();
|
||||
let future = self
|
||||
.previous_frame_end
|
||||
.take()
|
||||
.unwrap()
|
||||
.join(acquire_future)
|
||||
.then_execute(self.queue.clone(), command_buffer)
|
||||
.unwrap()
|
||||
.then_swapchain_present(
|
||||
self.queue.clone(),
|
||||
SwapchainPresentInfo::swapchain_image_index(self.swapchain.clone(), image_index),
|
||||
)
|
||||
.then_signal_fence_and_flush();
|
||||
|
||||
match future.map_err(Validated::unwrap) {
|
||||
Ok(future) => {
|
||||
self.previous_frame_end = Some(future.boxed());
|
||||
Ok(())
|
||||
}
|
||||
Err(VulkanError::OutOfDate) => {
|
||||
self.recreate_swapchain = true;
|
||||
self.previous_frame_end = Some(sync::now(self.queue.device().clone()).boxed());
|
||||
Ok(())
|
||||
}
|
||||
Err(e) => {
|
||||
println!("failed to flush future: {e}");
|
||||
self.previous_frame_end = Some(sync::now(self.queue.device().clone()).boxed());
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn recreate_swapchain(&mut self) {
|
||||
let image_extent: [u32; 2] = self.window.inner_size().into();
|
||||
if image_extent.contains(&0) {
|
||||
return;
|
||||
}
|
||||
|
||||
let surface_formats = self
|
||||
.queue
|
||||
.device()
|
||||
.physical_device()
|
||||
.surface_formats(&self.surface, Default::default())
|
||||
.unwrap();
|
||||
let surface_format = surface_formats[0];
|
||||
|
||||
let (new_swapchain, new_images) = self
|
||||
.swapchain
|
||||
.recreate(SwapchainCreateInfo {
|
||||
image_extent,
|
||||
image_usage: vulkano::image::ImageUsage::COLOR_ATTACHMENT,
|
||||
image_format: surface_format.0,
|
||||
..self.swapchain.create_info()
|
||||
})
|
||||
.expect("failed to recreate swapchain");
|
||||
|
||||
self.swapchain = new_swapchain;
|
||||
self.attachment_image_views = new_images
|
||||
.into_iter()
|
||||
.map(|image| ImageView::new_default(image).unwrap())
|
||||
.collect();
|
||||
self.viewport.extent = [image_extent[0] as f32, image_extent[1] as f32];
|
||||
}
|
||||
}
|
65
src/vulkan/resources/buffer.rs
Normal file
65
src/vulkan/resources/buffer.rs
Normal file
|
@ -0,0 +1,65 @@
|
|||
use std::sync::Arc;
|
||||
use vulkano::buffer::BufferContents;
|
||||
use vulkano::buffer::{Buffer, BufferCreateInfo, BufferUsage, Subbuffer};
|
||||
use vulkano::memory::allocator::{AllocationCreateInfo, MemoryTypeFilter, StandardMemoryAllocator};
|
||||
|
||||
pub struct BufferManager {
|
||||
memory_allocator: Arc<StandardMemoryAllocator>,
|
||||
}
|
||||
|
||||
impl BufferManager {
|
||||
pub fn new(memory_allocator: Arc<StandardMemoryAllocator>) -> Self {
|
||||
Self { memory_allocator }
|
||||
}
|
||||
|
||||
pub fn create_vertex_buffer<T: BufferContents + Clone>(&self, data: &[T]) -> Subbuffer<[T]> {
|
||||
Buffer::from_iter(
|
||||
self.memory_allocator.clone(),
|
||||
BufferCreateInfo {
|
||||
usage: BufferUsage::VERTEX_BUFFER,
|
||||
..Default::default()
|
||||
},
|
||||
AllocationCreateInfo {
|
||||
memory_type_filter: MemoryTypeFilter::PREFER_DEVICE
|
||||
| MemoryTypeFilter::HOST_SEQUENTIAL_WRITE,
|
||||
..Default::default()
|
||||
},
|
||||
data.iter().cloned(),
|
||||
)
|
||||
.unwrap()
|
||||
}
|
||||
|
||||
pub fn create_index_buffer(&self, data: &[u32]) -> Subbuffer<[u32]> {
|
||||
Buffer::from_iter(
|
||||
self.memory_allocator.clone(),
|
||||
BufferCreateInfo {
|
||||
usage: BufferUsage::INDEX_BUFFER,
|
||||
..Default::default()
|
||||
},
|
||||
AllocationCreateInfo {
|
||||
memory_type_filter: MemoryTypeFilter::PREFER_DEVICE
|
||||
| MemoryTypeFilter::HOST_SEQUENTIAL_WRITE,
|
||||
..Default::default()
|
||||
},
|
||||
data.iter().cloned(),
|
||||
)
|
||||
.unwrap()
|
||||
}
|
||||
|
||||
pub fn create_uniform_buffer<T: BufferContents + Copy>(&self, data: &T) -> Subbuffer<T> {
|
||||
Buffer::from_data(
|
||||
self.memory_allocator.clone(),
|
||||
BufferCreateInfo {
|
||||
usage: BufferUsage::UNIFORM_BUFFER,
|
||||
..Default::default()
|
||||
},
|
||||
AllocationCreateInfo {
|
||||
memory_type_filter: MemoryTypeFilter::PREFER_DEVICE
|
||||
| MemoryTypeFilter::HOST_SEQUENTIAL_WRITE,
|
||||
..Default::default()
|
||||
},
|
||||
*data,
|
||||
)
|
||||
.unwrap()
|
||||
}
|
||||
}
|
47
src/vulkan/resources/descriptor.rs
Normal file
47
src/vulkan/resources/descriptor.rs
Normal file
|
@ -0,0 +1,47 @@
|
|||
use std::sync::Arc;
|
||||
use vulkano::descriptor_set::DescriptorSet;
|
||||
use vulkano::descriptor_set::WriteDescriptorSet;
|
||||
use vulkano::descriptor_set::allocator::StandardDescriptorSetAllocator;
|
||||
use vulkano::descriptor_set::layout::{
|
||||
DescriptorSetLayout, DescriptorSetLayoutBinding, DescriptorSetLayoutCreateInfo, DescriptorType,
|
||||
};
|
||||
use vulkano::device::Device;
|
||||
use vulkano::shader::ShaderStages;
|
||||
|
||||
pub struct DescriptorManager {
|
||||
device: Arc<Device>,
|
||||
allocator: Arc<StandardDescriptorSetAllocator>,
|
||||
}
|
||||
|
||||
impl DescriptorManager {
|
||||
pub fn new(device: Arc<Device>, allocator: Arc<StandardDescriptorSetAllocator>) -> Self {
|
||||
Self { device, allocator }
|
||||
}
|
||||
|
||||
pub fn create_descriptor_set_layout(
|
||||
&self,
|
||||
bindings: &[(u32, DescriptorType, u32)],
|
||||
) -> Arc<DescriptorSetLayout> {
|
||||
let mut bindings_map = std::collections::BTreeMap::new();
|
||||
for (binding_index, ty, _count) in bindings {
|
||||
let mut binding = DescriptorSetLayoutBinding::descriptor_type(*ty);
|
||||
binding.stages = ShaderStages::all_graphics();
|
||||
bindings_map.insert(*binding_index, binding);
|
||||
}
|
||||
|
||||
let create_info = DescriptorSetLayoutCreateInfo {
|
||||
bindings: bindings_map,
|
||||
..Default::default()
|
||||
};
|
||||
|
||||
DescriptorSetLayout::new(self.device.clone(), create_info).unwrap()
|
||||
}
|
||||
|
||||
pub fn create_descriptor_set(
|
||||
&self,
|
||||
layout: &Arc<DescriptorSetLayout>,
|
||||
writes: Vec<WriteDescriptorSet>,
|
||||
) -> Arc<DescriptorSet> {
|
||||
DescriptorSet::new(self.allocator.clone(), layout.clone(), writes, []).unwrap()
|
||||
}
|
||||
}
|
3
src/vulkan/resources/mod.rs
Normal file
3
src/vulkan/resources/mod.rs
Normal file
|
@ -0,0 +1,3 @@
|
|||
pub mod buffer;
|
||||
pub mod descriptor;
|
||||
pub mod vertex;
|
46
src/vulkan/resources/vertex.rs
Normal file
46
src/vulkan/resources/vertex.rs
Normal file
|
@ -0,0 +1,46 @@
|
|||
use std::sync::Arc;
|
||||
use vulkano::Validated;
|
||||
use vulkano::buffer::{
|
||||
AllocateBufferError, Buffer, BufferContents, BufferCreateInfo, BufferUsage, Subbuffer,
|
||||
};
|
||||
use vulkano::memory::allocator::{AllocationCreateInfo, MemoryTypeFilter, StandardMemoryAllocator};
|
||||
use vulkano::pipeline::graphics::vertex_input::Vertex;
|
||||
|
||||
#[derive(BufferContents, Vertex, Clone)]
|
||||
#[repr(C)]
|
||||
pub struct Vertex2D {
|
||||
#[format(R32G32_SFLOAT)]
|
||||
pub position: [f32; 2],
|
||||
|
||||
#[format(R32G32B32_SFLOAT)]
|
||||
pub color: [f32; 3],
|
||||
}
|
||||
|
||||
#[derive(BufferContents, Clone)]
|
||||
#[repr(C)]
|
||||
pub struct MVPData {
|
||||
pub world: [[f32; 4]; 4],
|
||||
pub view: [[f32; 4]; 4],
|
||||
pub projection: [[f32; 4]; 4],
|
||||
}
|
||||
|
||||
impl Vertex2D {
|
||||
pub fn create_buffer(
|
||||
vertices: Vec<Vertex2D>,
|
||||
memory_allocator: &Arc<StandardMemoryAllocator>,
|
||||
) -> Result<Subbuffer<[Vertex2D]>, Validated<AllocateBufferError>> {
|
||||
Buffer::from_iter(
|
||||
memory_allocator.clone(),
|
||||
BufferCreateInfo {
|
||||
usage: BufferUsage::VERTEX_BUFFER,
|
||||
..Default::default()
|
||||
},
|
||||
AllocationCreateInfo {
|
||||
memory_type_filter: MemoryTypeFilter::PREFER_DEVICE
|
||||
| MemoryTypeFilter::HOST_SEQUENTIAL_WRITE,
|
||||
..Default::default()
|
||||
},
|
||||
vertices.into_iter(),
|
||||
)
|
||||
}
|
||||
}
|
Loading…
Add table
Add a link
Reference in a new issue