21 FEB 2026

rahulmnavneeth

vulkan backend

homedocs

Location

src/backend/vulkan/
  vulkan_backend.c    — RHI function table, device lifecycle, frame recording
  vulkan_pipeline.c   — Pipeline creation, render pass, descriptor set layout
  vulkan_memory.c     — Buffer and image allocation helpers
  vulkan_internal.h   — Shared internal types and constants
  vulkan_shaders.h    — Embedded SPIR-V bytecode
  shaders/            — GLSL source for SPIR-V compilation

Overview

Fully implemented Vulkan 1.0 headless backend. Renders offscreen with no surface or swapchain — the application reads back RGBA8 pixels via mop_viewport_read_color and blits to its own window (e.g. SDL3 texture). Enable with make MOP_ENABLE_VULKAN=1.

Instance and Device

Headless Vulkan — no surface or swapchain extensions required. The backend:

  1. Creates a VkInstance (with validation layers if available)
  2. Selects the first discrete GPU, falling back to integrated
  3. Creates a single-queue VkDevice on the graphics queue family
  4. Allocates a command pool, descriptor pool, fence, and staging buffer

Render Pass

Three attachments:

AttachmentFormatUsage
ColorVK_FORMAT_R8G8B8A8_SRGBsRGB color output
DepthVK_FORMAT_D32_SFLOATDepth testing
Object IDVK_FORMAT_R32_UINTPicking buffer

Pipeline

Command Recording

Per-frame:

  1. vkResetCommandBuffer + vkBeginCommandBuffer
  2. vkCmdBeginRenderPass (clear all attachments)
  3. Set viewport and scissor
  4. For each draw call: bind pipeline, push constants, bind vertex/index buffer, vkCmdDrawIndexed
  5. vkCmdEndRenderPass
  6. Pipeline barrier for color image transfer
  7. vkCmdCopyImageToBuffer — color, depth, and object ID to host-visible staging
  8. vkEndCommandBuffer
  9. vkQueueSubmit + fence wait

Readback

Host-visible staging buffers are persistently mapped. After fence completion, the application reads RGBA8 color, float depth, and uint32 object ID directly from the mapped pointers. Zero-copy on the CPU side.

RHI Handle Mapping

RHI HandleVulkan Concrete Type
MopRhiDeviceVkInstance, VkDevice, VkQueue, VkCommandPool, pipelines, UBO
MopRhiBufferVkBuffer + VkDeviceMemory (device-local)
MopRhiFramebufferVkFramebuffer + VkImage x 3 + VkImageView x 3 + staging buffers
MopRhiTextureVkImage + VkImageView + VkDeviceMemory

Buffer Updates

Vertex and index data are uploaded through a staging buffer (MOP_VK_STAGING_SIZE). The staging buffer is host-visible and persistently mapped. Data is copied to the staging buffer, then vkCmdCopyBuffer transfers it to device-local memory with a pipeline barrier for vertex/index access.

Runtime Selection

MopViewport *vp = mop_viewport_create(&(MopViewportDesc){
    .width = 800, .height = 600,
    .backend = MOP_BACKEND_VULKAN
});

Or via environment variable in the showcase:

MOP_BACKEND=vulkan ./build/mop_showcase