An attempt at Vulkan
jMonkeyEngine Hub
March 13, 2026
package com.jme3.renderer.vulkan;
import com.jme3.scene.VertexBuffer;
import java.util.EnumMap;
/**
* VkMeshGpu:一个 jME3 Mesh 在 GPU(Vulkan) 侧对应的资源集合。
*
* 作用:
* - 把 jME3 的各个 VertexBuffer(Position/Color/Normal/UV...)映射成 Vulkan 的 VkBuffer。
* - 可选地保存 index buffer(索引缓冲),用于 vkCmdDrawIndexed。
*
* 说明:
* - 本类仅是“句柄/元数据容器”,不负责创建/销毁,生命周期通常由 VulkanRuntime/VkResourceFactory 管理。
* - 当前最小实现一般只用 Position + Color(与你的 pipeline/shader 输入匹配),后续可扩展更多语义。
*/
public final class VkMeshGpu {
/**
* 顶点缓冲表:按 jME3 的 VertexBuffer.Type 索引到 Vulkan 的 VkBuffer。
*
* 例如:
* - Type.Position -> 一个包含 vec3(float) 的 Vulkan vertex buffer
* - Type.Color -> 一个包含 vec3(float) 的 Vulkan vertex buffer
*
* 使用时:
* - 你的 VulkanPipeline 的 vertex input 需要与这些 buffer 的 stride/format 对齐
* (比如 binding0=Position, binding1=Color)。
*/
public final EnumMap<VertexBuffer.Type, VkBuffer> vbos =
new EnumMap<>(VertexBuffer.Type.class);
/**
* 索引缓冲(可选)。
*
* - 如果为 null:表示 mesh 没有索引缓冲,使用 vkCmdDraw(vertexCount, ...)
* - 如果非 null:表示 mesh 有索引缓冲,使用 vkCmdDrawIndexed(indexCount, ...)
*/
public VkBuffer ibo;
/**
* index buffer 的索引数量(元素个数,不是字节数)。
* 仅在 ibo != null 时有效,用作 vkCmdDrawIndexed 的 indexCount 参数。
*/
public int indexCount;
/**
* 顶点数量(vertex count)。
* - 对于非索引绘制:用作 vkCmdDraw 的 vertexCount 参数
* - 对于索引绘制:仍可用于校验/调试,但绘制主要用 indexCount
*/
public int vertexCount;
/**
* Vulkan 的索引类型(vkCmdBindIndexBuffer 的 indexType 参数)。
*
* 常见取值:
* - VK_INDEX_TYPE_UINT16:对应 jME Format.UnsignedShort
* - VK_INDEX_TYPE_UINT32:对应 jME Format.UnsignedInt
* - VK_INDEX_TYPE_UINT8_EXT:对应 jME Format.UnsignedByte(需要 VK_EXT_index_type_uint8 扩展)
*
* 注意:
* - 如果 mesh 使用 UnsignedByte 但未启用扩展,这里可能无法正确绑定/绘制。
*/
public int vkIndexType; // VK_INDEX_TYPE_UINT16 / VK_INDEX_TYPE_UINT32 / VK_INDEX_TYPE_UINT8_EXT
}
package com.jme3.lwjgl.test;
import com.jme3.renderer.Renderer;
import com.jme3.renderer.vulkan.LwjglVulkanContext;
import com.jme3.renderer.vulkan.VKRenderer;
import com.jme3.scene.Mesh;
import com.jme3.scene.VertexBuffer;
import com.jme3.system.AppSettings;
import com.jme3.system.SystemListener;
import com.jme3.util.BufferUtils;
import java.nio.FloatBuffer;
public class VulkanMainMeshTest implements SystemListener {
private LwjglVulkanContext context;
private Mesh triMesh;
private FloatBuffer posBuf;
private FloatBuffer colBuf;
private long lastTimeNs;
private float angleRad = 0f;
// 初始三角形(未旋转)
// 正方形(2 triangles = 6 verts)
private final float[] basePos = new float[]{
// tri 1
-0.6f, -0.6f, 0f,
0.6f, -0.6f, 0f,
0.6f, 0.6f, 0f,
// tri 2
0.6f, 0.6f, 0f,
-0.6f, 0.6f, 0f,
-0.6f, -0.6f, 0f
};
public static void main(String[] args) {
VulkanMainMeshTest app = new VulkanMainMeshTest();
app.start();
}
public void start() {
AppSettings settings = new AppSettings(true);
settings.setTitle("Vulkan Rotating Triangle");
settings.setWidth(900);
settings.setHeight(700);
settings.setSamples(1);
context = new LwjglVulkanContext();
context.setSettings(settings);
context.setSystemListener(this);
context.create(false);
}
@Override
public void initialize() {
System.out.println("Vulkan System Initialized.");
triMesh = new Mesh();
triMesh.setMode(Mesh.Mode.Triangles);
// 位置/颜色 buffer(3 verts * 3 comps)
posBuf = BufferUtils.createFloatBuffer(18);
colBuf = BufferUtils.createFloatBuffer(new float[]{
// tri 1 colors
1f, 0f, 0f,
0f, 1f, 0f,
0f, 0f, 1f,
// tri 2 colors
0f, 0f, 1f,
1f, 1f, 0f,
1f, 0f, 0f
});
// 写入初始位置
posBuf.put(basePos).rewind();
colBuf.rewind();
triMesh.setBuffer(VertexBuffer.Type.Position, 3, VertexBuffer.Format.Float, posBuf);
triMesh.setBuffer(VertexBuffer.Type.Color, 3, VertexBuffer.Format.Float, colBuf);
triMesh.updateBound();
triMesh.updateCounts();
lastTimeNs = System.nanoTime();
}
@Override
public void update() {
// 计算 dt
long now = System.nanoTime();
float dt = (now - lastTimeNs) / 1_000_000_000f;
lastTimeNs = now;
// 累积角度(1 rad/s,可自行调)
angleRad += dt;
// CPU 侧旋转三角形(绕 Z)
float c = (float) Math.cos(angleRad);
float s = (float) Math.sin(angleRad);
posBuf.rewind();
for (int i = 0; i < 6; i++) {
float x = basePos[i * 3];
float y = basePos[i * 3 + 1];
float z = basePos[i * 3 + 2];
float rx = x * c - y * s;
float ry = x * s + y * c;
posBuf.put(rx).put(ry).put(z);
}
posBuf.rewind();
// 提交给 Vulkan renderer
Renderer r = context.getRenderer();
if (r instanceof VKRenderer) {
((VKRenderer) r).renderMesh(triMesh, 0, 1, null);
}
}
@Override
public void reshape(int width, int height) {
}
@Override
public void destroy() {
}
public void pause() {
}
public void resume() {
}
@Override
public void requestClose(boolean esc) {
}
@Override
public void gainFocus() {
}
@Override
public void loseFocus() {
}
@Override
public void handleError(String errorMsg, Throwable t) {
System.err.println(errorMsg);
if (t != null) {
t.printStackTrace();
}
}
}
Currently, I can use the mesh(class) to transfer vertices to Vulkan. However, I am unable to utilize functions like Geometry at the moment. I haven’t yet studied the material section. Once I am available next week, I will try to figure out a way to create a material backend so that the frontend of JME can directly use it (of course, this is an ideal situation and I’m not sure if it can be accomplished yet).
Discussion in the ATmosphere