Initial commit.

This commit is contained in:
Patrick 2025-01-31 17:16:39 +01:00
commit 2d531dafa9
8 changed files with 1732 additions and 0 deletions

8
SModule Normal file
View File

@ -0,0 +1,8 @@
Import('env')
LIB_CONFIG = {
'CPPPATH': [env.Dir('public')]
}
Return('LIB_CONFIG')

94
public/sdlpp/common.hpp Normal file
View File

@ -0,0 +1,94 @@
#pragma once
#if !defined(SDL_GPU_TEST_PRIVATE_SDL_GPU_TEST_SDLPP_COMMON_HPP_INCLUDED)
#define SDL_GPU_TEST_PRIVATE_SDL_GPU_TEST_SDLPP_COMMON_HPP_INCLUDED 1
#include <cstdint>
#include <optional>
#include <span>
#include <stdexcept>
#include <utility>
#include <vector>
#include <mijin/debug/assert.hpp>
#include <mijin/util/bitflags.hpp>
#include <SDL3/SDL.h>
#include <mijin/util/winundef.hpp>
namespace sdlpp
{
template<typename THandle, typename TConcrete>
class Base
{
protected:
THandle* mHandle = nullptr;
protected:
Base() noexcept = default;
explicit Base(THandle* handle) noexcept : mHandle(handle) {}
Base(const Base&) noexcept = default;
Base(Base&& other) noexcept : mHandle(std::exchange(other.mHandle, nullptr)) {}
Base& operator=(const Base&) noexcept = default;
Base& operator=(Base&& other) noexcept
{
if (this != &other)
{
static_cast<TConcrete&>(*this).destroy();
mHandle = std::exchange(other.mHandle, nullptr);
}
return *this;
}
public:
~Base() noexcept
{
static_cast<TConcrete&>(*this).destroy();
}
auto operator<=>(const Base& other) const noexcept = default;
operator THandle*() const noexcept { return mHandle; }
explicit operator bool() const noexcept
{
return mHandle != nullptr;
}
bool operator!() const noexcept
{
return mHandle == nullptr;
}
};
template<typename THandle, typename TConcrete>
class BaseWithDevice : public Base<THandle, TConcrete>
{
protected:
SDL_GPUDevice* mDevice = nullptr;
protected:
BaseWithDevice() noexcept = default;
BaseWithDevice(THandle* handle, SDL_GPUDevice* device) noexcept : Base<THandle, TConcrete>(handle), mDevice(device) {}
BaseWithDevice(BaseWithDevice&& other) noexcept : Base<THandle, TConcrete>(std::exchange(other.mHandle, nullptr)),
mDevice(std::exchange(other.mDevice, nullptr)){}
BaseWithDevice& operator=(BaseWithDevice&& other) noexcept
{
if (this != &other)
{
mDevice = std::exchange(other.mDevice, nullptr);
Base<THandle, TConcrete>::operator=(std::move(other));
}
return *this;
}
public:
BaseWithDevice(const BaseWithDevice&) = delete;
BaseWithDevice& operator=(const BaseWithDevice&) = delete;
auto operator<=>(const BaseWithDevice& other) const noexcept = default;
};
class SDLError : public std::runtime_error
{
public:
SDLError() : std::runtime_error(SDL_GetError()) {}
};
} // namespace sdlpp
#endif // !defined(SDL_GPU_TEST_PRIVATE_SDL_GPU_TEST_SDLPP_COMMON_HPP_INCLUDED)

145
public/sdlpp/event.hpp Normal file
View File

