Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion docs/adr/ADR-044-colour-contract-per-backend.md
Original file line number Diff line number Diff line change
Expand Up @@ -146,7 +146,7 @@ The helper keys on the *target's* format. Pinning a format and calling the helpe
- GL `gl_bind_layer_source(…, compose=false)`;
- vk_native `get_image_view`, not `get_true_image_view`.

**vk_native window-space layers have the same gap.** `vk_hud_blend` samples every source through a hard-coded `R8G8B8A8_UNORM` view (`auxiliary/vk/vk_hud_blend.c`) and blends into the atlas in encoded space. Measured on the model viewer's displayxr-common button bar: the atlas bytes are identical with swapchain 37 and 43, and the 55 % black fill lands at exactly 0.45 × the background's encoded value. The same hard-coded view would swap R and B for a `B8G8R8A8` window-space swapchain. D3D11, D3D12 and GL window-space *are* honest. Measured with the `windowspace_handle_*_win` probes, which draw opaque solid layers with authored byte 38: on D3D11, D3D12 and GL an `_SRGB` swapchain reads back 38 and a UNORM one reads back 108 (exactly one encode). On vk_native both read back 38.
**vk_native window-space layers had the same gap until #1795.** `vk_hud_blend` sampled every source through a hard-coded `R8G8B8A8_UNORM` view and blended into the atlas in encoded space: the model viewer's displayxr-common button bar gave byte-identical atlases with swapchain 37 and 43, and the hard-coded view would have swapped R and B for a `B8G8R8A8` source. Since #1795 the in-process vk_native pass raw-copies the atlas into the private compose target, blends each layer through its `_SRGB` view with the source sampled as its **declared** format, and raw-copies back (`vk_compositor_render_window_space_linear`). All four Windows backends are now honest. Measured with the `windowspace_handle_*_win` probes, which draw opaque layers authored at byte 38: an `_SRGB` swapchain reads back 38 and a UNORM one reads back 108 (exactly one encode) on D3D11, D3D12, GL and vk_native. The macOS service's workspace decorations (shell chrome, cursor, overlays, drawn into the shared atlas by `comp_multi`) got the same fix in #1795. The atlas is created `MUTABLE_FORMAT` with its `_SRGB` sibling, and the decorations render through that view, each sampled as its declared format. That change ships together with displayxr-shell-pvt#121, which declares the shell's CoreGraphics-filled macOS surfaces `_SRGB` on runtime ≥ v2.23.0; a UNORM shell on the fixed service would be washed out. Linux registers no workspace surfaces, so it has no such site.

