Compare commits
No commits in common. "refactor" and "main" have entirely different histories.
|
@ -20,7 +20,6 @@ IndentWidth: 2
|
||||||
NamespaceIndentation: All
|
NamespaceIndentation: All
|
||||||
SpaceBeforeCpp11BracedList: true
|
SpaceBeforeCpp11BracedList: true
|
||||||
SpacesBeforeTrailingComments: 1
|
SpacesBeforeTrailingComments: 1
|
||||||
Standard: Latest
|
|
||||||
|
|
||||||
IncludeBlocks: Regroup
|
IncludeBlocks: Regroup
|
||||||
IncludeCategories:
|
IncludeCategories:
|
||||||
|
|
|
@ -1,3 +1,4 @@
|
||||||
|
# noinspection SpellCheckingInspection
|
||||||
Checks: >
|
Checks: >
|
||||||
*,
|
*,
|
||||||
-ctad-maybe-unsupported,
|
-ctad-maybe-unsupported,
|
||||||
|
@ -7,11 +8,9 @@ Checks: >
|
||||||
-bugprone-implicit-widening-of-multiplication-result,
|
-bugprone-implicit-widening-of-multiplication-result,
|
||||||
-cert-env33-c,
|
-cert-env33-c,
|
||||||
-concurrency-mt-unsafe,
|
-concurrency-mt-unsafe,
|
||||||
-cppcoreguidelines-avoid-const-or-ref-data-members,
|
|
||||||
-cppcoreguidelines-avoid-magic-numbers,
|
-cppcoreguidelines-avoid-magic-numbers,
|
||||||
-cppcoreguidelines-owning-memory,
|
-cppcoreguidelines-owning-memory,
|
||||||
-cppcoreguidelines-pro-type-member-init,
|
-cppcoreguidelines-pro-type-member-init,
|
||||||
-cppcoreguidelines-pro-type-union-access,
|
|
||||||
-cppcoreguidelines-pro-type-vararg,
|
-cppcoreguidelines-pro-type-vararg,
|
||||||
-fuchsia-*,
|
-fuchsia-*,
|
||||||
-google-*,
|
-google-*,
|
||||||
|
|
4
.clangd
Normal file
4
.clangd
Normal file
|
@ -0,0 +1,4 @@
|
||||||
|
Diagnostics:
|
||||||
|
Suppress: >
|
||||||
|
-Wmissing-template-arg-list-after-template-kw,
|
||||||
|
-Wctad-maybe-unsupported
|
3
.envrc
3
.envrc
|
@ -1 +1,2 @@
|
||||||
use_flake
|
export NIXPKGS_ALLOW_UNSUPPORTED_SYSTEM=1
|
||||||
|
use_flake . --impure
|
||||||
|
|
1
.gitignore
vendored
1
.gitignore
vendored
|
@ -2,4 +2,3 @@
|
||||||
.direnv/
|
.direnv/
|
||||||
.vscode/
|
.vscode/
|
||||||
build/
|
build/
|
||||||
imgui.ini
|
|
||||||
|
|
|
@ -1,10 +1,6 @@
|
||||||
# This file was generated by nvfetcher, please do not modify it manually.
|
# This file was generated by nvfetcher, please do not modify it manually.
|
||||||
|
{ fetchgit, fetchurl, fetchFromGitHub, dockerTools }:
|
||||||
{
|
{
|
||||||
fetchgit,
|
|
||||||
fetchurl,
|
|
||||||
fetchFromGitHub,
|
|
||||||
dockerTools,
|
|
||||||
}: {
|
|
||||||
fmt = {
|
fmt = {
|
||||||
pname = "fmt";
|
pname = "fmt";
|
||||||
version = "11.0.2";
|
version = "11.0.2";
|
||||||
|
|
100
flake.lock
100
flake.lock
|
@ -1,78 +1,27 @@
|
||||||
{
|
{
|
||||||
"nodes": {
|
"nodes": {
|
||||||
"flake-compat": {
|
|
||||||
"locked": {
|
|
||||||
"lastModified": 1688025799,
|
|
||||||
"narHash": "sha256-ktpB4dRtnksm9F5WawoIkEneh1nrEvuxb5lJFt1iOyw=",
|
|
||||||
"owner": "nix-community",
|
|
||||||
"repo": "flake-compat",
|
|
||||||
"rev": "8bf105319d44f6b9f0d764efa4fdef9f1cc9ba1c",
|
|
||||||
"type": "github"
|
|
||||||
},
|
|
||||||
"original": {
|
|
||||||
"owner": "nix-community",
|
|
||||||
"repo": "flake-compat",
|
|
||||||
"type": "github"
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"nixos-asahi": {
|
|
||||||
"inputs": {
|
|
||||||
"flake-compat": "flake-compat",
|
|
||||||
"nixpkgs": "nixpkgs",
|
|
||||||
"rust-overlay": "rust-overlay"
|
|
||||||
},
|
|
||||||
"locked": {
|
|
||||||
"lastModified": 1731536673,
|
|
||||||
"narHash": "sha256-bEkcE98/AwmKzlipCruFdf3KK3CBmtfzabquHtrKURM=",
|
|
||||||
"owner": "zzywysm",
|
|
||||||
"repo": "nixos-asahi",
|
|
||||||
"rev": "09d4e26b7d49323faad37264763cf57988cfd720",
|
|
||||||
"type": "github"
|
|
||||||
},
|
|
||||||
"original": {
|
|
||||||
"owner": "zzywysm",
|
|
||||||
"repo": "nixos-asahi",
|
|
||||||
"type": "github"
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"nixpkgs": {
|
"nixpkgs": {
|
||||||
"locked": {
|
"locked": {
|
||||||
"lastModified": 1731139594,
|
"lastModified": 1727065772,
|
||||||
"narHash": "sha256-IigrKK3vYRpUu+HEjPL/phrfh7Ox881er1UEsZvw9Q4=",
|
"narHash": "sha256-U9baiEXL2YsS67QKlBAPIUq+OB+eUPKv8n1vGNdhiec=",
|
||||||
"owner": "nixos",
|
"owner": "NixOS",
|
||||||
"repo": "nixpkgs",
|
"repo": "nixpkgs",
|
||||||
"rev": "76612b17c0ce71689921ca12d9ffdc9c23ce40b2",
|
"rev": "989dc4cbf6a95f2e5fefc8cd61d2198a8fb6834a",
|
||||||
"type": "github"
|
"type": "github"
|
||||||
},
|
},
|
||||||
"original": {
|
"original": {
|
||||||
"owner": "nixos",
|
"owner": "NixOS",
|
||||||
"ref": "nixos-unstable",
|
|
||||||
"repo": "nixpkgs",
|
"repo": "nixpkgs",
|
||||||
"type": "github"
|
"type": "github"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"nixpkgs_2": {
|
"nixpkgs_2": {
|
||||||
"locked": {
|
"locked": {
|
||||||
"lastModified": 1731640008,
|
"lastModified": 1726481836,
|
||||||
"narHash": "sha256-81hruQPQXZf1xtcyYct9XPvBWvKIk6/DSDCc5XcYKT4=",
|
"narHash": "sha256-MWTBH4dd5zIz2iatDb8IkqSjIeFum9jAqkFxgHLdzO4=",
|
||||||
"owner": "NixOS",
|
|
||||||
"repo": "nixpkgs",
|
|
||||||
"rev": "63de88ed5f65084bb5cde3bdcb716e28cc03a933",
|
|
||||||
"type": "github"
|
|
||||||
},
|
|
||||||
"original": {
|
|
||||||
"owner": "NixOS",
|
|
||||||
"repo": "nixpkgs",
|
|
||||||
"type": "github"
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"nixpkgs_3": {
|
|
||||||
"locked": {
|
|
||||||
"lastModified": 1726871744,
|
|
||||||
"narHash": "sha256-V5LpfdHyQkUF7RfOaDPrZDP+oqz88lTJrMT1+stXNwo=",
|
|
||||||
"owner": "nixos",
|
"owner": "nixos",
|
||||||
"repo": "nixpkgs",
|
"repo": "nixpkgs",
|
||||||
"rev": "a1d92660c6b3b7c26fb883500a80ea9d33321be2",
|
"rev": "20f9370d5f588fb8c72e844c54511cab054b5f40",
|
||||||
"type": "github"
|
"type": "github"
|
||||||
},
|
},
|
||||||
"original": {
|
"original": {
|
||||||
|
@ -84,28 +33,11 @@
|
||||||
},
|
},
|
||||||
"root": {
|
"root": {
|
||||||
"inputs": {
|
"inputs": {
|
||||||
"nixos-asahi": "nixos-asahi",
|
"nixpkgs": "nixpkgs",
|
||||||
"nixpkgs": "nixpkgs_2",
|
|
||||||
"treefmt-nix": "treefmt-nix",
|
"treefmt-nix": "treefmt-nix",
|
||||||
"utils": "utils"
|
"utils": "utils"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"rust-overlay": {
|
|
||||||
"flake": false,
|
|
||||||
"locked": {
|
|
||||||
"lastModified": 1686795910,
|
|
||||||
"narHash": "sha256-jDa40qRZ0GRQtP9EMZdf+uCbvzuLnJglTUI2JoHfWDc=",
|
|
||||||
"owner": "oxalica",
|
|
||||||
"repo": "rust-overlay",
|
|
||||||
"rev": "5c2b97c0a9bc5217fc3dfb1555aae0fb756d99f9",
|
|
||||||
"type": "github"
|
|
||||||
},
|
|
||||||
"original": {
|
|
||||||
"owner": "oxalica",
|
|
||||||
"repo": "rust-overlay",
|
|
||||||
"type": "github"
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"systems": {
|
"systems": {
|
||||||
"locked": {
|
"locked": {
|
||||||
"lastModified": 1681028828,
|
"lastModified": 1681028828,
|
||||||
|
@ -123,14 +55,14 @@
|
||||||
},
|
},
|
||||||
"treefmt-nix": {
|
"treefmt-nix": {
|
||||||
"inputs": {
|
"inputs": {
|
||||||
"nixpkgs": "nixpkgs_3"
|
"nixpkgs": "nixpkgs_2"
|
||||||
},
|
},
|
||||||
"locked": {
|
"locked": {
|
||||||
"lastModified": 1730321837,
|
"lastModified": 1726734507,
|
||||||
"narHash": "sha256-vK+a09qq19QNu2MlLcvN4qcRctJbqWkX7ahgPZ/+maI=",
|
"narHash": "sha256-VUH5O5AcOSxb0uL/m34dDkxFKP6WLQ6y4I1B4+N3L2w=",
|
||||||
"owner": "numtide",
|
"owner": "numtide",
|
||||||
"repo": "treefmt-nix",
|
"repo": "treefmt-nix",
|
||||||
"rev": "746901bb8dba96d154b66492a29f5db0693dbfcc",
|
"rev": "ee41a466c2255a3abe6bc50fc6be927cdee57a9f",
|
||||||
"type": "github"
|
"type": "github"
|
||||||
},
|
},
|
||||||
"original": {
|
"original": {
|
||||||
|
@ -144,11 +76,11 @@
|
||||||
"systems": "systems"
|
"systems": "systems"
|
||||||
},
|
},
|
||||||
"locked": {
|
"locked": {
|
||||||
"lastModified": 1731533236,
|
"lastModified": 1726560853,
|
||||||
"narHash": "sha256-l0KFg5HjrsfsO/JpG+r7fRrqm12kzFHyUHqHCVpMMbI=",
|
"narHash": "sha256-X6rJYSESBVr3hBoH0WbKE5KvhPU5bloyZ2L4K60/fPQ=",
|
||||||
"owner": "numtide",
|
"owner": "numtide",
|
||||||
"repo": "flake-utils",
|
"repo": "flake-utils",
|
||||||
"rev": "11707dc2f618dd54ca8739b309ec4fc024de578b",
|
"rev": "c1dfcf08411b08f6b8615f7d8971a2bfa81d5e8a",
|
||||||
"type": "github"
|
"type": "github"
|
||||||
},
|
},
|
||||||
"original": {
|
"original": {
|
||||||
|
|
40
flake.nix
40
flake.nix
|
@ -2,7 +2,6 @@
|
||||||
description = "C/C++ environment";
|
description = "C/C++ environment";
|
||||||
|
|
||||||
inputs = {
|
inputs = {
|
||||||
nixos-asahi.url = "github:zzywysm/nixos-asahi";
|
|
||||||
nixpkgs.url = "github:NixOS/nixpkgs";
|
nixpkgs.url = "github:NixOS/nixpkgs";
|
||||||
treefmt-nix.url = "github:numtide/treefmt-nix";
|
treefmt-nix.url = "github:numtide/treefmt-nix";
|
||||||
utils.url = "github:numtide/flake-utils";
|
utils.url = "github:numtide/flake-utils";
|
||||||
|
@ -11,20 +10,13 @@
|
||||||
outputs = {
|
outputs = {
|
||||||
self,
|
self,
|
||||||
nixpkgs,
|
nixpkgs,
|
||||||
nixos-asahi,
|
|
||||||
treefmt-nix,
|
treefmt-nix,
|
||||||
utils,
|
utils,
|
||||||
...
|
...
|
||||||
}:
|
}:
|
||||||
utils.lib.eachDefaultSystem (
|
utils.lib.eachDefaultSystem (
|
||||||
system: let
|
system: let
|
||||||
pkgs = import nixpkgs {
|
pkgs = import nixpkgs {inherit system;};
|
||||||
inherit system;
|
|
||||||
config = {
|
|
||||||
allowUnfree = true;
|
|
||||||
allowUnsupportedSystem = true;
|
|
||||||
};
|
|
||||||
};
|
|
||||||
|
|
||||||
stdenv =
|
stdenv =
|
||||||
if pkgs.hostPlatform.isLinux
|
if pkgs.hostPlatform.isLinux
|
||||||
|
@ -42,21 +34,14 @@
|
||||||
|
|
||||||
fmt = mkPkg "fmt";
|
fmt = mkPkg "fmt";
|
||||||
|
|
||||||
imgui = pkgs.imgui.override {
|
|
||||||
IMGUI_BUILD_GLFW_BINDING = true;
|
|
||||||
IMGUI_BUILD_VULKAN_BINDING = true;
|
|
||||||
};
|
|
||||||
|
|
||||||
deps = with pkgs; [
|
deps = with pkgs; [
|
||||||
fmt
|
fmt
|
||||||
glfw
|
glfw
|
||||||
glm
|
glm
|
||||||
imgui
|
|
||||||
shaderc.dev
|
|
||||||
shaderc.lib
|
|
||||||
vulkan-extension-layer
|
vulkan-extension-layer
|
||||||
vulkan-memory-allocator
|
vulkan-memory-allocator
|
||||||
vulkan-utility-libraries
|
vulkan-utility-libraries
|
||||||
|
vulkan-headers
|
||||||
vulkan-loader
|
vulkan-loader
|
||||||
vulkan-tools
|
vulkan-tools
|
||||||
];
|
];
|
||||||
|
@ -97,6 +82,7 @@
|
||||||
projectRootFile = "flake.nix";
|
projectRootFile = "flake.nix";
|
||||||
programs = {
|
programs = {
|
||||||
alejandra.enable = true;
|
alejandra.enable = true;
|
||||||
|
deadnix.enable = true;
|
||||||
|
|
||||||
clang-format = {
|
clang-format = {
|
||||||
enable = true;
|
enable = true;
|
||||||
|
@ -106,17 +92,15 @@
|
||||||
};
|
};
|
||||||
|
|
||||||
devShell = mkShell.override {inherit stdenv;} {
|
devShell = mkShell.override {inherit stdenv;} {
|
||||||
buildInputs =
|
packages =
|
||||||
[
|
[
|
||||||
alejandra
|
alejandra
|
||||||
bear
|
bear
|
||||||
cmake
|
|
||||||
(llvmPackages_18.clang-tools.override {enableLibcxx = true;})
|
(llvmPackages_18.clang-tools.override {enableLibcxx = true;})
|
||||||
lldb
|
lldb
|
||||||
meson
|
meson
|
||||||
nil
|
|
||||||
ninja
|
ninja
|
||||||
#nvfetcher
|
nvfetcher
|
||||||
pkg-config
|
pkg-config
|
||||||
unzip
|
unzip
|
||||||
|
|
||||||
|
@ -129,20 +113,6 @@
|
||||||
|
|
||||||
VULKAN_SDK = "${vulkan-headers}";
|
VULKAN_SDK = "${vulkan-headers}";
|
||||||
VK_LAYER_PATH = "${vulkan-validation-layers}/share/vulkan/explicit_layer.d";
|
VK_LAYER_PATH = "${vulkan-validation-layers}/share/vulkan/explicit_layer.d";
|
||||||
VK_ICD_FILENAMES =
|
|
||||||
if stdenv.isDarwin
|
|
||||||
then "${darwin.moltenvk}/share/vulkan/icd.d/MoltenVK_icd.json"
|
|
||||||
else let
|
|
||||||
vulkanDir =
|
|
||||||
if stdenv.hostPlatform.isx86_64
|
|
||||||
then "${mesa.drivers}/share/vulkan/icd.d"
|
|
||||||
else "${nixos-asahi.packages.aarch64-linux.mesa-asahi-edge.drivers}/share/vulkan/icd.d";
|
|
||||||
vulkanFiles = builtins.filter (file: builtins.match ".*\\.json$" file != null) (builtins.attrNames (builtins.readDir vulkanDir));
|
|
||||||
vulkanPaths = lib.concatStringsSep ":" (map (file: "${vulkanDir}/${file}") vulkanFiles);
|
|
||||||
in
|
|
||||||
if stdenv.hostPlatform.isx86_64
|
|
||||||
then "${linuxPackages_latest.nvidia_x11_beta}/share/vulkan/icd.d/nvidia_icd.x86_64.json:${vulkanPaths}"
|
|
||||||
else vulkanPaths;
|
|
||||||
|
|
||||||
shellHook = ''
|
shellHook = ''
|
||||||
export PATH="${llvmPackages_18.clang-tools.override {enableLibcxx = true;}}/bin:$PATH"
|
export PATH="${llvmPackages_18.clang-tools.override {enableLibcxx = true;}}/bin:$PATH"
|
||||||
|
|
8617
include/stb_image.h
8617
include/stb_image.h
File diff suppressed because it is too large
Load diff
File diff suppressed because it is too large
Load diff
5015
include/vkfw.hpp
5015
include/vkfw.hpp
File diff suppressed because it is too large
Load diff
37
meson.build
37
meson.build
|
@ -2,7 +2,7 @@ project(
|
||||||
'graphics-test',
|
'graphics-test',
|
||||||
'cpp',
|
'cpp',
|
||||||
version: '0.1.0',
|
version: '0.1.0',
|
||||||
default_options: ['cpp_std=c++26', 'warning_level=everything', 'buildtype=debugoptimized'],
|
default_options: ['cpp_std=c++20', 'warning_level=everything', 'buildtype=debugoptimized'],
|
||||||
)
|
)
|
||||||
|
|
||||||
cpp = meson.get_compiler('cpp')
|
cpp = meson.get_compiler('cpp')
|
||||||
|
@ -12,32 +12,37 @@ common_cpp_args = [
|
||||||
'-Wno-c++20-extensions',
|
'-Wno-c++20-extensions',
|
||||||
'-Wno-c++98-compat',
|
'-Wno-c++98-compat',
|
||||||
'-Wno-c++98-compat-pedantic',
|
'-Wno-c++98-compat-pedantic',
|
||||||
'-Wno-pre-c++20-compat-pedantic',
|
'-Wno-disabled-macro-expansion',
|
||||||
|
'-Wno-missing-prototypes',
|
||||||
'-Wno-padded',
|
'-Wno-padded',
|
||||||
'-mavx2',
|
'-Wno-pre-c++20-compat-pedantic',
|
||||||
|
'-Wno-switch-default',
|
||||||
|
'-Wno-unsafe-buffer-usage',
|
||||||
|
'-Wunused-function',
|
||||||
|
'-fvisibility=hidden',
|
||||||
]
|
]
|
||||||
|
|
||||||
add_project_arguments(cpp.get_supported_arguments(common_cpp_args), language: 'cpp')
|
add_project_arguments(cpp.get_supported_arguments(common_cpp_args), language: 'cpp')
|
||||||
|
|
||||||
deps = [
|
source_file_names = [
|
||||||
dependency('fmt', include_type: 'system'),
|
'src/main.cpp',
|
||||||
dependency('glfw3', include_type: 'system'),
|
|
||||||
dependency('glm', include_type: 'system'),
|
|
||||||
dependency('vulkan', include_type: 'system'),
|
|
||||||
dependency('shaderc', include_type: 'system'),
|
|
||||||
]
|
]
|
||||||
|
|
||||||
imgui_dep = dependency('imgui', required: false, include_type: 'system')
|
sources = []
|
||||||
|
|
||||||
if not imgui_dep.found()
|
foreach file : source_file_names
|
||||||
imgui_dep = cpp.find_library('imgui', required: true)
|
sources += files(file)
|
||||||
endif
|
endforeach
|
||||||
|
|
||||||
deps += imgui_dep
|
deps = [
|
||||||
|
dependency('fmt', static: true),
|
||||||
|
dependency('glfw3'),
|
||||||
|
dependency('glm'),
|
||||||
|
dependency('vulkan'),
|
||||||
|
]
|
||||||
|
|
||||||
executable(
|
executable(
|
||||||
'graphics-test',
|
'graphics-test',
|
||||||
sources: files('src/main.cpp'),
|
sources,
|
||||||
include_directories: include_directories('include', is_system: true),
|
|
||||||
dependencies: deps,
|
dependencies: deps,
|
||||||
)
|
)
|
16053
models/viking_room.obj
16053
models/viking_room.obj
File diff suppressed because it is too large
Load diff
|
@ -1,12 +0,0 @@
|
||||||
#version 450
|
|
||||||
|
|
||||||
layout(binding = 1) uniform sampler2D texSampler;
|
|
||||||
|
|
||||||
layout(location = 0) in vec3 fragColor;
|
|
||||||
layout(location = 1) in vec2 fragTexCoord;
|
|
||||||
|
|
||||||
layout(location = 0) out vec4 outColor;
|
|
||||||
|
|
||||||
void main() {
|
|
||||||
outColor = texture(texSampler, fragTexCoord);
|
|
||||||
}
|
|
|
@ -1,20 +0,0 @@
|
||||||
#version 450
|
|
||||||
|
|
||||||
layout(binding = 0) uniform UniformBufferObject {
|
|
||||||
mat4 model;
|
|
||||||
mat4 view;
|
|
||||||
mat4 proj;
|
|
||||||
} ubo;
|
|
||||||
|
|
||||||
layout(location = 0) in vec3 inPosition;
|
|
||||||
layout(location = 1) in vec3 inColor;
|
|
||||||
layout(location = 2) in vec2 inTexCoord;
|
|
||||||
|
|
||||||
layout(location = 0) out vec3 fragColor;
|
|
||||||
layout(location = 1) out vec2 fragTexCoord;
|
|
||||||
|
|
||||||
void main() {
|
|
||||||
gl_Position = ubo.proj * ubo.view * ubo.model * vec4(inPosition, 1.0);
|
|
||||||
fragColor = inColor;
|
|
||||||
fragTexCoord = inTexCoord;
|
|
||||||
}
|
|
|
@ -1,34 +0,0 @@
|
||||||
#pragma once
|
|
||||||
|
|
||||||
#include <array>
|
|
||||||
#include <vulkan/vulkan.hpp>
|
|
||||||
|
|
||||||
#include "../core/types.hpp"
|
|
||||||
|
|
||||||
namespace config {
|
|
||||||
|
|
||||||
// Window settings
|
|
||||||
constexpr i32 WIDTH = 1920;
|
|
||||||
constexpr i32 HEIGHT = 1080;
|
|
||||||
|
|
||||||
// Vulkan settings
|
|
||||||
constexpr i32 MAX_FRAMES_IN_FLIGHT = 2;
|
|
||||||
|
|
||||||
// Shader paths
|
|
||||||
constexpr const char* VERTEX_SHADER_PATH = "shaders/vertex.glsl";
|
|
||||||
constexpr const char* FRAGMENT_SHADER_PATH = "shaders/fragment.glsl";
|
|
||||||
|
|
||||||
// Validation layers for debug builds
|
|
||||||
#ifndef NDEBUG
|
|
||||||
constexpr std::array<const char*, 1> validationLayers = { "VK_LAYER_KHRONOS_validation" };
|
|
||||||
#endif
|
|
||||||
|
|
||||||
// Required device extensions (platform-specific)
|
|
||||||
#ifdef __APPLE__
|
|
||||||
constexpr std::array<const char*, 2> deviceExtensions = { vk::KHRSwapchainExtensionName,
|
|
||||||
vk::KHRPortabilitySubsetExtensionName };
|
|
||||||
#else
|
|
||||||
constexpr std::array<const char*, 1> deviceExtensions = { vk::KHRSwapchainExtensionName };
|
|
||||||
#endif
|
|
||||||
|
|
||||||
} // namespace config
|
|
|
@ -1,95 +0,0 @@
|
||||||
#pragma once
|
|
||||||
|
|
||||||
#include <unordered_set>
|
|
||||||
#include <vulkan/vulkan.hpp>
|
|
||||||
|
|
||||||
#include "../config/config.hpp"
|
|
||||||
#include "queue_structures.hpp"
|
|
||||||
#include "types.hpp"
|
|
||||||
|
|
||||||
namespace core {
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @brief Gets the maximum usable sample count for MSAA.
|
|
||||||
*
|
|
||||||
* @param device Physical device to check
|
|
||||||
* @return vk::SampleCountFlagBits Maximum sample count supported
|
|
||||||
*/
|
|
||||||
static fn getMaxUsableSampleCount(const vk::PhysicalDevice& device) -> vk::SampleCountFlagBits {
|
|
||||||
vk::PhysicalDeviceProperties physicalDeviceProperties = device.getProperties();
|
|
||||||
|
|
||||||
vk::SampleCountFlags counts = physicalDeviceProperties.limits.framebufferColorSampleCounts &
|
|
||||||
physicalDeviceProperties.limits.framebufferDepthSampleCounts;
|
|
||||||
|
|
||||||
if (counts & vk::SampleCountFlagBits::e64)
|
|
||||||
return vk::SampleCountFlagBits::e64;
|
|
||||||
if (counts & vk::SampleCountFlagBits::e32)
|
|
||||||
return vk::SampleCountFlagBits::e32;
|
|
||||||
if (counts & vk::SampleCountFlagBits::e16)
|
|
||||||
return vk::SampleCountFlagBits::e16;
|
|
||||||
if (counts & vk::SampleCountFlagBits::e8)
|
|
||||||
return vk::SampleCountFlagBits::e8;
|
|
||||||
if (counts & vk::SampleCountFlagBits::e4)
|
|
||||||
return vk::SampleCountFlagBits::e4;
|
|
||||||
if (counts & vk::SampleCountFlagBits::e2)
|
|
||||||
return vk::SampleCountFlagBits::e2;
|
|
||||||
|
|
||||||
return vk::SampleCountFlagBits::e1;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @brief Checks if a device supports the required extensions.
|
|
||||||
*
|
|
||||||
* @param device Physical device to check
|
|
||||||
* @return bool True if all required extensions are supported
|
|
||||||
*/
|
|
||||||
static fn checkDeviceExtensionSupport(const vk::PhysicalDevice& device) -> bool {
|
|
||||||
// Get the available extensions
|
|
||||||
std::vector<vk::ExtensionProperties> availableExtensions = device.enumerateDeviceExtensionProperties();
|
|
||||||
|
|
||||||
// Create a set of required extensions
|
|
||||||
std::unordered_set<std::string> requiredExtensions(
|
|
||||||
config::deviceExtensions.begin(), config::deviceExtensions.end()
|
|
||||||
);
|
|
||||||
|
|
||||||
// For each available extension,
|
|
||||||
for (const auto& extension : availableExtensions)
|
|
||||||
// Remove it from the required extensions set if it's required
|
|
||||||
requiredExtensions.erase(extension.extensionName);
|
|
||||||
|
|
||||||
// If the set is empty, all required extensions are supported
|
|
||||||
return requiredExtensions.empty();
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @brief Checks if a physical device is suitable for the application.
|
|
||||||
*
|
|
||||||
* @param device Physical device to check
|
|
||||||
* @param surface Surface to check presentation support against
|
|
||||||
* @return bool True if device is suitable, false otherwise
|
|
||||||
*/
|
|
||||||
static fn isDeviceSuitable(const vk::PhysicalDevice& device, const vk::SurfaceKHR& surface) -> bool {
|
|
||||||
// Get the queue families that support the required operations
|
|
||||||
QueueFamilyIndices qfIndices = QueueFamilyIndices::findQueueFamilies(device, surface);
|
|
||||||
|
|
||||||
// Check if the device supports the required extensions
|
|
||||||
bool extensionsSupported = checkDeviceExtensionSupport(device);
|
|
||||||
|
|
||||||
bool swapChainAdequate = false;
|
|
||||||
|
|
||||||
if (extensionsSupported) {
|
|
||||||
SwapChainSupportDetails swapChainSupport =
|
|
||||||
SwapChainSupportDetails::querySwapChainSupport(device, surface);
|
|
||||||
// Check if the swap chain is adequate (make sure it has
|
|
||||||
// at least one supported format and presentation mode)
|
|
||||||
swapChainAdequate = !swapChainSupport.formats.empty() && !swapChainSupport.present_modes.empty();
|
|
||||||
}
|
|
||||||
|
|
||||||
// Check if the device supports the required features
|
|
||||||
vk::PhysicalDeviceFeatures supportedFeatures = device.getFeatures();
|
|
||||||
|
|
||||||
return qfIndices.isComplete() && extensionsSupported && swapChainAdequate &&
|
|
||||||
supportedFeatures.samplerAnisotropy;
|
|
||||||
}
|
|
||||||
|
|
||||||
} // namespace core
|
|
|
@ -1,144 +0,0 @@
|
||||||
#pragma once
|
|
||||||
|
|
||||||
#include <optional>
|
|
||||||
#include <vector>
|
|
||||||
#include <vulkan/vulkan.hpp>
|
|
||||||
|
|
||||||
#include "types.hpp"
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @brief Struct to store queue family indices.
|
|
||||||
*
|
|
||||||
* This struct contains the indices of the graphics and presentation queue families.
|
|
||||||
*/
|
|
||||||
struct QueueFamilyIndices {
|
|
||||||
std::optional<u32> graphics_family; ///< Index of graphics queue family
|
|
||||||
std::optional<u32> present_family; ///< Index of presentation queue family
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @brief Check if all required queue families are found.
|
|
||||||
*
|
|
||||||
* @return True if both graphics and presentation families are found, false otherwise.
|
|
||||||
*/
|
|
||||||
fn isComplete() -> bool { return graphics_family.has_value() && present_family.has_value(); }
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @brief Finds queue families that support graphics and presentation.
|
|
||||||
*
|
|
||||||
* @param device Physical device to check
|
|
||||||
* @param surface Surface to check presentation support against
|
|
||||||
* @return QueueFamilyIndices Struct containing queue family indices
|
|
||||||
*/
|
|
||||||
static fn findQueueFamilies(const vk::PhysicalDevice& device, const vk::SurfaceKHR& surface)
|
|
||||||
-> QueueFamilyIndices {
|
|
||||||
QueueFamilyIndices indices;
|
|
||||||
|
|
||||||
std::vector<vk::QueueFamilyProperties> queueFamilies = device.getQueueFamilyProperties();
|
|
||||||
|
|
||||||
for (u32 i = 0; i < queueFamilies.size(); i++) {
|
|
||||||
const auto& queueFamily = queueFamilies[i];
|
|
||||||
|
|
||||||
// Check for graphics support
|
|
||||||
if (queueFamily.queueFlags & vk::QueueFlagBits::eGraphics)
|
|
||||||
indices.graphics_family = i;
|
|
||||||
|
|
||||||
// Check for presentation support
|
|
||||||
if (device.getSurfaceSupportKHR(i, surface))
|
|
||||||
indices.present_family = i;
|
|
||||||
|
|
||||||
if (indices.isComplete())
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
|
|
||||||
return indices;
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @brief Struct to hold swap chain support details.
|
|
||||||
*
|
|
||||||
* This struct contains information about the surface capabilities,
|
|
||||||
* supported formats, and presentation modes.
|
|
||||||
*/
|
|
||||||
struct SwapChainSupportDetails {
|
|
||||||
vk::SurfaceCapabilitiesKHR capabilities; ///< Surface capabilities
|
|
||||||
std::vector<vk::SurfaceFormatKHR> formats; ///< Supported surface formats
|
|
||||||
std::vector<vk::PresentModeKHR> present_modes; ///< Supported presentation modes
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @brief Queries swap chain support details from a physical device.
|
|
||||||
*
|
|
||||||
* @param device Physical device to query
|
|
||||||
* @param surface Surface to check against
|
|
||||||
* @return SwapChainSupportDetails Struct containing surface capabilities and supported formats
|
|
||||||
*/
|
|
||||||
static fn querySwapChainSupport(const vk::PhysicalDevice& device, const vk::SurfaceKHR& surface)
|
|
||||||
-> SwapChainSupportDetails {
|
|
||||||
SwapChainSupportDetails details;
|
|
||||||
|
|
||||||
details.capabilities = device.getSurfaceCapabilitiesKHR(surface);
|
|
||||||
details.formats = device.getSurfaceFormatsKHR(surface);
|
|
||||||
details.present_modes = device.getSurfacePresentModesKHR(surface);
|
|
||||||
|
|
||||||
return details;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @brief Chooses the best surface format for the swap chain.
|
|
||||||
*
|
|
||||||
* @param availableFormats List of available surface formats
|
|
||||||
* @return vk::SurfaceFormatKHR The chosen surface format
|
|
||||||
*/
|
|
||||||
static fn chooseSwapSurfaceFormat(const std::vector<vk::SurfaceFormatKHR>& availableFormats
|
|
||||||
) -> vk::SurfaceFormatKHR {
|
|
||||||
// Prefer SRGB with nonlinear color space
|
|
||||||
for (const auto& availableFormat : availableFormats)
|
|
||||||
if (availableFormat.format == vk::Format::eB8G8R8A8Srgb &&
|
|
||||||
availableFormat.colorSpace == vk::ColorSpaceKHR::eSrgbNonlinear)
|
|
||||||
return availableFormat;
|
|
||||||
|
|
||||||
// If preferred format not found, use first available
|
|
||||||
return availableFormats[0];
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @brief Chooses the best presentation mode for the swap chain.
|
|
||||||
*
|
|
||||||
* @param availablePresentModes List of available presentation modes
|
|
||||||
* @return vk::PresentModeKHR The chosen presentation mode
|
|
||||||
*/
|
|
||||||
static fn chooseSwapPresentMode(const std::vector<vk::PresentModeKHR>& availablePresentModes
|
|
||||||
) -> vk::PresentModeKHR {
|
|
||||||
// Prefer mailbox mode (triple buffering)
|
|
||||||
for (const auto& availablePresentMode : availablePresentModes)
|
|
||||||
if (availablePresentMode == vk::PresentModeKHR::eMailbox)
|
|
||||||
return availablePresentMode;
|
|
||||||
|
|
||||||
// Fallback to FIFO (vsync)
|
|
||||||
return vk::PresentModeKHR::eFifo;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @brief Chooses the swap extent (resolution) for the swap chain.
|
|
||||||
*
|
|
||||||
* @param capabilities Surface capabilities
|
|
||||||
* @param width Desired width
|
|
||||||
* @param height Desired height
|
|
||||||
* @return vk::Extent2D The chosen swap extent
|
|
||||||
*/
|
|
||||||
static fn chooseSwapExtent(const vk::SurfaceCapabilitiesKHR& capabilities, u32 width, u32 height)
|
|
||||||
-> vk::Extent2D {
|
|
||||||
if (capabilities.currentExtent.width != std::numeric_limits<u32>::max())
|
|
||||||
return capabilities.currentExtent;
|
|
||||||
|
|
||||||
vk::Extent2D actualExtent = { width, height };
|
|
||||||
|
|
||||||
actualExtent.width =
|
|
||||||
std::clamp(actualExtent.width, capabilities.minImageExtent.width, capabilities.maxImageExtent.width);
|
|
||||||
|
|
||||||
actualExtent.height =
|
|
||||||
std::clamp(actualExtent.height, capabilities.minImageExtent.height, capabilities.maxImageExtent.height);
|
|
||||||
|
|
||||||
return actualExtent;
|
|
||||||
}
|
|
||||||
};
|
|
|
@ -1,152 +0,0 @@
|
||||||
/**
|
|
||||||
* @file types.hpp
|
|
||||||
* @brief Core type definitions and aliases for the project.
|
|
||||||
*
|
|
||||||
* This file provides a centralized location for type definitions used throughout
|
|
||||||
* the project. It includes fixed-width integer types, floating-point types, and
|
|
||||||
* commonly used GLM vector types. The type aliases are designed to improve code
|
|
||||||
* readability and ensure consistent type usage across the codebase.
|
|
||||||
*/
|
|
||||||
|
|
||||||
#pragma once
|
|
||||||
|
|
||||||
#include <cstddef>
|
|
||||||
#include <cstdint>
|
|
||||||
#include <string>
|
|
||||||
#include <glm/glm.hpp>
|
|
||||||
|
|
||||||
#define fn auto
|
|
||||||
|
|
||||||
// Integer Types
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @brief 8-bit unsigned integer.
|
|
||||||
* @details Range: 0 to 255
|
|
||||||
* Used for byte-level operations and color channel values.
|
|
||||||
*/
|
|
||||||
using u8 = std::uint8_t;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @brief 16-bit unsigned integer.
|
|
||||||
* @details Range: 0 to 65,535
|
|
||||||
* Used for texture coordinates and small indices.
|
|
||||||
*/
|
|
||||||
using u16 = std::uint16_t;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @brief 32-bit unsigned integer.
|
|
||||||
* @details Range: 0 to 4,294,967,295
|
|
||||||
* Used for array sizes, indices, and flags.
|
|
||||||
*/
|
|
||||||
using u32 = std::uint32_t;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @brief 64-bit unsigned integer.
|
|
||||||
* @details Range: 0 to 18,446,744,073,709,551,615
|
|
||||||
* Used for large indices and timestamps.
|
|
||||||
*/
|
|
||||||
using u64 = std::uint64_t;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @brief 8-bit signed integer.
|
|
||||||
* @details Range: -128 to 127
|
|
||||||
* Used for small signed values and relative offsets.
|
|
||||||
*/
|
|
||||||
using i8 = std::int8_t;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @brief 16-bit signed integer.
|
|
||||||
* @details Range: -32,768 to 32,767
|
|
||||||
* Used for medium-range signed values.
|
|
||||||
*/
|
|
||||||
using i16 = std::int16_t;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @brief 32-bit signed integer.
|
|
||||||
* @details Range: -2,147,483,648 to 2,147,483,647
|
|
||||||
* Primary signed integer type for general use.
|
|
||||||
*/
|
|
||||||
using i32 = std::int32_t;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @brief 64-bit signed integer.
|
|
||||||
* @details Range: -9,223,372,036,854,775,808 to 9,223,372,036,854,775,807
|
|
||||||
* Used for large signed values and time calculations.
|
|
||||||
*/
|
|
||||||
using i64 = std::int64_t;
|
|
||||||
|
|
||||||
// Floating-Point Types
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @brief 32-bit floating-point number.
|
|
||||||
* @details IEEE 754 single-precision
|
|
||||||
* Used for graphics calculations, positions, and colors.
|
|
||||||
*/
|
|
||||||
using f32 = float;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @brief 64-bit floating-point number.
|
|
||||||
* @details IEEE 754 double-precision
|
|
||||||
* Used for high-precision calculations and physics.
|
|
||||||
*/
|
|
||||||
using f64 = double;
|
|
||||||
|
|
||||||
// Size Types
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @brief Unsigned size type.
|
|
||||||
* @details Platform-dependent size (32/64-bit)
|
|
||||||
* Used for memory sizes and container sizes.
|
|
||||||
*/
|
|
||||||
using usize = std::size_t;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @brief Signed size type.
|
|
||||||
* @details Platform-dependent size (32/64-bit)
|
|
||||||
* Used for pointer arithmetic and container differences.
|
|
||||||
*/
|
|
||||||
using isize = std::ptrdiff_t;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @brief String type alias.
|
|
||||||
* @details Standard string type for text handling.
|
|
||||||
*/
|
|
||||||
using string = std::string;
|
|
||||||
|
|
||||||
// GLM Vector Types
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @brief 2D vector with 32-bit float components.
|
|
||||||
* @details Used for 2D positions, texture coordinates.
|
|
||||||
*/
|
|
||||||
using vec2 = glm::f32vec2;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @brief 3D vector with 32-bit float components.
|
|
||||||
* @details Used for 3D positions, colors, normals.
|
|
||||||
*/
|
|
||||||
using vec3 = glm::f32vec3;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @brief 4D vector with 32-bit float components.
|
|
||||||
* @details Used for homogeneous coordinates, quaternions.
|
|
||||||
*/
|
|
||||||
using vec4 = glm::f32vec4;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @brief 2D vector with 64-bit float components.
|
|
||||||
* @details Used for high-precision 2D calculations.
|
|
||||||
*/
|
|
||||||
using dvec2 = glm::f64vec2;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @brief 3D vector with 64-bit float components.
|
|
||||||
* @details Used for high-precision 3D positions and directions.
|
|
||||||
*/
|
|
||||||
using dvec3 = glm::f64vec3;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @brief 4D vector with 64-bit float components.
|
|
||||||
* @details Used for high-precision homogeneous coordinates.
|
|
||||||
*/
|
|
||||||
using dvec4 = glm::f64vec4;
|
|
|
@ -1,147 +0,0 @@
|
||||||
#pragma once
|
|
||||||
|
|
||||||
#include <glm/glm.hpp>
|
|
||||||
#include <glm/gtc/matrix_transform.hpp>
|
|
||||||
|
|
||||||
#include "../core/types.hpp"
|
|
||||||
#include "vkfw.hpp"
|
|
||||||
|
|
||||||
// Camera speed constant from main.cpp
|
|
||||||
constexpr f64 CAMERA_SPEED = 1.0;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @brief Represents a 3D camera in the scene.
|
|
||||||
*/
|
|
||||||
struct Camera {
|
|
||||||
glm::dvec3 position; ///< Camera's position in 3D space
|
|
||||||
glm::dvec3 front; ///< Direction the camera is facing
|
|
||||||
glm::dvec3 up; ///< Camera's up vector
|
|
||||||
glm::dvec3 right; ///< Camera's right vector
|
|
||||||
f64 yaw; ///< Yaw angle (rotation around vertical axis)
|
|
||||||
f64 pitch; ///< Pitch angle (rotation around horizontal axis)
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @brief Constructs a Camera with default settings.
|
|
||||||
*/
|
|
||||||
Camera()
|
|
||||||
: position(2.0, 2.0, 2.0),
|
|
||||||
front(glm::normalize(glm::dvec3(-2.0, -2.0, -2.0))),
|
|
||||||
up(0.0, 0.0, 1.0),
|
|
||||||
right(glm::normalize(glm::cross(front, up))),
|
|
||||||
yaw(-135.0),
|
|
||||||
pitch(-35.26) {
|
|
||||||
updateCameraVectors();
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @brief Gets the camera's current position.
|
|
||||||
* @return The camera's position as a 3D vector.
|
|
||||||
*/
|
|
||||||
[[nodiscard]] fn getPosition() const -> glm::dvec3 { return position; }
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @brief Calculates and returns the view matrix for the camera.
|
|
||||||
* @return The view matrix as a 4x4 matrix.
|
|
||||||
*/
|
|
||||||
[[nodiscard]] fn getViewMatrix() const -> glm::mat4 { return glm::lookAt(position, position + front, up); }
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @brief Moves the camera forward.
|
|
||||||
* @param deltaTime Time elapsed since last frame.
|
|
||||||
*/
|
|
||||||
fn moveForward(f64 deltaTime) -> void {
|
|
||||||
glm::dvec3 horizontalFront = glm::normalize(glm::dvec3(front.x, front.y, 0.0));
|
|
||||||
position += horizontalFront * CAMERA_SPEED * deltaTime;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @brief Moves the camera backward.
|
|
||||||
* @param deltaTime Time elapsed since last frame.
|
|
||||||
*/
|
|
||||||
fn moveBackward(f64 deltaTime) -> void {
|
|
||||||
glm::dvec3 horizontalFront = glm::normalize(glm::dvec3(front.x, front.y, 0.0));
|
|
||||||
position -= horizontalFront * CAMERA_SPEED * deltaTime;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @brief Moves the camera to the left.
|
|
||||||
* @param deltaTime Time elapsed since last frame.
|
|
||||||
*/
|
|
||||||
fn moveLeft(f64 deltaTime) -> void {
|
|
||||||
glm::dvec3 horizontalRight = glm::normalize(glm::dvec3(right.x, right.y, 0.0));
|
|
||||||
position -= horizontalRight * CAMERA_SPEED * deltaTime;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @brief Moves the camera to the right.
|
|
||||||
* @param deltaTime Time elapsed since last frame.
|
|
||||||
*/
|
|
||||||
fn moveRight(f64 deltaTime) -> void {
|
|
||||||
glm::dvec3 horizontalRight = glm::normalize(glm::dvec3(right.x, right.y, 0.0));
|
|
||||||
position += horizontalRight * CAMERA_SPEED * deltaTime;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @brief Moves the camera upward.
|
|
||||||
* @param deltaTime Time elapsed since last frame.
|
|
||||||
*/
|
|
||||||
fn moveUp(f64 deltaTime) -> void { position += glm::dvec3(0.0, 0.0, 1.0) * CAMERA_SPEED * deltaTime; }
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @brief Moves the camera downward.
|
|
||||||
* @param deltaTime Time elapsed since last frame.
|
|
||||||
*/
|
|
||||||
fn moveDown(f64 deltaTime) -> void { position -= glm::dvec3(0.0, 0.0, 1.0) * CAMERA_SPEED * deltaTime; }
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @brief Rotates the camera based on mouse movement.
|
|
||||||
* @param xoffset Horizontal mouse movement.
|
|
||||||
* @param yoffset Vertical mouse movement.
|
|
||||||
*/
|
|
||||||
fn rotate(f64 xoffset, f64 yoffset) -> void {
|
|
||||||
const f64 sensitivity = 0.1;
|
|
||||||
yaw += xoffset * sensitivity;
|
|
||||||
pitch += yoffset * sensitivity;
|
|
||||||
|
|
||||||
pitch = glm::clamp(pitch, -89.0, 89.0);
|
|
||||||
|
|
||||||
updateCameraVectors();
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @brief Processes input for camera movement and rotation.
|
|
||||||
* @param window The GLFW window.
|
|
||||||
* @param camera The camera to be controlled.
|
|
||||||
* @param deltaTime Time elapsed since last frame.
|
|
||||||
* @param cameraSpeed Speed multiplier for camera movement.
|
|
||||||
*/
|
|
||||||
static fn processInput(vkfw::Window& window, Camera& camera, const f32& deltaTime, const f32& cameraSpeed)
|
|
||||||
-> void {
|
|
||||||
if (window.getKey(vkfw::Key::eW) == vkfw::eTrue)
|
|
||||||
camera.moveForward(static_cast<f64>(deltaTime * cameraSpeed));
|
|
||||||
if (window.getKey(vkfw::Key::eA) == vkfw::eTrue)
|
|
||||||
camera.moveLeft(static_cast<f64>(deltaTime * cameraSpeed));
|
|
||||||
if (window.getKey(vkfw::Key::eS) == vkfw::eTrue)
|
|
||||||
camera.moveBackward(static_cast<f64>(deltaTime * cameraSpeed));
|
|
||||||
if (window.getKey(vkfw::Key::eD) == vkfw::eTrue)
|
|
||||||
camera.moveRight(static_cast<f64>(deltaTime * cameraSpeed));
|
|
||||||
if (window.getKey(vkfw::Key::eSpace) == vkfw::eTrue)
|
|
||||||
camera.moveUp(static_cast<f64>(deltaTime * cameraSpeed));
|
|
||||||
if (window.getKey(vkfw::Key::eLeftShift) == vkfw::eTrue)
|
|
||||||
camera.moveDown(static_cast<f64>(deltaTime * cameraSpeed));
|
|
||||||
}
|
|
||||||
|
|
||||||
private:
|
|
||||||
/**
|
|
||||||
* @brief Updates the camera's orientation vectors based on yaw and pitch.
|
|
||||||
*/
|
|
||||||
fn updateCameraVectors() -> void {
|
|
||||||
front = glm::normalize(glm::dvec3(
|
|
||||||
cos(glm::radians(yaw)) * cos(glm::radians(pitch)),
|
|
||||||
sin(glm::radians(yaw)) * cos(glm::radians(pitch)),
|
|
||||||
sin(glm::radians(pitch))
|
|
||||||
));
|
|
||||||
right = glm::normalize(glm::cross(front, glm::dvec3(0.0, 0.0, 1.0)));
|
|
||||||
up = glm::normalize(glm::cross(right, front));
|
|
||||||
}
|
|
||||||
};
|
|
|
@ -1,278 +0,0 @@
|
||||||
#pragma once
|
|
||||||
|
|
||||||
#define VULKAN_HPP_NO_CONSTRUCTORS
|
|
||||||
#include <vulkan/vulkan.hpp>
|
|
||||||
|
|
||||||
#include "../core/types.hpp"
|
|
||||||
|
|
||||||
namespace graphics {
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @brief Creates a Vulkan image view.
|
|
||||||
*
|
|
||||||
* This function creates and returns a unique Vulkan image view using the provided parameters.
|
|
||||||
*
|
|
||||||
* @param device The logical device to create the view on
|
|
||||||
* @param image The Vulkan image for which to create the view
|
|
||||||
* @param format The format of the image
|
|
||||||
* @param aspectFlags The aspect flags for the image view
|
|
||||||
* @param mipLevels The number of mip levels for the image view
|
|
||||||
* @return vk::UniqueImageView A unique handle to the created Vulkan image view
|
|
||||||
*
|
|
||||||
* @details
|
|
||||||
* The function creates an image view with the following properties:
|
|
||||||
* - 2D view type
|
|
||||||
* - Subresource range starting from base mip level 0
|
|
||||||
* - Single array layer starting from base array layer 0
|
|
||||||
*/
|
|
||||||
static fn createImageView(
|
|
||||||
const vk::Device& device,
|
|
||||||
const vk::Image& image,
|
|
||||||
const vk::Format& format,
|
|
||||||
const vk::ImageAspectFlags& aspectFlags,
|
|
||||||
const u32& mipLevels
|
|
||||||
) -> vk::UniqueImageView {
|
|
||||||
return device.createImageViewUnique({
|
|
||||||
.image = image,
|
|
||||||
.viewType = vk::ImageViewType::e2D,
|
|
||||||
.format = format,
|
|
||||||
.subresourceRange = {
|
|
||||||
.aspectMask = aspectFlags,
|
|
||||||
.baseMipLevel = 0,
|
|
||||||
.levelCount = mipLevels,
|
|
||||||
.baseArrayLayer = 0,
|
|
||||||
.layerCount = 1,
|
|
||||||
},
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @brief Finds a suitable memory type for allocation.
|
|
||||||
*
|
|
||||||
* @param physicalDevice The physical device to check
|
|
||||||
* @param typeFilter Filter for memory types
|
|
||||||
* @param properties Required memory properties
|
|
||||||
* @return u32 Index of the suitable memory type
|
|
||||||
*/
|
|
||||||
static fn findMemoryType(
|
|
||||||
const vk::PhysicalDevice& physicalDevice,
|
|
||||||
const u32& typeFilter,
|
|
||||||
const vk::MemoryPropertyFlags& properties
|
|
||||||
) -> u32 {
|
|
||||||
vk::PhysicalDeviceMemoryProperties memProperties = physicalDevice.getMemoryProperties();
|
|
||||||
|
|
||||||
for (u32 i = 0; i < memProperties.memoryTypeCount; i++)
|
|
||||||
if ((typeFilter & (1 << i)) && (memProperties.memoryTypes[i].propertyFlags & properties) == properties)
|
|
||||||
return i;
|
|
||||||
|
|
||||||
throw std::runtime_error("Failed to find suitable memory type!");
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @brief Creates a Vulkan image and allocates memory for it.
|
|
||||||
*
|
|
||||||
* @param device The logical device to create the image on
|
|
||||||
* @param width Width of the image
|
|
||||||
* @param height Height of the image
|
|
||||||
* @param mipLevels Number of mip levels
|
|
||||||
* @param numSamples Number of samples for multisampling
|
|
||||||
* @param format Format of the image
|
|
||||||
* @param tiling Tiling mode of the image
|
|
||||||
* @param usage Usage flags for the image
|
|
||||||
* @param properties Memory property flags
|
|
||||||
* @return std::pair<vk::UniqueImage, vk::UniqueDeviceMemory> A pair containing the image and its memory
|
|
||||||
*/
|
|
||||||
static fn createImage(
|
|
||||||
const vk::Device& device,
|
|
||||||
const vk::PhysicalDevice& physicalDevice,
|
|
||||||
const u32& width,
|
|
||||||
const u32& height,
|
|
||||||
const u32& mipLevels,
|
|
||||||
const vk::SampleCountFlagBits& numSamples,
|
|
||||||
const vk::Format& format,
|
|
||||||
const vk::ImageTiling& tiling,
|
|
||||||
const vk::ImageUsageFlags& usage,
|
|
||||||
const vk::MemoryPropertyFlags& properties
|
|
||||||
) -> std::pair<vk::UniqueImage, vk::UniqueDeviceMemory> {
|
|
||||||
// Define the image creation info
|
|
||||||
vk::ImageCreateInfo imageInfo {
|
|
||||||
.imageType = vk::ImageType::e2D,
|
|
||||||
.format = format,
|
|
||||||
.extent = { .width = width, .height = height, .depth = 1 },
|
|
||||||
.mipLevels = mipLevels,
|
|
||||||
.arrayLayers = 1,
|
|
||||||
.samples = numSamples,
|
|
||||||
.tiling = tiling,
|
|
||||||
.usage = usage,
|
|
||||||
.sharingMode = vk::SharingMode::eExclusive,
|
|
||||||
.initialLayout = vk::ImageLayout::eUndefined,
|
|
||||||
};
|
|
||||||
|
|
||||||
// Create the image
|
|
||||||
vk::UniqueImage image = device.createImageUnique(imageInfo);
|
|
||||||
|
|
||||||
// Get the memory requirements for the image
|
|
||||||
vk::MemoryRequirements memRequirements = device.getImageMemoryRequirements(image.get());
|
|
||||||
|
|
||||||
// Memory allocation info
|
|
||||||
vk::MemoryAllocateInfo allocInfo {
|
|
||||||
.allocationSize = memRequirements.size,
|
|
||||||
.memoryTypeIndex = findMemoryType(physicalDevice, memRequirements.memoryTypeBits, properties),
|
|
||||||
};
|
|
||||||
|
|
||||||
// Allocate memory
|
|
||||||
vk::UniqueDeviceMemory imageMemory = device.allocateMemoryUnique(allocInfo);
|
|
||||||
|
|
||||||
// Bind the allocated memory to the image
|
|
||||||
device.bindImageMemory(image.get(), imageMemory.get(), 0);
|
|
||||||
|
|
||||||
return { std::move(image), std::move(imageMemory) };
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @brief Transitions an image between layouts.
|
|
||||||
*
|
|
||||||
* @param device The logical device
|
|
||||||
* @param commandPool The command pool to allocate command buffers from
|
|
||||||
* @param queue The queue to submit commands to
|
|
||||||
* @param image The image to transition
|
|
||||||
* @param oldLayout The old layout
|
|
||||||
* @param newLayout The new layout
|
|
||||||
* @param mipLevels Number of mip levels
|
|
||||||
*/
|
|
||||||
static fn transitionImageLayout(
|
|
||||||
const vk::Device& device,
|
|
||||||
const vk::CommandPool& commandPool,
|
|
||||||
const vk::Queue& queue,
|
|
||||||
const vk::Image& image,
|
|
||||||
const vk::ImageLayout& oldLayout,
|
|
||||||
const vk::ImageLayout& newLayout,
|
|
||||||
const u32& mipLevels
|
|
||||||
) -> void {
|
|
||||||
// Create a command buffer
|
|
||||||
vk::CommandBufferAllocateInfo allocInfo {
|
|
||||||
.commandPool = commandPool,
|
|
||||||
.level = vk::CommandBufferLevel::ePrimary,
|
|
||||||
.commandBufferCount = 1,
|
|
||||||
};
|
|
||||||
|
|
||||||
vk::UniqueCommandBuffer commandBuffer = std::move(device.allocateCommandBuffersUnique(allocInfo)[0]);
|
|
||||||
|
|
||||||
// Begin recording
|
|
||||||
commandBuffer->begin({ .flags = vk::CommandBufferUsageFlagBits::eOneTimeSubmit });
|
|
||||||
|
|
||||||
// Define the image memory barrier
|
|
||||||
vk::ImageMemoryBarrier barrier {
|
|
||||||
.oldLayout = oldLayout,
|
|
||||||
.newLayout = newLayout,
|
|
||||||
.srcQueueFamilyIndex = vk::QueueFamilyIgnored,
|
|
||||||
.dstQueueFamilyIndex = vk::QueueFamilyIgnored,
|
|
||||||
.image = image,
|
|
||||||
.subresourceRange = {
|
|
||||||
.aspectMask = vk::ImageAspectFlagBits::eColor,
|
|
||||||
.baseMipLevel = 0,
|
|
||||||
.levelCount = mipLevels,
|
|
||||||
.baseArrayLayer = 0,
|
|
||||||
.layerCount = 1,
|
|
||||||
},
|
|
||||||
};
|
|
||||||
|
|
||||||
// Define the source and destination stages
|
|
||||||
vk::PipelineStageFlags sourceStage;
|
|
||||||
vk::PipelineStageFlags destinationStage;
|
|
||||||
|
|
||||||
// Define the access masks
|
|
||||||
if (oldLayout == vk::ImageLayout::eUndefined && newLayout == vk::ImageLayout::eTransferDstOptimal) {
|
|
||||||
barrier.srcAccessMask = {};
|
|
||||||
barrier.dstAccessMask = vk::AccessFlagBits::eTransferWrite;
|
|
||||||
|
|
||||||
sourceStage = vk::PipelineStageFlagBits::eTopOfPipe;
|
|
||||||
destinationStage = vk::PipelineStageFlagBits::eTransfer;
|
|
||||||
} else if (oldLayout == vk::ImageLayout::eTransferDstOptimal &&
|
|
||||||
newLayout == vk::ImageLayout::eShaderReadOnlyOptimal) {
|
|
||||||
barrier.srcAccessMask = vk::AccessFlagBits::eTransferWrite;
|
|
||||||
barrier.dstAccessMask = vk::AccessFlagBits::eShaderRead;
|
|
||||||
|
|
||||||
sourceStage = vk::PipelineStageFlagBits::eTransfer;
|
|
||||||
destinationStage = vk::PipelineStageFlagBits::eFragmentShader;
|
|
||||||
} else {
|
|
||||||
// Ensure that the layout transition is supported
|
|
||||||
throw std::invalid_argument("Unsupported layout transition!");
|
|
||||||
}
|
|
||||||
|
|
||||||
// Record the pipeline barrier
|
|
||||||
commandBuffer->pipelineBarrier(sourceStage, destinationStage, {}, {}, {}, barrier);
|
|
||||||
|
|
||||||
// End recording
|
|
||||||
commandBuffer->end();
|
|
||||||
|
|
||||||
// Submit the command buffer
|
|
||||||
vk::SubmitInfo submitInfo {
|
|
||||||
.commandBufferCount = 1,
|
|
||||||
.pCommandBuffers = &commandBuffer.get(),
|
|
||||||
};
|
|
||||||
|
|
||||||
queue.submit(submitInfo);
|
|
||||||
queue.waitIdle();
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @brief Copies data from a buffer to an image.
|
|
||||||
*
|
|
||||||
* @param device The logical device
|
|
||||||
* @param commandPool The command pool to allocate command buffers from
|
|
||||||
* @param queue The queue to submit commands to
|
|
||||||
* @param buffer Source buffer
|
|
||||||
* @param image Destination image
|
|
||||||
* @param width Image width
|
|
||||||
* @param height Image height
|
|
||||||
*/
|
|
||||||
static fn copyBufferToImage(
|
|
||||||
const vk::Device& device,
|
|
||||||
const vk::CommandPool& commandPool,
|
|
||||||
const vk::Queue& queue,
|
|
||||||
const vk::Buffer& buffer,
|
|
||||||
const vk::Image& image,
|
|
||||||
const u32& width,
|
|
||||||
const u32& height
|
|
||||||
) -> void {
|
|
||||||
// Create a command buffer
|
|
||||||
vk::CommandBufferAllocateInfo allocInfo {
|
|
||||||
.commandPool = commandPool,
|
|
||||||
.level = vk::CommandBufferLevel::ePrimary,
|
|
||||||
.commandBufferCount = 1,
|
|
||||||
};
|
|
||||||
|
|
||||||
vk::UniqueCommandBuffer commandBuffer = std::move(device.allocateCommandBuffersUnique(allocInfo)[0]);
|
|
||||||
|
|
||||||
// Begin recording
|
|
||||||
commandBuffer->begin({ .flags = vk::CommandBufferUsageFlagBits::eOneTimeSubmit });
|
|
||||||
|
|
||||||
vk::BufferImageCopy region {
|
|
||||||
.bufferOffset = 0,
|
|
||||||
.bufferRowLength = 0,
|
|
||||||
.bufferImageHeight = 0,
|
|
||||||
.imageSubresource = { .aspectMask = vk::ImageAspectFlagBits::eColor,
|
|
||||||
.mipLevel = 0,
|
|
||||||
.baseArrayLayer = 0,
|
|
||||||
.layerCount = 1 },
|
|
||||||
.imageOffset = { .x = 0, .y = 0, .z = 0 },
|
|
||||||
.imageExtent = { .width = width, .height = height, .depth = 1 },
|
|
||||||
};
|
|
||||||
|
|
||||||
commandBuffer->copyBufferToImage(buffer, image, vk::ImageLayout::eTransferDstOptimal, 1, ®ion);
|
|
||||||
|
|
||||||
// End recording
|
|
||||||
commandBuffer->end();
|
|
||||||
|
|
||||||
// Submit the command buffer
|
|
||||||
vk::SubmitInfo submitInfo {
|
|
||||||
.commandBufferCount = 1,
|
|
||||||
.pCommandBuffers = &commandBuffer.get(),
|
|
||||||
};
|
|
||||||
|
|
||||||
queue.submit(submitInfo);
|
|
||||||
queue.waitIdle();
|
|
||||||
}
|
|
||||||
|
|
||||||
} // namespace graphics
|
|
|
@ -1,230 +0,0 @@
|
||||||
/**
|
|
||||||
* @file shaders.hpp
|
|
||||||
* @brief SPIR-V shader compilation and caching system.
|
|
||||||
*
|
|
||||||
* This file provides functionality for compiling GLSL shaders to SPIR-V and
|
|
||||||
* managing a shader cache system. It supports automatic recompilation when
|
|
||||||
* source files are modified and efficient caching of compiled shaders.
|
|
||||||
*/
|
|
||||||
|
|
||||||
#pragma once
|
|
||||||
|
|
||||||
#include <filesystem>
|
|
||||||
#include <fmt/format.h>
|
|
||||||
#include <fstream>
|
|
||||||
#include <ios>
|
|
||||||
#include <shaderc/shaderc.hpp>
|
|
||||||
#include <string>
|
|
||||||
#include <vector>
|
|
||||||
|
|
||||||
#define VULKAN_HPP_NO_CONSTRUCTORS
|
|
||||||
#include <vulkan/vulkan.hpp>
|
|
||||||
|
|
||||||
#include "../core/types.hpp"
|
|
||||||
|
|
||||||
namespace graphics {
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @brief Handles shader compilation and caching operations.
|
|
||||||
*
|
|
||||||
* This class provides static methods for compiling GLSL shaders to SPIR-V
|
|
||||||
* and managing a cache system. It automatically detects when shaders need
|
|
||||||
* to be recompiled based on file timestamps and provides efficient caching
|
|
||||||
* of compiled shader binaries.
|
|
||||||
*/
|
|
||||||
class ShaderCompiler {
|
|
||||||
public:
|
|
||||||
ShaderCompiler() = default;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @brief Compiles or retrieves a cached SPIR-V shader.
|
|
||||||
*
|
|
||||||
* @param shaderPath Path to the GLSL shader source file
|
|
||||||
* @param kind Type of shader (vertex, fragment, compute, etc.)
|
|
||||||
* @return std::vector<u32> Compiled SPIR-V binary code
|
|
||||||
* @throws std::runtime_error If shader compilation fails or file is not found
|
|
||||||
*
|
|
||||||
* This function performs the following steps:
|
|
||||||
* 1. Checks if a cached version exists and is up-to-date
|
|
||||||
* 2. Loads from cache if available and valid
|
|
||||||
* 3. Otherwise, compiles the shader from source
|
|
||||||
* 4. Caches the newly compiled shader for future use
|
|
||||||
* 5. Returns the SPIR-V binary code
|
|
||||||
*/
|
|
||||||
static fn getCompiledShader(const std::filesystem::path& shaderPath, const shaderc_shader_kind& kind)
|
|
||||||
-> std::vector<u32> {
|
|
||||||
using namespace std;
|
|
||||||
|
|
||||||
// Convert to absolute path if relative
|
|
||||||
filesystem::path absPath = filesystem::absolute(shaderPath);
|
|
||||||
|
|
||||||
if (!filesystem::exists(absPath))
|
|
||||||
throw runtime_error("Shader file not found: " + absPath.string());
|
|
||||||
|
|
||||||
const string shaderName = absPath.stem().string();
|
|
||||||
const filesystem::path cacheFile = getCacheFilePath(shaderName);
|
|
||||||
|
|
||||||
// Check if we need to recompile by comparing timestamps
|
|
||||||
if (filesystem::exists(cacheFile)) {
|
|
||||||
const auto sourceTime = filesystem::last_write_time(absPath);
|
|
||||||
const auto cacheTime = filesystem::last_write_time(cacheFile);
|
|
||||||
|
|
||||||
if (cacheTime >= sourceTime) {
|
|
||||||
// Cache is up to date, load it
|
|
||||||
vector<u32> spirvCode = loadCachedShader(cacheFile);
|
|
||||||
if (!spirvCode.empty()) {
|
|
||||||
fmt::println("Loaded shader from cache: {}", cacheFile.string());
|
|
||||||
return spirvCode;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Need to compile the shader
|
|
||||||
fmt::println("Compiling shader: {}", absPath.string());
|
|
||||||
|
|
||||||
// Read shader source
|
|
||||||
ifstream file(absPath);
|
|
||||||
if (!file)
|
|
||||||
throw runtime_error("Failed to open shader file: " + absPath.string());
|
|
||||||
|
|
||||||
string shaderSource((istreambuf_iterator<char>(file)), istreambuf_iterator<char>());
|
|
||||||
file.close();
|
|
||||||
|
|
||||||
// Compile the shader
|
|
||||||
vector<u32> spirvCode = compileShader(shaderSource.c_str(), kind);
|
|
||||||
|
|
||||||
if (spirvCode.empty())
|
|
||||||
throw runtime_error("Shader compilation failed for: " + absPath.string());
|
|
||||||
|
|
||||||
// Cache the compiled SPIR-V binary
|
|
||||||
saveCompiledShader(spirvCode, cacheFile.string());
|
|
||||||
return spirvCode;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @brief Creates a shader module from SPIR-V code.
|
|
||||||
*
|
|
||||||
* @param device Logical device to create the shader module on
|
|
||||||
* @param code SPIR-V binary code
|
|
||||||
* @return vk::UniqueShaderModule Unique handle to the created shader module
|
|
||||||
*/
|
|
||||||
static fn createShaderModule(const vk::Device& device, const std::vector<u32>& code)
|
|
||||||
-> vk::UniqueShaderModule {
|
|
||||||
vk::ShaderModuleCreateInfo createInfo { .codeSize = code.size() * sizeof(u32), .pCode = code.data() };
|
|
||||||
|
|
||||||
return device.createShaderModuleUnique(createInfo);
|
|
||||||
}
|
|
||||||
|
|
||||||
private:
|
|
||||||
/**
|
|
||||||
* @brief Determines the platform-specific shader cache directory.
|
|
||||||
*
|
|
||||||
* @param shaderName Base name of the shader file
|
|
||||||
* @return std::filesystem::path Full path to the cache file
|
|
||||||
*
|
|
||||||
* Cache locations:
|
|
||||||
* - Windows: %LOCALAPPDATA%/VulkanApp/Shaders/
|
|
||||||
* - macOS: ~/Library/Application Support/VulkanApp/Shaders/
|
|
||||||
* - Linux: ~/.local/share/VulkanApp/Shaders/
|
|
||||||
*
|
|
||||||
* The directory is created if it doesn't exist.
|
|
||||||
*/
|
|
||||||
static fn getCacheFilePath(const string& shaderName) -> std::filesystem::path {
|
|
||||||
using namespace std::filesystem;
|
|
||||||
|
|
||||||
#ifdef _WIN32
|
|
||||||
path cacheDir = path(getenv("LOCALAPPDATA")) / "VulkanApp" / "Shaders";
|
|
||||||
#elif defined(__APPLE__)
|
|
||||||
path cacheDir = path(getenv("HOME")) / "Library" / "Application Support" / "VulkanApp" / "Shaders";
|
|
||||||
#else // Assume Linux or other UNIX-like systems
|
|
||||||
path cacheDir = path(getenv("HOME")) / ".local" / "share" / "VulkanApp" / "Shaders";
|
|
||||||
#endif
|
|
||||||
|
|
||||||
if (!exists(cacheDir))
|
|
||||||
create_directories(cacheDir);
|
|
||||||
|
|
||||||
return cacheDir / (shaderName + ".spv");
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @brief Loads a cached SPIR-V shader from disk.
|
|
||||||
*
|
|
||||||
* @param cachePath Path to the cached shader file
|
|
||||||
* @return std::vector<u32> SPIR-V binary code, empty if loading fails
|
|
||||||
*
|
|
||||||
* Reads the binary SPIR-V data from the cache file. Returns an empty
|
|
||||||
* vector if the file cannot be opened or read properly.
|
|
||||||
*/
|
|
||||||
static fn loadCachedShader(const std::filesystem::path& cachePath) -> std::vector<u32> {
|
|
||||||
std::ifstream file(cachePath, std::ios::binary);
|
|
||||||
if (!file.is_open())
|
|
||||||
return {};
|
|
||||||
|
|
||||||
// Read file size
|
|
||||||
file.seekg(0, std::ios::end);
|
|
||||||
const std::streamoff fileSize = file.tellg();
|
|
||||||
file.seekg(0, std::ios::beg);
|
|
||||||
|
|
||||||
// Allocate buffer and read data
|
|
||||||
std::vector<u32> buffer(static_cast<u32>(fileSize) / sizeof(u32));
|
|
||||||
file.read(std::bit_cast<char*>(buffer.data()), fileSize);
|
|
||||||
|
|
||||||
return buffer;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @brief Compiles GLSL source code to SPIR-V.
|
|
||||||
*
|
|
||||||
* @param source GLSL shader source code
|
|
||||||
* @param kind Type of shader being compiled
|
|
||||||
* @return std::vector<u32> Compiled SPIR-V binary code
|
|
||||||
*
|
|
||||||
* Uses the shaderc library to compile GLSL to SPIR-V. The compilation
|
|
||||||
* is performed with optimization level set to performance and generates
|
|
||||||
* debug information in debug builds.
|
|
||||||
*/
|
|
||||||
static fn compileShader(const char* source, shaderc_shader_kind kind) -> std::vector<u32> {
|
|
||||||
shaderc::Compiler compiler;
|
|
||||||
shaderc::CompileOptions options;
|
|
||||||
|
|
||||||
// Set compilation options
|
|
||||||
#ifdef NDEBUG
|
|
||||||
options.SetOptimizationLevel(shaderc_optimization_level_performance);
|
|
||||||
#else
|
|
||||||
options.SetOptimizationLevel(shaderc_optimization_level_zero);
|
|
||||||
options.SetGenerateDebugInfo();
|
|
||||||
#endif
|
|
||||||
|
|
||||||
// Compile the shader
|
|
||||||
shaderc::SpvCompilationResult module = compiler.CompileGlslToSpv(source, kind, "shader", options);
|
|
||||||
|
|
||||||
if (module.GetCompilationStatus() != shaderc_compilation_status_success)
|
|
||||||
return {};
|
|
||||||
|
|
||||||
return { module.cbegin(), module.cend() };
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @brief Saves compiled SPIR-V code to the cache.
|
|
||||||
*
|
|
||||||
* @param spirv Compiled SPIR-V binary code
|
|
||||||
* @param cachePath Path where the cache file should be saved
|
|
||||||
* @return bool True if save was successful, false otherwise
|
|
||||||
*
|
|
||||||
* Writes the SPIR-V binary to disk for future use. Creates any
|
|
||||||
* necessary parent directories if they don't exist.
|
|
||||||
*/
|
|
||||||
static fn saveCompiledShader(const std::vector<u32>& spirv, const std::string& cachePath) -> bool {
|
|
||||||
std::ofstream file(cachePath, std::ios::binary);
|
|
||||||
if (!file.is_open())
|
|
||||||
return false;
|
|
||||||
|
|
||||||
file.write(
|
|
||||||
std::bit_cast<const char*>(spirv.data()), static_cast<std::streamsize>(spirv.size() * sizeof(u32))
|
|
||||||
);
|
|
||||||
|
|
||||||
return file.good();
|
|
||||||
}
|
|
||||||
}; // class ShaderCompiler
|
|
||||||
|
|
||||||
} // namespace graphics
|
|
|
@ -1,14 +0,0 @@
|
||||||
#pragma once
|
|
||||||
|
|
||||||
#include <glm/glm.hpp>
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @brief Struct representing a uniform buffer object.
|
|
||||||
*
|
|
||||||
* This struct holds the model, view, and projection matrices for use in shaders.
|
|
||||||
*/
|
|
||||||
struct UniformBufferObject {
|
|
||||||
alignas(16) glm::mat4 model; ///< Model transformation matrix
|
|
||||||
alignas(16) glm::mat4 view; ///< View transformation matrix
|
|
||||||
alignas(16) glm::mat4 proj; ///< Projection matrix
|
|
||||||
};
|
|
|
@ -1,141 +0,0 @@
|
||||||
/**
|
|
||||||
* @file unique_image.hpp
|
|
||||||
* @brief Provides RAII wrapper for image loading and management.
|
|
||||||
*
|
|
||||||
* This file implements a RAII-compliant image handling class that uses stb_image
|
|
||||||
* for loading various image formats. It ensures proper resource management and
|
|
||||||
* provides a safe interface for image data access.
|
|
||||||
*/
|
|
||||||
|
|
||||||
#include <filesystem>
|
|
||||||
#include <fmt/format.h>
|
|
||||||
|
|
||||||
#define STB_IMAGE_IMPLEMENTATION
|
|
||||||
#include <stb_image.h>
|
|
||||||
|
|
||||||
#include "../core/types.hpp"
|
|
||||||
|
|
||||||
namespace stb {
|
|
||||||
/**
|
|
||||||
* @brief RAII wrapper for image data loaded via stb_image.
|
|
||||||
*
|
|
||||||
* This class provides safe resource management for loaded images, ensuring proper
|
|
||||||
* cleanup of image data. It supports move semantics but prevents copying to maintain
|
|
||||||
* single ownership of image resources.
|
|
||||||
*/
|
|
||||||
class UniqueImage {
|
|
||||||
public:
|
|
||||||
/**
|
|
||||||
* @brief Constructs a UniqueImage by loading from file.
|
|
||||||
*
|
|
||||||
* @param path Filesystem path to the image file.
|
|
||||||
* @throws std::runtime_error If image loading fails.
|
|
||||||
*
|
|
||||||
* Automatically loads the image data from the specified file using stb_image.
|
|
||||||
* The image data is stored in RGBA format with 8 bits per channel.
|
|
||||||
*/
|
|
||||||
UniqueImage(const std::filesystem::path& path) { load(path.string().c_str()); }
|
|
||||||
|
|
||||||
// Prevent copying to maintain single ownership
|
|
||||||
UniqueImage(const UniqueImage&) = delete;
|
|
||||||
fn operator=(const UniqueImage&)->UniqueImage& = delete;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @brief Move constructor for transferring image ownership.
|
|
||||||
*
|
|
||||||
* @param other Source UniqueImage to move from.
|
|
||||||
*
|
|
||||||
* Transfers ownership of image data from another UniqueImage instance,
|
|
||||||
* leaving the source in a valid but empty state.
|
|
||||||
*/
|
|
||||||
UniqueImage(UniqueImage&& other) noexcept
|
|
||||||
: mData(other.mData),
|
|
||||||
mWidth(static_cast<i32>(other.mWidth)),
|
|
||||||
mHeight(static_cast<i32>(other.mHeight)),
|
|
||||||
mChannels(static_cast<i32>(other.mChannels)) {
|
|
||||||
other.mData = nullptr;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @brief Move assignment operator for transferring image ownership.
|
|
||||||
*
|
|
||||||
* @param other Source UniqueImage to move from.
|
|
||||||
* @return Reference to this object.
|
|
||||||
*
|
|
||||||
* Safely transfers ownership of image data, ensuring proper cleanup of
|
|
||||||
* existing resources before the transfer.
|
|
||||||
*/
|
|
||||||
fn operator=(UniqueImage&& other) noexcept -> UniqueImage& {
|
|
||||||
if (this != &other) {
|
|
||||||
if (mData)
|
|
||||||
stbi_image_free(mData);
|
|
||||||
|
|
||||||
mData = other.mData;
|
|
||||||
mWidth = static_cast<i32>(other.mWidth);
|
|
||||||
mHeight = static_cast<i32>(other.mHeight);
|
|
||||||
mChannels = static_cast<i32>(other.mChannels);
|
|
||||||
other.mData = nullptr;
|
|
||||||
}
|
|
||||||
return *this;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @brief Destructor that ensures proper cleanup of image resources.
|
|
||||||
*
|
|
||||||
* Automatically frees the image data using stb_image_free when the
|
|
||||||
* object goes out of scope.
|
|
||||||
*/
|
|
||||||
~UniqueImage() {
|
|
||||||
if (mData)
|
|
||||||
stbi_image_free(mData);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @brief Get raw pointer to image data.
|
|
||||||
* @return Pointer to the raw image data in memory.
|
|
||||||
*
|
|
||||||
* The data is stored in row-major order with either RGB or RGBA format,
|
|
||||||
* depending on the source image.
|
|
||||||
*/
|
|
||||||
[[nodiscard]] fn getData() const -> u8* { return mData; }
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @brief Get image width in pixels.
|
|
||||||
* @return Width of the image.
|
|
||||||
*/
|
|
||||||
[[nodiscard]] fn getWidth() const -> i32 { return mWidth; }
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @brief Get image height in pixels.
|
|
||||||
* @return Height of the image.
|
|
||||||
*/
|
|
||||||
[[nodiscard]] fn getHeight() const -> i32 { return mHeight; }
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @brief Get number of color channels.
|
|
||||||
* @return Number of channels (e.g., 3 for RGB, 4 for RGBA).
|
|
||||||
*/
|
|
||||||
[[nodiscard]] fn getChannels() const -> i32 { return mChannels; }
|
|
||||||
|
|
||||||
private:
|
|
||||||
/**
|
|
||||||
* @brief Internal helper function to load image data.
|
|
||||||
*
|
|
||||||
* @param path Path to the image file.
|
|
||||||
* @throws std::runtime_error If image loading fails.
|
|
||||||
*
|
|
||||||
* Uses stb_image to load the image data, automatically detecting the
|
|
||||||
* format and number of channels from the file.
|
|
||||||
*/
|
|
||||||
fn load(const char* path) -> void {
|
|
||||||
mData = stbi_load(path, &mWidth, &mHeight, &mChannels, STBI_rgb_alpha);
|
|
||||||
if (!mData)
|
|
||||||
throw std::runtime_error(fmt::format("Failed to load texture image: {}", path));
|
|
||||||
}
|
|
||||||
|
|
||||||
u8* mData = nullptr; ///< Raw image data in memory
|
|
||||||
i32 mWidth = 0; ///< Image width in pixels
|
|
||||||
i32 mHeight = 0; ///< Image height in pixels
|
|
||||||
i32 mChannels = 0; ///< Number of color channels
|
|
||||||
};
|
|
||||||
}
|
|
|
@ -1,98 +0,0 @@
|
||||||
/**
|
|
||||||
* @file vertex.hpp
|
|
||||||
* @brief Defines the vertex structure and its associated utilities for 3D rendering.
|
|
||||||
*
|
|
||||||
* This file contains the Vertex structure used for 3D model representation in the Vulkan
|
|
||||||
* graphics pipeline. It includes position, color, and texture coordinate data, along with
|
|
||||||
* Vulkan-specific descriptors for vertex input handling.
|
|
||||||
*/
|
|
||||||
|
|
||||||
#define GLM_FORCE_DEPTH_ZERO_TO_ONE
|
|
||||||
#define GLM_ENABLE_EXPERIMENTAL
|
|
||||||
#include <glm/glm.hpp>
|
|
||||||
#include <glm/gtx/hash.hpp>
|
|
||||||
|
|
||||||
#define VULKAN_HPP_NO_CONSTRUCTORS
|
|
||||||
#include <vulkan/vulkan.hpp>
|
|
||||||
|
|
||||||
#include "../core/types.hpp"
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @brief Represents a vertex in 3D space with color and texture information.
|
|
||||||
*
|
|
||||||
* This structure defines a vertex with all its attributes required for rendering,
|
|
||||||
* including position in 3D space, RGB color, and texture coordinates. It also
|
|
||||||
* provides methods for Vulkan vertex input configuration.
|
|
||||||
*/
|
|
||||||
struct Vertex {
|
|
||||||
vec3 pos; ///< Position of the vertex in 3D space (x, y, z)
|
|
||||||
vec3 color; ///< RGB color values, each component in range [0, 1]
|
|
||||||
vec2 tex_coord; ///< Texture coordinates (u, v) for texture mapping
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @brief Provides the vertex binding description for Vulkan.
|
|
||||||
*
|
|
||||||
* @return vk::VertexInputBindingDescription Describes how to bind vertex data to GPU memory.
|
|
||||||
*
|
|
||||||
* The binding description specifies:
|
|
||||||
* - Binding index (0)
|
|
||||||
* - Stride (size of one vertex)
|
|
||||||
* - Input rate (per-vertex data)
|
|
||||||
*/
|
|
||||||
static fn getBindingDescription() -> vk::VertexInputBindingDescription {
|
|
||||||
return { .binding = 0, .stride = sizeof(Vertex), .inputRate = vk::VertexInputRate::eVertex };
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @brief Provides attribute descriptions for vertex data interpretation.
|
|
||||||
*
|
|
||||||
* @return std::array<vk::VertexInputAttributeDescription, 3> Array of descriptions for position, color, and
|
|
||||||
* texture coordinates.
|
|
||||||
*
|
|
||||||
* The attribute descriptions specify:
|
|
||||||
* - Location indices (0 for position, 1 for color, 2 for texture coordinates)
|
|
||||||
* - Binding point (0)
|
|
||||||
* - Data format (R32G32B32 for vec3, R32G32 for vec2)
|
|
||||||
* - Offset of each attribute in the vertex structure
|
|
||||||
*/
|
|
||||||
static fn getAttributeDescriptions() -> std::array<vk::VertexInputAttributeDescription, 3> {
|
|
||||||
return {
|
|
||||||
vk::VertexInputAttributeDescription { 0, 0, vk::Format::eR32G32B32Sfloat, offsetof(Vertex, pos) },
|
|
||||||
vk::VertexInputAttributeDescription { 1, 0, vk::Format::eR32G32B32Sfloat, offsetof(Vertex, color) },
|
|
||||||
vk::VertexInputAttributeDescription { 2, 0, vk::Format::eR32G32Sfloat, offsetof(Vertex, tex_coord) }
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @brief Compares two vertices for equality.
|
|
||||||
*
|
|
||||||
* @param other The vertex to compare with.
|
|
||||||
* @return bool True if vertices are identical in position, color, and texture coordinates.
|
|
||||||
*/
|
|
||||||
fn operator==(const Vertex& other) const->bool {
|
|
||||||
return pos == other.pos && color == other.color && tex_coord == other.tex_coord;
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
namespace std {
|
|
||||||
/**
|
|
||||||
* @brief Hash function specialization for Vertex type.
|
|
||||||
*
|
|
||||||
* This specialization allows Vertex objects to be used as keys in unordered containers.
|
|
||||||
* The hash combines position, color, and texture coordinate data using bit operations
|
|
||||||
* to create a unique hash value.
|
|
||||||
*/
|
|
||||||
template <>
|
|
||||||
struct hash<Vertex> {
|
|
||||||
/**
|
|
||||||
* @brief Computes hash value for a vertex.
|
|
||||||
*
|
|
||||||
* @param vertex The vertex to hash.
|
|
||||||
* @return size_t Hash value combining all vertex attributes.
|
|
||||||
*/
|
|
||||||
fn operator()(Vertex const& vertex) const->size_t {
|
|
||||||
return ((hash<vec3>()(vertex.pos) ^ (hash<vec3>()(vertex.color) << 1)) >> 1) ^
|
|
||||||
(hash<vec2>()(vertex.tex_coord) << 1);
|
|
||||||
}
|
|
||||||
};
|
|
||||||
}
|
|
2542
src/main.cpp
2542
src/main.cpp
File diff suppressed because it is too large
Load diff
1709
src/util/magic_enum.hpp
Normal file
1709
src/util/magic_enum.hpp
Normal file
File diff suppressed because it is too large
Load diff
128
src/util/types.h
Normal file
128
src/util/types.h
Normal file
|
@ -0,0 +1,128 @@
|
||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include <cstddef>
|
||||||
|
#include <cstdint>
|
||||||
|
#include <string>
|
||||||
|
|
||||||
|
#define fn auto
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @typedef u8
|
||||||
|
* @brief Represents an 8-bit unsigned integer.
|
||||||
|
*
|
||||||
|
* This type alias is used for 8-bit unsigned integers, ranging from 0 to 255.
|
||||||
|
* It is based on the std::uint8_t type.
|
||||||
|
*/
|
||||||
|
using u8 = std::uint8_t;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @typedef u16
|
||||||
|
* @brief Represents a 16-bit unsigned integer.
|
||||||
|
*
|
||||||
|
* This type alias is used for 16-bit unsigned integers, ranging from 0 to 65,535.
|
||||||
|
* It is based on the std::uint16_t type.
|
||||||
|
*/
|
||||||
|
using u16 = std::uint16_t;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @typedef u32
|
||||||
|
* @brief Represents a 32-bit unsigned integer.
|
||||||
|
*
|
||||||
|
* This type alias is used for 32-bit unsigned integers, ranging from 0 to 4,294,967,295.
|
||||||
|
* It is based on the std::uint32_t type.
|
||||||
|
*/
|
||||||
|
using u32 = std::uint32_t;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @typedef u64
|
||||||
|
* @brief Represents a 64-bit unsigned integer.
|
||||||
|
*
|
||||||
|
* This type alias is used for 64-bit unsigned integers, ranging from 0 to
|
||||||
|
* 18,446,744,073,709,551,615. It is based on the std::uint64_t type.
|
||||||
|
*/
|
||||||
|
using u64 = std::uint64_t;
|
||||||
|
|
||||||
|
// Type Aliases for Signed Integers
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @typedef i8
|
||||||
|
* @brief Represents an 8-bit signed integer.
|
||||||
|
*
|
||||||
|
* This type alias is used for 8-bit signed integers, ranging from -128 to 127.
|
||||||
|
* It is based on the std::int8_t type.
|
||||||
|
*/
|
||||||
|
using i8 = std::int8_t;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @typedef i16
|
||||||
|
* @brief Represents a 16-bit signed integer.
|
||||||
|
*
|
||||||
|
* This type alias is used for 16-bit signed integers, ranging from -32,768 to 32,767.
|
||||||
|
* It is based on the std::int16_t type.
|
||||||
|
*/
|
||||||
|
using i16 = std::int16_t;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @typedef i32
|
||||||
|
* @brief Represents a 32-bit signed integer.
|
||||||
|
*
|
||||||
|
* This type alias is used for 32-bit signed integers, ranging from -2,147,483,648 to 2,147,483,647.
|
||||||
|
* It is based on the std::int32_t type.
|
||||||
|
*/
|
||||||
|
using i32 = std::int32_t;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @typedef i64
|
||||||
|
* @brief Represents a 64-bit signed integer.
|
||||||
|
*
|
||||||
|
* This type alias is used for 64-bit signed integers, ranging from -9,223,372,036,854,775,808 to
|
||||||
|
* 9,223,372,036,854,775,807. It is based on the std::int64_t type.
|
||||||
|
*/
|
||||||
|
using i64 = std::int64_t;
|
||||||
|
|
||||||
|
// Type Aliases for Floating-Point Numbers
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @typedef f32
|
||||||
|
* @brief Represents a 32-bit floating-point number.
|
||||||
|
*
|
||||||
|
* This type alias is used for 32-bit floating-point numbers, which follow the IEEE 754 standard.
|
||||||
|
* It is based on the float type.
|
||||||
|
*/
|
||||||
|
using f32 = float;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @typedef f64
|
||||||
|
* @brief Represents a 64-bit floating-point number.
|
||||||
|
*
|
||||||
|
* This type alias is used for 64-bit floating-point numbers, which follow the IEEE 754 standard.
|
||||||
|
* It is based on the double type.
|
||||||
|
*/
|
||||||
|
using f64 = double;
|
||||||
|
|
||||||
|
// Type Aliases for Size Types
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @typedef usize
|
||||||
|
* @brief Represents an unsigned size type.
|
||||||
|
*
|
||||||
|
* This type alias is used for representing the size of objects in bytes.
|
||||||
|
* It is based on the std::size_t type, which is the result type of the sizeof operator.
|
||||||
|
*/
|
||||||
|
using usize = std::size_t;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @typedef isize
|
||||||
|
* @brief Represents a signed size type.
|
||||||
|
*
|
||||||
|
* This type alias is used for representing pointer differences.
|
||||||
|
* It is based on the std::ptrdiff_t type, which is the signed integer type returned when
|
||||||
|
* subtracting two pointers.
|
||||||
|
*/
|
||||||
|
using isize = std::ptrdiff_t;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @typedef string
|
||||||
|
* @brief Represents a string.
|
||||||
|
*/
|
||||||
|
using string = std::string;
|
Binary file not shown.
Before Width: | Height: | Size: 940 KiB |
Loading…
Reference in a new issue