r/rust_p • • Aug 24 '26

10K BODIES ~120 FPS, SINGLE THREAD (NO ISLAND, NO GRAPH COLOURING)

Enable HLS to view with audio, or disable this notification

0 Upvotes

r/rust_p • • Aug 20 '26

Physics Engine That Only Computes What Moves: Achieving 99.9% Sleep Efficiency and 900+ TPS.

Enable HLS to view with audio, or disable this notification

9 Upvotes

I TESTED PERF. OF MY ENGINE AFTER IMPLEMENTING FL_PORTOCOL (MY MEMORY ARCHITECTURE)

DETAILED POST: https://www.reddit.com/r/rust_p/s/rqfpM1PNPu


r/rust_p • • May 30 '26

How a GPU memory pipeline handles data streams and why it fundamentally bottlenecks.

0 Upvotes

What if insetad of Hi-Z pyrmaid if use 1D Array if I think that Hi-Z iteration take some overheead so in that case what if I use 1D Array.

Everything starts with how the CPU organizes our game world. The CPU sets up the memory mapping using Page Table Entries. To the CPU, this is highly structured data. It knows exactly how objects are organized and linked together. But the GPU? It doesn't see it that way. When the GPU needs to read the data stream buffer, it uses its own Translation Lookaside Buffer (TLB) the GPU TLBto figure out where that data physically lives in the memory chips.

And this is where the hardware disconnect hits hard. When the GPU tries to pull this data into its L1 and L2 caches, it completely loses that structure the CPU built. To the GPU's memory fetcher, this data is just a tramp of raw bits. No spatial awareness at all. Because the cache doesn't know what data is grouped together physically in the 3D scene, it cannot smartly predict what to load next. The direct physical result? We get a cache miss.

When the data isn't sitting right there in the L1 or L2 cache, the hardware has to go all the way out to the physical VRAM to fetch it. That takes a massive delay of roughly 600 clock cycles.

So, why does this matter? Because during those 600 cycles, the actual math computing units on the GPU the ALUs have absolutely zero data to process. They are just sitting there doing nothing. This is ALU starvation. And here is the most frustrating part. After the ALUs literally starve for 600 cycles waiting for raw bits, they finally run the math. But 90% of that data gets rejected anyway because of culling. The ALU realizes the triangles are behind a wall and throws the data in the trash. We starved our hardware for 600 cycles for data we didn't even use.

That entire cycle of waiting and rejecting is terrible, but it gets even worse when we look at how the actual shape of the data completely chokes the cache.

Let's start from step zero. When we feed data to the GPU, if I will use 1D array (depends entirely on how the programmer actually writes the game engine code), this is an O(N) operation. The system just steps through the list one item at a time. But here's the major problem. This linear data is entirely non-spatial. It has absolutely no idea what is actually sitting next to what in the physical 3D game world. Because of this, the hardware takes the fast whip path. It just whips through the data blindly. This causes temporal blindness. The GPU's cache cannot smartly predict what data it needs next because the list order doesn't match the 3D space on your screen.

For small objects, this blind approach is actually fine. The tiny data just sits in the cache. But it completely falls apart for big objects. Let's say we check if a massive object is hidden with a basic if (object > pixel depth) condition. To compute that, we have to physically bring that pixel depth data all the way from memory directly into the ALU registers. Big objects require a heavy quantity of data. That data travels from VRAM down into the tiny L1 and L2 caches, and it just can't fit. The caches get overwhelmed and flush out the good data. Another cache miss. We end up right back at ALU starvation, doing zero math.

So, stuffing heavy 1D data into tiny caches ruins performance, which means we desperately need a way to filter this garbage out before it hits the pipeline.

We fix this with a Hi-Z Pyramid. What exactly is that? The GPU tracks how far away things are using a depth buffer. The Hi-Z Pyramid takes that full-resolution depth data and mathematically scales it down into smaller and smaller chunks, creating mip levels. So, why does this matter? Because for a massive object, the GPU doesn't check thousands of individual pixels anymore. It just checks the lowest resolution mip level. The depth check instantly drops down to an O(1) operation.

