Keyboard shortcuts

Press ← or → to navigate between chapters

Press S or / to search in the book

Press ? to show this help

Press Esc to hide this help

Opacity Micromaps

VK_EXT_opacity_micromap stores per-microtriangle opacity states separately from triangle geometry. Check device support through device.physical.vk_ext_opacity_micromap. Micromap owns the Vulkan object and supports device and host operations. See the headless opacity_micromap.rs example for a complete device build and BLAS attachment.

Support

Check support before creating or recording micromap work:

#![allow(unused)]
fn main() {
let Some(extension) = &device.physical.vk_ext_opacity_micromap else {
    println!("VK_EXT_opacity_micromap is unavailable");

    return;
};

if extension.features.micromap {
    println!(
        "maximum two-state subdivision level: {}",
        extension.properties.max_opacity2_state_subdivision_level,
    );
}
}

micromap_host_commands is a separate feature. It is required by the synchronous host methods but not by CommandRef device operations. Capture replay is reported but is not exposed because BufferInfo cannot request a replayable backing-buffer address.

Data And Creation

OpacityMicromapUsage specifies a count, subdivision level, and two-state or four-state format. Build usage counts describe all micromap triangles constructed in the micromap. BLAS geometry usage counts instead describe the micromap triangles referenced by that geometry, so they can be a subset of the build counts when geometry selects entries through micromap indices. They are identical only for a simple one-to-one attachment such as the example. For each micromap-build input triangle, upload one vk::MicromapTriangleEXT describing the encoded-data offset, subdivision level, and format. Encoded opacity states are tightly bit-packed according to the Vulkan specification.

Query storage and scratch sizes before allocating buffers:

let usage_counts = [OpacityMicromapUsage::new(1, 0, vk::OpacityMicromapFormatEXT::TYPE_2_STATE)];
let flags = vk::BuildMicromapFlagsEXT::empty();
let sizes = Micromap::build_sizes(
    device, vk::AccelerationStructureBuildTypeKHR::DEVICE, flags, &usage_counts,
);
let micromap = Micromap::create(device, MicromapInfo::device_mem(sizes.micromap_size))?;
// Allocate separate scratch storage for sizes.build_scratch_size.

MicromapBuildSizes contains micromap_size, build_scratch_size, and discardable. Micromap::build_sizes is safe. Match flags and usage counts to the intended build; query with DEVICE or HOST_OR_DEVICE for device builds, and HOST or HOST_OR_DEVICE for host builds. Device input addresses and serialized device addresses require 256-byte alignment; build scratch uses min_accel_struct_scratch_offset_alignment. Micromaps have no update mode: every build is vk::BuildMicromapModeEXT::BUILD.

Device Operations

CommandRef provides:

OperationAccess declarations
build_micromapsdestination MicromapBuildWrite; encoded data and triangle array MicromapBuildInputRead; scratch MicromapBuildScratchReadWrite
copy_micromapsource MicromapBuildRead; destination MicromapBuildWrite
serialize_micromapsource MicromapBuildRead; destination buffer MicromapBuildBufferWrite
deserialize_micromapsource buffer MicromapBuildBufferRead; destination MicromapBuildWrite
write_micromaps_propertiesevery micromap MicromapBuildRead; the raw query pool is managed by the caller

build_micromaps accepts &[MicromapBuildInfo<'_>]. Each descriptor contains dst_micromap, flags, borrowed usage_counts: &[OpacityMicromapUsage], and device-only data, triangle_array, and scratch_data addresses plus triangle_array_stride. Usage metadata is borrowed during recording and converted to temporary Vulkan arrays; it does not keep buffers alive. Batch entries are not ordered or synchronized with each other and must not depend on each other’s writes.

Device addresses do not bind or identify graph resources. Bind every input, output, and scratch resource and declare it on the execution that uses the address. Micromap-build input and scratch buffers must remain alive until the micromap build completes; they may then be released independently of MicromapBuildSizes::discardable. That flag instead controls the lifetime of the micromap object: when true, it may be released after the acceleration-structure build or update completes; when false, the acceleration structure may retain references to its storage, so keep the micromap alive until ray traversal has concluded. Before a BLAS build consumes a completed micromap, declare AccelerationStructureBuildMicromapRead on the micromap. Also bind and declare the ordinary index, vertex, optional micromap-index, BLAS scratch, and BLAS resources.

Build, copy, serialization, deserialization, and property-query command methods are unsafe. The graph cannot verify input contents, device-address ranges, whether a source was built successfully, or required build flags. Follow each method’s Safety contract; access declarations alone do not satisfy these preconditions.

Host Operations

When micromap_host_commands is true, a micromap created with MicromapInfo::host_mem supports synchronous build_host, clone_from_host, compact_from_host, serialize_host, deserialize_host, property, serialization_size, and compacted_size. Host operations reject device-local micromaps, including either side of a copy. compatibility checks serialized version data before deserialization. These methods are unsafe where Rust cannot verify that a source was built successfully, serialized data is complete, or a source was built with ALLOW_COMPACTION. Follow each method’s Safety contract, the documented 16-byte host serialization alignment, and pointer lifetimes.

build_host(&HostMicromapBuildInfo<'_>) uses a separate descriptor with flags, borrowed usage_counts: &[OpacityMicromapUsage], data and triangle_array as *const c_void, triangle_array_stride, and writable scratch_data: *mut c_void. Raw pointers do not keep memory alive: inputs must be initialized and readable, scratch writable, and all ranges aligned, sufficiently sized, non-overlapping (except read-only inputs), and alive for the entire synchronous call. Null scratch is allowed only when the matching query requires zero bytes. The destination must not be in GPU use; synchronize conflicting host/device accesses explicitly.

Build with vk::BuildMicromapFlagsEXT::ALLOW_COMPACTION before requesting a compacted-size property or compacting a micromap on the host or device. The micromap type does not enforce this Vulkan requirement; the caller must check it before using these unsafe methods.

Host operations update the resource’s tracked access state, but they are not graph executions. The next submitted graph access synchronizes against that state.

BLAS Attachment

Call AccelerationStructureTriangles::opacity_micromap to attach an AccelerationStructureOpacityMicromap<'a>, then use .into() to convert the triangles to AccelerationStructureGeometryData<'a>. Its Triangles(AccelerationStructureTriangles<'a>) variant stores the optional attachment; there is no enum-wide attachment method.

The attachment borrows &[OpacityMicromapUsage] and holds the native micromap handle and optional index-buffer device address. Keep the usage slice alive through the query or recording call; the handle and address do not retain resources. The unsafe AS size query requires live, valid attachment handles from the querying device; it does not ignore them. base_triangle is added to non-negative micromap indices, not special opacity indices. With index_type == NONE_KHR, triangle i uses base_triangle + i.

For ray tracing pipelines that traverse opacity micromaps, opt in with RayTracingPipelineInfo::builder().opacity_micromap(true); this is disabled by default.

Do not use AccelerationStructureGeometry::opaque or set vk::GeometryFlagsKHR::OPAQUE on geometry using an opacity micromap. During tracing, do not use force-opaque instance flags or ray flags such as gl_RayFlagsOpaqueEXT / VK_RAY_FLAG_FORCE_OPAQUE_BIT_KHR; those settings force opaque treatment and prevent the micromap from controlling opacity.

The example is intentionally build-only. Rendering the resulting cutout additionally requires a ray tracing pipeline, shader binding table, TLAS, output image, and shaders, which are independent of micromap construction and graph synchronization.