Skip to content

Fix wgpu dpi scaling #42

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Open
wants to merge 1 commit into
base: main
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
8 changes: 6 additions & 2 deletions build.zig
Original file line number Diff line number Diff line change
Expand Up @@ -241,7 +241,11 @@ pub fn build(b: *std.Build) void {
"libs/imgui/backends/imgui_impl_glfw.cpp",
"libs/imgui/backends/imgui_impl_wgpu.cpp",
},
.flags = cflags,
.flags = &(cflags.* ++ .{
"-DGLFW_INCLUDE_NONE",
// TODO: This should be IMGUI_IMPL_WEBGPU_BACKEND_DAWN but we're using an old version of Dawn that looks more like wgpu_native
"-DIMGUI_IMPL_WEBGPU_BACKEND_WGPU",
}),
});
},
.glfw_opengl3 => {
Expand All @@ -265,7 +269,7 @@ pub fn build(b: *std.Build) void {
"libs/imgui/backends/imgui_impl_glfw.cpp",
"libs/imgui/backends/imgui_impl_dx12.cpp",
},
.flags = cflags,
.flags = &(cflags.* ++ .{"-DGLFW_INCLUDE_NONE"}),
});
imgui.linkSystemLibrary("d3dcompiler_47");
},
Expand Down
59 changes: 6 additions & 53 deletions libs/imgui/backends/imgui_impl_glfw.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -90,8 +90,7 @@

#include "imgui.h"
#ifndef IMGUI_DISABLE
// FIX(zig-gamedev):
// #include "imgui_impl_glfw.h"
#include "imgui_impl_glfw.h"

// Clang warnings with -Weverything
#if defined(__clang__)
Expand All @@ -101,8 +100,6 @@
#endif

// GLFW
// FIX(zig-gamedev):
#define GLFW_INCLUDE_NONE
#include <GLFW/glfw3.h>

#ifdef _WIN32
Expand Down Expand Up @@ -160,41 +157,6 @@
#define GLFW_HAS_GAMEPAD_API (GLFW_VERSION_COMBINED >= 3300) // 3.3+ glfwGetGamepadState() new api
#define GLFW_HAS_GETKEYNAME (GLFW_VERSION_COMBINED >= 3200) // 3.2+ glfwGetKeyName()
#define GLFW_HAS_GETERROR (GLFW_VERSION_COMBINED >= 3300) // 3.3+ glfwGetError()
#include <math.h>

// FIX(zig-gamedev):
extern "C" {

bool ImGui_ImplGlfw_InitForOpenGL(GLFWwindow* window, bool install_callbacks);
bool ImGui_ImplGlfw_InitForVulkan(GLFWwindow* window, bool install_callbacks);
bool ImGui_ImplGlfw_InitForOther(GLFWwindow* window, bool install_callbacks);
void ImGui_ImplGlfw_Shutdown();
void ImGui_ImplGlfw_NewFrame();

// GLFW callbacks install
// - When calling Init with 'install_callbacks=true': ImGui_ImplGlfw_InstallCallbacks() is called. GLFW callbacks will be installed for you. They will chain-call user's previously installed callbacks, if any.
// - When calling Init with 'install_callbacks=false': GLFW callbacks won't be installed. You will need to call individual function yourself from your own GLFW callbacks.
void ImGui_ImplGlfw_InstallCallbacks(GLFWwindow* window);
void ImGui_ImplGlfw_RestoreCallbacks(GLFWwindow* window);

// GFLW callbacks options:
// - Set 'chain_for_all_windows=true' to enable chaining callbacks for all windows (including secondary viewports created by backends or by user)
void ImGui_ImplGlfw_SetCallbacksChainForAllWindows(bool chain_for_all_windows);

// GLFW callbacks (individual callbacks to call yourself if you didn't install callbacks)
void ImGui_ImplGlfw_WindowFocusCallback(GLFWwindow* window, int focused); // Since 1.84
void ImGui_ImplGlfw_CursorEnterCallback(GLFWwindow* window, int entered); // Since 1.84
void ImGui_ImplGlfw_CursorPosCallback(GLFWwindow* window, double x, double y); // Since 1.87
void ImGui_ImplGlfw_MouseButtonCallback(GLFWwindow* window, int button, int action, int mods);
void ImGui_ImplGlfw_ScrollCallback(GLFWwindow* window, double xoffset, double yoffset);
void ImGui_ImplGlfw_KeyCallback(GLFWwindow* window, int key, int scancode, int action, int mods);
void ImGui_ImplGlfw_CharCallback(GLFWwindow* window, unsigned int c);
void ImGui_ImplGlfw_MonitorCallback(GLFWmonitor* monitor, int event);

// GLFW helpers
void ImGui_ImplGlfw_Sleep(int milliseconds);

} // extern "C"

