UH-OH

It looks like you don’t have access to that feature yet

Contact sales to get upgraded to the full DevStudio experience.

UH-OH

It looks like you don't have access to that feature yet.

to select
to navigate
escto close
Introduction to the Chimera SDK
Chimera SDK Quick Start Guide
Chimera SDK Command Line Interface (CLI)
Tutorial: Using SDK as a Library
Tutorials & Model Demos
Model Demos
Model Demo: Llama-2 15M (Baby Llama-2)
Model Demo: QWEN3 8B End-to-End CGC and ISS Execution
Model Demo: QWEN3 Prefill All Decoders
Model Demo: DeepSeek-R1-Distill-Qwen-1.5B End-to-End CGC and ISS Execution
Model Demo: QWEN3 Single Decoder
Model Demo: Qwen2.5-0.5B INT8 Quantization Pipeline
Model Demo: ConvNeXt Detection
Model Demo: QWEN3 Prefill Decoder Validation
Model Demo: ConvNeXt Segmentation
Model Demo: Classifiers Zoo
Model Demo: Detectors Zoo - MMDetection
Model Demo: Segmentors Zoo - MMSegmentation
Model Demo: Pose Estimators Zoo - MMPose
Model Demo: Detectors3D Zoo - MMDetection3D
MODEL Demo: Optical Character Recognition (OCR) Zoo - MMOCR
Model Demo: YOLOv3 Object Detection
Model Demo: YOLOv4 Object Detection
Model Demo: YOLOv5 Detection
Model Demo: YOLOv5 Detection and Segmentation
Model Demo: YOLOR Detection
Model Demo: YOLOX End-to-End Detection
Model Demo: YOLOv7 Detection
Model Demo: YOLOv8 Detection
Model Demo: YOLOv8 Pose Estimation
Model Demo: YOLOP Detection and Segmentation
Model Demo: QAT Vision Transformer (ViT)
Model Demo: QAT Swin Transformer
Model Demo: Mediapipe Face Pipeline
Demo: DOOM Renderer on Chimera GPNPU
Model Demo: Mediapipe Hand Pipeline
Model Demo: Whisper Tiny (Encoder + Decoder)
Model Demo: L2CS Fine-Grained Gaze Estimation
Model Demo: ASVspoof2021 LA Anti-Spoofing (LFCC-LCNN-BiLSTM)
Model Demo: UNET Tumor Segmentation
Model Demo: DETR Encoder
Model Demo: FFNet Segmentation
Model Demo: Centernet Detection
Model Demo: RetinaNet End-to-End Detection
Model Demo: Blazepose Pose Estimation
Model Demo: Pose Resnet Human Pose Estimation
Model Demo: MaskRCNN Detection and Segmentation
Model Demo: Keypoint R-CNN
Model Demo: Faster R-CNN Detection
Model Demo: FCOS Detection
Model Demo: DDRNet Classificationls
Model Demo: PI0.5 End-to-End VLA Inference
Model Demo: BEVFormer End-to-End 3D Detection
Multicore Demo
Chimera LLVM C++ Compiler
Chimera SDK Licensing Policy Documentation
Glossary
Chimera Software User GuideTutorials & Model DemosModel DemosDemo: DOOM Renderer on Chimera GPNPU

Demo: DOOM Renderer on Chimera GPNPU


NOTE: The Jupyter Notebook below is included in the Chimera SDK and can be run interactively by running the following CLI command:

$ quadric sdk notebook

From the Jupyter Notebook window in your browser, select the notebook named /quadric/sdk-cli/examples/games/doom/doom.ipynb.


DOOM Renderer on Chimera GPNPU

QUADRIC DOOM

A full DOOM-style raycasting engine — DDA raycasting, textured walls, perspective-correct floors/ceilings, sprite compositing with z-buffering, and distance-based lighting — running as a single fused CCL (Chimera Compute Language) kernel on the Chimera GPNPU.

No matrix multiplies. No convolutions. Not a single GEMM. Every pixel rendered on-chip.

SpecValue
Resolution320x200 (native DOOM)
KernelSingle EPU_ENTRY, ~800 lines CCL
TexturesReal DOOM WAD textures, 256-color indexed palette
TargetQC-N (8x8 core array, 8 MACs/PE)
OCM footprint~880 KB (texture atlases + framebuffer)
Per-frame host traffic32 bytes camera state + sprite list

Why this matters

Carmack's renderer demands fixed-point integer arithmetic, data-dependent branching, irregular memory access patterns, and texture lookups through random-access tables — all under a hard frame-time deadline. This is the exact class of workload that traditional AI accelerator architectures cannot express. The Chimera GPNPU handles it natively because it was designed as a programmable computer, not a fixed-function accelerator.

Read the full blog post: Can Your NPU Run DOOM?


1. Setup

import os
import re
import numpy as np
from PIL import Image
from IPython.display import display

SRC_DIR = os.path.join(os.getcwd(), "src")
ASSET_DIR = os.path.join(os.getcwd(), "assets")

