Theme

alex/beta-minecraft-rust

Minecraft Beta 1.7.3 Rewrite

A BASE Add present mode selection (Immediate/Mailbox/FIFO/FIFO Relaxed/FIFO Latest Ready), VK_EXT_present_timing frame pacing, and a Max FPS slider; settle falling blocks instantly during population 01a1184c-4ef1

Search or browse snapshots.

B COMPARE Draw terrain GPU-driven from packed per-section buffers with GPU occlusion culling, add a render distance slider (2-128) with a quad/disk shape, cut chunk bookkeeping at high render distances, and fix a light underflow at y=0 01a11aa0-3896

Search or browse snapshots.

27 changed files 1 added 0 deleted 26 modified

Files 27
M

crates/client/shaders/terrain.slang

+293 −31
File →
0 / 20
1−// Chunk section rendering with vertex pulling. Matches the fixed-function look of b1.7.3:
1+// Chunk sections, culled and drawn on the GPU.
2+//
3+// Culling runs in two phases: sections visible last frame draw first, then the bounding boxes
4+// of all sections in the frustum are rasterized against that depth, and the newly visible ones
5+// draw second. Draws skip the facing groups that point away from the camera.
6+//
7+// Vertices are pulled per face: a face references a deduplicated shape (corner positions
8+// relative to its block, shade and texture coordinates), light and tint. A fixed-function look:
29 // per-vertex light averaged from four brightness samples (smooth lighting), flat face shading,
310 // alpha test and linear radial fog.
5−struct TerrainVertex
12+// See `SectionRecord` in terrain.rs.
13+struct Section
614 {
7− uint xy; // 16-bit fixed point, 1/2048 block, offset by -8
8− uint zShade; // z | shade << 16
9− uint uv; // 16-bit normalized atlas coordinates
10− uint tint; // RGBA8
11− uint light; // four samples of sky light | block light << 4, summed in order
15+ int3 position;
16+ uint visible; // a fragment of the box passed the depth test this frame
17+ uint wasVisible;
18+ uint extents[3]; // signed bytes: minimum and maximum block bounds, then facing planes
19+ uint* shapes; // 12 words: per corner x | y << 16 (wrapping), z | shade << 16, uv
20+ uint4* lights; // per corner, four samples of sky light | block light << 4
21+ uint* tints; // RGBA8
22+ uint2* faces; // x | y << 4 | z << 8 | shape << 12, light | tint << 18
23+ uint counts[15]; // faces per group: solid and cutout per facing, then translucent
24+ uint total; // 0 for a free slot
1225 };
14−struct Push
27+struct DrawCommand
29+ uint indexCount;
30+ uint instanceCount;
31+ uint firstIndex;
32+ int vertexOffset;
33+ uint firstInstance;
34+};
36+static const uint FACINGS = 7;
37+static const uint TRANSLUCENT = 14;
38+/// Boxes reach this far past the geometry, so they are not coplanar with it.
39+static const float MARGIN = 1.0 / 16.0;
41+int extent(Section* section, uint i)
42+{
43+ return int(section.extents[i / 4] << (24 - 8 * (i % 4))) >> 24;
44+}
46+/// The section's minimum corner relative to the eye.
47+float3 sectionOrigin(Section* section, int4 eyeBlock, float4 eyeFraction)
48+{
49+ return float3(section.position * 16 - eyeBlock.xyz) - eyeFraction.xyz;
50+}
52+void sectionBox(Section* section, float3 origin, out float3 lo, out float3 hi)
53+{
54+ lo = origin + float3(extent(section, 0), extent(section, 1), extent(section, 2)) - MARGIN;
55+ hi = origin + float3(extent(section, 3), extent(section, 4), extent(section, 5)) + MARGIN;
56+}
58+// ---- Drawing ---------------------------------------------------
60+struct DrawPush
61+{
1662 float4x4 viewProjection;
17− TerrainVertex* vertices;
18− float4* origins;
1963 float4 fogColor;
2064 float4 camera;
65+ float4 eyeFraction;
66+ int4 eyeBlock;
67+ Section* sections;
2168 float fogStart;
2269 float fogEnd;
2370 uint skyDarkness;
2471 uint texture;
25− float brightness[16];
2672 float opacity;
73+ uint padding;
74+ float brightness[16];
2775 };
29−[[vk::push_constant]] Push push;
3077 [[vk::binding(0, 0)]] Texture2D textures[];
3178 [[vk::binding(1, 0)]] SamplerState samplers[];
3380 static const float SHADE[4] = { 1.0, 0.8, 0.6, 0.5 };
3582 struct Varyings
3784 float4 position : SV_Position;
3885 float2 uv : TEXCOORD0;
3986 float4 color : COLOR0;
4087 float distance : TEXCOORD1;
4188 };
4390 [shader("vertex")]
44−Varyings vertexMain(uint vertexIndex : SV_VulkanVertexID, uint drawIndex : SV_VulkanInstanceID)
91+Varyings vertexMain(uniform DrawPush push, uint vertexIndex : SV_VulkanVertexID, uint slot : SV_VulkanInstanceID)
46− TerrainVertex v = push.vertices[vertexIndex];
47− float3 local = float3(v.xy & 0xFFFF, v.xy >> 16, v.zShade & 0xFFFF) / 2048.0 - 8.0;
48− float3 position = local + push.origins[drawIndex].xyz;
93+ Section* section = push.sections + slot;
94+ uint2 face = section.faces[vertexIndex >> 2];
95+ uint corner = vertexIndex & 3;
96+ uint* shape = section.shapes + (face.x >> 12) * 12 + corner * 3;
97+ uint3 block = (face.x >> uint3(0, 4, 8)) & 15;
98+ uint3 fixed = (uint3(shape[0] & 0xFFFF, shape[0] >> 16, shape[1] & 0xFFFF) + (block << 11)) & 0xFFFF;
99+ float3 position = float3(fixed) / 2048.0 - 8.0 + sectionOrigin(section, push.eyeBlock, push.eyeFraction);
100+ uint shade = shape[1] >> 16;
101+ uint light = section.lights[face.y & 0x3FFFF][corner];
102+ uint tint = section.tints[face.y >> 18];
103+ uint uv = shape[2];
50105 float brightness = 0.0;
51106 for (uint i = 0; i < 4; i++)
53− uint sample = v.light >> (8 * i);
108+ uint sample = light >> (8 * i);
54109 int sky = int(sample & 15);
55− int block = int((sample >> 4) & 15);
56− brightness += push.brightness[clamp(max(sky - int(push.skyDarkness), block), 0, 15)];
110+ int blockLight = int((sample >> 4) & 15);
111+ brightness += push.brightness[clamp(max(sky - int(push.skyDarkness), blockLight), 0, 15)];
58− float light = brightness / 4.0 * SHADE[(v.zShade >> 16) & 3];
113+ float lit = brightness / 4.0 * SHADE[shade & 3];
59114 // Shade 4 glows: halfway towards full brightness
60− if (((v.zShade >> 18) & 1) != 0)
61− light = (light + 1.0) * 0.5;
62− float3 tint = float3(v.tint & 0xFF, (v.tint >> 8) & 0xFF, (v.tint >> 16) & 0xFF) / 255.0;
115+ if ((shade & 4) != 0)
116+ lit = (lit + 1.0) * 0.5;
117+ float3 color = float3(tint & 0xFF, (tint >> 8) & 0xFF, (tint >> 16) & 0xFF) / 255.0;
64119 Varyings o;
65120 o.position = mul(push.viewProjection, float4(position, 1.0));
66− o.uv = float2(v.uv & 0xFFFF, v.uv >> 16) / 65536.0;
67− o.color = float4(tint * light, 1.0);
121+ o.uv = float2(uv & 0xFFFF, uv >> 16) / 65536.0;
122+ o.color = float4(color * lit, 1.0);
68123 o.distance = length(position - push.camera.xyz);
69124 return o;
127+float4 fogged(DrawPush push, Varyings i, float4 color)
128+{
129+ // A negative start selects GL_EXP fog with the negated start as density
130+ float fog = push.fogStart < 0.0
131+ ? exp(push.fogStart * i.distance)
132+ : saturate((push.fogEnd - i.distance) / (push.fogEnd - push.fogStart));
133+ return float4(lerp(push.fogColor.rgb, color.rgb, fog), color.a * push.opacity);
134+}
72136 [shader("fragment")]
73−float4 fragmentMain(Varyings i) : SV_Target
137+float4 fragmentMain(uniform DrawPush push, Varyings i) : SV_Target
75139 float4 color = textures[push.texture].Sample(samplers[0], i.uv) * i.color;
76140 if (color.a <= 0.1)
77141 discard;
78− // A negative start selects GL_EXP fog with the negated start as density
79− float fog = push.fogStart < 0.0
80− ? exp(push.fogStart * i.distance)
81− : saturate((push.fogEnd - i.distance) / (push.fogEnd - push.fogStart));
82− return float4(lerp(push.fogColor.rgb, color.rgb, fog), color.a * push.opacity);
142+ return fogged(push, i, color);
143+}
145+/// For faces whose texels all pass the alpha test: without `discard`, depth is tested early.
146+[shader("fragment")]
147+float4 solidFragment(uniform DrawPush push, Varyings i) : SV_Target
148+{
149+ return fogged(push, i, textures[push.texture].Sample(samplers[0], i.uv) * i.color);
150+}
152+// ---- Box test --------------------------------------------------
154+struct BoxPush
155+{
156+ float4x4 viewProjection;
157+ float4 eyeFraction;
158+ int4 eyeBlock;
159+ Section* sections;
160+ uint* tested;
161+};
163+/// Corners of the twelve box triangles, bits 1, 2 and 4 picking the maximum x, y and z.
164+static const uint BOX[36] = {
165+ 0, 2, 6, 0, 6, 4, 1, 5, 7, 1, 7, 3,
166+ 0, 4, 5, 0, 5, 1, 2, 3, 7, 2, 7, 6,
167+ 0, 1, 3, 0, 3, 2, 4, 6, 7, 4, 7, 5,
168+};
170+struct BoxVaryings
171+{
172+ float4 position : SV_Position;
173+ nointerpolation uint slot : SLOT;
174+};
176+[shader("vertex")]
177+BoxVaryings boxVertex(uniform BoxPush push, uint vertexIndex : SV_VulkanVertexID)
178+{
179+ uint slot = push.tested[vertexIndex / 36];
180+ Section* section = push.sections + slot;
181+ float3 lo, hi;
182+ sectionBox(section, sectionOrigin(section, push.eyeBlock, push.eyeFraction), lo, hi);
183+ uint corner = BOX[vertexIndex % 36];
184+ float3 position = select((corner & uint3(1, 2, 4)) != 0, hi, lo);
186+ BoxVaryings o;
187+ o.position = mul(push.viewProjection, float4(position, 1.0));
188+ o.slot = slot;
189+ return o;
190+}
192+[shader("fragment")]
193+[earlydepthstencil]
194+void boxFragment(uniform BoxPush push, BoxVaryings i)
195+{
196+ push.sections[i.slot].visible = 1;
197+}
199+// ---- Culling ---------------------------------------------------
201+struct CullPush
202+{
203+ float4 planes[5];
204+ float4 eyeFraction;
205+ float4 camera; // relative to the eye
206+ int4 eyeBlock;
207+ Section* sections;
208+ uint* order;
209+ uint* counters; // solid and cutout draws, sections drawn, -, the box test's draw
210+ uint* tested;
211+ DrawCommand* commands; // solid, then cutout at `capacity`
212+ uint* translucent;
213+ DrawCommand* translucentCommands;
214+ uint count;
215+ uint capacity;
216+ uint mode;
217+ uint padding;
218+};
220+bool inFrustum(CullPush push, float3 lo, float3 hi)
221+{
222+ for (uint i = 0; i < 5; i++)
223+ {
224+ float4 plane = push.planes[i];
225+ if (dot(plane.xyz, select(plane.xyz >= 0.0, hi, lo)) + plane.w < 0.0)
226+ return false;
227+ }
228+ return true;
229+}
231+/// Bit per facing group whose faces may point at a camera this far into the section.
232+uint visibleFacings(Section* section, float3 camera)
233+{
234+ float plane[6];
235+ for (uint i = 0; i < 6; i++)
236+ plane[i] = float(extent(section, 6 + i));
237+ return uint(camera.y < plane[0])
238+ | uint(camera.y > plane[1]) << 1
239+ | uint(camera.z < plane[2]) << 2
240+ | uint(camera.z > plane[3]) << 3
241+ | uint(camera.x < plane[4]) << 4
242+ | uint(camera.x > plane[5]) << 5
243+ | 1u << 6;
244+}
246+DrawCommand draw(uint faces, uint first, uint slot)
247+{
248+ DrawCommand command = { faces * 6, 1, 0, int(first * 4), slot };
249+ return command;
250+}
252+/// Adds draws for the solid and cutout groups that face the camera, merging adjacent ones.
253+void emit(CullPush push, Section* section, uint slot, float3 camera)
254+{
255+ uint facings = visibleFacings(section, camera);
256+ uint first = 0;
257+ for (uint layer = 0; layer < 2; layer++)
258+ {
259+ DrawCommand commands[4];
260+ uint count = 0;
261+ uint start = first;
262+ uint run = 0;
263+ for (uint facing = 0; facing < FACINGS; facing++)
264+ {
265+ uint faces = section.counts[layer * FACINGS + facing];
266+ if ((facings >> facing & 1) != 0 || faces == 0)
267+ {
268+ run += faces;
269+ }
270+ else
271+ {
272+ if (run != 0)
273+ commands[count++] = draw(run, start, slot);
274+ run = 0;
275+ start = first + faces;
276+ }
277+ first += faces;
278+ }
279+ if (run != 0)
280+ commands[count++] = draw(run, start, slot);
281+ if (count == 0)
282+ continue;
283+ uint base;
284+ InterlockedAdd(push.counters[layer], count, base);
285+ for (uint i = 0; i < count && base + i < push.capacity; i++)
286+ push.commands[layer * push.capacity + base + i] = commands[i];
287+ }
288+ InterlockedAdd(push.counters[2], 1);
289+}
291+[shader("compute")]
292+[numthreads(64, 1, 1)]
293+void cullMain(uniform CullPush push, uint3 id : SV_DispatchThreadID)
294+{
295+ uint index = id.x;
296+ if (index >= push.count)
297+ return;
298+ if (push.mode == 2)
299+ {
300+ uint slot = push.translucent[index];
301+ Section* section = push.sections + slot;
302+ uint first = section.total - section.counts[TRANSLUCENT];
303+ DrawCommand none = {};
304+ push.translucentCommands[index] = section.wasVisible != 0
305+ ? draw(section.counts[TRANSLUCENT], first, slot)
306+ : none;
307+ return;
308+ }
310+ uint slot = push.order[index];
311+ Section* section = push.sections + slot;
312+ if (section.total == 0)
313+ return;
314+ float3 origin = sectionOrigin(section, push.eyeBlock, push.eyeFraction);
315+ float3 lo, hi;
316+ sectionBox(section, origin, lo, hi);
317+ bool visible = inFrustum(push, lo, hi);
318+ float3 camera = push.camera.xyz;
319+ if (push.mode == 0)
320+ {
321+ if (!visible)
322+ return;
323+ // Boxes this near may reach behind the near plane: count them visible untested
324+ if (all(camera > lo - 1.0) && all(camera < hi + 1.0))
325+ {
326+ section.visible = 1;
327+ }
328+ else
329+ {
330+ uint vertex;
331+ InterlockedAdd(push.counters[4], 36, vertex);
332+ push.tested[vertex / 36] = slot;
333+ }
334+ if (section.wasVisible != 0)
335+ emit(push, section, slot, camera - origin);
336+ }
337+ else
338+ {
339+ visible = visible && section.visible != 0;
340+ if (visible && section.wasVisible == 0)
341+ emit(push, section, slot, camera - origin);
342+ section.wasVisible = uint(visible);
343+ section.visible = 0;
344+ }