Skip to content
Merged
148 changes: 126 additions & 22 deletions src/xrt/auxiliary/vk/vk_local2d_composite.c
Original file line number Diff line number Diff line change
Expand Up @@ -350,6 +350,56 @@ vk_local2d_composite_init(struct vk_local2d_composite *lc,
return true;
}

bool
vk_local2d_composite_init_srgb_flatten(struct vk_local2d_composite *lc, struct vk_bundle *vk, VkFormat srgb_fmt)
{
if (!lc->initialized) {
return false;
}
if (lc->flatten_srgb_premul_pipe != VK_NULL_HANDLE && lc->flatten_srgb_fmt == srgb_fmt) {
return true;
}

// Same blend states as the scratch_fmt flatten — only the attachment format
// (and so the render pass the pipelines are compatible with) differs.
VkPipelineColorBlendAttachmentState premul_blend = {
.blendEnable = VK_TRUE,
.colorWriteMask = VK_COLOR_COMPONENT_R_BIT | VK_COLOR_COMPONENT_G_BIT | VK_COLOR_COMPONENT_B_BIT |
VK_COLOR_COMPONENT_A_BIT,
.srcColorBlendFactor = VK_BLEND_FACTOR_ONE,
.dstColorBlendFactor = VK_BLEND_FACTOR_ONE_MINUS_SRC_ALPHA,
.colorBlendOp = VK_BLEND_OP_ADD,
.srcAlphaBlendFactor = VK_BLEND_FACTOR_ONE,
.dstAlphaBlendFactor = VK_BLEND_FACTOR_ONE_MINUS_SRC_ALPHA,
.alphaBlendOp = VK_BLEND_OP_ADD,
};
VkPipelineColorBlendAttachmentState unpremul_blend = premul_blend;
unpremul_blend.srcColorBlendFactor = VK_BLEND_FACTOR_SRC_ALPHA;

if (make_render_pass(vk, srgb_fmt, VK_ATTACHMENT_LOAD_OP_LOAD, &lc->flatten_srgb_rp) != VK_SUCCESS ||
make_pipeline(vk, lc->vert_mod, lc->flatten_frag_mod, lc->flatten_layout, lc->flatten_srgb_rp,
&premul_blend, &lc->flatten_srgb_premul_pipe) != VK_SUCCESS ||
make_pipeline(vk, lc->vert_mod, lc->flatten_frag_mod, lc->flatten_layout, lc->flatten_srgb_rp,
&unpremul_blend, &lc->flatten_srgb_unpremul_pipe) != VK_SUCCESS) {
U_LOG_E("[local2d] _SRGB flatten (format %d) creation failed", (int)srgb_fmt);
if (lc->flatten_srgb_premul_pipe != VK_NULL_HANDLE) {
vk->vkDestroyPipeline(vk->device, lc->flatten_srgb_premul_pipe, NULL);
lc->flatten_srgb_premul_pipe = VK_NULL_HANDLE;
}
if (lc->flatten_srgb_unpremul_pipe != VK_NULL_HANDLE) {
vk->vkDestroyPipeline(vk->device, lc->flatten_srgb_unpremul_pipe, NULL);
lc->flatten_srgb_unpremul_pipe = VK_NULL_HANDLE;
}
if (lc->flatten_srgb_rp != VK_NULL_HANDLE) {
vk->vkDestroyRenderPass(vk->device, lc->flatten_srgb_rp, NULL);
lc->flatten_srgb_rp = VK_NULL_HANDLE;
}
return false;
}
lc->flatten_srgb_fmt = srgb_fmt;
return true;
}