// GLFW data
enum GlfwClientApi
Expand All @@ -218,8 +180,6 @@ struct ImGui_ImplGlfw_Data
bool InstalledCallbacks;
bool CallbacksChainForAllWindows;
bool WantUpdateMonitors;

ImVec2 DpiScale; // fix(zig-gamedev)
#ifdef EMSCRIPTEN_USE_EMBEDDED_GLFW3
const char* CanvasSelector;
#endif
Expand Down Expand Up @@ -510,7 +470,7 @@ void ImGui_ImplGlfw_CursorPosCallback(GLFWwindow* window, double x, double y)
{
ImGui_ImplGlfw_Data* bd = ImGui_ImplGlfw_GetBackendData();
if (bd->PrevUserCallbackCursorPos != nullptr && ImGui_ImplGlfw_ShouldChainCallback(window))
bd->PrevUserCallbackCursorPos(window, x * bd->DpiScale.x, y * bd->DpiScale.y); // fix(zig-gamedev)
bd->PrevUserCallbackCursorPos(window, x, y);

ImGuiIO& io = ImGui::GetIO();
if (io.ConfigFlags & ImGuiConfigFlags_ViewportsEnable)
Expand All @@ -520,8 +480,8 @@ void ImGui_ImplGlfw_CursorPosCallback(GLFWwindow* window, double x, double y)
x += window_x;
y += window_y;
}
io.AddMousePosEvent((float)x * bd->DpiScale.x, (float)y * bd->DpiScale.y); // fix(zig-gamedev)
bd->LastValidMousePos = ImVec2((float)x * bd->DpiScale.x, (float)y * bd->DpiScale.y); // fix(zig-gamedev)
io.AddMousePosEvent((float)x, (float)y);
bd->LastValidMousePos = ImVec2((float)x, (float)y);
}

// Workaround: X11 seems to send spurious Leave/Enter events which would make us lose our position,
Expand Down Expand Up @@ -668,8 +628,6 @@ static bool ImGui_ImplGlfw_Init(GLFWwindow* window, bool install_callbacks, Glfw
bd->Time = 0.0;
bd->WantUpdateMonitors = true;

bd->DpiScale = ImVec2{ 1.0f, 1.0f }; // fix(zig-gamedev)

ImGuiPlatformIO& platform_io = ImGui::GetPlatformIO();
platform_io.Platform_SetClipboardTextFn = [](ImGuiContext*, const char* text) { glfwSetClipboardString(nullptr, text); };
platform_io.Platform_GetClipboardTextFn = [](ImGuiContext*) { return glfwGetClipboardString(nullptr); };
Expand Down Expand Up @@ -837,8 +795,8 @@ static void ImGui_ImplGlfw_UpdateMouseData()
mouse_x += window_x;
mouse_y += window_y;
}
bd->LastValidMousePos = ImVec2((float)mouse_x * bd->DpiScale.x, (float)mouse_y * bd->DpiScale.y); // fix(zig-gamedev)
io.AddMousePosEvent((float)mouse_x * bd->DpiScale.x, (float)mouse_y * bd->DpiScale.y); // fix(zig-gamedev)
bd->LastValidMousePos = ImVec2((float)mouse_x, (float)mouse_y);
io.AddMousePosEvent((float)mouse_x, (float)mouse_y);
}
}