## Read resolution from the single source of truth
types_hpp = open(os.path.join(SRC_DIR, "types.hpp")).read()
if re.search(r"^\s*#define\s+DOOM_RES_224x168\s*$", types_hpp, re.MULTILINE):
    SW, SH = 224, 168
else:
    SW, SH = 320, 200
print(f"Resolution: {SW}x{SH} ({SW*SH:,} pixels, {SW//8}x{SH//8} = {(SW//8)*(SH//8):,} tiles)")
Resolution: 320x200 (64,000 pixels, 40x25 = 1,000 tiles)

2. Architecture

The renderer is a single EPU_ENTRY kernel with four phases:

DOOM Pipeline

  1. LoadmemCpy camera state + sprite data from DDR to OCM (On-Chip Memory). Texture atlases upload once and stay resident.
  2. Column prepass — Cast one ray per screen column via batched DDA (Digital Differential Analysis). 32 map cells fetched in a single rau::load::tiles call instead of 32 sequential round-trips.
  3. Tile loop — For each 8x8 tile (1,000 total at 320x200), render floor/ceiling, composite walls, then sprites. Per-PE predication handles pixels that straddle the horizon or a wall/floor boundary.
  4. OutputmemCpy the OCM framebuffer back to DDR.

Key architectural features exercised:

  • Batched RAU random-access loads — every PE needs a different texel from the atlas. rau::load::tiles fetches per-PE addresses in one flow.
  • Indexed color with palette in LRM — 8-bit texel indices keep atlases small; the 256-entry DOOM palette lives as a LUT in LRM (Local Register Memory), the per-PE register file.
  • SIMD predicationchimera::anyOf() early-exits sprite compositing when no PE in the tile overlaps a sprite. Per-PE ternary selects composite wall over floor, sprite over wall.
  • Hardware fixed-point divide — floor/ceiling depth uses math::fxDiv hardware divide intrinsic instead of software division.

Data-Movement Map

═══════════════════════════════════════════════════════════════════
 DOOM RENDERER — QC-N (8×8 core array, 64 PEs per tile)
 Screen: 320×20040×25 = 1,000 tiles
═══════════════════════════════════════════════════════════════════

 HOST (DDR)                          OCM (4MB)
 ──────────                          ─────────
 ┌──────────────────┐   memCpy       ┌──────────────────────────┐
 │ Camera State     │──────────────→ │ ocmState [1×8 i32]       │
 │ [32 bytes]       │  every frame   │                          │
 ├──────────────────┤                ├──────────────────────────┤
 │ Sprite Counts    │──────────────→ │ ocmSpriteCounts          │
 │ [25×40 i32]      │  every frame   │ [25×40 i32]              │
 ├──────────────────┤                ├──────────────────────────┤
 │ Sprite Data      │──────────────→ │ ocmSprites               │
 │ [4000×12 i32]    │  every frame   │ [4000×12 i32]            │
 ├──────────────────┤                ├──────────────────────────┤
 │ Map [16×16 i32]  │──────────────→ │ ocmMap [16×16 i32]       │
 ├──────────────────┤  first frame   ├──────────────────────────┤
 │ Wall Atlas       │──────────────→ │ ocmWallTex               │
 │ [1024×512 u8]    │  only          │ [1024×512 u8] = 512KB    │
 ├──────────────────┤                ├──────────────────────────┤
 │ Flat Atlas       │──────────────→ │ ocmFlatTex               │
 │ [384×256 u8]     │                │ [384×256 u8] = 96KB      │
 ├──────────────────┤                ├──────────────────────────┤
 │ Sprite Atlas     │──────────────→ │ ocmSpriteTex             │
 │ [576×256 u8]     │                │ [576×256 u8] = 144KB     │
 ├──────────────────┤                ├──────────────────────────┤
 │ Framebuffer      │ ←────────────  │ ocmFb                    │
 │ [200×320 u16]    │  memCpy out    │ [200×320 u16] = 128KB    │
 └──────────────────┘                └──────────────────────────┘

 LRM (per-PE registers)
 ──────────────────────
 ┌──────────────────────────────────┐
 │ paletteCache [256 × i32]        │  DOOM palette as LUT
 │ localCols [40 × 10 × i32]      │  column prepass results
 │ camera state (px,py,dir,plane)  │  qVar registers
 └──────────────────────────────────┘


═══════════════════════════════════════════════════════════════════
 ALGORITHM FLOW
