-
Notifications
You must be signed in to change notification settings - Fork 0
/
Copy path_frame.html
328 lines (326 loc) · 23.3 KB
/
_frame.html
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
<html xmlns="http://www.w3.org/1999/xhtml">
<head>
<meta http-equiv="Content-Type" content="text/xhtml;charset=UTF-8"/>
<meta http-equiv="X-UA-Compatible" content="IE=9"/>
<meta name="generator" content="Doxygen 1.13.2"/>
<meta name="viewport" content="width=device-width, initial-scale=1"/>
<title>Nebula: Frame</title>
<link href="tabs.css" rel="stylesheet" type="text/css"/>
<script type="text/javascript" src="jquery.js"></script>
<script type="text/javascript" src="dynsections.js"></script>
<link href="navtree.css" rel="stylesheet" type="text/css"/>
<script type="text/javascript" src="navtreedata.js"></script>
<script type="text/javascript" src="navtree.js"></script>
<script type="text/javascript" src="resize.js"></script>
<script type="text/javascript" src="cookie.js"></script>
<link href="search/search.css" rel="stylesheet" type="text/css"/>
<script type="text/javascript" src="search/searchdata.js"></script>
<script type="text/javascript" src="search/search.js"></script>
<link href="doxygen.css" rel="stylesheet" type="text/css" />
<link href="custom.css" rel="stylesheet" type="text/css"/>
<link href="custom_dark_theme.css" rel="stylesheet" type="text/css"/>
</head>
<body>
<div id="top"><!-- do not remove this div, it is closed by doxygen! -->
<div id="titlearea">
<table cellspacing="0" cellpadding="0">
<tbody>
<tr style="height: 56px;">
<td id="projectalign" style="padding-left: 0.5em;">
<div id="projectname">Nebula
</div>
</td>
</tr>
</tbody>
</table>
</div>
<!-- end header part -->
<!-- Generated by Doxygen 1.13.2 -->
<script type="text/javascript">
/* @license magnet:?xt=urn:btih:d3d9a9a6595521f9666a5e94cc830dab83b65699&dn=expat.txt MIT */
var searchBox = new SearchBox("searchBox", "search/",'.html');
/* @license-end */
</script>
<script type="text/javascript">
/* @license magnet:?xt=urn:btih:d3d9a9a6595521f9666a5e94cc830dab83b65699&dn=expat.txt MIT */
$(function() { codefold.init(0); });
/* @license-end */
</script>
<script type="text/javascript" src="menudata.js"></script>
<script type="text/javascript" src="menu.js"></script>
<script type="text/javascript">
/* @license magnet:?xt=urn:btih:d3d9a9a6595521f9666a5e94cc830dab83b65699&dn=expat.txt MIT */
$(function() {
initMenu('',true,false,'search.php','Search',true);
$(function() { init_search(); });
});
/* @license-end */
</script>
<div id="main-nav"></div>
</div><!-- top -->
<div id="side-nav" class="ui-resizable side-nav-resizable">
<div id="nav-tree">
<div id="nav-tree-contents">
<div id="nav-sync" class="sync"></div>
</div>
</div>
<div id="splitbar" style="-moz-user-select:none;"
class="ui-resizable-handle">
</div>
</div>
<script type="text/javascript">
/* @license magnet:?xt=urn:btih:d3d9a9a6595521f9666a5e94cc830dab83b65699&dn=expat.txt MIT */
$(function(){initNavTree('_frame.html',''); initResizable(true); });
/* @license-end */
</script>
<div id="doc-content">
<!-- window showing the filter options -->
<div id="MSearchSelectWindow"
onmouseover="return searchBox.OnSearchSelectShow()"
onmouseout="return searchBox.OnSearchSelectHide()"
onkeydown="return searchBox.OnSearchSelectKey(event)">
</div>
<!-- iframe showing the search results (closed by default) -->
<div id="MSearchResultsWindow">
<div id="MSearchResults">
<div class="SRPage">
<div id="SRIndex">
<div id="SRResults"></div>
<div class="SRStatus" id="Loading">Loading...</div>
<div class="SRStatus" id="Searching">Searching...</div>
<div class="SRStatus" id="NoMatches">No Matches</div>
</div>
</div>
</div>
</div>
<div><div class="header">
<div class="headertitle"><div class="title">Frame</div></div>
</div><!--header-->
<div class="contents">
<div class="textblock"><h1><a class="anchor" id="NebulaFrameSystem"></a>
The Frame Subsystem</h1>
<p>The <a class="el" href="namespace_nebula.html">Nebula</a> <a class="el" href="namespace_frame.html" title="ImGUI debug interface for inspecting frame scripts.">Frame</a> system provides a scriptable interface for defining the passes and resources in a frame. Where most engines define their passes, draw batches, computes, copies and such, the frame system uses a JSON-script which is loaded at runtime, and which can be modified and customized for each game, without changing a single line of C++ code. The point is to have a modular and interpretable rendering routine, which allows the engine to properly syncrhonize resources automatically, define resizing behavior, and to quickly add, remove and modify sections of the frame without rewriting huge swats of code or to have to think about unforseen side effects. As such, the frame script provides a JSON-based language which we will go through in this document.</p>
<h2><a class="anchor" id="Resource"></a>
Declarations</h2>
<p>The <a class="el" href="namespace_nebula.html">Nebula</a> frame script system allows rendering to be scripted, rather than it being hard-coded. <a class="el" href="namespace_frame.html" title="ImGUI debug interface for inspecting frame scripts.">Frame</a> scripts allows us to create resources needed for rendering, them being:</p>
<ul>
<li>Textures, either used for rendering, or shader writing, or for sampling.</li>
<li>ReadWrite Buffers, used for non-texture type data reads and writes.</li>
<li>Plugins, code hooks which allows explicit code to be run at some point during the frame.</li>
</ul>
<p>Textures are declared like so: </p><div class="fragment"><div class="line">textures: [</div>
<div class="line"> {...}</div>
<div class="line">]</div>
</div><!-- fragment --><p>And contains a series of texture objects. A texture object has a certain set of fields, these are:</p>
<ul>
<li><b>name</b> - Name.<br />
</li>
<li><b>format</b> - Pixel format, available options can be found in CoreGrapics::PixelFormat.<br />
</li>
<li><b>usage</b> - List of flags, combined with |, allowed values are: Render, ReadWrite, Copy, Immutable and Mapable found in <a class="el" href="namespace_core_graphics.html#a65f17fdc4f0fe0d1e874946cf6c153f1" title="type of texture usage">CoreGraphics::TextureUsage</a>.<br />
</li>
<li><b>relative</b> - Bool value, true means the texture will scale with the window the script is attached to, false means it wont.<br />
</li>
<li><b>width</b>, <b>height</b>, <b>depth</b> - Texture dimensions, percent of screen size if relative is true, otherwise absolute size in pixels.<br />
</li>
<li><b>mips</b>, <b>layers</b> - Specify how many mips a texture has, and how many layers. Layers are only relevant if the texture is of an array <em class="arg">type</em>.<br />
</li>
<li><b>type</b> - Textures can be different types, Texture1D, Texture1DArray, Texture2D, Texture2DArray, TextureCube, TextureCubeArray and Texture3D <a class="el" href="namespace_core_graphics.html#a1ee7114fb950f691d35fddcdf5c40ddc" title="texture types">CoreGraphics::TextureType</a>.<br />
An example texture declaration would look like this:</li>
</ul>
<div class="fragment"><div class="line">{</div>
<div class="line"> name: <span class="stringliteral">"AlbedoBuffer"</span>,</div>
<div class="line"> format: <span class="stringliteral">"R8G8B8A8"</span>,</div>
<div class="line"> usage: <span class="stringliteral">"Render"</span>,</div>
<div class="line"> relative: <span class="keyword">true</span>,</div>
<div class="line"> width: 1.0,</div>
<div class="line"> height: 1.0,</div>
<div class="line"> type: <span class="stringliteral">"Texture2D"</span></div>
<div class="line">}</div>
</div><!-- fragment --><p>Read write buffers can also be created in the framescript, although there is usually very little reason to do so, since they don't necessarily map very well to frame operations. However, if wanted, one can do it like so:</p>
<div class="fragment"><div class="line">read_write_buffers: [</div>
<div class="line"> {...}</div>
<div class="line">]</div>
</div><!-- fragment --><p>And each buffer will have the following fields:</p>
<ul>
<li><b>name</b> - Name of buffer.<br />
</li>
<li><b>size</b> - Size of buffer in bytes, absolute size if relative is false, otherwise the size will be a % of the screen size.<br />
</li>
<li><b>relative</b> - True if buffer should scale with the size of the screen.<br />
An example can look like this:</li>
</ul>
<div class="fragment"><div class="line">{</div>
<div class="line"> name: <span class="stringliteral">"ClusterBuffer"</span>,</div>
<div class="line"> size: <span class="stringliteral">"4096"</span>,</div>
<div class="line"> relative: <span class="keyword">true</span></div>
<div class="line">}</div>
</div><!-- fragment --><p>This will make the buffer 4096 * screen width * screen height in size.</p>
<h2><a class="anchor" id="Operations"></a>
Operations</h2>
<p>The operations allowed in the 'global' scope of a frame script are:</p>
<ul>
<li><b>blit</b> copies a texture to another with a filtering and/or image conversion method, such as like RGB -> sRGB, BGR->RGB.</li>
<li><b>copy</b> performs a flat copy without any filtering or conversion.</li>
<li><b>mipmap</b> generates a mip chain on a texture, be sure to specify the 'mips' field with the value 'auto' first.</li>
<li><b>compute</b> runs a compute kernel using a non-optional shader state.</li>
<li><b>plugin</b> performs an algorithm outside of a render pass.</li>
<li><b>pass</b> triggers a rendering pass, within which only subpasses and subpass-related operations are allowed. In a pass, we are inside a 'render' scope, wherein only and exclusively render related commands are allowed.</li>
<li><b>barrier</b> puts an explicit synchronization point in the script, which has some rare uses, for example when synchronizing a texture before presenting, as the present system knows nothing of the layout changes from the framescript.</li>
</ul>
<h3><a class="anchor" id="frame_dependency"></a>
Dependencies</h3>
<p>Operations may and usually should define a set of resources which they need to be in a certain state before being operated on. This is a necessity since <a class="el" href="namespace_vulkan.html" title="Vulkan implementation of GPU acceleration structure.">Vulkan</a>, DX12 and Metal requires explicit synchronization for resources, and texture require a transition of layouts depending on their intended usage. Therefore, all operations share two fields which are to be used for this type of behavior. The benefits are that if two operations are rearranged, the frame script system will ensure the synchronization is kept valid, which would otherwise require a lot of hassle.</p>
<div class="fragment"><div class="line">inputs: [</div>
<div class="line"> {...}</div>
<div class="line">],</div>
<div class="line">outputs: [</div>
<div class="line"> {...}</div>
<div class="line">]</div>
</div><!-- fragment --><p>The inputs and outputs actually behave the same way, but the point of their declarations is to know what resources are actually being written by the operation, and which ones are only being read. An example resource dependency can look like this:</p>
<div class="fragment"><div class="line">inputs: [</div>
<div class="line"> {</div>
<div class="line"> name: <span class="stringliteral">"ZBuffer"</span>,</div>
<div class="line"> access: <span class="stringliteral">"DepthAttachmentRead"</span>,</div>
<div class="line"> layout: <span class="stringliteral">"DepthStencilRead"</span>,</div>
<div class="line"> stage: <span class="stringliteral">"ComputeShader"</span>,</div>
<div class="line"> aspect: <span class="stringliteral">"Depth"</span></div>
<div class="line"> }</div>
<div class="line">]</div>
</div><!-- fragment --><p>This requires the operation to wait for the ZBuffer to finish any operation leading up to being able to read depth attachments, and will transition the resource to the state where the depth values can be read. It will do so for the computer shader stage, and it will only wait for depth data to finish. Here are the possible values and what they mean:</p>
<ul>
<li><b>access</b> - The resource stage in the pipeline, CoreGraphics::BarrierAccess.<br />
</li>
<li><b>layout</b> - The image layout, <a class="el" href="namespace_core_graphics.html#a04d8601e946dc91033eabcbd74df3ff3">CoreGraphics::ImageLayout</a>.</li>
<li><b>stage</b> - The execution stage in the pipeline, CoreGraphics::BarrierStage.<br />
</li>
<li><b>aspect</b> - The image bits, CoreGraphics::ImageAspect.<br />
</li>
<li><b>mip</b> - The start mip level being used.</li>
<li><b>mip_count</b> - The amount of mips to depend on.</li>
<li><b>layer</b> - The start layer being used.</li>
<li><b>layer_count</b> - The amount of layers to depend on.</li>
</ul>
<h2><a class="anchor" id="Pass"></a>
Pass</h2>
<p>Within a pass, we must list some color-renderable textures and a single (or none) depth-stencil renderable texture. If a clear value is provided for any of these textures, the texture will be cleared with said value when the pass begins. We call color or depth-stencil renderable textures inside passes as attachments, since they are used for attaching to a fragment shader in order to receive color outputs.</p>
<p>Attachments are by default considered to NOT be stored when the pass ends, meaning that the implementation may decide that the result of an attachment is null and void after the pass is done. To avoid this, an attachment must be provided with a store flag, making sure the implementation knows this attachment will be needed after the render pass is done. Try avoid using store as much as possible, since it could improve performance for high-resolution images if the implementation doesn't need to perform a write-back on every attachment.</p>
<p>Likewise, if an attachment should be retained and loaded when the pass enters, the load flag must be provided. Otherwise every attachment is considered to be made up of invalid data, which the implementation may chose to reallocate, resulting in no guarantee the old data can be read.</p>
<p>A typical pass would look like this:</p>
<div class="fragment"><div class="line">pass: {</div>
<div class="line"> name: <span class="stringliteral">"GBuffer"</span>,</div>
<div class="line"> attachments: [</div>
<div class="line"> {</div>
<div class="line"> name: <span class="stringliteral">"AlbedoBuffer"</span>,</div>
<div class="line"> clear: [ 0.7, 0.7, 0.7, 1 ],</div>
<div class="line"> store: <span class="keyword">true</span></div>
<div class="line"> },</div>
<div class="line"> {</div>
<div class="line"> name: <span class="stringliteral">"NormalBuffer"</span>,</div>
<div class="line"> clear: [ 0, 0, 0, 0 ],</div>
<div class="line"> store: <span class="keyword">true</span></div>
<div class="line"> },</div>
<div class="line"> {</div>
<div class="line"> name: <span class="stringliteral">"SpecularBuffer"</span>,</div>
<div class="line"> clear: [ 0, 0, 0, 0 ],</div>
<div class="line"> store: <span class="keyword">true</span></div>
<div class="line"> },</div>
<div class="line"> {</div>
<div class="line"> name: <span class="stringliteral">"SSSBuffer"</span>,</div>
<div class="line"> clear: [ 0.5, 0.5, 0.5, 1 ],</div>
<div class="line"> store: <span class="keyword">true</span></div>
<div class="line"> }</div>
<div class="line"> ],</div>
<div class="line"> </div>
<div class="line"> depth_stencil: {</div>
<div class="line"> name: <span class="stringliteral">"ZBuffer"</span>,</div>
<div class="line"> clear: 1,</div>
<div class="line"> clear_stencil: 0,</div>
<div class="line"> store: <span class="keyword">true</span></div>
<div class="line"> },</div>
<div class="line"> </div>
<div class="line"> ...</div>
<div class="line">}</div>
</div><!-- fragment --><p>After this pass initial setup, of attachments and optional depth_stencil, we add elements of type <b>subpass</b>. More about that later. In this example, we declare that we will update AlbedoBuffer, NormalBuffer, SpecularBuffer and SSSBuffer <em class="arg">at</em> <em class="arg">any</em> <em class="arg">time</em> during the pass. This means some subpass might write to, for example AlbedoBuffer, and some to SpecularBuffer. This syntax allows for subpasses to communicate dependencies and do rendering operations independently! The values accepted for attachments, which are supposed to be color attachments, are:</p>
<ul>
<li><b>name</b> - Name of the attachment to be used by subpasses.</li>
<li><b>clear</b> - If specified, is a FLOAT4 clear color value, if not specified, no clear will be done.</li>
<li><b>store</b> - If true, the pixel values will be kept after this pass ends, otherwise, there is no such guarantee.</li>
<li><b>load</b> - If true, the pass operations may load values, and perform blending on this attachment.</li>
</ul>
<p>For <b>depth_stencil</b>, the following fields are accepted:</p>
<ul>
<li><b>name</b> - Name of the attachment to be used by subpasses.</li>
<li><b>clear</b> - If specified, is a single FLOAT clear depth value, otherwise, depth will not be cleared.</li>
<li><b>clear_stencil</b> - If specified, is a single INT clear stencil value, if not, stencil will not be cleared.</li>
<li><b>store</b> - If true, the pixel values will be kept after this pass ends, otherwise, there is no such guarantee.</li>
<li><b>load</b> - If true, the pass operations may load values, and perform blending on this attachment.</li>
</ul>
<h3><a class="anchor" id="Subpass"></a>
Subpass</h3>
<p>Subpasses use a subset of the color attachments, and optionally the depth-stencil attachment in the pass. Only within a subpass is it legal to perform rendering calls. The following list of operations are available in a subpass:</p>
<ul>
<li><b>name</b> - Name of the subpass, this is important to remember, because it is used to setup subpass dependencies.</li>
<li><b>dependencies</b> - A STRING LIST of other subpasses this subpass dependends on, which means this subpass has to wait on some other subpass to finish.</li>
<li><b>attachments</b> - A STRING LIST of textures to render to.</li>
<li><b>inputs</b> - A STRING LIST of attachments from previous subpasses to read from, these will be InputAttachments in the shading system.</li>
<li><b>depth</b> - If true, this subpass will use Z-testing, otherwise the shaders in this subpass can't use z-testing.</li>
<li><b>resolve</b> - If true, this subpass will resolve any multisampling attachments being used.</li>
<li><b>resources</b> - Just like <b>inputs</b> and <b>outputs</b> from ordinary operations, this subpass declares dependencies on resources using the same syntax as in <a class="el" href="#frame_dependency">Dependencies</a></li>
<li><b>viewports</b> - A FLOAT4 LIST of viewports to use instead of the viewport inferred from the attachments.</li>
<li><b>scissors</b> - A FLOAT4 LIST of scissor rectangles to use instead of the viewport inferred from the attachments.</li>
<li><b>plugin</b> - A plugin callback for rendering.</li>
<li><b>batch</b> - A material sorted rendering batch, only takes a single string as the material name to render.</li>
<li><b>sorted_batch</b> - An unsorted rendering batch, which allows for arbitrary sorting. Not yet implemented.</li>
<li><b>fullscreen_effect</b> - A full screen quad using a specific shader, and texture to inherit its size from.</li>
</ul>
<p>Full screen effects provide an additional piece of syntax, an example can look like this:</p>
<div class="fragment"><div class="line">fullscreen_effect: {</div>
<div class="line"> name: <span class="stringliteral">"Finalize"</span>,</div>
<div class="line"> shader_state: {</div>
<div class="line"> shader: <span class="stringliteral">"finalize"</span>,</div>
<div class="line"> variables: [</div>
<div class="line"> {</div>
<div class="line"> semantic: <span class="stringliteral">"LuminanceTexture"</span>,</div>
<div class="line"> value: <span class="stringliteral">"AverageLumBuffer"</span></div>
<div class="line"> },</div>
<div class="line"> {</div>
<div class="line"> semantic: <span class="stringliteral">"DepthTexture"</span>,</div>
<div class="line"> value: <span class="stringliteral">"ZBuffer"</span></div>
<div class="line"> },</div>
<div class="line"> {</div>
<div class="line"> semantic: <span class="stringliteral">"ColorTexture"</span>,</div>
<div class="line"> value: <span class="stringliteral">"LightBuffer"</span></div>
<div class="line"> },</div>
<div class="line"> {</div>
<div class="line"> semantic: <span class="stringliteral">"BloomTexture"</span>,</div>
<div class="line"> value: <span class="stringliteral">"BloomBufferBlurred"</span></div>
<div class="line"> },</div>
<div class="line"> ]</div>
<div class="line"> },</div>
<div class="line"> size_from_texture: <span class="stringliteral">"ColorBuffer"</span></div>
<div class="line">},</div>
</div><!-- fragment --><p>The <b>shader_state</b> member defines an object encapsulating a shader and a set of variables to apply when rendering. The <b>semantic</b> corresponds to a shader variable name, and the <b>value</b> is the texture or buffer from the frame script. <b>size_from_texture</b> determines the size to use when rendering the post effect.</p>
<h2><a class="anchor" id="frame_interface"></a>
Script Interface</h2>
<p>A frame script then executes actions, and all actions are inherited from the class FrameOp. FrameOp provides a couple of default functions used to handle rendering. FrameOp is the base class, and is meant as the template from which a FrameOp::Compiled is constructed. Compiled frame ops are the finished product, constructed automatically when we run FrameScript::Compile, and will construct Compiled operations only for the operations which are enabled. This allows for the frame script to always run a minimal update, and arrange the internal synchronization correctly.</p>
<div class="fragment"><div class="line"></div>
<div class="line"><span class="keyword">virtual</span> <span class="keywordtype">void</span> Run(<span class="keyword">const</span> <a class="code hl_typedef" href="types_8h.html#ad119735112d40fd29a3017de944ccc98">IndexT</a> frameIndex);</div>
<div class="line"><span class="keyword">virtual</span> <span class="keywordtype">void</span> OnWindowResized();</div>
<div class="ttc" id="atypes_8h_html_ad119735112d40fd29a3017de944ccc98"><div class="ttname"><a href="types_8h.html#ad119735112d40fd29a3017de944ccc98">IndexT</a></div><div class="ttdeci">int IndexT</div><div class="ttdef"><b>Definition</b> types.h:48</div></div>
</div><!-- fragment --> </div></div><!-- contents -->
</div><!-- PageDoc -->
</div><!-- doc-content -->
<!-- start footer part -->
<div id="nav-path" class="navpath"><!-- id is needed for treeview function! -->
<ul>
<li class="navelem"><a class="el" href="render.html">Render Systems</a></li>
<li class="footer">
Generated on Tue Jan 28 2025 22:43:09 for Nebula. Dark theme by <a href="http://majerle.eu" target="_new">Tilen Majerle</a>. All rights reserved.
</li>
</ul>
</div>
<script src="custom.js"></script>
</body>
</html>