Expand Down Expand Up @@ -1007,11 +965,6 @@ void ImGui_ImplGlfw_NewFrame()
io.DisplaySize = ImVec2((float)w, (float)h);
if (w > 0 && h > 0)
io.DisplayFramebufferScale = ImVec2((float)display_w / (float)w, (float)display_h / (float)h);

// fix(zig-gamedev)
bd->DpiScale.x = ceil(io.DisplayFramebufferScale.x);
bd->DpiScale.y = ceil(io.DisplayFramebufferScale.y);

if (bd->WantUpdateMonitors)
ImGui_ImplGlfw_UpdateMonitors();

Expand Down
16 changes: 10 additions & 6 deletions libs/imgui/backends/imgui_impl_glfw.h
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,9 @@
// [X] Platform: Mouse cursor shape and visibility (ImGuiBackendFlags_HasMouseCursors). Resizing cursors requires GLFW 3.4+! Disable with 'io.ConfigFlags |= ImGuiConfigFlags_NoMouseCursorChange'.
// [X] Platform: Multi-viewport support (multiple windows). Enable with 'io.ConfigFlags |= ImGuiConfigFlags_ViewportsEnable'.
// Missing features or Issues:
// [ ] Platform: Multi-viewport: ParentViewportID not honored, and so io.ConfigViewportsNoDefaultParent has no effect (minor).
// [ ] Touch events are only correctly identified as Touch on Windows. This create issues with some interactions. GLFW doesn't provide a way to identify touch inputs from mouse inputs, we cannot call io.AddMouseSourceEvent() to identify the source. We provide a Windows-specific workaround.
// [ ] Missing ImGuiMouseCursor_Wait and ImGuiMouseCursor_Progress cursors.
// [ ] Multi-viewport: ParentViewportID not honored, and so io.ConfigViewportsNoDefaultParent has no effect (minor).

// You can use unmodified imgui_impl_* files in your project. See examples/ folder for examples of using this.
// Prefer including the entire imgui/ repository into your project (either as a copy or as a submodule), and only build the backends you need.
Expand All @@ -29,11 +31,13 @@ struct GLFWwindow;
struct GLFWmonitor;

// Follow "Getting Started" link and check examples/ folder to learn about using backends!
IMGUI_IMPL_API bool ImGui_ImplGlfw_InitForOpenGL(GLFWwindow* window, bool install_callbacks);
IMGUI_IMPL_API bool ImGui_ImplGlfw_InitForVulkan(GLFWwindow* window, bool install_callbacks);
IMGUI_IMPL_API bool ImGui_ImplGlfw_InitForOther(GLFWwindow* window, bool install_callbacks);
IMGUI_IMPL_API void ImGui_ImplGlfw_Shutdown();
IMGUI_IMPL_API void ImGui_ImplGlfw_NewFrame();
extern "C" { // fix(zig-gamedev)
IMGUI_IMPL_API bool ImGui_ImplGlfw_InitForOpenGL(GLFWwindow* window, bool install_callbacks);
IMGUI_IMPL_API bool ImGui_ImplGlfw_InitForVulkan(GLFWwindow* window, bool install_callbacks);
IMGUI_IMPL_API bool ImGui_ImplGlfw_InitForOther(GLFWwindow* window, bool install_callbacks);
IMGUI_IMPL_API void ImGui_ImplGlfw_Shutdown();
IMGUI_IMPL_API void ImGui_ImplGlfw_NewFrame();
};

// Emscripten related initialization phase methods (call after ImGui_ImplGlfw_InitForOpenGL)
#ifdef __EMSCRIPTEN__
Expand Down
50 changes: 3 additions & 47 deletions libs/imgui/backends/imgui_impl_wgpu.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -45,9 +45,6 @@
// When targeting native platforms (i.e. NOT emscripten), one of IMGUI_IMPL_WEBGPU_BACKEND_DAWN
// or IMGUI_IMPL_WEBGPU_BACKEND_WGPU must be provided. See imgui_impl_wgpu.h for more details.

// FIX(zig-gamedev)
#define IMGUI_IMPL_WEBGPU_BACKEND_WGPU

