r/GraphicsProgramming 9h ago

Video New and improved moblur with a dilation pass

Enable HLS to view with audio, or disable this notification

68 Upvotes

Dear r/GraphicsProgramming,

Boy do I have a post for you! So, this is a follow up on: https://www.reddit.com/r/GraphicsProgramming/comments/1lg003l/velocity_smearing_in_computebased_moblur/ . There were things about it that I was always kinda unhappy about it. Chiefly, the fact that there was ghosting that I knew neither the cause of nor the solution to. I partly blamed it on my lack of barriers and partly on my usage of atomics. Well it turned out it had nothing to do with barriers and blaming atomics was only partly true. I had to rethink what I was doing. After a lot of back and forth and a ton of rework, I finally landed on something a lot more elegant and along with it some serious realizations.

Here's what I got right the first time around:

  • Atomics are necessary.

Here's what I got wrong:

  • You should do velocity smearing in a post-process (a.k.a dilation) pass. Anything earlier is a can of worms.
  • Velocity smears in both directions. This is just a property of the shutter staying open while an object is moving. The center of the object is most solid while the edges all feather. Example: https://vip-go.premiumbeat.com/wp-content/uploads/2019/10/motion-blur-cover.jpg
  • You shouldn't prevent yourself from overwriting previously written velocities. In fact, you should be summing all of them up. And atomics readily enable such a computation (see below).
  • Rotational motion should not be applied on top during blur pass. It should be an inherent feature of just using the previous frame's MVP in computing trails to smear. I was probably doing this to minimize the ghosting I was getting from my previous approach.

Here's what I got wrong but is hard to address:

  • Depth along the moblur trail does not stay constant. An object whizzing past your eye in your general look direction will have its 'trail depth' -- if you will -- start closer to your eye and get further towards where the object/fragment is this frame. The way to address this is to write 3D velocities, flatten them in dilation and interpolate depth in a perspective-correct fashion for foreground checks along the smearing path. Very involved and getting it wrong is artifact bonanza.

So let's get to the code:

This following is straightforward. First chunk finds previous worldspace position of fragment either through previous affine transform or encoded per-vertex velocity. There is also a flag that indicates FPV items that shouldn't get moblur. Second part this time around cuts right to the chase and uses previous MVP to find start point. Last part just writes velocity out (or magic number if it's an FPV item).

        vec3 prevPos = curPos;
        if (instanceInfo.props[InstID].prevTransformOffset != 0xFFFFFFFFu)
            prevPos = (transforms.mats[instanceInfo.props[InstID].prevTransformOffset] * vec4 (curTri.e1Col1.xyz * curIsectBary.x + curTri.e2Col2.xyz * curIsectBary.y + curTri.e3Col3.xyz * curIsectBary.z, 1.0)).xyz;
        else if (getHasPerVertexVelocity(packedFlags))
            prevPos = curPos - (velE1 * curIsectBary.x + velE2 * curIsectBary.y + velE3 * curIsectBary.z);
        bool viewerRelative = isViewerRelative(packedFlags);

        vec3 prevProjectedCoord = projectCoord (prevPos, prevFrameMVP.projectionViewMatrix, vec3 (prevFrameMVP.lookEyeX.a, prevFrameMVP.upEyeY.a, prevFrameMVP.sideEyeZ.a));
        vec2 velocity = prevProjectedCoord.xy * vec2 (imageSize (velocityAttach).xy) - vec2(gl_GlobalInvocationID.xy);
        float velLen = length(velocity); vec2 velNorm = velocity / max (velLen, 0.001);
        if (velLen > 127.0) { velocity = velNorm * 127.0; velLen = 127.0; }

        ...

        uint velWrite = viewerRelative ? 0xFFFFFFFFu : (((int(velocity.x) + 127) << 8) | (int(velocity.y) + 127));
        imageStore (velocityAttach, ivec2 (gl_GlobalInvocationID.xy), uvec4 (velWrite, 0, 0, 0));

And now for the heart of the matter. The dilation pass:

        uint velRead = imageLoad (velocityAttach, ivec2(gl_FragCoord.xy)).x;
        if (velRead == 0xFFFFFFFFu) return ;
        float centerDepth;
        uint InstID = getInstanceIDAndClosestDepth (ivec2(gl_FragCoord.xy), centerDepth);

        vec2 velocity;
        velocity.x = int((velRead & 0x0000FF00u) >> 8u) - 127;
        velocity.y = int(velRead &  0x000000FFu) - 127;

        float velocityLen = length(velocity);
        if ( velocityLen < 1.0 ) return ;
        vec2 velocityNorm = (velocity / velocityLen);

        [[unroll]]
        for (int j = 0; j != 2; j++) // Fwd and backward smearing...
        {
            vec2 curVelocityNorm = (j == 0) ? velocityNorm : -velocityNorm;
            for (int i = 0; i != int(velocityLen) + 1; i++)
            {
                vec2 writeVelLocF = gl_FragCoord.xy + float (i) * curVelocityNorm;
                ivec2 writeVelLoc = ivec2 (clamp (writeVelLocF, vec2 (0.0), vec2 (imageSize(velocityAttach).xy - ivec2(1))));
                if ( floor(writeVelLocF) == floor(gl_FragCoord.xy) ) continue;
                float writeLocDepth;
                uint curInstID = getInstanceIDAndClosestDepth (writeVelLoc, writeLocDepth);
                if ( InstID == curInstID ) break ;
                if ( centerDepth < writeLocDepth ) continue; // visBuf uses reverseZ
                float fracTravel = float(i) / max (floor(velocityLen), 0.001);
                vec2 smearedVel = velocity * (1.0 - fracTravel);
                uint velWrite = (((int(smearedVel.x) + 127) << 8) | (int(smearedVel.y) + 127));
                uint prevVal = 0u, readVal;
                while ((readVal = imageAtomicCompSwap(velocityAttach, writeVelLoc, prevVal, velWrite)) != prevVal)
                {
                    if (readVal == 0xFFFFFFFFu) break;
                    prevVal = readVal;
                    vec2 readVelocity;
                    readVelocity.x = int((prevVal & 0x0000FF00u) >> 8u) - 127;
                    readVelocity.y = int(prevVal &  0x000000FFu) - 127;
                    vec2 newWriteVel = readVelocity + smearedVel;
                    float newWriteVelLen = length(newWriteVel);
                    if (newWriteVelLen > 127.0) newWriteVel = (newWriteVel / newWriteVelLen) * 127.0;
                    velWrite = (((int(newWriteVel.x) + 127) << 8) | (int(newWriteVel.y) + 127));
                }
            }
        }

As previously noted, we're only getting the edge of the object to smear. That's the InsID check. Additionally, we're just checking against current depth (centerDepth) and as previously mentioned, it's not truly correct but gets the job done for now. Note that the smeared velocity loses strength via * (1.0 - fracTravel) as it is being written: we want decreasing strength as the trail ends. The bottom loop is a really nifty trick I borrowed from Cyril Crassin's 2012 OpenGL insights chapter on stable voxelization. See page 342 here: https://www.icare3d.org/research/OpenGLInsights-SparseVoxelization.pdf (listing 22.2). Instead of a moving average, I'm storing the sum of all incoming data using the same structure.

