cursor fixes, add locking/unlocking cursor

This commit is contained in:
matthew 2025-07-30 07:31:37 +10:00
parent b24ff3cb8e
commit 0c8169713d
10 changed files with 137 additions and 36 deletions

Binary file not shown.

View File

@ -11,7 +11,7 @@
"sourceFiles-windows": [], "sourceFiles-windows": [],
"importPaths": ["src/gears", "src/shared", "src/generated", "external/xxhash", "external/inteli"], "importPaths": ["src/gears", "src/shared", "src/generated", "external/xxhash", "external/inteli"],
"sourcePaths": ["src/gears", "src/shared", "src/generated", "external/xxhash", "external/inteli"], "sourcePaths": ["src/gears", "src/shared", "src/generated", "external/xxhash", "external/inteli"],
"libs-linux": ["xcb", "X11", "X11-xcb", "vulkan", "stdc++"], "libs-linux": ["xcb", "X11", "X11-xcb", "vulkan", "stdc++", "xcb-xfixes"],
"libs-windows": [], "libs-windows": [],
"preGenerateCommands-linux": ["./build.sh", "build/Packer"], "preGenerateCommands-linux": ["./build.sh", "build/Packer"],
"preGenerateCommands-windows": [], "preGenerateCommands-windows": [],

View File