@ -0,0 +1,145 @@
#pragma once
#if !defined(SDL_GPU_TEST_PRIVATE_SDL_GPU_TEST_SDLPP_EVENT_HPP_INCLUDED)
#define SDL_GPU_TEST_PRIVATE_SDL_GPU_TEST_SDLPP_EVENT_HPP_INCLUDED 1
#include <optional>
#include <variant>
#include "./common.hpp"
namespace sdlpp
{
//
// flags
//
struct MouseButtonFlags : mijin::BitFlags<MouseButtonFlags>
{
bool left : 1 = false;
bool middle : 1 = false;
bool right : 1 = false;
bool x1 : 1 = false;
bool x2 : 1 = false;
static MouseButtonFlags from(SDL_MouseButtonFlags base) noexcept
{
return std::bit_cast<MouseButtonFlags>(static_cast<std::uint8_t>(base));
}
};
//
// structs
//
struct Event
{
Uint64 timestamp;
template<typename TBase>
explicit Event(const TBase& base) noexcept : timestamp(base.timestamp) {}
};
struct KeyboardEvent : Event
{
SDL_WindowID windowID;
SDL_KeyboardID which;
SDL_Scancode scancode;
SDL_Keycode key;
SDL_Keymod mod;
bool down;
bool repeat;
explicit KeyboardEvent(const SDL_KeyboardEvent& base) noexcept : Event(base), windowID(base.windowID),
which(base.which), scancode(base.scancode), key(base.key), mod(base.mod), down(base.down), repeat(base.repeat)
{
}
};
struct MouseButtonEvent : Event
{
SDL_WindowID windowID;
SDL_MouseID which;
Uint8 button;
bool down;
Uint8 clicks;
float x;
float y;
MouseButtonEvent(const SDL_MouseButtonEvent& base) noexcept : Event(base), windowID(base.windowID), which(base.which),
button(base.button), down(base.down), clicks(base.clicks), x(base.x), y(base.y)
{
}
};
struct MouseMotionEvent : Event
{
SDL_WindowID windowID;
SDL_MouseID which;
MouseButtonFlags state;
float x;
float y;
float xrel;
float yrel;
MouseMotionEvent(const SDL_MouseMotionEvent& base) noexcept : Event(base), windowID(base.windowID), which(base.which),
state(MouseButtonFlags::from(base.state)), x(base.x), y(base.y), xrel(base.xrel), yrel(base.yrel)
{
}
};
struct GamepadButtonEvent : Event
{
SDL_JoystickID which;
Uint8 button;
bool down;
GamepadButtonEvent(const SDL_GamepadButtonEvent& base) noexcept : Event(base), which(base.which), button(base.button),
down(base.down)
{
}
};
struct QuitEvent : Event
{
explicit QuitEvent(const SDL_QuitEvent& base) noexcept : Event(base) {}
};
using sdl_event_t = std::variant<
KeyboardEvent,
MouseButtonEvent,
MouseMotionEvent,
GamepadButtonEvent,
QuitEvent
>;
[[nodiscard]]
inline std::optional<sdl_event_t> pollEvent() noexcept
{
SDL_Event event;
while (SDL_PollEvent(&event))
{
switch (event.type)
{
case SDL_EVENT_KEY_DOWN:
case SDL_EVENT_KEY_UP:
return KeyboardEvent(event.key);
case SDL_EVENT_MOUSE_BUTTON_DOWN:
case SDL_EVENT_MOUSE_BUTTON_UP:
return MouseButtonEvent(event.button);
case SDL_EVENT_MOUSE_MOTION:
return MouseMotionEvent(event.motion);
case SDL_EVENT_GAMEPAD_BUTTON_DOWN:
case SDL_EVENT_GAMEPAD_BUTTON_UP:
return GamepadButtonEvent(event.gbutton);
case SDL_EVENT_QUIT:
return QuitEvent(event.quit);
default:
// can't translate this yet
break;
}
}
return std::nullopt;
}
} // namespace sdlpp
#endif // !defined(SDL_GPU_TEST_PRIVATE_SDL_GPU_TEST_SDLPP_EVENT_HPP_INCLUDED)

68
public/sdlpp/gamepad.hpp Normal file
View File

@ -0,0 +1,68 @@
#pragma once
#if !defined(SDL_GPU_TEST_PRIVATE_SDL_GPU_TEST_SDLPP_GAMEPAD_HPP_INCLUDED)
#define SDL_GPU_TEST_PRIVATE_SDL_GPU_TEST_SDLPP_GAMEPAD_HPP_INCLUDED 1
#include "./common.hpp"
namespace sdlpp
{
enum class GamepadAxis
{
INVALID = SDL_GAMEPAD_AXIS_INVALID,
LEFTX = SDL_GAMEPAD_AXIS_LEFTX,
LEFTY = SDL_GAMEPAD_AXIS_LEFTY,
RIGHTX = SDL_GAMEPAD_AXIS_RIGHTX,
RIGHTY = SDL_GAMEPAD_AXIS_RIGHTY,
LEFT_TRIGGER = SDL_GAMEPAD_AXIS_LEFT_TRIGGER,
RIGHT_TRIGGER = SDL_GAMEPAD_AXIS_RIGHT_TRIGGER,
};
class Gamepad : public Base<SDL_Gamepad, Gamepad>
{
public:
Gamepad() noexcept = default;
Gamepad(Gamepad&&) noexcept = default;
Gamepad& operator=(Gamepad&&) = default;
void open(SDL_JoystickID instanceID)
{
MIJIN_ASSERT(mHandle == nullptr, "Gamepad already opened.");
mHandle = SDL_OpenGamepad(instanceID);
if (mHandle == nullptr)
{
throw SDLError();
}
}
void destroy() noexcept
{
if (mHandle != nullptr)
{
SDL_CloseGamepad(mHandle);
mHandle = nullptr;
}
}
[[nodiscard]]
Sint16 getAxis(GamepadAxis axis) const noexcept
{
return SDL_GetGamepadAxis(mHandle, static_cast<SDL_GamepadAxis>(axis));
}
};
[[nodiscard]]
inline std::vector<SDL_JoystickID> getGamepads() noexcept
{
int count = 0;
SDL_JoystickID* gamepads = SDL_GetGamepads(&count);
std::vector<SDL_JoystickID> result;
result.resize(count);
std::copy_n(gamepads, count, result.begin());
return result;
}
} // namespace sdlpp
#endif // !defined(SDL_GPU_TEST_PRIVATE_SDL_GPU_TEST_SDLPP_GAMEPAD_HPP_INCLUDED)

