From 0c8169713db334132b00bbb2164e2bfd8f04ef47 Mon Sep 17 00:00:00 2001 From: matthew Date: Wed, 30 Jul 2025 07:31:37 +1000 Subject: [PATCH] cursor fixes, add locking/unlocking cursor --- assets/shaders/pbr.vert.spv | Bin 4644 -> 4588 bytes dub.json | 2 +- src/gears/game.d | 5 ++- src/gears/main.d | 17 ++------ src/gears/platform.d | 78 ++++++++++++++++++++++++++++++++---- src/gears/renderer.d | 14 ++++++- src/gears/vulkan.d | 39 ++++++++++++++---- src/gears/vulkan_funcs.d | 14 +++++-- src/shaders/pbr.vert.glsl | 2 +- src/shared/includes.c | 2 + 10 files changed, 137 insertions(+), 36 deletions(-) diff --git a/assets/shaders/pbr.vert.spv b/assets/shaders/pbr.vert.spv index b0cf5db39ba8f7236664cfa4290e1bf2b15dbaff..3330c72199e77ab1784b6ee38bc9c92163a3d123 100644 GIT binary patch delta 572 zcmYjOO-sW-6nyP&Q$=!65Rqd2h*yurs_3bpo{Q(QXtl*A(b%*a51u@UNXcI)cn}ZL zU*Zq&=*_=SaAq5^8z#Fm@8!MOm*M7*=c>F{6elNh;>!KNeOe4e9ue%&Inf$lXRR{V8A#{3<(_urp51aWY=UoBkYtxn1n+NsYPW z^h#}6W~G-tyI!Ra#)T={EOI*J;#XVPIXvF(k0+yV_f@StVjxAxK5z+et_Lu&OD+AJ dFI7XF$f(-?i}OB;(tZTAvKX&GK1Q1=@C&u^Mi&48 delta 642 zcmYjO%Syvg5S=!+X+^V8C?bXWh&$0;u`23DQFlUdU4m9yY!Z!)^%3a@bRi`x_ks&? zA^jGY{(-+x@SK~7op3t$%$Yln+aLRN*)p{V{t?8UKf)lTG?guW)n2g5*HCti58j!=sx_g1sJ#w9k zMjB(EmRI+1&uQzJl4$s?o{b(GmjT*A+L%0}xuF|paXv(84neaMr}No*5lHftUTtYi z=Qz|^gZ0VB^cnpI{|!0Us4%a3E66pG>jJRdE%0?5IIjULfCuTh1W%ou&r7|GU(U-K z%*8wE85m>%X%~Rk0p;! z!~?j(IHshaH6L+aT>~xw{xi(q!RVXte*7|Bs$Szn#weF3-$rvT-2s{aH7-s*Ld`1h E3&s>l_5c6? diff --git a/dub.json b/dub.json index bcecac5..2e2bc52 100644 --- a/dub.json +++ b/dub.json @@ -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": [], diff --git a/src/gears/game.d b/src/gears/game.d index 68e6c37..50e8994 100644 --- a/src/gears/game.d +++ b/src/gears/game.d @@ -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); diff --git a/src/gears/main.d b/src/gears/main.d index 85ac218..d434556 100644 --- a/src/gears/main.d +++ b/src/gears/main.d @@ -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); diff --git a/src/gears/platform.d b/src/gears/platform.d index 2be6df7..a466d5f 100644 --- a/src/gears/platform.d +++ b/src/gears/platform.d @@ -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); diff --git a/src/gears/renderer.d b/src/gears/renderer.d index 6243c16..a8a30a7 100644 --- a/src/gears/renderer.d +++ b/src/gears/renderer.d @@ -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); +} diff --git a/src/gears/vulkan.d b/src/gears/vulkan.d index 5ce5a6b..b3a1566 100644 --- a/src/gears/vulkan.d +++ b/src/gears/vulkan.d @@ -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); + } } } } diff --git a/src/gears/vulkan_funcs.d b/src/gears/vulkan_funcs.d index 00a3406..282195e 100644 --- a/src/gears/vulkan_funcs.d +++ b/src/gears/vulkan_funcs.d @@ -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"); diff --git a/src/shaders/pbr.vert.glsl b/src/shaders/pbr.vert.glsl index be0ea3d..83b793f 100644 --- a/src/shaders/pbr.vert.glsl +++ b/src/shaders/pbr.vert.glsl @@ -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; diff --git a/src/shared/includes.c b/src/shared/includes.c index ad0d5f7..297b8ef 100644 --- a/src/shared/includes.c +++ b/src/shared/includes.c @@ -1,9 +1,11 @@ #ifdef __linux__ # include +# include # include # include # include # include +# include #endif #if __linux__