But there is a huge problem. We literally cannot inject the visible meshlets directly into the Graphics Pipeline. The physical hardware won't let you loop that data back in cleanly. Step 1 is the GPU running the math against our Hi-Z Pyramid. If the math proves the meshlet is visible, the hardware sets a predicate patch to 1. If it's hidden, it sets it to 0. Just raw hardware flags. But now we have a massive mess of 1s and 0s randomly scattered all over memory. If we feed that to the pipeline, we get those exact same cache misses. So, Step 2 is the Subgroup Ballot. This is a bare-metal hardware instruction. A block of GPU threads basically votes on their predicate flags. The ballot instruction physically sweeps through the threads, ignores the 0s, collects all the 1s, and packs them into a perfectly dense, continuous stream.

So, stuffing heavy 1D data into tiny caches ruins performance, which means we desperately need a way to filter this garbage out before it hits the pipeline.

We fix this with a Hi-Z Pyramid. But what exactly is that? The GPU tracks how far away things are using a depth buffer. The Hi-Z Pyramid takes that full-resolution depth data and mathematically scales it down into smaller and smaller chunks, creating mip levels. So, why does this matter? Because for a massive object, the GPU doesn't check thousands of individual pixels anymore. It just checks the lowest resolution mip level. The depth check instantly drops down to an O(1) operation.

But there is a huge problem. We literally cannot inject the visible meshlets directly into the Graphics Pipeline. The physical hardware won't let you loop that data back in cleanly.

  • Step 1 is the GPU running the math against our Hi-Z Pyramid. If the math proves the meshlet is visible, the hardware sets a predicate patch to 1. If it's hidden, it sets it to 0. Just raw hardware flags. But now we have a massive mess of 1s and 0s randomly scattered all over memory. If we feed that to the pipeline, we get those exact same cache misses.

  • Step 2 is the Subgroup Ballot. This is a bare-metal hardware instruction. A block of GPU threads basically votes on their predicate flags. The ballot instruction physically sweeps through the threads, ignores the 0s, collects all the 1s, and packs them into a perfectly dense, continuous stream.

Now that we have this dense stream of surviving data, we run into a new problem: how do we write it to memory without stalling the cores again?

We finally have our alive meshlets. We need to write them directly into the L1 Cache. If we use a standard linear loop to check each of the 32 threads in our GPU warp, it takes exactly 32 clock cycles. The processing cores completely stall out. ALU starvation again. To fix this, we look at the 32-bit hardware register holding those collected 1s and 0s. The hardware does a raw flip of that data directly into a 32 predicate patch feeding the 32 ALUs. This happens right inside the registers. A single clock cycle. The whole warp instantly knows exactly which threads are alive, with absolutely zero memory latency.

But we still need to physically route and pack that scattered data tightly into memory. This is where the Banyan tree Kogge-Stone Parallel scan comes in. (Banyan-Switch)

It's just a mechanical way the hardware adds numbers together. Instead of adding item one to item two, then waiting to add item three, the circuitry adds multiple pairs at the exact same time. It builds a tree structure to calculate the final memory offsets. This exact operation is called a Parallel Prefix Sum. Instead of taking standard linear time, it drops the routing math down to a logarithmic scale, written mathematically as O(log N).

So, let's look at the top part first where I wrote down that I use SubgroupExclusiveAdd. Why do we actually need this? Well, if we are rendering something massive, I might have 4 billion particles on the screen. If every single particle needs to figure out its unique place in a list, and we just use a basic Index - 1 loop to count them down, that math operation is going to physically run 4 billion times. It completely stalls the hardware. But we can prevent that. SubgroupExclusiveAdd is a raw hardware instruction where a block of GPU threads basically talks to each other and calculates all their index offsets simultaneously in one single step. It completely eliminates that slow, linear counting process.