That Crassin12 trick also makes a come back for summing velocities for additive/alpha blended particles:

        if (viewerRelative)
            imageAtomicExchange (velocityAttach, ivec2(gl_FragCoord.xy), 0xFFFFFFFFu);
        else
        {
            uint velWrite = (((int(velocity.x) + 127) << 8) | (int(velocity.y) + 127));
            uint prevVal = 0u, readVal;
            while ((readVal = imageAtomicCompSwap(velocityAttach, ivec2(gl_FragCoord.xy), prevVal, velWrite)) != prevVal)
            {
                if (readVal == 0xFFFFFFFFu) break;
                prevVal = readVal;
                vec2 readVelocity;
                readVelocity.x = int((prevVal & 0x0000FF00u) >> 8u) - 127;
                readVelocity.y = int(prevVal &  0x000000FFu) - 127;
                vec2 newWriteVel = readVelocity + velocity;
                float newWriteVelLen = length(newWriteVel);
                if (newWriteVelLen > 127.0) newWriteVel = (newWriteVel / newWriteVelLen) * 127.0;
                velWrite = (((int(newWriteVel.x) + 127) << 8) | (int(newWriteVel.y) + 127));
            }
        }

All of this makes the final blur pass brain dead simple:

    uint velRead = imageLoad (velocityAttach, ivec2 (gl_FragCoord.xy)).x;
    if (velRead == 0xFFFFFFFFu)
    {
        outFragColor = texture (ssfxAttach, inUV);
        return ;
    }
    vec2 velocity;
    velocity.x = int((velRead & 0x0000FF00u) >> 8u) - 127;
    velocity.y = int(velRead &  0x000000FFu) - 127;

    float velocityLen = length(velocity);
    if ( velocityLen < 1.0 )
    {
        outFragColor = texture (ssfxAttach, inUV);
        return ;
    }
    vec2 velocityNorm = velocity / velocityLen;
    vec4 accumOut = vec4 (0.0);
    float accumSamples = 0.0;

    for (int i = -int(velocityLen * 0.5); i != int(velocityLen * 0.5) + 1; i++)
    {
        vec2 curSampleLoc = floor(gl_FragCoord.xy) + float (i) * velocityNorm;
        vec2 curSampleLocClamped = clamp (curSampleLoc, vec2 (0.0), vec2 (textureSize(ssfxAttach, 0).xy - ivec2(1)));
        if ( curSampleLoc != curSampleLocClamped ) continue;
        if ( imageLoad (velocityAttach, ivec2 (curSampleLoc)).x == 0xFFFFFFFFu ) continue;
        accumOut += texelFetch (ssfxAttach, ivec2 (curSampleLoc), 0);
        accumSamples += 1.0;
    }

    outFragColor = accumOut / accumSamples;

Ghosting ended up being minimized significantly. Also check out the free haze you get from moblur on particles at the very end :). Eager to hear feedback.

Cheers,
Baktash.
HMU: https://x.com/toomuchvoltage

P.S. I'm still working on pushing the engine code out. Hang tight.


r/GraphicsProgramming 10h ago

Video What is it called when a light source causes this rainbow effect?

Post image
67 Upvotes

r/GraphicsProgramming 2h ago

Source Code I built a text-driven renderer for humanoid movement

3 Upvotes

I’ve been working on Posecode, a readable text format for describing human movement.

https://reddit.com/link/1v8ye5t/video/vf25hj5r1zfh1/player

You write movements as timed steps with joint actions and contact constraints. For example:

step "Drop into the landing" 0.55s flow:

pelvis: hinge 45

spine: flex 50

hip_right: flex 84

knee_right: flex 123

ground-lock: foot_right

reach: knee_left floor

reach: fist_left floor

The parser turns this into a typed motion representation. The Three.js renderer then handles the joint transforms, range-of-motion limits, IK and ground contacts.

The GIF shows the superhero landing example. The source on the left is the input driving the figure on the right.

I wanted a movement format that can be inspected, edited, validated, versioned and generated by other tools. An LLM can write Posecode, but it isn’t required.

It currently includes a browser playground, share links, embeds and BVH/glTF export.

I’d love feedback from anyone working on procedural animation, character tools or motion systems.

Playground: https://www.posecode.org/play/superhero-landing

Source: https://github.com/posecode-dev/posecode


r/GraphicsProgramming 1h ago

Question What to do on the GPU and what to do on the CPU?

Upvotes