1210
public/sdlpp/gpu.hpp Normal file

File diff suppressed because it is too large Load Diff

20
public/sdlpp/keyboard.hpp Normal file
View File

@ -0,0 +1,20 @@
#pragma once
#if !defined(SDL_GPU_TEST_PRIVATE_SDL_GPU_TEST_SDLPP_KEYBOARD_HPP_INCLUDED)
#define SDL_GPU_TEST_PRIVATE_SDL_GPU_TEST_SDLPP_KEYBOARD_HPP_INCLUDED 1
#include "./common.hpp"
namespace sdlpp
{
[[nodiscard]]
inline std::span<const bool> getKeyboardState() noexcept
{
int numkeys = 0;
const bool* states = SDL_GetKeyboardState(&numkeys);
return {states, static_cast<std::size_t>(numkeys)};
}
} // namespace sdlpp
#endif // !defined(SDL_GPU_TEST_PRIVATE_SDL_GPU_TEST_SDLPP_KEYBOARD_HPP_INCLUDED)

74
public/sdlpp/vulkan.hpp Normal file
View File

@ -0,0 +1,74 @@
#pragma once
#if !defined(SDLPP_PUBLIC_SDLPP_VULKAN_HPP_INCLUDED)
#define SDLPP_PUBLIC_SDLPP_VULKAN_HPP_INCLUDED 1
#include "./common.hpp"
#include <SDL3/SDL_vulkan.h>
#include "suna/vulkan/vkwrapper.hpp" // TODO: this shouldn't be in a generic sdlpp header
namespace sdlpp
{
struct VkSurfaceCreateArgs
{
SDL_Window* window;
VkInstance instance;
};
class VkSurface : public Base<std::remove_pointer_t<VkSurfaceKHR>, VkSurface>
{
private:
VkInstance mInstance = nullptr;
public:
VkSurface() noexcept = default;
VkSurface(const VkSurface&) = delete;
VkSurface(VkSurface&& other) noexcept : Base(std::move(other)), mInstance(std::exchange(other.mInstance, nullptr)) {}
VkSurface& operator=(const VkSurface&) = delete;
VkSurface& operator=(VkSurface&& other) noexcept
{
Base::operator=(std::move(other));
mInstance = std::exchange(other.mInstance, nullptr);
return *this;
}
auto operator<=>(const VkSurface& other) const noexcept = default;
operator vk::SurfaceKHR() const noexcept { return mHandle; }
void create(const VkSurfaceCreateArgs& args)
{
MIJIN_ASSERT(mHandle == nullptr, "VkSurface has already been created.");
if (!SDL_Vulkan_CreateSurface(args.window, args.instance, nullptr, &mHandle))
{
throw SDLError();
}
mInstance = args.instance;
}
void destroy()
{
if (mHandle != nullptr)
{
SDL_Vulkan_DestroySurface(mInstance, mHandle, nullptr);
mHandle = nullptr;
mInstance = nullptr;
}
}
};
[[nodiscard]]
inline std::span<const char* const> getVulkanInstanceExtensions()
{
Uint32 count = 0;
const char* const* extensions = SDL_Vulkan_GetInstanceExtensions(&count);
if (extensions == nullptr)
{
throw SDLError();
}
return {extensions, count};
}
} // namespace suna
#endif // !defined(SDLPP_PUBLIC_SDLPP_VULKAN_HPP_INCLUDED)

113
public/sdlpp/window.hpp Normal file
View File