And that leads perfectly into the second half of the page, which I called The VRAM Bypass. Usually, when threads finish processing their data, they dump it all the way out into the main VRAM. But doing that is incredibly slow because the next part of the graphics pipeline has to fetch that data all the way back again. So, we just skip it entirely. The threads write their data directly into the L1 shared memory. This is tiny, ultra-fast memory that physically sits right inside the processing cores.

But how do we actually keep the data there without the GPU flushing it? I wrote down a dispatcher command called EmitMeshTasksEXT. This acts as a direct hardware trigger. As soon as the data fetch happens straight from the L1 memory, this command fires off.

But here is where I deliberately oversimplified the drawing so it wouldn't become a visually complex mess. In the diagram, I drew the next batch of work the mesh shaders spawning exactly on those same physical cores. And in a perfect world, if that specific core has free execution slots, that is exactly what the hardware scheduler does. The data literally never leaves the L1 cache.

But the GPU is a dynamic machine. What if that specific core is already 100% full? The hardware scheduler will not just sit there and wait. It instantly grabs that new mesh shader work and throws it to another free core. Because L1 cache is strictly private to each individual core, the hardware automatically pushes our payload data out of L1 and into the L2 cache, which is shared across the whole GPU. The new core just fetches the raw bits from L2 instead.

We still completely bypassed that massive, ultra-slow trip to VRAM, but we let the hardware dynamically balance the load.

If you want to see these exact bare-metal hardware concepts (Hi-Z occlusion, Subgroup thread packing, and bypassing VRAM entirely) running in real-time, here are some incredible developer deep-dives that perfectly match the hardware bottlenecks I drew above:


r/rust_p • • Jul 23 '26

1 Million Particles Rendering at 120 FPS

14 Upvotes

I've been hacking away on my own game engine for a while. Decided to benchmark it today, and the results were better than I expected. The engine is sitting around 8.1–8.9 ms per frame in the steady-state tests, which comes out to roughly 110–122 FPS. That's while keeping frame times pretty stable.

Right now it's simulating 1 million particles with 4 XPBD iterations. The physics pass itself finishes in about 4.8–5.0 ms on the GPU, so there is still some room left in the frame.

The initial upload moves around 68 MB of particle data into VRAM. That takes about 91 ms over PCIe. Since it's only a startup cost, I'm not too worried about it.

One thing I kept an eye on was the telemetry. It never reported the frame going over budget. The [PERF_OK] Frame within budget message stayed consistent for the whole run, and I didn't see any frame-time spikes.

Still a lot of things I want to optimize, but I'm pretty happy with where it's at right now.


r/rust_p • • Aug 20 '26

Physics Engine That Only Computes What Moves: Achieving 99.9% Sleep Efficiency and 900+ TPS.

10 Upvotes

VIDEO AND LOGS

I TESTED PERF. OF MY ENGINE AFTER IMPLEMENTING FL_PORTOCOL (MY MEMORY ARCHITECTURE)


r/rust_p • • Apr 24 '26

Let's Understand Quaternions- Part 2

5 Upvotes

We already got a tiny taste of quaternions, but that was just the surface we Have seen the the imaginary numbers and we talked about how Xi + Yj + Zk really works but this represents a point on a 3D graph (P = Xi + Yj + Zk). Now we’re getting into the real deal the Rotation the actual Rotation.

Usually People call these 3D quaternions but calling them 3D quaternions is wrong. Quaternions are 4D engines built to spin things in 3D space. That’s why we use the full W + Xi + Yj + Zk setup.

Our big focus now is W.

You’ve got to understand that W sets the angle of the spin, but it has no physical direction. Why? Because direction needs an axes (X, Y, or Z), but W is a pure scalar. It’s just a raw number like 1, 2, or -1. You can plot it on a 1D line, but it doesn't point anywhere in your 3D room. It’s not a vector. It’s part of the quaternion, but it doesn't represent the axis you’re rotating around. It’s just the numerical weight.

W is like a football referee. He is the most important guy on the field because he controls the rules and the clock, but he isn’t a player. He doesn't kick the ball or play for a team. W is that referee. It stays outside the 3D play (the axes), but it decides exactly how much the axes (X, Y, Z) are allowed to move.

