核心模块

核心 (Core) 模块,是 OverrunGL 各个模块的基础。

我们为核心模块添加了以下基本 API:

  • MemoryStack
  • MemoryUtil
  • RuntimeHelper

除此之外,我们还添加了Configurations,供开发者设置内部参数。

MemoryStack

由于目前 FFM API 并未提供栈操作 API,因此我们提供了与 LWJGL 相似的MemoryStack

MemoryStack允许我们进行一次性、快速的小堆内存分配。例如,以下为 OpenGL 中常见的栈操作:

unsigned int texture;
glGenTextures(1, &texture);
// use the texture

显然在 Java 中我们没有指针,无法直接传入值类型修改内容。因此,我们需要借助MemoryStack完成以上操作:

int texture;
try (MemoryStack stack = MemoryStack.stackPush()) {
    MemorySegment segment = stack.malloc(ValueLayout.JAVA_INT);
    GL.genTextures(1, segment);
    texture = segment.get(ValueLayout.JAVA_INT, 0);
}
// use the texture

Hint

注意ValueLayout.JAVA_INT是 FFM API针对目前通用泛型尚未加入的情况下临时提出的解决方案,它的类型为ValueLayout.OfInt
未来,通用泛型加入时,上面的代码可能会变成下面这样:

int texture;
try (MemoryStack stack = MemoryStack.stackPush()) {
    MemorySegment segment = stack.<int>malloc();
    GL.genTextures(1, segment);
    texture = segment.<int>get(0);
}
// use the texture

MemoryStack还实现了SegmentAllocator,这意味着我们能够将MemoryStack直接传入参数。

import java.lang.foreign.MemorySegment;
// imports...

class Example {
    private static MemorySegment windowHandle;

    public static void main(String[] args) {
        // init...
        try (MemoryStack stack = MemoryStack.stackPush()) {
            // 请注意只有小堆内存才能使用 MemoryStack 分配,
            // 如果字符串的长度超过了栈的大小会导致 OutOfMemoryError。
            windowHandle = GLFW.createWindow(stack,
                800,
                600,
                "Example",
                MemorySegment.NULL,
                MemorySegment.NULL);
        }
        // ...terminate
    }
}

MemoryStack默认大小可通过设置Configurations.STACK_SIZE修改。STACK_SIZE单位为KiB。

调用stackPush时,每个线程会使用默认大小创建一个堆栈,或者复用之前的堆栈。

使用create(long)显式指定创建堆栈的大小。

通过设置Configurations.DEBUG_STACK,启用或禁用调试模式。此配置必须在首个MemoryStack创建前设置。

MemoryUtil

Note

尽管我们提供了MemoryUtil,但是除非非常需要性能,否则仍然推荐使用Arena

Important

使用MemoryUtil分配的内存必须显式释放,否则会造成内存泄露!
幸运的是,通过设置Configurations.DEBUG_MEM_UTIL,我们能够快速定位内存泄露位置。

MemoryUtil是 C 标准内存分配函数的绑定,它的使用方法与 C 一致。

// import static ...
MemorySegment segment = malloc(ValueLayout.JAVA_LONG);
// 正常情况下返回值不会为 NULL,此处仅作示例
if (segment.address() == RuntimeHelper.NULL) {
    throw new OutOfMemoryError();
}
segment.set(ValueLayout.JAVA_LONG, 0, 42L);
System.out.println(segment.get(ValueLayout.JAVA_LONG, 0));
// 重要!
free(segment);

RuntimeHelper

Warning

RuntimeHelper为底层内容,如果你不知道自己在做什么,请跳过这一节。

API Logger

OverrunGL 内部使用名为apiLoggerConsumer来打印内部消息。

要将apiLogger设置为你自己的 logger(例如 slf4j),使用以下代码:

RuntimeHelper.setApiLogger(logger::warn);

Downcall Handle

FFM API将本机函数转换为MethodHandle以便我们在 Java 中调用。

关于这部分的详细说明请见前言中链接的 JEP 页面。

toArray

RuntimeHelper提供了toArray方法及其重载。toArrayMemorySegment中的内容读取到数组中。