986 lines
No EOL
37 KiB
C++
986 lines
No EOL
37 KiB
C++
/*
|
|
Crafter®.Graphics
|
|
Copyright (C) 2026 Catcrafts®
|
|
catcrafts.net
|
|
|
|
This library is free software; you can redistribute it and/or
|
|
modify it under the terms of the GNU Lesser General Public
|
|
License version 3.0 as published by the Free Software Foundation;
|
|
|
|
This library is distributed in the hope that it will be useful,
|
|
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
|
Lesser General Public License for more details.
|
|
|
|
You should have received a copy of the GNU Lesser General Public
|
|
License along with this library; if not, write to the Free Software
|
|
Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
|
|
*/
|
|
module;
|
|
#ifdef CRAFTER_GRAPHICS_WINDOW_WAYLAND
|
|
#include <stdio.h>
|
|
#include <stdlib.h>
|
|
#include <unistd.h>
|
|
#include <linux/input-event-codes.h>
|
|
#include "../lib/xdg-shell-client-protocol.h"
|
|
#include "../lib/wayland-xdg-decoration-unstable-v1-client-protocol.h"
|
|
#include "../lib/fractional-scale-v1.h"
|
|
#include "../lib/viewporter.h"
|
|
#include <string.h>
|
|
#include <cassert>
|
|
#include <linux/input.h>
|
|
#include <sys/mman.h>
|
|
#include <wayland-cursor.h>
|
|
#include <xkbcommon/xkbcommon.h>
|
|
#include <errno.h>
|
|
#include <fcntl.h>
|
|
#include <print>
|
|
#include <wayland-client.h>
|
|
#include <wayland-client-protocol.h>
|
|
#ifdef CRAFTER_GRAPHICS_RENDERER_SOFTWARE
|
|
#include <sys/stat.h>
|
|
#include <time.h>
|
|
#endif
|
|
#endif
|
|
#ifdef CRAFTER_GRAPHICS_WINDOW_WIN32
|
|
#include <windows.h>
|
|
#include <cassert>
|
|
#endif
|
|
#ifdef CRAFTER_GRAPHICS_RENDERER_VULKAN
|
|
#include "vulkan/vulkan.h"
|
|
#ifdef CRAFTER_GRAPHICS_WINDOW_WAYLAND
|
|
#include "vulkan/vulkan_wayland.h"
|
|
#endif
|
|
#ifdef CRAFTER_GRAPHICS_WINDOW_WIN32
|
|
#include "vulkan/vulkan_win32.h"
|
|
#endif
|
|
#endif
|
|
module Crafter.Graphics:Window_impl;
|
|
import :Window;
|
|
import :Transform2D;
|
|
import :MouseElement;
|
|
import :Device;
|
|
#ifdef CRAFTER_GRAPHICS_RENDERER_VULKAN
|
|
import :VulkanTransition;
|
|
#endif
|
|
import std;
|
|
|
|
using namespace Crafter;
|
|
|
|
|
|
#ifdef CRAFTER_GRAPHICS_WINDOW_WAYLAND
|
|
#ifdef CRAFTER_GRAPHICS_RENDERER_SOFTWARE
|
|
void randname(char *buf) {
|
|
struct timespec ts;
|
|
clock_gettime(CLOCK_REALTIME, &ts);
|
|
long r = ts.tv_nsec;
|
|
for (int i = 0; i < 6; ++i) {
|
|
buf[i] = 'A'+(r&15)+(r&16)*2;
|
|
r >>= 5;
|
|
}
|
|
}
|
|
|
|
int anonymous_shm_open(void) {
|
|
char name[] = "/hello-wayland-XXXXXX";
|
|
int retries = 100;
|
|
|
|
do {
|
|
randname(name + strlen(name) - 6);
|
|
|
|
--retries;
|
|
// shm_open guarantees that O_CLOEXEC is set
|
|
int fd = shm_open(name, O_RDWR | O_CREAT | O_EXCL, 0600);
|
|
if (fd >= 0) {
|
|
shm_unlink(name);
|
|
return fd;
|
|
}
|
|
} while (retries > 0 && errno == EEXIST);
|
|
|
|
return -1;
|
|
}
|
|
|
|
int create_shm_file(off_t size) {
|
|
int fd = anonymous_shm_open();
|
|
if (fd < 0) {
|
|
return fd;
|
|
}
|
|
|
|
if (ftruncate(fd, size) < 0) {
|
|
close(fd);
|
|
return -1;
|
|
}
|
|
|
|
return fd;
|
|
}
|
|
#endif
|
|
#endif
|
|
|
|
#ifdef CRAFTER_GRAPHICS_WINDOW_WIN32
|
|
CrafterKeys vk_to_crafter_key(WPARAM vk)
|
|
{
|
|
switch (vk)
|
|
{
|
|
// Alphabet
|
|
case 'A': return CrafterKeys::A;
|
|
case 'B': return CrafterKeys::B;
|
|
case 'C': return CrafterKeys::C;
|
|
case 'D': return CrafterKeys::D;
|
|
case 'E': return CrafterKeys::E;
|
|
case 'F': return CrafterKeys::F;
|
|
case 'G': return CrafterKeys::G;
|
|
case 'H': return CrafterKeys::H;
|
|
case 'I': return CrafterKeys::I;
|
|
case 'J': return CrafterKeys::J;
|
|
case 'K': return CrafterKeys::K;
|
|
case 'L': return CrafterKeys::L;
|
|
case 'M': return CrafterKeys::M;
|
|
case 'N': return CrafterKeys::N;
|
|
case 'O': return CrafterKeys::O;
|
|
case 'P': return CrafterKeys::P;
|
|
case 'Q': return CrafterKeys::Q;
|
|
case 'R': return CrafterKeys::R;
|
|
case 'S': return CrafterKeys::S;
|
|
case 'T': return CrafterKeys::T;
|
|
case 'U': return CrafterKeys::U;
|
|
case 'V': return CrafterKeys::V;
|
|
case 'W': return CrafterKeys::W;
|
|
case 'X': return CrafterKeys::X;
|
|
case 'Y': return CrafterKeys::Y;
|
|
case 'Z': return CrafterKeys::Z;
|
|
|
|
// Numbers
|
|
case '0': return CrafterKeys::_0;
|
|
case '1': return CrafterKeys::_1;
|
|
case '2': return CrafterKeys::_2;
|
|
case '3': return CrafterKeys::_3;
|
|
case '4': return CrafterKeys::_4;
|
|
case '5': return CrafterKeys::_5;
|
|
case '6': return CrafterKeys::_6;
|
|
case '7': return CrafterKeys::_7;
|
|
case '8': return CrafterKeys::_8;
|
|
case '9': return CrafterKeys::_9;
|
|
|
|
// Function keys
|
|
case VK_F1: return CrafterKeys::F1;
|
|
case VK_F2: return CrafterKeys::F2;
|
|
case VK_F3: return CrafterKeys::F3;
|
|
case VK_F4: return CrafterKeys::F4;
|
|
case VK_F5: return CrafterKeys::F5;
|
|
case VK_F6: return CrafterKeys::F6;
|
|
case VK_F7: return CrafterKeys::F7;
|
|
case VK_F8: return CrafterKeys::F8;
|
|
case VK_F9: return CrafterKeys::F9;
|
|
case VK_F10: return CrafterKeys::F10;
|
|
case VK_F11: return CrafterKeys::F11;
|
|
case VK_F12: return CrafterKeys::F12;
|
|
|
|
// Control keys
|
|
case VK_ESCAPE: return CrafterKeys::Escape;
|
|
case VK_TAB: return CrafterKeys::Tab;
|
|
case VK_RETURN: return CrafterKeys::Enter;
|
|
case VK_SPACE: return CrafterKeys::Space;
|
|
case VK_BACK: return CrafterKeys::Backspace;
|
|
case VK_DELETE: return CrafterKeys::Delete;
|
|
case VK_INSERT: return CrafterKeys::Insert;
|
|
case VK_HOME: return CrafterKeys::Home;
|
|
case VK_END: return CrafterKeys::End;
|
|
case VK_PRIOR: return CrafterKeys::PageUp;
|
|
case VK_NEXT: return CrafterKeys::PageDown;
|
|
case VK_CAPITAL: return CrafterKeys::CapsLock;
|
|
case VK_NUMLOCK: return CrafterKeys::NumLock;
|
|
case VK_SCROLL: return CrafterKeys::ScrollLock;
|
|
|
|
// Modifiers
|
|
case VK_LSHIFT: return CrafterKeys::LeftShift;
|
|
case VK_RSHIFT: return CrafterKeys::RightShift;
|
|
case VK_LCONTROL: return CrafterKeys::LeftCtrl;
|
|
case VK_RCONTROL: return CrafterKeys::RightCtrl;
|
|
case VK_LMENU: return CrafterKeys::LeftAlt;
|
|
case VK_RMENU: return CrafterKeys::RightAlt;
|
|
case VK_LWIN: return CrafterKeys::LeftSuper;
|
|
case VK_RWIN: return CrafterKeys::RightSuper;
|
|
|
|
// Arrows
|
|
case VK_UP: return CrafterKeys::Up;
|
|
case VK_DOWN: return CrafterKeys::Down;
|
|
case VK_LEFT: return CrafterKeys::Left;
|
|
case VK_RIGHT: return CrafterKeys::Right;
|
|
|
|
// Keypad
|
|
case VK_NUMPAD0: return CrafterKeys::keypad_0;
|
|
case VK_NUMPAD1: return CrafterKeys::keypad_1;
|
|
case VK_NUMPAD2: return CrafterKeys::keypad_2;
|
|
case VK_NUMPAD3: return CrafterKeys::keypad_3;
|
|
case VK_NUMPAD4: return CrafterKeys::keypad_4;
|
|
case VK_NUMPAD5: return CrafterKeys::keypad_5;
|
|
case VK_NUMPAD6: return CrafterKeys::keypad_6;
|
|
case VK_NUMPAD7: return CrafterKeys::keypad_7;
|
|
case VK_NUMPAD8: return CrafterKeys::keypad_8;
|
|
case VK_NUMPAD9: return CrafterKeys::keypad_9;
|
|
case VK_SEPARATOR: return CrafterKeys::keypad_enter;
|
|
case VK_ADD: return CrafterKeys::keypad_plus;
|
|
case VK_SUBTRACT: return CrafterKeys::keypad_minus;
|
|
case VK_MULTIPLY: return CrafterKeys::keypad_multiply;
|
|
case VK_DIVIDE: return CrafterKeys::keypad_divide;
|
|
case VK_DECIMAL: return CrafterKeys::keypad_decimal;
|
|
|
|
// Punctuation
|
|
case VK_OEM_3: return CrafterKeys::grave; // `
|
|
case VK_OEM_MINUS: return CrafterKeys::minus; // -
|
|
case VK_OEM_PLUS: return CrafterKeys::equal; // =
|
|
case VK_OEM_4: return CrafterKeys::bracket_left; // [
|
|
case VK_OEM_6: return CrafterKeys::bracket_right; // ]
|
|
case VK_OEM_5: return CrafterKeys::backslash; //
|
|
case VK_OEM_1: return CrafterKeys::semicolon; // ;
|
|
case VK_OEM_7: return CrafterKeys::quote; // '
|
|
case VK_OEM_COMMA:return CrafterKeys::comma; // ,
|
|
case VK_OEM_PERIOD:return CrafterKeys::period; // .
|
|
case VK_OEM_2: return CrafterKeys::slash; // /
|
|
|
|
default: throw std::runtime_error(std::format("Unkown VK {}", vk));
|
|
}
|
|
}
|
|
|
|
// Define a window class name
|
|
const char g_szClassName[] = "myWindowClass";
|
|
|
|
// Window procedure function that processes messages
|
|
LRESULT CALLBACK WndProc(HWND hwnd, UINT msg, WPARAM wParam, LPARAM lParam) {
|
|
|
|
Window* window = nullptr;
|
|
|
|
if (msg == WM_NCCREATE)
|
|
{
|
|
CREATESTRUCT* pCreate = reinterpret_cast<CREATESTRUCT*>(lParam);
|
|
window = static_cast<Window*>(pCreate->lpCreateParams);
|
|
|
|
SetWindowLongPtr(hwnd, GWLP_USERDATA, reinterpret_cast<LONG_PTR>(window));
|
|
|
|
return TRUE;
|
|
}
|
|
else
|
|
{
|
|
window = reinterpret_cast<Window*>(
|
|
GetWindowLongPtr(hwnd, GWLP_USERDATA)
|
|
);
|
|
}
|
|
|
|
switch (msg) {
|
|
case WM_DESTROY:{
|
|
PostQuitMessage(0);
|
|
break;
|
|
}
|
|
case WM_KEYDOWN:{
|
|
if ((lParam & (1 << 30)) == 0) { // only first press
|
|
CrafterKeys crafterKey = vk_to_crafter_key(wParam);
|
|
if(window->heldkeys[static_cast<std::uint8_t>(crafterKey)]) {
|
|
window->onKeyHold[static_cast<std::uint8_t>(crafterKey)].Invoke();
|
|
window->onAnyKeyHold.Invoke(crafterKey);
|
|
} else{
|
|
window->heldkeys[static_cast<std::uint8_t>(crafterKey)] = true;
|
|
window->onKeyDown[static_cast<std::uint8_t>(crafterKey)].Invoke();
|
|
window->onAnyKeyDown.Invoke(crafterKey);
|
|
}
|
|
}
|
|
break;
|
|
}
|
|
case WM_KEYUP: {
|
|
CrafterKeys crafterKey = vk_to_crafter_key(wParam);
|
|
window->heldkeys[static_cast<std::uint8_t>(crafterKey)] = false;
|
|
window->onKeyUp[static_cast<std::uint8_t>(crafterKey)].Invoke();
|
|
window->onAnyKeyUp.Invoke(crafterKey);
|
|
break;
|
|
}
|
|
case WM_MOUSEMOVE: {
|
|
int x = LOWORD(lParam);
|
|
int y = HIWORD(lParam);
|
|
|
|
Vector<float, 2> pos(x, y);
|
|
window->currentMousePos = pos;
|
|
window->onMouseMove.Invoke();
|
|
for(MouseElement* element : window->mouseElements) {
|
|
if(element) {
|
|
if(window->currentMousePos.x >= element->scaled.position.x && window->currentMousePos.x <= element->scaled.position.x+element->scaled.size.x && window->currentMousePos.y > element->scaled.position.y && window->currentMousePos.y < element->scaled.position.y+element->scaled.size.y) {
|
|
element->onMouseMove.Invoke();
|
|
if(!(window->lastMousePos.x >= element->scaled.position.x && window->lastMousePos.x <= element->scaled.position.x+element->scaled.size.x && window->lastMousePos.y > element->scaled.position.y && window->lastMousePos.y < element->scaled.position.y+element->scaled.size.y)) {
|
|
element->onMouseEnter.Invoke();
|
|
}
|
|
} else if(window->lastMousePos.x >= element->scaled.position.x && window->lastMousePos.x <= element->scaled.position.x+element->scaled.size.x && window->lastMousePos.y > element->scaled.position.y && window->lastMousePos.y < element->scaled.position.y+element->scaled.size.y) {
|
|
element->onMouseLeave.Invoke();
|
|
}
|
|
}
|
|
}
|
|
window->mouseElements.erase(std::remove(window->mouseElements.begin(), window->mouseElements.end(), static_cast<MouseElement*>(nullptr)), window->mouseElements.end());
|
|
break;
|
|
}
|
|
case WM_LBUTTONDOWN: {
|
|
window->mouseLeftHeld = true;
|
|
window->onMouseLeftClick.Invoke();
|
|
for(MouseElement* element : window->mouseElements) {
|
|
if(element) {
|
|
if(window->currentMousePos.x >= element->scaled.position.x && window->currentMousePos.x <= element->scaled.position.x+element->scaled.size.x && window->currentMousePos.y > element->scaled.position.y && window->currentMousePos.y < element->scaled.position.y+element->scaled.size.y) {
|
|
element->onMouseLeftClick.Invoke();
|
|
}
|
|
}
|
|
}
|
|
break;
|
|
}
|
|
|
|
case WM_LBUTTONUP: {
|
|
window->mouseLeftHeld = false;
|
|
window->onMouseLeftRelease.Invoke();
|
|
for(MouseElement* element : window->mouseElements) {
|
|
if(element) {
|
|
if(window->currentMousePos.x >= element->scaled.position.x && window->currentMousePos.x <= element->scaled.position.x+element->scaled.size.x && window->currentMousePos.y > element->scaled.position.y && window->currentMousePos.y < element->scaled.position.y+element->scaled.size.y) {
|
|
element->onMouseLeftRelease.Invoke();
|
|
}
|
|
}
|
|
}
|
|
break;
|
|
}
|
|
|
|
case WM_RBUTTONDOWN: {
|
|
window->mouseRightHeld = true;
|
|
window->onMouseRightClick.Invoke();
|
|
for(MouseElement* element : window->mouseElements) {
|
|
if(element) {
|
|
if(window->currentMousePos.x >= element->scaled.position.x && window->currentMousePos.x <= element->scaled.position.x+element->scaled.size.x && window->currentMousePos.y > element->scaled.position.y && window->currentMousePos.y < element->scaled.position.y+element->scaled.size.y) {
|
|
element->onMouseRightClick.Invoke();
|
|
}
|
|
}
|
|
}
|
|
break;
|
|
}
|
|
|
|
case WM_RBUTTONUP: {
|
|
window->mouseRightHeld = false;
|
|
window->onMouseRightRelease.Invoke();
|
|
for(MouseElement* element : window->mouseElements) {
|
|
if(element) {
|
|
if(window->currentMousePos.x >= element->scaled.position.x && window->currentMousePos.x <= element->scaled.position.x+element->scaled.size.x && window->currentMousePos.y > element->scaled.position.y && window->currentMousePos.y < element->scaled.position.y+element->scaled.size.y) {
|
|
element->onMouseRightRelease.Invoke();
|
|
}
|
|
}
|
|
}
|
|
break;
|
|
}
|
|
|
|
default: return DefWindowProc(hwnd, msg, wParam, lParam);
|
|
}
|
|
return 0;
|
|
}
|
|
|
|
#endif
|
|
|
|
|
|
Window::Window(std::uint32_t width, std::uint32_t height, const std::string_view title) : Window(width, height) {
|
|
SetTitle(title);
|
|
}
|
|
|
|
#ifdef CRAFTER_GRAPHICS_RENDERER_SOFTWARE
|
|
Window::Window(std::uint32_t width, std::uint32_t height) : width(width), height(height) : Rendertarget(width, height) {
|
|
#else
|
|
Window::Window(std::uint32_t width, std::uint32_t height) : width(width), height(height) {
|
|
#endif
|
|
#ifdef CRAFTER_GRAPHICS_WINDOW_WAYLAND
|
|
Device::windows.push_back(this);
|
|
surface = wl_compositor_create_surface(Device::compositor);
|
|
xdgSurface = xdg_wm_base_get_xdg_surface(Device::xdgWmBase, surface);
|
|
xdgToplevel = xdg_surface_get_toplevel(xdgSurface);
|
|
|
|
xdg_surface_add_listener(xdgSurface, &xdg_surface_listener, this);
|
|
xdg_toplevel_add_listener(xdgToplevel, &xdg_toplevel_listener, this);
|
|
wl_surface_commit(surface);
|
|
|
|
wp_scale = wp_fractional_scale_manager_v1_get_fractional_scale(Device::fractionalScaleManager, surface);
|
|
wp_fractional_scale_v1_add_listener(wp_scale, &wp_fractional_scale_v1_listener, this);
|
|
|
|
while (wl_display_dispatch(Device::display) != -1 && !configured) {}
|
|
|
|
wl_surface_commit(surface);
|
|
|
|
zxdg_toplevel_decoration_v1* decoration = zxdg_decoration_manager_v1_get_toplevel_decoration(Device::manager, xdgToplevel);
|
|
zxdg_toplevel_decoration_v1_set_mode(decoration, ZXDG_TOPLEVEL_DECORATION_V1_MODE_SERVER_SIDE);
|
|
|
|
wpViewport = wp_viewporter_get_viewport(Device::wpViewporter, surface);
|
|
wp_viewport_set_destination(wpViewport, std::ceil(width/scale), std::ceil(height/scale));
|
|
|
|
wl_surface_commit(surface);
|
|
#ifdef CRAFTER_GRAPHICS_RENDERER_SOFTWARE
|
|
// Create a wl_buffer, attach it to the surface and commit the surface
|
|
int stride = width * 4;
|
|
int size = stride * height;
|
|
|
|
// Allocate a shared memory file with the right size
|
|
int fd = create_shm_file(size);
|
|
if (fd < 0) {
|
|
throw std::runtime_error(std::format("creating a buffer file for {}B failed", size));
|
|
}
|
|
|
|
// Map the shared memory file
|
|
renderer.buffer = reinterpret_cast<Vector<std::uint8_t, 4>*>(mmap(NULL, size, PROT_READ | PROT_WRITE, MAP_SHARED, fd, 0));
|
|
renderer.sizeX = width;
|
|
renderer.sizeY = height;
|
|
if (renderer.buffer == MAP_FAILED) {
|
|
throw std::runtime_error("mmap failed");
|
|
}
|
|
|
|
wl_shm_pool *pool = wl_shm_create_pool(Device::shm, fd, size);
|
|
buffer = wl_shm_pool_create_buffer(pool, 0, width, height, stride, WL_SHM_FORMAT_ARGB8888);
|
|
wl_shm_pool_destroy(pool);
|
|
|
|
close(fd);
|
|
|
|
if (buffer == nullptr) {
|
|
throw std::runtime_error("wl_buffer creation failed");
|
|
}
|
|
|
|
wl_surface_attach(surface, buffer, 0, 0);
|
|
wl_surface_commit(surface);
|
|
#endif
|
|
#endif
|
|
|
|
#ifdef CRAFTER_GRAPHICS_RENDERER_VULKAN
|
|
|
|
#ifdef CRAFTER_GRAPHICS_WINDOW_WIN32
|
|
// Initialize the window class
|
|
WNDCLASS wc = {0};
|
|
wc.lpfnWndProc = WndProc; // Set window procedure
|
|
wc.hInstance = GetModuleHandle(NULL); // Get instance handle
|
|
wc.lpszClassName = g_szClassName;
|
|
wc.hCursor = LoadCursor(NULL, IDC_ARROW);
|
|
|
|
if (!RegisterClass(&wc)) {
|
|
MessageBox(NULL, "Window Class Registration Failed!", "Error", MB_ICONERROR);
|
|
}
|
|
|
|
RECT rc = {0, 0, static_cast<LONG>(width), static_cast<LONG>(height)};
|
|
AdjustWindowRect(&rc, WS_OVERLAPPEDWINDOW, FALSE);
|
|
|
|
HWND hwnd = CreateWindowEx(
|
|
0,
|
|
g_szClassName,
|
|
"",
|
|
WS_OVERLAPPEDWINDOW,
|
|
CW_USEDEFAULT, CW_USEDEFAULT,
|
|
rc.right - rc.left,
|
|
rc.bottom - rc.top,
|
|
NULL, NULL, wc.hInstance, this
|
|
);
|
|
|
|
|
|
if (hwnd == NULL) {
|
|
MessageBox(NULL, "Window Creation Failed!", "Error", MB_ICONERROR);
|
|
}
|
|
|
|
// Show the window
|
|
ShowWindow(hwnd, SW_SHOWNORMAL);
|
|
UpdateWindow(hwnd);
|
|
|
|
MSG msg;
|
|
while (PeekMessage(&msg, NULL, 0, 0, PM_REMOVE)) {
|
|
TranslateMessage(&msg);
|
|
DispatchMessage(&msg);
|
|
}
|
|
|
|
VkWin32SurfaceCreateInfoKHR createInfo = {};
|
|
createInfo.sType = VK_STRUCTURE_TYPE_WIN32_SURFACE_CREATE_INFO_KHR;
|
|
createInfo.hinstance = wc.hInstance;
|
|
createInfo.hwnd = hwnd;
|
|
Device::CheckVkResult(vkCreateWin32SurfaceKHR(Device::instance, &createInfo, NULL, &vulkanSurface));
|
|
#endif
|
|
|
|
#ifdef CRAFTER_GRAPHICS_WINDOW_WAYLAND
|
|
VkWaylandSurfaceCreateInfoKHR createInfo = {};
|
|
createInfo.sType = VK_STRUCTURE_TYPE_WAYLAND_SURFACE_CREATE_INFO_KHR;
|
|
createInfo.display = Device::display;
|
|
createInfo.surface = surface;
|
|
Device::CheckVkResult(vkCreateWaylandSurfaceKHR(Device::instance, &createInfo, NULL, &vulkanSurface));
|
|
#endif
|
|
|
|
// Get list of supported surface formats
|
|
std::uint32_t formatCount;
|
|
Device::CheckVkResult(vkGetPhysicalDeviceSurfaceFormatsKHR(Device::physDevice, vulkanSurface, &formatCount, NULL));
|
|
assert(formatCount > 0);
|
|
|
|
std::vector<VkSurfaceFormatKHR> surfaceFormats(formatCount);
|
|
Device::CheckVkResult(vkGetPhysicalDeviceSurfaceFormatsKHR(Device::physDevice, vulkanSurface, &formatCount, surfaceFormats.data()));
|
|
|
|
// We want to get a format that best suits our needs, so we try to get one from a set of preferred formats
|
|
// Initialize the format to the first one returned by the implementation in case we can't find one of the preffered formats
|
|
VkSurfaceFormatKHR selectedFormat = surfaceFormats[0];
|
|
std::vector<VkFormat> preferredImageFormats = {
|
|
VK_FORMAT_R8G8B8A8_UNORM,
|
|
VK_FORMAT_B8G8R8A8_UNORM
|
|
};
|
|
|
|
for (auto& availableFormat : surfaceFormats) {
|
|
if (std::find(preferredImageFormats.begin(), preferredImageFormats.end(), availableFormat.format) != preferredImageFormats.end()) {
|
|
selectedFormat = availableFormat;
|
|
break;
|
|
}
|
|
}
|
|
|
|
colorFormat = selectedFormat.format;
|
|
colorSpace = selectedFormat.colorSpace;
|
|
|
|
CreateSwapchain();
|
|
|
|
VkCommandBufferAllocateInfo cmdBufAllocateInfo {};
|
|
cmdBufAllocateInfo.sType = VK_STRUCTURE_TYPE_COMMAND_BUFFER_ALLOCATE_INFO;
|
|
cmdBufAllocateInfo.commandPool = Device::commandPool;
|
|
cmdBufAllocateInfo.level = VK_COMMAND_BUFFER_LEVEL_PRIMARY;
|
|
cmdBufAllocateInfo.commandBufferCount = numFrames;
|
|
Device::CheckVkResult(vkAllocateCommandBuffers(Device::device, &cmdBufAllocateInfo, drawCmdBuffers));
|
|
|
|
VkSemaphoreCreateInfo semaphoreCreateInfo {};
|
|
semaphoreCreateInfo.sType = VK_STRUCTURE_TYPE_SEMAPHORE_CREATE_INFO;
|
|
Device::CheckVkResult(vkCreateSemaphore(Device::device, &semaphoreCreateInfo, nullptr, &semaphores.presentComplete));
|
|
Device::CheckVkResult(vkCreateSemaphore(Device::device, &semaphoreCreateInfo, nullptr, &semaphores.renderComplete));
|
|
|
|
// Set up submit info structure
|
|
// Semaphores will stay the same during application lifetime
|
|
// Command buffer submission info is set by each example
|
|
submitInfo.sType = VK_STRUCTURE_TYPE_SUBMIT_INFO;
|
|
submitInfo.pWaitDstStageMask = &submitPipelineStages;
|
|
submitInfo.waitSemaphoreCount = 1;
|
|
submitInfo.pWaitSemaphores = &semaphores.presentComplete;
|
|
submitInfo.signalSemaphoreCount = 1;
|
|
submitInfo.pSignalSemaphores = &semaphores.renderComplete;
|
|
submitInfo.pNext = VK_NULL_HANDLE;
|
|
#endif
|
|
}
|
|
|
|
void Window::SetTitle(const std::string_view title) {
|
|
#ifdef CRAFTER_GRAPHICS_WINDOW_WAYLAND
|
|
xdg_toplevel_set_title(xdgToplevel, title.data());
|
|
#endif
|
|
}
|
|
|
|
void Window::StartSync() {
|
|
#ifdef CRAFTER_GRAPHICS_WINDOW_WAYLAND
|
|
while (open && wl_display_dispatch(Device::display) != -1) {
|
|
|
|
}
|
|
#endif
|
|
#ifdef CRAFTER_GRAHPICS_WINDOW_WIN32
|
|
while(open) {
|
|
MSG msg;
|
|
while (PeekMessage(&msg, NULL, 0, 0, PM_REMOVE)) {
|
|
TranslateMessage(&msg);
|
|
DispatchMessage(&msg);
|
|
}
|
|
if(updating) {
|
|
Update();
|
|
}
|
|
}
|
|
#endif
|
|
}
|
|
|
|
void Window::StartUpdate() {
|
|
lastFrameBegin = std::chrono::high_resolution_clock::now();
|
|
updating = true;
|
|
#ifdef CRAFTER_GRAPHICS_WINDOW_WAYLAND
|
|
cb = wl_surface_frame(surface);
|
|
wl_callback_add_listener(cb, &wl_callback_listener, this);
|
|
#endif
|
|
}
|
|
|
|
void Window::StopUpdate() {
|
|
updating = false;
|
|
}
|
|
|
|
std::chrono::time_point<std::chrono::high_resolution_clock> startTime;
|
|
|
|
void Window::Update() {
|
|
startTime = std::chrono::high_resolution_clock::now();
|
|
#ifdef CRAFTER_TIMING
|
|
vblank = duration_cast<std::chrono::milliseconds>(startTime - frameEnd);
|
|
#endif
|
|
|
|
#ifdef CRAFTER_TIMING
|
|
auto renderStart = std::chrono::high_resolution_clock::now();
|
|
renderTimings.clear();
|
|
#endif
|
|
Render();
|
|
#ifdef CRAFTER_TIMING
|
|
auto renderEnd = std::chrono::high_resolution_clock::now();
|
|
totalRender = renderEnd - renderStart;
|
|
#endif
|
|
|
|
#ifdef CRAFTER_TIMING
|
|
frameEnd = std::chrono::high_resolution_clock::now();
|
|
|
|
frameTimes.push_back(totalUpdate+totalRender);
|
|
|
|
// Keep only the last 100 frame times
|
|
if (frameTimes.size() > 100) {
|
|
frameTimes.erase(frameTimes.begin());
|
|
}
|
|
#endif
|
|
lastFrameBegin = startTime;
|
|
}
|
|
|
|
void Window::Render() {
|
|
#ifdef CRAFTER_GRAPHICS_RENDERER_SOFTWARE
|
|
// elements.erase(std::remove(elements.begin(), elements.end(), static_cast<Transform*>(nullptr)), elements.end());
|
|
// std::sort(elements.begin(), elements.end(), [](Transform* a, Transform* b){ return a->anchor.z < b->anchor.z; });
|
|
renderer.Render();
|
|
#ifdef CRAFTER_GRAPHICS_WINDOW_WAYLAND
|
|
wl_surface_attach(surface, buffer, 0, 0);
|
|
wl_surface_commit(surface);
|
|
wl_surface_damage(surface, 0, 0, 10000, 100000);
|
|
#endif
|
|
#endif
|
|
#ifdef CRAFTER_GRAPHICS_RENDERER_VULKAN
|
|
// Acquire the next image from the swap chain
|
|
Device::CheckVkResult(vkAcquireNextImageKHR(Device::device, swapChain, UINT64_MAX, semaphores.presentComplete, (VkFence)nullptr, ¤tBuffer));
|
|
submitInfo.commandBufferCount = 1;
|
|
submitInfo.pCommandBuffers = &drawCmdBuffers[currentBuffer];
|
|
|
|
VkCommandBufferBeginInfo cmdBufInfo {};
|
|
cmdBufInfo.sType = VK_STRUCTURE_TYPE_COMMAND_BUFFER_BEGIN_INFO;
|
|
|
|
Device::CheckVkResult(vkBeginCommandBuffer(drawCmdBuffers[currentBuffer], &cmdBufInfo));
|
|
|
|
VkImageSubresourceRange range{};
|
|
range.aspectMask = VK_IMAGE_ASPECT_COLOR_BIT;
|
|
range.baseMipLevel = 0;
|
|
range.levelCount = VK_REMAINING_MIP_LEVELS;
|
|
range.baseArrayLayer = 0;
|
|
range.layerCount = VK_REMAINING_ARRAY_LAYERS;
|
|
|
|
VkImageMemoryBarrier image_memory_barrier {
|
|
.sType = VK_STRUCTURE_TYPE_IMAGE_MEMORY_BARRIER,
|
|
.srcAccessMask = 0,
|
|
.dstAccessMask = VK_ACCESS_SHADER_WRITE_BIT,
|
|
.oldLayout = VK_IMAGE_LAYOUT_PRESENT_SRC_KHR,
|
|
.newLayout = VK_IMAGE_LAYOUT_GENERAL,
|
|
.srcQueueFamilyIndex = VK_QUEUE_FAMILY_IGNORED,
|
|
.dstQueueFamilyIndex = VK_QUEUE_FAMILY_IGNORED,
|
|
.image = images[currentBuffer],
|
|
.subresourceRange = range
|
|
};
|
|
|
|
vkCmdPipelineBarrier(drawCmdBuffers[currentBuffer], VK_PIPELINE_STAGE_BOTTOM_OF_PIPE_BIT, VK_PIPELINE_STAGE_RAY_TRACING_SHADER_BIT_KHR, 0, 0, nullptr, 0, nullptr, 1, &image_memory_barrier);
|
|
|
|
onUpdate.Invoke({startTime, startTime-lastFrameBegin});
|
|
#ifdef CRAFTER_TIMING
|
|
totalUpdate = std::chrono::nanoseconds(0);
|
|
updateTimings.clear();
|
|
for (const std::pair<const EventListener<FrameTime>*, std::chrono::nanoseconds>& entry : onUpdate.listenerTimes) {
|
|
updateTimings.push_back(entry);
|
|
totalUpdate += entry.second;
|
|
}
|
|
#endif
|
|
|
|
vkCmdBindPipeline(drawCmdBuffers[currentBuffer], VK_PIPELINE_BIND_POINT_RAY_TRACING_KHR, rtPipeline);
|
|
VkBindDescriptorSetsInfo bindDescriptorSetsInfo{
|
|
.sType = VK_STRUCTURE_TYPE_BIND_DESCRIPTOR_SETS_INFO,
|
|
.stageFlags = VK_SHADER_STAGE_ALL,
|
|
.layout = rtPipelineLayout,
|
|
.firstSet = 0,
|
|
.descriptorSetCount = static_cast<std::uint32_t>(descriptorsRt.size()),
|
|
.pDescriptorSets = descriptorsRt.data()
|
|
};
|
|
|
|
vkCmdBindDescriptorSets2(drawCmdBuffers[currentBuffer], &bindDescriptorSetsInfo);
|
|
Device::vkCmdTraceRaysKHR(drawCmdBuffers[currentBuffer], &raygenRegion, &missRegion, &hitRegion, &callableRegion, width, height, 1);
|
|
|
|
|
|
VkImageMemoryBarrier image_memory_barrier2 {
|
|
.sType = VK_STRUCTURE_TYPE_IMAGE_MEMORY_BARRIER,
|
|
.srcAccessMask = VK_ACCESS_SHADER_WRITE_BIT,
|
|
.dstAccessMask = 0,
|
|
.oldLayout = VK_IMAGE_LAYOUT_GENERAL,
|
|
.newLayout = VK_IMAGE_LAYOUT_PRESENT_SRC_KHR,
|
|
.srcQueueFamilyIndex = VK_QUEUE_FAMILY_IGNORED,
|
|
.dstQueueFamilyIndex = VK_QUEUE_FAMILY_IGNORED,
|
|
.image = images[currentBuffer],
|
|
.subresourceRange = range
|
|
};
|
|
|
|
vkCmdPipelineBarrier(drawCmdBuffers[currentBuffer], VK_PIPELINE_STAGE_RAY_TRACING_SHADER_BIT_KHR, VK_PIPELINE_STAGE_BOTTOM_OF_PIPE_BIT, 0, 0, nullptr, 0, nullptr, 1, &image_memory_barrier2);
|
|
|
|
Device::CheckVkResult(vkEndCommandBuffer(drawCmdBuffers[currentBuffer]));
|
|
|
|
Device::CheckVkResult(vkQueueSubmit(Device::queue, 1, &submitInfo, VK_NULL_HANDLE));
|
|
VkPresentInfoKHR presentInfo = {};
|
|
presentInfo.sType = VK_STRUCTURE_TYPE_PRESENT_INFO_KHR;
|
|
presentInfo.pNext = NULL;
|
|
presentInfo.swapchainCount = 1;
|
|
presentInfo.pSwapchains = &swapChain;
|
|
presentInfo.pImageIndices = ¤tBuffer;
|
|
// Check if a wait semaphore has been specified to wait for before presenting the image
|
|
if (semaphores.renderComplete != VK_NULL_HANDLE)
|
|
{
|
|
presentInfo.pWaitSemaphores = &semaphores.renderComplete;
|
|
presentInfo.waitSemaphoreCount = 1;
|
|
}
|
|
|
|
VkResult result = vkQueuePresentKHR(Device::queue, &presentInfo);
|
|
if(result == VK_SUBOPTIMAL_KHR) {
|
|
CreateSwapchain();
|
|
} else {
|
|
Device::CheckVkResult(result);
|
|
}
|
|
Device::CheckVkResult(vkQueueWaitIdle(Device::queue));
|
|
#endif
|
|
}
|
|
|
|
#ifdef CRAFTER_TIMING
|
|
void Window::LogTiming() {
|
|
std::cout << std::format("Update: {}", duration_cast<std::chrono::milliseconds>(totalUpdate)) << std::endl;
|
|
for (const std::pair<const EventListener<FrameTime>*, std::chrono::nanoseconds>& entry : updateTimings) {
|
|
std::cout << std::format("\t{} {}", reinterpret_cast<const void*>(entry.first), duration_cast<std::chrono::microseconds>(entry.second)) << std::endl;
|
|
}
|
|
std::cout << std::format("Render: {}", duration_cast<std::chrono::milliseconds>(totalRender)) << std::endl;
|
|
for (const std::tuple<const RenderingElement*, std::uint32_t, std::uint32_t, std::chrono::nanoseconds>& entry : renderer.renderTimings) {
|
|
std::cout << std::format("\t{} {}x{} {}", reinterpret_cast<const void*>(std::get<0>(entry)), std::get<1>(entry), std::get<2>(entry), duration_cast<std::chrono::microseconds>(std::get<3>(entry))) << std::endl;
|
|
}
|
|
std::cout << std::format("Total: {}", duration_cast<std::chrono::milliseconds>(totalUpdate+totalRender)) << std::endl;
|
|
std::cout << std::format("Vblank: {}", duration_cast<std::chrono::milliseconds>(vblank)) << std::endl;
|
|
|
|
// Add 100-frame average and min-max timing info
|
|
if (!frameTimes.empty()) {
|
|
// Calculate average
|
|
std::chrono::nanoseconds sum(0);
|
|
for (const auto& frameTime : frameTimes) {
|
|
sum += frameTime;
|
|
}
|
|
auto average = sum / frameTimes.size();
|
|
|
|
// Find min and max
|
|
auto min = frameTimes.front();
|
|
auto max = frameTimes.front();
|
|
for (const auto& frameTime : frameTimes) {
|
|
if (frameTime < min) min = frameTime;
|
|
if (frameTime > max) max = frameTime;
|
|
}
|
|
|
|
std::cout << std::format("Last 100 Frame Times - Avg: {}, Min: {}, Max: {}",
|
|
duration_cast<std::chrono::milliseconds>(average),
|
|
duration_cast<std::chrono::milliseconds>(min),
|
|
duration_cast<std::chrono::milliseconds>(max)) << std::endl;
|
|
}
|
|
}
|
|
#endif
|
|
|
|
#ifdef CRAFTER_GRAPHICS_RENDERER_VULKAN
|
|
void Window::CreateSwapchain()
|
|
{
|
|
// Store the current swap chain handle so we can use it later on to ease up recreation
|
|
VkSwapchainKHR oldSwapchain = swapChain;
|
|
|
|
// Get physical device surface properties and formats
|
|
VkSurfaceCapabilitiesKHR surfCaps;
|
|
Device::CheckVkResult(vkGetPhysicalDeviceSurfaceCapabilitiesKHR(Device::physDevice, vulkanSurface, &surfCaps));
|
|
|
|
VkExtent2D swapchainExtent = {};
|
|
// If width (and height) equals the special value 0xFFFFFFFF, the size of the surface will be set by the swapchain
|
|
if (surfCaps.currentExtent.width == (uint32_t)-1)
|
|
{
|
|
// If the surface size is undefined, the size is set to the size of the images requested
|
|
swapchainExtent.width = width;
|
|
swapchainExtent.height = height;
|
|
}
|
|
else
|
|
{
|
|
// If the surface size is defined, the swap chain size must match
|
|
swapchainExtent = surfCaps.currentExtent;
|
|
width = surfCaps.currentExtent.width;
|
|
height = surfCaps.currentExtent.height;
|
|
}
|
|
|
|
|
|
// Select a present mode for the swapchain
|
|
uint32_t presentModeCount;
|
|
Device::CheckVkResult(vkGetPhysicalDeviceSurfacePresentModesKHR(Device::physDevice, vulkanSurface, &presentModeCount, NULL));
|
|
assert(presentModeCount > 0);
|
|
|
|
std::vector<VkPresentModeKHR> presentModes(presentModeCount);
|
|
Device::CheckVkResult(vkGetPhysicalDeviceSurfacePresentModesKHR(Device::physDevice, vulkanSurface, &presentModeCount, presentModes.data()));
|
|
|
|
// The VK_PRESENT_MODE_FIFO_KHR mode must always be present as per spec
|
|
// This mode waits for the vertical blank ("v-sync")
|
|
VkPresentModeKHR swapchainPresentMode = VK_PRESENT_MODE_FIFO_KHR;
|
|
|
|
// Find the transformation of the surface
|
|
VkSurfaceTransformFlagsKHR preTransform;
|
|
if (surfCaps.supportedTransforms & VK_SURFACE_TRANSFORM_IDENTITY_BIT_KHR)
|
|
{
|
|
// We prefer a non-rotated transform
|
|
preTransform = VK_SURFACE_TRANSFORM_IDENTITY_BIT_KHR;
|
|
}
|
|
else
|
|
{
|
|
preTransform = surfCaps.currentTransform;
|
|
}
|
|
|
|
// Find a supported composite alpha format (not all devices support alpha opaque)
|
|
VkCompositeAlphaFlagBitsKHR compositeAlpha = VK_COMPOSITE_ALPHA_OPAQUE_BIT_KHR;
|
|
// Simply select the first composite alpha format available
|
|
std::vector<VkCompositeAlphaFlagBitsKHR> compositeAlphaFlags = {
|
|
VK_COMPOSITE_ALPHA_OPAQUE_BIT_KHR,
|
|
VK_COMPOSITE_ALPHA_PRE_MULTIPLIED_BIT_KHR,
|
|
VK_COMPOSITE_ALPHA_POST_MULTIPLIED_BIT_KHR,
|
|
VK_COMPOSITE_ALPHA_INHERIT_BIT_KHR,
|
|
};
|
|
for (auto& compositeAlphaFlag : compositeAlphaFlags) {
|
|
if (surfCaps.supportedCompositeAlpha & compositeAlphaFlag) {
|
|
compositeAlpha = compositeAlphaFlag;
|
|
break;
|
|
};
|
|
}
|
|
|
|
VkSwapchainCreateInfoKHR swapchainCI = {};
|
|
swapchainCI.sType = VK_STRUCTURE_TYPE_SWAPCHAIN_CREATE_INFO_KHR;
|
|
swapchainCI.surface = vulkanSurface;
|
|
swapchainCI.minImageCount = numFrames;
|
|
swapchainCI.imageFormat = colorFormat;
|
|
swapchainCI.imageColorSpace = colorSpace;
|
|
swapchainCI.imageExtent = { swapchainExtent.width, swapchainExtent.height };
|
|
swapchainCI.imageUsage = VK_IMAGE_USAGE_STORAGE_BIT;
|
|
swapchainCI.preTransform = (VkSurfaceTransformFlagBitsKHR)preTransform;
|
|
swapchainCI.imageArrayLayers = 1;
|
|
swapchainCI.imageSharingMode = VK_SHARING_MODE_EXCLUSIVE;
|
|
swapchainCI.queueFamilyIndexCount = 0;
|
|
swapchainCI.presentMode = swapchainPresentMode;
|
|
// Setting oldSwapChain to the saved handle of the previous swapchain aids in resource reuse and makes sure that we can still present already acquired images
|
|
swapchainCI.oldSwapchain = oldSwapchain;
|
|
// Setting clipped to VK_TRUE allows the implementation to discard rendering outside of the surface area
|
|
swapchainCI.clipped = VK_TRUE;
|
|
swapchainCI.compositeAlpha = compositeAlpha;
|
|
|
|
Device::CheckVkResult(vkCreateSwapchainKHR(Device::device, &swapchainCI, nullptr, &swapChain));
|
|
|
|
// If an existing swap chain is re-created, destroy the old swap chain and the ressources owned by the application (image views, images are owned by the swap chain)
|
|
if (oldSwapchain != VK_NULL_HANDLE) {
|
|
for (auto i = 0; i < numFrames; i++) {
|
|
vkDestroyImageView(Device::device, imageViews[i], nullptr);
|
|
}
|
|
vkDestroySwapchainKHR(Device::device, oldSwapchain, nullptr);
|
|
}
|
|
uint32_t imageCount{ 0 };
|
|
Device::CheckVkResult(vkGetSwapchainImagesKHR(Device::device, swapChain, &imageCount, nullptr));
|
|
|
|
// Get the swap chain images
|
|
Device::CheckVkResult(vkGetSwapchainImagesKHR(Device::device, swapChain, &imageCount, images));
|
|
|
|
for (auto i = 0; i < numFrames; i++)
|
|
{
|
|
VkImageViewCreateInfo colorAttachmentView = {};
|
|
colorAttachmentView.sType = VK_STRUCTURE_TYPE_IMAGE_VIEW_CREATE_INFO;
|
|
colorAttachmentView.pNext = NULL;
|
|
colorAttachmentView.format = colorFormat;
|
|
colorAttachmentView.components = {
|
|
VK_COMPONENT_SWIZZLE_R,
|
|
VK_COMPONENT_SWIZZLE_G,
|
|
VK_COMPONENT_SWIZZLE_B,
|
|
VK_COMPONENT_SWIZZLE_A
|
|
};
|
|
colorAttachmentView.subresourceRange.aspectMask = VK_IMAGE_ASPECT_COLOR_BIT;
|
|
colorAttachmentView.subresourceRange.baseMipLevel = 0;
|
|
colorAttachmentView.subresourceRange.levelCount = 1;
|
|
colorAttachmentView.subresourceRange.baseArrayLayer = 0;
|
|
colorAttachmentView.subresourceRange.layerCount = 1;
|
|
colorAttachmentView.viewType = VK_IMAGE_VIEW_TYPE_2D;
|
|
colorAttachmentView.flags = 0;
|
|
colorAttachmentView.image = images[i];
|
|
Device::CheckVkResult(vkCreateImageView(Device::device, &colorAttachmentView, nullptr, &imageViews[i]));
|
|
|
|
VkImageSubresourceRange range{};
|
|
range.aspectMask = VK_IMAGE_ASPECT_COLOR_BIT;
|
|
range.baseMipLevel = 0;
|
|
range.levelCount = VK_REMAINING_MIP_LEVELS;
|
|
range.baseArrayLayer = 0;
|
|
range.layerCount = VK_REMAINING_ARRAY_LAYERS;
|
|
}
|
|
}
|
|
|
|
VkCommandBuffer Window::StartInit() {
|
|
VkCommandBufferBeginInfo cmdBufInfo {};
|
|
cmdBufInfo.sType = VK_STRUCTURE_TYPE_COMMAND_BUFFER_BEGIN_INFO;
|
|
Device::CheckVkResult(vkBeginCommandBuffer(drawCmdBuffers[currentBuffer], &cmdBufInfo));
|
|
|
|
VkImageSubresourceRange range{};
|
|
range.aspectMask = VK_IMAGE_ASPECT_COLOR_BIT;
|
|
range.baseMipLevel = 0;
|
|
range.levelCount = VK_REMAINING_MIP_LEVELS;
|
|
range.baseArrayLayer = 0;
|
|
range.layerCount = VK_REMAINING_ARRAY_LAYERS;
|
|
|
|
for(std::uint32_t i = 0; i < numFrames; i++) {
|
|
image_layout_transition(drawCmdBuffers[currentBuffer],
|
|
images[i],
|
|
VK_PIPELINE_STAGE_TOP_OF_PIPE_BIT,
|
|
VK_PIPELINE_STAGE_BOTTOM_OF_PIPE_BIT,
|
|
0,
|
|
0,
|
|
VK_IMAGE_LAYOUT_UNDEFINED,
|
|
VK_IMAGE_LAYOUT_PRESENT_SRC_KHR,
|
|
range
|
|
);
|
|
}
|
|
|
|
return drawCmdBuffers[currentBuffer];
|
|
}
|
|
|
|
void Window::FinishInit() {
|
|
VkSubmitInfo submitInfo{};
|
|
submitInfo.sType = VK_STRUCTURE_TYPE_SUBMIT_INFO;
|
|
submitInfo.commandBufferCount = 1;
|
|
submitInfo.pCommandBuffers = &drawCmdBuffers[currentBuffer];
|
|
Device::CheckVkResult(vkEndCommandBuffer(drawCmdBuffers[currentBuffer]));
|
|
Device::CheckVkResult(vkQueueSubmit(Device::queue, 1, &submitInfo, VK_NULL_HANDLE));
|
|
Device::CheckVkResult(vkQueueWaitIdle(Device::queue));
|
|
}
|
|
|
|
#endif
|
|
|
|
#ifdef CRAFTER_GRAPHICS_WINDOW_WAYLAND
|
|
void Window::wl_surface_frame_done(void* data, struct wl_callback *cb, uint32_t time) {
|
|
wl_callback_destroy(cb);
|
|
Window* window = reinterpret_cast<Window*>(data);
|
|
|
|
if(window->updating) {
|
|
cb = wl_surface_frame(window->surface);
|
|
wl_callback_add_listener(cb, &Window::wl_callback_listener, window);
|
|
window->Update();
|
|
} else {
|
|
cb = nullptr;
|
|
}
|
|
}
|
|
|
|
|
|
void Window::xdg_toplevel_configure(void*, xdg_toplevel*, std::int32_t, std::int32_t, wl_array*){
|
|
|
|
}
|
|
|
|
void Window::xdg_toplevel_handle_close(void* data, xdg_toplevel*) {
|
|
Window* window = reinterpret_cast<Window*>(data);
|
|
window->onClose.Invoke();
|
|
window->open = false;
|
|
}
|
|
|
|
void Window::xdg_surface_handle_configure(void* data, xdg_surface* xdg_surface, std::uint32_t serial) {
|
|
Window* window = reinterpret_cast<Window*>(data);
|
|
// The compositor configures our surface, acknowledge the configure event
|
|
xdg_surface_ack_configure(xdg_surface, serial);
|
|
|
|
if (window->configured) {
|
|
// If this isn't the first configure event we've received, we already
|
|
// have a buffer attached, so no need to do anything. Commit the
|
|
// surface to apply the configure acknowledgement.
|
|
wl_surface_commit(window->surface);
|
|
}
|
|
|
|
window->configured = true;
|
|
}
|
|
|
|
void Window::xdg_surface_handle_preferred_scale(void* data, wp_fractional_scale_v1*, std::uint32_t scale) {
|
|
Window* window = reinterpret_cast<Window*>(data);
|
|
window->scale = scale / 120.0f;
|
|
}
|
|
|
|
#endif |