═══════════════════════════════════════════════════════════════════

 ┌─────────────────────────────────────────────────────────────┐
 │ PHASE 1: LOAD                                              │
 │                                                             │
 │  DDR ──memCpy──→ OCM (state, sprites every frame)          │
 │  DDR ──memCpy──→ OCM (map + 3 atlases, first frame only)  │
 │                                                             │
 │  OCM ──rau::load::oneTile──→ LRM (8 camera fields)        │
 │  generated_assets.hpp ──→ LRM (256-entry palette LUT)      │
 └─────────────────────────────────────────────────────────────┘
                            │
                            ▼
 ┌─────────────────────────────────────────────────────────────┐
 │ PHASE 2: COLUMN PREPASS  (tileX = 0..39 → pixels 0..319)  │
 │                                                             │
 │  For each of 40 columns:                                    │
 │    ┌───────────────────────────────────────────────┐        │
 │    │ castRay():                                    │        │
 │    │                                               │        │
 │    │  Step 1: 32 DDA steps, pure ALU               │        │
 │    │          compute 32 map cell addresses         │        │
 │    │                  │                             │        │
 │    │                  ▼                             │        │
 │    │  Step 2: rau::load::tiles (32 addrs, 1 flow)  │        │
 │    │          OCM ocmMap ──→ 32 cell values         │        │
 │    │                  │                             │        │
 │    │                  ▼                             │        │
 │    │  Step 3: scan for first hit — per-PE predicate │        │
 │    │          perpDist = firstHit ? dist : perpDist │        │
 │    │          → perpDist, wallTop/Bot, texU, shade  │        │
 │    └───────────────────────────────────────────────┘        │
 │    Store 10 fields → localCols[tileX] (LRM registers)      │
 │                                                             │
 │  Each tileX covers 8 PEs = 8 adjacent screen columns       │
 │  tileX=0 → px 0..7, tileX=1 → px 8..15, ... tileX=39 → px 312..319
 └─────────────────────────────────────────────────────────────┘
                            │
                            ▼
 ┌─────────────────────────────────────────────────────────────┐
 │ PHASE 3: TILE LOOP                                          │
 │  tileY = 0..24  → rows 0..7, 8..15, ... 192..199           │
 │  tileX = 0..39  → cols 0..7, 8..15, ... 312..319           │
 │  = 1,000 tiles, 64 PEs each, each PE = one screen pixel    │
 │                                                             │
 │  Per tile row (25×):                                        │
 │    buildFloorRowSample() ── LUT in LRM (no division)       │
 │    buildCeilingRowSample()                                  │
 │                                                             │
 │  Per tile (1000×):                                          │
 │  ┌─────────────────────────────────────────────────────┐   │
 │  │                                                     │   │
 │  │  ┌─ FLOOR/CEILING ─────────────────────────────┐    │   │
 │  │  │ if allFloor/allCeil: skip other plane        │    │   │
 │  │  │ else: compute both, per-PE predicate select  │    │   │
 │  │  │   addr = isFloor ? floorAddr : ceilAddr      │    │   │
 │  │  │         │                                    │    │   │
 │  │  │         ▼                                    │    │   │
 │  │  │ rau::load::tiles (1 addr per PE, 1 flow)    │    │   │
 │  │  │ OCM ocmFlatTex ──→ palette index             │    │   │
 │  │  │         │                                    │    │   │
 │  │  │         ▼                                    │    │   │
 │  │  │ paletteCache[idx] → shade → RGB565 pixel     │    │   │
 │  │  └──────────────────────────────────────────────┘    │   │
 │  │                     │                                │   │
 │  │                     ▼                                │   │
 │  │  ┌─ WALL ──────────────────────────────────────┐    │   │
 │  │  │ read wallTop/Bot/texU/shade from localCols   │    │   │
 │  │  │ compute per-PE texel addr                    │    │   │
 │  │  │         │                                    │    │   │
 │  │  │         ▼                                    │    │   │
 │  │  │ rau::load::tiles (1 addr per PE, 1 flow)    │    │   │
 │  │  │ OCM ocmWallTex ──→ palette index             │    │   │
 │  │  │         │                                    │    │   │
 │  │  │         ▼                                    │    │   │
 │  │  │ pixel = isWall ? wallPixel : floorPixel      │    │   │
 │  │  └──────────────────────────────────────────────┘    │   │
 │  │                     │                                │   │
 │  │                     ▼                                │   │
 │  │  ┌─ SPRITES (up to 4 per tile) ───────────────┐    │   │
 │  │  │ rau::load::oneTile → spriteCount             │    │   │
 │  │  │                                              │    │   │
 │  │  │ Per sprite:                                  │    │   │
 │  │  │   rau::load::tiles (12 addrs, 1 flow)       │    │   │
 │  │  │   OCM ocmSprites ──→ bounds,UV,depth,shade   │    │   │
 │  │  │            │                                 │    │   │
 │  │  │   if !anyOf(inSprite): continue ◄── early out│    │   │
 │  │  │            │                                 │    │   │
 │  │  │   rau::load::tiles (1 addr per PE, 1 flow)  │    │   │
 │  │  │   OCM ocmSpriteTex ──→ palette index          │    │   │
 │  │  │            │                                 │    │   │
 │  │  │   pixel = sprCloser ? sprPixel : pixel        │    │   │
 │  │  └──────────────────────────────────────────────┘    │   │
 │  │                     │                                │   │
 │  │                     ▼                                │   │
 │  │  ┌─ WRITEBACK ─────────────────────────────────┐    │   │
 │  │  │ WriteFlow<TileAccess> (MinRoiDescriptor)     │    │   │
 │  │  │ 64 PEs write 64 pixels → ocmFb               │    │   │
 │  │  └──────────────────────────────────────────────┘    │   │
 │  └─────────────────────────────────────────────────────┘   │
 └─────────────────────────────────────────────────────────────┘
                            │
                            ▼
 ┌─────────────────────────────────────────────────────────────┐
 │ PHASE 4: OUTPUT                                             │
 │                                                             │
 │  OCM ocmFb ──memCpy──→ DDR ddrFb [200×320 u16 = 128KB]    │
 └─────────────────────────────────────────────────────────────┘


═══════════════════════════════════════════════════════════════════
 DATA MOVEMENT SUMMARY
═══════════════════════════════════════════════════════════════════

 Type              │ Pattern              │ Per frame
 ──────────────────┼──────────────────────┼──────────────────
 DDR→OCM memCpy    │ bulk DMA             │ 32B state + sprites
 DDR→OCM memCpy    │ bulk DMA             │ ~880KB first frame
 OCM→LRM rau       │ oneTile (scalar)     │ 8 camera fields
 OCM→LRM rau       │ load::tiles (batched)│ 40 × 32-addr (ray)
 OCM→LRM rau       │ load::tiles (batched)│ 1000 × 1-addr (flat)
 OCM→LRM rau       │ load::tiles (batched)│ 1000 × 1-addr (wall)
 OCM→LRM rau       │ load::tiles (batched)│ ≤4000 × 13-addr (spr)
 LRM→OCM WriteFlow │ MinRoi (tile)        │ 1000 tiles
 OCM→DDR memCpy    │ bulk DMA             │ 128KB framebuffer

Build & Run

make               # compile and run renderer on ISS
make play          # interactive game (requires pygame)
make headless      # render 3 test views, no display
make clean         # remove build artifacts

Resolution Toggle

Edit src/types.hpp — uncomment #define DOOM_RES_224x168 for the smaller resolution, or leave it commented for native 320x200. Both the C++ kernel and Python game read from this single source of truth.

ResolutionTilesCycles (QC-N ISS)
320x200 (native DOOM)40x25 = 1,000~735K
224x16828x21 = 588~458K

Controls

WASD: move, Arrow keys: turn, SPACE: shoot, SHIFT: run, TAB: minimap, ESC: quit


3. The CCL Renderer Kernel

The kernel is structured across these files:

FilePurpose
renderer.cppMain kernel (EPU_ENTRY) + host test harness
types.hppTensor types, screen geometry, resolution toggle
ray.hppBatched DDA raycasting (32 steps, 1 RAU flow)
floor.hppFloor/ceiling perspective sampling
wall.hppWall texture shading
sprite.hppSprite compositing with anyOf() early-exit
texture.hppAtlas address computation, palette lookup
lighting.hpp4-directional linalg::dot lighting + fog
generated_assets.hppAtlas dimensions, DOOM palette (auto-generated)

Let's look at the kernel entry point:

## Show the kernel signature and phase structure
renderer_src = open(os.path.join(SRC_DIR, "renderer.cpp")).read()
## Extract just the kernel function (EPU_ENTRY to the end of the kernel)
start = renderer_src.find("EPU_ENTRY")
end = renderer_src.find("HOST_MAIN")
kernel_code = renderer_src[start:end].strip()
print(f"Kernel: {len(kernel_code.splitlines())} lines\n")
## Show first 40 lines (setup + phase 1)
for i, line in enumerate(kernel_code.splitlines()[:40], 1):
    print(f"{i:3d}  {line}")