void
vk_local2d_composite_begin_frame(struct vk_local2d_composite *lc, struct vk_bundle *vk)
{
Expand Down Expand Up @@ -663,26 +713,32 @@ vk_local2d_composite_raster_mask_zones(struct vk_local2d_composite *lc,
vk->vkCmdEndRenderPass(cmd);
}

void
vk_local2d_composite_flatten_draw(struct vk_local2d_composite *lc,
struct vk_bundle *vk,
VkCommandBuffer cmd,
VkFramebuffer scratch_fb,
uint32_t fb_w,
uint32_t fb_h,
VkImageView src_view,
int32_t dst_x,
int32_t dst_y,
uint32_t dst_w,
uint32_t dst_h,
float src_x,
float src_y,
float src_w,
float src_h,
bool unpremultiplied)
// The flatten draw, parameterised on the render pass + pipeline pair so the
// scratch_fmt flatten and the #1795 `_SRGB` flatten share one body.
static void
flatten_draw_impl(struct vk_local2d_composite *lc,
struct vk_bundle *vk,
VkCommandBuffer cmd,
VkRenderPass rp,
VkPipeline premul_pipe,
VkPipeline unpremul_pipe,
VkFramebuffer scratch_fb,
uint32_t fb_w,
uint32_t fb_h,
VkImageView src_view,
int32_t dst_x,
int32_t dst_y,
uint32_t dst_w,
uint32_t dst_h,
float src_x,
float src_y,
float src_w,
float src_h,
bool unpremultiplied)
{
if (!lc->initialized || scratch_fb == VK_NULL_HANDLE || src_view == VK_NULL_HANDLE || dst_w == 0 ||
dst_h == 0) {
if (!lc->initialized || rp == VK_NULL_HANDLE || premul_pipe == VK_NULL_HANDLE ||
unpremul_pipe == VK_NULL_HANDLE || scratch_fb == VK_NULL_HANDLE || src_view == VK_NULL_HANDLE ||
dst_w == 0 || dst_h == 0) {
return;
}

Expand All @@ -693,7 +749,7 @@ vk_local2d_composite_flatten_draw(struct vk_local2d_composite *lc,

VkRenderPassBeginInfo rp_bi = {
.sType = VK_STRUCTURE_TYPE_RENDER_PASS_BEGIN_INFO,
.renderPass = lc->flatten_rp,
.renderPass = rp,
.framebuffer = scratch_fb,
.renderArea = {{0, 0}, {fb_w, fb_h}},
};
Expand All @@ -706,8 +762,7 @@ vk_local2d_composite_flatten_draw(struct vk_local2d_composite *lc,
VkRect2D scissor = {{dst_x, dst_y}, {dst_w, dst_h}};
vk->vkCmdSetScissor(cmd, 0, 1, &scissor);

vk->vkCmdBindPipeline(cmd, VK_PIPELINE_BIND_POINT_GRAPHICS,
unpremultiplied ? lc->flatten_unpremul_pipe : lc->flatten_premul_pipe);
vk->vkCmdBindPipeline(cmd, VK_PIPELINE_BIND_POINT_GRAPHICS, unpremultiplied ? unpremul_pipe : premul_pipe);
vk->vkCmdBindDescriptorSets(cmd, VK_PIPELINE_BIND_POINT_GRAPHICS, lc->flatten_layout, 0, 1, &set, 0,
NULL);

Expand All @@ -718,6 +773,52 @@ vk_local2d_composite_flatten_draw(struct vk_local2d_composite *lc,
vk->vkCmdEndRenderPass(cmd);
}

void
vk_local2d_composite_flatten_draw(struct vk_local2d_composite *lc,
struct vk_bundle *vk,
VkCommandBuffer cmd,
VkFramebuffer scratch_fb,
uint32_t fb_w,
uint32_t fb_h,
VkImageView src_view,
int32_t dst_x,
int32_t dst_y,
uint32_t dst_w,
uint32_t dst_h,
float src_x,
float src_y,
float src_w,
float src_h,
bool unpremultiplied)
{
flatten_draw_impl(lc, vk, cmd, lc->flatten_rp, lc->flatten_premul_pipe, lc->flatten_unpremul_pipe, scratch_fb,
fb_w, fb_h, src_view, dst_x, dst_y, dst_w, dst_h, src_x, src_y, src_w, src_h,
unpremultiplied);
}

void
vk_local2d_composite_flatten_draw_srgb(struct vk_local2d_composite *lc,
struct vk_bundle *vk,
VkCommandBuffer cmd,
VkFramebuffer scratch_fb,
uint32_t fb_w,
uint32_t fb_h,
VkImageView src_view,
int32_t dst_x,
int32_t dst_y,
uint32_t dst_w,
uint32_t dst_h,
float src_x,
float src_y,
float src_w,
float src_h,
bool unpremultiplied)
{
flatten_draw_impl(lc, vk, cmd, lc->flatten_srgb_rp, lc->flatten_srgb_premul_pipe,
lc->flatten_srgb_unpremul_pipe, scratch_fb, fb_w, fb_h, src_view, dst_x, dst_y, dst_w, dst_h,
src_x, src_y, src_w, src_h, unpremultiplied);
}

void
vk_local2d_composite_draw(struct vk_local2d_composite *lc,
struct vk_bundle *vk,
Expand Down Expand Up @@ -839,13 +940,16 @@ vk_local2d_composite_fini(struct vk_local2d_composite *lc, struct vk_bundle *vk)
DESTROY(vkDestroyPipeline, lc->composite_pipe);
DESTROY(vkDestroyPipeline, lc->flatten_premul_pipe);
DESTROY(vkDestroyPipeline, lc->flatten_unpremul_pipe);
DESTROY(vkDestroyPipeline, lc->flatten_srgb_premul_pipe);
DESTROY(vkDestroyPipeline, lc->flatten_srgb_unpremul_pipe);
DESTROY(vkDestroyPipelineLayout, lc->composite_layout);
DESTROY(vkDestroyPipelineLayout, lc->flatten_layout);
DESTROY(vkDestroyDescriptorSetLayout, lc->composite_dsl);
DESTROY(vkDestroyDescriptorSetLayout, lc->flatten_dsl);
DESTROY(vkDestroyDescriptorPool, lc->desc_pool);
DESTROY(vkDestroyRenderPass, lc->composite_rp);
DESTROY(vkDestroyRenderPass, lc->flatten_rp);
DESTROY(vkDestroyRenderPass, lc->flatten_srgb_rp);
DESTROY(vkDestroyRenderPass, lc->mask_rp);
DESTROY(vkDestroySampler, lc->sampler_point);
DESTROY(vkDestroySampler, lc->sampler_linear);
Expand Down
51 changes: 51 additions & 0 deletions src/xrt/auxiliary/vk/vk_local2d_composite.h
Original file line number Diff line number Diff line change
Expand Up @@ -75,6 +75,14 @@ struct vk_local2d_composite
VkPipeline flatten_premul_pipe; //!< One / OneMinusSrcAlpha.
VkPipeline flatten_unpremul_pipe; //!< SrcAlpha / OneMinusSrcAlpha.

//! #1795 / ADR-044 §7 — OPTIONAL linear-light flatten (see
//! vk_local2d_composite_init_srgb_flatten). VK_NULL_HANDLE until a caller
//! opts in; the scratch_fmt flatten above is untouched either way.
VkFormat flatten_srgb_fmt;
VkRenderPass flatten_srgb_rp; //!< flatten_srgb_fmt, LOAD_OP_LOAD.
VkPipeline flatten_srgb_premul_pipe; //!< One / OneMinusSrcAlpha.
VkPipeline flatten_srgb_unpremul_pipe; //!< SrcAlpha / OneMinusSrcAlpha.

VkDescriptorPool desc_pool; //!< Reset each frame by begin_frame.

bool initialized;
Expand All @@ -96,6 +104,23 @@ vk_local2d_composite_init(struct vk_local2d_composite *lc,
VkFormat target_fmt,
VkFormat scratch_fmt);

/*!
* #1795 / ADR-044 §7 — opt in to a second flatten render pass + pipeline pair
* whose attachment format is the `_SRGB` sibling @p srgb_fmt of the scratch.
*
* Drawn through vk_local2d_composite_flatten_draw_srgb into an `_SRGB` view of
* a MUTABLE scratch, the blend runs in linear light and the attachment encodes
* once on write — the encode is a property of the render target, never shader
* arithmetic. The scratch's UNORM view still reads ENCODED bytes, so every
* downstream reader is unchanged. Callers that never call this keep exactly
* the scratch_fmt flatten they had. Call after vk_local2d_composite_init.
*
* @return true when the `_SRGB` flatten is usable.
* @ingroup aux_vk
*/
bool
vk_local2d_composite_init_srgb_flatten(struct vk_local2d_composite *lc, struct vk_bundle *vk, VkFormat srgb_fmt);

/*!
* Reset the per-frame descriptor pool. Call once before the frame's flatten /
* composite draws (the vk_native compositor drains the GPU each frame, so any
Expand Down Expand Up @@ -206,6 +231,32 @@ vk_local2d_composite_flatten_draw(struct vk_local2d_composite *lc,
float src_h,
bool unpremultiplied);

/*!
* #1795 — vk_local2d_composite_flatten_draw into an `_SRGB` attachment: same
* contract, but @p scratch_fb must be built over `flatten_srgb_rp` with an
* `_SRGB` view, and @p src_view should be the layer's FORMAT-HONEST view (an
* `_SRGB` source decodes on sample, a UNORM source is read as the linear values
* it holds). No-op unless vk_local2d_composite_init_srgb_flatten succeeded.
* @ingroup aux_vk
*/
void
vk_local2d_composite_flatten_draw_srgb(struct vk_local2d_composite *lc,
struct vk_bundle *vk,
VkCommandBuffer cmd,
VkFramebuffer scratch_fb,
uint32_t fb_w,
uint32_t fb_h,
VkImageView src_view,
int32_t dst_x,
int32_t dst_y,
uint32_t dst_w,
uint32_t dst_h,
float src_x,
float src_y,
float src_w,
float src_h,
bool unpremultiplied);

/*!
* Masked composite into the target. Samples @p twod_view / @p mask_view /
* @p weave_view at 1:1 over the region and lerps M*weave + (1-M)*twod, or — when
Expand Down
58 changes: 47 additions & 11 deletions src/xrt/compositor/d3d11/comp_d3d11_compositor.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -5902,6 +5902,14 @@ d3d11_sync_zone_mask_to_dp(struct comp_d3d11_compositor *c)
// on every REALLOCATION so the bridge knows to re-open. Under the split these
// are allocated at the PANEL extent exactly once, so the generation moves during
// warmup and never again.
// #1795 / ADR-044: do the Local2D flattens (over and 2D-under) blend in linear
// through an `_SRGB` view? Off only under the legacy colour hatch.
static inline bool
d3d11_local2d_honest(void)
{
return !u_color_legacy_unorm_encoded();
}

static bool
d3d11_ensure_rt_srv_scratch(ID3D11Device *device,
ID3D11Texture2D **tex,
Expand All @@ -5912,13 +5920,19 @@ d3d11_ensure_rt_srv_scratch(ID3D11Device *device,
DXGI_FORMAT fmt,
const char *what,
HANDLE *share = nullptr,
uint64_t *gen = nullptr)
uint64_t *gen = nullptr,
DXGI_FORMAT rtv_fmt = DXGI_FORMAT_UNKNOWN)
{
// #1795: an RTV in a different member of the family (the `_SRGB` sibling,
// so a pass blends in linear and encodes on write) needs a TYPELESS
// texture; the SRV stays @p fmt, so every reader sees the same bytes.
const bool split_views = rtv_fmt != DXGI_FORMAT_UNKNOWN && rtv_fmt != fmt;
const DXGI_FORMAT tex_fmt = split_views ? d3d_dxgi_format_to_typeless_dxgi(fmt) : fmt;
bool need_alloc = *tex == nullptr || *rtv == nullptr;
if (!need_alloc) {
D3D11_TEXTURE2D_DESC cur;
(*tex)->GetDesc(&cur);
need_alloc = (cur.Width != w || cur.Height != h || cur.Format != fmt);
need_alloc = (cur.Width != w || cur.Height != h || cur.Format != tex_fmt);
}
if (!need_alloc) {
return true;
Expand All @@ -5944,7 +5958,7 @@ d3d11_ensure_rt_srv_scratch(ID3D11Device *device,
td.Height = h;
td.MipLevels = 1;
td.ArraySize = 1;
td.Format = fmt;
td.Format = tex_fmt;
td.SampleDesc.Count = 1;
td.Usage = D3D11_USAGE_DEFAULT;
td.BindFlags = D3D11_BIND_SHADER_RESOURCE | D3D11_BIND_RENDER_TARGET;
Expand Down Expand Up @@ -5975,14 +5989,21 @@ d3d11_ensure_rt_srv_scratch(ID3D11Device *device,
(*gen)++;
}
}
hr = device->CreateShaderResourceView(*tex, nullptr, srv);
D3D11_SHADER_RESOURCE_VIEW_DESC sd = {};
sd.Format = fmt;
sd.ViewDimension = D3D11_SRV_DIMENSION_TEXTURE2D;
sd.Texture2D.MipLevels = 1;
hr = device->CreateShaderResourceView(*tex, &sd, srv);
if (FAILED(hr) || *srv == nullptr) {
U_LOG_W("%s: RT scratch SRV failed: 0x%08x", what, hr);
(*tex)->Release();
*tex = nullptr;
return false;
}
hr = device->CreateRenderTargetView(*tex, nullptr, rtv);
D3D11_RENDER_TARGET_VIEW_DESC rd = {};
rd.Format = split_views ? rtv_fmt : fmt;
rd.ViewDimension = D3D11_RTV_DIMENSION_TEXTURE2D;
hr = device->CreateRenderTargetView(*tex, &rd, rtv);
if (FAILED(hr) || *rtv == nullptr) {
U_LOG_W("%s: RT scratch RTV failed: 0x%08x", what, hr);
(*srv)->Release();
Expand Down Expand Up @@ -6591,10 +6612,16 @@ d3d11_flatten_one_local2d_layer(struct comp_d3d11_compositor *c,
return;
}
uint32_t img_idx = layer->data.local_2d.sub.image_index;
// sRGB-passthrough: get_srv returns the swapchain's UNORM sibling SRV
// (no auto-decode), the same SRV the projection draws sample.
ID3D11ShaderResourceView *src_srv =
static_cast<ID3D11ShaderResourceView *>(comp_d3d11_swapchain_get_srv(sc, img_idx));
// #1795: the view follows the TARGET. Into an `_SRGB` RTV (the honest
// path) sample the layer as its declared format — an `_SRGB` source
// decodes, a UNORM source is read as the linear values it holds — so the
// blend is linear and the RTV encodes once. Into a UNORM RTV (legacy
// hatch) keep the non-decoding view: bytes pass through as before.
D3D11_RENDER_TARGET_VIEW_DESC rtv_desc = {};
rtv->GetDesc(&rtv_desc);
const bool linear_target = d3d_dxgi_format_is_srgb(rtv_desc.Format);
ID3D11ShaderResourceView *src_srv = static_cast<ID3D11ShaderResourceView *>(
linear_target ? comp_d3d11_swapchain_get_compose_srv(sc, img_idx) : comp_d3d11_swapchain_get_srv(sc, img_idx));
if (src_srv == nullptr) {
return; // swapchain not SAMPLED — nothing to flatten
}
Expand Down Expand Up @@ -6941,10 +6968,13 @@ d3d11_flatten_backdrop_2d(struct comp_d3d11_compositor *c, uint32_t dst_w, uint3
const bool split = c->split_active;
const uint32_t alloc_w = split ? c->split_panel_w : region_w;
const uint32_t alloc_h = split ? c->split_panel_h : region_h;
// #1795: same contract as the over-flatten — `_SRGB` RTV, encoded bytes.
if (!d3d11_ensure_rt_srv_scratch(c->device, &c->backdrop_scratch, &c->backdrop_scratch_srv,
&c->backdrop_scratch_rtv, alloc_w, alloc_h, DXGI_FORMAT_R8G8B8A8_UNORM,
"backdrop scratch", split ? &c->backdrop_scratch_share : nullptr,
split ? &c->backdrop_scratch_gen : nullptr)) {
split ? &c->backdrop_scratch_gen : nullptr,
d3d11_local2d_honest() ? DXGI_FORMAT_R8G8B8A8_UNORM_SRGB
: DXGI_FORMAT_UNKNOWN)) {
return nullptr;
}

Expand Down Expand Up @@ -7245,11 +7275,17 @@ d3d11_composite_zone_mask(struct comp_d3d11_compositor *c,
*/
const uint32_t alloc_w = c->split_active ? c->split_panel_w : region_w;
const uint32_t alloc_h = c->split_active ? c->split_panel_h : region_h;
// #1795: the flatten writes through the `_SRGB` sibling, so the
// Local2D layers blend in linear and are encoded once on write;
// the scratch still holds ENCODED bytes for every reader (weave
// snapshot lerp, bridge plane). The legacy hatch keeps UNORM.
const DXGI_FORMAT l2d_rtv_fmt =
d3d11_local2d_honest() ? d3d_dxgi_format_srgb_rtv(unorm_fmt) : DXGI_FORMAT_UNKNOWN;
if (!d3d11_ensure_rt_srv_scratch(c->device, &c->local2d_scratch, &c->local2d_scratch_srv,
&c->local2d_scratch_rtv, alloc_w, alloc_h, unorm_fmt,
"local2d scratch",
c->split_active ? &c->local2d_scratch_share : nullptr,
c->split_active ? &c->local2d_scratch_gen : nullptr)) {
c->split_active ? &c->local2d_scratch_gen : nullptr, l2d_rtv_fmt)) {
c->repaint.composite_bail = 4;
return false;
}
Expand Down
Loading
Loading