Skip to content

Commit a917006

Browse files
authored
Render Pipeline Article (#180)
* Add render pipeline article
1 parent 483a4d1 commit a917006

File tree

2 files changed

+170
-0
lines changed

2 files changed

+170
-0
lines changed

docs/modules/core/nav.adoc

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -59,6 +59,8 @@
5959
** xref:renderer/remote-controlling_the_camera.adoc[Remote-Controlling]
6060
** xref:renderer/multiple_camera_views.adoc[Multiple Camera Views]
6161
** xref:renderer/jme3_renderbuckets.adoc[Render Buckets]
62+
* Rendering
63+
** xref:renderer/render_pipeline.adoc[Render Pipelines]
6264
* User Interaction
6365
** xref:input/input_handling.adoc[Input Handling]
6466
** xref:input/combo_moves.adoc[Combo Moves]
Lines changed: 168 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,168 @@
1+
= Render Pipelines
2+
:revnumber: 1.0
3+
:revdate: 2025/04/22
4+
:keywords: rendering, viewport, pipeline
5+
6+
Since JMonkeyEngine 3.8, ViewPorts are rendered using render pipelines. Before, the RenderManager was entirely responsible for processing each ViewPort's scenes and rendering their geometries. Only basic forward rendering could really be used without great difficulty, so render pipelines were introduced to allow developers to implement whatever rendering techniques their game demands.
7+
8+
The sky is the limit with render pipelines, however, because they control every aspect of rendering a ViewPort (including SceneProcessors and profiling), a great deal goes into implementing a pipeline.
9+
10+
== PipelineContext
11+
12+
Before diving into building render pipelines, one must first have at least a rudimentary knowledge of PipelineContexts, which are responsible for handling global objects for pipelines. Pipelines themselves cannot manage these global objects because they are localized to specific ViewPorts. PipelineContexts are stored directly by the RenderManager, so they are better suited for this task.
13+
14+
[source,java]
15+
----
16+
public class MyPipelineContext implements PipelineContext {
17+
18+
// an example of a global object to manage
19+
private GlobalResources resources;
20+
21+
@Override
22+
public boolean startViewPortRender(RenderManager rm, ViewPort vp) {
23+
// Called when a ViewPort begins rendering that
24+
// this context is involved in.
25+
// Must return true if this context was already involved
26+
// in a ViewPort rendering this frame.
27+
}
28+
29+
@Override
30+
public void endViewPortRender(RenderManager rm, ViewPort vp) {
31+
// Called when a ViewPort ends rendering that
32+
// this context was involved in.
33+
}
34+
35+
@Override
36+
public void endContextRenderFrame(RenderManager rm) {
37+
// Called after all rendering this frame is complete AND this
38+
// context was involved in rendering a ViewPort this frame.
39+
}
40+
41+
// give pipelines access to the example global object
42+
public GlobalResources getResources() {
43+
return resources;
44+
}
45+
46+
}
47+
----
48+
49+
Note that PipelineContexts get run only if they become involved in rendering a ViewPort. They do so when a pipeline specifically selects them for rendering, which will be covered later.
50+
51+
In order to be selected at all, PipelineContexts must be registered with the RenderManager at the time. This can either be done manually, or the pipeline itself can create and register the context if it does not yet exist.
52+
53+
[source,java,opts=novalidate]
54+
----
55+
// register context manually
56+
renderManager.registerContext(MyPipelineContext.class, new MyPipelineContext());
57+
----
58+
59+
Contexts are registered by a class type by which they can then be retrieved.
60+
61+
== RenderPipeline
62+
63+
The RenderPipeline interface is the primary element of the pipeline system, as it is directly responsible for rendering a ViewPort. RenderPipeline provides five methods to implement:
64+
65+
[source,java]
66+
----
67+
public class MyPipeline implements RenderPipeline<MyPipelineContext> {
68+
69+
@Override
70+
public MyPipelineContext fetchPipelineContext(RenderManager rm) {
71+
// Returns a PipelineContext from the RenderManager
72+
// that handles global objects for this pipeline. The
73+
// returned context is passed to pipelineRender.
74+
}
75+
76+
@Override
77+
public boolean hasRenderedThisFrame() {
78+
// Returns true if this context has performed any
79+
// rendering previously on this frame.
80+
}
81+
82+
@Override
83+
public void startRenderFrame() {
84+
// Called before pipelineRender on the first rendering
85+
// this pipeline is to perform this frame.
86+
}
87+
88+
@Override
89+
public void pipelineRender(RenderManager rm, MyPipelineContext context, ViewPort vp, float tpf) {
90+
// Does the actual rendering of the ViewPort.
91+
}
92+
93+
@Override
94+
public void endRenderFrame(RenderManager rm) {
95+
// Called after all rendering is complete in a frame in
96+
// which this pipeline rendered a ViewPort.
97+
}
98+
99+
}
100+
----
101+
102+
The `pipelineRender` method can get quite complicated, as rendering is a complicated process. Fortunately, the pipeline system imposes little to no restriction on what pipelines actually do during rendering. Here is a quick `renderPipeline` implementation to get started with:
103+
104+
[source,java]
105+
----
106+
@Override
107+
public void pipelineRender(RenderManager rm, MyPipelineContext context, ViewPort vp, float tpf) {
108+
// apply viewport to rendering context
109+
rm.getRenderer().setFrameBuffer(vp.getOutputFrameBuffer());
110+
rm.getRenderer().clearBuffers(true, true, true);
111+
rm.getRenderer().setBackgroundColor(vp.getBackgroundColor());
112+
rm.setCamera(vp.getCamera(), false);
113+
// render each geometry in all viewport scenes
114+
for (Spatial scene : vp.getScenes()) {
115+
scene.depthFirstTraversal(s -> {
116+
if (s instanceof Geometry) {
117+
rm.renderGeometry((Geometry)s);
118+
}
119+
});
120+
}
121+
// reset clip rect
122+
rm.getRenderer().clearClipRect();
123+
}
124+
----
125+
126+
As previously mentioned, there is very little restriction over what `pipelineRender` does, so following the above example is not required. In fact, JMonkeyEngine's default renderer, https://github.com/jMonkeyEngine/jmonkeyengine/blob/master/jme3-core/src/main/java/com/jme3/renderer/pipeline/ForwardPipeline.java[ForwardPipeline], looks vastly different.
127+
128+
=== Fetching Contexts to Use
129+
130+
Since pipelines often depend on global objects (as stated before), the RenderPipeline interface has a generic specifying the type of context to be expected (set as MyPipelineContext in the example above), and the interface provides the `fetchPipelineContext` method to select the context to use during rendering. The context returned by `fetchPipelineContext` will then be passed to `pipelineRender` to actually be used.
131+
132+
For example, if the pipeline wanted to select MyPipelineContext that is already registered with the RenderManager:
133+
134+
[source,java]
135+
----
136+
@Override
137+
public MyPipelineContext fetchPipelineContext(RenderManager rm) {
138+
// assuming MyPipelineContext is registered under MyPipelineContext.class
139+
return rm.getContext(MyPipelineContext.class);
140+
}
141+
----
142+
143+
Even if a RenderPipeline does not need to use a PipelineContext, it is still required that `fetchPipelineContext` return a non-null context. For such cases, returning `rm.getDefaultContext()` is acceptable.
144+
145+
== Usage
146+
147+
In order to get a RenderPipeline to render a ViewPort, simply assign the pipeline to the ViewPort. When the rendering step occurs, the RenderManager uses each ViewPort's assigned pipeline to render the ViewPort.
148+
149+
[source,java,opts=novalidate]
150+
----
151+
viewPort.setPipeline(new MyRenderPipeline());
152+
----
153+
154+
Note that RenderPipelines (unless otherwise specified) can be assigned to multiple ViewPorts at once.
155+
156+
[source,java,opts=novalidate]
157+
----
158+
MyRenderPipeline p = new MyRenderPipeline();
159+
viewPort.setPipeline(p);
160+
guiViewPort.setPipeline(p);
161+
----
162+
163+
If no pipeline is assigned to a ViewPort, the RenderManager uses a default pipeline to render that ViewPort. The default pipeline can be set as so:
164+
165+
[source,java,opts=novalidate]
166+
----
167+
renderManager.setPipeline(new MyRenderPipeline());
168+
----

0 commit comments

Comments
 (0)