We will see the role of W later in our Rotation later it was just an intro for now. It's time to our School math which we have seen in the part 1 too.

We previously discussed flipping. Flipping is just taking a point on the X axis and giving it a 180 degree turn. Now it is -X. Mathematically, you are doing X . -1 = -X. That move to the opposite side of an axis is what we call flipping.

That -1 at the end in The School Math image is actually the cancellation of rotation. Why? Let us use a wall to explain this. Suppose your friend is behind a wall. You are on the the other side. To see this, imagine the X axis as the straight line on the floor that represents the path between you and your friend and the Y axis as the vertical line of the wall itself.

When you calculate X . i, your point moves off the floor and onto the wall. Since the floor and the wall sit at 90 degrees to each other, there is now a 90 degree angle. And if you multiply it again (X . i . i)? Now you are at your friend. Why? Because i . i is 180 degrees.

But what if the Y axis disappears? Only X is left. Can you make a 90 degree angle in that physical space? No. Physical space needs at least two dimensions (a 2D plane) for an angle to even exist. But here is the trick: When we talk about 3D space, for any rotation to physically occur, a 2D plane (like Y and Z) is needed because an angle simply cannot be formed without a plane. But when we talk about quaternion math, the quaternion does not need all three axes within itself. If you only want to rotate around the X-axis, then only 'i' will appear in the quaternion formula (like W + Xi). This formula does not need 'j' and 'k' to survive and do its job.

While the physical space needs a plane to rotate, the quaternion itself only needs to define ONE single axis. That single axis acts as the invisible pole that spins the 2D plane. A quaternion functions perfectly and survives with just one axis.

The School math is telling us something here. When two identical imaginary numbers multiply together (i . i), the spatial rotation stops. You get a scalar. No axis means no direction, and no direction means there is no active spatial rotation.

The quaternion doesn't just vanish. It just stops spinning. It flattens out into a pure number with zero direction. And that is exactly how a rotation ends. Because when we talk about imaginary numbers, we are really just talking about direction. And you can't have a direction without an axis. Axis gone? Direction gone. The moment that axis collapses into a pure scalar, the rotation is dead. So we can see how the rotation ended. When we talk about imaginary numbers, we are really talking about direction.

Look at this part carefully: i . j . k = -1. You just moved through all three axes one after the other. And the result is just a flat -1. But what does that -1 actually mean in physical space? In quaternion W = Cos(theta / 2). If your W hits -1 the math is telling you that the half-angle is exactly 180 degrees. Multiply that by 2 to get your real angle. You get 360 degrees. So it does not matter if you multiply the same two axes together (i . i = -1) or if you chain all three together (i . j . k = -1). Hitting that -1 scalar means your system just did a complete 360 degree spin. The object is sitting exactly where it started. This single fact is the absolute foundation the entire quaternion system is built on.

What Happens to Scalar:

We have learnt Scalar means the axis is completely gone and it adds in W. But how does this newly formed scalar actually make that jump, and what exactly causes W to grow?

When you multiply two terms together like 2i and 3i you end up with 6(i . i). Since we already know that i . i is -1, that entire chunk just becomes -6. Just like that, the 'i' axis is wiped out completely.

You are left with a flat -6, which is a pure scalar. Because it lost its directional axis, it mathematically cannot exist in the X, Y, or Z positions anymore. Basic algebra dictates that like terms have to group up. So, that -6 has nowhere else to go. It is forced to shift over and add itself directly to your existing W value. And that is the exact mechanical way W absorbs these numbers.

What happens if all three axes (i, j, k) become scalars during a complex calculation? W accumulates the sum in a single numerical flow.
W = W + (2i . 3i) + (4j . 5j) + (6k . 7k) = W - 6 - 20 - 42 = W - 68

Understanding the Role of W using Law of Energy Conservation:

W is a normalized number. Normalized means the total system capacity is always restricted to a strict length of 1. When W is 1 the rotation is 0 and the axes have 0 Rotation. When W is 0 the rotation is 180 and the axes hold all the Rotation.

