DEV Community

orca_forge
orca_forge

Posted on Originally published at forge.workstyle.tech

Borrowed an H100 but couldn't draw a single frame — Why compute GPUs and rendering GPUs are different beasts

📝 Originally published (in Japanese) at forge.workstyle.tech.

When someone says, "You need a GPU," they're actually referring to two distinct scenarios:

  • Compute: Training and inference. As long as CUDA works, it's fine.
  • Rendering: 3D rendering, gaming, video production. Requires OpenGL / Vulkan / EGL.

Even within NVIDIA's GPU lineup, some models can only handle one of these tasks.

I was building an unmanned AI avatar streaming system where a 3D avatar would live-stream on video platforms 24/7. During this process, I hit the "compute vs. rendering" wall twice: once with CPU rendering and once with MIG. It seemed like a resource issue, but in reality, it was a missing functionality problem.

Below is a GPU selection procedure for rendering tasks and a record of my failures, based on actual measurements using rented cloud GPUs.

What I Was Trying to Achieve

The setup was simple:

Headless Chromium (rendering the 3D avatar using WebGL)
  → Capture screen and audio
  → Encode to H.264 + AAC using ffmpeg
  → Stream to the platform via RTMP
Enter fullscreen mode Exit fullscreen mode

The avatar would speak using an LLM and TTS (text-to-speech), rendered in the browser, encoded, and streamed. The biggest challenge was deciding where to run the "headless Chromium for 3D rendering" part.

Failure 1: CPU Rendering (SwiftShader) Can't Be Saved by Lowering Resolution

Initially, I assumed the renderer would run solely on the CPU. Chromium has SwiftShader, a CPU-based WebGL backend, so WebGL "works" without a GPU.

It did work, but just barely. While plain WebGL ran at 60fps, the 3D avatar scene (toon shader + skinning) managed only about 4fps.

Typically, you'd try lowering the resolution to reduce the load. I did. Whether at 720p, 540p, or 360p, it stayed at 4fps. Since the performance didn't scale with resolution, the bottleneck wasn't pixel processing but scene processing itself. My fallback plan of "downgrade to 540p / 24fps if it's too heavy" was useless from the start.

CPU rendering was a no-go. I needed a GPU.

Failure 2: MIG Slice "Allocated" but "Couldn't Render"

My cluster had an H100, divided into 1g.10gb slices using MIG (Multi-Instance GPU) for LLM and speech inference. MIG splits a single GPU into multiple instances, great for packing inference services. I thought, "If I borrow one slice, there's no extra cost," and tried running the renderer there.

Here’s what happened with the test Pod:

  • Pod scheduled successfully (slice was available)
  • nvidia-smi inside the Pod showed the H100 and MIG devices
  • Chromium's WebGL still fell back to SwiftShader

It’s natural to think, "Maybe the slice isn’t enough," but that wasn’t it. No matter how I configured Chromium’s flags, whether using --use-gl=angle with egl or vulkan, the result was the same. Checking Vulkan’s ICD (driver registration info), it was empty.

The issue was NVIDIA's official spec: MIG is compute-only and doesn’t support graphics APIs.

This isn’t a performance issue but a functionality one. No matter how many slices you stack, even the largest 7g.80gb, WebGL won’t render a single frame on the GPU. It’s like trying to park in a spot that’s not a parking space but a compute-only room. Adding more slots won’t help.

"Disabling MIG Would Fix It" Is a Bad Idea

Theoretically, disabling MIG and using the entire H100 would enable graphics APIs. However, data center-focused compute GPUs lack units needed for rendering. For the H100:

  • Only 2 graphics TPCs (architecture stripped of rendering units for AI compute)
  • No NVENC (hardware encoder)

Even if graphics APIs worked, rendering performance would be poor, and without H.264 hardware encoding, streaming benefits would be minimal. Operationally, disabling MIG while all slices were in use by inference services was impossible.

Catalog specs like CUDA cores and VRAM don’t reveal this. The lesson here: "Expensive GPUs don’t always do everything." The H100 is for compute, not rendering.

Solution: Rent a Rendering-Capable GPU from the Cloud

Finally, I moved the renderer to a cloud GPU capable of rendering. For rendering, choose GPUs designed for graphics, like L4 / T4 / RTX series. An RTX 4000 Ada / RTX 2000 Ada ($0.24–0.28/hour) easily handled 720p30. More expensive isn’t always better.

Here’s the final setup:

Task Location Reason
LLM, TTS, Application On-prem cluster (H100 MIG) Compute tasks. MIG’s strength
Browser Rendering, Encoding Cloud rendering-capable GPU Requires graphics APIs

Treat compute and rendering GPUs as separate inventories. This was the biggest design change.

Ensuring the Container Can See the GPU