@ -0,0 +1,113 @@
#pragma once
#if !defined(SDL_GPU_TEST_PRIVATE_SDL_GPU_TEST_SDLPP_WINDOW_HPP_INCLUDED)
#define SDL_GPU_TEST_PRIVATE_SDL_GPU_TEST_SDLPP_WINDOW_HPP_INCLUDED 1
#include "./common.hpp"
namespace sdlpp
{
struct WindowFlags : mijin::BitFlags<WindowFlags>
{
bool fullscreen : 1; // 0x0000000000000001 /**< window is in fullscreen mode */
bool opengl : 1; // 0x0000000000000002 /**< window usable with OpenGL context */
bool occluded : 1; // 0x0000000000000004 /**< window is occluded */
bool hidden : 1; // 0x0000000000000008 /**< window is neither mapped onto the desktop nor shown in the taskbar/dock/window list; SDL_ShowWindow( is required for it to become visible */
bool borderless : 1; // 0x0000000000000010 /**< no window decoration */
bool resizable : 1; // 0x0000000000000020 /**< window can be resized */
bool minimized : 1; // 0x0000000000000040 /**< window is minimized */
bool maximized : 1; // 0x0000000000000080 /**< window is maximized */
bool mouse_grabbed : 1; // 0x0000000000000100 /**< window has grabbed mouse input */
bool input_focus : 1; // 0x0000000000000200 /**< window has input focus */
bool mouse_focus : 1; // 0x0000000000000400 /**< window has mouse focus */
bool external : 1; // 0x0000000000000800 /**< window not created by SDL */
bool modal : 1; // 0x0000000000001000 /**< window is modal */
bool high_pixel_density : 1; // 0x0000000000002000 /**< window uses high pixel density back buffer if possible */
bool mouse_capture : 1; // 0x0000000000004000 /**< window has mouse captured (unrelated to MOUSE_GRABBED */
bool mouse_relative_mode : 1; // 0x0000000000008000 /**< window has relative mode enabled */
bool always_on_top : 1; // 0x0000000000010000 /**< window should always be above others */
bool utility : 1; // 0x0000000000020000 /**< window should be treated as a utility window, not showing in the task bar and window list */
bool tooltip : 1; // 0x0000000000040000 /**< window should be treated as a tooltip and does not get mouse or keyboard focus, requires a parent window */
bool popup_menu : 1; // 0x0000000000080000 /**< window should be treated as a popup menu, requires a parent window */
bool keyboard_grabbed : 1; // 0x0000000000100000 /**< window has grabbed keyboard input */
bool unused0 : 1; // 0x0000000000200000
bool unused1 : 1; // 0x0000000000400000
bool unused2 : 1; // 0x0000000000800000
bool unused3 : 1; // 0x0000000001000000
bool unused4 : 1; // 0x0000000002000000
bool unused5 : 1; // 0x0000000004000000
bool unused6 : 1; // 0x0000000008000000
bool vulkan : 1; // 0x0000000010000000 /**< window usable for Vulkan surface */
bool metal : 1; // 0x0000000020000000 /**< window usable for Metal view */
bool transparent : 1; // 0x0000000040000000 /**< window with transparent buffer */
bool notFocusable : 1; // 0x0000000080000000 /**< window should not be focusable */
constexpr operator SDL_WindowFlags () const noexcept
{
return std::bit_cast<std::uint32_t>(*this);
}
};
struct WindowCreateArgs
{
const char* title = "Window";
int width = 1280;
int height = 720;
WindowFlags flags = {};
};
class Window : public Base<SDL_Window, Window>
{
public:
Window() noexcept = default;
Window(const Window&) = delete;
Window(Window&& other) noexcept : Base(std::move(other)) {}
Window& operator=(const Window&) = delete;
Window& operator=(Window&& other) noexcept
{
Base::operator=(std::move(other));
return *this;
}
auto operator<=>(const Window& other) const noexcept = default;
void create(const WindowCreateArgs& args = {})
{
MIJIN_ASSERT(mHandle == nullptr, "Window has already been created.");
mHandle = SDL_CreateWindow(args.title, args.width, args.height, args.flags);
if (mHandle == nullptr)
{
throw SDLError();
}
}
void destroy() noexcept
{
if (mHandle != nullptr)
{
SDL_DestroyWindow(mHandle);
mHandle = nullptr;
}
}
bool setRelativeMouseMode(bool enabled)
{
return SDL_SetWindowRelativeMouseMode(mHandle, enabled);
}
[[nodiscard]]
std::pair<int, int> getSizeInPixels() const
{
int width = 0;
int height = 0;
if (!SDL_GetWindowSizeInPixels(mHandle, &width, &height))
{
throw SDLError();
}
return {width, height};
}
};
} // namespace sdlpp
#endif // !defined(SDL_GPU_TEST_PRIVATE_SDL_GPU_TEST_SDLPP_WINDOW_HPP_INCLUDED)