Skip to content

[Feature] Default buffer properties to DEVICE_LOCAL for static geometry #104

Description

@vmarcella

Overview

Buffer defaults bias toward CPU-visible memory (adds COPY_DST), even for static vertex buffers that are uploaded once and never modified. This results in suboptimal memory placement on discrete GPUs and unnecessary overhead. Static geometry should default to DEVICE_LOCAL memory, with CPU-visible properties reserved for buffers that require CPU updates (uniform streams, dynamic meshes).

Current State

Properties::default() returns CPU_VISIBLE:

// crates/lambda-rs/src/render/buffer.rsimplDefaultforProperties{fndefault() -> Self{Properties::CPU_VISIBLE}}

Additionally, build_from_mesh hardcodes CPU_VISIBLE for vertex buffers created from meshes:

// crates/lambda-rs/src/render/buffer.rspubfnbuild_from_mesh(mesh:&Mesh,gpu:&Gpu,) -> Result<Buffer,&'staticstr>{let builder = Self::new();return builder
.with_length(std::mem::size_of_val(mesh.vertices())).with_usage(Usage::VERTEX).with_properties(Properties::CPU_VISIBLE).with_buffer_type(BufferType::Vertex).build(gpu, mesh.vertices().to_vec());}

This means static meshes that are loaded once and never modified are placed in CPU-visible memory, which on discrete GPUs means system RAM accessed over PCIe rather than fast VRAM.

Scope

Goals:

  • Default Properties to DEVICE_LOCAL for optimal GPU performance
  • Update build_from_mesh to use DEVICE_LOCAL
  • Provide clear guidance on when to use CPU_VISIBLE vs DEVICE_LOCAL

Non-Goals:

  • Automatic memory tier selection based on update frequency
  • Staging buffer abstraction for uploads (future work)

Proposed API

// crates/lambda-rs/src/render/buffer.rsimplDefaultforProperties{fndefault() -> Self{Properties::DEVICE_LOCAL// Changed from CPU_VISIBLE}}// build_from_mesh updated to use DEVICE_LOCALpubfnbuild_from_mesh(mesh:&Mesh,gpu:&Gpu,) -> Result<Buffer,&'staticstr>{let builder = Self::new();return builder
.with_length(std::mem::size_of_val(mesh.vertices())).with_usage(Usage::VERTEX).with_properties(Properties::DEVICE_LOCAL)// Changed from CPU_VISIBLE.with_buffer_type(BufferType::Vertex).build(gpu, mesh.vertices().to_vec());}

Usage guidance:

// Static geometry (loaded once, never updated) - use default DEVICE_LOCALlet static_mesh_buffer = BufferBuilder::new().with_usage(Usage::VERTEX).build(&gpu, vertices)?;// Dynamic geometry (updated per-frame) - explicit CPU_VISIBLElet dynamic_buffer = BufferBuilder::new().with_usage(Usage::VERTEX).with_properties(Properties::CPU_VISIBLE).build(&gpu, initial_vertices)?;// Uniform buffers (updated frequently) - explicit CPU_VISIBLElet uniform_buffer = BufferBuilder::new().with_usage(Usage::UNIFORM).with_properties(Properties::CPU_VISIBLE).build(&gpu, uniform_data)?;

Acceptance Criteria

  • Properties::default() returns DEVICE_LOCAL
  • build_from_mesh uses DEVICE_LOCAL
  • Documentation updated explaining DEVICE_LOCAL vs CPU_VISIBLE tradeoffs
  • Examples using uniform buffers explicitly specify CPU_VISIBLE
  • Migration guide for users relying on implicit CPU_VISIBLE
  • Unit tests updated to reflect new defaults

Affected Crates

lambda-rs

Notes

  • This is a breaking change for users who rely on implicit CPU_VISIBLE for buffers they update from the CPU
  • Migration: add explicit .with_properties(Properties::CPU_VISIBLE) for dynamic buffers
  • Future work: staging buffer helper for efficient DEVICE_LOCAL uploads

Metadata

Metadata

Assignees

No one assigned

    Labels

    enhancementNew feature or request

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions