Shader Debugging Guide

Step through shaders and find bugs with AI assistance

Overview

Shader bugs are notoriously hard to debug—there's no printf, no breakpoints, and errors manifest as wrong pixels. rendara changes this with full shader debugging support across all major shading languages.

Supported Languages

Pixel-Level Debugging

The most common shader debugging workflow: click a pixel to debug.

  1. Capture a frame and open it in rendara
  2. Navigate to the draw call that renders your target
  3. Click on a pixel in the render target
  4. Select "Debug Pixel" from the context menu

rendara will show you:

Step-Through Debugging

rendara simulates shader execution on the CPU, allowing traditional debugging:

Controls

Setting Breakpoints

Click the gutter next to any line to set a breakpoint. Conditional breakpoints are supported:

// Break when this condition is true
fragColor.a < 0.5

Variable Inspection

Hover over any variable to see its current value. For vectors and matrices:

vec3 normal = normalize(v_normal);
// Hover shows: normal = vec3(0.577, 0.577, 0.577)

mat4 mvp = projection * view * model;
// Hover shows full 4x4 matrix with expandable view
Tip: Watch Expressions Add custom expressions to the Watch panel to track computed values like dot(normal, lightDir) or length(position - cameraPos).

AI-Powered Debugging

rendara's AI can explain shader behavior in plain English:

rendara ask "why is this pixel black?"

AI: The pixel is black because the fragment shader's lighting calculation
returns 0. Looking at line 45:

  float NdotL = max(dot(normal, lightDir), 0.0);

The normal vector (0, -1, 0) is pointing away from the light direction
(0, 1, 0), resulting in NdotL = 0. This causes the diffuse term to be
zero, and since there's no ambient term, the final color is black.

Suggestion: Add an ambient term or check if your normals are flipped.

Common Shader Bugs

NaN/Inf Values

Floating point errors propagate and cause visual artifacts:

// Common cause: division by zero
float attenuation = 1.0 / distance; // NaN if distance = 0

// Fix: add small epsilon
float attenuation = 1.0 / max(distance, 0.0001);

rendara highlights NaN/Inf values in red in the variable inspector.

Incorrect Normalization

// Bug: normalizing in vertex shader, interpolating, then using
// Interpolated normals are NOT unit length!

// Fix: re-normalize in fragment shader
vec3 N = normalize(v_normal);

Texture Sampling Issues

Shader Performance Analysis

rendara analyzes shader performance and suggests optimizations:

rendara analyze frame.rdc --shader-perf

⚠ PERFORMANCE: Fragment shader "pbr.frag" (40% of frame time)

  Issue: Texture sample inside loop (line 87)
  for (int i = 0; i < numLights; i++) {
      vec3 shadowCoord = getShadowCoord(i);
      float shadow = texture(shadowMaps[i], shadowCoord).r;  // ← Hot spot
  }

  Suggestion: Sample shadow maps outside the loop if coordinates
  are independent, or use texture arrays with single sample.

Live Shader Editing

rendara supports hot-reload of shaders without recapturing:

  1. Modify shader code in the built-in editor
  2. Press Ctrl+S to recompile
  3. See results immediately in the frame view
Iterate Faster Live editing means you can fix shader bugs and verify the fix without rebuilding your application or recapturing. This alone can save hours of debugging time.