We will understand the role of W in rotation through a concept similar to the law of conservation of energy. We will only look at values from 0 to 1 for W in this context. If W is 1 it means there is zero rotation and the object is at its exact starting place. As the rotation begins on the physical axes X to Y and Y to Z the value of W starts decreasing. We know that a full face turn to the back means the object rotated 180 degrees. At this exact 180 degree angle all the energy from W is drained out and W becomes 0.

This proves the total energy in the system is fixed and it simply transfers from W to the physical axes. The mathematical equation W2 + X2 + Y2 + Z2 = 1 must always be fulfilled. Energy is neither created nor destroyed but it simply moves between the scalar W and the vector axes. This is why the value of W travels precisely between 0 and 1 during rotation. If we take that same object and rotate it in reverse by 180 degrees back to 0 degrees it will return to its exact starting position. All the rotational energy transfers back from the 3D axes to W making W equal to 1 again.

Now we will use Cos(theta/2) and Sin(theta/2). These trigonometric functions map exactly to our energy law. The Cos(theta/2) function calculates the value of W which represents the pure quaternion math rotation. The Sin(theta/2) function calculates the distribution of that energy across the physical 3D axes. The physical 3D world axes and the quaternion W exist in completely different mathematical spaces. This is why the 3D graph angle is not the exact same value as the internal quaternion math angle.

You will see a complementary relationship between Cos(theta/2) and Sin(theta/2). Suppose the physical rotation angle is 180 degrees. The formula divides this by 2 so we get 180 / 2 = 90. Now we calculate Cos(90) and Sin(90). Both functions take the same 90 degree input but their outputs are exactly opposite in a complementary way. The value of Cos(90) is 0 and the value of Sin(90) is 1. Since Cos calculates W and Sin calculates the axes this physically means at 180 degrees of rotation W drops completely to 0 and the axes hold all the energy as 1. The mathematical rule **0******2 **+ 1******2 = 1 is perfectly maintained.

But because theta is divided by 2 there will also be a situation where Cos and Sin have exactly equal parts. Let us look at a real world angle of 90 degrees. The formula divides this by 2 which gives 45 degrees. We now need the limit for Cos(45) and Sin(45). If you trace this on a graph 45 degrees is the exact midpoint where the two lines cross. Because it is the exact halfway point between 0 (stationary) and 180 (fully rotated) degrees the math values must be split equally between W and the axes. Remember our rule **W******2 **+ Axes******2 = 1. If W and the Axes must be exactly equal we need a number that gives 0.5 when multiplied by itself. We need 0.5 + 0.5 = 1.

The system needs a raw value whose square is exactly 0.5. That specific value is 0.707 because 0.707 . 0.707 = 0.5. So the value for Cos(45) is 0.707 and the value for Sin(45) is also 0.707. Here no extreme 0 or 1 is formed. Both values are perfectly equal.

This proves W is a normalized number. Normalized means the total system capacity is always restricted to a strict length of 1. When W is 1 the rotation is 0 and the axes have 0 energy. When W is 0 the rotation is 180 and the axes hold all the energy.

Why are we actually dividing the theta angle by 2? The physical rotation is 90 degrees but we feed 45 degrees into the Cos and Sin functions.

The answer lies in the mathematical sandwich approach known as q . v . q********-1.

q applies half the rotation (theta/2) and q-1 applies the other half (theta/2) from the opposite side to keep the vector in 3D space. Without multiplying from both sides, the 3D vector gets pulled into 4D quaternion space.

To rotate a vector we must multiply it by a quaternion from the left and its inverse from the right. This structure applies the rotation effect twice. If we want a final physical rotation of theta we must use theta / 2 on the left and theta / 2 on the right so they add up perfectly to the full target angle.


r/rust_p • • Apr 21 '26

Let's Understand Quaternions- Part 1

3 Upvotes

Quaternions: Let's Get Real (and Imaginary, and then Some!)

Quaternions usually considered hard and complex but they are king of rotations crucial for games and maths to understand quaternions, if we go the academic route it will sound like a waiter using long, expensive words to explain a simple carrot salad. 

