{
  "$type": "site.standard.document",
  "bskyPostRef": {
    "cid": "bafyreihe4hxke5mamz4afikmxaccjq4oq5sob7mm3ziogwpo5wct6xavmq",
    "uri": "at://did:plc:dxjzgxe7cvirxkwfjr2tjspt/app.bsky.feed.post/3mncoz7mcvjs2"
  },
  "path": "/t/jme-3-10-0-beta1/49603?page=2#post_21",
  "publishedAt": "2026-06-02T12:09:55.000Z",
  "site": "https://hub.jmonkeyengine.org",
  "tags": [
    "@Override"
  ],
  "textContent": "\n    package com.jme3.renderer.vulkan.context;\n\n    import com.jme3.renderer.vulkan.VKRenderer;\n    import com.jme3.renderer.vulkan.VulkanRuntime;\n    import com.jme3.system.AppSettings;\n    import com.jme3.system.lwjgl.LwjglWindow;\n    import org.lwjgl.system.MemoryStack;\n\n    import java.nio.IntBuffer;\n\n    import static org.lwjgl.sdl.SDLError.*;\n    import static org.lwjgl.sdl.SDLEvents.*;\n    import static org.lwjgl.sdl.SDLInit.*;\n    import static org.lwjgl.sdl.SDLStdinc.*;\n    import static org.lwjgl.sdl.SDLVideo.*;\n    import org.lwjgl.sdl.SDL_Event;\n\n    /**\n     * 彻底切断父类的 GLFW 调用,使用纯血 SDL3 驱动窗口生命周期和事件拉取。\n     */\n    public class LwjglVulkanContext extends LwjglWindow {\n\n        private VulkanRuntime runtime;\n        private VKRenderer renderer;\n        private long sdlWindow; // 【修复1】:单独保存 SDL 句柄,不依赖父类\n\n        public LwjglVulkanContext() {\n            super(Type.Display);\n        }\n\n        @Override\n        protected void createContext(AppSettings settings) {\n            if (SDL_setenv_unsafe(\"__GL_THREADED_OPTIMIZATIONS\", \"0\", 1) != 0) {\n                throw new IllegalStateException(\"Unable to disable NVIDIA threaded optimizations: \" + SDL_GetError());\n            }\n\n            if (!SDL_InitSubSystem(SDL_INIT_VIDEO | SDL_INIT_EVENTS)) {\n                throw new IllegalStateException(\"Unable to initialize SDL video subsystem: \" + SDL_GetError());\n            }\n\n            long windowFlags = SDL_WINDOW_VULKAN | SDL_WINDOW_HIDDEN;\n            if (settings.isResizable()) {\n                windowFlags |= SDL_WINDOW_RESIZABLE;\n            }\n\n            sdlWindow = SDL_CreateWindow(settings.getTitle(), settings.getWidth(), settings.getHeight(), windowFlags);\n            if (sdlWindow == 0L) {\n                throw new RuntimeException(\"Failed to create SDL Vulkan window: \" + SDL_GetError());\n            }\n\n            // 依然注入给父类,防止 JME3 内部某些日志或验证报 NullPointer,但后续逻辑全部由我们自己接管\n            try {\n                java.lang.reflect.Field fWin = LwjglWindow.class.getDeclaredField(\"window\");\n                fWin.setAccessible(true);\n                fWin.set(this, sdlWindow);\n            } catch (Exception ignored) {\n            }\n\n            runtime = new VulkanRuntime(settings);\n            renderer = new VKRenderer(runtime);\n\n            try {\n                runtime.init(sdlWindow);\n            } catch (Exception e) {\n                throw new RuntimeException(e);\n            }\n            renderer.initialize();\n\n            setWindowIcon(settings);\n            showWindow(); // 会安全调用重写后的 SDL_ShowWindow\n            updateVulkanSizes(); // 初始化真实的物理像素尺寸\n        }\n\n        // 【修复2】:重写 showWindow,强行接管调用 SDL API 抛弃 GLFW\n        @Override\n        protected void showWindow() {\n            if (sdlWindow != 0L) {\n                SDL_ShowWindow(sdlWindow);\n            }\n        }\n\n        protected void updateVulkanSizes() {\n            if (sdlWindow == 0L) {\n                return;\n            }\n\n            try (MemoryStack stack = MemoryStack.stackPush()) {\n                IntBuffer w = stack.mallocInt(1);\n                IntBuffer h = stack.mallocInt(1);\n\n                SDL_GetWindowSizeInPixels(sdlWindow, w, h);\n                int width = w.get(0);\n                int height = h.get(0);\n\n                if (width > 0 && height > 0) {\n                    // 如果尺寸发生了变化,或者这是第一次初始化\n                    if (settings.getWidth() != width || settings.getHeight() != height) {\n                        settings.setWidth(width);\n                        settings.setHeight(height);\n                        if (runtime != null && isCreated()) {\n                            runtime.requestResize(width, height);\n                        }\n                        // 【修复2】:标记摄像机分辨率需要更新\n                        needReshape = true;\n                    }\n                }\n            }\n        }\n\n        // 【修复4】:重写销毁方法,调用 SDL 销毁,不要调 super 避免导致 glfwDestroyWindow 崩溃\n        @Override\n        protected void destroyContext() {\n            if (runtime != null) {\n                runtime.cleanup();\n            }\n            if (sdlWindow != 0L) {\n                SDL_DestroyWindow(sdlWindow);\n                sdlWindow = 0L;\n            }\n            SDL_QuitSubSystem(SDL_INIT_VIDEO | SDL_INIT_EVENTS);\n        }\n\n        @Override\n        protected void runLoop() {\n            if (!created.get()) {\n                throw new IllegalStateException();\n            }\n\n            if (needReshape && listener != null) {\n                listener.reshape(settings.getWidth(), settings.getHeight());\n                needReshape = false;\n            }\n\n            if (listener != null) {\n                listener.update();\n            }\n\n            if (runtime != null) {\n                runtime.renderFrame(timer.getTimePerFrame(), renderer, null);\n            }\n\n            if (renderer != null) {\n                renderer.postFrame();\n            }\n\n            int targetFramerate = getSettings().getFrameRate();\n            if (targetFramerate <= 0 && !autoFlush) {\n                targetFramerate = 20;\n            }\n            if (targetFramerate > 0) {\n                com.jme3.system.lwjgl.Sync.sync(targetFramerate);\n            }\n\n            pollSdlEvents();\n        }\n\n        private void pollSdlEvents() {\n            try (MemoryStack stack = MemoryStack.stackPush()) {\n                SDL_Event event = SDL_Event.malloc(stack);\n\n\n                while (SDL_PollEvent(event)) {\n                    int type = event.type();\n                    if (type == SDL_EVENT_QUIT) {\n                        listener.requestClose(false);\n                    } else if (type == SDL_EVENT_WINDOW_PIXEL_SIZE_CHANGED || type == SDL_EVENT_WINDOW_RESIZED) {\n                        updateVulkanSizes();\n                    }\n                    // TODO: 可以在这里将 SDL 键盘/鼠标事件翻译并转交给 JME3 InputSystem\n                }\n            }\n        }\n\n\n        protected void initContext() {\n\n        }\n\n        // =========================================================\n        // 【修复2】:阻断 JME3 去 new 默认的 GLRenderer\n        // =========================================================\n        @Override\n        protected void initContextFirstTime() {\n\n        }\n\n        // =========================================================\n        // 【修复3】:确保 JME3 引擎核心获取到的是 Vulkan 渲染器\n        // =========================================================\n        @Override\n        public com.jme3.renderer.Renderer getRenderer() {\n\n            return renderer;\n        }\n\n        @Override\n        public com.jme3.input.MouseInput getMouseInput() {\n            // 返回虚拟鼠标输入,防止引擎内部的 NullPointerException\n            return new com.jme3.input.dummy.DummyMouseInput();\n        }\n\n        @Override\n        public com.jme3.input.KeyInput getKeyInput() {\n            // 返回虚拟键盘输入\n            return new com.jme3.input.dummy.DummyKeyInput();\n        }\n\n        @Override\n        public com.jme3.input.JoyInput getJoyInput() {\n            // 不需要手柄输入,直接返回 null,JME3 能安全处理\n            return null;\n        }\n\n        @Override\n        public com.jme3.input.TouchInput getTouchInput() {\n            // 不需要触摸输入,直接返回 null\n            return null;\n        }\n        // 标记是否需要更新 JME3 的摄像机分辨率\n        private boolean needReshape = true;\n\n        @Override\n        public boolean isRenderable() {\n            // 只要我们的 SDL 窗口句柄不为 0,就坚决要求 JME3 进行渲染!\n            return sdlWindow != 0L;\n        }\n    }\n\n\n\nThank you for your guidance. I have re-constructed the LwjglVulkanContext.\n\nThanks! That workaround works for now.\nIn the long run, it would be really helpful if we could have an official configuration option​ for plugging in custom renderers.\nThis would give us a clear and stable development convention, instead of having to replace core modules every time.\n\nThanks again for all you hard work on this!",
  "title": "jME 3.10.0-beta1"
}