Kernel: 272 lines

  1  EPU_ENTRY void renderFrame(
  2      std::int32_t         loadStaticFlag,
  3      DdrState::ptrType    statePtr,
  4      DdrMap::ptrType      mapPtr,
  5      DdrSpriteCounts::ptrType spriteCountsPtr,
  6      DdrSprites::ptrType  spritesPtr,
  7      DdrWallTextures::ptrType wallTexPtr,
  8      DdrFlatTextures::ptrType flatTexPtr,
  9      DdrSpriteTextures::ptrType spriteTexPtr,
 10      DdrScreen::ptrType   fbPtr
 11  ) {
 12      DdrState    ddrState(statePtr);
 13      DdrMap      ddrMap(mapPtr);
 14      DdrSpriteCounts ddrSpriteCounts(spriteCountsPtr);
 15      DdrSprites  ddrSprites(spritesPtr);
 16      DdrWallTextures ddrWallTex(wallTexPtr);
 17      DdrFlatTextures ddrFlatTex(flatTexPtr);
 18      DdrSpriteTextures ddrSpriteTex(spriteTexPtr);
 19      DdrScreen   ddrFb(fbPtr);
 20  
 21      MemAllocator ocmMem;
 22      OcmState    ocmState;   ocmMem.allocate(ocmState);
 23      OcmMap      ocmMap;     ocmMem.allocate(ocmMap);
 24      OcmSpriteCounts ocmSpriteCounts; ocmMem.allocate(ocmSpriteCounts);
 25      OcmSprites  ocmSprites; ocmMem.allocate(ocmSprites);
 26      OcmWallTextures ocmWallTex; ocmMem.allocate(ocmWallTex);
 27      OcmFlatTextures ocmFlatTex; ocmMem.allocate(ocmFlatTex);
 28      OcmSpriteTextures ocmSpriteTex; ocmMem.allocate(ocmSpriteTex);
 29      OcmScreen   ocmFb;      ocmMem.allocate(ocmFb);
 30  
 31      DOOM_PROFILE_START("region: 1 / 8 state+static_load");
 32      memCpy(ddrState,   ocmState);
 33      memCpy(ddrSpriteCounts, ocmSpriteCounts);
 34      memCpy(ddrSprites, ocmSprites);
 35      if(loadStaticFlag != 0) {
 36          memCpy(ddrMap,      ocmMap);
 37          memCpy(ddrWallTex,  ocmWallTex);
 38          memCpy(ddrFlatTex,  ocmFlatTex);
 39          memCpy(ddrSpriteTex,  ocmSpriteTex);
 40      }

4. Compile & Run

The kernel compiles with sdk source — one command handles the full LLVM pipeline (CCL → Quadric LLVM → EPU binary) and runs on the ISS (Instruction Set Simulator).

!make renderer
sdk source src/renderer.cpp --target QC-N --ocm-size 4MB --macs-per-pe 8
[SDK-CLI] : Compiling source
2026-06-19 03:53 - DEBUG - sdk - cli - Executing command: cmake CMakeLists.txt -B /quadric/sdk-cli/examples/games/doom/renderer_QC-N_4MB_4kB_128GBps_128GBps/build -DNUM_GPNPUS=1 -DNUM_CORES=8 -DNUM_BORDERS=2 -DEPU_VERSION=2.0.0 -DQLLVM_ROOT_PATH=/quadric/llvm -DOCM_SIZE_KIBIBYTES=4096 -DNUM_PE_MACS=8 -DASSERT_MLS_WIDTH_LINE_ALIGN=ON -DHARDWARE_TARGET=OFF 
CMAKE_CXX_COMPILER: /quadric/llvm/bin/clang++
-- The C compiler identification is Clang 8.0.1
-- The CXX compiler identification is Clang 8.0.1
-- Detecting C compiler ABI info
-- Detecting C compiler ABI info - done
-- Check for working C compiler: /quadric/llvm/bin/clang - skipped
-- Detecting C compile features
-- Detecting C compile features - done
-- Detecting CXX compiler ABI info
-- Detecting CXX compiler ABI info - done
-- Check for working CXX compiler: /quadric/llvm/bin/clang++ - skipped
-- Detecting CXX compile features
-- Detecting CXX compile features - done
-- Configuring done (0.2s)
-- Generating done (0.0s)
-- Build files have been written to: /quadric/sdk-cli/examples/games/doom/renderer_QC-N_4MB_4kB_128GBps_128GBps/build

2026-06-19 03:53 - DEBUG - sdk - cli - Executing command: make -j8
clang-8: warning: /quadric/llvm/bin/clang++: 'linker' input unused [-Wunused-command-line-argument]
renderer_epu: EPU opt stage
renderer_epu: EPU .s to elf
In file included from /quadric/sdk-cli/examples/games/doom/src/renderer.cpp:23:
In file included from /quadric/sdk-cli/examples/games/doom/src/ray.hpp:13:
/quadric/sdk-cli/examples/games/doom/src/lighting.hpp:15:29: warning: loop not
      unrolled: the optimizer was unable to perform the requested
      transformation; the transformation might be disabled or specified as part
      of an unsupported transformation ordering
      [-Wpass-failed=transform-warning]
INLINE qVar_t<std::int32_t> computeLighting(
                            ^
/quadric/sdk-cli/examples/games/doom/src/lighting.hpp:15:29: warning: loop not
      unrolled: the optimizer was unable to perform the requested
      transformation; the transformation might be disabled or specified as part
      of an unsupported transformation ordering
      [-Wpass-failed=transform-warning]
/quadric/sdk-cli/examples/games/doom/src/lighting.hpp:15:29: warning: loop not
      unrolled: the optimizer was unable to perform the requested
      transformation; the transformation might be disabled or specified as part
      of an unsupported transformation ordering
      [-Wpass-failed=transform-warning]
/quadric/sdk-cli/examples/games/doom/src/lighting.hpp:15:29: warning: loop not
      unrolled: the optimizer was unable to perform the requested
      transformation; the transformation might be disabled or specified as part
      of an unsupported transformation ordering
      [-Wpass-failed=transform-warning]
In file included from /quadric/sdk-cli/examples/games/doom/src/renderer.cpp:23:
/quadric/sdk-cli/examples/games/doom/src/ray.hpp:15:15: warning: loop not
      unrolled: the optimizer was unable to perform the requested
      transformation; the transformation might be disabled or specified as part
      of an unsupported transformation ordering
      [-Wpass-failed=transform-warning]
INLINE RayHit castRay(
              ^
/quadric/sdk-cli/examples/games/doom/src/ray.hpp:15:15: warning: loop not
      unrolled: the optimizer was unable to perform the requested
      transformation; the transformation might be disabled or specified as part
      of an unsupported transformation ordering
      [-Wpass-failed=transform-warning]
/quadric/sdk-cli/examples/games/doom/src/ray.hpp:15:15: warning: loop not
      unrolled: the optimizer was unable to perform the requested
      transformation; the transformation might be disabled or specified as part
      of an unsupported transformation ordering
      [-Wpass-failed=transform-warning]
/quadric/sdk-cli/examples/games/doom/src/ray.hpp:15:15: warning: loop not
      unrolled: the optimizer was unable to perform the requested
      transformation; the transformation might be disabled or specified as part
      of an unsupported transformation ordering
      [-Wpass-failed=transform-warning]
8 warnings generated.
make[1]: Entering directory '/quadric/sdk-cli/examples/games/doom/renderer_QC-N_4MB_4kB_128GBps_128GBps/build'
make[2]: Entering directory '/quadric/sdk-cli/examples/games/doom/renderer_QC-N_4MB_4kB_128GBps_128GBps/build'
make[3]: Entering directory '/quadric/sdk-cli/examples/games/doom/renderer_QC-N_4MB_4kB_128GBps_128GBps/build'
make[3]: Entering directory '/quadric/sdk-cli/examples/games/doom/renderer_QC-N_4MB_4kB_128GBps_128GBps/build'
make[3]: Leaving directory '/quadric/sdk-cli/examples/games/doom/renderer_QC-N_4MB_4kB_128GBps_128GBps/build'
make[3]: Leaving directory '/quadric/sdk-cli/examples/games/doom/renderer_QC-N_4MB_4kB_128GBps_128GBps/build'
make[3]: Entering directory '/quadric/sdk-cli/examples/games/doom/renderer_QC-N_4MB_4kB_128GBps_128GBps/build'
make[3]: Entering directory '/quadric/sdk-cli/examples/games/doom/renderer_QC-N_4MB_4kB_128GBps_128GBps/build'
[ 25%] Building CXX object CMakeFiles/renderer_epu.dir/quadric/sdk-cli/examples/games/doom/src/renderer.cpp.o
[ 50%] Building CXX object CMakeFiles/renderer_host.dir/quadric/sdk-cli/examples/games/doom/src/renderer.cpp.o
[ 75%] Linking EPU executable /quadric/sdk-cli/examples/games/doom/renderer_QC-N_4MB_4kB_128GBps_128GBps/output/renderer_epu.s
make[3]: Leaving directory '/quadric/sdk-cli/examples/games/doom/renderer_QC-N_4MB_4kB_128GBps_128GBps/build'
[ 75%] Built target renderer_epu
[100%] Linking CXX executable /quadric/sdk-cli/examples/games/doom/renderer_QC-N_4MB_4kB_128GBps_128GBps/output/renderer_host
make[3]: Leaving directory '/quadric/sdk-cli/examples/games/doom/renderer_QC-N_4MB_4kB_128GBps_128GBps/build'
[100%] Built target renderer_host
make[2]: Leaving directory '/quadric/sdk-cli/examples/games/doom/renderer_QC-N_4MB_4kB_128GBps_128GBps/build'
make[1]: Leaving directory '/quadric/sdk-cli/examples/games/doom/renderer_QC-N_4MB_4kB_128GBps_128GBps/build'

[SDK-CLI] : Executing on QC-N simulator
2026-06-19 03:53 - DEBUG - sdk - cli - Executing command: ./renderer_host -c --ddrRdBwTotal 1048576.0 --ddrWrBwTotal 1048576.0 --ddrAxiWidth 128 --instMemDepth 1310720 --ocmSize 4194304 --cycleTimeNS 0.5882352941176471 --no-check --ddrRdAvgPct 100 --ddrRdMaxPct 100 --ddrWrAvgPct 100 --ddrWrMaxPct 100 --postKernelFlowTimeoutCycles 4000000 --clusterSize 1 --numClusters 1 
seed: 2942817044
Fused renderer: camera=(8,5) angle=0.0, 2 enemies
Global seed is 2942817044
[PROFILE_KGVCID_{00:00}] Total execution time (cycles): 637931
------------------------------------------------
[PROFILE_KGVCID_{00:00}] Profile: default
[PROFILE_KGVCID_{00:00}] TotalCycles = 637931
[PROFILE_KGVCID_{00:00}]     ExecCycles[COMPUTE] = 132111
[PROFILE_KGVCID_{00:00}]     ExecCycles[MAC] = 0
[PROFILE_KGVCID_{00:00}]     ExecCycles[MAC_REDUCE] = 0
[PROFILE_KGVCID_{00:00}]     ExecCycles[COMPARE] = 17329
[PROFILE_KGVCID_{00:00}]     ExecCycles[BRANCH] = 8834
[PROFILE_KGVCID_{00:00}]     ExecCycles[CONDITIONAL] = 10346
[PROFILE_KGVCID_{00:00}]     ExecCycles[ITERATIVE] = 0
[PROFILE_KGVCID_{00:00}]     ExecCycles[PREDICATION] = 7120
[PROFILE_KGVCID_{00:00}]     ExecCycles[CONSTANT_MATERIALIZATION] = 32502
[PROFILE_KGVCID_{00:00}]     ExecCycles[FLOW_SETUP] = 35606
[PROFILE_KGVCID_{00:00}]     ExecCycles[LOOP_SETUP] = 81
[PROFILE_KGVCID_{00:00}]     ExecCycles[REG_MOVEMENT] = 54576
[PROFILE_KGVCID_{00:00}]     ExecCycles[DATA_MOVEMENT] = 8296
[PROFILE_KGVCID_{00:00}]     ExecCycles[KERNEL_SETUP] = 13
[PROFILE_KGVCID_{00:00}]     ExecCycles[INTERRUPT] = 1
[PROFILE_KGVCID_{00:00}]     StallCycles[DIV] = 9300
[PROFILE_KGVCID_{00:00}]     StallCycles[MODE_WRITE] = 16
[PROFILE_KGVCID_{00:00}]     StallCycles[SPRF_RAW] = 81
[PROFILE_KGVCID_{00:00}]     StallCycles[SPRF_RARMW] = 0
[PROFILE_KGVCID_{00:00}]     StallCycles[SPRF_PWARMW] = 0
[PROFILE_KGVCID_{00:00}]     StallCycles[DEREF_REGPTR_CONFLICT] = 1792
[PROFILE_KGVCID_{00:00}]     StallCycles[BRANCH] = 17387
[PROFILE_KGVCID_{00:00}]     StallCycles[LOAD] = 238755
[PROFILE_KGVCID_{00:00}]     StallCycles[STORE] = 30
[PROFILE_KGVCID_{00:00}]     StallCycles[EXT_LOAD] = 0
[PROFILE_KGVCID_{00:00}]     StallCycles[EXT_STORE] = 8115
[PROFILE_KGVCID_{00:00}]     StallCycles[MLS_SEND] = 0
[PROFILE_KGVCID_{00:00}]     StallCycles[RAU] = 0
[PROFILE_KGVCID_{00:00}]     StallCycles[PREDICATION] = 6000
[PROFILE_KGVCID_{00:00}]     StallCycles[MUL] = 10610
[PROFILE_KGVCID_{00:00}]     StallCycles[FX32TOFP16] = 0
[PROFILE_KGVCID_{00:00}]     StallCycles[MAC_REDUCE] = 0
[PROFILE_KGVCID_{00:00}]     StallCycles[NBR_RAW] = 0
[PROFILE_KGVCID_{00:00}]     StallCycles[MEU] = 0
[PROFILE_KGVCID_{00:00}]     StallCycles[PATCH_MESH] = 0
[PROFILE_KGVCID_{00:00}]     StallCycles[QLS_Q_FULL] = 33206
[PROFILE_KGVCID_{00:00}]     StallCycles[BAR_LOAD] = 0
[PROFILE_KGVCID_{00:00}]     StallCycles[BAR_STORE] = 0
[PROFILE_KGVCID_{00:00}]     StallCycles[BAR_BUBBLE] = 4048
[PROFILE_KGVCID_{00:00}]     StallCycles[BARRIER_MLS_DONE] = 0
[PROFILE_KGVCID_{00:00}]     StallCycles[INTERRUPT] = 0
[PROFILE_KGVCID_{00:00}]     StallCycles[TENSOR_TABLE_FULL] = 0
[PROFILE_KGVCID_{00:00}]     StallCycles[MLS_FIFO_FULL] = 0
[PROFILE_KGVCID_{00:00}]     StallCycles[INSTRUCTION_FETCH] = 1775
------------------------------------------------
[PROFILE_KGVCID_{00:00}] ExtBytes[LOAD] = 967104
[PROFILE_KGVCID_{00:00}] ExtBytes[STORE] = 128000
Saved renderer_frame.ppm

[SDK-CLI] : TotalCycles: 637,931
[SDK-CLI] : Executions/second: 2,665

compute      : ▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇ 294.952K
data_array   : ▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇ 96.078K
mac          :  0
data_ocm     : ▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇ 238.785K
data_external: ▇ 8.115K

[SDK-CLI] : Execution completed.


5. Visualize the Rendered Frame

The ISS writes a .ppm framebuffer alongside the compiled kernel.

import glob

ppm_candidates = glob.glob("**/renderer_frame.ppm", recursive=True)
if ppm_candidates:
    frame = Image.open(ppm_candidates[0])
    scaled = frame.resize((frame.width * 3, frame.height * 3), Image.NEAREST)
    display(scaled)
    print(f"Frame: {frame.width}x{frame.height}, rendered on Chimera GPNPU (ISS)")
else:
    print("No rendered frame found. Run the compile step first.")

Frame: 320x200, rendered on Chimera GPNPU (ISS)
previews = [
    ("Wall Textures", "doom_wall_preview.png"),
    ("Floor/Ceiling Flats", "doom_flat_preview.png"),
    ("Sprite Sheet", "doom_sprite_preview.png"),
]

for title, fname in previews:
    path = os.path.join(ASSET_DIR, fname)
    if os.path.exists(path):
        img = Image.open(path)
        print(f"\n{title} ({img.width}x{img.height}):")
        display(img)
Wall Textures (1024x1024):

Floor/Ceiling Flats (256x384):

Sprite Sheet (256x576):


Summary

MetricValue
Resolution320x200 (native DOOM)
Kernel size~800 lines CCL
Parameters0 (no neural network)
GMACs0
OCM footprint~880 KB
Per-frame DDR traffic32B in + 128KB out
TechniqueWhy
Batched RAU loads32 DDA map lookups in 1 flow instead of 32
Indexed color + LRM palette1 byte/texel, palette lookup is free
anyOf() sprite early-exitSkip texture fetch when no PE overlaps
math::fxDiv hardware divideFloor/ceiling depth without software division
Static OCM residencyTextures upload once, stay on-chip
Per-PE predicationWall/floor/ceiling/sprite compositing without branching

Key Takeaways

  • The Chimera GPNPU runs a complete raycasting renderer as a single kernel — no graph compiler, no operator library, just C++.
  • RAU random-access loads are what make texture mapping possible on a tile-based SIMD array.
  • The same architectural properties that enable this (CCL programmability, RAU, OCM autonomy, mega-kernel execution) are what close the workload gap for real-world edge AI: custom operators, sensor fusion, pre/post-processing.

Blog post: Can Your NPU Run DOOM?

Table of Contents
Introduction to the Chimera SDK
Chimera SDK Quick Start Guide
Chimera SDK Command Line Interface (CLI)
Tutorial: Using SDK as a Library
Tutorials & Model Demos
Model Demos
Model Demo: Llama-2 15M (Baby Llama-2)
Model Demo: QWEN3 8B End-to-End CGC and ISS Execution
Model Demo: QWEN3 Prefill All Decoders
Model Demo: DeepSeek-R1-Distill-Qwen-1.5B End-to-End CGC and ISS Execution
Model Demo: QWEN3 Single Decoder
Model Demo: Qwen2.5-0.5B INT8 Quantization Pipeline
Model Demo: ConvNeXt Detection
Model Demo: QWEN3 Prefill Decoder Validation
Model Demo: ConvNeXt Segmentation
Model Demo: Classifiers Zoo
Model Demo: Detectors Zoo - MMDetection
Model Demo: Segmentors Zoo - MMSegmentation
Model Demo: Pose Estimators Zoo - MMPose
Model Demo: Detectors3D Zoo - MMDetection3D
MODEL Demo: Optical Character Recognition (OCR) Zoo - MMOCR
Model Demo: YOLOv3 Object Detection
Model Demo: YOLOv4 Object Detection
Model Demo: YOLOv5 Detection
Model Demo: YOLOv5 Detection and Segmentation
Model Demo: YOLOR Detection
Model Demo: YOLOX End-to-End Detection
Model Demo: YOLOv7 Detection
Model Demo: YOLOv8 Detection
Model Demo: YOLOv8 Pose Estimation
Model Demo: YOLOP Detection and Segmentation
Model Demo: QAT Vision Transformer (ViT)
Model Demo: QAT Swin Transformer
Model Demo: Mediapipe Face Pipeline
Demo: DOOM Renderer on Chimera GPNPU
Model Demo: Mediapipe Hand Pipeline
Model Demo: Whisper Tiny (Encoder + Decoder)
Model Demo: L2CS Fine-Grained Gaze Estimation
Model Demo: ASVspoof2021 LA Anti-Spoofing (LFCC-LCNN-BiLSTM)
Model Demo: UNET Tumor Segmentation
Model Demo: DETR Encoder
Model Demo: FFNet Segmentation
Model Demo: Centernet Detection
Model Demo: RetinaNet End-to-End Detection
Model Demo: Blazepose Pose Estimation
Model Demo: Pose Resnet Human Pose Estimation
Model Demo: MaskRCNN Detection and Segmentation
Model Demo: Keypoint R-CNN
Model Demo: Faster R-CNN Detection
Model Demo: FCOS Detection
Model Demo: DDRNet Classificationls
Model Demo: PI0.5 End-to-End VLA Inference
Model Demo: BEVFormer End-to-End 3D Detection
Multicore Demo
Chimera LLVM C++ Compiler
Chimera SDK Licensing Policy Documentation
Glossary


© Copyright 2026 Quadric All Rights Reserved • Privacy Policy
This documentation is preliminary and confidential. It is subject to change. Quadric does not give any warranty express or implied that the contents will be complete or accurate or up to date. The company shall not be liable for any loss, actions, claims, proceedings, demands or costs or damages whatsoever or howsoever caused arising directly or indirectly in connection with or arising out of the use of this material.

Sign in to your account

Don't have an account? Create an Account
By signing in, you are agreeing to our Terms of Use and Privacy Policy.

Develop.

Simulate.

Profile.

Collaborate.

We use cookies to enhance your browsing experience, and analyze our traffic.
By clicking "Accept All", you consent to our use of cookies.