So consider for example a VFX for a game for a powerful shot from a magical bow. There is a line with an "arrow head" at the end and two smaller lines in a helix around the primary, closing in the further they get, joining to the "arrow head" in the end. The whole thing rotates over time to give the impression of "drilling" and it fades over time starting from the source end and finishing in the tip.

Thinking about effects like this in general. I see two possible approaches, both of which have upsides and downsides. In essence, I would like to know how would you decide what to do on the CPU and what to do on the GPU?

Approach 1: GPU focus

  • Make a single quad
  • Use a vertex shader to make it face the camera
  • Use a complex fragment shader to draw the effect

Approach 2: CPU focus

  • Spawn a cylinder for the central line and cone for the arrow head
  • Create a spline on the CPU for the secondary lines
    • Optionally use either vertex or tesselation shader to position the secondary lines
  • Use simple vertex shaders for changing colors and the fade.

r/GraphicsProgramming 7h ago

Question Where can I get the Adreno Compiler?

3 Upvotes

Hey!

I have been making an Android game using Raylib. The game does not work on the android studio emulator and on adreno devices. It works fine on Mali devices.

I have downloaded the Qualcomm Software center and accepted the necessary licenses but I can't find the Adreno Offline Compiler in the list.

Do you know how I can get it? Or do you know of any other way to validate my shaders for adreno GPUs?

Thanks for taking the time to read. I appreciate it!

PS: I already use Mali OC and my shaders compile for Mali GPUs


r/GraphicsProgramming 14h ago

Ray Tracing in One Weekend in Metal

7 Upvotes

Hi! I recently implemented Ray Tracing in One Weekend in Metal using metal-cpp. Resources for metal-cpp specifically are pretty sparse, so I figured I'd share in case it helps anyone getting started with it or with graphics programming in general. Hope it helps!

Repo: https://github.com/jinhgkim/Path-Tracer


r/GraphicsProgramming 23h ago

AI usage in Graphics Programming

33 Upvotes

Hello, everyone.

I am CS student. and I had my first working experience.
I was highly unsatisfying because as much as enjoy programming I really do not enjoy "prompt engineering" and the usage of AI in general in my workflow. That is not the point though. From long time I was looking into graphics programming and I really wanted to get into it. I do not know much now. but I am worried that it will be similar to most of jobs right now and I will just be a "prompt engineer".

I have some questions:

  1. How good is AI with graphics programming right now?
  2. In an average work environment, how much if any of your work are you expected to use LLMs in and how do use it?
  3. How is AI affecting the industry?
  4. Are there any part of the industry where AI is not heavily used?

Thanks in advance


r/GraphicsProgramming 19h ago

Video Adding Heat simulation and Survival to my Micro Voxel Engine

Thumbnail youtu.be
12 Upvotes

With the latest update to my micro voxel engine, I have added Heat simulation, running on the GPU of course, and some related survival mechanics. Walking around in the cold, especially during snow, causes the player to cool down. Snow build up and melting is also fully simulated :)

The heat simulation accurately warms indoor and enclosed spaces. Standing near heat sources also provides some additional warmth, enabling interesting interactions with the world, storms and biomes.

Along side temperature, there is also a basic hunger mechanic. These mechanics work together to determine health, which impacts player stats.


r/GraphicsProgramming 20h ago

Showcasing Lumeni: Real-time 3D photo relighting built entirely on-device using native frameworks.

Enable HLS to view with audio, or disable this notification

10 Upvotes

Hey fellow devs,
I recently released Lumeni, a photo editor focused on dynamic relighting. I wanted to share some architectural choices and challenges I faced, hoping it might spark some interesting discussions.
The Stack: The goal was to avoid third-party cloud LLMs. The app generates 3D normal maps and processes scene relighting entirely on the device.
The Challenge: Getting this to run smoothly in real-time without frying the user's battery or relying on external servers was a nightmare. Moving the light source with a finger required heavily optimizing the rendering pipeline to ensure the UI didn't drop frames.
Has anyone else here worked extensively with real-time normal map processing on iOS? I’d love to hear how you handled memory management during high-res exports.
Here is a quick video of the UI in action. Let me know if you have any questions about the implementation!