#ifndef __EMSCRIPTEN__
#if defined(IMGUI_IMPL_WEBGPU_BACKEND_DAWN) == defined(IMGUI_IMPL_WEBGPU_BACKEND_WGPU)
#error exactly one of IMGUI_IMPL_WEBGPU_BACKEND_DAWN or IMGUI_IMPL_WEBGPU_BACKEND_WGPU must be defined!
Expand All @@ -61,8 +58,7 @@
#include "imgui.h"
#ifndef IMGUI_DISABLE

// FIX(zig-gamedev):
// #include "imgui_impl_wgpu.h"
#include "imgui_impl_wgpu.h"

#include <limits.h>
#include <webgpu/webgpu.h>
Expand All @@ -71,47 +67,6 @@
extern ImGuiID ImHashData(const void* data_p, size_t data_size, ImU32 seed = 0);
#define MEMALIGN(_SIZE,_ALIGN) (((_SIZE) + ((_ALIGN) - 1)) & ~((_ALIGN) - 1)) // Memory align (copied from IM_ALIGN() macro).

// FIX(zig-gamedev): We removed header file and declare all our external functions here.
extern "C" {

// Initialization data, for ImGui_ImplWGPU_Init()
struct ImGui_ImplWGPU_InitInfo
{
WGPUDevice Device;
int NumFramesInFlight = 3;
WGPUTextureFormat RenderTargetFormat = WGPUTextureFormat_Undefined;
WGPUTextureFormat DepthStencilFormat = WGPUTextureFormat_Undefined;
WGPUMultisampleState PipelineMultisampleState = {};

ImGui_ImplWGPU_InitInfo()
{
PipelineMultisampleState.count = 1;
PipelineMultisampleState.mask = UINT32_MAX;
PipelineMultisampleState.alphaToCoverageEnabled = false;
}
};

// Follow "Getting Started" link and check examples/ folder to learn about using backends!
IMGUI_IMPL_API bool ImGui_ImplWGPU_Init(ImGui_ImplWGPU_InitInfo* init_info);
IMGUI_IMPL_API void ImGui_ImplWGPU_Shutdown();
IMGUI_IMPL_API void ImGui_ImplWGPU_NewFrame();
IMGUI_IMPL_API void ImGui_ImplWGPU_RenderDrawData(ImDrawData* draw_data, WGPURenderPassEncoder pass_encoder);

// Use if you want to reset your rendering device without losing Dear ImGui state.
IMGUI_IMPL_API bool ImGui_ImplWGPU_CreateDeviceObjects();
IMGUI_IMPL_API void ImGui_ImplWGPU_InvalidateDeviceObjects();

// [BETA] Selected render state data shared with callbacks.
// This is temporarily stored in GetPlatformIO().Renderer_RenderState during the ImGui_ImplWGPU_RenderDrawData() call.
// (Please open an issue if you feel you need access to more data)
struct ImGui_ImplWGPU_RenderState
{
WGPUDevice Device;
WGPURenderPassEncoder RenderPassEncoder;
};

} // extern "C"