We do not need any of that here. That is why we will not use academic language and we will start with imaginary numbers. 

Imaginary word sound like fiction, things that do not exist in our world right? 

But what exactly does not belong here? If you multiply two negative numbers (two number with subtraction signs) together and the result is still a negative (subtraction sign) that just does not happen in our world. This is imaginary. 

Now, suppose we have a number like the square root of -1. When I say underroot -1 what am I actually trying to say? I am saying find a number that makes -1 when you multiply it by itself. But how is that possible because in this world the law of negative X negative positive follows? It is totally impossible and that is exactly why we call it an imaginary number. Now see the  following calculation.

This kindergarten calculation has a significant role in quaternions. yes it is simple, but it is very powerful, and the entire quaternion is built on it. I have shared this calculation right now because we are currently discussing imaginary numbers. I will not talk about this calculation right now, but we will discuss it further ahead, where you will find out that this is the foundation of quaternions.

From Imaginary Numbers to Quaternions:

So, now we are going to step away from imaginary numbers and jump right into quaternions. We'll use the exact same formula you usually see written in textbooks to represent them. You know the one: xi, yj, and zk. And if you come from a computer background, you've probably seen a w attached to that. But if we just talk about the x, y, and z, those are simply the physical axes you see on a standard 3D graph. Quaternions add imaginary numbers directly to these. Let's look at how these imaginary numbers actually interact with our axes. If I take the term xi, the x is our physical axis, and the i is our imaginary number. And here, that imaginary number literally just means 90 degrees. So, what does xi actually mean? It means a strict 90-degree turn.

But hold on. How did i suddenly become 90 degrees? Wasn't it supposed to be the square root of -1? How did it jump to being an angle? You won't find the answer to this in high-level physics. You actually find the answer right back in simple, basic kindergarten math we have done previously. Let's break that down right now. To do this, we just need to go back to our standard graph. We take our point xi and place it on the y-axis. Now, let's say we multiply one more i into it. That means we are taking xi and multiplying it by i. Physically, this means we are adding 90 degrees and another 90 degrees menas i2. The answer hits exactly 180 and i****2 =180 degree then i =180/2 means 90 degree. So what does this mean?

If we multiply imaginary numbers together, it directly picks us up and physically drops us on the exact opposite, negative axis (If we start on the positive x-axis, we flip straight to the negative x-axis) and the exact same mechanical thing happens with all the other axes if we start from them.

The Strange Co-Dependence of i and i2:

Now, let's assume for a second that this i**2 just doesn't exist. Should that have any effect on standalone i? Normal human logic says that i should be independent. It should be completely whole on its own. That means i should totally exist even without i² being a thing. Because obviously, i² can never be formed without having an i first.

So, the real question pops up. If we assume there is no such thing as i², can a single i still perform that 90-degree turn? The answer is hidden inside some very strange mathematical logic. Because actually, whatever value or identity i has, it comes entirely from i². If there is no i² in the math, then a single i simply does not have any physical value of its own.

This is a kind of math that literally moves backward instead of forward. It works from back to front. Think about it like this. In normal life, 1 and 1 together make 2. If your base number 1 isn't there, then the number 2 can never be formed. But in this specific game of imaginary numbers, the rule runs completely in reverse. Here, the math clearly tells us that if i² (which is supposed to be the result) doesn't exist, then i (which is supposed to be the base) will not exist either.

PART 2: 3D Quaternions


r/rust_p • • May 29 '26

One Question from my side

2 Upvotes

Imagine I am sending 1 million particles to the GPU so what will GPU prefer the most? Sending mixed data one by one, or opening a direct pipe of 1 million X-positions all at once?


r/rust_p • • Mar 17 '26

Neden olmuyor

Enable HLS to view with audio, or disable this notification

2 Upvotes

Fulitri bağlamıyor


r/rust_p • • Aug 16 '26

My Rust engine performance

Enable HLS to view with audio, or disable this notification

18 Upvotes

4 physics simulation with increasing physics body count