Skip to content

How the boundary between rendering modules and the graphics API works

Module dependencies

flowchart TB
    Host[modelling: Minecraft host adapter] --> GL[render-opengl: OpenGL / LWJGL]
    Host --> Core[render-core: UML and rendering contracts]
    GL --> Core
    Future[Future Vulkan / Metal backend] -. implements the same contracts .-> Core

render-core is an ordinary Java library: it does not apply the ModDev plugin and does not depend on Minecraft, NeoForge, LWJGL, or a concrete graphics API. checkGraphicsApiIsolation runs before compileJava and inspects source imports plus compile and runtime dependencies, so those dependencies cannot re-enter the core. DataFixerUpper is used for the existing data codecs; it is not Blaze3D or the Minecraft renderer. Dependencies on formula and utils have transitive resolution disabled, so only the existing Java expressions and data structures are reused.

render-opengl does not apply ModDev either. It depends on the core and on LWJGL, and manages GL buffers, textures, shaders, VAO, fences, FBOs, readback, and the preview window. The host that runs it must supply the current GL context, the correct thread, and the LWJGL natives for the platform.

modelling keeps the Minecraft world render events, RenderType, VertexFormat, texture atlas, lighting, Iris/Sodium compatibility, and multi-camera world rendering. These classes are host adapters for the current Minecraft + OpenGL combination and cannot serve as a generic interface for another graphics API.

UML and resource lifecycle

sequenceDiagram
    participant H as Host
    participant P as ModelPipeLine / ModelInstance
    participant B as RenderBackend
    participant C as RenderContext
    participant D as Concrete graphics API
    H->>P: load model, create instance, bind PoseDriver
    H->>B: add(key, adapter, instance)
    B->>C: create context and apply()
    C->>D: create backend resources
    loop every view, every render stage
        H->>B: renderAllObjects(hostContext)
        B->>P: schedule, sample pose per frame
        B->>C: beforeRender(hostContext)
        B->>D: upload data, submit draws
    end
    H->>B: remove(key) / close()
    B->>C: close()
    C->>D: release or defer reclamation of resources

The core contains model loading, model structure, skeleton, morph, animation, FSM, pose sampling, and scheduling, plus the RenderBackend, RenderContext, ModelGeometryAdapter, RenderableFactory, RenderBuffer, and TypedBuffer contracts and the output routing. Its resource type, host frame context, and transform metadata are all generic and do not require GL objects or Minecraft classes.

Backend / BackendContext provide the default mount and release behavior, and a concrete backend implements context creation, data packing, and drawing. A failed mount does not publish a half-finished context; removing an instance releases only that mount's backend resources, while the model instance itself is managed by the model pipeline.

The core still contains the existing Java/JNI physics adaptation for Box3D. Physics is not part of a graphics API; when used standalone, a host that enables Box3D has to provide the native library for its platform as well.

Upload and output

UploadRing handles only slot rotation, dirty-element merging, version tracking, and resource lifecycle, and performs allocation, writes, and completion status queries through UploadDevice. GpuUploadRing in the production path is now its OpenGL adapter and keeps the original constructors, statistics types, and FloatBuffer entry points. The macOS TBO orphan policy is still provided by MacTboUploadDevice in the OpenGL module.

The integer buffer token and long completion token of UploadDevice are opaque handles internal to a backend, not native resource numbers shared across APIs. A new backend can map them onto its own resource table. A device must guarantee that reads already submitted stay valid after the storage is replaced, and a completion query must not block. A device that cannot meet this contract should use its own upload implementation and reuse the higher-level UploadBuffer / RenderBuffer.

FrameOutputRouter<T>, FrameOutputTarget<T>, OutputFrame<T>, and camera state belong to the core. T is defined by the backend. FrameTexture is the OpenGL framebuffer/texture carrier and belongs to the OpenGL module; other APIs should provide their own output carrier and target.

Adaptation boundary

A new backend implements its own resource representation, shaders, synchronization, and submission; windows, host events, and interop stay in the host adaptation layer. For the extension steps see the API reference.

The existing public class names are kept for call-site and serialized-name compatibility. Some GL classes therefore still use the historical uml.backend.gpu package name, and the pure-math Direction keeps its historical models.mc.util package name. Whether something belongs to the core follows the Gradle module dependencies; the core cannot import the OpenGL module.

This split did not implement a Vulkan/Metal backend, and it does not let Minecraft's Blaze3D switch APIs at runtime. The Minecraft effect pipeline descriptor still uses its own draw state for now; replacing the whole renderer of that host would also require the corresponding host adaptation and shader generation path.