@ -83,7 +83,9 @@ InitGame(PlatformWindow* window)
g.ui_pipeline = BuildGfxPipeline(&g.rd, &ui_info); g.ui_pipeline = BuildGfxPipeline(&g.rd, &ui_info);
g.compute_pipeline = BuildCompPipeline(&g.rd, &gradient_info); g.compute_pipeline = BuildCompPipeline(&g.rd, &gradient_info);
g.model = LoadModel(&g.rd, "models/yoda.m3d"); PrintShader(&g.rd, &g.pbr_pipeline, VK_SHADER_STAGE_VERTEX_BIT);
g.model = LoadModel(&g.rd, "models/Tree01.m3d");
return g; return g;
} }
@ -190,6 +192,7 @@ Cycle(Game* g)
void void
Destroy(Game* g) Destroy(Game* g)
{ {
WaitIdle(&g.rd);
Destroy(&g.rd, &g.pbr_pipeline); Destroy(&g.rd, &g.pbr_pipeline);
Destroy(&g.rd, &g.triangle_pipeline); Destroy(&g.rd, &g.triangle_pipeline);
Destroy(&g.rd, &g.ui_pipeline); Destroy(&g.rd, &g.ui_pipeline);

View File

@ -11,24 +11,13 @@ import math;
// TODO: // TODO:
// 1. Remove bindless (informed to be perf death) // 1. Remove bindless (informed to be perf death)
// 2. Set up VK_AMD_shader_info // 2. Set up VK_AMD_shader_info
// 3. Determine how to better handle inputs
// 4. Make assets loaded from the disk in debug mode
void main() void main(string[] argv)
{ {
PlatformWindow window = CreateWindow("Video Game", 1920, 1080); PlatformWindow window = CreateWindow("Video Game", 1920, 1080);
Vec4 vec = Vec4(Vec3(22.0, 53.0, 3.0), 12.0);
Mat4 mat = Mat4(
1.0, 55.0, 23.0, 0.0,
0.0, 1.0, 0.0, 0.0,
123.0, 0.0, 13.0, 0.0,
0.0, 77.0, 0.0, 17.0
);
Vec4 res = mat * vec;
Logf("%s", res.v);
Game g = InitGame(&window); Game g = InitGame(&window);
scope(exit) Destroy(&g); scope(exit) Destroy(&g);

View File

@ -64,6 +64,7 @@ struct PlatformWindow
{ {
Display *display; Display *display;
xcb_connection_t *conn; xcb_connection_t *conn;
xcb_screen_t *screen;
xcb_window_t window; xcb_window_t window;
xcb_atom_t close_event; xcb_atom_t close_event;
xcb_atom_t minimize_event; xcb_atom_t minimize_event;
@ -71,6 +72,7 @@ struct PlatformWindow
u16 h; u16 h;
i32 mouse_prev_x; i32 mouse_prev_x;
i32 mouse_prev_y; i32 mouse_prev_y;
bool locked_cursor;
bool close; bool close;
InputEvent[10] inputs; InputEvent[10] inputs;
u32 input_count; u32 input_count;
@ -112,7 +114,7 @@ PlatformWindow CreateWindow(string name, u16 width, u16 height)
xcb_setup_t *setup = xcb_get_setup(window.conn); xcb_setup_t *setup = xcb_get_setup(window.conn);
xcb_screen_iterator_t iter = xcb_setup_roots_iterator(setup); xcb_screen_iterator_t iter = xcb_setup_roots_iterator(setup);
xcb_screen_t *screen = iter.data; window.screen = iter.data;
i32 event_mask = XCB_EVENT_MASK_EXPOSURE | i32 event_mask = XCB_EVENT_MASK_EXPOSURE |
XCB_EVENT_MASK_KEY_PRESS | XCB_EVENT_MASK_KEY_PRESS |
@ -122,7 +124,7 @@ PlatformWindow CreateWindow(string name, u16 width, u16 height)
XCB_EVENT_MASK_POINTER_MOTION | XCB_EVENT_MASK_POINTER_MOTION |
XCB_EVENT_MASK_STRUCTURE_NOTIFY; XCB_EVENT_MASK_STRUCTURE_NOTIFY;
i32[] val_win = [screen.black_pixel, event_mask]; i32[] val_win = [window.screen.black_pixel, event_mask];
i32 val_mask = XCB_CW_BACK_PIXEL | XCB_CW_EVENT_MASK; i32 val_mask = XCB_CW_BACK_PIXEL | XCB_CW_EVENT_MASK;
window.window = xcb_generate_id(window.conn); window.window = xcb_generate_id(window.conn);
@ -131,23 +133,25 @@ PlatformWindow CreateWindow(string name, u16 width, u16 height)
window.conn, window.conn,
XCB_COPY_FROM_PARENT, XCB_COPY_FROM_PARENT,
window.window, window.window,
screen.root, window.screen.root,
0, // x pos 0, // x pos
0, // y pos 0, // y pos
window.w, // width window.w, // width
window.h, // height window.h, // height
0, 0,
XCB_WINDOW_CLASS_INPUT_OUTPUT, XCB_WINDOW_CLASS_INPUT_OUTPUT,
screen.root_visual, window.screen.root_visual,
val_mask, val_mask,
val_win.ptr val_win.ptr
); );
error = xcb_request_check(window.conn, cookie); error = xcb_request_check(window.conn, cookie);
CheckErr(&window, &cookie, error, "xcb_create_window failure"); CheckErr(&window, &cookie, error, "xcb_create_window failure");
pureFree(error);
cookie = xcb_map_window_checked(window.conn, window.window); cookie = xcb_map_window_checked(window.conn, window.window);
error = xcb_request_check(window.conn, cookie); error = xcb_request_check(window.conn, cookie);
CheckErr(&window, &cookie, error, "xcb_map_window_checked failure"); CheckErr(&window, &cookie, error, "xcb_map_window_checked failure");
pureFree(error);
cookie = xcb_change_property_checked( cookie = xcb_change_property_checked(
window.conn, window.conn,
@ -161,19 +165,22 @@ PlatformWindow CreateWindow(string name, u16 width, u16 height)
); );
error = xcb_request_check(window.conn, cookie); error = xcb_request_check(window.conn, cookie);
CheckErr(&window, &cookie, error, "xcb_change_property_checked failure"); CheckErr(&window, &cookie, error, "xcb_change_property_checked failure");
pureFree(error);
xcb_intern_atom_cookie_t c_proto = xcb_intern_atom(window.conn, 1, 12, "WM_PROTOCOLS"); xcb_intern_atom_cookie_t c_proto = xcb_intern_atom(window.conn, 1, 12, "WM_PROTOCOLS");
xcb_intern_atom_reply_t *r_proto = xcb_intern_atom_reply(window.conn, c_proto, &error); xcb_intern_atom_reply_t *r_proto = xcb_intern_atom_reply(window.conn, c_proto, &error);
CheckErr(&window, &cookie, error, "xcb_intern_atom WM_PROTOCOLS failure"); CheckErr(&window, &cookie, error, "xcb_intern_atom WM_PROTOCOLS failure");
pureFree(error);
xcb_intern_atom_cookie_t c_close = xcb_intern_atom(window.conn, 0, 16, "WM_DELETE_WINDOW"); xcb_intern_atom_cookie_t c_close = xcb_intern_atom(window.conn, 0, 16, "WM_DELETE_WINDOW");
xcb_intern_atom_reply_t *r_close = xcb_intern_atom_reply(window.conn, c_close, &error); xcb_intern_atom_reply_t *r_close = xcb_intern_atom_reply(window.conn, c_close, &error);
CheckErr(&window, &cookie, error, "xcb_intern_atom WM_DELETE_WINDOW failure"); CheckErr(&window, &cookie, error, "xcb_intern_atom WM_DELETE_WINDOW failure");
pureFree(error);
xcb_intern_atom_cookie_t c_minimize = xcb_intern_atom(window.conn, 0, 20, "_NET_WM_STATE_HIDDEN"); xcb_intern_atom_cookie_t c_minimize = xcb_intern_atom(window.conn, 0, 20, "_NET_WM_STATE_HIDDEN");
xcb_intern_atom_reply_t *r_minimize = xcb_intern_atom_reply(window.conn, c_minimize, &error); xcb_intern_atom_reply_t *r_minimize = xcb_intern_atom_reply(window.conn, c_minimize, &error);
CheckErr(&window, &cookie, error, "xcb_intern_atom _NET_WM_STATE_HIDDEN failure"); CheckErr(&window, &cookie, error, "xcb_intern_atom _NET_WM_STATE_HIDDEN failure");
pureFree(error);
cookie = xcb_change_property_checked( cookie = xcb_change_property_checked(
window.conn, window.conn,
@ -187,6 +194,7 @@ PlatformWindow CreateWindow(string name, u16 width, u16 height)
); );
error = xcb_request_check(window.conn, cookie); error = xcb_request_check(window.conn, cookie);
CheckErr(&window, &cookie, error, "xcb_change_property_checked failure"); CheckErr(&window, &cookie, error, "xcb_change_property_checked failure");
pureFree(error);
window.close_event = r_close.atom; window.close_event = r_close.atom;
window.minimize_event = r_minimize.atom; window.minimize_event = r_minimize.atom;
@ -200,11 +208,27 @@ PlatformWindow CreateWindow(string name, u16 width, u16 height)
i32 stream_result = xcb_flush(window.conn); i32 stream_result = xcb_flush(window.conn);
assert(stream_result > 0, "xcb_flush failure"); assert(stream_result > 0, "xcb_flush failure");
xcb_xfixes_query_version(window.conn, 4, 0);
return window;
};
void
LockCursor(PlatformWindow* window)
{
u32 counter = 0; u32 counter = 0;
for(;;) for(;;)
{ {
xcb_generic_error_t *error;
xcb_grab_pointer_cookie_t grab_cookie = xcb_grab_pointer(window.conn, true, window.window, 0, XCB_GRAB_MODE_ASYNC, XCB_GRAB_MODE_ASYNC, window.window, XCB_NONE, XCB_CURRENT_TIME); xcb_grab_pointer_cookie_t grab_cookie = xcb_grab_pointer(window.conn, true, window.window, 0, XCB_GRAB_MODE_ASYNC, XCB_GRAB_MODE_ASYNC, window.window, XCB_NONE, XCB_CURRENT_TIME);
xcb_grab_pointer_reply_t* grab_reply = xcb_grab_pointer_reply(window.conn, grab_cookie, &error); xcb_grab_pointer_reply_t* grab_reply = xcb_grab_pointer_reply(window.conn, grab_cookie, &error);
scope(exit)
{
pureFree(error);
pureFree(grab_reply);
}
if (grab_reply.status == XCB_GRAB_STATUS_SUCCESS) if (grab_reply.status == XCB_GRAB_STATUS_SUCCESS)
{ {
break; break;
@ -215,8 +239,36 @@ PlatformWindow CreateWindow(string name, u16 width, u16 height)
Thread.sleep(dur!("msecs")(50)); Thread.sleep(dur!("msecs")(50));
} }
return window; HideCursor(window);
}; window.locked_cursor = true;
}
void
UnlockCursor(PlatformWindow* window)
{
xcb_ungrab_pointer(window.conn, XCB_CURRENT_TIME);
ShowCursor(window);
window.locked_cursor = false;
}
// TODO: improve error handling for show/hide cursor
void
HideCursor(PlatformWindow* window)
{
xcb_void_cookie_t hide_cursor_cookie = xcb_xfixes_hide_cursor_checked(window.conn, window.window);
xcb_generic_error_t *error = xcb_request_check(window.conn, hide_cursor_cookie);
CheckErr(window, &hide_cursor_cookie, error, "xcb_xfixes_hide_cursor_checked failure");
pureFree(error);
}
void
ShowCursor(PlatformWindow* window)
{
xcb_void_cookie_t show_cursor_cookie = xcb_xfixes_show_cursor_checked(window.conn, window.window);
xcb_generic_error_t *error = xcb_request_check(window.conn, show_cursor_cookie);
CheckErr(window, &show_cursor_cookie, error, "xcb_xfixes_show_cursor_checked failure");
pureFree(error);
}
void void
FlushEvents(PlatformWindow* window) FlushEvents(PlatformWindow* window)
@ -279,6 +331,15 @@ HandleEvents(PlatformWindow* window)
window.inputs[window.input_count].key = input; window.inputs[window.input_count].key = input;
window.inputs[window.input_count].pressed = pressed; window.inputs[window.input_count].pressed = pressed;
window.input_count += 1; window.input_count += 1;
if (input == KBI.F2)
{
LockCursor(window);
}
else if (input == KBI.F3)
{
UnlockCursor(window);
}
} }
} break; } break;
case XCB_BUTTON_PRESS: case XCB_BUTTON_PRESS:
@ -333,7 +394,8 @@ HandleEvents(PlatformWindow* window)
window.mouse_prev_x = x; window.mouse_prev_x = x;
window.mouse_prev_y = y; window.mouse_prev_y = y;
if (x < WINDOW_EDGE_BUFFER || y < WINDOW_EDGE_BUFFER || x > window.w - WINDOW_EDGE_BUFFER || y > window.h - WINDOW_EDGE_BUFFER) if (window.locked_cursor &&
(x < WINDOW_EDGE_BUFFER || y < WINDOW_EDGE_BUFFER || x > window.w - WINDOW_EDGE_BUFFER || y > window.h - WINDOW_EDGE_BUFFER))
{ {
i16 new_x = cast(i16)(window.w / 2); i16 new_x = cast(i16)(window.w / 2);
i16 new_y = cast(i16)(window.h / 2); i16 new_y = cast(i16)(window.h / 2);

View File

@ -4,7 +4,7 @@ import assets;
import util; import util;
import alloc; import alloc;
import vulkan; import vulkan;
import vulkan : Destroy, Init, Draw, DrawIndexed, Bind, BindUIBuffers, BeginRender, SetUniform, PrepComputeDrawImage, Dispatch, FinishFrame, BeginFrame; import vulkan : Destroy, Init, Draw, DrawIndexed, Bind, BindUIBuffers, BeginRender, SetUniform, PrepComputeDrawImage, Dispatch, FinishFrame, BeginFrame, WaitIdle;
import assets; import assets;
import std.math.traits : isNaN; import std.math.traits : isNaN;
import util : Logf; import util : Logf;
@ -522,3 +522,15 @@ Destroy(Renderer* rd, Pipeline* pipeline)
{ {
Destroy(&rd.vk, pipeline); Destroy(&rd.vk, pipeline);
} }
void
WaitIdle(Renderer* rd)
{
WaitIdle(&rd.vk);
}
void
PrintShader(Renderer* rd, Pipeline* pipeline, VkShaderStageFlagBits stage)
{
PrintShaderDisassembly(&rd.vk, pipeline, stage);
}

View File

@ -48,12 +48,28 @@ version(Windows)
const char*[] VK_INSTANCE_EXT_DEBUG = VK_INSTANCE_EXT ~ [ cast(char*)VK_EXT_DEBUG_UTILS_EXTENSION_NAME ]; const char*[] VK_INSTANCE_EXT_DEBUG = VK_INSTANCE_EXT ~ [ cast(char*)VK_EXT_DEBUG_UTILS_EXTENSION_NAME ];
} }
const char*[] VK_DEVICE_EXTENSIONS = [ const char*[] VK_BASE_DEVICE_EXTENSIONS = [
cast(char*)VK_KHR_SWAPCHAIN_EXTENSION_NAME, cast(char*)VK_KHR_SWAPCHAIN_EXTENSION_NAME,
cast(char*)VK_KHR_UNIFORM_BUFFER_STANDARD_LAYOUT_EXTENSION_NAME, cast(char*)VK_KHR_UNIFORM_BUFFER_STANDARD_LAYOUT_EXTENSION_NAME,
cast(char*)VK_KHR_8BIT_STORAGE_EXTENSION_NAME, cast(char*)VK_KHR_8BIT_STORAGE_EXTENSION_NAME,
]; ];
const char*[] VK_AMD_DEVICE_EXTENSIONS = [
cast(char*)VK_AMD_SHADER_INFO_EXTENSION_NAME,
];
version(AMD_GPU)
{
debug
{
const char*[] VK_DEVICE_EXTENSIONS = VK_AMD_DEVICE_EXTENSIONS ~ VK_BASE_DEVICE_EXTENSIONS;
}
}
else
{
const char*[] VK_DEVICE_EXTENSIONS = VK_BASE_DEVICE_EXTENSIONS;
}
const VkFormat[] VK_IMAGE_FORMATS = [ const VkFormat[] VK_IMAGE_FORMATS = [
VK_FORMAT_R16G16B16A16_UNORM, VK_FORMAT_R16G16B16A16_UNORM,
VK_FORMAT_R8G8B8A8_UNORM, VK_FORMAT_R8G8B8A8_UNORM,
@ -1473,6 +1489,12 @@ SetUniform(Vulkan* vk, GlobalUniforms* globals)
vkUpdateDescriptorSets(vk.device, 1, &write, 0, null); vkUpdateDescriptorSets(vk.device, 1, &write, 0, null);
} }
void
WaitIdle(Vulkan* vk)
{
vkDeviceWaitIdle(vk.device);
}
void void
Destroy(Vulkan* vk, Shader shader) Destroy(Vulkan* vk, Shader shader)
{ {
@ -2930,13 +2952,16 @@ PrintShaderDisassembly(Vulkan* vk, Pipeline* pipeline, VkShaderStageFlagBits sta
{ {
version(AMD_GPU) version(AMD_GPU)
{ {
u64 size; debug
VkResult result = vkGetShaderInfoAMD(vk.device, pipeline.handle, stage, VK_SHADER_INFO_TYPE_DISASSEMBLY_AMD, &size, null);
if (result == VK_SUCCESS)
{ {
u8[] buf = AllocArray!(u8)(vk.frame_arenas[vk.frame_index], size); u64 size;
vkGetShaderInfoAMD(vk.device, pipeline.handle, stage, VK_SHADER_INFO_TYPE_DISASSEMBLY_AMD, &size, buf.ptr); VkResult result = vkGetShaderInfoAMD(vk.device, pipeline.handle, stage, VK_SHADER_INFO_TYPE_DISASSEMBLY_AMD, &size, null);
Logf("DISASSEMBLY:\n%r", buf); if (result == VK_SUCCESS)
{
u8[] buf = AllocArray!(u8)(&vk.frame_arenas[vk.frame_index], size);
vkGetShaderInfoAMD(vk.device, pipeline.handle, stage, VK_SHADER_INFO_TYPE_DISASSEMBLY_AMD, &size, buf.ptr);
Logf("DISASSEMBLY:\n%r", buf);
}
} }
} }
} }

View File

@ -43,9 +43,13 @@ version(Windows)
PFN_vkCreateWin32SurfaceKHR vkCreateWin32SurfaceKHR = null; PFN_vkCreateWin32SurfaceKHR vkCreateWin32SurfaceKHR = null;
}; };
debug
version(AMD_GPU)
{ {
PFN_vkGetShaderInfoAMD vkGetShaderInfoAMD = null; debug
{
PFN_vkGetShaderInfoAMD vkGetShaderInfoAMD = null;
}
} }
@ -212,7 +216,11 @@ LoadDeviceFunctions(Vulkan* vk)
version(AMD_GPU) version(AMD_GPU)
{ {
vkGetShaderInfoAMD = cast(PFN_vkGetShaderInfoAMD)vkGetShaderInfoAMD(vk.device, "vkGetShaderInfoAMD"); debug
{
vkGetShaderInfoAMD = cast(PFN_vkGetShaderInfoAMD)vkGetDeviceProcAddr(vk.device, "vkGetShaderInfoAMD");
assert(vkGetShaderInfoAMD != null, "vkGetShaderInfoAMD pointer is null");
}
} }
assert(vkCreateSwapchainKHR != null, "LoadDeviceFunctions failure: function pointer is null"); assert(vkCreateSwapchainKHR != null, "LoadDeviceFunctions failure: function pointer is null");

View File

@ -29,7 +29,7 @@ void main()
if (Materials[nonuniformEXT(PC.mat_id)].albedo_has_texture) if (Materials[nonuniformEXT(PC.mat_id)].albedo_has_texture)
{ {
vec4 tex_col = texture(sampler2D(Textures[nonuniformEXT(Materials[nonuniformEXT(PC.mat_id)].albedo_texture)], SamplerNearest), in_uv); vec4 tex_col = texture(sampler2D(Textures[nonuniformEXT(Materials[nonuniformEXT(PC.mat_id)].albedo_texture)], SamplerNearest), in_uv);
col = mix(col, tex_col, 0.5); col = col * tex_col;
} }
out_col = col; out_col = col;

View File

@ -1,9 +1,11 @@
#ifdef __linux__ #ifdef __linux__
# include <xcb/xcb.h> # include <xcb/xcb.h>
# include <xcb/xfixes.h>
# include <X11/XKBlib.h> # include <X11/XKBlib.h>
# include <X11/Xlib-xcb.h> # include <X11/Xlib-xcb.h>
# include <X11/Xlib.h> # include <X11/Xlib.h>
# include <X11/keysym.h> # include <X11/keysym.h>
# include <X11/extensions/Xfixes.h>
#endif #endif
#if __linux__ #if __linux__