Encoded bytes therefore pass through whether the swapchain says UNORM or `_SRGB`. Neither is washed out today, but a true-linear UNORM Local2D app renders too dark. Local2D is also flattened after the atlas, so §5's atlas capture cannot see it. App guidance does not change: submit Local2D as `_SRGB` with raw-copied bytes (displayxr-unity v2.21.0 already does). **Order matters:** apps move first. Making the flatten honest before they do would wash out every app that still declares UNORM Local2D.
- **The displayxr-common Windows window-space HUD swapchain** stays `R8G8B8A8_UNORM` with display-referred bytes. On any format-honest backend it is encoded twice; its move to `_SRGB` is pending (displayxr-common).
Expand Down
41 changes: 34 additions & 7 deletions src/xrt/auxiliary/vk/vk_hud_blend.c
Original file line number Diff line number Diff line change
Expand Up @@ -346,10 +346,16 @@ vk_hud_blend_init_ex(struct vk_hud_blend *blend,
static VkDescriptorSet
get_or_create_image_desc(struct vk_hud_blend *blend,
struct vk_bundle *vk,
VkImage hud_image)
VkImage hud_image,
VkFormat view_fmt)
{
// #1795: the legacy view. Encoded-space callers keep it; honest callers
// pass the swapchain's declared format.
if (view_fmt == VK_FORMAT_UNDEFINED) {
view_fmt = VK_FORMAT_R8G8B8A8_UNORM;
}
for (uint32_t i = 0; i < blend->image_count; i++) {
if (blend->cached_images[i].image == hud_image) {
if (blend->cached_images[i].image == hud_image && blend->cached_images[i].format == view_fmt) {
return blend->cached_images[i].desc_set;
}
}
Expand All @@ -363,7 +369,7 @@ get_or_create_image_desc(struct vk_hud_blend *blend,
.sType = VK_STRUCTURE_TYPE_IMAGE_VIEW_CREATE_INFO,
.image = hud_image,
.viewType = VK_IMAGE_VIEW_TYPE_2D,
.format = VK_FORMAT_R8G8B8A8_UNORM,
.format = view_fmt,
.subresourceRange = {VK_IMAGE_ASPECT_COLOR_BIT, 0, 1, 0, 1},
};
VkImageView view = VK_NULL_HANDLE;
Expand Down Expand Up @@ -401,6 +407,7 @@ get_or_create_image_desc(struct vk_hud_blend *blend,
vk->vkUpdateDescriptorSets(vk->device, 1, &write, 0, NULL);

blend->cached_images[blend->image_count].image = hud_image;
blend->cached_images[blend->image_count].format = view_fmt;
blend->cached_images[blend->image_count].view = view;
blend->cached_images[blend->image_count].desc_set = desc_set;
blend->image_count++;
Expand Down Expand Up @@ -429,7 +436,7 @@ vk_hud_blend_draw(struct vk_hud_blend *blend,
(void)hud_w;
(void)hud_h;

VkDescriptorSet desc_set = get_or_create_image_desc(blend, vk, hud_image);
VkDescriptorSet desc_set = get_or_create_image_desc(blend, vk, hud_image, VK_FORMAT_UNDEFINED);
if (desc_set == VK_NULL_HANDLE) {
return;
}
Expand Down Expand Up @@ -554,13 +561,32 @@ vk_hud_blend_draw_no_layout_ex(struct vk_hud_blend *blend,
uint32_t dst_w,
uint32_t dst_h,
bool premultiplied)
{
vk_hud_blend_draw_no_layout_fmt(blend, vk, cmd, fb, fb_w, fb_h, hud_image, VK_FORMAT_UNDEFINED, dst_x, dst_y,
dst_w, dst_h, premultiplied);
}

void
vk_hud_blend_draw_no_layout_fmt(struct vk_hud_blend *blend,
struct vk_bundle *vk,
VkCommandBuffer cmd,
VkFramebuffer fb,
uint32_t fb_w,
uint32_t fb_h,
VkImage hud_image,
VkFormat src_view_fmt,
int32_t dst_x,
int32_t dst_y,
uint32_t dst_w,
uint32_t dst_h,
bool premultiplied)
{
if (!blend->initialized || hud_image == VK_NULL_HANDLE || fb == VK_NULL_HANDLE ||
dst_w == 0 || dst_h == 0) {
return;
}

VkDescriptorSet desc_set = get_or_create_image_desc(blend, vk, hud_image);
VkDescriptorSet desc_set = get_or_create_image_desc(blend, vk, hud_image, src_view_fmt);
if (desc_set == VK_NULL_HANDLE) {
return;
}
Expand Down Expand Up @@ -594,8 +620,10 @@ vk_hud_blend_forget_image(struct vk_hud_blend *blend, struct vk_bundle *vk, VkIm
return;
}

for (uint32_t i = 0; i < blend->image_count; i++) {
// #1795: an image may be cached under more than one view format.
for (uint32_t i = 0; i < blend->image_count;) {
if (blend->cached_images[i].image != hud_image) {
i++;
continue;
}
vk->vkDestroyImageView(vk->device, blend->cached_images[i].view, NULL);
Expand All @@ -604,7 +632,6 @@ vk_hud_blend_forget_image(struct vk_hud_blend *blend, struct vk_bundle *vk, VkIm
// Order does not matter: move the last entry into the hole.
blend->image_count--;
blend->cached_images[i] = blend->cached_images[blend->image_count];
return;
}
}

Expand Down
34 changes: 33 additions & 1 deletion src/xrt/auxiliary/vk/vk_hud_blend.h
Original file line number Diff line number Diff line change
Expand Up @@ -58,6 +58,7 @@ struct vk_hud_blend
//! Lookup is O(N) but N is bounded (≤ swapchain image count).
struct {
VkImage image;
VkFormat format; //!< View format the entry was created with (#1795).
VkImageView view;
VkDescriptorSet desc_set;
} cached_images[VK_HUD_BLEND_MAX_IMAGES];
Expand Down Expand Up @@ -163,6 +164,37 @@ vk_hud_blend_draw_no_layout_ex(struct vk_hud_blend *blend,
uint32_t dst_h,
bool premultiplied);

/*!
* vk_hud_blend_draw_no_layout_ex(), sampling the source through a view of
* @p src_view_fmt instead of the legacy hard-coded R8G8B8A8_UNORM (#1795).
*
* Pass the swapchain's DECLARED format to read it honestly: an `_SRGB`
* source then decodes to linear on sample, a UNORM source reads the linear
* values it holds, and a B8G8R8A8 source is no longer read with R and B
* swapped. Honest sampling only belongs in a target that blends in linear
* and encodes on write (an `_SRGB` attachment); an encoded UNORM target
* keeps the legacy view. VK_FORMAT_UNDEFINED selects the legacy view.
*
* The view must be legal for the image: its own creation format always is;
* a sibling needs VK_IMAGE_CREATE_MUTABLE_FORMAT_BIT.
*
* @ingroup aux_vk
*/
void
vk_hud_blend_draw_no_layout_fmt(struct vk_hud_blend *blend,
struct vk_bundle *vk,
VkCommandBuffer cmd,
VkFramebuffer fb,
uint32_t fb_w,
uint32_t fb_h,
VkImage hud_image,
VkFormat src_view_fmt,
int32_t dst_x,
int32_t dst_y,
uint32_t dst_w,
uint32_t dst_h,
bool premultiplied);

/*!
* Draw without managing target image layout transitions or framebuffer
* caching. Use this when blending into a non-swapchain target (e.g. an
Expand Down Expand Up @@ -197,7 +229,7 @@ vk_hud_blend_draw_no_layout(struct vk_hud_blend *blend,
uint32_t dst_h);

/*!
* Drop the cached view + descriptor set for a HUD source image (#1782).
* Drop the cached view(s) + descriptor set(s) for a HUD source image (#1782).
*
* The cache is keyed by the raw VkImage handle, and drivers hand a destroyed
* image's handle value to the next image they create. An entry that outlives
Expand Down
18 changes: 18 additions & 0 deletions src/xrt/compositor/multi/comp_multi_private.h
Original file line number Diff line number Diff line change
Expand Up @@ -1305,6 +1305,24 @@ struct multi_system_compositor
//! shared_atlas_fb (render-pass compatible with shared_chrome_blend).
struct comp_multi_content_blend shared_content_blend;
bool shared_content_blend_initialized;

//! #1795 (ADR-044 §7): format-honest decorations. The atlas holds ENCODED
//! bytes (the DP and the content pass see its plain view), so the chrome /
//! overlay / cursor pass renders through an `_SRGB` view of it instead:
//! sources are sampled as their DECLARED format, blend in linear light and
//! are encoded once on write. The atlas is created MUTABLE_FORMAT with that
//! sibling in its view-format list, so the view is legal and no copy is
//! needed. All UNDEFINED / VK_NULL_HANDLE under the legacy hatch.
VkFormat shared_atlas_deco_format; //!< `_SRGB` sibling the atlas is mutable for.
VkImageView shared_atlas_deco_view; //!< `_SRGB` view of the atlas (decoration target).
VkFramebuffer shared_atlas_deco_fb; //!< Framebuffer over shared_atlas_deco_view.
VkImageView shared_atlas_deco_fb_view; //!< The deco view the fb was built for (recreate key).
struct vk_hud_blend shared_deco_blend; //!< Flat decorations into the `_SRGB` view.
bool shared_deco_blend_initialized;
struct comp_multi_content_blend shared_deco_content_blend; //!< Quad / SBS decorations, `_SRGB` view.
bool shared_deco_content_blend_initialized;
bool shared_deco_honest_failed; //!< Sticky: setup failed once, legacy decorations from then on.
bool shared_deco_logged; //!< The one-time regime WARN has been emitted.
//! @}
#endif
};
Expand Down
Loading
Loading