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": [],
"importPaths": ["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": [],
"preGenerateCommands-linux": ["./build.sh", "build/Packer"],
"preGenerateCommands-windows": [],

View File

@ -83,7 +83,9 @@ InitGame(PlatformWindow* window)
g.ui_pipeline = BuildGfxPipeline(&g.rd, &ui_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;
}
@ -190,6 +192,7 @@ Cycle(Game* g)
void
Destroy(Game* g)
{
WaitIdle(&g.rd);
Destroy(&g.rd, &g.pbr_pipeline);
Destroy(&g.rd, &g.triangle_pipeline);
Destroy(&g.rd, &g.ui_pipeline);

View File

@ -11,24 +11,13 @@ import math;
// TODO:
// 1. Remove bindless (informed to be perf death)
// 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);
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);
scope(exit) Destroy(&g);

View File

@ -64,6 +64,7 @@ struct PlatformWindow
{
Display *display;
xcb_connection_t *conn;
xcb_screen_t *screen;
xcb_window_t window;
xcb_atom_t close_event;
xcb_atom_t minimize_event;
@ -71,6 +72,7 @@ struct PlatformWindow
u16 h;
i32 mouse_prev_x;
i32 mouse_prev_y;
bool locked_cursor;
bool close;
InputEvent[10] inputs;
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_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 |
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_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;
window.window = xcb_generate_id(window.conn);
@ -131,23 +133,25 @@ PlatformWindow CreateWindow(string name, u16 width, u16 height)
window.conn,
XCB_COPY_FROM_PARENT,
window.window,
screen.root,
window.screen.root,
0, // x pos
0, // y pos
window.w, // width
window.h, // height
0,
XCB_WINDOW_CLASS_INPUT_OUTPUT,
screen.root_visual,
window.screen.root_visual,
val_mask,
val_win.ptr
);
error = xcb_request_check(window.conn, cookie);
CheckErr(&window, &cookie, error, "xcb_create_window failure");
pureFree(error);
cookie = xcb_map_window_checked(window.conn, window.window);
error = xcb_request_check(window.conn, cookie);
CheckErr(&window, &cookie, error, "xcb_map_window_checked failure");
pureFree(error);
cookie = xcb_change_property_checked(
window.conn,
@ -161,19 +165,22 @@ PlatformWindow CreateWindow(string name, u16 width, u16 height)
);
error = xcb_request_check(window.conn, cookie);
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_reply_t *r_proto = xcb_intern_atom_reply(window.conn, c_proto, &error);
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_reply_t *r_close = xcb_intern_atom_reply(window.conn, c_close, &error);
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_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");
pureFree(error);
cookie = xcb_change_property_checked(
window.conn,
@ -187,6 +194,7 @@ PlatformWindow CreateWindow(string name, u16 width, u16 height)
);
error = xcb_request_check(window.conn, cookie);
CheckErr(&window, &cookie, error, "xcb_change_property_checked failure");
pureFree(error);
window.close_event = r_close.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);
assert(stream_result > 0, "xcb_flush failure");
xcb_xfixes_query_version(window.conn, 4, 0);
return window;
};
void
LockCursor(PlatformWindow* window)
{
u32 counter = 0;
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_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)
{
break;
@ -215,8 +239,36 @@ PlatformWindow CreateWindow(string name, u16 width, u16 height)
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
FlushEvents(PlatformWindow* window)
@ -279,6 +331,15 @@ HandleEvents(PlatformWindow* window)
window.inputs[window.input_count].key = input;
window.inputs[window.input_count].pressed = pressed;
window.input_count += 1;
if (input == KBI.F2)
{
LockCursor(window);
}
else if (input == KBI.F3)
{
UnlockCursor(window);
}
}
} break;
case XCB_BUTTON_PRESS:
@ -333,7 +394,8 @@ HandleEvents(PlatformWindow* window)
window.mouse_prev_x = x;
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_y = cast(i16)(window.h / 2);

View File

@ -4,7 +4,7 @@ import assets;
import util;
import alloc;
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 std.math.traits : isNaN;
import util : Logf;
@ -522,3 +522,15 @@ Destroy(Renderer* rd, Pipeline* 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_DEVICE_EXTENSIONS = [
const char*[] VK_BASE_DEVICE_EXTENSIONS = [
cast(char*)VK_KHR_SWAPCHAIN_EXTENSION_NAME,
cast(char*)VK_KHR_UNIFORM_BUFFER_STANDARD_LAYOUT_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 = [
VK_FORMAT_R16G16B16A16_UNORM,
VK_FORMAT_R8G8B8A8_UNORM,
@ -1473,6 +1489,12 @@ SetUniform(Vulkan* vk, GlobalUniforms* globals)
vkUpdateDescriptorSets(vk.device, 1, &write, 0, null);
}
void
WaitIdle(Vulkan* vk)
{
vkDeviceWaitIdle(vk.device);
}
void
Destroy(Vulkan* vk, Shader shader)
{
@ -2930,13 +2952,16 @@ PrintShaderDisassembly(Vulkan* vk, Pipeline* pipeline, VkShaderStageFlagBits sta
{
version(AMD_GPU)
{
u64 size;
VkResult result = vkGetShaderInfoAMD(vk.device, pipeline.handle, stage, VK_SHADER_INFO_TYPE_DISASSEMBLY_AMD, &size, null);
if (result == VK_SUCCESS)
debug
{
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);
u64 size;
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);
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;
};
debug
version(AMD_GPU)
{
PFN_vkGetShaderInfoAMD vkGetShaderInfoAMD = null;
debug
{
PFN_vkGetShaderInfoAMD vkGetShaderInfoAMD = null;
}
}
@ -212,7 +216,11 @@ LoadDeviceFunctions(Vulkan* vk)
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");

View File

@ -29,7 +29,7 @@ void main()
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);
col = mix(col, tex_col, 0.5);
col = col * tex_col;
}
out_col = col;

View File

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