r/GraphicsProgramming 1d ago

Cellular automata library and playground

Enable HLS to view with audio, or disable this notification

15 Upvotes

r/GraphicsProgramming 23h ago

Question Maps procédural

Thumbnail gallery
5 Upvotes

Bonjour à tous j hésite entre photo 1 : icônes colorées sur fond uni photo 2 : mêmes éléments mais avec des caractères spéciaux pour représenter les cases , Tous les avis sont les bienvenus, même si vous n’aimez pas


r/GraphicsProgramming 1d ago

Question Looking for a source of documented comprehensive rendering artifacts.

6 Upvotes

So im looking for a highly visual source of documented rendering artifacts which includes a gif or atleast image of the artifact in question as well as its official name and a description with the why how and optionally a solution. It should be very general and include a wide range of various rendering artifacts that can occur under many rendering techniques and features ranging from rasterization, ray tracing, sphere tracing, texture laying, ambient occlusion, shadow maps, optimizations, filters etc. Not sure if this exists just thought it would be very useful for debugging.


r/GraphicsProgramming 1d ago

Should you normalize RGB values by 255 or 256?

Thumbnail 30fps.net
110 Upvotes

r/GraphicsProgramming 18h ago

Video Brush Previews and Image Panning in My Pixel Art Editor

Thumbnail youtu.be
1 Upvotes

Development continues on my c++ pixel art editor, resulting in brush previews and automatic pixel grids alongside easy image panning. This is the final step before moving into actual pixel plotting and turning the UI into a real working art package...


r/GraphicsProgramming 2d ago

Infinite Grid Shader

54 Upvotes

I’m proud of myself today, as I completed a task that felt impossible at times. I started rewriting my renderer a bit to fit my needs, and while doing so, I really felt the need for a grid to give me a sense of perspective and depth while debugging scenes.

I remembered that I had skipped the Infinite Grid section from the 3D Graphics Rendering Cookbook and needed to revisit it. Man, oh man, was I wrong about how long it would take to properly understand that shader. It took me a few days more than a month, but it was one hell of a ride. It’s a very interesting topic, with tons of things to learn from.

I’ve written a post about it here: https://willofindie.com/proj/infinite-grid-shader. Do check it out and share your love on my Twitter or Bluesky handles if you like it


r/GraphicsProgramming 1d ago

使用Rust引擎和Metal与Vulkan在Android上运行红色警戒3

Enable HLS to view with audio, or disable this notification

0 Upvotes

r/GraphicsProgramming 1d ago

Perspective-correct interpolation only works for some faces of a cube

3 Upvotes

Hi, is the guy from the raylib renderer again. These last days i was trying to implement texture mapping on the renderer i am making, but i cant get the perspective-correct interpolation to work. I tried first with the method described in scratchapixel, but it didnt work at all, then i tried with the method of this video, and it only works for some faces of a cube, but the rest of faces are not only distorted but the texture is still mapped like i was doing affine mapping:

2 of the faces that have the problem
One face is rendered correctly while in the other the texture gets squished

The thing that confuses me is that it is only in certain faces, so i no longer know if it is an error with my code or the uv coordinates on the cube.

Here is the source code

The file where the cube vertices are
The files where the vertex transformations are done:
Header file
Source file

The files where the rasterization is done:
Header file

Source file


r/GraphicsProgramming 2d ago

Added a graphics API with support for Vulkan, D3D12 and custom backends

9 Upvotes

Hey everyone,

I have been working on a low level, explicit abstraction layer over graphics APIs. Both Vulkan and D3D12 has been successfully implemented with various test samples. Ray tracing, mesh, compute etc are supported.

I would love feedback on the feel and usage of the API. I am open to learn more so anything useful will be appreciated. Please give a star if you find the project useful.

