This repository contains a macOS experiment that renders a Vulkan triangle from Java by using the Foreign Function & Memory API for both the Vulkan SDK and GLFW native library.
The current result is:
- A GLFW window created from Java through FFM
- A Vulkan render path driven from Java through FFM
- A colored triangle rendered on the left
- A text overlay rendered on the right:
我是 GPT-5 Codex 模型,我完成了 Vulkan + GLFW + Java FFM 画三角形,哈哈哈。
- Vulkan native interop: Java FFM +
jextract-generated Vulkan bindings - GLFW native interop: Java FFM + handwritten GLFW bindings
- Text rendering: Java2D rasterization into an RGBA bitmap, then uploaded as a Vulkan texture
- Platform target: macOS with MoltenVK through the LunarG Vulkan SDK
This is not a pure Cocoa/AppKit windowing implementation. The window layer is handled by GLFW, but GLFW itself is still called from Java through FFM.
This project was validated in the following environment:
- Apple Silicon Mac
- macOS 26.1
- Xcode 16.4
- JDK 25.0.2
jextract25- Vulkan SDK 1.4.341.1 for macOS
- GLFW 3.4 built locally as a dynamic library
The current scripts and NativePaths class assume that local dependencies are staged under .deps with these paths:
.deps/tools/jdk/jdk-25.0.2.jdk/Contents/Home.deps/tools/jextract/jextract-25.deps/tools/vulkan-sdk/macOS.deps/build/glfw/src/libglfw.dylib
The repository does not commit SDKs or local toolchains. If your tools live elsewhere, either mirror this layout or update:
scripts/build.shscripts/generate-bindings.shscripts/build-shaders.shscripts/run.shsrc/main/java/dev/ffmvulkan/NativePaths.java
Build:
./scripts/build.shRun:
./scripts/run.shImportant notes:
- The authoritative build path for this repository is
scripts/build.shandscripts/run.sh, not Maven. - The app must start with
-XstartOnFirstThreadon macOS.scripts/run.shalready does this. scripts/build.shregenerates Vulkan FFM bindings and shader binaries before compiling Java sources.- The first build emits many
jextractwarnings about unsupported bitfields or opaque types. That is expected here and does not prevent the demo from running.
src/main/java/dev/ffmvulkan/VulkanTriangleApp.javaMain application: window setup, Vulkan initialization, swapchain, pipelines, command buffers, draw loop.src/main/java/dev/ffmvulkan/Glfw.javaHandwritten Java FFM wrapper for the subset of GLFW used by the demo.src/main/java/dev/ffmvulkan/TextBitmap.javaOffscreen text rasterization used to build the overlay texture.src/main/java/dev/ffmvulkan/NativePaths.javaCentralized local path assumptions for the JDK, Vulkan SDK,glslc, and GLFW dylib.scripts/generate-bindings.shRegenerates Vulkan bindings withjextract.scripts/build-shaders.shCompiles GLSL shaders into SPIR-V.scripts/build.shOrchestrates binding generation, shader compilation, and Java compilation.scripts/run.shExports the Vulkan/MoltenVK environment and launches the demo.ai-dialogue-log.mdExperiment dialogue log for prompt/response traceability.
The .gitignore is tuned for this experiment:
- Tracked: Java source, GLSL source, shell scripts, docs, screenshot proof, dialogue log
- Ignored:
local SDKs in
.deps, generated Vulkan bindings, generated SPIR-V, build outputs, temporary artifacts, editor junk
This keeps the repository reviewable while still allowing the project to be rebuilt from source with the expected local toolchain.
- Vulkan bindings are regenerated on each build instead of being committed.
- The triangle rendering bug was fixed by writing mapped float data with
ByteOrder.nativeOrder(). - Text orientation was corrected by flipping the
vtexture coordinate in the text vertex shader. - Back-face culling is disabled for the current pipelines to keep the text quad robust across winding differences.
- Proof screenshot:
docs/vulkan-glfw-java-ffm-proof.png - Dialogue log:
ai-dialogue-log.md
