{
"$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"
}