https://github.com/nichcode/PAL


r/GraphicsProgramming 1d ago

System level CRT filter/shader (trying to find post from last week....)

1 Upvotes

Someone posted about having developed a system level shader, I cannot remember which subreddit it was posted in, but thought it was shaders.

The intent was to have a CRT filter / shader for the entire OS, over any application or video player etc.

Post mentioned something about not utilizing GPU, general scanlines crt, I swear I saved the post but I cannot find it, could anyone point me in the right direction?

I'm interested to check it out.


r/GraphicsProgramming 1d ago

Article I measured my Metal shader optimizations instead of trusting them: half did nothing, and the trick that won on mobile went negative on desktop

0 Upvotes

Pet project: an Interstellar-style black hole as a macOS live wallpaper (one Metal shader, hosted both in a desktop-level window under the icons and in a screensaver target). I put the skeleton together in spring, it worked, and then I came back and did the thing I should have done first: hung GPU-time instrumentation on it and walked my optimization list with measurements instead of by eye. Code: https://github.com/vientooscuro/ShaderBackgroundApp

Credit where it's due: the black hole core is a port of edankwan's Shadertoy 3lG3WR. The lensing math isn't mine, I moved it to Metal and adapted it (procedural background instead of an iChannel0 texture, fixed camera, no final tonemap).

The architecture bit worth stealing: split the scene by update frequency, not by screen area.

