initial
This commit is contained in:
+78
@@ -0,0 +1,78 @@
|
||||
#include <SDL2/SDL_error.h>
|
||||
#include <SDL_video.h>
|
||||
#include <volk.h>
|
||||
#include <stdexcept>
|
||||
#include "SDLWindow.h"
|
||||
#include "LoftException.hpp"
|
||||
#include <SDL2/SDL_vulkan.h>
|
||||
#include <vector>
|
||||
#include <vulkan/vulkan_core.h>
|
||||
|
||||
namespace lft::win {
|
||||
|
||||
//
|
||||
// Created by martin on 11/12/23.
|
||||
//
|
||||
void SDLWindow::resize() {
|
||||
|
||||
}
|
||||
|
||||
VkExtent2D SDLWindow::get_size() const {
|
||||
VkExtent2D extent = {};
|
||||
SDL_GetWindowSize(m_pWindow, (int*)&extent.width, (int*)&extent.height);
|
||||
|
||||
return extent;
|
||||
}
|
||||
|
||||
SDLWindow::SDLWindow(std::string name, VkRect2D rect) :
|
||||
m_extent{rect.extent}
|
||||
{
|
||||
if(SDL_Init(SDL_INIT_VIDEO | SDL_INIT_EVERYTHING) < 0) {
|
||||
throw std::runtime_error("Failed to initialize sdl");
|
||||
}
|
||||
|
||||
m_pWindow = SDL_CreateWindow(name.c_str(),
|
||||
SDL_WINDOWPOS_CENTERED,
|
||||
SDL_WINDOWPOS_CENTERED,
|
||||
rect.extent.width,
|
||||
rect.extent.height,
|
||||
SDL_WINDOW_VULKAN | SDL_WINDOW_ALLOW_HIGHDPI |
|
||||
SDL_WINDOW_RESIZABLE);
|
||||
|
||||
if(m_pWindow == nullptr) {
|
||||
throw GpuException(std::format("Failed to create window:\n{}", SDL_GetError()));
|
||||
}
|
||||
}
|
||||
|
||||
Surface SDLWindow::create_surface(const Instance* instance) const {
|
||||
VkSurfaceKHR surface = VK_NULL_HANDLE;
|
||||
SDL_Vulkan_CreateSurface(m_pWindow, instance->instance(), &surface);
|
||||
return Surface(instance, surface);
|
||||
}
|
||||
|
||||
bool SDLWindow::is_open() const {
|
||||
return true;
|
||||
}
|
||||
|
||||
int32_t SDLWindow::poll_event(SDL_Event *pOutEvent) const {
|
||||
return SDL_PollEvent(pOutEvent);
|
||||
}
|
||||
|
||||
std::vector<std::string> SDLWindow::get_required_extensions() const {
|
||||
uint32_t count = 0;
|
||||
SDL_Vulkan_GetInstanceExtensions(m_pWindow, &count, nullptr);
|
||||
|
||||
std::vector<const char*> extensions(count);
|
||||
SDL_Vulkan_GetInstanceExtensions(m_pWindow, &count, extensions.data());
|
||||
|
||||
std::vector<std::string> extension_strings(count);
|
||||
std::transform(extensions.begin(), extensions.end(),
|
||||
extension_strings.begin(),
|
||||
[](const char* str) {
|
||||
return std::string(str);
|
||||
});
|
||||
|
||||
return extension_strings;
|
||||
}
|
||||
|
||||
}
|
||||
+213
@@ -0,0 +1,213 @@
|
||||
#include "Swapchain.hpp"
|
||||
#include "Instance.hpp"
|
||||
|
||||
#include <assert.h>
|
||||
#include <print>
|
||||
|
||||
VkSurfaceFormatKHR Swapchain::choose_format() {
|
||||
uint32_t formatCount = 0;
|
||||
vkGetPhysicalDeviceSurfaceFormatsKHR(m_gpu->gpu(), m_surface, &formatCount, nullptr);
|
||||
assert(formatCount > 0);
|
||||
|
||||
auto formats = std::vector<VkSurfaceFormatKHR>(formatCount);
|
||||
vkGetPhysicalDeviceSurfaceFormatsKHR(m_gpu->gpu(), m_surface, &formatCount, formats.data());
|
||||
|
||||
for(uint32_t i = 0; i < formatCount; i++) {
|
||||
if(formats[i].format == VK_FORMAT_B8G8R8A8_UNORM &&
|
||||
formats[i].colorSpace == VK_COLOR_SPACE_SRGB_NONLINEAR_KHR) {
|
||||
return formats[i];
|
||||
}
|
||||
}
|
||||
|
||||
auto result = formats[0];
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
VkPresentModeKHR Swapchain::choose_present_mode() {
|
||||
uint32_t presentModeCount = 0;
|
||||
vkGetPhysicalDeviceSurfacePresentModesKHR(m_gpu->gpu(), m_surface,
|
||||
&presentModeCount, nullptr);
|
||||
|
||||
VkPresentModeKHR presentModeResult = VK_PRESENT_MODE_FIFO_KHR;
|
||||
|
||||
auto presentModes = new VkPresentModeKHR[presentModeCount];
|
||||
vkGetPhysicalDeviceSurfacePresentModesKHR(m_gpu->gpu(), m_surface,
|
||||
&presentModeCount, presentModes);
|
||||
|
||||
for(uint32_t i = 0; i < presentModeCount; i++) {
|
||||
if(presentModes[i] == VK_PRESENT_MODE_MAILBOX_KHR) {
|
||||
presentModeResult = presentModes[i];
|
||||
}
|
||||
}
|
||||
|
||||
delete [] presentModes;
|
||||
|
||||
return presentModeResult;
|
||||
}
|
||||
|
||||
VkExtent2D Swapchain::choose_extent(VkExtent2D actualExtent) {
|
||||
VkSurfaceCapabilitiesKHR capabilities = {};
|
||||
vkGetPhysicalDeviceSurfaceCapabilitiesKHR(m_gpu->gpu(), m_surface, &capabilities);
|
||||
|
||||
VkExtent2D result = {};
|
||||
|
||||
if(capabilities.currentExtent.width != UINT32_MAX) {
|
||||
result = capabilities.currentExtent;
|
||||
} else {
|
||||
result.width = std::max(
|
||||
capabilities.minImageExtent.width,
|
||||
std::min(capabilities.maxImageExtent.width, actualExtent.width)
|
||||
);
|
||||
|
||||
result.height = std::max(
|
||||
capabilities.minImageExtent.height,
|
||||
std::min(capabilities.maxImageExtent.height, actualExtent.height)
|
||||
);
|
||||
}
|
||||
|
||||
assert(result.width > 0 && result.height > 0);
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
ResultCode Swapchain::get_images(uint32_t *pOutNumImages, VkImage *pOutImages) {
|
||||
/* Get images to render to, rendertargets */
|
||||
EXPECT(vkGetSwapchainImagesKHR(m_gpu->dev(), m_swapchain, pOutNumImages, pOutImages) == VK_SUCCESS,
|
||||
"Failed to get swapchain images");
|
||||
return RESULT_OK;
|
||||
}
|
||||
|
||||
ResultCode Swapchain::create_views() {
|
||||
m_pImageViews = (VkImageView*)malloc(sizeof(VkImageView) * m_numImages);
|
||||
assert(m_pImageViews);
|
||||
|
||||
for(unsigned i = 0; i < m_numImages; i++) {
|
||||
VkImageViewCreateInfo viewInfo = {
|
||||
.sType = VK_STRUCTURE_TYPE_IMAGE_VIEW_CREATE_INFO,
|
||||
.image = m_pImages[i],
|
||||
.viewType = VK_IMAGE_VIEW_TYPE_2D,
|
||||
.format = m_format.format,
|
||||
.components = {
|
||||
VK_COMPONENT_SWIZZLE_IDENTITY,
|
||||
VK_COMPONENT_SWIZZLE_IDENTITY,
|
||||
VK_COMPONENT_SWIZZLE_IDENTITY,
|
||||
VK_COMPONENT_SWIZZLE_IDENTITY
|
||||
},
|
||||
.subresourceRange = {
|
||||
.aspectMask = VK_IMAGE_ASPECT_COLOR_BIT,
|
||||
.baseMipLevel = 0,
|
||||
.levelCount = 1,
|
||||
.baseArrayLayer = 0,
|
||||
.layerCount = 1
|
||||
}
|
||||
};
|
||||
|
||||
EXPECT(vkCreateImageView(m_gpu->dev(), &viewInfo, NULL, &m_pImageViews[i]) == VK_SUCCESS,
|
||||
"Failed to create image view");
|
||||
}
|
||||
return RESULT_OK;
|
||||
}
|
||||
|
||||
ResultCode Swapchain::create_swapchain() {
|
||||
VkSurfaceCapabilitiesKHR capabilities = {};
|
||||
vkGetPhysicalDeviceSurfaceCapabilitiesKHR(m_gpu->gpu(), m_surface, &capabilities);
|
||||
|
||||
VkSwapchainCreateInfoKHR swapchainInfo = {
|
||||
.sType = VK_STRUCTURE_TYPE_SWAPCHAIN_CREATE_INFO_KHR,
|
||||
.surface = m_surface,
|
||||
.minImageCount = m_numImages,
|
||||
.imageFormat = m_format.format,
|
||||
.imageColorSpace = m_format.colorSpace,
|
||||
.imageExtent = m_extent,
|
||||
.imageArrayLayers = 1,
|
||||
.imageUsage = VK_IMAGE_USAGE_COLOR_ATTACHMENT_BIT,
|
||||
.preTransform = capabilities.currentTransform,
|
||||
.compositeAlpha = VK_COMPOSITE_ALPHA_OPAQUE_BIT_KHR,
|
||||
.presentMode = choose_present_mode(),
|
||||
.clipped = VK_TRUE
|
||||
};
|
||||
|
||||
/* Allow sharing across graphics and present queue when those two are not
|
||||
* the same. Meaning we can draw with commands submitted to graphics queue
|
||||
* to swapchain images, that need to be used by command submitted to
|
||||
* present queue. */
|
||||
auto presentQueues = m_gpu->present_queue_ids();
|
||||
if(presentQueues.size() > 1) {
|
||||
swapchainInfo.imageSharingMode = VK_SHARING_MODE_CONCURRENT;
|
||||
swapchainInfo.queueFamilyIndexCount = presentQueues.size();
|
||||
swapchainInfo.pQueueFamilyIndices = presentQueues.data();
|
||||
} else {
|
||||
/* Don't need to share the images */
|
||||
swapchainInfo.imageSharingMode = VK_SHARING_MODE_EXCLUSIVE;
|
||||
}
|
||||
|
||||
EXPECT(vkCreateSwapchainKHR(m_gpu->dev(), &swapchainInfo, NULL, &m_swapchain) == VK_SUCCESS, "Failed to create swapchain");
|
||||
|
||||
return RESULT_OK;
|
||||
}
|
||||
|
||||
void Swapchain::cleanup() {
|
||||
for(int i = 0; i < m_numImages; i++) {
|
||||
vkDestroyImageView(m_gpu->dev(), m_pImageViews[i], nullptr);
|
||||
}
|
||||
|
||||
vkDestroySwapchainKHR(m_gpu->dev(), m_swapchain, nullptr);
|
||||
}
|
||||
|
||||
void Swapchain::resize(VkExtent2D extent) {
|
||||
cleanup();
|
||||
|
||||
m_extent = extent;
|
||||
|
||||
create_swapchain();
|
||||
|
||||
get_images(&m_numImages, NULL);
|
||||
m_pImages = new VkImage[m_numImages];
|
||||
get_images(&m_numImages, m_pImages);
|
||||
|
||||
create_views();
|
||||
|
||||
m_images.resize(m_numImages);
|
||||
m_views.resize(m_numImages);
|
||||
for(int i = 0; i < m_numImages; i++) {
|
||||
m_images[i].img = m_pImages[i];
|
||||
std::println("View: {:#06x}", (unsigned long)m_pImageViews[i]);
|
||||
m_views[i].view = m_pImageViews[i];
|
||||
}
|
||||
}
|
||||
|
||||
Swapchain::Swapchain(const Gpu* gpu, VkExtent2D extent, Surface* surface) :
|
||||
m_gpu(gpu),
|
||||
m_surface(surface->surface())
|
||||
{
|
||||
VkSurfaceCapabilitiesKHR capabilities = {};
|
||||
vkGetPhysicalDeviceSurfaceCapabilitiesKHR(m_gpu->gpu(), m_surface, &capabilities);
|
||||
|
||||
m_format = choose_format();
|
||||
m_extent = choose_extent(extent);
|
||||
|
||||
uint32_t imageCount = capabilities.minImageCount + 1;
|
||||
m_numImages = imageCount;
|
||||
|
||||
if(capabilities.maxImageCount > 0 &&
|
||||
imageCount < capabilities.maxImageCount) {
|
||||
m_numImages = capabilities.maxImageCount;
|
||||
}
|
||||
m_numFrames = m_numImages;
|
||||
|
||||
create_swapchain();
|
||||
|
||||
get_images(&m_numImages, NULL);
|
||||
m_pImages = new VkImage[m_numImages];
|
||||
get_images(&m_numImages, m_pImages);
|
||||
|
||||
create_views();
|
||||
|
||||
m_images.resize(m_numImages);
|
||||
m_views.resize(m_numImages);
|
||||
for(int i = 0; i < m_numImages; i++) {
|
||||
m_images[i].img = m_pImages[i];
|
||||
m_views[i].view = m_pImageViews[i];
|
||||
}
|
||||
}
|
||||
+235
@@ -0,0 +1,235 @@
|
||||
#include "../include/event.h"
|
||||
#include "../include/window.h"
|
||||
|
||||
#if PLATFORM_WINDOWS
|
||||
#include <X11/X.h>
|
||||
#include <stdlib.h>
|
||||
#include <stdio.h>
|
||||
#include <assert.h>
|
||||
#include <string.h>
|
||||
|
||||
#include <X11/Xutil.h>
|
||||
#include <X11/Xlib.h>
|
||||
|
||||
#include <vulkan/vulkan.h>
|
||||
#include <vulkan/vulkan_xlib.h>
|
||||
|
||||
struct WindowHandle_T {
|
||||
Window window;
|
||||
|
||||
/* Used for comparing when ConfigureNotify event occurs, to see if we just
|
||||
* moved or scaled */
|
||||
int x, y;
|
||||
unsigned width, height;
|
||||
|
||||
Display *pDisplay;
|
||||
XVisualInfo *pVisual;
|
||||
Colormap colormap;
|
||||
|
||||
Atom wmDeleteWindow;
|
||||
int isOpen;
|
||||
|
||||
int isMinimized;
|
||||
};
|
||||
|
||||
static int
|
||||
xlib_debug_callback(Display* display, XErrorEvent* event) {
|
||||
char* buffer = (char*)malloc(2048);
|
||||
XGetErrorText(display, event->error_code, buffer, 2048);
|
||||
|
||||
printf("Xlib error => %s", buffer);
|
||||
free(buffer);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
WindowResult window_new(WindowCreateInfo *pInfo, WindowHandle *pOut) {
|
||||
*pOut = (struct WindowHandle_T*)malloc(sizeof(struct WindowHandle_T));
|
||||
if(!pOut) {
|
||||
return WINDOW_ALLOCATION_FAILED;
|
||||
}
|
||||
// memset(pOut, 0, sizeof(struct WindowHandle_T));
|
||||
|
||||
/* XInitThreads(); */
|
||||
XSetErrorHandler(xlib_debug_callback);
|
||||
|
||||
(*pOut)->pDisplay = XOpenDisplay(NULL);
|
||||
if(!(*pOut)->pDisplay) {
|
||||
return WINDOW_CONNECTION_FAILED;
|
||||
}
|
||||
|
||||
long visualMask = VisualScreenMask;
|
||||
XVisualInfo visualInfo = {};
|
||||
visualInfo.screen = DefaultScreen((*pOut)->pDisplay);
|
||||
|
||||
int visualCount = 0;
|
||||
(*pOut)->pVisual = XGetVisualInfo((*pOut)->pDisplay, visualMask, &visualInfo, &visualCount);
|
||||
(*pOut)->colormap = XCreateColormap((*pOut)->pDisplay,
|
||||
DefaultRootWindow((*pOut)->pDisplay),
|
||||
(*pOut)->pVisual->visual, AllocNone);
|
||||
|
||||
{
|
||||
XSetWindowAttributes windowAttributes = {};
|
||||
windowAttributes.colormap = (*pOut)->colormap;
|
||||
windowAttributes.event_mask = ExposureMask | ButtonPressMask | ButtonReleaseMask | KeyPressMask | KeyReleaseMask | PointerMotionMask | StructureNotifyMask;
|
||||
windowAttributes.override_redirect = False;
|
||||
|
||||
(*pOut)->window = XCreateWindow((*pOut)->pDisplay, DefaultRootWindow((*pOut)->pDisplay),
|
||||
pInfo->x, pInfo->y,
|
||||
pInfo->width, pInfo->height, 0,
|
||||
(*pOut)->pVisual->depth, InputOutput,
|
||||
(*pOut)->pVisual->visual,
|
||||
CWColormap | CWEventMask | CWOverrideRedirect,
|
||||
&windowAttributes);
|
||||
}
|
||||
|
||||
(*pOut)->isOpen = 1;
|
||||
|
||||
/* handles closing the window by the user */
|
||||
(*pOut)->wmDeleteWindow = XInternAtom((*pOut)->pDisplay, "WM_DELETE_WINDOW", False);
|
||||
XSetWMProtocols((*pOut)->pDisplay, (*pOut)->window, &(*pOut)->wmDeleteWindow, 1);
|
||||
|
||||
XMapWindow((*pOut)->pDisplay, (*pOut)->window);
|
||||
XFlush((*pOut)->pDisplay);
|
||||
|
||||
(*pOut)->width = pInfo->width;
|
||||
(*pOut)->height = pInfo->height;
|
||||
|
||||
return WINDOW_SUCCESS;
|
||||
}
|
||||
|
||||
|
||||
VkSurfaceKHR window_vk_create_surface(VkInstance instance, WindowHandle window) {
|
||||
VkXlibSurfaceCreateInfoKHR surfaceInfo = {
|
||||
.sType = VK_STRUCTURE_TYPE_XLIB_SURFACE_CREATE_INFO_KHR,
|
||||
.dpy = window->pDisplay,
|
||||
.window = window->window,
|
||||
};
|
||||
|
||||
VkSurfaceKHR result = VK_NULL_HANDLE;
|
||||
|
||||
if(vkCreateXlibSurfaceKHR(instance, &surfaceInfo, NULL, &result) != VK_SUCCESS) {
|
||||
return VK_NULL_HANDLE;
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
int window_solve_event(XEvent *pEvent, WindowEvent *pOutEvent) {
|
||||
assert(pEvent && pOutEvent);
|
||||
|
||||
WindowEvent result = {};
|
||||
result.type = NONE_EVENT_TYPE;
|
||||
switch(pEvent->type) {
|
||||
case KeyPress:
|
||||
result.type = KEY_EVENT_TYPE;
|
||||
result.key.state = STATE_PRESSED;
|
||||
result.key.key = XLookupKeysym(&pEvent->xkey, 0);
|
||||
break;
|
||||
case KeyRelease:
|
||||
result.type = KEY_EVENT_TYPE;
|
||||
result.key.state = STATE_RELEASED;
|
||||
result.key.key = XLookupKeysym(&pEvent->xkey, 0);
|
||||
result.key.txt = XKeysymToString(result.key.key);
|
||||
break;
|
||||
case ButtonPress:
|
||||
result.type = BUTTON_EVENT_TYPE;
|
||||
result.button.state = STATE_PRESSED;
|
||||
result.button.button = pEvent->xbutton.button;
|
||||
result.button.x = pEvent->xbutton.x;
|
||||
result.button.y = pEvent->xbutton.y;
|
||||
break;
|
||||
case ButtonRelease:
|
||||
result.type = BUTTON_EVENT_TYPE;
|
||||
result.button.state = STATE_RELEASED;
|
||||
result.button.button = pEvent->xbutton.button;
|
||||
result.button.x = pEvent->xbutton.x;
|
||||
result.button.y = pEvent->xbutton.y;
|
||||
break;
|
||||
case MotionNotify:
|
||||
result.type = MOTION_EVENT_TYPE;
|
||||
result.motion.x = pEvent->xmotion.x;
|
||||
result.motion.y = pEvent->xmotion.y;
|
||||
break;
|
||||
case ConfigureNotify:
|
||||
result.type = TRANSFORM_EVENT_TYPE;
|
||||
result.transform.x = pEvent->xconfigure.x;
|
||||
result.transform.y = pEvent->xconfigure.y;
|
||||
result.transform.width = pEvent->xconfigure.width;
|
||||
result.transform.height = pEvent->xconfigure.height;
|
||||
break;
|
||||
}
|
||||
|
||||
*pOutEvent = result;
|
||||
|
||||
return result.type != NONE_EVENT_TYPE;
|
||||
}
|
||||
|
||||
int window_is_closed(WindowHandle window) {
|
||||
return !window->isOpen;
|
||||
}
|
||||
|
||||
void window_close(WindowHandle window) {
|
||||
window->isOpen = 0;
|
||||
}
|
||||
|
||||
int window_next_event(WindowHandle window, WindowEvent *pOutEvent) {
|
||||
assert(window && pOutEvent);
|
||||
|
||||
XEvent event;
|
||||
int status = XNextEvent(window->pDisplay, &event);
|
||||
|
||||
WindowEvent solvedEvent = {};
|
||||
status = window_solve_event(&event, &solvedEvent);
|
||||
|
||||
if(event.type == ClientMessage) {
|
||||
if(event.xclient.data.l[0] == window->wmDeleteWindow) {
|
||||
window_close(window);
|
||||
}
|
||||
} else if(event.type == MapNotify) {
|
||||
window->isMinimized = 1;
|
||||
} else if(event.type == UnmapNotify) {
|
||||
window->isMinimized = 0;
|
||||
} else if(event.type == ConfigureNotify) {
|
||||
window->width = event.xconfigure.width;
|
||||
window->height = event.xconfigure.height;
|
||||
}
|
||||
|
||||
if(status) {
|
||||
*pOutEvent = solvedEvent;
|
||||
return 1;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
int window_pending_event(WindowHandle window) {
|
||||
return XPending(window->pDisplay);
|
||||
}
|
||||
|
||||
void window_destroy(WindowHandle window) {
|
||||
XDestroyWindow(window->pDisplay, window->window);
|
||||
XCloseDisplay(window->pDisplay);
|
||||
}
|
||||
|
||||
int window_is_minimized(WindowHandle window) {
|
||||
return window->isMinimized;
|
||||
}
|
||||
|
||||
void window_get_extent(WindowHandle window, int *pOutWidth, int *pOutHeight) {
|
||||
*pOutWidth = window->width;
|
||||
*pOutHeight = window->height;
|
||||
}
|
||||
|
||||
void window_set_clipboard(WindowHandle window, const char *text) {
|
||||
|
||||
}
|
||||
|
||||
char *window_get_clipboard(WindowHandle window) {
|
||||
return NULL;
|
||||
}
|
||||
|
||||
WindowCursor window_create_cursor(WindowHandle window, int idx) {
|
||||
return 0;
|
||||
}
|
||||
#endif
|
||||
Reference in New Issue
Block a user