// WebGPU data
struct RenderResources
{
Expand Down Expand Up @@ -399,7 +354,8 @@ static void ImGui_ImplWGPU_SetupRenderState(ImDrawData* draw_data, WGPURenderPas
}

// Setup viewport
wgpuRenderPassEncoderSetViewport(ctx, 0, 0, draw_data->FramebufferScale.x * draw_data->DisplaySize.x, draw_data->FramebufferScale.y * draw_data->DisplaySize.y, 0, 1);
// FIX(zig-gamedev): Clamp bounds to workaround WGPU on Vulkan validation error
wgpuRenderPassEncoderSetViewport(ctx, 0, 0, (float)(int)(draw_data->FramebufferScale.x * draw_data->DisplaySize.x), (float)(int)(draw_data->FramebufferScale.y * draw_data->DisplaySize.y), 0, 1);

// Bind shader and vertex buffers
wgpuRenderPassEncoderSetVertexBuffer(ctx, 0, fr->VertexBuffer, 0, fr->VertexBufferSize * sizeof(ImDrawVert));
Expand Down
9 changes: 6 additions & 3 deletions libs/imgui/backends/imgui_impl_wgpu.h
Original file line number Diff line number Diff line change
Expand Up @@ -8,9 +8,6 @@
// This requirement will be removed once WebGPU stabilizes and backends converge on a unified interface.
//#define IMGUI_IMPL_WEBGPU_BACKEND_DAWN

// FIX(zig-gamedev)
#define IMGUI_IMPL_WEBGPU_BACKEND_WGPU

// Implemented features:
// [X] Renderer: User texture binding. Use 'WGPUTextureView' as ImTextureID. Read the FAQ about ImTextureID!
// [X] Renderer: Large meshes support (64k+ vertices) even with 16-bit indices (ImGuiBackendFlags_RendererHasVtxOffset).
Expand All @@ -32,6 +29,9 @@

#include <webgpu/webgpu.h>

// FIX(zig-gamedev)
extern "C" {

// Initialization data, for ImGui_ImplWGPU_Init()
struct ImGui_ImplWGPU_InitInfo
{
Expand Down Expand Up @@ -68,4 +68,7 @@ struct ImGui_ImplWGPU_RenderState
WGPURenderPassEncoder RenderPassEncoder;
};

// FIX(zig-gamedev)
} // extern "C"

#endif // #ifndef IMGUI_DISABLE
29 changes: 18 additions & 11 deletions src/backend_glfw_wgpu.zig
Original file line number Diff line number Diff line change
Expand Up @@ -10,8 +10,6 @@ pub fn init(
wgpu_swap_chain_format: u32, // wgpu.TextureFormat
wgpu_depth_format: u32, // wgpu.TextureFormat
) void {
backend_glfw.init(window);

var info = ImGui_ImplWGPU_InitInfo{
.device = wgpu_device,
.num_frames_in_flight = 1,
Expand All @@ -23,20 +21,27 @@ pub fn init(
if (!ImGui_ImplWGPU_Init(&info)) {
unreachable;
}

backend_glfw.init(window);
}

pub fn deinit() void {
ImGui_ImplWGPU_Shutdown();
backend_glfw.deinit();
}

pub fn newFrame(fb_width: u32, fb_height: u32) void {
var _width: u32 = 0;
var _height: u32 = 0;
pub fn newFrame(width: u32, height: u32) void {
if (width != _width or height != _height) {
ImGui_ImplWGPU_InvalidateDeviceObjects();
if (ImGui_ImplWGPU_CreateDeviceObjects()) {
_width = width;
_height = height;
}
}
ImGui_ImplWGPU_NewFrame();
backend_glfw.newFrame();

gui.io.setDisplaySize(@floatFromInt(fb_width), @floatFromInt(fb_height));
gui.io.setDisplayFramebufferScale(1.0, 1.0);

gui.newFrame();
}

Expand All @@ -61,7 +66,9 @@ pub const ImGui_ImplWGPU_InitInfo = extern struct {

// Those functions are defined in 'imgui_impl_wgpu.cpp`
// (they include few custom changes).
extern fn ImGui_ImplWGPU_Init(init_info: *ImGui_ImplWGPU_InitInfo) bool;
extern fn ImGui_ImplWGPU_NewFrame() void;
extern fn ImGui_ImplWGPU_RenderDrawData(draw_data: *const anyopaque, pass_encoder: *const anyopaque) void;
extern fn ImGui_ImplWGPU_Shutdown() void;
extern fn ImGui_ImplWGPU_Init(init_info: *ImGui_ImplWGPU_InitInfo) callconv(.c) bool;
extern fn ImGui_ImplWGPU_InvalidateDeviceObjects() callconv(.c) void;
extern fn ImGui_ImplWGPU_CreateDeviceObjects() callconv(.c) bool;
extern fn ImGui_ImplWGPU_NewFrame() callconv(.c) void;
extern fn ImGui_ImplWGPU_RenderDrawData(draw_data: *const anyopaque, pass_encoder: *const anyopaque) callconv(.c) void;
extern fn ImGui_ImplWGPU_Shutdown() callconv(.c) void;
Loading