LukasBanana / LLGL

Low Level Graphics Library (LLGL) is a thin abstraction layer for the modern graphics APIs OpenGL, Direct3D, Vulkan, and Metal
BSD 3-Clause "New" or "Revised" License
2.03k stars 135 forks source link

No input events from GLFW with the Metal backend #116

Closed st0rmbtw closed 2 months ago

st0rmbtw commented 2 months ago

GLFW doesn't register input events when Metal is set as a renderer backend. Everything is fine with OpenGL though.

main.cpp

#include <stdio.h>
#include <LLGL/LLGL.h>
#include <memory>
#include "custom_surface.hpp"

#if defined(_WIN32)
#define BACKEND "DirectX11"
#elif defined(__MACH__)
#define BACKEND "Metal"
#else
#define BACKEND "OpenGL"
#endif

struct Game {
    float delta_time;
};

Game g = {};

void update() {
}

void render(LLGL::CommandBuffer* cmdBuffer, LLGL::SwapChain *swapChain) {
    cmdBuffer->Begin();
    cmdBuffer->BeginRenderPass(*swapChain);
    cmdBuffer->SetViewport(swapChain->GetResolution());
    cmdBuffer->Clear(LLGL::ClearFlags::Color, LLGL::ClearValue(7.0f, 0.3f, 0.3f, 1.0f));

    cmdBuffer->EndRenderPass();
    cmdBuffer->End();
    swapChain->Present();
}

static void handle_keyboard_events(GLFWwindow* window, int key, int scancode, int action, int mods) {
    printf("KEYBOARD\n");
}

void handle_mouse_button_events(GLFWwindow* window, int button, int action, int mods) {
    printf("MOUSE\n");
}

void handle_mouse_scroll_events(GLFWwindow* window, double xoffset, double yoffset) {
}

void handle_cursor_pos_events(GLFWwindow* window, double xpos, double ypos) {
    printf("CURSOR\n");
}

void handle_window_resize_events(GLFWwindow* window, int width, int height) {
    printf("WINDOW RESIZE\n");
}

inline const char* glfwGetError(void) {
    const char* description;
    glfwGetError(&description);
    return description;
}

int main(void) {
    LLGL::Log::RegisterCallbackStd();

    if (!glfwInit()) {
        printf("Couldn't initialize GLFW: %s\n", glfwGetError());
        return -1;
    }

    glfwWindowHint(GLFW_FOCUSED, 1);

    GLFWwindow *window = glfwCreateWindow(1280, 720, "AAA", nullptr, nullptr);
    if (window == nullptr) {
        printf("Couldn't create a window: %s\n", glfwGetError());
        return -1;
    }

    glfwMakeContextCurrent(window);

    glfwSetKeyCallback(window, handle_keyboard_events);
    glfwSetMouseButtonCallback(window, handle_mouse_button_events);
    glfwSetScrollCallback(window, handle_mouse_scroll_events);
    glfwSetCursorPosCallback(window, handle_cursor_pos_events);
    glfwSetWindowSizeCallback(window, handle_window_resize_events);

    auto surface = std::make_shared<CustomSurface>(window, LLGL::Extent2D(1280, 720));

    LLGL::Report report;
    LLGL::RenderSystemPtr renderer = LLGL::RenderSystem::Load("Metal", &report);
    if (!renderer) {
        LLGL::Log::Errorf("%s", report.GetText());
        return -1;
    }

    LLGL::SwapChainDescriptor swapChainDesc;
    swapChainDesc.resolution = {1280, 720};

    auto swapChain = renderer->CreateSwapChain(swapChainDesc, surface);
    auto cmdBuffer = renderer->CreateCommandBuffer(LLGL::CommandBufferFlags::ImmediateSubmit);

    while (surface->ProcessEvents()) {
        update();
        render(cmdBuffer, swapChain);
    }

    return 0;
}

custom_surface.hpp

#if defined(_WIN32)
    #define GLFW_EXPOSE_NATIVE_WIN32
#elif defined(__MACH__)
    #define GLFW_EXPOSE_NATIVE_COCOA
#else
    #if defined(WAYLAND)
        #define GLFW_EXPOSE_NATIVE_WAYLAND
    #elif defined(X11)
        #define GLFW_EXPOSE_NATIVE_X11
  #endif
#endif

#include <GLFW/glfw3.h>
#include <GLFW/glfw3native.h>
#include <LLGL/LLGL.h>
#include <LLGL/Platform/NativeHandle.h>

class CustomSurface : public LLGL::Surface {
public:
    CustomSurface(GLFWwindow *window, const LLGL::Extent2D& size);
    ~CustomSurface();

    bool GetNativeHandle(void* nativeHandle, std::size_t nativeHandleSize) override;
    LLGL::Extent2D GetContentSize() const override;
    bool AdaptForVideoMode(LLGL::Extent2D* resolution, bool* fullscreen) override;
    void ResetPixelFormat() override;
    LLGL::Display* FindResidentDisplay() const override;

    bool ProcessEvents();
private:
    LLGL::Extent2D m_size;
    GLFWwindow* m_wnd = nullptr;

};

custom_surface.cpp

#include "custom_surface.hpp"

CustomSurface::CustomSurface(GLFWwindow *window, const LLGL::Extent2D& size) :
    m_size(size),
    m_wnd(window) {}

CustomSurface::~CustomSurface() {
    glfwDestroyWindow(m_wnd);
}

void CustomSurface::ResetPixelFormat() {
    // glfwDestroyWindow(m_wnd);
    // m_wnd = CreateGLFWWindow();
    printf("RESET PIXEL FORMAT CALLED\n");
}

bool CustomSurface::GetNativeHandle(void* nativeHandle, std::size_t nativeHandleSize) {
    auto handle = reinterpret_cast<LLGL::NativeHandle*>(nativeHandle);
#if defined(_WIN32)
    handle->window = glfwGetWin32Window(m_wnd);
#elif defined(__MACH__)
    handle->responder = glfwGetCocoaWindow(m_wnd);
#else
    // TODO
    #if defined(WAYLAND)
    #elif defined(X11)
  #endif
#endif
    return true;
}

LLGL::Extent2D CustomSurface::GetContentSize() const {
    return m_size;
}

bool CustomSurface::AdaptForVideoMode(LLGL::Extent2D *resolution, bool *fullscreen) {
    m_size = *resolution;
    glfwSetWindowSize(m_wnd, m_size.width, m_size.height);
    return true;
}

LLGL::Display* CustomSurface::FindResidentDisplay() const {
    return LLGL::Display::GetPrimary();
}

bool CustomSurface::ProcessEvents() {
    glfwPollEvents();
    return !glfwWindowShouldClose(m_wnd);
}

macOS version: Ventura 13.6.7

LukasBanana commented 2 months ago

Thank you for reporting this issue with your code sample. I was able to reproduce the issue and submitted a fix. Your example should work with ec3e3d3.

LLGL previously replaced the NSWindow contentView with the MTKViewinstead of just adding it as a subview, like it does on iOS or when an NSView is provided instead of an NSWindow.