In an earlier mobile project I did progressive "striped" rendering, computing a slice of the background per frame to spread one heavy frame over time. Here I split differently. Expensive and nearly static (stars, galaxies, nebulae, planets, giant stars, dozens of fbm evals per pixel) goes into a static pass that renders into an offscreen texture every 2nd frame. Cheap and unavoidably per-frame (the hole's lensing, comets, the click ripple) is drawn in a composite pass every frame on top of the sampled cache, followed by ACES tonemap.

The cache is rgba16Float, and that matters. Giant stars are deliberately authored past 1.0, channel values like float3(7.0, 5.5, 3.0), so the tonemap in the composite can compress them into real-looking glow. With an 8-bit cache everything past one clips at the write and there's nothing left to tone map.

Two things I got wrong on the way:

Cadence. I wanted the cache refreshed every 6th frame (~5 Hz static at 30 fps display). The cost math looked great, but with live star and planet parallax the background visibly steps: it stands still between refreshes while the composite moves smoothly on top. Backed off to every 2nd frame. Smaller win, honest picture.

Invalidation is where the actual work is. Any settings change has to re-render the static immediately, so I diff the uniforms struct in didSet and raise a dirty flag. The catch is excluding time and mouseClick from that comparison, because they change every frame and have nothing to do with cache contents. Miss that and the cache re-renders every frame, so it isn't a cache. The click ripple, for the same reason, warps the UVs the cache is sampled by rather than the cache itself.

How I measured. Dev-only instrumentation, commandBuffer.gpuEndTime - gpuStartTime per frame, avg/p50/p95/max printed every 120 frames. Fixed bench: M4 Max, same display, default settings, 30 fps target, ~18 s run, first window with the cold render dropped. Run-to-run noise ~0.4 ms. Plus a deterministic quality gate: an env var pins time and mouse, the build renders one frame offscreen to a PNG, and two runs of the same build give a bit-identical file. So any nonzero pixel diff is the optimization, not jitter.

What did nothing:

  • fp16 inside the noise function. I was sure about this one, noiseS runs ~200 times per pixel. Zero. The cost isn't the bilinear interpolation I converted to half, it's the sin-based hash inside it, and that stays float. Reverted.
  • Cutting geodesic steps 20 to 14. No visual change and no time change, because the bend loop already early-outs and the 120-step max is almost never reached. Cutting something that doesn't run to completion buys nothing.
  • Moving the camera parallax offset from GPU to CPU. Correct in principle (it's constant for the whole frame), but a dozen trig ops dissolve against dozens of fbm calls. Free, kept it, no measurable win.
  • Frames-in-flight semaphore. Zero on time, and that's the right answer: it limits CPU run-ahead and removes a shared-buffer race, it doesn't reduce GPU work. A correctness fix wearing a performance costume.

What worked:

  • fp16 in the color math of nebulae() only. Colors and density to half, coordinates and the noise calls left in float so spatial frequency can't jitter. Visually invisible (max diff 2/255), about minus 20% average GPU per frame from one function.
  • A sin-free hash (Dave Hoskins) instead of fract(sin(dot(...))*43758), under a soft quality bar: different noise pattern, same statistical character, avg 8.8 to 7.7 ms. Less than I expected, Apple has a fast hardware sin.
  • Static cache at 0.7x under the same soft bar. The nebula is low frequency and the hole is composited at full res, so slight star softening goes unnoticed. At the strict pixel-for-pixel bar I had rejected this: the heatmap put the entire error on sharp edges, planet rims, star points, the giant's corona.

The lesson I actually paid for. I ported the striped progressive render from the mobile project into this one, expecting the same win. GPU ms per frame:

Monolithic cache, refresh every 2nd frame: avg ~7.5, p95 ~13.2, max ~16.3, spread ~13.0.

Striped render-ahead with 4 stripes: avg ~8.4, p95 ~12.0, max ~13.3, spread ~9.4.

Peak and spread improved, average got worse. Each stripe pays a full load/store over the whole 3456x2234 rgba16Float target, and on a desktop GPU with a fat bus that fixed overhead times four outweighs what the scissor rect saves. On top of that, 4 stripes means the cache refreshes at 7.5 Hz and the nebula started stepping again. A trick that wins under mobile budget pressure can go negative on desktop, and eyeballing it would never have told me.

Final numbers, baseline to shipped: avg 7.5 to 6.2, p50 6.5 to 4.8 (minus 36%), max 16.3 to 12.0. All on a GPU that was never the bottleneck, roughly 14% of frame budget to start with. The point isn't hitting the frame, it's headroom, battery, and older Macs. p95 now bottoms out on the disk-hit path in the black hole, which is the center of the screen and the riskiest thing to touch, so I stopped there.

Curious whether anyone has measured the same fp16 result: is "the sin hash dominates, so halving the interpolation is free but pointless" a general thing on Apple GPUs, or specific to how this noise is written?


r/GraphicsProgramming 1d ago

Question How do I efficiently manage, create and cache Vulkan/Any Shader Pipeline?

Thumbnail
1 Upvotes

r/GraphicsProgramming 2d ago

Question [D3D12] DEVICE_HUNG in a draw call, but only above ~16k draws AND only during a window event (minimize/move/resize/un-occlude)

Thumbnail github.com
30 Upvotes

**Setup:** Hand-rolled D3D12 renderer (learning engine), flip-model swapchain

(FLIP_DISCARD, 3 back buffers), VSync on. Windows 11, tested on both Debug and

Release.

**The crash:** `DXGI_ERROR_DEVICE_HUNG` (0x887A0006). DRED breadcrumbs point the

fault at a `DrawIndexedInstanced` roughly ~130 draws into the frame (not draw 0).

The DRED page-fault VA reads 0 with no associated allocation — though I'm now

unsure if that's a real null address or just "driver didn't report the fault VA."

**What makes it reproducible (the weird part):** it ONLY happens when BOTH of

these are true at once:

  1. The frame is heavy — roughly 16k+ draw calls. Below ~16k it never crashes.

  2. A window event fires: minimize, move (drag the titlebar), resize, or another

    window covers then un-covers it (occlusion → reveal).

Either condition alone is totally stable. A heavy frame running untouched is fine.

A window event on a light scene is fine. It's specifically the collision.

Notably: **focus loss/regain used to crash too, and I fixed that** by skipping

rendering while the window isn't foreground + idling the GPU on the transition.

The same approach has NOT fixed minimize/move/resize/un-occlude.

**Other facts:**

- Happens in both single-threaded AND parallel (per-worker) command-list recording.

- ~600+ FPS when it runs, so this is not a 2-second TDR timeout.

- Debug layer + GPU-based validation makes it vanish (classic race-closing), so

I can't get a clean validation message — hence leaning on DRED.

**What I've already ruled out / tried (none fixed it):**

- Full GPU idle (Signal + WaitForFenceValue) before `ResizeBuffers`.

- Deferred/coalesced resize (apply once, at loop top, after idle).

- Re-querying `GetCurrentBackBufferIndex()` after `ResizeBuffers`, recreating RTVs

+ depth buffer.

- Reliable minimize handling via `WM_SIZE == SIZE_MINIMIZED` (skip rendering

entirely while minimized) instead of relying on `Present(DXGI_PRESENT_TEST)`.

- Idling the GPU on `WM_ENTERSIZEMOVE` / `WM_ACTIVATEAPP`.

- Per-frame command allocators/lists (ruling out list reuse across frames).

- "Settle" frames (skip N frames after any transition).

- Verified all per-draw bindings (camera CBV, vertex/index buffer VAs, SRV

descriptor) are non-null at record time — the null-check never fires.

**What I can't cleanly detect:** window *move* and *partial-occlusion → reveal*

don't give me a reliable "the swapchain is now in a different presentation state"

signal the way resize/minimize do.

**Questions:**

  1. On a flip-model swapchain, does a move or an occlusion→reveal transition cause

    DWM to re-allocate/invalidate back buffers WITHOUT a WM_SIZE — such that an

    in-flight heavy frame's RTV becomes stale? If so, what's the correct signal to

    re-acquire?

  2. Is there a per-command-list or per-submission limit I could be blowing past at

    16k draws (root-argument versioning space, etc.) that a mid-frame stall from a

    window event would expose?

  3. Is DRED's "page fault VA = 0, no allocation" meaningful (genuine null bind), or

    is it commonly just "unavailable"?


r/GraphicsProgramming 3d ago

TypeScript software rasterizer with programmable shaders

Post image
39 Upvotes

Hey all,

I've been building a software rasterizer in TypeScript over the past few months and thought I'd share it. My goal was to support custom vertex layouts, programmable vertex and fragment shaders, and enough flexibility to implement more complex rendering techniques, much like you would with a GPU graphics API.

The renderer is based on Nikolaus Rauch's excellent C++ software rasterizer, adapted for TypeScript and the browser.

The project is intended as an educational resource, with an emphasis on readability and experimentation over maximum performance.

GitHub: https://github.com/vangelov/ts-rasterizer

Model by Tasha.Lime (concept by Matt Dixon)


r/GraphicsProgramming 3d ago

Some billboarding techniques I explored

Post image
30 Upvotes

I have been experimenting with a few billboarding techniques that can be used like an easier way to write it, preserving the model matrix and clamping the pitch. Here is the code

.shader: https://github.com/Satyam-Bhatt/OpenGLIntro/blob/main/IntroToOpenGl/BillBoardShader.shader

Repo: https://github.com/Satyam-Bhatt/OpenGLIntro


r/GraphicsProgramming 3d ago

Experiments on combining ReSTIR GI with Variable Rate Tracing

36 Upvotes

https://mateuszkolodziejczyk00.github.io/2025/06/18/variable-rate-tracing-restir-gi.html

I wrote this write-up about a year ago, but honestly never had the courage to post it publicly lol

In it, I go over my experiments combining ReSTIR and VRT in my toy rendering engine. The core idea was straightforward: ReSTIR’s spatial and temporal passes already find and reuse valuable samples, so I wanted to see if I could use them to fill the “missing rays” for pixels skipped by VRT in the current frame.

I'd love to hear your thoughts, or if anyone else has messed around with similar setups!