Compare commits
10 commits
76e38a87e2
...
d35a31e4bc
Author | SHA1 | Date | |
---|---|---|---|
Tulpen | d35a31e4bc | ||
Tulpen | e74a21b365 | ||
Tulpen | 7d3ccdb15f | ||
Tulpen | 5fc2069fc0 | ||
Tulpen | 1cca66b66f | ||
Tulpen | a5380c4417 | ||
Tulpen | 493ab7eb6e | ||
Tulpen | 0e6c5d63c3 | ||
Tulpen | 205919e1a9 | ||
Tulpen | b00ece6f2a |
0
.gitmodules
vendored
Normal file
0
.gitmodules
vendored
Normal file
|
@ -13,9 +13,8 @@ To compile Hibis, you will need:
|
||||||
- Meson-compatible compiler (recommended: Ninja or ccache + G++)
|
- Meson-compatible compiler (recommended: Ninja or ccache + G++)
|
||||||
- A few minutes of your time
|
- A few minutes of your time
|
||||||
<!-- -->
|
<!-- -->
|
||||||
Each non-core library also has specific dependencies, including:
|
Each module also has specific dependencies, which include:
|
||||||
- (RSDL) SDL2
|
- (RGLCore) OpenGL drivers
|
||||||
- (RSDL) SDL2_ttf
|
|
||||||
<!-- -->
|
<!-- -->
|
||||||
Linux users should be able to get all of these (except time) from their package manager.<br>
|
Linux users should be able to get all of these (except time) from their package manager.<br>
|
||||||
Windows users will need to use vcpkg or similar to easily get these dependencies.
|
Windows users will need to use vcpkg or similar to easily get these dependencies.
|
||||||
|
@ -25,4 +24,4 @@ Run `compile.sh` in the folder you cloned Hibis into.
|
||||||
#### Customising
|
#### Customising
|
||||||
In the folder you cloned Hibis into, run `meson build` to create a folder where the magic happens.<br>
|
In the folder you cloned Hibis into, run `meson build` to create a folder where the magic happens.<br>
|
||||||
Move into the build directory using CD and you can now use `meson configure` to adjust compile options.<br>
|
Move into the build directory using CD and you can now use `meson configure` to adjust compile options.<br>
|
||||||
Once you have configured everything, run `meson compile` in the build directory and it should finish successfully.
|
Once you have configured everything, run `ninja` or `meson compile` in the build directory and it should finish successfully.
|
||||||
|
|
66
TODO.md
66
TODO.md
|
@ -1,40 +1,52 @@
|
||||||
# Hibis Engine - Feature TODOs
|
# Hibis Engine - Feature TODOs
|
||||||
## Core
|
## Core Module
|
||||||
- [ ] BetterC compatibility
|
### Base
|
||||||
- [ ] Texture loading
|
|
||||||
- [ ] Model loading
|
|
||||||
- [ ] Base Renderer class
|
- [ ] Base Renderer class
|
||||||
- [ ] Base Physics class
|
- [ ] Base Physics class
|
||||||
- [ ] Base Node class
|
- [ ] Figure out how scenes should be handled
|
||||||
- [ ] Base UI Node class
|
- [ ] Split other sections into seperate modules (renderers, physics)
|
||||||
- [ ] Base Audio Playback Node class
|
- [ ] Application class (where the main loop is handled)
|
||||||
- [ ] Split other sections into seperate libraries (renderers, physics)
|
### 2D/3D
|
||||||
## Audio
|
- [ ] Texture loading
|
||||||
|
- [ ] Model loading
|
||||||
|
## Audio Module
|
||||||
|
### Base
|
||||||
- [ ] OGG and WAV support (do first)
|
- [ ] OGG and WAV support (do first)
|
||||||
- [ ] MP3 (later)
|
- [ ] Soundfont and Midi support (ideal)
|
||||||
- [ ] Audio playing nodes
|
- [ ] MP3 (later, can just ignore)
|
||||||
## Hibis UI (NEED A NAME)
|
- [ ] Loop audio (with timepoints)
|
||||||
|
- [ ] Modify sound/pitch of outputted audio
|
||||||
|
### 2D/3D
|
||||||
|
- [ ] Position-based audio
|
||||||
|
## Hibis UI (NEEDS A NAME)
|
||||||
- [ ] GUI creation
|
- [ ] GUI creation
|
||||||
- [ ] Text Object (rendering via SDL2_TTF (RSDL) or FreeType2 (EVERYTHING ELSE))
|
- [ ] Text Object (rendering via FreeType2)
|
||||||
## RSDL (2D ONLY)
|
## Aethiopicus (Vulkan renderer, 2D and 3D)
|
||||||
- [X] Window creation
|
### Base
|
||||||
- [X] Renderer creation
|
- [X] Window creation (via GLFW)
|
||||||
- [X] Render present
|
- [ ] Basic deferred renderering system
|
||||||
- [X] Clear renderer
|
|
||||||
- [ ] Texture drawing
|
|
||||||
- [ ] Draw section of texture from sprite sheet
|
|
||||||
## RVK (2D AND 3D, NEEDS A NAME)
|
|
||||||
- [ ] Window creation (either using GLFW or a custom window creator)
|
|
||||||
- [ ] Basic deferred renderer
|
|
||||||
- [ ] Render present
|
- [ ] Render present
|
||||||
- [ ] Clear renderer
|
- [ ] Clear renderer
|
||||||
|
- [ ] Figure out how the renderer should even work
|
||||||
|
- [ ] (DO FOR A VERY LATE VERSION) Replace GLFW with a custom window creator to reduce bloat (I will not be using most of GLFW's shit)
|
||||||
|
### 2D
|
||||||
- [ ] Texture drawing
|
- [ ] Texture drawing
|
||||||
|
- [ ] TileMaps
|
||||||
|
- [ ] TileMap Culling
|
||||||
|
### 3D
|
||||||
- [ ] Model drawing
|
- [ ] Model drawing
|
||||||
- [ ] Culling (hiding what you can't see)
|
- [ ] Model Culling (hiding what you can't see)
|
||||||
- [ ] Level of Detail (3D ONLY, might not impl (developers using Hibis could do this themselves))
|
- [ ] Level of Detail (3D ONLY)
|
||||||
## Hibis Physics (NEED A NAME)
|
## Hibis Physics (NEED A NAME)
|
||||||
|
### Base
|
||||||
- [ ] Physics Shape creation
|
- [ ] Physics Shape creation
|
||||||
|
- [ ] Option to *allow* for Physics Shapes to be able to be drawn (on by default, both compile time and run time, turn off at compile time via a flag (`-DNoPhysShapeDraw=1`?)) (official releases will include PhysShapeDraw capabilities)
|
||||||
|
### 2D/3D
|
||||||
|
- [ ] Raycasting
|
||||||
- [ ] Collision via raycasting
|
- [ ] Collision via raycasting
|
||||||
- [ ] Option to *allow* for Physics Shapes to be able to be drawn (on by default, both compile time and run time, turn off at compile time via version(`-version=NoPhysShapeDraw`?))
|
- [ ] Space type-specific shapes
|
||||||
## Examples
|
## Examples
|
||||||
- [ ] Example Game: Megaman clone
|
- [ ] Example: UI test
|
||||||
|
- [ ] Example Game: Megaman clone (external repo)
|
||||||
|
- [ ] Stress test using sprites
|
||||||
|
- [ ] Stress test using models
|
||||||
|
|
50
core/application/application.cpp
Normal file
50
core/application/application.cpp
Normal file
|
@ -0,0 +1,50 @@
|
||||||
|
#include "enginever.hpp"
|
||||||
|
#include "engine.hpp"
|
||||||
|
#include "../pragmautil.hpp"
|
||||||
|
#include <core/graphics/drawable.hpp>
|
||||||
|
|
||||||
|
namespace hibis {
|
||||||
|
Engine::Engine(Renderer* renderer, LoggerCallback logger) {
|
||||||
|
this->mRenderer = renderer;
|
||||||
|
mLoggerCallback = logger;
|
||||||
|
mPreviousProcessTick = std::chrono::steady_clock::now();
|
||||||
|
mPreviousPhysicsProcessTick = std::chrono::steady_clock::now();
|
||||||
|
|
||||||
|
//watch.start();
|
||||||
|
|
||||||
|
mLoggerCallback(Information, "Starting FreeType2 library...");
|
||||||
|
int err = FT_Init_FreeType(&mFreeTypeLibrary);
|
||||||
|
if (err) {
|
||||||
|
mLoggerCallback(Fatal, "Failed to init FreeType2");
|
||||||
|
}
|
||||||
|
|
||||||
|
mLoggerCallback(Information, "Started Hibis [using v" + (std::string)getEngineVersion() + "]!");
|
||||||
|
}
|
||||||
|
|
||||||
|
Engine::~Engine() {
|
||||||
|
//watch.stop();
|
||||||
|
}
|
||||||
|
|
||||||
|
float Engine::calculateDelta(bool isPhysics) {
|
||||||
|
const std::chrono::time_point<std::chrono::steady_clock> current = std::chrono::steady_clock::now();
|
||||||
|
if (isPhysics) {
|
||||||
|
auto delta = std::chrono::duration<float>(current - mPreviousPhysicsProcessTick);
|
||||||
|
|
||||||
|
return delta.count();
|
||||||
|
} else {
|
||||||
|
auto delta = std::chrono::duration<float>(current - mPreviousProcessTick);
|
||||||
|
|
||||||
|
return delta.count();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void Engine::updateDelta(bool isPhysics) {
|
||||||
|
if (isPhysics) {
|
||||||
|
mPreviousPhysicsProcessTick = std::chrono::steady_clock::now();
|
||||||
|
} else {
|
||||||
|
mPreviousProcessTick = std::chrono::steady_clock::now();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
const char* Engine::getEngineVersion() { return HIBIS_VERSION; }
|
||||||
|
}
|
|
@ -2,10 +2,10 @@
|
||||||
|
|
||||||
#include <vector>
|
#include <vector>
|
||||||
#include <chrono>
|
#include <chrono>
|
||||||
|
#include <freetype/freetype.h>
|
||||||
|
|
||||||
#include "../renderer/renderer.hpp"
|
#include "../renderer/renderer.hpp"
|
||||||
#include "../callback.hpp"
|
#include "../callback.hpp"
|
||||||
#include "../node/node.hpp"
|
|
||||||
|
|
||||||
namespace hibis {
|
namespace hibis {
|
||||||
class Engine {
|
class Engine {
|
||||||
|
@ -13,20 +13,19 @@ namespace hibis {
|
||||||
Engine(Renderer* renderer, LoggerCallback logger);
|
Engine(Renderer* renderer, LoggerCallback logger);
|
||||||
~Engine();
|
~Engine();
|
||||||
|
|
||||||
void runNodeProcesses();
|
float calculateDelta(bool isPhysics);
|
||||||
|
|
||||||
void runNodePhysicsProcesses();
|
void updateDelta(bool isPhysics);
|
||||||
|
|
||||||
void drawNodes();
|
|
||||||
|
|
||||||
const char* getEngineVersion();
|
const char* getEngineVersion();
|
||||||
|
|
||||||
|
FT_Library mFreeTypeLibrary;
|
||||||
private:
|
private:
|
||||||
Renderer* mRenderer;
|
Renderer* mRenderer;
|
||||||
//StopWatch watch;
|
//StopWatch watch;
|
||||||
std::chrono::time_point<std::chrono::steady_clock> mPreviousProcessTick;
|
std::chrono::time_point<std::chrono::steady_clock> mPreviousProcessTick;
|
||||||
std::chrono::time_point<std::chrono::steady_clock> mPreviousPhysicsProcessTick;
|
std::chrono::time_point<std::chrono::steady_clock> mPreviousPhysicsProcessTick;
|
||||||
|
|
||||||
std::vector<Node*> mNodeList;
|
|
||||||
LoggerCallback mLoggerCallback;
|
LoggerCallback mLoggerCallback;
|
||||||
};
|
};
|
||||||
}
|
}
|
|
@ -1,56 +0,0 @@
|
||||||
#include "enginever.hpp"
|
|
||||||
#include "engine.hpp"
|
|
||||||
#include "../pragmautil.hpp"
|
|
||||||
#include <core/graphics/drawable.hpp>
|
|
||||||
|
|
||||||
namespace hibis {
|
|
||||||
Engine::Engine(Renderer* renderer, LoggerCallback logger) {
|
|
||||||
this->mRenderer = renderer;
|
|
||||||
mLoggerCallback = logger;
|
|
||||||
mPreviousProcessTick = std::chrono::steady_clock::now();
|
|
||||||
mPreviousPhysicsProcessTick = std::chrono::steady_clock::now();
|
|
||||||
|
|
||||||
//watch.start();
|
|
||||||
|
|
||||||
this->mLoggerCallback(Information, "Started Hibis [using v" + (std::string)getEngineVersion() + "]!");
|
|
||||||
}
|
|
||||||
|
|
||||||
Engine::~Engine() {
|
|
||||||
//watch.stop();
|
|
||||||
}
|
|
||||||
|
|
||||||
void Engine::runNodeProcesses() {
|
|
||||||
TODO("check if delta calc is correct")
|
|
||||||
TODO("get workaround for MSVC being fucking stupid (std::chrono + std::this_thread)")
|
|
||||||
const std::chrono::time_point<std::chrono::steady_clock> current = std::chrono::steady_clock::now();
|
|
||||||
auto delta = std::chrono::duration<float>(current - mPreviousProcessTick);
|
|
||||||
|
|
||||||
for (Node* node : mNodeList) {
|
|
||||||
node->process(delta.count());
|
|
||||||
}
|
|
||||||
|
|
||||||
mPreviousProcessTick = std::chrono::steady_clock::now();
|
|
||||||
}
|
|
||||||
|
|
||||||
void Engine::runNodePhysicsProcesses() {
|
|
||||||
const std::chrono::time_point<std::chrono::steady_clock> current = std::chrono::steady_clock::now();
|
|
||||||
auto delta = std::chrono::duration<float>(current - mPreviousProcessTick);
|
|
||||||
|
|
||||||
for (Node* node : mNodeList) {
|
|
||||||
node->physicsProcess(delta.count());
|
|
||||||
}
|
|
||||||
|
|
||||||
mPreviousProcessTick = std::chrono::steady_clock::now();
|
|
||||||
}
|
|
||||||
|
|
||||||
void Engine::drawNodes() {
|
|
||||||
TODO("check if this works")
|
|
||||||
for (Node* node : mNodeList) {
|
|
||||||
if (Drawable* drawNode = (Drawable*)&node) {
|
|
||||||
drawNode->draw(mRenderer);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
const char* Engine::getEngineVersion() { return HIBIS_VERSION; }
|
|
||||||
}
|
|
26
core/input/manager.cpp
Normal file
26
core/input/manager.cpp
Normal file
|
@ -0,0 +1,26 @@
|
||||||
|
#include "manager.hpp"
|
||||||
|
|
||||||
|
namespace hibis {
|
||||||
|
InputManager::InputManager() {}
|
||||||
|
InputManager::~InputManager() {}
|
||||||
|
|
||||||
|
bool InputManager::isKeyPressed(int scancode) {
|
||||||
|
if (mKeyScancodes.find(scancode) != mKeyScancodes.end()) return (mKeyScancodes[scancode] == Pressed || mKeyScancodes[scancode] == Held);
|
||||||
|
else return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool InputManager::isKeyJustPressed(int scancode) {
|
||||||
|
if (mKeyScancodes.find(scancode) != mKeyScancodes.end()) return mKeyScancodes[scancode] == Pressed;
|
||||||
|
else return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool InputManager::isKeyJustReleased(int scancode) {
|
||||||
|
if (mKeyScancodes.find(scancode) != mKeyScancodes.end()) return mKeyScancodes[scancode] == Released;
|
||||||
|
else return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
void InputManager::setKeyPressState(int scancode, PressTypes type) {
|
||||||
|
mKeyScancodes[scancode] = type;
|
||||||
|
}
|
||||||
|
}
|
21
core/input/manager.hpp
Normal file
21
core/input/manager.hpp
Normal file
|
@ -0,0 +1,21 @@
|
||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include <map>
|
||||||
|
|
||||||
|
#include "presstypes.hpp"
|
||||||
|
|
||||||
|
namespace hibis {
|
||||||
|
class InputManager final {
|
||||||
|
public:
|
||||||
|
InputManager();
|
||||||
|
~InputManager();
|
||||||
|
|
||||||
|
bool isKeyPressed(int scancode);
|
||||||
|
bool isKeyJustPressed(int scancode);
|
||||||
|
bool isKeyJustReleased(int scancode);
|
||||||
|
|
||||||
|
void setKeyPressState(int scancode, PressTypes type);
|
||||||
|
private:
|
||||||
|
std::map<int, PressTypes> mKeyScancodes;
|
||||||
|
};
|
||||||
|
}
|
11
core/input/presstypes.hpp
Normal file
11
core/input/presstypes.hpp
Normal file
|
@ -0,0 +1,11 @@
|
||||||
|
#pragma once
|
||||||
|
|
||||||
|
namespace hibis {
|
||||||
|
enum PressTypes {
|
||||||
|
Unpressed,
|
||||||
|
Pressed,
|
||||||
|
Held,
|
||||||
|
Released,
|
||||||
|
Unknown = __INT32_MAX__
|
||||||
|
};
|
||||||
|
}
|
|
@ -5,7 +5,7 @@ namespace hibis {
|
||||||
// - Image
|
// - Image
|
||||||
/// RGBA value struct. Value is between 0 and 255
|
/// RGBA value struct. Value is between 0 and 255
|
||||||
struct Color {
|
struct Color {
|
||||||
unsigned char r, g, b, a;
|
double r, g, b, a;
|
||||||
};
|
};
|
||||||
|
|
||||||
// - 2D
|
// - 2D
|
||||||
|
|
|
@ -1,9 +0,0 @@
|
||||||
#pragma once
|
|
||||||
|
|
||||||
namespace hibis {
|
|
||||||
class Node {
|
|
||||||
public:
|
|
||||||
virtual void process(float delta) {}
|
|
||||||
virtual void physicsProcess(float delta) {}
|
|
||||||
};
|
|
||||||
}
|
|
5
core/renderer/renderer.cpp
Normal file
5
core/renderer/renderer.cpp
Normal file
|
@ -0,0 +1,5 @@
|
||||||
|
#include "renderer.hpp"
|
||||||
|
|
||||||
|
namespace hibis {
|
||||||
|
Renderer::Renderer(LoggerCallback logger) : mLogger(logger), mInputManager() {}
|
||||||
|
}
|
|
@ -1,19 +1,27 @@
|
||||||
#pragma once
|
#pragma once
|
||||||
|
|
||||||
|
#include <string>
|
||||||
|
|
||||||
|
#include "../input/manager.hpp"
|
||||||
#include "../math/types.hpp"
|
#include "../math/types.hpp"
|
||||||
#include "../resources/resource.hpp"
|
#include "../resources/resource.hpp"
|
||||||
#include "../resources/texture.hpp"
|
#include "../resources/shader.hpp"
|
||||||
#include <string>
|
#include "../callback.hpp"
|
||||||
|
|
||||||
namespace hibis {
|
namespace hibis {
|
||||||
class Renderer {
|
class Renderer {
|
||||||
public:
|
public:
|
||||||
|
Renderer(LoggerCallback logger);
|
||||||
|
|
||||||
// Draw
|
// Draw
|
||||||
virtual void clearScreen(Color col) = 0;
|
virtual void clearScreen(Color col) = 0;
|
||||||
virtual void renderCurrent() = 0;
|
virtual void renderCurrent() = 0;
|
||||||
|
|
||||||
virtual void drawText(Resource* resource, std::string text, IntVec2 pos, Color color) = 0;
|
virtual void drawText(Resource* resource, std::string text, IntVec2 pos, Color color) = 0;
|
||||||
virtual void drawTexture(Texture* resource, float scale, IntVec2 pos) = 0;
|
virtual void drawTexture(Resource* resource, IntRect size) = 0;
|
||||||
|
|
||||||
|
virtual void useShader(Shader* shader, Point2D points[3]) = 0;
|
||||||
|
virtual void stopUsingShaders() = 0;
|
||||||
|
|
||||||
// Pre and Post draw
|
// Pre and Post draw
|
||||||
virtual void preDraw() = 0;
|
virtual void preDraw() = 0;
|
||||||
|
@ -23,8 +31,14 @@ namespace hibis {
|
||||||
virtual void update() = 0;
|
virtual void update() = 0;
|
||||||
|
|
||||||
// Util
|
// Util
|
||||||
|
virtual void compileShader(Shader* shader) = 0;
|
||||||
|
virtual void toggleWireframe() = 0;
|
||||||
virtual void setWindowTitle(std::string title) = 0;
|
virtual void setWindowTitle(std::string title) = 0;
|
||||||
|
|
||||||
bool mKeepOpen = true;
|
bool mKeepOpen = true;
|
||||||
|
LoggerCallback mLogger;
|
||||||
|
InputManager mInputManager;
|
||||||
|
protected:
|
||||||
|
bool mIsWireframeMode = false;
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
22
core/resources/font.cpp
Normal file
22
core/resources/font.cpp
Normal file
|
@ -0,0 +1,22 @@
|
||||||
|
#include "font.hpp"
|
||||||
|
#include "../pragmautil.hpp"
|
||||||
|
|
||||||
|
namespace hibis {
|
||||||
|
Font::Font(FT_Library& lib, const char* path, u_int16_t size) {
|
||||||
|
int err = FT_New_Face(lib, path, 0, &mFontFace);
|
||||||
|
if (err) {
|
||||||
|
TODO("Error message involving `int err`");
|
||||||
|
}
|
||||||
|
|
||||||
|
setFontSize(size);
|
||||||
|
mPath = path;
|
||||||
|
}
|
||||||
|
|
||||||
|
Font::~Font() {}
|
||||||
|
|
||||||
|
void Font::setFontSize(u_int16_t newSize) {
|
||||||
|
FT_Set_Pixel_Sizes(mFontFace, 0, newSize);
|
||||||
|
mCurrentSize = newSize;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
22
core/resources/font.hpp
Normal file
22
core/resources/font.hpp
Normal file
|
@ -0,0 +1,22 @@
|
||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include <freetype/freetype.h>
|
||||||
|
|
||||||
|
#include "resource.hpp"
|
||||||
|
|
||||||
|
namespace hibis {
|
||||||
|
class Font : public Resource {
|
||||||
|
public:
|
||||||
|
Font(FT_Library& lib, const char* path, u_int16_t size);
|
||||||
|
~Font();
|
||||||
|
|
||||||
|
void setFontSize(u_int16_t newSize);
|
||||||
|
u_int16_t getCurrentSize() { return mCurrentSize; }
|
||||||
|
|
||||||
|
const char* mPath;
|
||||||
|
private:
|
||||||
|
FT_Face mFontFace;
|
||||||
|
|
||||||
|
u_int16_t mCurrentSize;
|
||||||
|
};
|
||||||
|
}
|
10
core/resources/shader.cpp
Normal file
10
core/resources/shader.cpp
Normal file
|
@ -0,0 +1,10 @@
|
||||||
|
#include "shader.hpp"
|
||||||
|
|
||||||
|
namespace hibis {
|
||||||
|
Shader::Shader(std::string vertexShaderPath, std::string fragShaderPath) {
|
||||||
|
mShaderPaths[0] = vertexShaderPath;
|
||||||
|
mShaderPaths[1] = fragShaderPath;
|
||||||
|
}
|
||||||
|
|
||||||
|
Shader::~Shader() {}
|
||||||
|
}
|
18
core/resources/shader.hpp
Normal file
18
core/resources/shader.hpp
Normal file
|
@ -0,0 +1,18 @@
|
||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include <string>
|
||||||
|
|
||||||
|
#include "resource.hpp"
|
||||||
|
|
||||||
|
namespace hibis {
|
||||||
|
class Shader : public Resource {
|
||||||
|
public:
|
||||||
|
Shader(std::string vertexShaderPath, std::string fragShaderPath);
|
||||||
|
~Shader();
|
||||||
|
unsigned int mShaderProgram = 0;
|
||||||
|
unsigned int mShaderVAO = 0;
|
||||||
|
unsigned int mShaderVBO = 0;
|
||||||
|
|
||||||
|
std::string mShaderPaths[2];
|
||||||
|
};
|
||||||
|
}
|
|
@ -1,20 +0,0 @@
|
||||||
#include "texture.hpp"
|
|
||||||
|
|
||||||
namespace hibis {
|
|
||||||
Texture::Texture(char* path) : mData(), mImageWidth(0), mImageHeight(0), mBuffer() {
|
|
||||||
unsigned int error = lodepng::load_file(mBuffer, path);
|
|
||||||
if (error) {
|
|
||||||
TODO("error message")
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
error = lodepng::decode(mData, mImageWidth, mImageHeight, mBuffer);
|
|
||||||
|
|
||||||
if (error) {
|
|
||||||
TODO("error message")
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
Texture::~Texture() {}
|
|
||||||
}
|
|
|
@ -1,21 +0,0 @@
|
||||||
#pragma once
|
|
||||||
|
|
||||||
#include <vector>
|
|
||||||
|
|
||||||
#include <lodepng/lodepng.h>
|
|
||||||
|
|
||||||
#include "resource.hpp"
|
|
||||||
#include "../pragmautil.hpp"
|
|
||||||
|
|
||||||
namespace hibis {
|
|
||||||
TODO("Make this function")
|
|
||||||
class Texture : Resource {
|
|
||||||
public:
|
|
||||||
Texture(char* path);
|
|
||||||
~Texture();
|
|
||||||
std::vector<unsigned char> mData;
|
|
||||||
unsigned int mImageWidth, mImageHeight;
|
|
||||||
private:
|
|
||||||
std::vector<unsigned char> mBuffer;
|
|
||||||
};
|
|
||||||
}
|
|
159
external/glfw3webgpu/glfw3webgpu.c
vendored
Normal file
159
external/glfw3webgpu/glfw3webgpu.c
vendored
Normal file
|
@ -0,0 +1,159 @@
|
||||||
|
/**
|
||||||
|
* This is an extension of GLFW for WebGPU, abstracting away the details of
|
||||||
|
* OS-specific operations.
|
||||||
|
*
|
||||||
|
* This file is part of the "Learn WebGPU for C++" book.
|
||||||
|
* https://eliemichel.github.io/LearnWebGPU
|
||||||
|
*
|
||||||
|
* Most of this code comes from the wgpu-native triangle example:
|
||||||
|
* https://github.com/gfx-rs/wgpu-native/blob/master/examples/triangle/main.c
|
||||||
|
*
|
||||||
|
* MIT License
|
||||||
|
* Copyright (c) 2022-2023 Elie Michel and the wgpu-native authors
|
||||||
|
*
|
||||||
|
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||||
|
* of this software and associated documentation files (the "Software"), to deal
|
||||||
|
* in the Software without restriction, including without limitation the rights
|
||||||
|
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||||
|
* copies of the Software, and to permit persons to whom the Software is
|
||||||
|
* furnished to do so, subject to the following conditions:
|
||||||
|
*
|
||||||
|
* The above copyright notice and this permission notice shall be included in all
|
||||||
|
* copies or substantial portions of the Software.
|
||||||
|
*
|
||||||
|
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||||
|
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||||
|
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||||
|
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||||
|
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||||
|
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||||
|
* SOFTWARE.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include "glfw3webgpu.h"
|
||||||
|
|
||||||
|
#include <webgpu/webgpu.h>
|
||||||
|
|
||||||
|
#define WGPU_TARGET_MACOS 1
|
||||||
|
#define WGPU_TARGET_LINUX_X11 2
|
||||||
|
#define WGPU_TARGET_WINDOWS 3
|
||||||
|
#define WGPU_TARGET_LINUX_WAYLAND 4
|
||||||
|
|
||||||
|
#if defined(_WIN32)
|
||||||
|
#define WGPU_TARGET WGPU_TARGET_WINDOWS
|
||||||
|
#elif defined(__APPLE__)
|
||||||
|
#define WGPU_TARGET WGPU_TARGET_MACOS
|
||||||
|
#else
|
||||||
|
#define WGPU_TARGET WGPU_TARGET_LINUX_X11
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#if WGPU_TARGET == WGPU_TARGET_MACOS
|
||||||
|
#include <Foundation/Foundation.h>
|
||||||
|
#include <QuartzCore/CAMetalLayer.h>
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#include <GLFW/glfw3.h>
|
||||||
|
#if WGPU_TARGET == WGPU_TARGET_MACOS
|
||||||
|
#define GLFW_EXPOSE_NATIVE_COCOA
|
||||||
|
#elif WGPU_TARGET == WGPU_TARGET_LINUX_X11
|
||||||
|
#define GLFW_EXPOSE_NATIVE_X11
|
||||||
|
#elif WGPU_TARGET == WGPU_TARGET_LINUX_WAYLAND
|
||||||
|
#define GLFW_EXPOSE_NATIVE_WAYLAND
|
||||||
|
#elif WGPU_TARGET == WGPU_TARGET_WINDOWS
|
||||||
|
#define GLFW_EXPOSE_NATIVE_WIN32
|
||||||
|
#endif
|
||||||
|
#include <GLFW/glfw3native.h>
|
||||||
|
|
||||||
|
WGPUSurface glfwGetWGPUSurface(WGPUInstance instance, GLFWwindow* window) {
|
||||||
|
#if WGPU_TARGET == WGPU_TARGET_MACOS
|
||||||
|
{
|
||||||
|
id metal_layer = NULL;
|
||||||
|
NSWindow* ns_window = glfwGetCocoaWindow(window);
|
||||||
|
[ns_window.contentView setWantsLayer : YES] ;
|
||||||
|
metal_layer = [CAMetalLayer layer];
|
||||||
|
[ns_window.contentView setLayer : metal_layer] ;
|
||||||
|
return wgpuInstanceCreateSurface(
|
||||||
|
instance,
|
||||||
|
&(WGPUSurfaceDescriptor){
|
||||||
|
.label = NULL,
|
||||||
|
.nextInChain =
|
||||||
|
(const WGPUChainedStruct*)&(
|
||||||
|
WGPUSurfaceDescriptorFromMetalLayer) {
|
||||||
|
.chain =
|
||||||
|
(WGPUChainedStruct){
|
||||||
|
.next = NULL,
|
||||||
|
.sType = WGPUSType_SurfaceDescriptorFromMetalLayer,
|
||||||
|
},
|
||||||
|
.layer = metal_layer,
|
||||||
|
},
|
||||||
|
});
|
||||||
|
}
|
||||||
|
#elif WGPU_TARGET == WGPU_TARGET_LINUX_X11
|
||||||
|
{
|
||||||
|
Display* x11_display = glfwGetX11Display();
|
||||||
|
Window x11_window = glfwGetX11Window(window);
|
||||||
|
return wgpuInstanceCreateSurface(
|
||||||
|
instance,
|
||||||
|
&(WGPUSurfaceDescriptor){
|
||||||
|
.label = NULL,
|
||||||
|
.nextInChain =
|
||||||
|
(const WGPUChainedStruct*)&(
|
||||||
|
WGPUSurfaceDescriptorFromXlibWindow) {
|
||||||
|
.chain =
|
||||||
|
(WGPUChainedStruct){
|
||||||
|
.next = NULL,
|
||||||
|
.sType = WGPUSType_SurfaceDescriptorFromXlibWindow,
|
||||||
|
},
|
||||||
|
.display = x11_display,
|
||||||
|
.window = x11_window,
|
||||||
|
},
|
||||||
|
});
|
||||||
|
}
|
||||||
|
#elif WGPU_TARGET == WGPU_TARGET_LINUX_WAYLAND
|
||||||
|
{
|
||||||
|
struct wl_display* wayland_display = glfwGetWaylandDisplay();
|
||||||
|
struct wl_surface* wayland_surface = glfwGetWaylandWindow(window);
|
||||||
|
return wgpuInstanceCreateSurface(
|
||||||
|
instance,
|
||||||
|
&(WGPUSurfaceDescriptor){
|
||||||
|
.label = NULL,
|
||||||
|
.nextInChain =
|
||||||
|
(const WGPUChainedStruct*)&(
|
||||||
|
WGPUSurfaceDescriptorFromWaylandSurface) {
|
||||||
|
.chain =
|
||||||
|
(WGPUChainedStruct){
|
||||||
|
.next = NULL,
|
||||||
|
.sType =
|
||||||
|
WGPUSType_SurfaceDescriptorFromWaylandSurface,
|
||||||
|
},
|
||||||
|
.display = wayland_display,
|
||||||
|
.surface = wayland_surface,
|
||||||
|
},
|
||||||
|
});
|
||||||
|
}
|
||||||
|
#elif WGPU_TARGET == WGPU_TARGET_WINDOWS
|
||||||
|
{
|
||||||
|
HWND hwnd = glfwGetWin32Window(window);
|
||||||
|
HINSTANCE hinstance = GetModuleHandle(NULL);
|
||||||
|
return wgpuInstanceCreateSurface(
|
||||||
|
instance,
|
||||||
|
&(WGPUSurfaceDescriptor){
|
||||||
|
.label = NULL,
|
||||||
|
.nextInChain =
|
||||||
|
(const WGPUChainedStruct*)&(
|
||||||
|
WGPUSurfaceDescriptorFromWindowsHWND) {
|
||||||
|
.chain =
|
||||||
|
(WGPUChainedStruct){
|
||||||
|
.next = NULL,
|
||||||
|
.sType = WGPUSType_SurfaceDescriptorFromWindowsHWND,
|
||||||
|
},
|
||||||
|
.hinstance = hinstance,
|
||||||
|
.hwnd = hwnd,
|
||||||
|
},
|
||||||
|
});
|
||||||
|
}
|
||||||
|
#else
|
||||||
|
#error "Unsupported WGPU_TARGET"
|
||||||
|
#endif
|
||||||
|
}
|
||||||
|
|
49
external/glfw3webgpu/glfw3webgpu.h
vendored
Normal file
49
external/glfw3webgpu/glfw3webgpu.h
vendored
Normal file
|
@ -0,0 +1,49 @@
|
||||||
|
/**
|
||||||
|
* This is an extension of GLFW for WebGPU, abstracting away the details of
|
||||||
|
* OS-specific operations.
|
||||||
|
*
|
||||||
|
* This file is part of the "Learn WebGPU for C++" book.
|
||||||
|
* https://eliemichel.github.io/LearnWebGPU
|
||||||
|
*
|
||||||
|
* MIT License
|
||||||
|
* Copyright (c) 2022-2023 Elie Michel and the wgpu-native authors
|
||||||
|
*
|
||||||
|
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||||
|
* of this software and associated documentation files (the "Software"), to deal
|
||||||
|
* in the Software without restriction, including without limitation the rights
|
||||||
|
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||||
|
* copies of the Software, and to permit persons to whom the Software is
|
||||||
|
* furnished to do so, subject to the following conditions:
|
||||||
|
*
|
||||||
|
* The above copyright notice and this permission notice shall be included in all
|
||||||
|
* copies or substantial portions of the Software.
|
||||||
|
*
|
||||||
|
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||||
|
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||||
|
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||||
|
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||||
|
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||||
|
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||||
|
* SOFTWARE.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#ifndef _glfw3_webgpu_h_
|
||||||
|
#define _glfw3_webgpu_h_
|
||||||
|
|
||||||
|
#include <webgpu/webgpu.h>
|
||||||
|
#include <GLFW/glfw3.h>
|
||||||
|
|
||||||
|
#ifdef __cplusplus
|
||||||
|
extern "C" {
|
||||||
|
#endif
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Get a WGPUSurface from a GLFW window.
|
||||||
|
*/
|
||||||
|
WGPUSurface glfwGetWGPUSurface(WGPUInstance instance, GLFWwindow* window);
|
||||||
|
|
||||||
|
#ifdef __cplusplus
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#endif // _glfw3_webgpu_h_
|
6991
external/lodepng/lodepng.cpp
vendored
6991
external/lodepng/lodepng.cpp
vendored
File diff suppressed because it is too large
Load diff
2089
external/lodepng/lodepng.h
vendored
2089
external/lodepng/lodepng.h
vendored
File diff suppressed because it is too large
Load diff
7987
external/stb_image/stb_image.h
vendored
Normal file
7987
external/stb_image/stb_image.h
vendored
Normal file
File diff suppressed because it is too large
Load diff
30
meson.build
30
meson.build
|
@ -1,6 +1,10 @@
|
||||||
project('hibis', 'cpp', version: '0.0.0' + (not get_option('buildtype').startswith('release') ? '-' + run_command('git', 'rev-parse', '--short', 'HEAD', check: true).stdout().strip() : ''),
|
project('hibis', 'cpp', 'c', version: '0.0.0' + (not get_option('buildtype').startswith('release') ? '-' + run_command('git', 'rev-parse', '--short', 'HEAD', check: true).stdout().strip() : ''),
|
||||||
license: 'LGPL-3.0-only', meson_version: '>=0.60.3', default_options: ['cpp_std=c++17'])
|
license: 'LGPL-3.0-only', meson_version: '>=0.60.3', default_options: ['cpp_std=c++17'])
|
||||||
|
|
||||||
|
# Imports
|
||||||
|
|
||||||
|
cmake = import('cmake')
|
||||||
|
|
||||||
# Configure data
|
# Configure data
|
||||||
confdata = configuration_data()
|
confdata = configuration_data()
|
||||||
confdata.set('version', meson.project_version())
|
confdata.set('version', meson.project_version())
|
||||||
|
@ -12,19 +16,25 @@ include_dirs = include_directories('./external')
|
||||||
|
|
||||||
# Files
|
# Files
|
||||||
libhibis_src_core = files('core/engine/engine.cpp')
|
libhibis_src_core = files('core/engine/engine.cpp')
|
||||||
libhibis_src_resources = files('core/resources/texture.cpp')
|
libhibis_src_renderer = files('core/renderer/renderer.cpp')
|
||||||
libhibis_src = [libhibis_src_core, libhibis_src_resources]
|
libhibis_src_resources = files('core/resources/font.cpp', 'core/resources/shader.cpp')
|
||||||
|
libhibis_src = [libhibis_src_core, libhibis_src_renderer, libhibis_src_resources]
|
||||||
|
|
||||||
libhibis_rsdl_src = files('renderer/rsdl/rsdl.cpp', 'renderer/rsdl/resources/font.cpp')
|
libhibis_rwgpu_src = files('renderer/rwgpu/rwgpu.cpp')
|
||||||
libhibis_test_src = files('test/app.cpp')
|
libhibis_test_src = files('test/app.cpp')
|
||||||
|
|
||||||
# Dependencies
|
# Dependencies
|
||||||
libsdl2 = dependency('SDL2')
|
libgl = dependency('gl')
|
||||||
libsdl2_ttf = dependency('SDL2_ttf')
|
libglfw = dependency('glfw3')
|
||||||
|
libglew = dependency('GLEW')
|
||||||
libfmt = dependency('fmt')
|
libfmt = dependency('fmt')
|
||||||
liblodepng = static_library('lodepng', 'external/lodepng/lodepng.cpp')
|
libwgpu = meson.get_compiler('cpp').find_library('wgpu_native')
|
||||||
|
|
||||||
|
libwgpu_glfw = static_library('glfw3webgpu', 'external/glfw3webgpu/glfw3webgpu.c', dependencies: [libglfw])
|
||||||
|
|
||||||
|
libfreetype2 = dependency('freetype2')
|
||||||
|
|
||||||
# Compile
|
# Compile
|
||||||
libhibis = library('hibis', libhibis_src, include_directories: include_dirs, link_with: liblodepng)
|
libhibis = library('hibis', libhibis_src, include_directories: include_dirs, dependencies: [libfreetype2])
|
||||||
libhibis_rsdl = library('hibis_rsdl', libhibis_rsdl_src, include_directories: [include_dirs, './core'], link_with: libhibis, dependencies: [libsdl2, libsdl2_ttf, libfmt])
|
libhibis_rwgpu = library('hibis_rwgpu', libhibis_rwgpu_src, include_directories: [include_dirs, './core'], link_with: [libhibis, libwgpu_glfw], dependencies: [libfreetype2, libglfw, libfmt, libwgpu])
|
||||||
hibistest = executable('hibistest.exec', libhibis_test_src, include_directories: [include_dirs, './core', './renderer/rsdl'], link_with: [libhibis, libhibis_rsdl], dependencies: [libsdl2, libsdl2_ttf, libfmt])
|
hibistest = executable('hibistest.exec', libhibis_test_src, include_directories: [include_dirs, './core', './renderer/rwgpu'], link_with: [libhibis, libhibis_rwgpu], dependencies: [libfreetype2, libglfw, libfmt, libwgpu])
|
||||||
|
|
496
renderer/aethiopicus/aethiopicus.cpp
Normal file
496
renderer/aethiopicus/aethiopicus.cpp
Normal file
|
@ -0,0 +1,496 @@
|
||||||
|
#include <fmt/format.h>
|
||||||
|
#include <vector>
|
||||||
|
#include <stdexcept>
|
||||||
|
#include <string.h>
|
||||||
|
#include <iostream>
|
||||||
|
#include <map>
|
||||||
|
#include <set>
|
||||||
|
|
||||||
|
#include "aethiopicus.hpp"
|
||||||
|
#include "vkutil.hpp"
|
||||||
|
|
||||||
|
#include "enginever.hpp"
|
||||||
|
|
||||||
|
std::vector<int> split(std::string phrase, std::string delimiter){
|
||||||
|
std::vector<int> list;
|
||||||
|
size_t pos = 0;
|
||||||
|
std::string token;
|
||||||
|
while ((pos = phrase.find(delimiter)) != std::string::npos) {
|
||||||
|
token = phrase.substr(0, pos);
|
||||||
|
list.push_back(stoi(token));
|
||||||
|
phrase.erase(0, pos + delimiter.length());
|
||||||
|
}
|
||||||
|
|
||||||
|
#ifndef NDEBUG
|
||||||
|
if ((pos = phrase.find("-")) != std::string::npos) {
|
||||||
|
phrase = phrase.substr(0, 1);
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
|
list.push_back(stoi(phrase));
|
||||||
|
return list;
|
||||||
|
}
|
||||||
|
|
||||||
|
VKAPI_ATTR VkBool32 VKAPI_CALL debugCallback(
|
||||||
|
VkDebugUtilsMessageSeverityFlagBitsEXT messageSeverity,
|
||||||
|
VkDebugUtilsMessageTypeFlagsEXT messageType,
|
||||||
|
const VkDebugUtilsMessengerCallbackDataEXT* pCallbackData,
|
||||||
|
void* pUserData) {
|
||||||
|
|
||||||
|
if (hibis::aethiopicus::Aethiopicus* renderer = static_cast<hibis::aethiopicus::Aethiopicus*>(pUserData)) {
|
||||||
|
LoggingSeverity severity;
|
||||||
|
|
||||||
|
switch (messageSeverity) {
|
||||||
|
case VK_DEBUG_UTILS_MESSAGE_SEVERITY_VERBOSE_BIT_EXT:
|
||||||
|
severity = Message;
|
||||||
|
break;
|
||||||
|
case VK_DEBUG_UTILS_MESSAGE_SEVERITY_INFO_BIT_EXT:
|
||||||
|
severity = Information;
|
||||||
|
break;
|
||||||
|
case VK_DEBUG_UTILS_MESSAGE_SEVERITY_WARNING_BIT_EXT:
|
||||||
|
severity = Warning;
|
||||||
|
break;
|
||||||
|
case VK_DEBUG_UTILS_MESSAGE_SEVERITY_ERROR_BIT_EXT:
|
||||||
|
severity = Error;
|
||||||
|
break;
|
||||||
|
case VK_DEBUG_UTILS_MESSAGE_SEVERITY_FLAG_BITS_MAX_ENUM_EXT:
|
||||||
|
severity = Message;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
renderer->mLogger(severity, "VULKAN: " + (std::string)(pCallbackData->pMessage));
|
||||||
|
} else std::cout << "pUserData is not equivalent to hibis::aethiopicus::Aethiopicus." << std::endl;
|
||||||
|
|
||||||
|
return VK_FALSE;
|
||||||
|
}
|
||||||
|
|
||||||
|
void keyCallback(GLFWwindow* window, int key, int scancode, int action, int mods) {
|
||||||
|
if (hibis::aethiopicus::Aethiopicus* renderer = static_cast<hibis::aethiopicus::Aethiopicus*>(glfwGetWindowUserPointer(window))) {
|
||||||
|
// Prepare type
|
||||||
|
hibis::PressTypes type;
|
||||||
|
switch (action) {
|
||||||
|
case GLFW_PRESS:
|
||||||
|
type = hibis::Pressed;
|
||||||
|
break;
|
||||||
|
case GLFW_REPEAT:
|
||||||
|
type = hibis::Held;
|
||||||
|
break;
|
||||||
|
case GLFW_RELEASE:
|
||||||
|
type = hibis::Released;
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
type = hibis::Unknown;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Tell input manager the new state
|
||||||
|
renderer->mInputManager.setKeyPressState(scancode, type);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
namespace hibis::aethiopicus {
|
||||||
|
Aethiopicus::Aethiopicus(std::string title, std::string internalName, std::string version, IntVec2 size, LoggerCallback logger, bool useValidationLayers) : Renderer(logger), mUseValidationLayers(useValidationLayers) {
|
||||||
|
// GLFW Window
|
||||||
|
glfwInit();
|
||||||
|
|
||||||
|
glfwWindowHint(GLFW_CLIENT_API, GLFW_NO_API);
|
||||||
|
glfwWindowHint(GLFW_RESIZABLE, GLFW_FALSE);
|
||||||
|
mWindow = glfwCreateWindow(size.x, size.y, title.c_str(), nullptr, nullptr);
|
||||||
|
|
||||||
|
if (mWindow == NULL) throw std::runtime_error("Couldn't create a window");
|
||||||
|
|
||||||
|
glfwSetWindowUserPointer(mWindow, this);
|
||||||
|
glfwSetKeyCallback(mWindow, keyCallback);
|
||||||
|
|
||||||
|
std::vector<int> hibisVerNums = split(HIBIS_VERSION, ".");
|
||||||
|
std::vector<int> appVerNums = split(version, ".");
|
||||||
|
|
||||||
|
VkApplicationInfo appInfo {};
|
||||||
|
appInfo.sType = VK_STRUCTURE_TYPE_APPLICATION_INFO;
|
||||||
|
appInfo.pApplicationName = internalName.c_str();
|
||||||
|
appInfo.applicationVersion = VK_MAKE_VERSION(appVerNums[0], appVerNums[1], appVerNums[2]);
|
||||||
|
appInfo.pEngineName = "Hibis";
|
||||||
|
appInfo.engineVersion = VK_MAKE_VERSION(hibisVerNums[0], hibisVerNums[1], hibisVerNums[2]);
|
||||||
|
appInfo.apiVersion = VK_API_VERSION_1_3;
|
||||||
|
|
||||||
|
VkInstanceCreateInfo instanceCreateInfo{};
|
||||||
|
instanceCreateInfo.sType = VK_STRUCTURE_TYPE_INSTANCE_CREATE_INFO;
|
||||||
|
instanceCreateInfo.pApplicationInfo = &appInfo;
|
||||||
|
|
||||||
|
mGLFWExtensions = glfwGetRequiredInstanceExtensions(&mGLFWExtensionCount);
|
||||||
|
|
||||||
|
for(uint32_t i = 0; i < mGLFWExtensionCount; i++) {
|
||||||
|
mGLFWExtensionVector.emplace_back(mGLFWExtensions[i]);
|
||||||
|
}
|
||||||
|
|
||||||
|
mGLFWExtensionVector.emplace_back(VK_KHR_PORTABILITY_ENUMERATION_EXTENSION_NAME);
|
||||||
|
|
||||||
|
if (useValidationLayers) {
|
||||||
|
mGLFWExtensionVector.push_back(VK_EXT_DEBUG_UTILS_EXTENSION_NAME);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (useValidationLayers && !checkValidationLayerSupport()) {
|
||||||
|
throw std::runtime_error("validation layers requested, but not available!");
|
||||||
|
}
|
||||||
|
|
||||||
|
instanceCreateInfo.flags |= VK_INSTANCE_CREATE_ENUMERATE_PORTABILITY_BIT_KHR;
|
||||||
|
|
||||||
|
instanceCreateInfo.enabledExtensionCount = (uint32_t) mGLFWExtensionVector.size();
|
||||||
|
instanceCreateInfo.ppEnabledExtensionNames = mGLFWExtensionVector.data();
|
||||||
|
|
||||||
|
uint32_t extensionCount = 0;
|
||||||
|
vkEnumerateInstanceExtensionProperties(nullptr, &extensionCount, nullptr);
|
||||||
|
|
||||||
|
mVulkanExtensions.resize(extensionCount);
|
||||||
|
|
||||||
|
vkEnumerateInstanceExtensionProperties(nullptr, &extensionCount, mVulkanExtensions.data());
|
||||||
|
|
||||||
|
if (useValidationLayers) {
|
||||||
|
instanceCreateInfo.enabledLayerCount = static_cast<uint32_t>(mValidationLayers.size());
|
||||||
|
instanceCreateInfo.ppEnabledLayerNames = mValidationLayers.data();
|
||||||
|
} else {
|
||||||
|
instanceCreateInfo.enabledLayerCount = 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
VkResult result = vkCreateInstance(&instanceCreateInfo, nullptr, &mVulkanInstance);
|
||||||
|
|
||||||
|
if (useValidationLayers && !checkValidationLayerSupport()) {
|
||||||
|
throw std::runtime_error("validation layers requested, but not available!");
|
||||||
|
}
|
||||||
|
|
||||||
|
if (result != VK_SUCCESS) {
|
||||||
|
logger(Fatal, "Could not create an instance of Vulkan");
|
||||||
|
throw std::runtime_error("aethiopicus launch err, check logs");
|
||||||
|
}
|
||||||
|
|
||||||
|
if (glfwCreateWindowSurface(mVulkanInstance, mWindow, nullptr, &mVulkanSurface) != VK_SUCCESS) throw std::runtime_error("Couldn't make a surface for vulkan to use");
|
||||||
|
|
||||||
|
if (useValidationLayers) {
|
||||||
|
VkDebugUtilsMessengerCreateInfoEXT createInfo;
|
||||||
|
|
||||||
|
createInfo.sType = VK_STRUCTURE_TYPE_DEBUG_UTILS_MESSENGER_CREATE_INFO_EXT;
|
||||||
|
createInfo.messageSeverity = VK_DEBUG_UTILS_MESSAGE_SEVERITY_VERBOSE_BIT_EXT | VK_DEBUG_UTILS_MESSAGE_SEVERITY_WARNING_BIT_EXT | VK_DEBUG_UTILS_MESSAGE_SEVERITY_ERROR_BIT_EXT;
|
||||||
|
createInfo.messageType = VK_DEBUG_UTILS_MESSAGE_TYPE_GENERAL_BIT_EXT | VK_DEBUG_UTILS_MESSAGE_TYPE_VALIDATION_BIT_EXT | VK_DEBUG_UTILS_MESSAGE_TYPE_PERFORMANCE_BIT_EXT;
|
||||||
|
createInfo.pfnUserCallback = debugCallback;
|
||||||
|
createInfo.pUserData = (void*)this;
|
||||||
|
|
||||||
|
// Remove vulkan warnings
|
||||||
|
createInfo.pNext = NULL;
|
||||||
|
createInfo.flags = 0;
|
||||||
|
|
||||||
|
if (createDebugUtilsMessengerEXT(mVulkanInstance, &createInfo, nullptr, &mDebugMessenger) != VK_SUCCESS) {
|
||||||
|
throw std::runtime_error("failed to set up debug messenger!");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
uint32_t deviceCount = 0;
|
||||||
|
vkEnumeratePhysicalDevices(mVulkanInstance, &deviceCount, nullptr);
|
||||||
|
|
||||||
|
if (deviceCount == 0) throw std::runtime_error("No vulkan support on available graphics cards");
|
||||||
|
|
||||||
|
std::vector<VkPhysicalDevice> devices(deviceCount);
|
||||||
|
vkEnumeratePhysicalDevices(mVulkanInstance, &deviceCount, devices.data());
|
||||||
|
|
||||||
|
std::multimap<int, VkPhysicalDevice> candidates;
|
||||||
|
|
||||||
|
for (const auto& device : devices) {
|
||||||
|
// Get properties here for logging purposes
|
||||||
|
VkPhysicalDeviceProperties deviceProperties;
|
||||||
|
vkGetPhysicalDeviceProperties(device, &deviceProperties);
|
||||||
|
|
||||||
|
int score = rateDeviceSuitability(device, deviceProperties);
|
||||||
|
|
||||||
|
std::string deviceTypeAsString;
|
||||||
|
|
||||||
|
switch (deviceProperties.deviceType) {
|
||||||
|
case VK_PHYSICAL_DEVICE_TYPE_DISCRETE_GPU:
|
||||||
|
deviceTypeAsString = "VK_PHYSICAL_DEVICE_TYPE_DISCRETE_GPU";
|
||||||
|
break;
|
||||||
|
case VK_PHYSICAL_DEVICE_TYPE_INTEGRATED_GPU:
|
||||||
|
deviceTypeAsString = "VK_PHYSICAL_DEVICE_TYPE_INTEGRATED_GPU";
|
||||||
|
break;
|
||||||
|
case VK_PHYSICAL_DEVICE_TYPE_CPU:
|
||||||
|
deviceTypeAsString = "VK_PHYSICAL_DEVICE_TYPE_CPU";
|
||||||
|
break;
|
||||||
|
case VK_PHYSICAL_DEVICE_TYPE_MAX_ENUM:
|
||||||
|
deviceTypeAsString = "VK_PHYSICAL_DEVICE_TYPE_MAX_ENUM";
|
||||||
|
break;
|
||||||
|
case VK_PHYSICAL_DEVICE_TYPE_OTHER:
|
||||||
|
deviceTypeAsString = "VK_PHYSICAL_DEVICE_TYPE_OTHER";
|
||||||
|
break;
|
||||||
|
case VK_PHYSICAL_DEVICE_TYPE_VIRTUAL_GPU:
|
||||||
|
deviceTypeAsString = "VK_PHYSICAL_DEVICE_TYPE_VIRTUAL_GPU";
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Logging the info
|
||||||
|
mLogger(Information, fmt::format("Device {} has a score of {}", deviceProperties.deviceName, score));
|
||||||
|
mLogger(Information, fmt::format("{}'s device type: {}", deviceProperties.deviceName, deviceTypeAsString));
|
||||||
|
|
||||||
|
// Insert into candidates map
|
||||||
|
candidates.insert(std::make_pair(score, device));
|
||||||
|
}
|
||||||
|
|
||||||
|
// Validate that the first candidate has a better score than 0.
|
||||||
|
if (candidates.rbegin()->first > 0) {
|
||||||
|
mVulkanGraphicsDevice = candidates.rbegin()->second;
|
||||||
|
} else {
|
||||||
|
throw std::runtime_error("failed to find a suitable GPU!");
|
||||||
|
}
|
||||||
|
|
||||||
|
// Logical device creation
|
||||||
|
QueueFamilyIndicies indices = findQueueFamilies(mVulkanGraphicsDevice, mVulkanSurface);
|
||||||
|
|
||||||
|
std::vector<VkDeviceQueueCreateInfo> queueCreateInfos;
|
||||||
|
std::set<uint32_t> uniqueQueueFamilies = {indices.graphicsFamily.value(), indices.presentFamily.value()};
|
||||||
|
|
||||||
|
float queuePriority = 1.0f;
|
||||||
|
for (uint32_t queueFamily : uniqueQueueFamilies) {
|
||||||
|
VkDeviceQueueCreateInfo queueCreateInfo{};
|
||||||
|
queueCreateInfo.sType = VK_STRUCTURE_TYPE_DEVICE_QUEUE_CREATE_INFO;
|
||||||
|
queueCreateInfo.queueFamilyIndex = queueFamily;
|
||||||
|
queueCreateInfo.queueCount = 1;
|
||||||
|
queueCreateInfo.pQueuePriorities = &queuePriority;
|
||||||
|
queueCreateInfos.push_back(queueCreateInfo);
|
||||||
|
}
|
||||||
|
|
||||||
|
VkPhysicalDeviceFeatures deviceFeatures{};
|
||||||
|
|
||||||
|
VkDeviceCreateInfo logicalDeviceCreateInfo{};
|
||||||
|
logicalDeviceCreateInfo.sType = VK_STRUCTURE_TYPE_DEVICE_CREATE_INFO;
|
||||||
|
|
||||||
|
logicalDeviceCreateInfo.queueCreateInfoCount = static_cast<uint32_t>(queueCreateInfos.size());
|
||||||
|
logicalDeviceCreateInfo.pQueueCreateInfos = queueCreateInfos.data();
|
||||||
|
|
||||||
|
logicalDeviceCreateInfo.pEnabledFeatures = &deviceFeatures;
|
||||||
|
|
||||||
|
logicalDeviceCreateInfo.enabledExtensionCount = static_cast<uint32_t>(mRequiredDeviceExtensions.size());
|
||||||
|
logicalDeviceCreateInfo.ppEnabledExtensionNames = mRequiredDeviceExtensions.data();
|
||||||
|
|
||||||
|
if (mUseValidationLayers) {
|
||||||
|
logicalDeviceCreateInfo.enabledLayerCount = static_cast<uint32_t>(mValidationLayers.size());
|
||||||
|
logicalDeviceCreateInfo.ppEnabledLayerNames = mValidationLayers.data();
|
||||||
|
} else {
|
||||||
|
logicalDeviceCreateInfo.enabledLayerCount = 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (vkCreateDevice(mVulkanGraphicsDevice, &logicalDeviceCreateInfo, nullptr, &mVulkanLogicalDevice) != VK_SUCCESS) {
|
||||||
|
throw std::runtime_error("failed to create logical device!");
|
||||||
|
}
|
||||||
|
|
||||||
|
vkGetDeviceQueue(mVulkanLogicalDevice, indices.graphicsFamily.value(), 0, &mVulkanGraphicsQueue);
|
||||||
|
vkGetDeviceQueue(mVulkanLogicalDevice, indices.presentFamily.value(), 0, &mVulkanPresentQueue);
|
||||||
|
|
||||||
|
// Create a swap chain
|
||||||
|
SwapChainSupportDetails swapChainSupport = querySwapChainSupport(mVulkanGraphicsDevice, mVulkanSurface);
|
||||||
|
|
||||||
|
VkSurfaceFormatKHR surfaceFormat = chooseSwapSurfaceFormat(swapChainSupport.formats);
|
||||||
|
VkPresentModeKHR presentMode = chooseSwapPresentMode(swapChainSupport.presentModes);
|
||||||
|
VkExtent2D extent = chooseSwapExtent(swapChainSupport.capabilities, mWindow);
|
||||||
|
|
||||||
|
uint32_t imageCount = swapChainSupport.capabilities.minImageCount + 1;
|
||||||
|
|
||||||
|
if (swapChainSupport.capabilities.maxImageCount > 0 && imageCount > swapChainSupport.capabilities.maxImageCount) {
|
||||||
|
imageCount = swapChainSupport.capabilities.maxImageCount;
|
||||||
|
}
|
||||||
|
|
||||||
|
VkSwapchainCreateInfoKHR createInfo{};
|
||||||
|
createInfo.sType = VK_STRUCTURE_TYPE_SWAPCHAIN_CREATE_INFO_KHR;
|
||||||
|
createInfo.surface = mVulkanSurface;
|
||||||
|
|
||||||
|
createInfo.minImageCount = imageCount;
|
||||||
|
createInfo.imageFormat = surfaceFormat.format;
|
||||||
|
createInfo.imageColorSpace = surfaceFormat.colorSpace;
|
||||||
|
createInfo.imageExtent = extent;
|
||||||
|
createInfo.imageArrayLayers = 1;
|
||||||
|
createInfo.imageUsage = VK_IMAGE_USAGE_COLOR_ATTACHMENT_BIT;
|
||||||
|
|
||||||
|
QueueFamilyIndicies graphicsIndicies = findQueueFamilies(mVulkanGraphicsDevice, mVulkanSurface);
|
||||||
|
uint32_t queueFamilyIndices[] = {graphicsIndicies.graphicsFamily.value(), graphicsIndicies.presentFamily.value()};
|
||||||
|
|
||||||
|
if (graphicsIndicies.graphicsFamily != graphicsIndicies.presentFamily) {
|
||||||
|
createInfo.imageSharingMode = VK_SHARING_MODE_CONCURRENT;
|
||||||
|
createInfo.queueFamilyIndexCount = 2;
|
||||||
|
createInfo.pQueueFamilyIndices = queueFamilyIndices;
|
||||||
|
} else {
|
||||||
|
createInfo.imageSharingMode = VK_SHARING_MODE_EXCLUSIVE;
|
||||||
|
createInfo.queueFamilyIndexCount = 0; // Optional
|
||||||
|
createInfo.pQueueFamilyIndices = nullptr; // Optional
|
||||||
|
}
|
||||||
|
|
||||||
|
createInfo.preTransform = swapChainSupport.capabilities.currentTransform;
|
||||||
|
createInfo.compositeAlpha = VK_COMPOSITE_ALPHA_OPAQUE_BIT_KHR;
|
||||||
|
|
||||||
|
createInfo.presentMode = presentMode;
|
||||||
|
createInfo.clipped = VK_TRUE;
|
||||||
|
|
||||||
|
createInfo.oldSwapchain = VK_NULL_HANDLE;
|
||||||
|
|
||||||
|
if (vkCreateSwapchainKHR(mVulkanLogicalDevice, &createInfo, nullptr, &mVulkanSwapchain) != VK_SUCCESS) {
|
||||||
|
throw std::runtime_error("failed to create swap chain!");
|
||||||
|
}
|
||||||
|
|
||||||
|
mSwapChainImageFormat = surfaceFormat.format;
|
||||||
|
mSwapChainExtent = extent;
|
||||||
|
|
||||||
|
vkGetSwapchainImagesKHR(mVulkanLogicalDevice, mVulkanSwapchain, &imageCount, nullptr);
|
||||||
|
mSwapchainImages.resize(imageCount);
|
||||||
|
vkGetSwapchainImagesKHR(mVulkanLogicalDevice, mVulkanSwapchain, &imageCount, mSwapchainImages.data());
|
||||||
|
|
||||||
|
// We're still fucking going with initialization, fucking christ
|
||||||
|
mSwapChainImageViews.resize(mSwapchainImages.size());
|
||||||
|
for (size_t i = 0; i < mSwapchainImages.size(); i++) {
|
||||||
|
VkImageViewCreateInfo createInfo{};
|
||||||
|
createInfo.sType = VK_STRUCTURE_TYPE_IMAGE_VIEW_CREATE_INFO;
|
||||||
|
createInfo.image = mSwapchainImages[i];
|
||||||
|
createInfo.viewType = VK_IMAGE_VIEW_TYPE_2D;
|
||||||
|
createInfo.format = mSwapChainImageFormat;
|
||||||
|
|
||||||
|
createInfo.components.r = VK_COMPONENT_SWIZZLE_IDENTITY;
|
||||||
|
createInfo.components.g = VK_COMPONENT_SWIZZLE_IDENTITY;
|
||||||
|
createInfo.components.b = VK_COMPONENT_SWIZZLE_IDENTITY;
|
||||||
|
createInfo.components.a = VK_COMPONENT_SWIZZLE_IDENTITY;
|
||||||
|
|
||||||
|
createInfo.subresourceRange.aspectMask = VK_IMAGE_ASPECT_COLOR_BIT;
|
||||||
|
createInfo.subresourceRange.baseMipLevel = 0;
|
||||||
|
createInfo.subresourceRange.levelCount = 1;
|
||||||
|
createInfo.subresourceRange.baseArrayLayer = 0;
|
||||||
|
createInfo.subresourceRange.layerCount = 1;
|
||||||
|
|
||||||
|
if (vkCreateImageView(mVulkanLogicalDevice, &createInfo, nullptr, &mSwapChainImageViews[i]) != VK_SUCCESS) {
|
||||||
|
throw std::runtime_error("failed to create image views!");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
Aethiopicus::~Aethiopicus() {
|
||||||
|
if (mUseValidationLayers) {
|
||||||
|
destroyDebugUtilsMessengerEXT(mVulkanInstance, mDebugMessenger, nullptr);
|
||||||
|
}
|
||||||
|
|
||||||
|
for (auto imageView : mSwapChainImageViews) {
|
||||||
|
vkDestroyImageView(mVulkanLogicalDevice, imageView, nullptr);
|
||||||
|
}
|
||||||
|
|
||||||
|
vkDestroySwapchainKHR(mVulkanLogicalDevice, mVulkanSwapchain, nullptr);
|
||||||
|
vkDestroyDevice(mVulkanLogicalDevice, nullptr);
|
||||||
|
vkDestroySurfaceKHR(mVulkanInstance, mVulkanSurface, nullptr);
|
||||||
|
vkDestroyInstance(mVulkanInstance, nullptr);
|
||||||
|
glfwDestroyWindow(mWindow);
|
||||||
|
glfwTerminate();
|
||||||
|
}
|
||||||
|
|
||||||
|
bool Aethiopicus::checkValidationLayerSupport() {
|
||||||
|
uint32_t layerCount;
|
||||||
|
vkEnumerateInstanceLayerProperties(&layerCount, nullptr);
|
||||||
|
|
||||||
|
std::vector<VkLayerProperties> availableLayers(layerCount);
|
||||||
|
vkEnumerateInstanceLayerProperties(&layerCount, availableLayers.data());
|
||||||
|
|
||||||
|
for (const char* layerName : mValidationLayers) {
|
||||||
|
bool layerFound = false;
|
||||||
|
|
||||||
|
for (const auto& layerProperties : availableLayers) {
|
||||||
|
if (strcmp(layerName, layerProperties.layerName) == 0) {
|
||||||
|
layerFound = true;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!layerFound) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
int Aethiopicus::rateDeviceSuitability(VkPhysicalDevice device, VkPhysicalDeviceProperties deviceProperties) {
|
||||||
|
// TODO: Better scoring, accounting for 3D limits
|
||||||
|
VkPhysicalDeviceFeatures deviceFeatures;
|
||||||
|
vkGetPhysicalDeviceFeatures(device, &deviceFeatures);
|
||||||
|
|
||||||
|
QueueFamilyIndicies indicies = findQueueFamilies(device, mVulkanSurface);
|
||||||
|
|
||||||
|
bool extensionsSupported = checkDeviceExtensionSupport(device);
|
||||||
|
|
||||||
|
int score = 0;
|
||||||
|
|
||||||
|
if (deviceProperties.deviceType == VK_PHYSICAL_DEVICE_TYPE_DISCRETE_GPU) score += 1000;
|
||||||
|
else if (deviceProperties.deviceType == VK_PHYSICAL_DEVICE_TYPE_INTEGRATED_GPU) score += 500;
|
||||||
|
|
||||||
|
score += deviceProperties.limits.maxImageDimension2D;
|
||||||
|
|
||||||
|
if (!deviceFeatures.geometryShader) return 0;
|
||||||
|
|
||||||
|
if (!indicies.isComplete()) return 0;
|
||||||
|
|
||||||
|
if (!extensionsSupported) return 0;
|
||||||
|
|
||||||
|
bool swapChainAdequate = false;
|
||||||
|
|
||||||
|
if (extensionsSupported) {
|
||||||
|
SwapChainSupportDetails swapChainSupport = querySwapChainSupport(device, mVulkanSurface);
|
||||||
|
swapChainAdequate = !swapChainSupport.formats.empty() && !swapChainSupport.presentModes.empty();
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!swapChainAdequate) return 0;
|
||||||
|
|
||||||
|
return score;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool Aethiopicus::checkDeviceExtensionSupport(VkPhysicalDevice device) {
|
||||||
|
uint32_t extensionCount;
|
||||||
|
vkEnumerateDeviceExtensionProperties(device, nullptr, &extensionCount, nullptr);
|
||||||
|
|
||||||
|
std::vector<VkExtensionProperties> availableExtensions(extensionCount);
|
||||||
|
vkEnumerateDeviceExtensionProperties(device, nullptr, &extensionCount, availableExtensions.data());
|
||||||
|
|
||||||
|
std::set<std::string> requiredExtensions(mRequiredDeviceExtensions.begin(), mRequiredDeviceExtensions.end());
|
||||||
|
|
||||||
|
for (const auto& extension : availableExtensions) {
|
||||||
|
requiredExtensions.erase(extension.extensionName);
|
||||||
|
}
|
||||||
|
|
||||||
|
return requiredExtensions.empty();
|
||||||
|
}
|
||||||
|
|
||||||
|
VkResult Aethiopicus::createDebugUtilsMessengerEXT(VkInstance instance, const VkDebugUtilsMessengerCreateInfoEXT* pCreateInfo, const VkAllocationCallbacks* pAllocator, VkDebugUtilsMessengerEXT* pDebugMessenger) {
|
||||||
|
auto func = (PFN_vkCreateDebugUtilsMessengerEXT) vkGetInstanceProcAddr(instance, "vkCreateDebugUtilsMessengerEXT");
|
||||||
|
if (func != nullptr) {
|
||||||
|
return func(instance, pCreateInfo, pAllocator, pDebugMessenger);
|
||||||
|
} else {
|
||||||
|
return VK_ERROR_EXTENSION_NOT_PRESENT;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void Aethiopicus::destroyDebugUtilsMessengerEXT(VkInstance instance, VkDebugUtilsMessengerEXT debugMessenger, const VkAllocationCallbacks* pAllocator) {
|
||||||
|
auto func = (PFN_vkDestroyDebugUtilsMessengerEXT) vkGetInstanceProcAddr(instance, "vkDestroyDebugUtilsMessengerEXT");
|
||||||
|
if (func != nullptr) {
|
||||||
|
func(instance, debugMessenger, pAllocator);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void Aethiopicus::clearScreen(Color col) {}
|
||||||
|
|
||||||
|
void Aethiopicus::renderCurrent() {}
|
||||||
|
|
||||||
|
void Aethiopicus::drawText(Resource* resource, std::string text, IntVec2 pos, Color color) {}
|
||||||
|
void Aethiopicus::drawTexture(Resource* resource, IntRect size) {}
|
||||||
|
|
||||||
|
void Aethiopicus::useShader(Shader* shader, Point2D points[3]) {}
|
||||||
|
void Aethiopicus::stopUsingShaders() {}
|
||||||
|
|
||||||
|
// Pre and Post draw
|
||||||
|
void Aethiopicus::preDraw() {}
|
||||||
|
void Aethiopicus::postDraw() {}
|
||||||
|
|
||||||
|
// Update
|
||||||
|
void Aethiopicus::update() {}
|
||||||
|
|
||||||
|
// Util
|
||||||
|
void Aethiopicus::compileShader(Shader* shader) {}
|
||||||
|
void Aethiopicus::toggleWireframe() {}
|
||||||
|
void Aethiopicus::setWindowTitle(std::string title) {}
|
||||||
|
void Aethiopicus::resize(unsigned int width, unsigned int height) {}
|
||||||
|
}
|
100
renderer/aethiopicus/aethiopicus.hpp
Normal file
100
renderer/aethiopicus/aethiopicus.hpp
Normal file
|
@ -0,0 +1,100 @@
|
||||||
|
#pragma once
|
||||||
|
|
||||||
|
#define GLFW_INCLUDE_VULKAN
|
||||||
|
#include <GLFW/glfw3.h>
|
||||||
|
|
||||||
|
#define GLM_FORCE_RADIANS
|
||||||
|
#define GLM_FORCE_DEPTH_ZERO_TO_ONE
|
||||||
|
#include <glm/vec4.hpp>
|
||||||
|
#include <glm/mat4x4.hpp>
|
||||||
|
#include <vector>
|
||||||
|
|
||||||
|
#include <renderer/renderer.hpp>
|
||||||
|
#include <vulkan/vulkan_core.h>
|
||||||
|
|
||||||
|
namespace hibis::aethiopicus {
|
||||||
|
/** \class Aethiopicus
|
||||||
|
* Vulkan-based renderer for the Hibis game engine.
|
||||||
|
*
|
||||||
|
* @param title Application window title
|
||||||
|
* @param internalName Application internal name (used by Aethiopicus::mVulkanInstance)
|
||||||
|
* @param version A semantic version stored in an std::string
|
||||||
|
* @param size Size of the application window
|
||||||
|
* @param callback Logging callback
|
||||||
|
*/
|
||||||
|
class Aethiopicus : public Renderer {
|
||||||
|
public:
|
||||||
|
Aethiopicus(std::string title, std::string internalName, std::string version, IntVec2 size, LoggerCallback callback, bool useValidationLayers = true);
|
||||||
|
~Aethiopicus();
|
||||||
|
|
||||||
|
// Draw
|
||||||
|
void clearScreen(Color col = Color {0, 0, 0, 255}) override;
|
||||||
|
void renderCurrent() override;
|
||||||
|
|
||||||
|
void drawText(Resource* resource, std::string text, IntVec2 pos, Color color) override;
|
||||||
|
void drawTexture(Resource* resource, IntRect size) override;
|
||||||
|
|
||||||
|
void useShader(Shader* shader, Point2D points[3]) override;
|
||||||
|
void stopUsingShaders() override;
|
||||||
|
|
||||||
|
// Pre and Post draw
|
||||||
|
void preDraw() override;
|
||||||
|
void postDraw() override;
|
||||||
|
|
||||||
|
// Update
|
||||||
|
void update() override;
|
||||||
|
|
||||||
|
// Util
|
||||||
|
void compileShader(Shader* shader) override;
|
||||||
|
void toggleWireframe() override;
|
||||||
|
void setWindowTitle(std::string title) override;
|
||||||
|
void resize(unsigned int width, unsigned int height);
|
||||||
|
|
||||||
|
std::vector<const char*> mGLFWExtensionVector;
|
||||||
|
uint32_t mGLFWExtensionCount = 0;
|
||||||
|
const char** mGLFWExtensions;
|
||||||
|
|
||||||
|
std::vector<VkExtensionProperties> mVulkanExtensions;
|
||||||
|
private:
|
||||||
|
// Functions
|
||||||
|
bool checkValidationLayerSupport();
|
||||||
|
|
||||||
|
int rateDeviceSuitability(VkPhysicalDevice device, VkPhysicalDeviceProperties deviceProperties);
|
||||||
|
|
||||||
|
bool checkDeviceExtensionSupport(VkPhysicalDevice device);
|
||||||
|
|
||||||
|
VkResult createDebugUtilsMessengerEXT(VkInstance instance, const VkDebugUtilsMessengerCreateInfoEXT* pCreateInfo, const VkAllocationCallbacks* pAllocator, VkDebugUtilsMessengerEXT* pDebugMessenger);
|
||||||
|
void destroyDebugUtilsMessengerEXT(VkInstance instance, VkDebugUtilsMessengerEXT debugMessenger, const VkAllocationCallbacks* pAllocator);
|
||||||
|
// GLFW
|
||||||
|
GLFWwindow* mWindow;
|
||||||
|
|
||||||
|
// Vulkan
|
||||||
|
VkInstance mVulkanInstance;
|
||||||
|
VkPhysicalDevice mVulkanGraphicsDevice = VK_NULL_HANDLE;
|
||||||
|
VkDevice mVulkanLogicalDevice;
|
||||||
|
|
||||||
|
VkQueue mVulkanGraphicsQueue;
|
||||||
|
VkQueue mVulkanPresentQueue;
|
||||||
|
|
||||||
|
VkSurfaceKHR mVulkanSurface;
|
||||||
|
VkSwapchainKHR mVulkanSwapchain;
|
||||||
|
|
||||||
|
VkDebugUtilsMessengerEXT mDebugMessenger;
|
||||||
|
|
||||||
|
VkFormat mSwapChainImageFormat;
|
||||||
|
VkExtent2D mSwapChainExtent;
|
||||||
|
|
||||||
|
std::vector<VkImage> mSwapchainImages;
|
||||||
|
std::vector<VkImageView> mSwapChainImageViews;
|
||||||
|
|
||||||
|
bool mUseValidationLayers = false;
|
||||||
|
|
||||||
|
const std::vector<const char*> mValidationLayers = {
|
||||||
|
"VK_LAYER_KHRONOS_validation"
|
||||||
|
};
|
||||||
|
|
||||||
|
const std::vector<const char*> mRequiredDeviceExtensions = {
|
||||||
|
VK_KHR_SWAPCHAIN_EXTENSION_NAME
|
||||||
|
};
|
||||||
|
};
|
||||||
|
}
|
99
renderer/aethiopicus/vkutil.cpp
Normal file
99
renderer/aethiopicus/vkutil.cpp
Normal file
|
@ -0,0 +1,99 @@
|
||||||
|
#include <vector>
|
||||||
|
#include <cstdint>
|
||||||
|
#include <limits>
|
||||||
|
#include <algorithm>
|
||||||
|
|
||||||
|
#include "vkutil.hpp"
|
||||||
|
|
||||||
|
QueueFamilyIndicies findQueueFamilies(VkPhysicalDevice device, VkSurfaceKHR surface) {
|
||||||
|
QueueFamilyIndicies indicies;
|
||||||
|
|
||||||
|
uint32_t queueFamilyCount = 0;
|
||||||
|
vkGetPhysicalDeviceQueueFamilyProperties(device, &queueFamilyCount, nullptr);
|
||||||
|
|
||||||
|
std::vector<VkQueueFamilyProperties> queueFamilies(queueFamilyCount);
|
||||||
|
vkGetPhysicalDeviceQueueFamilyProperties(device, &queueFamilyCount, queueFamilies.data());
|
||||||
|
|
||||||
|
int i = 0;
|
||||||
|
for (const auto& queueFamily : queueFamilies) {
|
||||||
|
if (queueFamily.queueFlags & VK_QUEUE_GRAPHICS_BIT) {
|
||||||
|
indicies.graphicsFamily = i;
|
||||||
|
}
|
||||||
|
|
||||||
|
VkBool32 presentSupport = false;
|
||||||
|
vkGetPhysicalDeviceSurfaceSupportKHR(device, i, surface, &presentSupport);
|
||||||
|
|
||||||
|
if (presentSupport) {
|
||||||
|
indicies.presentFamily = i;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (indicies.isComplete()) break;
|
||||||
|
|
||||||
|
i++;
|
||||||
|
}
|
||||||
|
|
||||||
|
return indicies;
|
||||||
|
}
|
||||||
|
|
||||||
|
SwapChainSupportDetails querySwapChainSupport(VkPhysicalDevice device, VkSurfaceKHR surface) {
|
||||||
|
SwapChainSupportDetails details;
|
||||||
|
|
||||||
|
vkGetPhysicalDeviceSurfaceCapabilitiesKHR(device, surface, &details.capabilities);
|
||||||
|
|
||||||
|
uint32_t formatCount;
|
||||||
|
vkGetPhysicalDeviceSurfaceFormatsKHR(device, surface, &formatCount, nullptr);
|
||||||
|
|
||||||
|
if (formatCount != 0) {
|
||||||
|
details.formats.resize(formatCount);
|
||||||
|
vkGetPhysicalDeviceSurfaceFormatsKHR(device, surface, &formatCount, details.formats.data());
|
||||||
|
}
|
||||||
|
|
||||||
|
uint32_t presentModeCount;
|
||||||
|
vkGetPhysicalDeviceSurfacePresentModesKHR(device, surface, &presentModeCount, nullptr);
|
||||||
|
|
||||||
|
if (presentModeCount != 0) {
|
||||||
|
details.presentModes.resize(presentModeCount);
|
||||||
|
vkGetPhysicalDeviceSurfacePresentModesKHR(device, surface, &presentModeCount, details.presentModes.data());
|
||||||
|
}
|
||||||
|
|
||||||
|
return details;
|
||||||
|
}
|
||||||
|
|
||||||
|
VkSurfaceFormatKHR chooseSwapSurfaceFormat(const std::vector<VkSurfaceFormatKHR>& availableFormats) {
|
||||||
|
for (const auto& availableFormat : availableFormats) {
|
||||||
|
if (availableFormat.format == VK_FORMAT_B8G8R8A8_SRGB && availableFormat.colorSpace == VK_COLOR_SPACE_SRGB_NONLINEAR_KHR) {
|
||||||
|
return availableFormat;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return availableFormats[0];
|
||||||
|
}
|
||||||
|
|
||||||
|
VkPresentModeKHR chooseSwapPresentMode(const std::vector<VkPresentModeKHR>& availablePresentModes) {
|
||||||
|
for (const auto& availablePresentMode : availablePresentModes) {
|
||||||
|
if (availablePresentMode == VK_PRESENT_MODE_MAILBOX_KHR) {
|
||||||
|
return availablePresentMode;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return VK_PRESENT_MODE_FIFO_KHR;
|
||||||
|
}
|
||||||
|
|
||||||
|
VkExtent2D chooseSwapExtent(const VkSurfaceCapabilitiesKHR& capabilities, GLFWwindow* window) {
|
||||||
|
if (capabilities.currentExtent.width != std::numeric_limits<uint32_t>::max()) {
|
||||||
|
return capabilities.currentExtent;
|
||||||
|
} else {
|
||||||
|
int width, height;
|
||||||
|
glfwGetFramebufferSize(window, &width, &height);
|
||||||
|
|
||||||
|
VkExtent2D actualExtent = {
|
||||||
|
static_cast<uint32_t>(width),
|
||||||
|
static_cast<uint32_t>(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;
|
||||||
|
}
|
||||||
|
}
|
30
renderer/aethiopicus/vkutil.hpp
Normal file
30
renderer/aethiopicus/vkutil.hpp
Normal file
|
@ -0,0 +1,30 @@
|
||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include <optional>
|
||||||
|
#include <vulkan/vulkan_core.h>
|
||||||
|
|
||||||
|
#define GLFW_INCLUDE_VULKAN
|
||||||
|
#include <GLFW/glfw3.h>
|
||||||
|
|
||||||
|
struct QueueFamilyIndicies {
|
||||||
|
std::optional<uint32_t> graphicsFamily;
|
||||||
|
std::optional<uint32_t> presentFamily;
|
||||||
|
|
||||||
|
bool isComplete() { return graphicsFamily.has_value() && presentFamily.has_value(); }
|
||||||
|
};
|
||||||
|
|
||||||
|
struct SwapChainSupportDetails {
|
||||||
|
VkSurfaceCapabilitiesKHR capabilities;
|
||||||
|
std::vector<VkSurfaceFormatKHR> formats;
|
||||||
|
std::vector<VkPresentModeKHR> presentModes;
|
||||||
|
};
|
||||||
|
|
||||||
|
extern QueueFamilyIndicies findQueueFamilies(VkPhysicalDevice device, VkSurfaceKHR surface);
|
||||||
|
|
||||||
|
extern SwapChainSupportDetails querySwapChainSupport(VkPhysicalDevice device, VkSurfaceKHR surface);
|
||||||
|
|
||||||
|
extern VkSurfaceFormatKHR chooseSwapSurfaceFormat(const std::vector<VkSurfaceFormatKHR>& availableFormats);
|
||||||
|
|
||||||
|
extern VkPresentModeKHR chooseSwapPresentMode(const std::vector<VkPresentModeKHR>& availablePresentModes);
|
||||||
|
|
||||||
|
extern VkExtent2D chooseSwapExtent(const VkSurfaceCapabilitiesKHR& capabilities, GLFWwindow* window);
|
|
@ -1,32 +0,0 @@
|
||||||
#include <iostream>
|
|
||||||
#include <string>
|
|
||||||
#include <fmt/format.h>
|
|
||||||
#include "font.hpp"
|
|
||||||
|
|
||||||
namespace hibis::rsdl {
|
|
||||||
Font::Font(std::string path, uint size) {
|
|
||||||
this->mSize = size;
|
|
||||||
this->mPath = path;
|
|
||||||
loadFont();
|
|
||||||
}
|
|
||||||
|
|
||||||
Font::~Font() {
|
|
||||||
TTF_CloseFont(mLoadedFont);
|
|
||||||
}
|
|
||||||
|
|
||||||
void Font::loadFont(bool reload) {
|
|
||||||
// If already loaded, close font
|
|
||||||
if (mLoadedFont != NULL) {
|
|
||||||
TTF_CloseFont(mLoadedFont);
|
|
||||||
mLoadedFont = NULL;
|
|
||||||
}
|
|
||||||
|
|
||||||
mLoadedFont = TTF_OpenFont(mPath.c_str(), mSize);
|
|
||||||
|
|
||||||
// Do the message
|
|
||||||
if (!mDidReload) {
|
|
||||||
std::cout << fmt::format((reload ? "Reloaded font from" : "Loaded font at") + (std::string)" {}", mPath) << std::endl;
|
|
||||||
mDidReload = reload;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -1,27 +0,0 @@
|
||||||
#pragma once
|
|
||||||
|
|
||||||
#include <string>
|
|
||||||
#include <resources/resource.hpp>
|
|
||||||
|
|
||||||
#include <SDL2/SDL_ttf.h>
|
|
||||||
|
|
||||||
namespace hibis::rsdl {
|
|
||||||
class Font : public Resource {
|
|
||||||
public:
|
|
||||||
Font(std::string path, uint size);
|
|
||||||
|
|
||||||
~Font();
|
|
||||||
|
|
||||||
uint getFontSize() { return mSize; }
|
|
||||||
|
|
||||||
void setFontSize(uint newSize) { mSize = newSize; loadFont(true); }
|
|
||||||
|
|
||||||
TTF_Font* mLoadedFont = NULL;
|
|
||||||
private:
|
|
||||||
void loadFont(bool reload = false);
|
|
||||||
uint mSize;
|
|
||||||
std::string mPath;
|
|
||||||
|
|
||||||
bool mDidReload = false;
|
|
||||||
};
|
|
||||||
}
|
|
|
@ -1,101 +0,0 @@
|
||||||
#include <SDL2/SDL_ttf.h>
|
|
||||||
|
|
||||||
#include <fmt/format.h>
|
|
||||||
#include "rsdl.hpp"
|
|
||||||
#include "resources/font.hpp"
|
|
||||||
|
|
||||||
#include <pragmautil.hpp>
|
|
||||||
|
|
||||||
namespace hibis::rsdl {
|
|
||||||
RSDL::RSDL(std::string title, IntVec2 size, LoggerCallback callback) {
|
|
||||||
mLoggerCallback = callback;
|
|
||||||
|
|
||||||
SDL_Init(SDL_INIT_VIDEO);
|
|
||||||
|
|
||||||
// Create window. `title` is cast to a char* here as tostd::stringz returns an immutable char* (causing an error)
|
|
||||||
mWindow = SDL_CreateWindow(title.c_str(), SDL_WINDOWPOS_UNDEFINED, SDL_WINDOWPOS_UNDEFINED,
|
|
||||||
size.x, size.y, SDL_WINDOW_RESIZABLE);
|
|
||||||
|
|
||||||
if (mWindow == NULL) {
|
|
||||||
mLoggerCallback(Fatal, fmt::format("Couldn't create window! what: {}", SDL_GetError()));
|
|
||||||
SDL_Quit();
|
|
||||||
exit(1);
|
|
||||||
}
|
|
||||||
|
|
||||||
mRendererContext = SDL_CreateRenderer(mWindow, -1, SDL_RENDERER_ACCELERATED);
|
|
||||||
|
|
||||||
if (mRendererContext == NULL) {
|
|
||||||
mLoggerCallback(Fatal, fmt::format("Couldn't create renderer! what: {}", SDL_GetError()));
|
|
||||||
SDL_DestroyWindow(mWindow);
|
|
||||||
SDL_Quit();
|
|
||||||
exit(1);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (TTF_Init() != 0) {
|
|
||||||
mLoggerCallback(Fatal, fmt::format("Couldn't load SDL_TTF! what: %d", TTF_GetError()));
|
|
||||||
SDL_DestroyRenderer(mRendererContext);
|
|
||||||
SDL_DestroyWindow(mWindow);
|
|
||||||
SDL_Quit();
|
|
||||||
TTF_Quit();
|
|
||||||
exit(1);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
RSDL::~RSDL() {
|
|
||||||
TTF_Quit();
|
|
||||||
SDL_DestroyRenderer(mRendererContext);
|
|
||||||
SDL_DestroyWindow(mWindow);
|
|
||||||
SDL_Quit();
|
|
||||||
}
|
|
||||||
|
|
||||||
void RSDL::clearScreen(Color col) {
|
|
||||||
SDL_SetRenderDrawColor(mRendererContext, col.r, col.g, col.b, col.a);
|
|
||||||
SDL_RenderClear(mRendererContext);
|
|
||||||
}
|
|
||||||
|
|
||||||
void RSDL::renderCurrent() {
|
|
||||||
SDL_UpdateWindowSurface(mWindow);
|
|
||||||
SDL_RenderPresent(mRendererContext);
|
|
||||||
}
|
|
||||||
|
|
||||||
void RSDL::drawText(Resource* resource, std::string text, IntVec2 pos, Color color) {
|
|
||||||
WARNING("(Tulip, this is abysmal) Avoid remaking textures every time text has to be drawn")
|
|
||||||
if (Font* font = (Font*)resource) {
|
|
||||||
SDL_Surface* textSurface = TTF_RenderText_Solid(font->mLoadedFont, text.c_str(), SDL_Color {color.r, color.g, color.b, color.a });
|
|
||||||
SDL_Texture* textTexture = SDL_CreateTextureFromSurface(mRendererContext, textSurface);
|
|
||||||
|
|
||||||
SDL_Rect textRect;
|
|
||||||
textRect.x = pos.x;
|
|
||||||
textRect.y = pos.y;
|
|
||||||
|
|
||||||
TTF_SizeText(font->mLoadedFont, text.c_str(), &textRect.w, &textRect.h);
|
|
||||||
|
|
||||||
SDL_RenderCopy(mRendererContext, textTexture, NULL, &textRect);
|
|
||||||
|
|
||||||
SDL_DestroyTexture(textTexture);
|
|
||||||
SDL_FreeSurface(textSurface);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
void RSDL::drawTexture(Texture* resource, float scale, hibis::IntVec2 pos) {
|
|
||||||
if (resource->mData.empty()) return;
|
|
||||||
|
|
||||||
TODO("this nonsense")
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
void RSDL::preDraw() {}
|
|
||||||
void RSDL::postDraw() {}
|
|
||||||
|
|
||||||
void RSDL::update() {
|
|
||||||
SDL_Event event;
|
|
||||||
|
|
||||||
while(SDL_PollEvent(&event)) {
|
|
||||||
if (event.type == SDL_QUIT) mKeepOpen = false;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
void RSDL::setWindowTitle(std::string title) {
|
|
||||||
SDL_SetWindowTitle(mWindow, title.c_str());
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -1,42 +0,0 @@
|
||||||
#pragma once
|
|
||||||
|
|
||||||
#include <format>
|
|
||||||
#include <string>
|
|
||||||
#include <stdexcept>
|
|
||||||
#include <SDL2/SDL.h>
|
|
||||||
|
|
||||||
#include <callback.hpp>
|
|
||||||
#include <math/types.hpp>
|
|
||||||
#include <renderer/renderer.hpp>
|
|
||||||
|
|
||||||
namespace hibis::rsdl {
|
|
||||||
/** \class RSDL
|
|
||||||
* Renderer implementation using SDL2 Renderer for the Hibis game engine
|
|
||||||
*/
|
|
||||||
class RSDL : public Renderer {
|
|
||||||
public:
|
|
||||||
RSDL(std::string title, IntVec2 size, LoggerCallback callback);
|
|
||||||
|
|
||||||
~RSDL();
|
|
||||||
|
|
||||||
void clearScreen(Color col = Color {0, 0, 0, 255}) override;
|
|
||||||
|
|
||||||
void renderCurrent() override;
|
|
||||||
|
|
||||||
void drawText(Resource* resource, std::string text, IntVec2 pos, Color color) override;
|
|
||||||
void drawTexture(Texture* resource, float scale, hibis::IntVec2 pos) override;
|
|
||||||
|
|
||||||
void preDraw() override;
|
|
||||||
void postDraw() override;
|
|
||||||
|
|
||||||
void update() override;
|
|
||||||
|
|
||||||
void setWindowTitle(std::string title) override;
|
|
||||||
|
|
||||||
private:
|
|
||||||
SDL_Window* mWindow;
|
|
||||||
SDL_Renderer* mRendererContext;
|
|
||||||
|
|
||||||
LoggerCallback mLoggerCallback;
|
|
||||||
};
|
|
||||||
}
|
|
262
renderer/rwgpu/rwgpu.cpp
Normal file
262
renderer/rwgpu/rwgpu.cpp
Normal file
|
@ -0,0 +1,262 @@
|
||||||
|
#define WEBGPU_CPP_IMPLEMENTATION
|
||||||
|
#include <fmt/format.h>
|
||||||
|
#include <sstream>
|
||||||
|
#include <webgpu/webgpu.hpp>
|
||||||
|
|
||||||
|
#include <glfw3webgpu/glfw3webgpu.h>
|
||||||
|
|
||||||
|
#include <pragmautil.hpp>
|
||||||
|
|
||||||
|
#include "rwgpu.hpp"
|
||||||
|
|
||||||
|
void onWindowResize(GLFWwindow* window, int width, int height) {
|
||||||
|
auto that = reinterpret_cast<hibis::rwgpu::RWGPU*>(glfwGetWindowUserPointer(window));
|
||||||
|
|
||||||
|
// Resize that so that this can work
|
||||||
|
if (that != nullptr) that->resize(width, height);
|
||||||
|
}
|
||||||
|
|
||||||
|
namespace hibis::rwgpu {
|
||||||
|
RWGPU::RWGPU(std::string title, IntVec2 size, LoggerCallback callback) : Renderer(callback) {
|
||||||
|
wgpu::InstanceDescriptor instanceDescriptor = {};
|
||||||
|
wgpu::RequestAdapterOptions reqAdaptOpts = {};
|
||||||
|
wgpu::DeviceDescriptor deviceDescriptor = {};
|
||||||
|
|
||||||
|
deviceDescriptor.nextInChain = nullptr;
|
||||||
|
deviceDescriptor.label = "My Device"; // anything works here, that's your call
|
||||||
|
deviceDescriptor.requiredFeaturesCount = 0; // we do not require any specific feature
|
||||||
|
deviceDescriptor.requiredLimits = nullptr; // we do not require any specific limit
|
||||||
|
deviceDescriptor.defaultQueue.nextInChain = nullptr;
|
||||||
|
deviceDescriptor.defaultQueue.label = "The default queue";
|
||||||
|
|
||||||
|
mWebGPUInstance = wgpu::createInstance(instanceDescriptor);
|
||||||
|
mWebGPUAdapter = requestAdapter(&reqAdaptOpts);
|
||||||
|
mWebGPUDevice = requestDevice(&deviceDescriptor);
|
||||||
|
mWebGPUQueue = mWebGPUDevice.getQueue();
|
||||||
|
|
||||||
|
if (!mWebGPUInstance) {
|
||||||
|
mLogger(Fatal, "Could not initialize WebGPU!");
|
||||||
|
exit(1);
|
||||||
|
} else {
|
||||||
|
const void * address = static_cast<const void*>(mWebGPUInstance);
|
||||||
|
std::stringstream pointerAsStream;
|
||||||
|
pointerAsStream << address;
|
||||||
|
mLogger(Information, fmt::format("WGPU Instance Addr: {}", pointerAsStream.str()));
|
||||||
|
}
|
||||||
|
|
||||||
|
glfwInit();
|
||||||
|
glfwWindowHint(GLFW_CLIENT_API, GLFW_NO_API);
|
||||||
|
|
||||||
|
mWindow = glfwCreateWindow(size.x, size.y, title.c_str(), NULL, NULL);
|
||||||
|
glfwSetWindowUserPointer(mWindow, this);
|
||||||
|
glfwSetFramebufferSizeCallback(mWindow, onWindowResize);
|
||||||
|
|
||||||
|
mWebGPUSurface = glfwGetWGPUSurface(mWebGPUInstance, mWindow);
|
||||||
|
|
||||||
|
size_t featureCount = wgpuAdapterEnumerateFeatures(mWebGPUAdapter, nullptr);
|
||||||
|
|
||||||
|
// Allocate memory (could be a new, or a malloc() if this were a C program)
|
||||||
|
mWebGPUFeatures.resize(featureCount);
|
||||||
|
|
||||||
|
// Call the function a second time, with a non-null return address
|
||||||
|
wgpuAdapterEnumerateFeatures(mWebGPUAdapter, mWebGPUFeatures.data());
|
||||||
|
|
||||||
|
mLogger(Information, "Adapter features: ");
|
||||||
|
for (wgpu::FeatureName f : mWebGPUFeatures) {
|
||||||
|
mLogger(Information, fmt::format("WebGPU Feature: {}", f));
|
||||||
|
}
|
||||||
|
|
||||||
|
auto onDeviceError = [](WGPUErrorType type, char const* message, void* /* pUserData */) {
|
||||||
|
std::cout << "Uncaptured device error: type " << type;
|
||||||
|
if (message) std::cout << " (" << message << ")";
|
||||||
|
std::cout << std::endl;
|
||||||
|
};
|
||||||
|
|
||||||
|
wgpuDeviceSetUncapturedErrorCallback(mWebGPUDevice, onDeviceError, nullptr /* pUserData */);
|
||||||
|
|
||||||
|
// Not implemented, causes a panic
|
||||||
|
// auto onQueueWorkDone = [](WGPUQueueWorkDoneStatus status, void* /* pUserData */) {
|
||||||
|
// std::cout << "Queued work finished with status: " << status << std::endl;
|
||||||
|
// };
|
||||||
|
// wgpuQueueOnSubmittedWorkDone(mWebGPUQueue, onQueueWorkDone, nullptr /* pUserData */);
|
||||||
|
|
||||||
|
setupSwapChain(size.x, size.y);
|
||||||
|
}
|
||||||
|
|
||||||
|
RWGPU::~RWGPU() {
|
||||||
|
glfwDestroyWindow(mWindow);
|
||||||
|
|
||||||
|
mWebGPUSwapChain.drop();
|
||||||
|
mWebGPUSurface.drop();
|
||||||
|
mWebGPUDevice.drop();
|
||||||
|
mWebGPUAdapter.drop();
|
||||||
|
mWebGPUInstance.drop();
|
||||||
|
}
|
||||||
|
|
||||||
|
WGPUAdapter RWGPU::requestAdapter(WGPURequestAdapterOptions const * options) {
|
||||||
|
// A simple structure holding the local information shared with the
|
||||||
|
// onAdapterRequestEnded callback.
|
||||||
|
struct UserData {
|
||||||
|
WGPUAdapter adapter = nullptr;
|
||||||
|
bool requestEnded = false;
|
||||||
|
};
|
||||||
|
UserData userData;
|
||||||
|
|
||||||
|
// Callback called by wgpuInstanceRequestAdapter when the request returns
|
||||||
|
// This is a C++ lambda function, but could be any function defined in the
|
||||||
|
// global scope. It must be non-capturing (the brackets [] are empty) so
|
||||||
|
// that it behaves like a regular C function pointer, which is what
|
||||||
|
// wgpuInstanceRequestAdapter expects (WebGPU being a C API). The workaround
|
||||||
|
// is to convey what we want to capture through the pUserData pointer,
|
||||||
|
// provided as the last argument of wgpuInstanceRequestAdapter and received
|
||||||
|
// by the callback as its last argument.
|
||||||
|
auto onAdapterRequestEnded = [](WGPURequestAdapterStatus status, WGPUAdapter adapter, char const * message, void * pUserData) {
|
||||||
|
UserData& userData = *reinterpret_cast<UserData*>(pUserData);
|
||||||
|
if (status == WGPURequestAdapterStatus_Success) {
|
||||||
|
userData.adapter = adapter;
|
||||||
|
} else {
|
||||||
|
std::cout << "Could not get WebGPU adapter: " << message << std::endl;
|
||||||
|
}
|
||||||
|
userData.requestEnded = true;
|
||||||
|
};
|
||||||
|
|
||||||
|
// Call to the WebGPU request adapter procedure
|
||||||
|
wgpuInstanceRequestAdapter(
|
||||||
|
mWebGPUInstance /* equivalent of navigator.gpu */,
|
||||||
|
options,
|
||||||
|
onAdapterRequestEnded,
|
||||||
|
(void*)&userData
|
||||||
|
);
|
||||||
|
|
||||||
|
// In theory we should wait until onAdapterReady has been called, which
|
||||||
|
// could take some time (what the 'await' keyword does in the JavaScript
|
||||||
|
// code). In practice, we know that when the wgpuInstanceRequestAdapter()
|
||||||
|
// function returns its callback has been called.
|
||||||
|
assert(userData.requestEnded);
|
||||||
|
|
||||||
|
return userData.adapter;
|
||||||
|
}
|
||||||
|
|
||||||
|
WGPUDevice RWGPU::requestDevice(WGPUDeviceDescriptor const * descriptor) {
|
||||||
|
struct UserData {
|
||||||
|
WGPUDevice device = nullptr;
|
||||||
|
bool requestEnded = false;
|
||||||
|
};
|
||||||
|
UserData userData;
|
||||||
|
|
||||||
|
auto onDeviceRequestEnded = [](WGPURequestDeviceStatus status, WGPUDevice device, char const * message, void * pUserData) {
|
||||||
|
UserData& userData = *reinterpret_cast<UserData*>(pUserData);
|
||||||
|
if (status == WGPURequestDeviceStatus_Success) {
|
||||||
|
userData.device = device;
|
||||||
|
} else {
|
||||||
|
std::cout << "Could not get WebGPU device: " << message << std::endl;
|
||||||
|
}
|
||||||
|
userData.requestEnded = true;
|
||||||
|
};
|
||||||
|
|
||||||
|
wgpuAdapterRequestDevice(
|
||||||
|
mWebGPUAdapter,
|
||||||
|
descriptor,
|
||||||
|
onDeviceRequestEnded,
|
||||||
|
(void*)&userData
|
||||||
|
);
|
||||||
|
|
||||||
|
assert(userData.requestEnded);
|
||||||
|
|
||||||
|
return userData.device;
|
||||||
|
}
|
||||||
|
|
||||||
|
void RWGPU::setupSwapChain(unsigned int width, unsigned int height) {
|
||||||
|
// Drop swap chain if it currently exists
|
||||||
|
if (mWebGPUSwapChain) mWebGPUSwapChain.drop();
|
||||||
|
|
||||||
|
// Remake swap chain
|
||||||
|
WGPUSwapChainDescriptor swapChainDesc = {};
|
||||||
|
swapChainDesc.nextInChain = nullptr;
|
||||||
|
swapChainDesc.width = width;
|
||||||
|
swapChainDesc.height = height;
|
||||||
|
|
||||||
|
WGPUTextureFormat swapChainFormat = wgpuSurfaceGetPreferredFormat(mWebGPUSurface, mWebGPUAdapter);
|
||||||
|
swapChainDesc.format = swapChainFormat;
|
||||||
|
|
||||||
|
swapChainDesc.usage = WGPUTextureUsage_RenderAttachment;
|
||||||
|
swapChainDesc.presentMode = WGPUPresentMode_Fifo;
|
||||||
|
|
||||||
|
mWebGPUSwapChain = wgpuDeviceCreateSwapChain(mWebGPUDevice, mWebGPUSurface, &swapChainDesc);
|
||||||
|
}
|
||||||
|
|
||||||
|
void RWGPU::clearScreen(Color col) {
|
||||||
|
mCurrentClearColor = {col.r, col.g, col.b, col.a};
|
||||||
|
}
|
||||||
|
|
||||||
|
void RWGPU::renderCurrent() {
|
||||||
|
wgpu::TextureView nextTexture = mWebGPUSwapChain.getCurrentTextureView();
|
||||||
|
if (!nextTexture) {
|
||||||
|
mLogger(Error, "Couldn't create next texture");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
WGPUCommandEncoderDescriptor encoderDesc = {};
|
||||||
|
encoderDesc.nextInChain = nullptr;
|
||||||
|
encoderDesc.label = "HibisDrawCommandEncoder";
|
||||||
|
WGPUCommandEncoder encoder = mWebGPUDevice.createCommandEncoder(encoderDesc);
|
||||||
|
|
||||||
|
TODO("Implement Deferred Renderer")
|
||||||
|
|
||||||
|
WGPURenderPassDescriptor renderPassDesc = {};
|
||||||
|
|
||||||
|
WGPURenderPassColorAttachment renderPassColorAttachment = {};
|
||||||
|
|
||||||
|
renderPassDesc.colorAttachmentCount = 1;
|
||||||
|
renderPassDesc.colorAttachments = &renderPassColorAttachment;
|
||||||
|
|
||||||
|
renderPassColorAttachment.view = nextTexture;
|
||||||
|
renderPassColorAttachment.resolveTarget = nullptr;
|
||||||
|
renderPassColorAttachment.loadOp = WGPULoadOp_Clear;
|
||||||
|
renderPassColorAttachment.storeOp = WGPUStoreOp_Store;
|
||||||
|
renderPassColorAttachment.clearValue = mCurrentClearColor;
|
||||||
|
renderPassDesc.depthStencilAttachment = nullptr;
|
||||||
|
|
||||||
|
renderPassDesc.timestampWriteCount = 0;
|
||||||
|
renderPassDesc.timestampWrites = nullptr;
|
||||||
|
renderPassDesc.nextInChain = nullptr;
|
||||||
|
|
||||||
|
wgpu::RenderPassEncoder renderPass = wgpuCommandEncoderBeginRenderPass(encoder, &renderPassDesc);
|
||||||
|
wgpuRenderPassEncoderEnd(renderPass);
|
||||||
|
|
||||||
|
WGPUCommandBufferDescriptor cmdBufferDescriptor = {};
|
||||||
|
cmdBufferDescriptor.nextInChain = nullptr;
|
||||||
|
cmdBufferDescriptor.label = "Command buffer";
|
||||||
|
WGPUCommandBuffer command = wgpuCommandEncoderFinish(encoder, &cmdBufferDescriptor);
|
||||||
|
wgpuQueueSubmit(mWebGPUQueue, 1, &command);
|
||||||
|
|
||||||
|
mWebGPUSwapChain.present();
|
||||||
|
nextTexture.drop();
|
||||||
|
}
|
||||||
|
|
||||||
|
void RWGPU::drawText(Resource* resource, std::string text, IntVec2 pos, Color color) {}
|
||||||
|
void RWGPU::drawTexture(Resource* resource, IntRect size) {}
|
||||||
|
|
||||||
|
void RWGPU::useShader(Shader* shader, Point2D points[3]) {}
|
||||||
|
void RWGPU::stopUsingShaders() {}
|
||||||
|
|
||||||
|
void RWGPU::preDraw() {}
|
||||||
|
void RWGPU::postDraw() {}
|
||||||
|
|
||||||
|
void RWGPU::update() {
|
||||||
|
mKeepOpen = !glfwWindowShouldClose(mWindow);
|
||||||
|
glfwPollEvents();
|
||||||
|
}
|
||||||
|
|
||||||
|
void RWGPU::compileShader(Shader* shader) {}
|
||||||
|
void RWGPU::toggleWireframe() {}
|
||||||
|
|
||||||
|
void RWGPU::setWindowTitle(std::string title) {
|
||||||
|
glfwSetWindowTitle(mWindow, title.c_str());
|
||||||
|
}
|
||||||
|
|
||||||
|
void RWGPU::resize(unsigned int width, unsigned int height) {
|
||||||
|
if (width < 0 || height < 0) return;
|
||||||
|
glfwSetWindowSize(mWindow, width, height);
|
||||||
|
setupSwapChain(width, height);
|
||||||
|
}
|
||||||
|
}
|
58
renderer/rwgpu/rwgpu.hpp
Normal file
58
renderer/rwgpu/rwgpu.hpp
Normal file
|
@ -0,0 +1,58 @@
|
||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include <vector>
|
||||||
|
#include <webgpu/webgpu.hpp>
|
||||||
|
|
||||||
|
#define GLFW_INCLUDE_NONE
|
||||||
|
#include <GLFW/glfw3.h>
|
||||||
|
|
||||||
|
#include <renderer/renderer.hpp>
|
||||||
|
|
||||||
|
namespace hibis::rwgpu {
|
||||||
|
class RWGPU : public Renderer {
|
||||||
|
public:
|
||||||
|
RWGPU(std::string title, IntVec2 size, LoggerCallback callback);
|
||||||
|
~RWGPU();
|
||||||
|
|
||||||
|
// Draw
|
||||||
|
void clearScreen(Color col = Color {0, 0, 0, 255}) override;
|
||||||
|
void renderCurrent() override;
|
||||||
|
|
||||||
|
void drawText(Resource* resource, std::string text, IntVec2 pos, Color color) override;
|
||||||
|
void drawTexture(Resource* resource, IntRect size) override;
|
||||||
|
|
||||||
|
void useShader(Shader* shader, Point2D points[3]) override;
|
||||||
|
void stopUsingShaders() override;
|
||||||
|
|
||||||
|
// Pre and Post draw
|
||||||
|
void preDraw() override;
|
||||||
|
void postDraw() override;
|
||||||
|
|
||||||
|
// Update
|
||||||
|
void update() override;
|
||||||
|
|
||||||
|
// Util
|
||||||
|
void compileShader(Shader* shader) override;
|
||||||
|
void toggleWireframe() override;
|
||||||
|
void setWindowTitle(std::string title) override;
|
||||||
|
void resize(unsigned int width, unsigned int height);
|
||||||
|
|
||||||
|
std::vector<WGPUFeatureName> mWebGPUFeatures;
|
||||||
|
private:
|
||||||
|
WGPUAdapter requestAdapter(WGPURequestAdapterOptions const * options);
|
||||||
|
WGPUDevice requestDevice(WGPUDeviceDescriptor const * descriptor);
|
||||||
|
|
||||||
|
void setupSwapChain(unsigned int width, unsigned int height);
|
||||||
|
|
||||||
|
GLFWwindow* mWindow;
|
||||||
|
wgpu::Instance mWebGPUInstance = nullptr;
|
||||||
|
wgpu::Adapter mWebGPUAdapter = nullptr;
|
||||||
|
wgpu::Device mWebGPUDevice = nullptr;
|
||||||
|
wgpu::Queue mWebGPUQueue = nullptr;
|
||||||
|
wgpu::SwapChain mWebGPUSwapChain = nullptr;
|
||||||
|
|
||||||
|
wgpu::Surface mWebGPUSurface = nullptr;
|
||||||
|
|
||||||
|
wgpu::Color mCurrentClearColor = {0.9, 0.1, 0.2, 1.0};
|
||||||
|
};
|
||||||
|
}
|
41
test/app.cpp
41
test/app.cpp
|
@ -1,8 +1,7 @@
|
||||||
#include <logging/types.hpp>
|
#include <logging/types.hpp>
|
||||||
#include <fmt/format.h>
|
#include <fmt/format.h>
|
||||||
#include <renderer/renderer.hpp>
|
#include <renderer/renderer.hpp>
|
||||||
#include <rsdl.hpp>
|
#include <rwgpu.hpp>
|
||||||
#include <resources/font.hpp>
|
|
||||||
#include <resources/font.hpp>
|
#include <resources/font.hpp>
|
||||||
#include <string>
|
#include <string>
|
||||||
#include <iostream>
|
#include <iostream>
|
||||||
|
@ -18,25 +17,25 @@ WARNING("Please avoid using MSVC in C++ projects utilising std::chrono and std::
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
using namespace hibis;
|
using namespace hibis;
|
||||||
using namespace hibis::rsdl;
|
using namespace hibis::rwgpu;
|
||||||
|
|
||||||
void logger(LoggingSeverity severity, std::string message) {
|
void logger(LoggingSeverity severity, std::string message) {
|
||||||
std::string sevString;
|
std::string sevString;
|
||||||
switch (severity) {
|
switch (severity) {
|
||||||
case Message:
|
case Message:
|
||||||
sevString = "Message";
|
sevString = " Message ";
|
||||||
break;
|
break;
|
||||||
case Information:
|
case Information:
|
||||||
sevString = "Information";
|
sevString = "Information";
|
||||||
break;
|
break;
|
||||||
case Warning:
|
case Warning:
|
||||||
sevString = "Warning";
|
sevString = " Warning ";
|
||||||
break;
|
break;
|
||||||
case Error:
|
case Error:
|
||||||
sevString = "Error";
|
sevString = " Error ";
|
||||||
break;
|
break;
|
||||||
case Fatal:
|
case Fatal:
|
||||||
sevString = "Fatal";
|
sevString = " Fatal ";
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -44,27 +43,34 @@ void logger(LoggingSeverity severity, std::string message) {
|
||||||
}
|
}
|
||||||
|
|
||||||
int main() {
|
int main() {
|
||||||
RSDL renderer = RSDL("test", IntVec2 {800, 600}, &logger);
|
logger(Information, fmt::format("PWD: {}", std::getenv("PWD")));
|
||||||
|
RWGPU renderer = RWGPU("test", IntVec2 {800, 800}, &logger);
|
||||||
Engine engine = Engine(&renderer, &logger);
|
Engine engine = Engine(&renderer, &logger);
|
||||||
|
/*
|
||||||
|
|
||||||
#ifdef _WIN32
|
#ifdef _WIN32
|
||||||
Font font = Font("C:\\Windows\\Fonts\\Arial.ttf", 16);
|
Font font = Font(engine.mFreeTypeLibrary, "C:\\Windows\\Fonts\\Arial.ttf", 16);
|
||||||
#else
|
#else
|
||||||
Font font = Font("/usr/share/fonts/noto/NotoSans-Light.ttf", 16);
|
Font font = Font(engine.mFreeTypeLibrary, "/usr/share/fonts/noto/NotoSans-Light.ttf", 16);
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
Texture image = Texture((char*)"test.png");
|
Texture image = Texture((char*)"test.png");
|
||||||
|
|
||||||
unsigned char red = 0;
|
system("cp ../test/test.vert ./ && cp ../test/test.frag ./");
|
||||||
|
Shader shader = Shader("test.vert", "test.frag");
|
||||||
|
renderer.compileShader(&shader);
|
||||||
|
|
||||||
|
uint8_t red = 0;
|
||||||
bool increaseRed = true;
|
bool increaseRed = true;
|
||||||
bool increaseSize = true;
|
bool increaseSize = true;
|
||||||
uint size = 16;
|
uint size = 16;
|
||||||
uint f = 0;
|
uint f = 0;
|
||||||
|
|
||||||
|
//Point2D points[3] = {{-0.5f, -0.5f}, {0.5f, -0.5f}, {0.0f, 0.5f}};
|
||||||
|
*/
|
||||||
logger(Information, "Started Hibis test app! BEHOLD: Colours.");
|
logger(Information, "Started Hibis test app! BEHOLD: Colours.");
|
||||||
while (renderer.mKeepOpen) {
|
while (renderer.mKeepOpen) {
|
||||||
engine.runNodeProcesses();
|
/*
|
||||||
|
|
||||||
// Colour changing background!
|
// Colour changing background!
|
||||||
if ((red == 255 && increaseRed) || (red == 0 && !increaseRed)) {
|
if ((red == 255 && increaseRed) || (red == 0 && !increaseRed)) {
|
||||||
increaseRed = !increaseRed;
|
increaseRed = !increaseRed;
|
||||||
|
@ -82,13 +88,14 @@ int main() {
|
||||||
else size -= 2;
|
else size -= 2;
|
||||||
font.setFontSize(size);
|
font.setFontSize(size);
|
||||||
}
|
}
|
||||||
|
*/
|
||||||
|
|
||||||
// Clear screen then sleep for ~16ms
|
// Clear screen then sleep for ~16ms
|
||||||
renderer.clearScreen(Color {red, 0, 0, 255});
|
//renderer.clearScreen(Color {red, 0, 0, 255});
|
||||||
renderer.drawText(&font, "Testing Text", IntVec2 {0, 0}, Color {255, 255, 255, 255});
|
//renderer.useShader(&shader, points);
|
||||||
renderer.drawTexture(&image, 1.0f, IntVec2 {10, 10});
|
//renderer.drawText(&font, "Testing Text", IntVec2 {0, 0}, Color {255, 255, 255, 255});
|
||||||
|
//renderer.drawTexture(&image, 1.0f, IntVec2 {10, 10});
|
||||||
|
|
||||||
engine.drawNodes();
|
|
||||||
renderer.renderCurrent();
|
renderer.renderCurrent();
|
||||||
renderer.update();
|
renderer.update();
|
||||||
std::this_thread::sleep_for(std::chrono::milliseconds(16));
|
std::this_thread::sleep_for(std::chrono::milliseconds(16));
|
||||||
|
|
Loading…
Reference in a new issue