Background Service
Waxed Background Service
Overview
The BackgroundService is a centralized service for managing desktop background images in the Waxed compositor. It provides async image loading, Vulkan descriptor management, and DMA-BUF export capabilities for zero-copy background rendering.
Key Responsibilities
- Async Image Loading: Loads background images from disk without blocking the render thread
- Descriptor Management: Provides Vulkan descriptor sets for background rendering
- Mode Switching: Supports multiple display modes (contain, cover, tile, stretch)
- DMA-BUF Export: Exports backgrounds as DMA-BUF for zero-copy import by plugins
- Provider Handoff: Handles Vulkan provider plugin changes gracefully
Thread Safety
The service uses atomic flags and reader-writer synchronization to allow concurrent descriptor access from multiple plugins. All state-changing operations are synchronized, while read operations (like get_background_descriptor()) are lock-free.
BackgroundMode Enum
Defines how background images are scaled and positioned relative to the display.
enum class BackgroundMode {
Contain, // Scale image to fit entirely within display (preserve aspect ratio)
Cover, // Scale image to cover entire display (preserve aspect ratio, may crop)
Tile, // Display image at native resolution, tiling if smaller
Stretch, // Stretch image to fill display (may distort)
};
Mode Behaviors
| Mode | Aspect Ratio | Cropping | Distortion | Use Case |
|---|---|---|---|---|
| Contain | Preserved | Never | None | Seeing entire image |
| Cover | Preserved | May crop | None | Filling display completely |
| Tile | Preserved | Never | None | Patterns, textures |
| Stretch | Not preserved | Never | Yes | Exact fill regardless of ratio |
BackgroundType Enum
Identifies the type of background content.
enum class BackgroundType {
None, // No background set
StaticImage, // Static image background
Video, // Video background (future, not yet implemented)
};
BackgroundDescriptor Structure
Contains all information needed by the compositor to render the background.
struct BackgroundDescriptor {
VkDescriptorSet descriptor_set{}; // Vulkan descriptor set for sampling
VkImageLayout image_layout{}; // Current image layout
BackgroundMode mode; // Display mode
BackgroundType type; // Content type
uint32_t width; // Image width in pixels
uint32_t height; // Image height in pixels
uint64_t timeline_value; // Timeline semaphore value (unused)
};
Usage Pattern
Plugins retrieve the descriptor via get_background_descriptor() during render:
auto descriptor_opt = background_service->get_background_descriptor();
if (descriptor_opt.has_value()) {
const auto& desc = descriptor_opt.value();
if (desc.descriptor_set != VK_NULL_HANDLE) {
// Bind descriptor set and render
vkCmdBindDescriptorSets(cmd, ..., &desc.descriptor_set, ...);
// Draw fullscreen quad with desc.mode push constants
}
}
BackgroundErrorCode Handling
Error codes returned via std::expected<T, BackgroundErrorCode>.
enum class BackgroundErrorCode {
Success = 0,
InvalidPath = 1, // Empty or malformed path
FileNotFound = 2, // File doesn't exist on disk
UnsupportedFormat = 3, // File extension not supported
LoadFailed = 4, // Image loading failed
NoBackgroundSet = 5, // Tried to get descriptor when none set
InvalidMode = 6, // Invalid BackgroundMode value
VulkanError = 7, // Vulkan operation failed
};
Supported Formats
.jpg,.jpeg- JPEG images.png- PNG images.bmp- Bitmap images.tga- Targa images.psd- Photoshop documents.gif- GIF images (first frame only).hdr- HDR images.pic,.pnm- Portable image formats
Initialization
Constructor
BackgroundService::Config config{};
config.max_width = 3840;
config.max_height = 2160;
config.ring_buffer_size = 2;
BackgroundService service(config);
init_vulkan()
Initializes Vulkan resources. Must be called after a Vulkan provider plugin registers.
auto result = service.init_vulkan(
instance, // vk::raii::Instance& (borrowed)
physical_device, // vk::raii::PhysicalDevice& (borrowed)
device, // vk::raii::Device& (borrowed)
graphics_queue, // vk::raii::Queue& (borrowed)
queue_family_index // uint32_t
);
Internal Operations:
- Stores device handle (non-owning reference)
- Creates
TextureStreamerwith 32MB staging buffer - Configures triple buffering (ring buffer size = 3)
- Sets
is_ready_atomic flag
Re-initialization: If called when already initialized, the service calls invalidate_vulkan() first to handle provider handoff.
set_background_image()
Sets a background image from a file path. Returns immediately; loading happens async.
auto result = service.set_background_image(
"/path/to/image.jpg", // std::string
BackgroundMode::Cover // Display mode
);
Flow:
Error Handling:
- Returns
std::unexpected(InvalidPath)if path is empty - Returns
std::unexpected(FileNotFound)if file doesn’t exist - Returns
std::unexpected(UnsupportedFormat)if extension not recognized - Returns
std::unexpected(LoadFailed)if Vulkan not initialized
get_background_descriptor()
Retrieves the current background descriptor for rendering. Thread-safe.
auto descriptor_opt = service.get_background_descriptor();
if (descriptor_opt.has_value()) {
const auto& desc = descriptor_opt.value();
// Use desc.descriptor_set, desc.mode, etc.
}
Behavior:
- Returns
std::nulloptif no background is set or not ready - Returns
std::nulloptif Vulkan is not initialized - Returns freshest ready slot from
TextureStreamer - Double-dereferences
vk::raii::DescriptorSetoptional to get raw handle
Thread Safety: Multiple threads can call this concurrently without locks.
clear_background()
Resets the background state to None.
service.clear_background();
Operations:
- Sets
current_mode_toBackgroundMode::Contain - Sets
current_type_toBackgroundType::None - Clears
current_descriptor_ - Does NOT invalidate Vulkan resources
Idempotent: Safe to call even when no background is set.
Push Constant Calculation
calculate_push_constants()
Static method for calculating shader push constants based on mode and dimensions.
auto push = BackgroundService::calculate_push_constants(
BackgroundMode::Cover, // Render mode
1920, // Source image width
1080, // Source image height
2560, // Target display width
1440 // Target display height
);
PushConstants Structure
struct PushConstants {
float scale_x; // UV/Position scale X
float scale_y; // UV/Position scale Y
float offset_x; // UV/Position offset X
float offset_y; // UV/Position offset Y
int32_t mode; // 0=contain, 1=cover, 2=tile, 3=stretch
};
Mode Calculations
DMA-BUF Export
BackgroundDmaBuf Structure
DMA-BUF handle for zero-copy background transfer. Ownership is transferred to caller.
struct BackgroundDmaBuf {
core::utils::UniqueFd dma_buf_fd; // DMA-BUF file descriptor (RAII)
uint32_t width; // Image width in pixels
uint32_t height; // Image height in pixels
uint32_t stride; // Row stride in bytes
uint32_t format; // DRM fourcc format (DRM_FORMAT_ABGR8888)
uint64_t modifier; // DRM format modifier
VkDeviceSize allocation_size; // Total allocation size
uint32_t memory_type_index; // Vulkan memory type index
// Pre-calculated push constants
float scale_x;
float scale_y;
float offset_x;
float offset_y;
int32_t mode;
};
set_background_dma_buf()
Async callback-based API for DMA-BUF export.
auto result = service.set_background_dma_buf(
"/path/to/image.jpg", // Image path
BackgroundMode::Contain, // Display mode
1920, // Target display width
1080, // Target display height
[](const BackgroundDmaBuf& dma_buf, void* user_data) {
// Callback invoked from worker thread when ready
// Import dma_buf.dma_buf_fd into your Vulkan texture
},
nullptr // User data pointer
);
Flow:
Callback Thread Behavior
- Polls every 10ms for texture readiness
- Stops when callback is invoked (callback cleared)
- Stops when stop token is requested
- Runs
check_texture_and_notify()in loop
check_texture_and_notify()
Internal method that polls texture readiness and invokes callback.
Operations:
- Check
has_pending_load_atomic flag - Check
dma_buf_callback_is set - Query
texture_streamer_->is_ready(pending_sequence_) - When ready: export to DMA-BUF
- Calculate push constants for target resolution
- Duplicate DMA-BUF file descriptor (via
dup()) - Populate
BackgroundDmaBufstructure - Invoke callback with populated structure
- Clear callback and pending state
Vulkan Provider Handoff
invalidate_vulkan()
Called when the Vulkan provider plugin is unloaded. Shuts down TextureStreamer and nullifies handles.
service.invalidate_vulkan();
Operations:
- Calls
texture_streamer_->shutdown() - Resets
texture_streamer_unique_ptr - Sets
is_ready_to false - Clears
device_handle (non-owning, just nulls)
After Invalidation:
- All operations fail until new provider registers
last_image_path_andlast_image_mode_are preserved- Background can be restored after re-init
restore_background()
Re-enqueues the last background after Vulkan re-initialization.
service.restore_background();
Operations:
- Checks if
last_image_path_is not empty - Checks if
texture_streamer_is available - Calls
texture_streamer_->request_load()with stored path and mode - Logs restoration
Provider Handoff Sequence:
Integration with TextureStreamer
TextureStreamer Role
The TextureStreamer handles:
- Async image loading from disk (stbi/stbi_load)
- Vulkan buffer/image allocation
- Staging buffer uploads
- DMA-BUF export via
vkGetMemoryFdKHR
Ring Buffer
Triple buffering (ring buffer size = 3) ensures:
- Smooth frame delivery
- No stalls during upload
- Always a ready frame for rendering
Sequence Numbers
Each load request returns a sequence number:
- Used to track specific texture load
- Queried via
is_ready(sequence) - Retrieved via
get_ready_texture(sequence)
Complete Loading Flow Diagram
Utility Functions
parse_background_mode()
Parses a mode string (case-insensitive) to BackgroundMode.
auto mode = BackgroundService::parse_background_mode("Cover"); // BackgroundMode::Cover
auto mode = BackgroundService::parse_background_mode("COVER"); // BackgroundMode::Cover
auto mode = BackgroundService::parse_background_mode("invalid"); // BackgroundMode::Contain (default)
background_mode_to_string()
Converts BackgroundMode to lowercase string.
auto str = BackgroundService::background_mode_to_string(BackgroundMode::Tile); // "tile"
State Queries
bool ready = service.is_ready(); // Vulkan initialized?
bool valid = service.is_vulkan_valid(); // Same as is_ready()
auto mode = service.get_current_mode(); // Current BackgroundMode
auto type = service.get_current_type(); // Current BackgroundType
Usage Example: Desktop Plugin
// In plugin init
auto result = core->funcs->set_background(
core,
"/usr/share/backgrounds/wallpaper.jpg",
"cover"
);
// In render loop
BackgroundDescriptor bg_desc;
if (core->funcs->get_background_descriptor(core, &bg_desc) == 0) {
if (bg_desc.descriptor_set != VK_NULL_HANDLE) {
// Background is ready
vkCmdBindDescriptorSets(cmd, ..., &bg_desc.descriptor_set, ...);
// Get push constants
auto push = BackgroundService::calculate_push_constants(
bg_desc.mode,
bg_desc.width,
bg_desc.height,
display_width,
display_height
);
vkCmdPushConstants(cmd, ..., &push);
// Draw fullscreen quad
vkCmdDraw(cmd, 4, 1, 0, 0);
}
}
Related Services
- TextureStreamer: Handles async texture loading and DMA-BUF export
- ShaderService: Provides shader hot-reload for background rendering
- PluginManager: Manages Vulkan provider registration and handoff