Having a GPU allocated doesn’t mean graphics drivers are accessible. Even with a rendering-capable GPU, container settings can hide drivers. Check these:

  • Set NVIDIA_DRIVER_CAPABILITIES=all in the container (default excludes graphics libraries)
  • Ensure EGL ICD (10_nvidia.json equivalent) is present. Use eglinfo to list NVIDIA EGL devices
  • Vulkan ICD (icd.d/) isn’t empty
  • libglvnd is installed

Without these, applications will see "no NVIDIA implementation available."

Don’t Trust "It Works"

This is the most critical lesson.

Headless Chromium silently falls back to CPU rendering (SwiftShader) if GPU rendering fails. No errors, no warnings. It starts, gets a WebGL context, and displays the screen—just slowly. If you assume "it’s working," you won’t notice until just before production.

Always verify from the output side:

Check Method CPU Fallback GPU Rendering
GL_RENDERER string SwiftShader NVIDIA
Measured 3D scene fps ~4 57–58

The 10x difference leaves no room for doubt. Create a minimal probe page (just GL_RENDERER and fps) to speed up troubleshooting. Since this stack fails silently, double-check with fps or GPU usage.

CPU Often Determines Capacity

Testing "how many streams per GPU" yielded a surprise:

Item Measured
Concurrent streams 4 (720p30, real-time)
GPU usage 26%
First to saturate CPU (16 vCPU)

The GPU had 3x capacity left. Why? The GPU only handles rendering.

Task Where
3D Rendering GPU
Frame Capture CPU / Transfer
H.264 Encoding CPU (if software)
Audio Mixing, Muxing, Sending CPU

So, NVENC availability directly impacts capacity. Hardware encoding frees up CPU, allowing more streams. When choosing a GPU, consider vCPU count and encoder presence.

Renting: Community vs. Secure, and the Stop Trap

Cheap GPU clouds come in two types: community (individuals/businesses renting out excess GPUs) and secure (operated by providers).

Community GPUs are cheaper but hit-or-miss. With the same image/settings, one host ran flawlessly for 10 minutes, while another crashed every 60–150 seconds.

Here’s how to use them and essential safeguards:

  • Development experiments → Community
  • Production streaming/long tests → Secure
  • Always include a mechanism to detect and replace bad hosts (even secure hosts fail)

Another operational trap: Stopping an instance can make the GPU unavailable for restart, as it’s allocated to others. You’d keep paying without being able to restart. Always Terminate when done.

Cost Calculation

$0.28/hour × 720 hours/month = $201.6/month (1 GPU)
$201.6 ÷ 4 streams = $50.4/stream ≈ ¥7,600/stream (150 JPY/USD)
Enter fullscreen mode Exit fullscreen mode

Easily overlooked costs:

  • Outbound bandwidth: 2.5Mbps streaming ≈ 27GB/day. Clouds with egress fees ($0.09/GB) add ~$10,000/month. GPU-specialized clouds often include bandwidth
  • Operational hours: 24/7 = ¥7,600/stream, 8 hours/day = ¥2,500/stream. A 3x difference. Treat it as a scheduling problem, not "always on"
  • Inference costs: If AI speaks 24/7, LLM and TTS costs can exceed rendering

Selection Checklist

When renting a GPU for rendering, check in this order:

  1. Is it MIG? → MIG doesn’t support graphics APIs. End evaluation
  2. Does it have graphics TPCs and NVENC? → Compute-focused GPUs (H100) lack these. CUDA cores alone don’t tell the full story
  3. Is vCPU sufficient? → Capacity is often CPU-bound
  4. Can the container see drivers?NVIDIA_DRIVER_CAPABILITIES=all, EGL/Vulkan ICD, libglvnd
  5. Is it really rendering on the GPU? → Verify with GL_RENDERER and fps. "It started" isn’t proof
  6. Can you replace bad hosts? → Essential for community, include for secure
  7. Is it set to Terminate, not Stop? → Check billing and restartability

Summary

  • GPUs are separate catalogs for compute and rendering. CUDA cores and VRAM aren’t enough to decide
  • MIG doesn’t support graphics APIs. It’s compute-only, so WebGL won’t work regardless of slices
  • Compute-focused GPUs (H100) have tiny graphics TPCs and no NVENC. Disabling MIG won’t help rendering
  • CPU rendering (SwiftShader) is impractical for 3D scenes and doesn’t scale with resolution
  • Failures are silent. Suspect CPU fallback, verify with GL_RENDERER and fps
  • Capacity is often CPU-bound. NVENC matters
  • Community GPUs are hit-or-miss. Assume you’ll need to replace hosts
  • Costs vary 3x with operational hours

"Rent the strongest GPU available" is the easiest way to fail. Cheaper, purpose-built cards can be faster and cheaper. Distinguish between "no space available" and "wrong room entirely." Mistaking missing functionality for insufficient resources is a common GPU pitfall.

Top comments (0)