diff --git a/.gitignore b/.gitignore index be3efd2..95390de 100644 --- a/.gitignore +++ b/.gitignore @@ -5,3 +5,28 @@ /WiVRn-0.24.tar.gz /monado-src-848a24aa106758fd6c7afcab6d95880c57dbe450.tar.bz2 /WiVRn-0.24.1.tar.gz +/WiVRn-0.25.tar.gz +/monado-src-2a6932d46dad9aa957205e8a47ec2baa33041076.tar.bz2 +/WiVRn-25.6.tar.gz +/monado-src-bb9bcee2a3be75592de819d9e3fb2c8ed27bb7dc.tar.bz2 +/WiVRn-25.6.1.tar.gz +/WiVRn-25.8.tar.gz +/monado-src-5c137fe28b232fe460f9b03defa7749adc32ee48.tar.bz2 +/WiVRn-25.9.tar.gz +/monado-src-7a4018e2d89151e60e562fac79eba90ca7a328d8.tar.bz2 +/WiVRn-de32f6e24f1f457dcea78974b945e1cb3ac6dee8.tar.gz +/monado-src-06e62fc7d9c5cbcbc43405bb86dfde3bf01ce043.tar.bz2 +/WiVRn-25.11.tar.gz +/WiVRn-25.11.1.tar.gz +/WiVRn-25.12.tar.gz +/monado-src-20e0dacbdd2de863923790326beec76e848b056a.tar.bz2 +/WiVRn-26.2.tar.gz +/monado-src-9dcc3e1de2f7449d9757f5db332c867b4d794fb3.tar.bz2 +/WiVRn-26.2.1.tar.gz +/monado-src-723652b545a79609f9f04cb89fcbf807d9d6451a.tar.bz2 +/WiVRn-26.2.2.tar.gz +/WiVRn-v26.2.3.tar.gz +/WiVRn-v26.6.tar.gz +/monado-src-1b526bb3a0ff326ecd05af4c2c541407f53c6d4b.tar.bz2 +/WiVRn-v26.6.1.tar.gz +/WiVRn-v26.6.2.tar.gz diff --git a/0001-c-multi-early-wake-of-compositor.patch b/0001-c-multi-early-wake-of-compositor.patch index 173b124..03cb41e 100644 --- a/0001-c-multi-early-wake-of-compositor.patch +++ b/0001-c-multi-early-wake-of-compositor.patch @@ -1,7 +1,7 @@ -From a0e439f723394452ab79a09c04df5135c3613d11 Mon Sep 17 00:00:00 2001 +From de3b592fc26d7b26b4850fcc78d7e6b21147f76b Mon Sep 17 00:00:00 2001 From: Patrick Nicolas Date: Tue, 22 Oct 2024 15:23:19 +0200 -Subject: [PATCH 01/13] c/multi: early wake of compositor +Subject: [PATCH 1/8] c/multi: early wake of compositor --- .../compositor/multi/comp_multi_compositor.c | 14 +++++++++ @@ -10,10 +10,10 @@ Subject: [PATCH 01/13] c/multi: early wake of compositor 3 files changed, 41 insertions(+), 7 deletions(-) diff --git a/src/xrt/compositor/multi/comp_multi_compositor.c b/src/xrt/compositor/multi/comp_multi_compositor.c -index 2895ccb0f..0c940cb3a 100644 +index 33675d43f..71c5ebf9e 100644 --- a/src/xrt/compositor/multi/comp_multi_compositor.c +++ b/src/xrt/compositor/multi/comp_multi_compositor.c -@@ -231,7 +231,21 @@ wait_for_scheduled_free(struct multi_compositor *mc) +@@ -236,7 +236,21 @@ wait_for_scheduled_free(struct multi_compositor *mc) os_mutex_lock(&mc->slot_lock); slot_move_and_clear_locked(mc, &mc->scheduled, &mc->progress); os_mutex_unlock(&mc->slot_lock); @@ -36,10 +36,10 @@ index 2895ccb0f..0c940cb3a 100644 static void * diff --git a/src/xrt/compositor/multi/comp_multi_private.h b/src/xrt/compositor/multi/comp_multi_private.h -index 3351defa4..e8f0c8295 100644 +index 6c7ec05f8..588878e38 100644 --- a/src/xrt/compositor/multi/comp_multi_private.h +++ b/src/xrt/compositor/multi/comp_multi_private.h -@@ -359,6 +359,10 @@ struct multi_system_compositor +@@ -365,6 +365,10 @@ struct multi_system_compositor */ struct os_mutex list_and_timing_lock; @@ -51,19 +51,19 @@ index 3351defa4..e8f0c8295 100644 { int64_t predicted_display_time_ns; diff --git a/src/xrt/compositor/multi/comp_multi_system.c b/src/xrt/compositor/multi/comp_multi_system.c -index b06b03a58..249e07d7d 100644 +index 5a4f35b81..cbda39c24 100644 --- a/src/xrt/compositor/multi/comp_multi_system.c +++ b/src/xrt/compositor/multi/comp_multi_system.c -@@ -23,6 +23,8 @@ - #include "util/u_trace_marker.h" - #include "util/u_distortion_mesh.h" +@@ -14,6 +14,8 @@ + #include "xrt/xrt_defines.h" + #include "xrt/xrt_session.h" +#include "math/m_api.h" + - #ifdef XRT_OS_LINUX - #include "util/u_linux.h" - #endif -@@ -392,17 +394,24 @@ broadcast_timings_to_pacers(struct multi_system_compositor *msc, + #include "os/os_time.h" + #include "os/os_threading.h" + +@@ -426,17 +428,24 @@ broadcast_timings_to_pacers(struct multi_system_compositor *msc, } static void @@ -93,7 +93,7 @@ index b06b03a58..249e07d7d 100644 } static void -@@ -525,10 +534,13 @@ multi_main_loop(struct multi_system_compositor *msc) +@@ -562,10 +571,13 @@ multi_main_loop(struct multi_system_compositor *msc) broadcast_timings_to_clients(msc, predicted_display_time_ns); // Now we can wait. @@ -109,7 +109,7 @@ index b06b03a58..249e07d7d 100644 // Now we know the diff, broadcast to pacers. broadcast_timings_to_pacers(msc, predicted_display_time_ns, predicted_display_period_ns, diff_ns); -@@ -705,6 +717,8 @@ system_compositor_destroy(struct xrt_system_compositor *xsc) +@@ -785,6 +797,8 @@ system_compositor_destroy(struct xrt_system_compositor *xsc) xrt_comp_native_destroy(&msc->xcn); os_mutex_destroy(&msc->list_and_timing_lock); @@ -118,8 +118,8 @@ index b06b03a58..249e07d7d 100644 free(msc); } -@@ -759,6 +773,8 @@ comp_multi_create_system_compositor(struct xrt_compositor_native *xcn, - msc->sessions.state = do_warm_start ? MULTI_SYSTEM_STATE_INIT_WARM_START : MULTI_SYSTEM_STATE_STOPPED; +@@ -843,6 +857,8 @@ comp_multi_create_system_compositor(struct xrt_compositor_native *xcn, + msc->get_view_config_callback = get_view_config_callback; os_mutex_init(&msc->list_and_timing_lock); + os_mutex_init(&msc->wake_mutex); @@ -128,5 +128,5 @@ index b06b03a58..249e07d7d 100644 //! @todo Make the clients not go from IDLE to READY before we have completed a first frame. // Make sure there is at least some sort of valid frame data here. -- -2.48.1 +2.54.0 diff --git a/0003-Use-extern-socket-fd.patch b/0002-Use-extern-socket-fd.patch similarity index 71% rename from 0003-Use-extern-socket-fd.patch rename to 0002-Use-extern-socket-fd.patch index db26adc..8f507e1 100644 --- a/0003-Use-extern-socket-fd.patch +++ b/0002-Use-extern-socket-fd.patch @@ -1,26 +1,26 @@ -From 95631921f2dd8f422281d5d94bca167bcb773471 Mon Sep 17 00:00:00 2001 +From 22d9bf87e9dd6a0d5970258a6b6cd6ca82c3bb26 Mon Sep 17 00:00:00 2001 From: Patrick Nicolas Date: Tue, 8 Oct 2024 22:13:15 +0200 -Subject: [PATCH 03/10] Use extern socket fd +Subject: [PATCH 2/8] Use extern socket fd --- src/xrt/ipc/server/ipc_server_mainloop_linux.c | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/src/xrt/ipc/server/ipc_server_mainloop_linux.c b/src/xrt/ipc/server/ipc_server_mainloop_linux.c -index ad6b473b7..ea6fad65b 100644 +index a0bef3dde..453dd38ca 100644 --- a/src/xrt/ipc/server/ipc_server_mainloop_linux.c +++ b/src/xrt/ipc/server/ipc_server_mainloop_linux.c -@@ -55,6 +55,8 @@ - */ - DEBUG_GET_ONCE_BOOL_OPTION(skip_stdin, "XRT_NO_STDIN", false) +@@ -50,6 +50,8 @@ + #endif + +extern int listen_socket; + /* * * Static functions. -@@ -63,6 +65,12 @@ DEBUG_GET_ONCE_BOOL_OPTION(skip_stdin, "XRT_NO_STDIN", false) +@@ -58,6 +60,12 @@ static int get_systemd_socket(struct ipc_server_mainloop *ml, int *out_fd) { @@ -34,5 +34,5 @@ index ad6b473b7..ea6fad65b 100644 // We may have been launched with socket activation int num_fds = sd_listen_fds(0); -- -2.48.0 +2.54.0 diff --git a/0002-ipc-server-Always-listen-to-stdin.patch b/0002-ipc-server-Always-listen-to-stdin.patch deleted file mode 100644 index 7908f3c..0000000 --- a/0002-ipc-server-Always-listen-to-stdin.patch +++ /dev/null @@ -1,25 +0,0 @@ -From 6e5926f76f8a7ee66eff164ebe630c65d024237f Mon Sep 17 00:00:00 2001 -From: Patrick Nicolas -Date: Tue, 2 Apr 2024 17:14:42 +0200 -Subject: [PATCH 02/10] ipc/server: Always listen to stdin - ---- - src/xrt/ipc/server/ipc_server_mainloop_linux.c | 2 +- - 1 file changed, 1 insertion(+), 1 deletion(-) - -diff --git a/src/xrt/ipc/server/ipc_server_mainloop_linux.c b/src/xrt/ipc/server/ipc_server_mainloop_linux.c -index d364fbed3..ad6b473b7 100644 ---- a/src/xrt/ipc/server/ipc_server_mainloop_linux.c -+++ b/src/xrt/ipc/server/ipc_server_mainloop_linux.c -@@ -186,7 +186,7 @@ init_epoll(struct ipc_server_mainloop *ml) - - struct epoll_event ev = {0}; - -- if (!ml->launched_by_socket && !debug_get_bool_option_skip_stdin()) { -+ if (!debug_get_bool_option_skip_stdin()) { - // Can't do this when launched by systemd socket activation by - // default. - // This polls stdin. --- -2.48.0 - diff --git a/0005-change-environment-blend-mode-selection-logic.patch b/0003-change-environment-blend-mode-selection-logic.patch similarity index 88% rename from 0005-change-environment-blend-mode-selection-logic.patch rename to 0003-change-environment-blend-mode-selection-logic.patch index 13bab63..2529eaa 100644 --- a/0005-change-environment-blend-mode-selection-logic.patch +++ b/0003-change-environment-blend-mode-selection-logic.patch @@ -1,17 +1,17 @@ -From 9a3e2f9bdc3149aaa6db1c54c95b0b4f9ab379ff Mon Sep 17 00:00:00 2001 +From 3c7450d800b0e3f35440f41d3bfcf5b5abe6dfe8 Mon Sep 17 00:00:00 2001 From: galister <85970-galister@users.noreply.gitlab.freedesktop.org> Date: Sat, 23 Nov 2024 02:42:12 +0900 -Subject: [PATCH 05/10] change environment blend mode selection logic +Subject: [PATCH 3/8] change environment blend mode selection logic --- src/xrt/compositor/multi/comp_multi_system.c | 26 +++++++++++++------- 1 file changed, 17 insertions(+), 9 deletions(-) diff --git a/src/xrt/compositor/multi/comp_multi_system.c b/src/xrt/compositor/multi/comp_multi_system.c -index b7d2c2174..62a5a0c27 100644 +index cbda39c24..c0c6c9f60 100644 --- a/src/xrt/compositor/multi/comp_multi_system.c +++ b/src/xrt/compositor/multi/comp_multi_system.c -@@ -232,23 +232,31 @@ find_active_blend_mode(struct multi_compositor **overlay_sorted_clients, size_t +@@ -231,23 +231,31 @@ find_active_blend_mode(struct multi_compositor **overlay_sorted_clients, size_t if (overlay_sorted_clients == NULL) return XRT_BLEND_MODE_OPAQUE; @@ -53,5 +53,5 @@ index b7d2c2174..62a5a0c27 100644 } -- -2.48.0 +2.54.0 diff --git a/0004-c-render-Add-storage-usage-to-distortion-mesh.patch b/0004-c-render-Add-storage-usage-to-distortion-mesh.patch deleted file mode 100644 index bea9296..0000000 --- a/0004-c-render-Add-storage-usage-to-distortion-mesh.patch +++ /dev/null @@ -1,25 +0,0 @@ -From c95362727746c19ff039168ebec3e6e21e6bcc3c Mon Sep 17 00:00:00 2001 -From: galister <3123227-galister@users.noreply.gitlab.com> -Date: Mon, 19 Aug 2024 18:51:45 +0900 -Subject: [PATCH 04/10] c/render: Add storage usage to distortion mesh - ---- - src/xrt/compositor/render/render_distortion.c | 2 +- - 1 file changed, 1 insertion(+), 1 deletion(-) - -diff --git a/src/xrt/compositor/render/render_distortion.c b/src/xrt/compositor/render/render_distortion.c -index 8ac24db8e..5a7e400c3 100644 ---- a/src/xrt/compositor/render/render_distortion.c -+++ b/src/xrt/compositor/render/render_distortion.c -@@ -42,7 +42,7 @@ create_distortion_image_and_view(struct vk_bundle *vk, - vk, // vk_bundle - extent, // extent - format, // format -- VK_IMAGE_USAGE_SAMPLED_BIT | VK_IMAGE_USAGE_TRANSFER_DST_BIT, // usage -+ VK_IMAGE_USAGE_SAMPLED_BIT | VK_IMAGE_USAGE_STORAGE_BIT | VK_IMAGE_USAGE_TRANSFER_DST_BIT, // usage - &device_memory, // out_device_memory - &image); // out_image - VK_CHK_AND_RET(ret, "vk_create_image_simple"); --- -2.48.0 - diff --git a/0004-st-oxr-forward-0-refresh-rate.patch b/0004-st-oxr-forward-0-refresh-rate.patch new file mode 100644 index 0000000..6a4fecf --- /dev/null +++ b/0004-st-oxr-forward-0-refresh-rate.patch @@ -0,0 +1,47 @@ +From b26876c29205f2d2fb07fbdbe009d5635eb32bfd Mon Sep 17 00:00:00 2001 +From: Patrick Nicolas +Date: Sat, 15 Mar 2025 22:19:48 +0100 +Subject: [PATCH 4/8] st/oxr: forward 0 refresh rate + +--- + src/xrt/compositor/main/comp_compositor.c | 2 +- + src/xrt/state_trackers/oxr/oxr_api_session.c | 6 +----- + 2 files changed, 2 insertions(+), 6 deletions(-) + +diff --git a/src/xrt/compositor/main/comp_compositor.c b/src/xrt/compositor/main/comp_compositor.c +index b44271622..a1e112e89 100644 +--- a/src/xrt/compositor/main/comp_compositor.c ++++ b/src/xrt/compositor/main/comp_compositor.c +@@ -391,7 +391,7 @@ compositor_request_display_refresh_rate(struct xrt_compositor *xc, float display + if (c->target && c->target->request_refresh_rate) { + xrt_result_t result = comp_target_request_refresh_rate(c->target, display_refresh_rate_hz); + // Assume refresh rate change is immediate +- if (result == XRT_SUCCESS) ++ if (result == XRT_SUCCESS && display_refresh_rate_hz > 0) + c->frame_interval_ns = U_TIME_1S_IN_NS / display_refresh_rate_hz; + return result; + } +diff --git a/src/xrt/state_trackers/oxr/oxr_api_session.c b/src/xrt/state_trackers/oxr/oxr_api_session.c +index 09297f885..9f3a7ebc2 100644 +--- a/src/xrt/state_trackers/oxr/oxr_api_session.c ++++ b/src/xrt/state_trackers/oxr/oxr_api_session.c +@@ -455,15 +455,11 @@ oxr_xrRequestDisplayRefreshRateFB(XrSession session, float displayRefreshRate) + OXR_VERIFY_SESSION_AND_INIT_LOG(&log, session, sess, "xrRequestDisplayRefreshRateFB"); + OXR_VERIFY_SESSION_NOT_LOST(&log, sess); + +- if (displayRefreshRate == 0.0f) { +- return XR_SUCCESS; +- } +- + /* + * For the requested display refresh rate, truncating to two decimal + * places and checking if it's in the supported refresh rates. + */ +- bool found = false; ++ bool found = displayRefreshRate == 0.0f; + for (int i = 0; i < (int)sess->sys->xsysc->info.refresh_rate_count; ++i) { + if ((int)(displayRefreshRate * 100.0f) == (int)(sess->sys->xsysc->info.refresh_rates_hz[i] * 100.0f)) { + found = true; +-- +2.54.0 + diff --git a/0005-d-steamvr_lh-prevent-crash-on-vive-pro2-WiVRn.patch b/0005-d-steamvr_lh-prevent-crash-on-vive-pro2-WiVRn.patch new file mode 100644 index 0000000..698c0ad --- /dev/null +++ b/0005-d-steamvr_lh-prevent-crash-on-vive-pro2-WiVRn.patch @@ -0,0 +1,25 @@ +From 7230746571d1256d32557fd99528ac90a1d75957 Mon Sep 17 00:00:00 2001 +From: no name +Date: Sun, 28 Sep 2025 11:33:10 +0200 +Subject: [PATCH 5/8] d/steamvr_lh: prevent crash on vive pro2/WiVRn + +--- + src/xrt/drivers/steamvr_lh/steamvr_lh.cpp | 2 +- + 1 file changed, 1 insertion(+), 1 deletion(-) + +diff --git a/src/xrt/drivers/steamvr_lh/steamvr_lh.cpp b/src/xrt/drivers/steamvr_lh/steamvr_lh.cpp +index 347d22068..d34d3331e 100644 +--- a/src/xrt/drivers/steamvr_lh/steamvr_lh.cpp ++++ b/src/xrt/drivers/steamvr_lh/steamvr_lh.cpp +@@ -985,7 +985,7 @@ steamvr_lh_create_devices(struct xrt_prober *xp, struct xrt_system_devices **out + + // Include the HMD + if (svrs->ctx->hmd) { +- if (svrs->ctx->hmd->variant == VIVE_VARIANT_PRO2 && !svrs->ctx->hmd->init_vive_pro_2(xp)) { ++ if (xp && svrs->ctx->hmd->variant == VIVE_VARIANT_PRO2 && !svrs->ctx->hmd->init_vive_pro_2(xp)) { + U_LOG_IFL_W(level, "Found Vive Pro 2, but failed to initialize."); + } + +-- +2.54.0 + diff --git a/0006-Use-mipmaps-for-distortion-shader.patch b/0006-Use-mipmaps-for-distortion-shader.patch deleted file mode 100644 index bd8a0ff..0000000 --- a/0006-Use-mipmaps-for-distortion-shader.patch +++ /dev/null @@ -1,2763 +0,0 @@ -From 3f6883949ac235cc1e3f1ef8480ad08bd25da503 Mon Sep 17 00:00:00 2001 -From: galister <3123227-galister@users.noreply.gitlab.com> -Date: Thu, 31 Oct 2024 04:37:27 +0900 -Subject: [PATCH 06/10] Use mipmaps for distortion shader - ---- - src/external/CMakeLists.txt | 23 + - src/external/nvpro_pyramid/CMakeLists.txt | 5 + - src/external/nvpro_pyramid/LICENSE | 216 +++++ - src/external/nvpro_pyramid/nvpro_pyramid.glsl | 889 ++++++++++++++++++ - .../nvpro_pyramid/nvpro_pyramid_dispatch.hpp | 292 ++++++ - .../srgba8_mipmap_fast_pipeline.comp | 14 + - .../srgba8_mipmap_general_pipeline.comp | 14 + - .../nvpro_pyramid/srgba8_mipmap_preamble.glsl | 128 +++ - src/xrt/auxiliary/vk/vk_helpers.c | 1 + - src/xrt/compositor/CMakeLists.txt | 6 +- - src/xrt/compositor/main/comp_renderer.c | 58 +- - src/xrt/compositor/main/comp_settings.c | 6 + - src/xrt/compositor/main/comp_settings.h | 3 + - src/xrt/compositor/render/render_compute.c | 14 + - src/xrt/compositor/render/render_gfx.c | 6 +- - src/xrt/compositor/render/render_interface.h | 24 + - src/xrt/compositor/render/render_resources.c | 130 ++- - src/xrt/compositor/render/render_shaders.c | 24 +- - src/xrt/compositor/shaders/distortion.comp | 39 +- - src/xrt/compositor/util/comp_mipmap.cpp | 25 + - src/xrt/compositor/util/comp_mipmap.h | 22 + - src/xrt/compositor/util/comp_render.h | 12 + - src/xrt/compositor/util/comp_render_cs.c | 92 ++ - src/xrt/compositor/util/comp_render_helpers.h | 26 +- - src/xrt/compositor/util/comp_scratch.c | 69 +- - src/xrt/compositor/util/comp_scratch.h | 7 +- - 26 files changed, 2071 insertions(+), 74 deletions(-) - create mode 100644 src/external/nvpro_pyramid/CMakeLists.txt - create mode 100644 src/external/nvpro_pyramid/LICENSE - create mode 100644 src/external/nvpro_pyramid/nvpro_pyramid.glsl - create mode 100644 src/external/nvpro_pyramid/nvpro_pyramid_dispatch.hpp - create mode 100644 src/external/nvpro_pyramid/srgba8_mipmap_fast_pipeline.comp - create mode 100644 src/external/nvpro_pyramid/srgba8_mipmap_general_pipeline.comp - create mode 100644 src/external/nvpro_pyramid/srgba8_mipmap_preamble.glsl - create mode 100644 src/xrt/compositor/util/comp_mipmap.cpp - create mode 100644 src/xrt/compositor/util/comp_mipmap.h - -diff --git a/src/external/CMakeLists.txt b/src/external/CMakeLists.txt -index 0705627ae..51f89714f 100644 ---- a/src/external/CMakeLists.txt -+++ b/src/external/CMakeLists.txt -@@ -211,3 +211,26 @@ add_library(xrt-external-vdf INTERFACE) - target_include_directories( - xrt-external-vdf SYSTEM INTERFACE ${CMAKE_CURRENT_SOURCE_DIR}/valve-file-vdf - ) -+ -+# nvpro pyramid -+if(XRT_HAVE_VULKAN) -+ -+ spirv_shaders( -+ SHADER_HEADERS -+ SPIRV_VERSION -+ 1.3 -+ SOURCES -+ nvpro_pyramid/srgba8_mipmap_fast_pipeline.comp -+ nvpro_pyramid/srgba8_mipmap_general_pipeline.comp -+ ) -+ add_library(xrt-external-nvpro-pyramid INTERFACE -+ ${SHADER_HEADERS} -+ nvpro_pyramid/nvpro_pyramid_dispatch.hpp -+ ) -+ target_include_directories( -+ xrt-external-nvpro-pyramid SYSTEM INTERFACE -+ ${CMAKE_CURRENT_SOURCE_DIR}/nvpro_pyramid -+ ${CMAKE_CURRENT_BINARY_DIR}/nvpro_pyramid -+ ) -+ add_subdirectory(nvpro_pyramid) -+endif() -diff --git a/src/external/nvpro_pyramid/CMakeLists.txt b/src/external/nvpro_pyramid/CMakeLists.txt -new file mode 100644 -index 000000000..2d4ea96e2 ---- /dev/null -+++ b/src/external/nvpro_pyramid/CMakeLists.txt -@@ -0,0 +1,5 @@ -+# Copyright 2024, Collabora, Ltd. -+# SPDX-License-Identifier: BSL-1.0 -+ -+# CMakeLists.txt required for CMake to create the directory in the build tree. -+# Generated shaders will be placed there. -diff --git a/src/external/nvpro_pyramid/LICENSE b/src/external/nvpro_pyramid/LICENSE -new file mode 100644 -index 000000000..9a5f6e6e2 ---- /dev/null -+++ b/src/external/nvpro_pyramid/LICENSE -@@ -0,0 +1,216 @@ -+Copyright 2021 NVIDIA CORPORATION -+ -+Licensed under the Apache License, Version 2.0 (the "License"); -+you may not use this file except in compliance with the License. -+You may obtain a copy of the License at -+ -+ http://www.apache.org/licenses/LICENSE-2.0 -+ -+Unless required by applicable law or agreed to in writing, software -+distributed under the License is distributed on an "AS IS" BASIS, -+WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -+See the License for the specific language governing permissions and -+limitations under the License. -+ -+ -+ Apache License -+ Version 2.0, January 2004 -+ http://www.apache.org/licenses/ -+ -+ TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION -+ -+ 1. Definitions. -+ -+ "License" shall mean the terms and conditions for use, reproduction, -+ and distribution as defined by Sections 1 through 9 of this document. -+ -+ "Licensor" shall mean the copyright owner or entity authorized by -+ the copyright owner that is granting the License. -+ -+ "Legal Entity" shall mean the union of the acting entity and all -+ other entities that control, are controlled by, or are under common -+ control with that entity. For the purposes of this definition, -+ "control" means (i) the power, direct or indirect, to cause the -+ direction or management of such entity, whether by contract or -+ otherwise, or (ii) ownership of fifty percent (50%) or more of the -+ outstanding shares, or (iii) beneficial ownership of such entity. -+ -+ "You" (or "Your") shall mean an individual or Legal Entity -+ exercising permissions granted by this License. -+ -+ "Source" form shall mean the preferred form for making modifications, -+ including but not limited to software source code, documentation -+ source, and configuration files. -+ -+ "Object" form shall mean any form resulting from mechanical -+ transformation or translation of a Source form, including but -+ not limited to compiled object code, generated documentation, -+ and conversions to other media types. -+ -+ "Work" shall mean the work of authorship, whether in Source or -+ Object form, made available under the License, as indicated by a -+ copyright notice that is included in or attached to the work -+ (an example is provided in the Appendix below). -+ -+ "Derivative Works" shall mean any work, whether in Source or Object -+ form, that is based on (or derived from) the Work and for which the -+ editorial revisions, annotations, elaborations, or other modifications -+ represent, as a whole, an original work of authorship. For the purposes -+ of this License, Derivative Works shall not include works that remain -+ separable from, or merely link (or bind by name) to the interfaces of, -+ the Work and Derivative Works thereof. -+ -+ "Contribution" shall mean any work of authorship, including -+ the original version of the Work and any modifications or additions -+ to that Work or Derivative Works thereof, that is intentionally -+ submitted to Licensor for inclusion in the Work by the copyright owner -+ or by an individual or Legal Entity authorized to submit on behalf of -+ the copyright owner. For the purposes of this definition, "submitted" -+ means any form of electronic, verbal, or written communication sent -+ to the Licensor or its representatives, including but not limited to -+ communication on electronic mailing lists, source code control systems, -+ and issue tracking systems that are managed by, or on behalf of, the -+ Licensor for the purpose of discussing and improving the Work, but -+ excluding communication that is conspicuously marked or otherwise -+ designated in writing by the copyright owner as "Not a Contribution." -+ -+ "Contributor" shall mean Licensor and any individual or Legal Entity -+ on behalf of whom a Contribution has been received by Licensor and -+ subsequently incorporated within the Work. -+ -+ 2. Grant of Copyright License. Subject to the terms and conditions of -+ this License, each Contributor hereby grants to You a perpetual, -+ worldwide, non-exclusive, no-charge, royalty-free, irrevocable -+ copyright license to reproduce, prepare Derivative Works of, -+ publicly display, publicly perform, sublicense, and distribute the -+ Work and such Derivative Works in Source or Object form. -+ -+ 3. Grant of Patent License. Subject to the terms and conditions of -+ this License, each Contributor hereby grants to You a perpetual, -+ worldwide, non-exclusive, no-charge, royalty-free, irrevocable -+ (except as stated in this section) patent license to make, have made, -+ use, offer to sell, sell, import, and otherwise transfer the Work, -+ where such license applies only to those patent claims licensable -+ by such Contributor that are necessarily infringed by their -+ Contribution(s) alone or by combination of their Contribution(s) -+ with the Work to which such Contribution(s) was submitted. If You -+ institute patent litigation against any entity (including a -+ cross-claim or counterclaim in a lawsuit) alleging that the Work -+ or a Contribution incorporated within the Work constitutes direct -+ or contributory patent infringement, then any patent licenses -+ granted to You under this License for that Work shall terminate -+ as of the date such litigation is filed. -+ -+ 4. Redistribution. You may reproduce and distribute copies of the -+ Work or Derivative Works thereof in any medium, with or without -+ modifications, and in Source or Object form, provided that You -+ meet the following conditions: -+ -+ (a) You must give any other recipients of the Work or -+ Derivative Works a copy of this License; and -+ -+ (b) You must cause any modified files to carry prominent notices -+ stating that You changed the files; and -+ -+ (c) You must retain, in the Source form of any Derivative Works -+ that You distribute, all copyright, patent, trademark, and -+ attribution notices from the Source form of the Work, -+ excluding those notices that do not pertain to any part of -+ the Derivative Works; and -+ -+ (d) If the Work includes a "NOTICE" text file as part of its -+ distribution, then any Derivative Works that You distribute must -+ include a readable copy of the attribution notices contained -+ within such NOTICE file, excluding those notices that do not -+ pertain to any part of the Derivative Works, in at least one -+ of the following places: within a NOTICE text file distributed -+ as part of the Derivative Works; within the Source form or -+ documentation, if provided along with the Derivative Works; or, -+ within a display generated by the Derivative Works, if and -+ wherever such third-party notices normally appear. The contents -+ of the NOTICE file are for informational purposes only and -+ do not modify the License. You may add Your own attribution -+ notices within Derivative Works that You distribute, alongside -+ or as an addendum to the NOTICE text from the Work, provided -+ that such additional attribution notices cannot be construed -+ as modifying the License. -+ -+ You may add Your own copyright statement to Your modifications and -+ may provide additional or different license terms and conditions -+ for use, reproduction, or distribution of Your modifications, or -+ for any such Derivative Works as a whole, provided Your use, -+ reproduction, and distribution of the Work otherwise complies with -+ the conditions stated in this License. -+ -+ 5. Submission of Contributions. Unless You explicitly state otherwise, -+ any Contribution intentionally submitted for inclusion in the Work -+ by You to the Licensor shall be under the terms and conditions of -+ this License, without any additional terms or conditions. -+ Notwithstanding the above, nothing herein shall supersede or modify -+ the terms of any separate license agreement you may have executed -+ with Licensor regarding such Contributions. -+ -+ 6. Trademarks. This License does not grant permission to use the trade -+ names, trademarks, service marks, or product names of the Licensor, -+ except as required for reasonable and customary use in describing the -+ origin of the Work and reproducing the content of the NOTICE file. -+ -+ 7. Disclaimer of Warranty. Unless required by applicable law or -+ agreed to in writing, Licensor provides the Work (and each -+ Contributor provides its Contributions) on an "AS IS" BASIS, -+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or -+ implied, including, without limitation, any warranties or conditions -+ of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A -+ PARTICULAR PURPOSE. You are solely responsible for determining the -+ appropriateness of using or redistributing the Work and assume any -+ risks associated with Your exercise of permissions under this License. -+ -+ 8. Limitation of Liability. In no event and under no legal theory, -+ whether in tort (including negligence), contract, or otherwise, -+ unless required by applicable law (such as deliberate and grossly -+ negligent acts) or agreed to in writing, shall any Contributor be -+ liable to You for damages, including any direct, indirect, special, -+ incidental, or consequential damages of any character arising as a -+ result of this License or out of the use or inability to use the -+ Work (including but not limited to damages for loss of goodwill, -+ work stoppage, computer failure or malfunction, or any and all -+ other commercial damages or losses), even if such Contributor -+ has been advised of the possibility of such damages. -+ -+ 9. Accepting Warranty or Additional Liability. While redistributing -+ the Work or Derivative Works thereof, You may choose to offer, -+ and charge a fee for, acceptance of support, warranty, indemnity, -+ or other liability obligations and/or rights consistent with this -+ License. However, in accepting such obligations, You may act only -+ on Your own behalf and on Your sole responsibility, not on behalf -+ of any other Contributor, and only if You agree to indemnify, -+ defend, and hold each Contributor harmless for any liability -+ incurred by, or claims asserted against, such Contributor by reason -+ of your accepting any such warranty or additional liability. -+ -+ END OF TERMS AND CONDITIONS -+ -+ APPENDIX: How to apply the Apache License to your work. -+ -+ To apply the Apache License to your work, attach the following -+ boilerplate notice, with the fields enclosed by brackets "[]" -+ replaced with your own identifying information. (Don't include -+ the brackets!) The text should be enclosed in the appropriate -+ comment syntax for the file format. We also recommend that a -+ file or class name and description of purpose be included on the -+ same "printed page" as the copyright notice for easier -+ identification within third-party archives. -+ -+ Copyright [yyyy] [name of copyright owner] -+ -+ Licensed under the Apache License, Version 2.0 (the "License"); -+ you may not use this file except in compliance with the License. -+ You may obtain a copy of the License at -+ -+ http://www.apache.org/licenses/LICENSE-2.0 -+ -+ Unless required by applicable law or agreed to in writing, software -+ distributed under the License is distributed on an "AS IS" BASIS, -+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -+ See the License for the specific language governing permissions and -+ limitations under the License. -diff --git a/src/external/nvpro_pyramid/nvpro_pyramid.glsl b/src/external/nvpro_pyramid/nvpro_pyramid.glsl -new file mode 100644 -index 000000000..6a5b17e4c ---- /dev/null -+++ b/src/external/nvpro_pyramid/nvpro_pyramid.glsl -@@ -0,0 +1,889 @@ -+// Copyright 2021 NVIDIA CORPORATION -+// SPDX-License-Identifier: Apache-2.0 -+ -+// Skeleton code for an image pyramid generation compute shader. This -+// only defines a "schedule" for doing work; you need to provide the -+// reduction (kernel) implementation and code for loading and storing -+// samples. This code is injected into nvproPyramidMain, the entry -+// point to the pyramid generation shader. -+// -+// Since GLSL doesn't really have advanced meta-programming -+// capabilities, this is all configured with preprocessor macros, -+// which must be defined before including this file. Your macros, this -+// include file, and a main function that does any needed -+// initialization before calling nvproPyramidMain, together form the -+// complete compute shader. -+// -+// When you compile the full compute pipeline, the pipeline must -+// include a 32-bit integer within its push constants; this is needed -+// to communicate to the shader which mip levels to work on. -+// By default, this push constant is at offset 0; see -+// NvproPyramidPipelines::pushConstantOffset and the -+// NVPRO_PYRAMID_PUSH_CONSTANT macro if you need to change this. -+// -+// This shader is designed to be dispatched by the host code in -+// nvpro_pyramid_dispatch.hpp -+// -+// The following macros are required: -+// -+// * NVPRO_PYRAMID_REDUCE(a0, v0, a1, v1, a2, v2, out_) -+// Set out_ to the reduction of the three inputs v0...v2, each using -+// a0...a2 as their weights. -+// -+// Please see the optional NVPRO_PYRAMID_LOAD_REDUCE4 macro as well. -+// -+// * NVPRO_PYRAMID_LOAD(coord : ivec2, level : int, out_) -+// Load the sample at the given texel and mip level, and store in out_ -+// You DO NOT have to do bounds-checking; and level is dynamically uniform. -+// Note there's nothing stopping you from loading from multiple images. -+// -+// * NVPRO_PYRAMID_STORE(coord : ivec2, level : int, in_) -+// Store the sample in_ into the given texel of the given mip level. -+// You DO NOT have to do bounds-checking; and level is dynamically uniform. -+// -+// * NVPRO_PYRAMID_TYPE -+// The data type of in_ and out_, above. -+// Recommend 32 bytes max to avoid excessive memory usage. -+// -+// * NVPRO_PYRAMID_LEVEL_SIZE(level : int) -+// Resolve to an ivec2 giving the size of the given mip level. -+// -+// * NVPRO_PYRAMID_IS_FAST_PIPELINE -+// If nonzero, this shader compiles to the pipeline stored in -+// NvproPyramidPipelines::fastPipeline. Otherwise, corresponds to pipeline -+// NvproPyramidPipelines::generalPipeline. -+// -+// NvproPyramidPipelines::generalPipeline has no special hardware requirements. -+// -+// NvproPyramidPipelines::fastPipeline requires these three abilities: -+// #extension GL_KHR_shader_subgroup_shuffle : enable -+// VkPhysicalDeviceSubgroupProperties::subgroupSize >= 16 -+// VkPhysicalDeviceSubgroupProperties::supportedOperations & VK_SUBGROUP_FEATURE_SHUFFLE_BIT -+// -+// The following macros are optional: -+// -+// * NVPRO_PYRAMID_PUSH_CONSTANT -+// If you need to declare your shader push constant manually, this macro -+// must resolve to the name of the 32-bit int variable used for push constant. -+// If not provided, the shader uses the 32-bits at offset 0. -+// Related: NvproPyramidPipelines::pushConstantOffset -+// -+// * NVPRO_PYRAMID_REDUCE2(v0, v1, out_) -+// Special case of reducing 2 samples of equal weight. -+// -+// * NVPRO_PYRAMID_REDUCE4(v00, v01, v10, v11, out_) -+// Special case of reducing a square of 4 samples of equal weight. -+// For fast pipelines only, this removes the need for NVPRO_PYRAMID_REDUCE. -+// -+// * NVPRO_PYRAMID_LOAD_REDUCE4(srcCoord : ivec2, srcLevel : int, out_) -+// Load the 2x2 texel square from srcCoord to (srcCoord + (1,1)), inclusive, -+// from mip level srcLevel. Reduce the 4 texels, and write the result to out_. -+// This can be used, for example, to take advantage of bilinear filtering. -+// For fast pipelines only, this removes the need for NVPRO_PYRAMID_LOAD. -+// -+// *********************************************************************** -+// * This macro is optional to simplify getting a minimum viable shader * -+// * in working condition, but it is STRONGLY recommended to define this * -+// * macro to use hardware samplers whenever practical. Please do not * -+// * do speed comparisons without defining NVPRO_PYRAMID_LOAD_REDUCE4!!! * -+// *********************************************************************** -+// -+// * NVPRO_PYRAMID_SHUFFLE_XOR(in_, mask_) -+// Conceptually identical to subgroupShuffleXor(in_, mask_) -+// Advanced feature, only needed for potential edge cases. -+// This macro is only used when NVPRO_PYRAMID_IS_FAST_PIPELINE != 0 -+// -+// The following must all be undefined or all be defined: -+// -+// * NVPRO_PYRAMID_SHARED_TYPE -+// The code needs to cache some texel outputs in shared memory to use -+// as inputs for subsequent mip levels. This can be used to customize -+// the cached texel type, e.g. to store a compacted reperesentation to -+// reduce memory usage. -+// -+// * NVPRO_PYRAMID_SHARED_LOAD(smem_, out_) -+// Convert the NVPRO_PYRAMID_SHARED_TYPE smem_ to NVPRO_PYRAMID_TYPE out_ -+// -+// * NVPRO_PYRAMID_SHARED_STORE(smem_, in_) -+// Convert the NVPRO_PYRAMID_TYPE in_ to NVPRO_PYRAMID_SHARED_TYPE smem_. -+// -+// Macro details: -+// -+// For function-like macros, it's guaranteed that the output does not -+// alias the inputs, and you do not have to parenthesize any -+// arguments. I parenthesize all arguments before calling your macros -+// (unless I missed a spot). -+// -+// You may optionally enclose multiple statements or variable -+// declarations in braces in function-like macros; to help with this, -+// I declare all variables in this file with an underscore suffix, to -+// avoid name collisions. -+ -+ -+ -+// Check required macros -+#ifndef NVPRO_PYRAMID_IS_FAST_PIPELINE -+#error "Missing required macro NVPRO_PYRAMID_IS_FAST_PIPELINE" -+#endif -+#ifndef NVPRO_PYRAMID_REDUCE -+ #if !defined(NVPRO_PYRAMID_REDUCE4) || !NVPRO_PYRAMID_IS_FAST_PIPELINE -+ #error "Missing required macro NVPRO_PYRAMID_REDUCE" -+ #endif -+#endif -+#ifndef NVPRO_PYRAMID_LOAD -+ #if !defined(NVPRO_PYRAMID_LOAD_REDUCE4) || !NVPRO_PYRAMID_IS_FAST_PIPELINE -+ #error "Missing required macro NVPRO_PYRAMID_LOAD" -+ #endif -+#endif -+#ifndef NVPRO_PYRAMID_STORE -+#error "Missing required macro NVPRO_PYRAMID_STORE" -+#endif -+#ifndef NVPRO_PYRAMID_TYPE -+#error "Missing required macro NVPRO_PYRAMID_TYPE" -+#endif -+#ifndef NVPRO_PYRAMID_LEVEL_SIZE -+#error "Missing required macro NVPRO_PYRAMID_LEVEL_SIZE" -+#endif -+ -+// Provide defaults for optional macros. -+#ifndef NVPRO_PYRAMID_PUSH_CONSTANT -+layout(push_constant) uniform NvproPyramidPushConstantBlock_ -+{ -+ uint nvproPyramidPushConstant_; -+}; -+#define NVPRO_PYRAMID_PUSH_CONSTANT nvproPyramidPushConstant_ -+#endif -+ -+// The mip level used as source data for the current dispatch. -+// Change nvpro_pyramid_dispatch.hpp nvproPyramidInputLevelShift if changed. -+#define NVPRO_PYRAMID_INPUT_LEVEL_ int(uint(NVPRO_PYRAMID_PUSH_CONSTANT) >> 5u) -+ -+// Number of subsequent mip levels to fill. -+#define NVPRO_PYRAMID_LEVEL_COUNT_ int(uint(NVPRO_PYRAMID_PUSH_CONSTANT) & 31u) -+ -+#ifndef NVPRO_PYRAMID_REDUCE2 -+#define NVPRO_PYRAMID_REDUCE2(v0, v1, out_) \ -+ NVPRO_PYRAMID_REDUCE(0.5, v0, 0.5, v1, 0, v1, out_) -+#endif -+ -+#ifndef NVPRO_PYRAMID_REDUCE4 -+#define NVPRO_PYRAMID_REDUCE4(v00, v01, v10, v11, out_) \ -+{ \ -+ NVPRO_PYRAMID_TYPE v0_, v1_; \ -+ NVPRO_PYRAMID_REDUCE2(v00, v01, v0_); \ -+ NVPRO_PYRAMID_REDUCE2(v10, v11, v1_); \ -+ NVPRO_PYRAMID_REDUCE2(v0_, v1_, out_); \ -+} -+#endif -+ -+#ifndef NVPRO_PYRAMID_LOAD_REDUCE4 -+#define NVPRO_PYRAMID_LOAD_REDUCE4(srcCoord_, srcLevel_, out_) \ -+{ \ -+ NVPRO_PYRAMID_TYPE v00_, v01_, v10_, v11_; \ -+ NVPRO_PYRAMID_LOAD((srcCoord_) + ivec2(0, 0), srcLevel_, v00_); \ -+ NVPRO_PYRAMID_LOAD((srcCoord_) + ivec2(0, 1), srcLevel_, v01_); \ -+ NVPRO_PYRAMID_LOAD((srcCoord_) + ivec2(1, 0), srcLevel_, v10_); \ -+ NVPRO_PYRAMID_LOAD((srcCoord_) + ivec2(1, 1), srcLevel_, v11_); \ -+ NVPRO_PYRAMID_REDUCE4(v00_, v01_, v10_, v11_, out_); \ -+} -+#endif -+ -+#if !defined(NVPRO_PYRAMID_SHUFFLE_XOR) && NVPRO_PYRAMID_IS_FAST_PIPELINE != 0 -+#define NVPRO_PYRAMID_SHUFFLE_XOR(in_, mask_) subgroupShuffleXor(in_, mask_) -+#endif -+ -+// Handle optional specialized shared memory type. -+#ifdef NVPRO_PYRAMID_SHARED_TYPE -+ #if !defined(NVPRO_PYRAMID_SHARED_LOAD) || !defined(NVPRO_PYRAMID_SHARED_STORE) -+ #error "Missing NVPRO_PYRAMID_SHARED_LOAD or NVPRO_PYRAMID_SHARED_STORE; needed when NVPRO_PYRAMID_SHARED_TYPE is defined." -+ #endif -+#else -+ #if defined(NVPRO_PYRAMID_SHARED_LOAD) || defined(NVPRO_PYRAMID_SHARED_STORE) -+ #error "Missing NVPRO_PYRAMID_SHARED_TYPE, needed when NVPRO_PYRAMID_SHARED_LOAD or NVPRO_PYRAMID_SHARED_STORE is defined." -+ #endif -+ #define NVPRO_PYRAMID_SHARED_TYPE NVPRO_PYRAMID_TYPE -+ #define NVPRO_PYRAMID_SHARED_LOAD(smem_, out_) out_ = smem_ -+ #define NVPRO_PYRAMID_SHARED_STORE(smem_, in_) smem_ = in_ -+#endif -+ -+#if NVPRO_PYRAMID_IS_FAST_PIPELINE != 0 -+ -+// Code for testing alternative designs during development, can ignore. -+#if defined(NVPRO_USE_FAST_PIPELINE_ALTERNATIVE_) && NVPRO_USE_FAST_PIPELINE_ALTERNATIVE_ != 0 -+#include "fast_pipeline_alternative.glsl" -+#else -+ -+// Efficient special case image pyramid generation kernel. Generates -+// up to 6 levels at once: each workgroup reads up to N samples (see below) -+// of the input mip level and generates the resulting samples for the next -+// up to 6 levels. Dispatch with y, z = 1. -+// -+// N = 1024 if NVPRO_PYRAMID_LEVEL_COUNT_ <= 5; 4096 otherwise. -+// -+// This only works when the input mip level has edges divisible by 2 -+// to the power of NVPRO_PYRAMID_LEVEL_COUNT_, and -+// NVPRO_PYRAMID_LEVEL_COUNT_ can be at most 6. -+// -+// TODO: Test if this works for subgroup size != 32. -+ -+layout(local_size_x = 256) in; -+ -+// Cache for the tile generated for level NVPRO_PYRAMID_INPUT_LEVEL_ + N -+// used only when NVPRO_PYRAMID_LEVEL_COUNT_ >= 4. -+// N is 3 if said level count is 4 or 5, 4 if level count is 6. -+// If level count is 5 or higher, the layout of the tile is: -+// +---+---++---+---+ -+// | 0 | 1 || 4 | 5 | -+// +---+---++---+---+ -+// | 2 | 3 || 6 | 7 | -+// +===+===++===+===+ -+// | 8 | 9 ||12 |13 | -+// +---+---++---+---+ -+// |10 |11 ||14 |15 | -+// +---+---++---+---+ -+// -+// If level count is 4, there are up to 4 consecutive tiles of layout: -+// +---+---+ -+// | 0 | 1 | -+// +---+---+ -+// | 2 | 3 | -+// +---+---+ -+// -+// These diagrams are referenced later. -+shared NVPRO_PYRAMID_SHARED_TYPE sharedTile_[16]; -+ -+ -+// Handle the tile at the given input level and offset (position -+// of upper-left corner), and write out the resulting minified tiles -+// in the next 1 to 4 mip levels (depending on levelCount_). -+// Tiles are squares with edge length 1 << levelCount_ -+// -+// Only 1 to 3 levels are supported if !sharedMemoryWrite_; -+// Only 3 to 4 levels are supported otherwise (micro-optimization). -+// -+// Must be executed by N consecutive threads with same inputs, with -+// the lowest thread number being a multiple of N; N given by -+// -+// levelCount_ 1 2 3 4 -+// N 1 4 16 16 [NOT 64] -+// -+// If sharedMemoryWrite_ == true, then the 1x1 sample generated for -+// the final output level is written to sharedTile_[sharedMemoryIdx_]. -+void handleTile_(ivec2 srcTileOffset_, int inputLevel_, uint levelCount_, -+ bool sharedMemoryWrite_, uint sharedMemoryIdx_) -+{ -+ // Discussion for levelCount_ == 3 -+ // -+ // Break the input tile into 16 2x2 sub-tiles. Each thread in the team -+ // generates 1 sample (from 4 inputs), writes them to inputLevel_ + 1. -+ // +---+---++---+---+ -+ // | 0 | 1 || 4 | 5 | -+ // +---+---++---+---+ -+ // | 2 | 3 || 6 | 7 | -+ // +===+===++===+===+ -+ // | 8 | 9 ||12 |13 | -+ // +---+---++---+---+ -+ // |10 |11 ||14 |15 | -+ // +---+---++---+---+ -+ // -+ // For levelCount_ < 3, just cut out the parts of the 8x8 tile -+ // that are not applicable. -+ // -+ // For levelCount_ == 4, the input 16x16 tile is broken into 4x4 -+ // sub-tiles in the same pattern. Each thread generates the -+ // corresponding 2x2 sub-tile of the inputLevel_ + 1 8x8 tile. -+ // The problem then reduces to the levelCount_ == 3 case. -+ NVPRO_PYRAMID_TYPE sample00_, sample01_, sample10_, sample11_, out_; -+ int dstLevel_ = inputLevel_ + 1; -+ ivec2 dstSubTile_; -+ -+ // Calculate the index of this thread within the team. -+ uint teamMask_ = levelCount_ >= 3 ? 15 : levelCount_ == 2 ? 3 : 0; -+ uint idxInTeam_ = gl_LocalInvocationIndex & teamMask_; -+ -+ // NOTE the extra sharedMemoryWrite_ requirement!!! -+ if (sharedMemoryWrite_ && levelCount_ == 4) -+ { -+ // The location of the sub-tile assigned to this thread in level inputLevel_ -+ uint xOffset_ = (idxInTeam_ & 1) << 2 | (idxInTeam_ & 4) << 1; -+ uint yOffset_ = (idxInTeam_ & 2) << 1 | (idxInTeam_ & 8); -+ ivec2 srcSubTile_ = srcTileOffset_ + ivec2(xOffset_, yOffset_); -+ dstSubTile_ = srcSubTile_ >> 1; -+ -+ // Thread calculates upper-left sample of 2x2 output sub-tile. -+ ivec2 srcCoord_, dstCoord_; -+ srcCoord_ = srcSubTile_; -+ dstCoord_ = dstSubTile_; -+ NVPRO_PYRAMID_LOAD_REDUCE4(srcCoord_, inputLevel_, sample00_); -+ NVPRO_PYRAMID_STORE(dstCoord_, dstLevel_, sample00_); -+ -+ // Thread calculates lower-left sample of 2x2 output sub-tile. -+ srcCoord_ = srcSubTile_ + ivec2(0, 2); -+ dstCoord_ = dstSubTile_ + ivec2(0, 1); -+ NVPRO_PYRAMID_LOAD_REDUCE4(srcCoord_, inputLevel_, sample01_); -+ NVPRO_PYRAMID_STORE(dstCoord_, dstLevel_, sample01_); -+ -+ // Thread calculates upper-right sample of 2x2 output sub-tile. -+ srcCoord_ = srcSubTile_ + ivec2(2, 0); -+ dstCoord_ = dstSubTile_ + ivec2(1, 0); -+ NVPRO_PYRAMID_LOAD_REDUCE4(srcCoord_, inputLevel_, sample10_); -+ NVPRO_PYRAMID_STORE(dstCoord_, dstLevel_, sample10_); -+ -+ // Thread calculates lower-right sample of 2x2 output sub-tile. -+ srcCoord_ = srcSubTile_ + ivec2(2, 2); -+ dstCoord_ = dstSubTile_ + ivec2(1, 1); -+ NVPRO_PYRAMID_LOAD_REDUCE4(srcCoord_, inputLevel_, sample11_); -+ NVPRO_PYRAMID_STORE(dstCoord_, dstLevel_, sample11_); -+ -+ // Now the full assigned 2x2 subtile has been filled, move on to -+ // the 1x1 sample of the next level assigned to this thread. -+ dstLevel_++; -+ dstSubTile_ >>= 1; -+ NVPRO_PYRAMID_REDUCE4(sample00_, sample01_, sample10_, sample11_, out_); -+ NVPRO_PYRAMID_STORE(dstSubTile_, dstLevel_, out_); -+ } -+ else // levelCount_ != 4 -+ { -+ // The location of the sub-tile assigned to this thread in the -+ // level after level inputLevel_. -+ uint xOffset_ = (idxInTeam_ & 1) << 1 | (idxInTeam_ & 4); -+ uint yOffset_ = (idxInTeam_ & 2) | (idxInTeam_ & 8) >> 1; -+ ivec2 srcSubTile_ = srcTileOffset_ + ivec2(xOffset_, yOffset_); -+ dstSubTile_ = srcSubTile_ >> 1; -+ -+ // Thread calculates the sample in that sub-tile. -+ NVPRO_PYRAMID_LOAD_REDUCE4(srcSubTile_, inputLevel_, out_); -+ NVPRO_PYRAMID_STORE(dstSubTile_, dstLevel_, out_); -+ } -+ -+ if (!sharedMemoryWrite_ && levelCount_ == 1) return; -+ -+ // The whole team computes the 2x2 tile in the next level; only 1 -+ // out of every 4 threads does this. Use shuffle to get the needed -+ // data from the other three threads. -+ dstLevel_++; -+ dstSubTile_ >>= 1; -+ sample00_ = out_; -+ sample01_ = NVPRO_PYRAMID_SHUFFLE_XOR(out_, 1); -+ sample10_ = NVPRO_PYRAMID_SHUFFLE_XOR(out_, 2); -+ sample11_ = NVPRO_PYRAMID_SHUFFLE_XOR(out_, 3); -+ -+ if (0 == (gl_SubgroupInvocationID & 3)) -+ { -+ NVPRO_PYRAMID_REDUCE4(sample00_, sample01_, sample10_, sample11_, out_); -+ NVPRO_PYRAMID_STORE(dstSubTile_, dstLevel_, out_); -+ } -+ -+ if (!sharedMemoryWrite_ && levelCount_ == 2) return; -+ -+ // Compute 1x1 "tile" in the last level handled by this function; -+ // only 1 thread per 16 does this. Shuffle again. -+ // This is also the thread that does the optional shared memory write. -+ dstLevel_++; -+ dstSubTile_ >>= 1; -+ sample00_ = out_; -+ sample01_ = NVPRO_PYRAMID_SHUFFLE_XOR(out_, 4); -+ sample10_ = NVPRO_PYRAMID_SHUFFLE_XOR(out_, 8); -+ sample11_ = NVPRO_PYRAMID_SHUFFLE_XOR(out_, 12); -+ -+ if (0 == (gl_SubgroupInvocationID & 15)) -+ { -+ NVPRO_PYRAMID_REDUCE4(sample00_, sample01_, sample10_, sample11_, out_); -+ NVPRO_PYRAMID_STORE(dstSubTile_, dstLevel_, out_); -+ if (sharedMemoryWrite_) -+ { -+ NVPRO_PYRAMID_SHARED_STORE(sharedTile_[sharedMemoryIdx_], out_); -+ } -+ } -+} -+ -+ -+void nvproPyramidMain() -+{ -+ // Cut the input mip level into square tiles of edge length -+ // 2 to the power of NVPRO_PYRAMID_LEVEL_COUNT_. -+ int levelCount_ = NVPRO_PYRAMID_LEVEL_COUNT_; -+ int inputLevel_ = NVPRO_PYRAMID_INPUT_LEVEL_; -+ ivec2 srcImageSize_ = NVPRO_PYRAMID_LEVEL_SIZE(inputLevel_); -+ uint horizontalTiles_ = uint(srcImageSize_.x) >> levelCount_; -+ uint verticalTiles_ = uint(srcImageSize_.y) >> levelCount_; -+ -+ // Calculate the team size from the level count. Each thread -+ // handles 4 inupt samples, except when levelCount_ == 6, then each -+ // handles 16 samples. -+ uint teamSizeLog2_ = min(8u, levelCount_ * 2u - 2u); -+ -+ // Assign tiles to each team. -+ uint tileIndex_ = gl_GlobalInvocationID.x >> teamSizeLog2_; -+ uint horizontalIndex_ = tileIndex_ % horizontalTiles_; -+ uint verticalIndex_ = tileIndex_ / horizontalTiles_; -+ ivec2 tileOffset_ = ivec2(horizontalIndex_, verticalIndex_) << levelCount_; -+ -+ if (levelCount_ <= 3) -+ { -+ if (verticalIndex_ < verticalTiles_) -+ { -+ // Reminder to self: can't handle 4 level case when -+ // sharedMemoryWrite_ is false. -+ handleTile_(tileOffset_, inputLevel_, levelCount_, false, 0); -+ } -+ return; -+ } -+ -+ // For 4 or more levels, team size is too big for shuffle communication. -+ // Need to split the tile into sub-tiles and teams into 16 thread sub-teams. -+ // Each sub-team writes one sample to shared memory. -+ // Refer to sharedTile_ diagram for details. -+ if (verticalIndex_ < verticalTiles_) -+ { -+ // Number of levels to fill for now. -+ int subLevelCount_ = levelCount_ == 6 ? 4 : 3; -+ -+ // Calculate the index of the sub-team within the team. -+ int subTeamMask_ = levelCount_ == 4 ? 3 : 15; -+ int subTeamIdx_ = int(gl_GlobalInvocationID.x >> 4) & subTeamMask_; -+ -+ // Location of sub-tile; they are 8x8 or 16x16 depending on subLevelCount_ -+ ivec2 subTeamOffset_; -+ subTeamOffset_.x = (subTeamIdx_ & 1) << 3 | (subTeamIdx_ & 4) << 2; -+ subTeamOffset_.y = (subTeamIdx_ & 2) << 2 | (subTeamIdx_ & 8) << 1; -+ if (subLevelCount_ == 4) -+ { -+ subTeamOffset_ <<= 1; -+ } -+ -+ // Index in shared memory that this sub-team will write to. -+ uint sharedMemoryIndex_ = (gl_GlobalInvocationID.x >> 4u) & 15u; -+ -+ // Handle the sub-tile and write the last level 1x1 sample to shared memory. -+ handleTile_(tileOffset_ + subTeamOffset_, inputLevel_, subLevelCount_, -+ true, sharedMemoryIndex_); -+ -+ // Problem reduces to handling 1 or 2 remaining levels. -+ inputLevel_ += subLevelCount_; -+ levelCount_ -= subLevelCount_; -+ } -+ -+ // Wait for shared memory to fill. -+ barrier(); -+ -+ // Handle the remaining 1 or 2 levels. -+ // Only 4 threads have to do this per workgroup (NOT per team) -+ if (gl_LocalInvocationIndex < 4) -+ { -+ NVPRO_PYRAMID_TYPE in00_, in01_, in10_, in11_, out_; -+ if (levelCount_ == 1) -+ { -+ // Handle up to 4 2x2 tiles in shared memory, 1 tile per thread. -+ // Tile location calculated for the output level (final level). -+ tileIndex_ = gl_WorkGroupID.x * 4 + gl_LocalInvocationIndex; -+ horizontalIndex_ = tileIndex_ % horizontalTiles_; -+ verticalIndex_ = tileIndex_ / horizontalTiles_; -+ tileOffset_ = ivec2(horizontalIndex_, verticalIndex_); -+ uint smemOffset_ = gl_LocalInvocationIndex * 4u; -+ -+ if (verticalIndex_ < verticalTiles_) -+ { -+ NVPRO_PYRAMID_SHARED_LOAD(sharedTile_[smemOffset_ + 0u], in00_); -+ NVPRO_PYRAMID_SHARED_LOAD(sharedTile_[smemOffset_ + 1u], in10_); -+ NVPRO_PYRAMID_SHARED_LOAD(sharedTile_[smemOffset_ + 2u], in01_); -+ NVPRO_PYRAMID_SHARED_LOAD(sharedTile_[smemOffset_ + 3u], in11_); -+ NVPRO_PYRAMID_REDUCE4(in00_, in01_, in10_, in11_, out_); -+ NVPRO_PYRAMID_STORE(tileOffset_, (inputLevel_ + 1), out_); -+ } -+ } -+ else // levelCount_ == 2 -+ { -+ // Handle the 4x4 tile in shared memory, 1 2x2 sub-tile per -+ // thread. Tile location calculated for the final output level -+ // (here we first calculate an intermediate level). -+ tileIndex_ = gl_WorkGroupID.x; -+ horizontalIndex_ = tileIndex_ % horizontalTiles_; -+ verticalIndex_ = tileIndex_ / horizontalTiles_; -+ tileOffset_ = ivec2(horizontalIndex_, verticalIndex_); -+ uint smemOffset_ = gl_LocalInvocationIndex * 4u; -+ -+ if (verticalIndex_ < verticalTiles_) -+ { -+ // Handle first level after inputLevel_ -+ NVPRO_PYRAMID_SHARED_LOAD(sharedTile_[smemOffset_ + 0u], in00_); -+ NVPRO_PYRAMID_SHARED_LOAD(sharedTile_[smemOffset_ + 1u], in10_); -+ NVPRO_PYRAMID_SHARED_LOAD(sharedTile_[smemOffset_ + 2u], in01_); -+ NVPRO_PYRAMID_SHARED_LOAD(sharedTile_[smemOffset_ + 3u], in11_); -+ NVPRO_PYRAMID_REDUCE4(in00_, in01_, in10_, in11_, out_); -+ ivec2 threadOffset_ = ivec2(gl_LocalInvocationIndex & 1, -+ (gl_LocalInvocationIndex & 2) >> 1); -+ NVPRO_PYRAMID_STORE((tileOffset_ * 2 + threadOffset_), -+ (inputLevel_ + 1), out_); -+ // Shuffle 4 samples and produce sole last level sample. -+ in00_ = out_; -+ in10_ = NVPRO_PYRAMID_SHUFFLE_XOR(out_, 1); -+ in01_ = NVPRO_PYRAMID_SHUFFLE_XOR(out_, 2); -+ in11_ = NVPRO_PYRAMID_SHUFFLE_XOR(out_, 3); -+ -+ if (gl_LocalInvocationIndex == 0u) -+ { -+ NVPRO_PYRAMID_REDUCE4(in00_, in01_, in10_, in11_, out_); -+ NVPRO_PYRAMID_STORE(tileOffset_, (inputLevel_ + 2), out_); -+ } -+ } -+ } -+ } -+} -+ -+#endif /* !NVPRO_USE_FAST_PIPELINE_ALTERNATIVE_ */ -+ -+#else /* non-fast path */ -+ -+// Code for testing alternative designs during development, can ignore. -+#if defined(NVPRO_USE_GENERAL_PIPELINE_ALTERNATIVE_) && NVPRO_USE_GENERAL_PIPELINE_ALTERNATIVE_ != 0 -+#include "general_pipeline_alternative.glsl" -+#else -+ -+// General-case shader for generating 1 or 2 levels of the mip pyramid. -+// When generating 1 level, each workgroup handles up to 128 samples of the -+// output mip level. When generating 2 levels, each workgroup handles -+// a 8x8 tile of the last (2nd) output mip level, generating up to -+// 17x17 samples of the intermediate (1st) output mip level along the way. -+// -+// Dispatch with y, z = 1 -+layout(local_size_x = 4 * 32) in; -+ -+// When generating 2 levels, the results of generating the intermediate -+// level (first level generated) are cached here; this is the input tile -+// needed to generate the 8x8 tile of the second level generated. -+shared NVPRO_PYRAMID_SHARED_TYPE sharedLevel_[17][17]; // [y][x] -+ -+ivec2 kernelSizeFromInputSize_(ivec2 inputSize_) -+{ -+ return ivec2(inputSize_.x == 1 ? 1 : (2 | (inputSize_.x & 1)), -+ inputSize_.y == 1 ? 1 : (2 | (inputSize_.y & 1))); -+} -+ -+NVPRO_PYRAMID_TYPE -+loadSample_(ivec2 srcCoord_, int srcLevel_, bool loadFromShared_); -+ -+// Handle loading and reducing a rectangle of size kernelSize_ -+// with the given upper-left coordinate srcCoord_. Samples read from -+// mip level srcLevel_ if !loadFromShared_, sharedLevel_ otherwise. -+// -+// kernelSize_ must range from 1x1 to 3x3. -+// -+// Once computed, the sample is written to the given coordinate of the -+// specified destination mip level, and returned. The destination -+// image size is needed to compute the kernel weights. -+NVPRO_PYRAMID_TYPE reduceStoreSample_(ivec2 srcCoord_, int srcLevel_, -+ bool loadFromShared_, -+ ivec2 kernelSize_, -+ ivec2 dstImageSize_, -+ ivec2 dstCoord_, int dstLevel_) -+{ -+ bool lfs_ = loadFromShared_; -+ float n_ = dstImageSize_.y; -+ float rcp_ = 1.0f / (2 * n_ + 1); -+ float w0_ = rcp_ * (n_ - dstCoord_.y); -+ float w1_ = rcp_ * n_; -+ float w2_ = 1.0f - w0_ - w1_; -+ -+ NVPRO_PYRAMID_TYPE v0_, v1_, v2_, h0_, h1_, h2_, out_; -+ -+ // Reduce vertically up to 3 times (depending on kernel horizontal size) -+ switch (kernelSize_.x) -+ { -+ case 3: -+ switch (kernelSize_.y) -+ { -+ case 3: v2_ = loadSample_(srcCoord_ + ivec2(2, 2), srcLevel_, lfs_); -+ case 2: v1_ = loadSample_(srcCoord_ + ivec2(2, 1), srcLevel_, lfs_); -+ case 1: v0_ = loadSample_(srcCoord_ + ivec2(2, 0), srcLevel_, lfs_); -+ } -+ switch (kernelSize_.y) -+ { -+ case 3: NVPRO_PYRAMID_REDUCE(w0_, v0_, w1_, v1_, w2_, v2_, h2_); break; -+ case 2: NVPRO_PYRAMID_REDUCE2(v0_, v1_, h2_); break; -+ case 1: h2_ = v0_; break; -+ } -+ // fallthru -+ case 2: -+ switch (kernelSize_.y) -+ { -+ case 3: v2_ = loadSample_(srcCoord_ + ivec2(1, 2), srcLevel_, lfs_); -+ case 2: v1_ = loadSample_(srcCoord_ + ivec2(1, 1), srcLevel_, lfs_); -+ case 1: v0_ = loadSample_(srcCoord_ + ivec2(1, 0), srcLevel_, lfs_); -+ } -+ switch (kernelSize_.y) -+ { -+ case 3: NVPRO_PYRAMID_REDUCE(w0_, v0_, w1_, v1_, w2_, v2_, h1_); break; -+ case 2: NVPRO_PYRAMID_REDUCE2(v0_, v1_, h1_); break; -+ case 1: h1_ = v0_; break; -+ } -+ case 1: -+ switch (kernelSize_.y) -+ { -+ case 3: v2_ = loadSample_(srcCoord_ + ivec2(0, 2), srcLevel_, lfs_); -+ case 2: v1_ = loadSample_(srcCoord_ + ivec2(0, 1), srcLevel_, lfs_); -+ case 1: v0_ = loadSample_(srcCoord_ + ivec2(0, 0), srcLevel_, lfs_); -+ } -+ switch (kernelSize_.y) -+ { -+ case 3: NVPRO_PYRAMID_REDUCE(w0_, v0_, w1_, v1_, w2_, v2_, h0_); break; -+ case 2: NVPRO_PYRAMID_REDUCE2(v0_, v1_, h0_); break; -+ case 1: h0_ = v0_; break; -+ } -+ } -+ -+ // Reduce up to 3 samples horizontally. -+ switch (kernelSize_.x) -+ { -+ case 3: -+ n_ = dstImageSize_.x; -+ rcp_ = 1.0f / (2 * n_ + 1); -+ w0_ = rcp_ * (n_ - dstCoord_.x); -+ w1_ = rcp_ * n_; -+ w2_ = 1.0f - w0_ - w1_; -+ NVPRO_PYRAMID_REDUCE(w0_, h0_, w1_, h1_, w2_, h2_, out_); -+ break; -+ case 2: -+ NVPRO_PYRAMID_REDUCE2(h0_, h1_, out_); -+ break; -+ case 1: -+ out_ = h0_; -+ } -+ -+ // Write out sample. -+ NVPRO_PYRAMID_STORE(dstCoord_, dstLevel_, out_); -+ return out_; -+} -+ -+NVPRO_PYRAMID_TYPE -+loadSample_(ivec2 srcCoord_, int srcLevel_, bool loadFromShared_) -+{ -+ NVPRO_PYRAMID_TYPE loaded_; -+ if (loadFromShared_) -+ { -+ NVPRO_PYRAMID_SHARED_LOAD((sharedLevel_[srcCoord_.y][srcCoord_.x]), loaded_); -+ } -+ else -+ { -+ NVPRO_PYRAMID_LOAD(srcCoord_, srcLevel_, loaded_); -+ } -+ return loaded_; -+} -+ -+ -+ -+// Compute and write out (to the 1st mip level generated) the samples -+// at coordinates -+// initDstCoord_, -+// initDstCoord_ + step_, ... -+// initDstCoord_ + (iterations_-1) * step_ -+// and cache them at in the sharedLevel_ tile at coordinates -+// initSharedCoord_, -+// initSharedCoord_ + step_, ... -+// initSharedCoord_ + (iterations_-1) * step_ -+// If boundsCheck_ is true, skip coordinates that are out of bounds. -+void intermediateLevelLoop_(ivec2 initDstCoord_, -+ ivec2 initSharedCoord_, -+ ivec2 step_, -+ int iterations_, -+ bool boundsCheck_) -+{ -+ ivec2 dstCoord_ = initDstCoord_; -+ ivec2 sharedCoord_ = initSharedCoord_; -+ int srcLevel_ = int(NVPRO_PYRAMID_INPUT_LEVEL_); -+ int dstLevel_ = srcLevel_ + 1; -+ ivec2 srcImageSize_ = NVPRO_PYRAMID_LEVEL_SIZE(srcLevel_); -+ ivec2 dstImageSize_ = NVPRO_PYRAMID_LEVEL_SIZE(dstLevel_); -+ ivec2 kernelSize_ = kernelSizeFromInputSize_(srcImageSize_); -+ -+ for (int i_ = 0; i_ < iterations_; ++i_) -+ { -+ ivec2 srcCoord_ = dstCoord_ * 2; -+ -+ // Optional bounds check. -+ if (boundsCheck_) -+ { -+ if (uint(dstCoord_.x) >= uint(dstImageSize_.x)) continue; -+ if (uint(dstCoord_.y) >= uint(dstImageSize_.y)) continue; -+ } -+ -+ bool loadFromShared_ = false; -+ NVPRO_PYRAMID_TYPE sample_ = -+ reduceStoreSample_(srcCoord_, srcLevel_, loadFromShared_, kernelSize_, -+ dstImageSize_, dstCoord_, dstLevel_); -+ -+ // Above function handles writing to the actual output; manually -+ // cache into shared memory here. -+ NVPRO_PYRAMID_SHARED_STORE((sharedLevel_[sharedCoord_.y][sharedCoord_.x]), -+ sample_); -+ dstCoord_ += step_; -+ sharedCoord_ += step_; -+ } -+} -+ -+// Function for the workgroup that handles filling the intermediate level -+// (caching it in shared memory as well). -+// -+// We need somewhere from 16x16 to 17x17 samples, depending -+// on what the kernel size for the 2nd mip level generation will be. -+// -+// dstTileCoord_ : upper left coordinate of the tile to generate. -+// boundsCheck_ : whether to skip samples that are out-of-bounds. -+void fillIntermediateTile_(ivec2 dstTileCoord_, bool boundsCheck_) -+{ -+ uint localIdx_ = int(gl_LocalInvocationIndex); -+ -+ ivec2 initThreadOffset_; -+ ivec2 step_; -+ int iterations_; -+ -+ ivec2 dstImageSize_ = -+ NVPRO_PYRAMID_LEVEL_SIZE((int(NVPRO_PYRAMID_INPUT_LEVEL_) + 1)); -+ ivec2 futureKernelSize_ = kernelSizeFromInputSize_(dstImageSize_); -+ -+ if (futureKernelSize_.x == 3) -+ { -+ if (futureKernelSize_.y == 3) -+ { -+ // Fill in 2 17x7 steps and 1 17x3 step (9 idle threads) -+ initThreadOffset_ = ivec2(localIdx_ % 17u, localIdx_ / 17u); -+ step_ = ivec2(0, 7); -+ iterations_ = localIdx_ >= 7 * 17 ? 0 : localIdx_ < 3 * 17 ? 3 : 2; -+ } -+ else // Future 3x[2,1] kernel -+ { -+ // Fill in 2 8x16 steps and 1 1x16 step -+ initThreadOffset_ = ivec2(localIdx_ / 16u, localIdx_ % 16u); -+ step_ = ivec2(8, 0); -+ iterations_ = localIdx_ < 1 * 16 ? 3 : 2; -+ } -+ } -+ else -+ { -+ if (futureKernelSize_.y == 3) -+ { -+ // Fill in 2 16x8 steps and 1 16x1 step -+ initThreadOffset_ = ivec2(localIdx_ % 16u, localIdx_ / 16u); -+ step_ = ivec2(0, 8); -+ iterations_ = localIdx_ < 1 * 16 ? 3 : 2; -+ } -+ else -+ { -+ // Fill in 2 16x8 steps -+ initThreadOffset_ = ivec2(localIdx_ % 16u, localIdx_ / 16u); -+ step_ = ivec2(0, 8); -+ iterations_ = 2; -+ } -+ } -+ -+ intermediateLevelLoop_(dstTileCoord_ + initThreadOffset_, initThreadOffset_, -+ step_, iterations_, boundsCheck_); -+} -+ -+ -+ -+// Function for the workgroup that handles filling the last level tile -+// (2nd level after the original input level), using as input the -+// tile in shared memory. -+// -+// dstTileCoord_ : upper left coordinate of the tile to generate. -+// boundsCheck_ : whether to skip samples that are out-of-bounds. -+void fillLastTile_(ivec2 dstTileCoord_, bool boundsCheck_) -+{ -+ uint localIdx_ = gl_LocalInvocationIndex; -+ -+ if (localIdx_ < 8 * 8) -+ { -+ ivec2 threadOffset_ = ivec2(localIdx_ % 8u, localIdx_ / 8u); -+ int srcLevel_ = int(NVPRO_PYRAMID_INPUT_LEVEL_) + 1; -+ int dstLevel_ = int(NVPRO_PYRAMID_INPUT_LEVEL_) + 2; -+ ivec2 srcImageSize_ = NVPRO_PYRAMID_LEVEL_SIZE(srcLevel_); -+ ivec2 dstImageSize_ = NVPRO_PYRAMID_LEVEL_SIZE(dstLevel_); -+ -+ ivec2 srcSharedCoord_ = threadOffset_ * 2; -+ bool loadFromShared_ = true; -+ ivec2 kernelSize_ = kernelSizeFromInputSize_(srcImageSize_); -+ ivec2 dstCoord_ = threadOffset_ + dstTileCoord_; -+ -+ bool inBounds_ = true; -+ if (boundsCheck_) -+ { -+ inBounds_ = (uint(dstCoord_.x) < uint(dstImageSize_.x)) -+ && (uint(dstCoord_.y) < uint(dstImageSize_.y)); -+ } -+ if (inBounds_) -+ { -+ reduceStoreSample_(srcSharedCoord_, 0, loadFromShared_, kernelSize_, -+ dstImageSize_, dstCoord_, dstLevel_); -+ } -+ } -+} -+ -+ -+ -+void nvproPyramidMain() -+{ -+ int inputLevel_ = int(NVPRO_PYRAMID_INPUT_LEVEL_); -+ -+ if (NVPRO_PYRAMID_LEVEL_COUNT_ == 1u) -+ { -+ ivec2 kernelSize_ = -+ kernelSizeFromInputSize_(NVPRO_PYRAMID_LEVEL_SIZE(inputLevel_)); -+ ivec2 dstImageSize_ = NVPRO_PYRAMID_LEVEL_SIZE((inputLevel_ + 1)); -+ ivec2 dstCoord_ = ivec2(int(gl_GlobalInvocationID.x) % dstImageSize_.x, -+ int(gl_GlobalInvocationID.x) / dstImageSize_.x); -+ ivec2 srcCoord_ = dstCoord_ * 2; -+ -+ if (dstCoord_.y < dstImageSize_.y) -+ { -+ reduceStoreSample_(srcCoord_, inputLevel_, false, kernelSize_, -+ dstImageSize_, dstCoord_, inputLevel_ + 1); -+ } -+ } -+ else // Handling two levels. -+ { -+ // Assign a 8x8 tile of mip level inputLevel_ + 2 to this workgroup. -+ int level2_ = inputLevel_ + 2; -+ ivec2 level2Size_ = NVPRO_PYRAMID_LEVEL_SIZE(level2_); -+ ivec2 tileCount_; -+ tileCount_.x = int(uint(level2Size_.x + 7) / 8u); -+ tileCount_.y = int(uint(level2Size_.y + 7) / 8u); -+ ivec2 tileIdx_ = ivec2(gl_WorkGroupID.x % uint(tileCount_.x), -+ gl_WorkGroupID.x / uint(tileCount_.x)); -+ uint localIdx_ = gl_LocalInvocationIndex; -+ -+ // Determine if bounds checking is needed; this is only the case -+ // for tiles at the right or bottom fringe that might be cut off -+ // by the image border. Note that later, I use if statements rather -+ // than passing boundsCheck_ directly to convince the compiler -+ // to inline everything. -+ bool boundsCheck_ = tileIdx_.x >= tileCount_.x - 1 || -+ tileIdx_.y >= tileCount_.y - 1; -+ -+ if (boundsCheck_) -+ { -+ // Compute the tile in level inputLevel_ + 1 that's needed to -+ // compute the above 8x8 tile. -+ fillIntermediateTile_(tileIdx_ * 2 * ivec2(8, 8), true); -+ barrier(); -+ -+ // Compute the inputLevel_ + 2 tile of size 8x8, loading -+ // inupts from shared memory. -+ fillLastTile_(tileIdx_ * ivec2(8, 8), true); -+ } -+ else -+ { -+ // Same with no bounds checking. -+ fillIntermediateTile_(tileIdx_ * 2 * ivec2(8, 8), false); -+ barrier(); -+ fillLastTile_(tileIdx_ * ivec2(8, 8), false); -+ } -+ } -+} -+ -+#endif /* !NVPRO_USE_GENERAL_PIPELINE_ALTERNATIVE_ */ -+#endif /* !NVPRO_PYRAMID_IS_FAST_PIPELINE */ -+ -+#undef NVPRO_PYRAMID_2D_REDUCE_ -+#undef NVPRO_PYRAMID_LEVEL_COUNT_ -+#undef NVPRO_PYRAMID_INPUT_LEVEL_ -diff --git a/src/external/nvpro_pyramid/nvpro_pyramid_dispatch.hpp b/src/external/nvpro_pyramid/nvpro_pyramid_dispatch.hpp -new file mode 100644 -index 000000000..a84b23843 ---- /dev/null -+++ b/src/external/nvpro_pyramid/nvpro_pyramid_dispatch.hpp -@@ -0,0 +1,292 @@ -+// Copyright 2021 NVIDIA CORPORATION -+// SPDX-License-Identifier: Apache-2.0 -+#ifndef NVPRO_SAMPLES_COMPUTE_MIPMAPS_NVPRO_PYRAMID_DISPATCH_HPP_ -+#define NVPRO_SAMPLES_COMPUTE_MIPMAPS_NVPRO_PYRAMID_DISPATCH_HPP_ -+ -+#include -+#include -+ -+// Struct for passing the pipelines and associated data for the mipmap -+// dispatch function. -+// -+// generalPipeline: compute pipeline, created as described in -+// nvpro_pyramid.glsl with NVPRO_PYRAMID_IS_FAST_PIPELINE defined as 0. -+// Mandatory; see nvproPyramidDefaultFastDispatcher if you want to use -+// fastPipeline alone and handle the non-fast case yourself in an alternate way. -+// -+// fastPipeline: optional (may be VK_NULL_HANDLE). -+// Compute pipeline, created as described in nvpro_pyramid.glsl -+// with NVPRO_PYRAMID_IS_FAST_PIPELINE defined as nonzero. -+// Must be VK_NULL_HANDLE if the executing device lacks the needed -+// features (see nvpro_pyramid.glsl). -+// -+// layout: shared pipeline layout for both pipelines. -+// -+// pushConstantOffset: offset of the 32-bit push constant needed by -+// nvpro_pyramid.glsl; this must be 0 if the user did not manually -+// override the default push constant by defining NVPRO_PYRAMID_PUSH_CONSTANT. -+struct NvproPyramidPipelines -+{ -+ VkPipeline generalPipeline; -+ VkPipeline fastPipeline; -+ VkPipelineLayout layout; -+ uint32_t pushConstantOffset; -+}; -+ -+// Record commands for dispatching the compute shaders in NvproPyramidPipelines -+// that are appropriate for an image with the given base mip width, -+// height, and mip levels (defaults to the maximum number of mip -+// levels theoretically allowed for the given image size). -+// -+// This handles: -+// -+// * Recording dispatch commands -+// * Binding compute pipelines -+// * Inserting appropriate barriers strictly between dispatches -+// -+// The caller is responsible for: -+// -+// * Performing any needed synchronization before and after -+// * Binding any needed descriptor sets -+// * Setting any needed push constants, except the push constant declared -+// by NVPRO_PYRAMID_PUSH_CONSTANT (if any) -+inline void nvproCmdPyramidDispatch(VkCommandBuffer cmdBuf, -+ NvproPyramidPipelines pipelines, -+ uint32_t baseWidth, -+ uint32_t baseHeight, -+ uint32_t mipLevels = 0u); -+ -+// Struct used for tracking the progress of scheduling mipmap -+// generation commands. -+struct NvproPyramidState -+{ -+ // Input level for the next dispatch. -+ uint32_t currentLevel; -+ -+ // Levels that remain to be filled, i.e. -+ // nvproCmdPyramidDispatch::mipLevels - currentLevel - 1. -+ // Will never be 0 when passed to an nvpro_pyramid_dispatcher_t instance. -+ uint32_t remainingLevels; -+ -+ // Width and height of mip level currentLevel. -+ uint32_t currentX, currentY; -+}; -+ -+constexpr uint32_t nvproPyramidInputLevelShift = 5u; // TODO Use consistently -+ -+ -+// Callback host function for a pipeline. Attempt to record commands -+// for one bind and dispatch of the given pipeline, which may be -+// VK_NULL_HANDLE (to indicate that the pipeline is already bound and -+// need not be bound again). This function should not record any barriers. -+// -+// The return value is the number of mip levels filled by the dispatch. -+// -+// If this is a callback for a fast pipeline, this may fail (return 0) -+// if the pipeline is not suitable (e.g. the current mip level fails -+// some divisibility requirements). -+// -+// Callbacks for general pipelines must never return 0. -+// -+// This function may set the 32-bit push constant at offset -+// `pushConstantOffset` (and no other push constant). It may use this -+// push constant as it sees fit, but the NVPRO_PYRAMID_INPUT_LEVEL_ -+// and NVPRO_PYRAMID_LEVEL_COUNT_ macros assume -+// -+// { input level } << nvproPyramidInputLevelShift | { levels filled } -+typedef uint32_t (*nvpro_pyramid_dispatcher_t)(VkCommandBuffer cmdBuf, -+ VkPipelineLayout layout, -+ uint32_t pushConstantOffset, -+ VkPipeline pipelineIfNeeded, -+ const NvproPyramidState& state); -+ -+// Base implementation function for the typical user-facing -+// nvproCmdPyramidDispatch. Try to use the fastPipeline if possible, -+// then fall back to the general pipeline if not usable. -+inline void -+nvproCmdPyramidDispatch(VkCommandBuffer cmdBuf, -+ NvproPyramidPipelines pipelines, -+ uint32_t baseWidth, -+ uint32_t baseHeight, -+ uint32_t mipLevels, -+ nvpro_pyramid_dispatcher_t generalDispatcher, -+ nvpro_pyramid_dispatcher_t fastDispatcher) -+{ -+ VkMemoryBarrier barrier{ -+ VK_STRUCTURE_TYPE_MEMORY_BARRIER, 0, -+ VK_ACCESS_SHADER_WRITE_BIT, VK_ACCESS_SHADER_READ_BIT}; -+ if (mipLevels == 0) -+ { -+ uint32_t srcWidth = baseWidth, srcHeight = baseHeight; -+ while (srcWidth != 0 || srcHeight != 0) -+ { -+ srcWidth >>= 1; -+ srcHeight >>= 1; -+ ++mipLevels; -+ } -+ } -+ NvproPyramidState state; -+ state.currentLevel = 0u; -+ state.remainingLevels = mipLevels - 1u; -+ state.currentX = baseWidth; -+ state.currentY = baseHeight; -+ -+ VkPipeline fastPipelineIfNeeded = pipelines.fastPipeline; -+ VkPipeline generalPipelineIfNeeded = pipelines.generalPipeline; -+ -+ while (1) -+ { -+ uint32_t levelsDone = 0; -+ -+ // Try to use the fast pipeline if possible. -+ if (pipelines.fastPipeline) -+ { -+ levelsDone = -+ fastDispatcher(cmdBuf, pipelines.layout, pipelines.pushConstantOffset, -+ fastPipelineIfNeeded, state); -+ } -+ -+ if (levelsDone != 0) -+ { -+ fastPipelineIfNeeded = VK_NULL_HANDLE; -+ generalPipelineIfNeeded = pipelines.generalPipeline; -+ } -+ else -+ { -+ // Otherwise fall back on general pipeline. -+ levelsDone = generalDispatcher(cmdBuf, pipelines.layout, -+ pipelines.pushConstantOffset, -+ generalPipelineIfNeeded, state); -+ -+ fastPipelineIfNeeded = pipelines.fastPipeline; -+ generalPipelineIfNeeded = VK_NULL_HANDLE; -+ } -+ assert(levelsDone != 0); -+ -+ // Update the progress. -+ assert(levelsDone <= state.remainingLevels); -+ state.currentLevel += levelsDone; -+ state.remainingLevels -= levelsDone; -+ state.currentX >>= levelsDone; -+ state.currentX = state.currentX ? state.currentX : 1u; -+ state.currentY >>= levelsDone; -+ state.currentY = state.currentY ? state.currentY : 1u; -+ -+ // Put barriers only between dispatches. -+ if (state.remainingLevels == 0u) break; -+ vkCmdPipelineBarrier(cmdBuf, -+ VK_PIPELINE_STAGE_COMPUTE_SHADER_BIT, -+ VK_PIPELINE_STAGE_COMPUTE_SHADER_BIT, -+ 0, 1, &barrier, 0, 0, 0, 0); -+ } -+} -+ -+ -+// nvpro_pyramid_dispatcher_t implementation for nvpro_pyramid.glsl -+// shaders with NVPRO_PYRAMID_IS_FAST_PIPELINE != 0 -+// -+// Note: this function is referenced by name in ComputeMipmapPipeline::cmdBindGenerate -+template -+static uint32_t -+nvproPyramidDefaultFastDispatcher(VkCommandBuffer cmdBuf, -+ VkPipelineLayout layout, -+ uint32_t pushConstantOffset, -+ VkPipeline pipelineIfNeeded, -+ const NvproPyramidState& state) -+{ -+ // For maybequad pipeline. -+ static_assert(DivisibilityRequirement > 0 && DivisibilityRequirement % 2 == 0, -+ "Can only handle even sizes."); -+ bool success = state.currentX % DivisibilityRequirement == 0u -+ && state.currentY % DivisibilityRequirement == 0u; -+ if (success) -+ { -+ if (pipelineIfNeeded) -+ { -+ vkCmdBindPipeline(cmdBuf, VK_PIPELINE_BIND_POINT_COMPUTE, -+ pipelineIfNeeded); -+ } -+ -+ // Choose the number of levels to fill. -+ static_assert(MaxLevels <= 6, "Can only handle up to 6 levels"); -+ uint32_t x = state.currentX, y = state.currentY; -+ uint32_t levels = 0u; -+ while (x % 2u == 0u && y % 2u == 0u && levels < state.remainingLevels -+ && levels < MaxLevels) -+ { -+ x /= 2u; -+ y /= 2u; -+ levels++; -+ } -+ uint32_t srcLevel = state.currentLevel; -+ uint32_t pc = srcLevel << nvproPyramidInputLevelShift | levels; -+ vkCmdPushConstants(cmdBuf, layout, VK_SHADER_STAGE_COMPUTE_BIT, -+ pushConstantOffset, sizeof pc, &pc); -+ // Each workgroup handles up to 4096 input samples if levels > 5; 1024 otherwise. -+ uint32_t shift = levels > 5 ? 12u : 10u; -+ uint32_t mask = levels > 5 ? 4095u : 1023u; -+ uint32_t samples = state.currentX * state.currentY; -+ vkCmdDispatch(cmdBuf, (samples + mask) >> shift, 1u, 1u); -+ return levels; -+ } -+ return 0u; -+} -+ -+ -+// nvpro_pyramid_dispatcher_t implementation for nvpro_pyramid.glsl -+// shaders with NVPRO_PYRAMID_IS_FAST_PIPELINE == 0 -+inline uint32_t -+nvproPyramidDefaultGeneralDispatcher(VkCommandBuffer cmdBuf, -+ VkPipelineLayout layout, -+ uint32_t pushConstantOffset, -+ VkPipeline pipelineIfNeeded, -+ const NvproPyramidState& state) -+{ -+ // Use py2_4_8_8 pipeline parameters. -+ constexpr uint32_t MaxLevels = 2, Warps = 4, TileWidth = 8, TileHeight = 8; -+ -+ if (pipelineIfNeeded) -+ { -+ vkCmdBindPipeline(cmdBuf, VK_PIPELINE_BIND_POINT_COMPUTE, -+ pipelineIfNeeded); -+ } -+ static_assert(MaxLevels <= 2u && MaxLevels != 0, "can do 1 or 2 levels"); -+ uint32_t levels = -+ state.remainingLevels >= MaxLevels ? MaxLevels : state.remainingLevels; -+ uint32_t srcLevel = state.currentLevel; -+ uint32_t pc = srcLevel << nvproPyramidInputLevelShift | levels; -+ vkCmdPushConstants(cmdBuf, layout, VK_SHADER_STAGE_COMPUTE_BIT, -+ pushConstantOffset, sizeof pc, &pc); -+ uint32_t dstWidth = state.currentX >> levels; -+ dstWidth = dstWidth ? dstWidth : 1u; -+ uint32_t dstHeight = state.currentY >> levels; -+ dstHeight = dstHeight ? dstHeight : 1u; -+ -+ if (levels == 1u) -+ { -+ // Each thread writes one sample. -+ uint32_t samples = dstWidth * dstHeight; -+ uint32_t threads = Warps * 32u; -+ vkCmdDispatch(cmdBuf, (samples + (threads - 1u)) / threads, 1u, 1u); -+ } -+ else -+ { -+ // Each workgroup handles a tile. -+ uint32_t horizontalTiles = (dstWidth + (TileWidth - 1)) / TileWidth; -+ uint32_t verticalTiles = (dstHeight + (TileHeight - 1)) / TileHeight; -+ vkCmdDispatch(cmdBuf, horizontalTiles * verticalTiles, 1u, 1u); -+ } -+ return levels; -+} -+ -+inline void nvproCmdPyramidDispatch(VkCommandBuffer cmdBuf, -+ NvproPyramidPipelines pipelines, -+ uint32_t baseWidth, -+ uint32_t baseHeight, -+ uint32_t mipLevels) -+{ -+ nvproCmdPyramidDispatch(cmdBuf, pipelines, baseWidth, baseHeight, mipLevels, -+ nvproPyramidDefaultGeneralDispatcher, -+ nvproPyramidDefaultFastDispatcher); -+} -+#endif -diff --git a/src/external/nvpro_pyramid/srgba8_mipmap_fast_pipeline.comp b/src/external/nvpro_pyramid/srgba8_mipmap_fast_pipeline.comp -new file mode 100644 -index 000000000..71d09b894 ---- /dev/null -+++ b/src/external/nvpro_pyramid/srgba8_mipmap_fast_pipeline.comp -@@ -0,0 +1,14 @@ -+// Copyright 2021 NVIDIA CORPORATION -+// SPDX-License-Identifier: Apache-2.0 -+#version 460 -+#extension GL_GOOGLE_include_directive : enable -+#extension GL_KHR_shader_subgroup_shuffle : enable -+ -+#define NVPRO_PYRAMID_IS_FAST_PIPELINE 1 -+#include "srgba8_mipmap_preamble.glsl" -+#include "nvpro_pyramid.glsl" -+ -+void main() -+{ -+ nvproPyramidMain(); -+} -diff --git a/src/external/nvpro_pyramid/srgba8_mipmap_general_pipeline.comp b/src/external/nvpro_pyramid/srgba8_mipmap_general_pipeline.comp -new file mode 100644 -index 000000000..e8e7a85e7 ---- /dev/null -+++ b/src/external/nvpro_pyramid/srgba8_mipmap_general_pipeline.comp -@@ -0,0 +1,14 @@ -+// Copyright 2021 NVIDIA CORPORATION -+// SPDX-License-Identifier: Apache-2.0 -+#version 460 -+#extension GL_GOOGLE_include_directive : enable -+/* #extension GL_KHR_shader_subgroup_shuffle : enable */ -+ -+#define NVPRO_PYRAMID_IS_FAST_PIPELINE 0 -+#include "srgba8_mipmap_preamble.glsl" -+#include "nvpro_pyramid.glsl" -+ -+void main() -+{ -+ nvproPyramidMain(); -+} -diff --git a/src/external/nvpro_pyramid/srgba8_mipmap_preamble.glsl b/src/external/nvpro_pyramid/srgba8_mipmap_preamble.glsl -new file mode 100644 -index 000000000..524a74120 ---- /dev/null -+++ b/src/external/nvpro_pyramid/srgba8_mipmap_preamble.glsl -@@ -0,0 +1,128 @@ -+// Copyright 2021 NVIDIA CORPORATION -+// SPDX-License-Identifier: Apache-2.0 -+ -+// Defines the pipeline interface and macros for nvproPyramidMain for -+// the srgba8-mipmap-generation shader; EXCEPT that -+// NVPRO_PYRAMID_IS_FAST_PIPELINE is not defined. -+ -+// ************************************************************************ -+// Input: Entire sRGBA8 texture with bilinear filtering. -+layout(set=0, binding=0) uniform sampler2D srgbTex; -+// Output: Same texture, imageMipLevels[n] refers to mip level n. -+// Requires manual linear (vec4) color to sRGBA8 conversion. -+layout(set=0, binding=1, rgba8ui) uniform writeonly uimage2D imageMipLevels[16]; -+ -+// ************************************************************************ -+// Mandatory macros, except NVPRO_PYRAMID_IS_FAST_PIPELINE -+uvec4 srgbFromLinearVec(vec4 arg); -+ -+#define NVPRO_PYRAMID_TYPE vec4 -+ -+#define NVPRO_PYRAMID_LOAD(coord, level, out_) \ -+ out_ = texelFetch(srgbTex, coord, level) -+ -+#define NVPRO_PYRAMID_REDUCE(a0, v0, a1, v1, a2, v2, out_) \ -+ out_ = a0 * v0 + a1 * v1 + a2 * v2 -+ -+#define NVPRO_PYRAMID_STORE(coord, level, in_) \ -+ imageStore(imageMipLevels[level], coord, srgbFromLinearVec(in_)) -+ -+ivec2 levelSize(int level) { return imageSize(imageMipLevels[level]); } -+#define NVPRO_PYRAMID_LEVEL_SIZE levelSize -+ -+// ************************************************************************ -+// Optional macros (including recommended NVPRO_PYRAMID_LOAD_REDUCE4) -+#define NVPRO_PYRAMID_REDUCE2(v0, v1, out_) out_ = 0.5 * (v0 + v1) -+ -+#define NVPRO_PYRAMID_REDUCE4(v00, v01, v10, v11, out_) \ -+ out_ = 0.25 * ((v00 + v01) + (v10 + v11)) -+ -+#if !defined(USE_BILINEAR_SAMPLING) || USE_BILINEAR_SAMPLING -+ void loadReduce4(in ivec2 srcTexelCoord, in int srcLevel, out vec4 out_) -+ { -+ // Construct the normalized coordinate in the exact center of the 4 -+ // texels we want and use it to sample the input texture. -+ // +--+--+ -- srcTexelCoord.y / imageSize.y -+ // | | | -+ // +--X <--- normCoord -+ // | | | -+ // +--+--+ -- (srcTexelCoord.y + 2) / imageSize.y -+ // | | -+ // | (srcTexelCoord.x + 2) / imageSize.x -+ // srcTexelCoord.x / imageSize.x -+ vec2 normCoord = (vec2(srcTexelCoord) + vec2(1)) -+ / vec2(imageSize(imageMipLevels[srcLevel])); -+ out_ = textureLod(srgbTex, normCoord, srcLevel); -+ } -+ #define NVPRO_PYRAMID_LOAD_REDUCE4 loadReduce4 -+#endif -+ -+#if defined(SRGB_SHARED) && SRGB_SHARED -+ #define RED_SHIFT 0 -+ #define GREEN_SHIFT 8 -+ #define BLUE_SHIFT 16 -+ #define ALPHA_SHIFT 24 -+ #define NVPRO_PYRAMID_SHARED_TYPE uint -+ -+ // Convert float (0-1) sRGB red/green/blue component value to linear. -+ float linearFromSrgbComponent(float srgb) -+ { -+ return srgb <= 0.04045 ? srgb * (25 / 323.) -+ : pow((200 * srgb + 11) * (1/211.), 2.4); -+ } -+ -+ // Convert linear red/green/blue component to float (0-1) sRGB -+ float srgbComponentFromLinear(float linear) -+ { -+ return linear <= 0.0031308 ? (323 / 25.) * linear -+ : 1.055 * pow(linear, 1 / 2.4) - 0.055; -+ } -+ -+ void srgbUnpack(in uint srgbPacked, out vec4 linearColor) -+ { -+ vec4 srgba = unpackUnorm4x8(srgbPacked); -+ linearColor.r = linearFromSrgbComponent(srgba.r); -+ linearColor.g = linearFromSrgbComponent(srgba.g); -+ linearColor.b = linearFromSrgbComponent(srgba.b); -+ linearColor.a = srgba.a; -+ } -+ #define NVPRO_PYRAMID_SHARED_LOAD srgbUnpack -+ -+ void srgbPack(out uint srgbPacked, in vec4 linearColor) -+ { -+ vec4 srgba; -+ srgba.r = srgbComponentFromLinear(linearColor.r); -+ srgba.g = srgbComponentFromLinear(linearColor.g); -+ srgba.b = srgbComponentFromLinear(linearColor.b); -+ srgba.a = linearColor.a; -+ srgbPacked = packUnorm4x8(srgba); -+ } -+ #define NVPRO_PYRAMID_SHARED_STORE srgbPack -+#endif -+ -+#if defined(F16_SHARED) && F16_SHARED -+ // Requires GL_EXT_shader_explicit_arithmetic_types -+ #define NVPRO_PYRAMID_SHARED_TYPE f16vec4 -+ #define NVPRO_PYRAMID_SHARED_LOAD(smem_, out_) out_ = vec4(smem_) -+ #define NVPRO_PYRAMID_SHARED_STORE(smem_, in_) smem_ = f16vec4(in_) -+#endif -+ -+uint srgbFromLinearBias(float arg, float bias) -+{ -+ float srgb = arg <= 0.0031308 ? (323/25.) * arg -+ : 1.055 * pow(arg, 1/2.4) - 0.055; -+ return uint(clamp(srgb * 255. + bias, 0, 255)); -+} -+ -+// Convert float linear red/green/blue value to 8-bit sRGB component. -+uint srgbFromLinear(float arg) -+{ -+ return srgbFromLinearBias(arg, 0.5); -+} -+ -+uvec4 srgbFromLinearVec(vec4 arg) -+{ -+ uint alpha = clamp(uint(arg.a * 255.0f + 0.5f), 0u, 255u); -+ return uvec4(srgbFromLinear(arg.r), srgbFromLinear(arg.g), -+ srgbFromLinear(arg.b), alpha); -+} -diff --git a/src/xrt/auxiliary/vk/vk_helpers.c b/src/xrt/auxiliary/vk/vk_helpers.c -index 62f505391..80193f1ab 100644 ---- a/src/xrt/auxiliary/vk/vk_helpers.c -+++ b/src/xrt/auxiliary/vk/vk_helpers.c -@@ -1382,6 +1382,7 @@ vk_create_sampler(struct vk_bundle *vk, VkSamplerAddressMode clamp_mode, VkSampl - .addressModeW = clamp_mode, - .borderColor = VK_BORDER_COLOR_FLOAT_OPAQUE_BLACK, - .unnormalizedCoordinates = VK_FALSE, -+ .maxLod = VK_LOD_CLAMP_NONE, - }; - - ret = vk->vkCreateSampler(vk->device, &info, NULL, &sampler); -diff --git a/src/xrt/compositor/CMakeLists.txt b/src/xrt/compositor/CMakeLists.txt -index f1c54ed4d..3bc9302f7 100644 ---- a/src/xrt/compositor/CMakeLists.txt -+++ b/src/xrt/compositor/CMakeLists.txt -@@ -127,7 +127,7 @@ if(XRT_HAVE_VULKAN) - target_link_libraries( - comp_render - PUBLIC xrt-interfaces aux_vk -- PRIVATE aux_util aux_os -+ PRIVATE aux_util aux_os xrt-external-nvpro-pyramid - ) - # So it can be used without any other compositor libraries. - target_include_directories(comp_render PUBLIC ${CMAKE_CURRENT_SOURCE_DIR}) -@@ -146,6 +146,8 @@ if(XRT_HAVE_VULKAN) - comp_util STATIC - util/comp_base.h - util/comp_base.c -+ util/comp_mipmap.h -+ util/comp_mipmap.cpp - util/comp_layer_accum.h - util/comp_layer_accum.c - util/comp_render.h -@@ -166,7 +168,7 @@ if(XRT_HAVE_VULKAN) - target_link_libraries( - comp_util - PUBLIC xrt-interfaces -- PRIVATE aux_util aux_os aux_vk comp_render -+ PRIVATE aux_util aux_os aux_vk comp_render xrt-external-nvpro-pyramid - ) - target_include_directories(comp_util PUBLIC ${CMAKE_CURRENT_SOURCE_DIR}) - endif() -diff --git a/src/xrt/compositor/main/comp_renderer.c b/src/xrt/compositor/main/comp_renderer.c -index bd07a7744..112d14e32 100644 ---- a/src/xrt/compositor/main/comp_renderer.c -+++ b/src/xrt/compositor/main/comp_renderer.c -@@ -366,6 +366,9 @@ renderer_build_rendering_target_resources(struct comp_renderer *r, - VkImageView image_view = r->c->target->images[index].view; - VkExtent2D extent = {r->c->target->width, r->c->target->height}; - -+ if (r->settings->use_compute) -+ return; -+ - render_gfx_target_resources_init( // - rtr, // - &c->nr, // -@@ -574,12 +577,16 @@ renderer_init(struct comp_renderer *r, struct comp_compositor *c, VkExtent2D scr - VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL); // final_layout - - for (uint32_t i = 0; i < c->nr.view_count; i++) { -- bret = comp_scratch_single_images_ensure(&r->c->scratch.views[i], &r->c->base.vk, scratch_extent); -+ bret = comp_scratch_single_images_ensure(&r->c->scratch.views[i], &r->c->base.vk, scratch_extent, -+ r->settings->mip_levels); - if (!bret) { - COMP_ERROR(c, "comp_scratch_single_images_ensure: false"); - assert(false && "Whelp, can't return an error. But should never really fail."); - } - -+ if (r->settings->use_compute) -+ continue; -+ - for (uint32_t k = 0; k < COMP_SCRATCH_NUM_IMAGES; k++) { - struct render_scratch_color_image *rsci = &c->scratch.views[i].images[k]; - -@@ -919,18 +926,19 @@ dispatch_graphics(struct comp_renderer *r, - // Scratch image covers the whole image. - struct xrt_normalized_rect layer_norm_rect = {.x = 0.0f, .y = 0.0f, .w = 1.0f, .h = 1.0f}; - -- comp_render_gfx_add_view( // -- &data, // -- &world_poses[i], // -- &eye_poses[i], // -- &fovs[i], // -- rsci_rtr, // -- &layer_viewport_data, // -- &layer_norm_rect, // -- rsci->image, // -- rsci->srgb_view, // -- &vertex_rots[i], // -- &viewport_datas[i]); // target_viewport_data -+ comp_render_gfx_add_view( // -+ &data, // -+ &world_poses[i], // -+ &eye_poses[i], // -+ &fovs[i], // -+ rsci_rtr, // -+ &layer_viewport_data, // -+ &layer_norm_rect, // -+ rsci->image, // -+ rsci->srgb_view, // -+ scratch_view->info.mip_count, // -+ &vertex_rots[i], // -+ &viewport_datas[i]); // target_viewport_data - - if (layer_count == 0) { - crss->views[i].used = false; -@@ -1037,17 +1045,19 @@ dispatch_compute(struct comp_renderer *r, - // Scratch image covers the whole image. - struct xrt_normalized_rect layer_norm_rect = {.x = 0.0f, .y = 0.0f, .w = 1.0f, .h = 1.0f}; - -- comp_render_cs_add_view( // -- &data, // -- &world_poses[i], // -- &eye_poses[i], // -- &fovs[i], // -- &layer_viewport_data, // -- &layer_norm_rect, // -- rsci->image, // -- rsci->srgb_view, // -- rsci->unorm_view, // -- &views[i]); // target_viewport_data -+ comp_render_cs_add_view( // -+ &data, // -+ &world_poses[i], // -+ &eye_poses[i], // -+ &fovs[i], // -+ &layer_viewport_data, // -+ &layer_norm_rect, // -+ rsci->image, // -+ rsci->srgb_view, // -+ rsci->unorm_view, // -+ rsci->mip_views, // -+ scratch_view->info.mip_count, // -+ &views[i]); // target_viewport_data - - if (layer_count == 0) { - crss->views[i].used = false; -diff --git a/src/xrt/compositor/main/comp_settings.c b/src/xrt/compositor/main/comp_settings.c -index 7aad08906..0fb1da013 100644 ---- a/src/xrt/compositor/main/comp_settings.c -+++ b/src/xrt/compositor/main/comp_settings.c -@@ -30,6 +30,7 @@ DEBUG_GET_ONCE_BOOL_OPTION(xcb_fullscreen, "XRT_COMPOSITOR_XCB_FULLSCREEN", fals - DEBUG_GET_ONCE_NUM_OPTION(xcb_display, "XRT_COMPOSITOR_XCB_DISPLAY", -1) - DEBUG_GET_ONCE_NUM_OPTION(default_framerate, "XRT_COMPOSITOR_DEFAULT_FRAMERATE", 60) - DEBUG_GET_ONCE_BOOL_OPTION(compute, "XRT_COMPOSITOR_COMPUTE", USE_COMPUTE_DEFAULT) -+DEBUG_GET_ONCE_NUM_OPTION(distortion_mip_levels, "XRT_DISTORTION_MIP_LEVELS", 1) - // clang-format on - - static inline void -@@ -81,6 +82,8 @@ comp_settings_init(struct comp_settings *s, struct xrt_device *xdev) - - // Untested: Super constrained platforms. - add_format(s, VK_FORMAT_A1R5G5B5_UNORM_PACK16); -+ -+ s->mip_levels = debug_get_num_option_distortion_mip_levels(); - } else { - #if defined(XRT_OS_ANDROID) - /* -@@ -109,6 +112,9 @@ comp_settings_init(struct comp_settings *s, struct xrt_device *xdev) - - // Seen as the only sRGB format on some NVIDIA cards. - add_format(s, VK_FORMAT_A8B8G8R8_SRGB_PACK32); -+ -+ // mipmaps not supported on graphics pipeline -+ s->mip_levels = 1; - } - - s->display = debug_get_num_option_xcb_display(); -diff --git a/src/xrt/compositor/main/comp_settings.h b/src/xrt/compositor/main/comp_settings.h -index 2766b9228..b3d20c451 100644 ---- a/src/xrt/compositor/main/comp_settings.h -+++ b/src/xrt/compositor/main/comp_settings.h -@@ -107,6 +107,9 @@ struct comp_settings - - //! Try to choose the mode with this index for direct mode - int desired_mode; -+ -+ //! Number of mip levels for distortion, 0 for full mipmaps, only when using compute -+ int mip_levels; - }; - - /*! -diff --git a/src/xrt/compositor/render/render_compute.c b/src/xrt/compositor/render/render_compute.c -index 27b1953cf..131b97066 100644 ---- a/src/xrt/compositor/render/render_compute.c -+++ b/src/xrt/compositor/render/render_compute.c -@@ -315,6 +315,17 @@ render_compute_init(struct render_compute *render, struct render_resources *r) - - VK_NAME_DESCRIPTOR_SET(vk, render->shared_descriptor_set, "render_compute shared descriptor set"); - -+ for (uint32_t i = 0; i < ARRAY_SIZE(render->mip_descriptor_set); ++i) { -+ ret = vk_create_descriptor_set( // -+ vk, // vk_bundle -+ r->compute.descriptor_pool, // descriptor_pool -+ r->compute.mipmap.descriptor_set_layout, // descriptor_set_layout -+ &render->mip_descriptor_set[i]); // descriptor_set -+ VK_CHK_WITH_RET(ret, "vk_create_descriptor_set", false); -+ VK_NAME_DESCRIPTOR_SET(vk, render->mip_descriptor_set[i], "render_compute mipmap descriptor set"); -+ } -+ -+ - return true; - } - -@@ -377,6 +388,9 @@ render_compute_fini(struct render_compute *render) - - struct vk_bundle *vk = vk_from_render(render); - -+ for (uint32_t i = 0; i < ARRAY_SIZE(render->mip_descriptor_set); i++) { -+ render->mip_descriptor_set[i] = VK_NULL_HANDLE; -+ } - // Reclaimed by vkResetDescriptorPool. - render->shared_descriptor_set = VK_NULL_HANDLE; - for (uint32_t i = 0; i < ARRAY_SIZE(render->layer_descriptor_sets); i++) { -diff --git a/src/xrt/compositor/render/render_gfx.c b/src/xrt/compositor/render/render_gfx.c -index d1a155e1d..a06ac2ba1 100644 ---- a/src/xrt/compositor/render/render_gfx.c -+++ b/src/xrt/compositor/render/render_gfx.c -@@ -942,9 +942,11 @@ render_gfx_target_resources_init(struct render_gfx_target_resources *rtr, - void - render_gfx_target_resources_fini(struct render_gfx_target_resources *rtr) - { -- struct vk_bundle *vk = vk_from_rtr(rtr); -+ if (rtr->r) { -+ struct vk_bundle *vk = vk_from_rtr(rtr); - -- D(Framebuffer, rtr->framebuffer); -+ D(Framebuffer, rtr->framebuffer); -+ } - - U_ZERO(rtr); - } -diff --git a/src/xrt/compositor/render/render_interface.h b/src/xrt/compositor/render/render_interface.h -index d5d2bc4cb..aab965e0c 100644 ---- a/src/xrt/compositor/render/render_interface.h -+++ b/src/xrt/compositor/render/render_interface.h -@@ -64,6 +64,11 @@ extern "C" { - #define RENDER_MAX_IMAGES_SIZE (RENDER_MAX_LAYERS * XRT_MAX_VIEWS) - #define RENDER_MAX_IMAGES_COUNT(RENDER_RESOURCES) (RENDER_MAX_LAYERS * RENDER_RESOURCES->view_count) - -+/*! -+ * Max number of mip levels for distortion calculation -+ */ -+#define COMP_MAX_MIP_LEVELS 16 -+ - /*! - * Maximum number of times that the layer squasher shader can run per - * @ref render_compute. Since you run the layer squasher shader once per view -@@ -138,6 +143,8 @@ struct render_shaders - VkShaderModule clear_comp; - VkShaderModule layer_comp; - VkShaderModule distortion_comp; -+ VkShaderModule mipmap_fast_comp; -+ VkShaderModule mipmap_general_comp; - - VkShaderModule mesh_vert; - VkShaderModule mesh_frag; -@@ -535,6 +542,19 @@ struct render_resources - - //! @todo other resources - } clear; -+ -+ struct -+ { -+ //! Descriptor set layout for compute mipmaps. -+ VkDescriptorSetLayout descriptor_set_layout; -+ -+ //! Pipeline layout used for compute mipmaps. -+ VkPipelineLayout pipeline_layout; -+ -+ //! Doesn't depend on target so is static. -+ VkPipeline fast_pipeline; -+ VkPipeline general_pipeline; -+ } mipmap; - } compute; - - struct -@@ -645,6 +665,7 @@ struct render_scratch_color_image - VkImage image; - VkImageView srgb_view; - VkImageView unorm_view; -+ VkImageView mip_views[COMP_MAX_MIP_LEVELS]; - }; - - /*! -@@ -1176,6 +1197,9 @@ struct render_compute - * @ref render_compute_projection, and @ref render_compute_clear. - */ - VkDescriptorSet shared_descriptor_set; -+ -+ //! Descriptor set to generate mipmaps -+ VkDescriptorSet mip_descriptor_set[XRT_MAX_VIEWS]; - }; - - /*! -diff --git a/src/xrt/compositor/render/render_resources.c b/src/xrt/compositor/render/render_resources.c -index ccc6f10cf..b9b8d4a22 100644 ---- a/src/xrt/compositor/render/render_resources.c -+++ b/src/xrt/compositor/render/render_resources.c -@@ -287,6 +287,45 @@ create_compute_distortion_descriptor_set_layout(struct vk_bundle *vk, - return VK_SUCCESS; - } - -+XRT_CHECK_RESULT static VkResult -+create_compute_mipmap_descriptor_set_layout(struct vk_bundle *vk, VkDescriptorSetLayout *out_descriptor_set_layout) -+{ -+ VkResult ret; -+ -+ VkDescriptorSetLayoutBinding set_layout_bindings[] = { -+ { -+ .binding = 0, -+ .descriptorType = VK_DESCRIPTOR_TYPE_COMBINED_IMAGE_SAMPLER, -+ .descriptorCount = 1, -+ .stageFlags = VK_SHADER_STAGE_COMPUTE_BIT, -+ }, -+ { -+ .binding = 1, -+ .descriptorType = VK_DESCRIPTOR_TYPE_STORAGE_IMAGE, -+ .descriptorCount = COMP_MAX_MIP_LEVELS, -+ .stageFlags = VK_SHADER_STAGE_COMPUTE_BIT, -+ }, -+ }; -+ -+ VkDescriptorSetLayoutCreateInfo set_layout_info = { -+ .sType = VK_STRUCTURE_TYPE_DESCRIPTOR_SET_LAYOUT_CREATE_INFO, -+ .bindingCount = ARRAY_SIZE(set_layout_bindings), -+ .pBindings = set_layout_bindings, -+ }; -+ -+ VkDescriptorSetLayout descriptor_set_layout = VK_NULL_HANDLE; -+ ret = vk->vkCreateDescriptorSetLayout( // -+ vk->device, // -+ &set_layout_info, // -+ NULL, // -+ &descriptor_set_layout); // -+ VK_CHK_AND_RET(ret, "vkCreateDescriptorSetLayout"); -+ -+ *out_descriptor_set_layout = descriptor_set_layout; -+ -+ return VK_SUCCESS; -+} -+ - struct compute_layer_params - { - VkBool32 do_timewarp; -@@ -481,6 +520,29 @@ create_scratch_image_and_view(struct vk_bundle *vk, VkExtent2D extent, struct re - - VK_NAME_IMAGE_VIEW(vk, unorm_view, "render_scratch_color_image image view unorm"); - -+ for (uint32_t level = 0; level < COMP_MAX_MIP_LEVELS; level++) { -+ const VkImageSubresourceRange mip_subresource_range = { -+ .aspectMask = VK_IMAGE_ASPECT_COLOR_BIT, -+ .baseMipLevel = level, -+ .levelCount = 1, -+ .baseArrayLayer = 0, -+ .layerCount = VK_REMAINING_ARRAY_LAYERS, -+ }; -+ ret = vk_create_view_usage( // -+ vk, // vk_bundle -+ image, // image -+ view_type, // type -+ unorm_format, // format -+ VK_IMAGE_USAGE_STORAGE_BIT, // image_usage -+ mip_subresource_range, // subresource_range -+ &rsci->mip_views[level]); // out_image_view -+ VK_CHK_WITH_RET(ret, "vk_create_view_usage(mip)", false); -+ -+ char buf[100]; -+ sprintf(buf, "render_scratch_image_view(mip)[%d]", level); -+ VK_NAME_IMAGE_VIEW(vk, rsci->mip_views[level], buf); -+ } -+ - rsci->device_memory = device_memory; - rsci->image = image; - rsci->srgb_view = srgb_view; -@@ -829,13 +891,14 @@ render_resources_init(struct render_resources *r, - - const uint32_t compute_descriptor_count = // - 1 + // Shared/distortion run(s). -- RENDER_MAX_LAYER_RUNS_COUNT(r); // Layer shader run(s). -+ RENDER_MAX_LAYER_RUNS_COUNT(r) + // Layer shader run(s). -+ XRT_MAX_VIEWS; // Mipmap - - struct vk_descriptor_pool_info compute_pool_info = { - .uniform_per_descriptor_count = 1, - // layer images -- .sampler_per_descriptor_count = r->compute.layer.image_array_size + RENDER_DISTORTION_IMAGES_COUNT(r), -- .storage_image_per_descriptor_count = 1, -+ .sampler_per_descriptor_count = r->compute.layer.image_array_size + RENDER_DISTORTION_IMAGES_COUNT(r) + 1, -+ .storage_image_per_descriptor_count = 1 + COMP_MAX_MIP_LEVELS, - .storage_buffer_per_descriptor_count = 0, - .descriptor_count = compute_descriptor_count, - .freeable = false, -@@ -1034,6 +1097,62 @@ render_resources_init(struct render_resources *r, - &r->compute.clear.ubo); // buffer - VK_CHK_WITH_RET(ret, "render_buffer_map", false); - -+ /* -+ * Mipmap pipeline -+ */ -+ ret = create_compute_mipmap_descriptor_set_layout( -+ vk, // vk_bundle -+ &r->compute.mipmap.descriptor_set_layout // out_descriptor_set_layout -+ ); -+ VK_CHK_WITH_RET(ret, "create_compute_mipmap_descriptor_set_layout", false); -+ -+ VK_NAME_DESCRIPTOR_SET_LAYOUT(vk, r->compute.mipmap.descriptor_set_layout, -+ "render_resources compute mipmap descriptor set layout"); -+ -+ VkPushConstantRange push_constant_range = { -+ .size = 4, -+ .offset = 0, -+ .stageFlags = VK_SHADER_STAGE_COMPUTE_BIT, -+ }; -+ -+ VkPipelineLayoutCreateInfo pipeline_layout_info = { -+ .sType = VK_STRUCTURE_TYPE_PIPELINE_LAYOUT_CREATE_INFO, -+ .setLayoutCount = 1, -+ .pSetLayouts = &r->compute.mipmap.descriptor_set_layout, -+ .pushConstantRangeCount = 1, -+ .pPushConstantRanges = &push_constant_range, -+ }; -+ -+ ret = vk->vkCreatePipelineLayout( // -+ vk->device, // device -+ &pipeline_layout_info, // pCreateInfo -+ NULL, // pAllocator -+ &r->compute.mipmap.pipeline_layout); // pPipelineLayout -+ VK_CHK_WITH_RET(ret, "vk_create_pipeline_layout", false); -+ -+ VK_NAME_PIPELINE_LAYOUT(vk, r->compute.distortion.pipeline_layout, -+ "render_resources compute mipmap pipeline layout"); -+ -+ ret = vk_create_compute_pipeline( // -+ vk, // vk_bundle -+ r->pipeline_cache, // pipeline_cache -+ r->shaders->mipmap_fast_comp, // shader -+ r->compute.mipmap.pipeline_layout, // pipeline_layout -+ NULL, // specialization_info -+ &r->compute.mipmap.fast_pipeline); // out_compute_pipeline -+ VK_CHK_WITH_RET(ret, "vk_create_compute_pipeline", false); -+ VK_NAME_PIPELINE(vk, r->compute.mipmap.fast_pipeline, "render_resources compute mipmap fast pipeline"); -+ -+ ret = vk_create_compute_pipeline( // -+ vk, // vk_bundle -+ r->pipeline_cache, // pipeline_cache -+ r->shaders->mipmap_general_comp, // shader -+ r->compute.mipmap.pipeline_layout, // pipeline_layout -+ NULL, // specialization_info -+ &r->compute.mipmap.general_pipeline); // out_compute_pipeline -+ VK_CHK_WITH_RET(ret, "vk_create_compute_pipeline", false); -+ VK_NAME_PIPELINE(vk, r->compute.mipmap.general_pipeline, "render_resources compute mipmap general pipeline"); -+ - - /* - * Compute distortion textures, not created until later. -@@ -1129,6 +1248,11 @@ render_resources_fini(struct render_resources *r) - - D(Pipeline, r->compute.clear.pipeline); - -+ D(DescriptorSetLayout, r->compute.mipmap.descriptor_set_layout); -+ D(Pipeline, r->compute.mipmap.fast_pipeline); -+ D(Pipeline, r->compute.mipmap.general_pipeline); -+ D(PipelineLayout, r->compute.mipmap.pipeline_layout); -+ - render_distortion_images_fini(r); - render_buffer_fini(vk, &r->compute.clear.ubo); - for (uint32_t i = 0; i < r->view_count; i++) { -diff --git a/src/xrt/compositor/render/render_shaders.c b/src/xrt/compositor/render/render_shaders.c -index b6bae2690..02847ca45 100644 ---- a/src/xrt/compositor/render/render_shaders.c -+++ b/src/xrt/compositor/render/render_shaders.c -@@ -39,6 +39,8 @@ - #include "shaders/layer_shared.frag.h" - #include "shaders/mesh.frag.h" - #include "shaders/mesh.vert.h" -+#include "srgba8_mipmap_fast_pipeline.comp.h" -+#include "srgba8_mipmap_general_pipeline.comp.h" - - #if defined(__GNUC__) - #pragma GCC diagnostic pop -@@ -51,22 +53,23 @@ - * - */ - --#define LOAD(SHADER) \ -+#define LOAD_NAME(VAR, SHADER) \ - do { \ -- const uint32_t *code = shaders_##SHADER; \ -- size_t size = sizeof(shaders_##SHADER); \ -- VkResult ret = shader_load(vk, /* vk_bundle */ \ -- code, /* code */ \ -- size, /* size */ \ -- &s->SHADER); /* out */ \ -+ const uint32_t *code = SHADER; \ -+ size_t size = sizeof(SHADER); \ -+ VkResult ret = shader_load(vk, /* vk_bundle */ \ -+ code, /* code */ \ -+ size, /* size */ \ -+ &s->VAR); /* out */ \ - if (ret != VK_SUCCESS) { \ - VK_ERROR(vk, "Failed to load shader '" #SHADER "'"); \ - render_shaders_fini(s, vk); \ - return false; \ - } \ -- VK_NAME_SHADER_MODULE(vk, s->SHADER, #SHADER); \ -+ VK_NAME_SHADER_MODULE(vk, s->VAR, #SHADER); \ - } while (false) - -+#define LOAD(SHADER) LOAD_NAME(SHADER, shaders_##SHADER) - - /* - * -@@ -109,6 +112,9 @@ render_shaders_load(struct render_shaders *s, struct vk_bundle *vk) - - LOAD(distortion_comp); - -+ LOAD_NAME(mipmap_fast_comp, nvpro_pyramid_srgba8_mipmap_fast_pipeline_comp); -+ LOAD_NAME(mipmap_general_comp, nvpro_pyramid_srgba8_mipmap_general_pipeline_comp); -+ - LOAD(mesh_vert); - LOAD(mesh_frag); - -@@ -131,6 +137,8 @@ render_shaders_fini(struct render_shaders *s, struct vk_bundle *vk) - D(ShaderModule, s->blit_comp); - D(ShaderModule, s->clear_comp); - D(ShaderModule, s->distortion_comp); -+ D(ShaderModule, s->mipmap_fast_comp); -+ D(ShaderModule, s->mipmap_general_comp); - D(ShaderModule, s->layer_comp); - D(ShaderModule, s->mesh_vert); - D(ShaderModule, s->mesh_frag); -diff --git a/src/xrt/compositor/shaders/distortion.comp b/src/xrt/compositor/shaders/distortion.comp -index 08b44d4cf..9a09c03db 100644 ---- a/src/xrt/compositor/shaders/distortion.comp -+++ b/src/xrt/compositor/shaders/distortion.comp -@@ -102,6 +102,26 @@ vec2 transform_uv(vec2 uv, uint iz) - } - } - -+mat3x2 source_coord(ivec3 xyv, ivec2 extent) -+{ -+ uint ix = xyv.x; -+ uint iy = xyv.y; -+ uint iz = xyv.z; -+ -+ vec2 dist_uv = position_to_uv(extent, ix, iy); -+ -+ vec2 r_uv = texture(distortion[iz + 0], dist_uv).xy; -+ vec2 g_uv = texture(distortion[iz + 2], dist_uv).xy; -+ vec2 b_uv = texture(distortion[iz + 4], dist_uv).xy; -+ -+ // Do any transformation needed. -+ mat3x2 res; -+ res[0] = transform_uv(r_uv, iz); -+ res[1] = transform_uv(g_uv, iz); -+ res[2] = transform_uv(b_uv, iz); -+ return res; -+} -+ - void main() - { - uint ix = gl_GlobalInvocationID.x; -@@ -117,21 +137,14 @@ void main() - - vec2 dist_uv = position_to_uv(extent, ix, iy); - -- vec2 r_uv = texture(distortion[iz + 0], dist_uv).xy; -- vec2 g_uv = texture(distortion[iz + 2], dist_uv).xy; -- vec2 b_uv = texture(distortion[iz + 4], dist_uv).xy; -+ vec2 uv = texture(distortion[iz + 0], dist_uv).xy; -+ vec2 lod = texture(distortion[iz + 2], dist_uv).xy; - - // Do any transformation needed. -- r_uv = transform_uv(r_uv, iz); -- g_uv = transform_uv(g_uv, iz); -- b_uv = transform_uv(b_uv, iz); -- -- // Sample the source with distorted and chromatic-aberration corrected samples. -- vec4 colour = vec4( -- texture(source[iz], r_uv).r, -- texture(source[iz], g_uv).g, -- texture(source[iz], b_uv).b, -- 1); -+ uv = transform_uv(uv, iz); -+ -+ // Sample the source -+ vec4 colour = textureLod(source[iz], uv, mix(lod.x, lod.y, 0.5)); - - // Do colour correction here since there are no automatic conversion in hardware available. - colour = vec4(from_linear_to_srgb(colour.rgb), 1); -diff --git a/src/xrt/compositor/util/comp_mipmap.cpp b/src/xrt/compositor/util/comp_mipmap.cpp -new file mode 100644 -index 000000000..37a5d5f60 ---- /dev/null -+++ b/src/xrt/compositor/util/comp_mipmap.cpp -@@ -0,0 +1,25 @@ -+// Copyright 2023, Collabora, Ltd. -+// SPDX-License-Identifier: BSL-1.0 -+ -+#include "comp_mipmap.h" -+ -+#include "nvpro_pyramid_dispatch.hpp" -+ -+void -+comp_do_mipmaps(VkCommandBuffer cmdBuf, -+ VkPipeline fast_pipeline, -+ VkPipeline general_pipeline, -+ VkPipelineLayout layout, -+ uint32_t width, -+ uint32_t height, -+ uint32_t mip_levels) -+{ -+ nvproCmdPyramidDispatch(cmdBuf, -+ NvproPyramidPipelines{ -+ .generalPipeline = general_pipeline, -+ .fastPipeline = fast_pipeline, -+ .layout = layout, -+ .pushConstantOffset = 0, -+ }, -+ width, height, mip_levels); -+} -diff --git a/src/xrt/compositor/util/comp_mipmap.h b/src/xrt/compositor/util/comp_mipmap.h -new file mode 100644 -index 000000000..e5d3c670d ---- /dev/null -+++ b/src/xrt/compositor/util/comp_mipmap.h -@@ -0,0 +1,22 @@ -+// Copyright 2023, Collabora, Ltd. -+// SPDX-License-Identifier: BSL-1.0 -+ -+#include "vulkan/vulkan.h" -+ -+#ifdef __cplusplus -+extern "C" { -+#endif -+ -+void -+comp_do_mipmaps(VkCommandBuffer cmdBuf, -+ VkPipeline fast_pipeline, -+ VkPipeline general_pipeline, -+ VkPipelineLayout layout, -+ uint32_t width, -+ uint32_t height, -+ uint32_t mip_levels); -+ -+ -+#ifdef __cplusplus -+} -+#endif -diff --git a/src/xrt/compositor/util/comp_render.h b/src/xrt/compositor/util/comp_render.h -index 579b91413..e6fd072be 100644 ---- a/src/xrt/compositor/util/comp_render.h -+++ b/src/xrt/compositor/util/comp_render.h -@@ -125,6 +125,11 @@ struct comp_render_view_data - { - //! Only used on compute path. - VkImageView unorm_view; -+ -+ //! Number of mip levels of scratch images -+ int mip_count; -+ -+ VkImageView mip_views[COMP_MAX_MIP_LEVELS]; - } cs; - }; - -@@ -297,6 +302,7 @@ comp_render_gfx_add_view(struct comp_render_dispatch_data *data, - const struct xrt_normalized_rect *layer_norm_rect, - VkImage image, - VkImageView srgb_view, -+ int mip_count, - const struct xrt_matrix_2x2 *vertex_rot, - const struct render_viewport_data *target_viewport_data) - { -@@ -314,6 +320,8 @@ comp_render_gfx_add_view(struct comp_render_dispatch_data *data, - // TODO why is the one in data not used instead - view->gfx.rtr = rtr; - view->gfx.vertex_rot = *vertex_rot; -+ -+ view->cs.mip_count = mip_count; - } - - /*! -@@ -469,6 +477,8 @@ comp_render_cs_add_view(struct comp_render_dispatch_data *data, - VkImage image, - VkImageView srgb_view, - VkImageView unorm_view, -+ VkImageView *mip_views, -+ int mip_count, - const struct render_viewport_data *target_viewport_data) - { - struct comp_render_view_data *view = comp_render_dispatch_add_view( // -@@ -483,6 +493,8 @@ comp_render_cs_add_view(struct comp_render_dispatch_data *data, - target_viewport_data); - - view->cs.unorm_view = unorm_view; -+ view->cs.mip_count = mip_count; -+ memcpy(view->cs.mip_views, mip_views, mip_count * sizeof(VkImageView)); - } - - /*! -diff --git a/src/xrt/compositor/util/comp_render_cs.c b/src/xrt/compositor/util/comp_render_cs.c -index dbc2fec7e..fc11a0521 100644 ---- a/src/xrt/compositor/util/comp_render_cs.c -+++ b/src/xrt/compositor/util/comp_render_cs.c -@@ -25,6 +25,7 @@ - - #include "render/render_interface.h" - -+#include "util/comp_mipmap.h" - #include "util/comp_render.h" - #include "util/comp_render_helpers.h" - #include "util/comp_base.h" -@@ -328,6 +329,92 @@ do_cs_quad_layer(const struct comp_layer *layer, - *out_cur_image = cur_image; - } - -+static void -+fill_mip_levels(struct render_resources *r, const struct comp_render_view_data *view, VkDescriptorSet descriptor_set) -+{ -+ if (view->cs.mip_count == 1) -+ return; -+ -+ uint32_t width = view->layer_viewport_data.w; -+ uint32_t height = view->layer_viewport_data.h; -+ -+ VkDescriptorImageInfo src_image_info = { -+ .sampler = r->samplers.clamp_to_edge, -+ .imageView = view->srgb_view, -+ .imageLayout = VK_IMAGE_LAYOUT_GENERAL, -+ }; -+ -+ VkDescriptorImageInfo target_image_info[COMP_MAX_MIP_LEVELS]; -+ for (int i = 0; i < COMP_MAX_MIP_LEVELS; ++i) { -+ target_image_info[i].sampler = r->samplers.mock; -+ if (i < view->cs.mip_count) { -+ target_image_info[i].imageView = view->cs.mip_views[i]; -+ } else { -+ // descriptors must be bound, even if unused -+ target_image_info[i].imageView = view->cs.mip_views[0]; -+ } -+ target_image_info[i].imageLayout = VK_IMAGE_LAYOUT_GENERAL; -+ }; -+ -+ VkWriteDescriptorSet write_descriptor_sets[] = { -+ { -+ .sType = VK_STRUCTURE_TYPE_WRITE_DESCRIPTOR_SET, -+ .dstSet = descriptor_set, -+ .dstBinding = 0, -+ .descriptorCount = 1, -+ .descriptorType = VK_DESCRIPTOR_TYPE_COMBINED_IMAGE_SAMPLER, -+ .pImageInfo = &src_image_info, -+ }, -+ { -+ .sType = VK_STRUCTURE_TYPE_WRITE_DESCRIPTOR_SET, -+ .dstSet = descriptor_set, -+ .dstBinding = 1, -+ .descriptorCount = ARRAY_SIZE(target_image_info), -+ .descriptorType = VK_DESCRIPTOR_TYPE_STORAGE_IMAGE, -+ .pImageInfo = target_image_info, -+ }, -+ }; -+ -+ r->vk->vkUpdateDescriptorSets( // -+ r->vk->device, // -+ ARRAY_SIZE(write_descriptor_sets), // descriptorWriteCount -+ write_descriptor_sets, // pDescriptorWrites -+ 0, // descriptorCopyCount -+ NULL); // pDescriptorCopies -+ -+ r->vk->vkCmdBindDescriptorSets( // -+ r->cmd, // commandBuffer -+ VK_PIPELINE_BIND_POINT_COMPUTE, // pipelineBindPoint -+ r->compute.mipmap.pipeline_layout, // layout -+ 0, // firstSet -+ 1, // descriptorSetCount -+ &descriptor_set, // pDescriptorSets -+ 0, // dynamicOffsetCount -+ NULL); // pDynamicOffsets -+ comp_do_mipmaps(r->cmd, r->compute.mipmap.fast_pipeline, r->compute.mipmap.general_pipeline, -+ r->compute.mipmap.pipeline_layout, width, height, view->cs.mip_count); -+ -+ VkImageSubresourceRange range = { -+ .aspectMask = VK_IMAGE_ASPECT_COLOR_BIT, -+ .baseMipLevel = 0, -+ .levelCount = view->cs.mip_count, -+ .baseArrayLayer = 0, -+ .layerCount = 1, -+ }; -+ -+ vk_cmd_image_barrier_locked( // -+ r->vk, // vk_bundle -+ r->cmd, // cmd_buffer -+ view->image, // image -+ VK_ACCESS_MEMORY_WRITE_BIT, // src_access_mask -+ VK_ACCESS_MEMORY_READ_BIT, // dst_access_mask -+ VK_IMAGE_LAYOUT_GENERAL, // old_image_layout -+ VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL, // new_image_layout -+ VK_PIPELINE_STAGE_COMPUTE_SHADER_BIT, // src_stage_mask -+ VK_PIPELINE_STAGE_COMPUTE_SHADER_BIT, // dst_stage_mask -+ range); // subresource_range -+} -+ - static void - crc_clear_output(struct render_compute *render, const struct comp_render_dispatch_data *d) - { -@@ -374,6 +461,8 @@ crc_distortion_after_squash(struct render_compute *render, const struct comp_ren - struct xrt_normalized_rect src_norm_rects[XRT_MAX_VIEWS]; - - for (uint32_t i = 0; i < d->view_count; i++) { -+ fill_mip_levels(render->r, &d->views[i], render->mip_descriptor_set[i]); -+ - // Data to be filled in. - VkImageView src_image_view; - struct render_viewport_data viewport_data; -@@ -762,6 +851,9 @@ comp_render_cs_dispatch(struct render_compute *render, - U_LOG_W("Wanted fast path but no projection layer, falling back to layer squasher."); - } - -+ if (d->views[0].cs.mip_count != 1) -+ transition_to = VK_IMAGE_LAYOUT_GENERAL; -+ - /* - * Layer squashing. - */ -diff --git a/src/xrt/compositor/util/comp_render_helpers.h b/src/xrt/compositor/util/comp_render_helpers.h -index aa5de56c2..bbf295c0a 100644 ---- a/src/xrt/compositor/util/comp_render_helpers.h -+++ b/src/xrt/compositor/util/comp_render_helpers.h -@@ -145,10 +145,10 @@ cmd_barrier_view_images(struct vk_bundle *vk, - VkPipelineStageFlags src_stage_mask, - VkPipelineStageFlags dst_stage_mask) - { -- VkImageSubresourceRange first_color_level_subresource_range = { -+ VkImageSubresourceRange all_color_level_subresource_range = { - .aspectMask = VK_IMAGE_ASPECT_COLOR_BIT, - .baseMipLevel = 0, -- .levelCount = 1, -+ .levelCount = VK_REMAINING_MIP_LEVELS, - .baseArrayLayer = 0, - .layerCount = 1, - }; -@@ -172,16 +172,16 @@ cmd_barrier_view_images(struct vk_bundle *vk, - continue; - } - -- vk_cmd_image_barrier_locked( // -- vk, // vk_bundle -- cmd, // cmd_buffer -- image, // image -- src_access_mask, // src_access_mask -- dst_access_mask, // dst_access_mask -- transition_from, // old_image_layout -- transition_to, // new_image_layout -- src_stage_mask, // src_stage_mask -- dst_stage_mask, // dst_stage_mask -- first_color_level_subresource_range); // subresource_range -+ vk_cmd_image_barrier_locked( // -+ vk, // vk_bundle -+ cmd, // cmd_buffer -+ image, // image -+ src_access_mask, // src_access_mask -+ dst_access_mask, // dst_access_mask -+ transition_from, // old_image_layout -+ transition_to, // new_image_layout -+ src_stage_mask, // src_stage_mask -+ dst_stage_mask, // dst_stage_mask -+ all_color_level_subresource_range); // subresource_range - } - } -diff --git a/src/xrt/compositor/util/comp_scratch.c b/src/xrt/compositor/util/comp_scratch.c -index 48bfda7c7..1d892a31b 100644 ---- a/src/xrt/compositor/util/comp_scratch.c -+++ b/src/xrt/compositor/util/comp_scratch.c -@@ -16,7 +16,7 @@ - - #include "vk/vk_mini_helpers.h" - #include "vk/vk_image_allocator.h" -- -+#include - - /* - * -@@ -25,7 +25,7 @@ - */ - - static inline void --fill_info(VkExtent2D extent, struct xrt_swapchain_create_info *out_info) -+fill_info(VkExtent2D extent, int mip_count, struct xrt_swapchain_create_info *out_info) - { - enum xrt_swapchain_create_flags create = 0; - -@@ -46,12 +46,22 @@ fill_info(VkExtent2D extent, struct xrt_swapchain_create_info *out_info) - .height = extent.height, - .face_count = 1, - .array_size = 1, -- .mip_count = 1, -+ .mip_count = mip_count, - }; - -+ if (info.mip_count == 0) { -+ uint32_t w = info.width, h = info.height; -+ while (w != 0 || h != 0) { -+ w >>= 1; -+ h >>= 1; -+ ++(info.mip_count); -+ } -+ } -+ - // Use format list to get good performance everywhere. - info.formats[info.format_count++] = VK_FORMAT_R8G8B8A8_UNORM; - info.formats[info.format_count++] = VK_FORMAT_R8G8B8A8_SRGB; -+ info.formats[info.format_count++] = VK_FORMAT_R8G8B8A8_UINT; - - *out_info = info; - } -@@ -131,6 +141,9 @@ struct tmp - - //! For storage operations in compute shaders. - VkImageView unorm_views[COMP_SCRATCH_NUM_IMAGES]; -+ -+ //! For mipmap generation -+ VkImageView mip_views[COMP_SCRATCH_NUM_IMAGES * COMP_MAX_MIP_LEVELS]; - }; - - static inline bool -@@ -159,6 +172,7 @@ tmp_init_and_create(struct tmp *t, struct vk_bundle *vk, const struct xrt_swapch - // Base info. - const VkFormat srgb_format = VK_FORMAT_R8G8B8A8_SRGB; - const VkFormat unorm_format = VK_FORMAT_R8G8B8A8_UNORM; -+ const VkFormat mip_format = VK_FORMAT_R8G8B8A8_UINT; - const VkImageViewType view_type = VK_IMAGE_VIEW_TYPE_2D; - - // Both usages are common. -@@ -201,6 +215,29 @@ tmp_init_and_create(struct tmp *t, struct vk_bundle *vk, const struct xrt_swapch - VK_CHK_WITH_GOTO(ret, "vk_create_view_usage(unorm)", err_destroy_views); - - VK_NAME_IMAGE_VIEW(vk, t->unorm_views[i], "comp_scratch_image_view(unorm)"); -+ -+ for (uint32_t level = 0; level < info->mip_count; level++) { -+ const VkImageSubresourceRange mip_subresource_range = { -+ .aspectMask = VK_IMAGE_ASPECT_COLOR_BIT, -+ .baseMipLevel = level, -+ .levelCount = 1, -+ .baseArrayLayer = 0, -+ .layerCount = VK_REMAINING_ARRAY_LAYERS, -+ }; -+ ret = vk_create_view_usage( // -+ vk, // vk_bundle -+ image, // image -+ view_type, // type -+ mip_format, // format -+ VK_IMAGE_USAGE_STORAGE_BIT, // image_usage -+ mip_subresource_range, // subresource_range -+ &t->mip_views[i * COMP_MAX_MIP_LEVELS + level]); // out_image_view -+ VK_CHK_WITH_GOTO(ret, "vk_create_view_usage(mip)", err_destroy_views); -+ -+ char buf[100]; -+ sprintf(buf, "comp_scratch_image_view(mip)[%d]", level); -+ VK_NAME_IMAGE_VIEW(vk, t->mip_views[i * COMP_MAX_MIP_LEVELS + level], buf); -+ } - } - - return true; -@@ -210,6 +247,9 @@ err_destroy_views: - D(ImageView, t->srgb_views[i]); - D(ImageView, t->unorm_views[i]); - } -+ for (uint32_t i = 0; i < ARRAY_SIZE(t->mip_views); i++) { -+ D(ImageView, t->mip_views[i]); -+ } - - err_destroy_vkic: - vk_ic_destroy(vk, &t->vkic); -@@ -227,6 +267,8 @@ tmp_take(struct tmp *t, - images[i].device_memory = t->vkic.images[i].memory; - images[i].srgb_view = t->srgb_views[i]; - images[i].unorm_view = t->unorm_views[i]; -+ memcpy(images[i].mip_views, &t->mip_views[COMP_MAX_MIP_LEVELS * i], -+ COMP_MAX_MIP_LEVELS * sizeof(VkImageView)); - - native_images[i].size = t->vkic.images[i].size; - native_images[i].use_dedicated_allocation = t->vkic.images[i].use_dedicated_allocation; -@@ -236,6 +278,7 @@ tmp_take(struct tmp *t, - t->unorm_views[i] = VK_NULL_HANDLE; - t->handles[i] = XRT_GRAPHICS_BUFFER_HANDLE_INVALID; - } -+ memset(t->mip_views, 0, sizeof(t->mip_views)); - - // We now own everything. - U_ZERO(&t->vkic); -@@ -250,6 +293,9 @@ tmp_destroy(struct tmp *t, struct vk_bundle *vk) - D(ImageView, t->srgb_views[i]); - D(ImageView, t->unorm_views[i]); - } -+ for (uint32_t i = 0; i < ARRAY_SIZE(t->mip_views); i++) { -+ D(ImageView, t->mip_views[i]); -+ } - } - - -@@ -276,7 +322,10 @@ comp_scratch_single_images_init(struct comp_scratch_single_images *cssi) - } - - bool --comp_scratch_single_images_ensure(struct comp_scratch_single_images *cssi, struct vk_bundle *vk, VkExtent2D extent) -+comp_scratch_single_images_ensure(struct comp_scratch_single_images *cssi, -+ struct vk_bundle *vk, -+ VkExtent2D extent, -+ int mip_count) - { - if (cssi->info.width == extent.width && cssi->info.height == extent.height) { - // Our work here is done! -@@ -284,7 +333,7 @@ comp_scratch_single_images_ensure(struct comp_scratch_single_images *cssi, struc - } - - struct xrt_swapchain_create_info info = XRT_STRUCT_INIT; -- fill_info(extent, &info); -+ fill_info(extent, mip_count, &info); - - struct tmp t; // Is initialized in function. - if (!tmp_init_and_create(&t, vk, &info)) { -@@ -316,6 +365,9 @@ comp_scratch_single_images_free(struct comp_scratch_single_images *cssi, struct - - D(ImageView, cssi->images[i].srgb_view); - D(ImageView, cssi->images[i].unorm_view); -+ for (uint32_t j = 0; j < ARRAY_SIZE(cssi->images[i].mip_views); j++) { -+ D(ImageView, cssi->images[i].mip_views[j]); -+ } - D(Image, cssi->images[i].image); - DF(Memory, cssi->images[i].device_memory); - } -@@ -400,7 +452,7 @@ comp_scratch_stereo_images_init(struct comp_scratch_stereo_images *cssi) - } - - bool --comp_scratch_stereo_images_ensure(struct comp_scratch_stereo_images *cssi, struct vk_bundle *vk, VkExtent2D extent) -+comp_scratch_stereo_images_ensure(struct comp_scratch_stereo_images *cssi, struct vk_bundle *vk, VkExtent2D extent, int mip_count) - { - if (cssi->info.width == extent.width && cssi->info.height == extent.height) { - // Our work here is done! -@@ -409,7 +461,7 @@ comp_scratch_stereo_images_ensure(struct comp_scratch_stereo_images *cssi, struc - - // Get info we need to share with. - struct xrt_swapchain_create_info info = XRT_STRUCT_INIT; -- fill_info(extent, &info); -+ fill_info(extent, mip_count, &info); - - struct tmp ts[2]; // Is initialized in function. - if (!tmp_init_and_create(&ts[0], vk, &info)) { -@@ -465,6 +517,9 @@ comp_scratch_stereo_images_free(struct comp_scratch_stereo_images *cssi, struct - // Organised into scratch images, then views. - D(ImageView, cssi->rsis[i].color[view].srgb_view); - D(ImageView, cssi->rsis[i].color[view].unorm_view); -+ for (uint32_t j = 0; j < ARRAY_SIZE(cssi->rsis[i].color[view].mip_views); j++) { -+ D(ImageView, cssi->rsis[i].color[view].mip_views[j]); -+ } - D(Image, cssi->rsis[i].color[view].image); - DF(Memory, cssi->rsis[i].color[view].device_memory); - } -diff --git a/src/xrt/compositor/util/comp_scratch.h b/src/xrt/compositor/util/comp_scratch.h -index 44849c20e..f8c891e49 100644 ---- a/src/xrt/compositor/util/comp_scratch.h -+++ b/src/xrt/compositor/util/comp_scratch.h -@@ -106,7 +106,10 @@ comp_scratch_single_images_init(struct comp_scratch_single_images *cssi); - * @ingroup comp_util - */ - bool --comp_scratch_single_images_ensure(struct comp_scratch_single_images *cssi, struct vk_bundle *vk, VkExtent2D extent); -+comp_scratch_single_images_ensure(struct comp_scratch_single_images *cssi, -+ struct vk_bundle *vk, -+ VkExtent2D extent, -+ int mip_count); - - /*! - * Free all images allocated, @p init must be called before calling this -@@ -215,7 +218,7 @@ void - comp_scratch_stereo_images_init(struct comp_scratch_stereo_images *cssi); - - bool --comp_scratch_stereo_images_ensure(struct comp_scratch_stereo_images *cssi, struct vk_bundle *vk, VkExtent2D extent); -+comp_scratch_stereo_images_ensure(struct comp_scratch_stereo_images *cssi, struct vk_bundle *vk, VkExtent2D extent, int mip_count); - - void - comp_scratch_stereo_images_free(struct comp_scratch_stereo_images *cssi, struct vk_bundle *vk); --- -2.48.0 - diff --git a/0006-st-oxr-push-XrEventDataInteractionProfileChanged-whe.patch b/0006-st-oxr-push-XrEventDataInteractionProfileChanged-whe.patch new file mode 100644 index 0000000..44f0322 --- /dev/null +++ b/0006-st-oxr-push-XrEventDataInteractionProfileChanged-whe.patch @@ -0,0 +1,27 @@ +From f400f0e63259bcadd9a7955ae0275caa5aea3ded Mon Sep 17 00:00:00 2001 +From: Sapphire +Date: Fri, 7 Nov 2025 20:41:16 -0600 +Subject: [PATCH 6/8] st/oxr: push XrEventDataInteractionProfileChanged when + profile changes to NULL + +--- + src/xrt/state_trackers/oxr/actions/oxr_input.c | 3 --- + 1 file changed, 3 deletions(-) + +diff --git a/src/xrt/state_trackers/oxr/actions/oxr_input.c b/src/xrt/state_trackers/oxr/actions/oxr_input.c +index 2bd3ea009..93659ba35 100644 +--- a/src/xrt/state_trackers/oxr/actions/oxr_input.c ++++ b/src/xrt/state_trackers/oxr/actions/oxr_input.c +@@ -1932,9 +1932,6 @@ session_update_action_bindings(struct oxr_logger *log, + #define POPULATE_PROFILE(X) \ + do { \ + XrPath path = profiles.X != NULL ? profiles.X->path : XR_NULL_PATH; \ +- if (path == XR_NULL_PATH) { \ +- break; /* Only update on "active" interaction profiles per sub-action path. */ \ +- } \ + if (action_context->X != path) { \ + action_context->X = path; \ + interaction_profile_changed = true; \ +-- +2.54.0 + diff --git a/0007-Convert-to-YCbCr-in-Monado.patch b/0007-Convert-to-YCbCr-in-Monado.patch deleted file mode 100644 index f1c9ad3..0000000 --- a/0007-Convert-to-YCbCr-in-Monado.patch +++ /dev/null @@ -1,719 +0,0 @@ -From cf75548500d8aff912f2e63d68e0ea3628941bf8 Mon Sep 17 00:00:00 2001 -From: Patrick Nicolas -Date: Wed, 11 Sep 2024 22:56:29 +0200 -Subject: [PATCH 07/10] Convert to YCbCr in Monado - ---- - src/xrt/compositor/main/comp_renderer.c | 6 +- - src/xrt/compositor/main/comp_target.h | 3 +- - src/xrt/compositor/render/render_compute.c | 104 +++++++++++++------ - src/xrt/compositor/render/render_interface.h | 15 ++- - src/xrt/compositor/render/render_resources.c | 35 +++++-- - src/xrt/compositor/shaders/distortion.comp | 55 +++++++--- - src/xrt/compositor/util/comp_render.h | 12 ++- - src/xrt/compositor/util/comp_render_cs.c | 41 +++++--- - 8 files changed, 186 insertions(+), 85 deletions(-) - -diff --git a/src/xrt/compositor/main/comp_renderer.c b/src/xrt/compositor/main/comp_renderer.c -index 104f761bf..3b3439d5a 100644 ---- a/src/xrt/compositor/main/comp_renderer.c -+++ b/src/xrt/compositor/main/comp_renderer.c -@@ -1009,7 +1009,8 @@ dispatch_compute(struct comp_renderer *r, - - // Target Vulkan resources.. - VkImage target_image = r->c->target->images[r->acquired_buffer].handle; -- VkImageView target_image_view = r->c->target->images[r->acquired_buffer].view; -+ VkImageView target_image_view_y = r->c->target->images[r->acquired_buffer].view; -+ VkImageView target_image_view_cbcr = r->c->target->images[r->acquired_buffer].view_cbcr; - - // Target view information. - struct render_viewport_data views[XRT_MAX_VIEWS]; -@@ -1020,7 +1021,8 @@ dispatch_compute(struct comp_renderer *r, - comp_render_cs_initial_init( // - &data, // data - target_image, // target_image -- target_image_view, // target_unorm_view -+ target_image_view_y, // target_unorm_view_y -+ target_image_view_cbcr, // target_unorm_view_cbcr - fast_path, // fast_path - do_timewarp); // do_timewarp - -diff --git a/src/xrt/compositor/main/comp_target.h b/src/xrt/compositor/main/comp_target.h -index 876ff8cd9..924b4ea08 100644 ---- a/src/xrt/compositor/main/comp_target.h -+++ b/src/xrt/compositor/main/comp_target.h -@@ -61,7 +61,8 @@ enum comp_target_display_timing_usage - struct comp_target_image - { - VkImage handle; -- VkImageView view; -+ VkImageView view; // Y -+ VkImageView view_cbcr; - }; - - /*! -diff --git a/src/xrt/compositor/render/render_compute.c b/src/xrt/compositor/render/render_compute.c -index 131b97066..a9552cf2c 100644 ---- a/src/xrt/compositor/render/render_compute.c -+++ b/src/xrt/compositor/render/render_compute.c -@@ -66,8 +66,8 @@ calc_dispatch_dims_views(const struct render_viewport_data views[XRT_MAX_VIEWS], - #undef IMAX - - // Power of two divide and round up. -- w = uint_divide_and_round_up(w, 8); -- h = uint_divide_and_round_up(h, 8); -+ w = uint_divide_and_round_up(w, 16); -+ h = uint_divide_and_round_up(h, 16); - - *out_w = w; - *out_h = h; -@@ -86,8 +86,10 @@ update_compute_layer_descriptor_set(struct vk_bundle *vk, - VkSampler src_samplers[RENDER_MAX_IMAGES_SIZE], - VkImageView src_image_views[RENDER_MAX_IMAGES_SIZE], - uint32_t image_count, -- uint32_t target_binding, -- VkImageView target_image_view, -+ uint32_t target_binding_y, -+ uint32_t target_binding_cbcr, -+ VkImageView target_image_view_y, -+ VkImageView target_image_view_cbcr, - uint32_t ubo_binding, - VkBuffer ubo_buffer, - VkDeviceSize ubo_size, -@@ -100,8 +102,13 @@ update_compute_layer_descriptor_set(struct vk_bundle *vk, - src_image_info[i].imageLayout = VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL; - } - -- VkDescriptorImageInfo target_image_info = { -- .imageView = target_image_view, -+ VkDescriptorImageInfo target_image_info_y = { -+ .imageView = target_image_view_y, -+ .imageLayout = VK_IMAGE_LAYOUT_GENERAL, -+ }; -+ -+ VkDescriptorImageInfo target_image_info_cbcr = { -+ .imageView = target_image_view_cbcr, - .imageLayout = VK_IMAGE_LAYOUT_GENERAL, - }; - -@@ -111,7 +118,7 @@ update_compute_layer_descriptor_set(struct vk_bundle *vk, - .range = ubo_size, - }; - -- VkWriteDescriptorSet write_descriptor_sets[3] = { -+ VkWriteDescriptorSet write_descriptor_sets[4] = { - { - .sType = VK_STRUCTURE_TYPE_WRITE_DESCRIPTOR_SET, - .dstSet = descriptor_set, -@@ -123,10 +130,10 @@ update_compute_layer_descriptor_set(struct vk_bundle *vk, - { - .sType = VK_STRUCTURE_TYPE_WRITE_DESCRIPTOR_SET, - .dstSet = descriptor_set, -- .dstBinding = target_binding, -+ .dstBinding = target_binding_y, - .descriptorCount = 1, - .descriptorType = VK_DESCRIPTOR_TYPE_STORAGE_IMAGE, -- .pImageInfo = &target_image_info, -+ .pImageInfo = &target_image_info_y, - }, - { - .sType = VK_STRUCTURE_TYPE_WRITE_DESCRIPTOR_SET, -@@ -136,6 +143,14 @@ update_compute_layer_descriptor_set(struct vk_bundle *vk, - .descriptorType = VK_DESCRIPTOR_TYPE_UNIFORM_BUFFER, - .pBufferInfo = &buffer_info, - }, -+ { -+ .sType = VK_STRUCTURE_TYPE_WRITE_DESCRIPTOR_SET, -+ .dstSet = descriptor_set, -+ .dstBinding = target_binding_cbcr, -+ .descriptorCount = 1, -+ .descriptorType = VK_DESCRIPTOR_TYPE_STORAGE_IMAGE, -+ .pImageInfo = &target_image_info_cbcr, -+ }, - }; - - vk->vkUpdateDescriptorSets( // -@@ -154,8 +169,10 @@ update_compute_shared_descriptor_set(struct vk_bundle *vk, - uint32_t distortion_binding, - VkSampler distortion_samplers[3 * XRT_MAX_VIEWS], - VkImageView distortion_image_views[3 * XRT_MAX_VIEWS], -- uint32_t target_binding, -- VkImageView target_image_view, -+ uint32_t target_binding_y, -+ uint32_t target_binding_cbcr, -+ VkImageView target_image_view_y, -+ VkImageView target_image_view_cbcr, - uint32_t ubo_binding, - VkBuffer ubo_buffer, - VkDeviceSize ubo_size, -@@ -176,8 +193,13 @@ update_compute_shared_descriptor_set(struct vk_bundle *vk, - distortion_image_info[i].imageLayout = VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL; - } - -- VkDescriptorImageInfo target_image_info = { -- .imageView = target_image_view, -+ VkDescriptorImageInfo target_image_y_info = { -+ .imageView = target_image_view_y, -+ .imageLayout = VK_IMAGE_LAYOUT_GENERAL, -+ }; -+ -+ VkDescriptorImageInfo target_image_cbcr_info = { -+ .imageView = target_image_view_cbcr, - .imageLayout = VK_IMAGE_LAYOUT_GENERAL, - }; - -@@ -187,7 +209,7 @@ update_compute_shared_descriptor_set(struct vk_bundle *vk, - .range = ubo_size, - }; - -- VkWriteDescriptorSet write_descriptor_sets[4] = { -+ VkWriteDescriptorSet write_descriptor_sets[5] = { - { - .sType = VK_STRUCTURE_TYPE_WRITE_DESCRIPTOR_SET, - .dstSet = descriptor_set, -@@ -207,10 +229,10 @@ update_compute_shared_descriptor_set(struct vk_bundle *vk, - { - .sType = VK_STRUCTURE_TYPE_WRITE_DESCRIPTOR_SET, - .dstSet = descriptor_set, -- .dstBinding = target_binding, -+ .dstBinding = target_binding_y, - .descriptorCount = 1, - .descriptorType = VK_DESCRIPTOR_TYPE_STORAGE_IMAGE, -- .pImageInfo = &target_image_info, -+ .pImageInfo = &target_image_y_info, - }, - { - .sType = VK_STRUCTURE_TYPE_WRITE_DESCRIPTOR_SET, -@@ -220,6 +242,14 @@ update_compute_shared_descriptor_set(struct vk_bundle *vk, - .descriptorType = VK_DESCRIPTOR_TYPE_UNIFORM_BUFFER, - .pBufferInfo = &buffer_info, - }, -+ { -+ .sType = VK_STRUCTURE_TYPE_WRITE_DESCRIPTOR_SET, -+ .dstSet = descriptor_set, -+ .dstBinding = target_binding_cbcr, -+ .descriptorCount = 1, -+ .descriptorType = VK_DESCRIPTOR_TYPE_STORAGE_IMAGE, -+ .pImageInfo = &target_image_cbcr_info, -+ }, - }; - - vk->vkUpdateDescriptorSets( // -@@ -409,7 +439,8 @@ render_compute_layers(struct render_compute *render, - VkSampler src_samplers[RENDER_MAX_IMAGES_SIZE], - VkImageView src_image_views[RENDER_MAX_IMAGES_SIZE], - uint32_t num_srcs, -- VkImageView target_image_view, -+ VkImageView target_image_view_y, -+ VkImageView target_image_view_cbcr, - const struct render_viewport_data *view, - bool do_timewarp) - { -@@ -429,8 +460,10 @@ render_compute_layers(struct render_compute *render, - src_samplers, // - src_image_views, // - num_srcs, // -- r->compute.target_binding, // -- target_image_view, // -+ r->compute.target_binding_y, // -+ r->compute.target_binding_cbcr, // -+ target_image_view_y, // -+ target_image_view_cbcr, // - r->compute.ubo_binding, // - ubo, // - VK_WHOLE_SIZE, // -@@ -473,7 +506,8 @@ render_compute_projection_timewarp(struct render_compute *render, - const struct xrt_fov src_fovs[XRT_MAX_VIEWS], - const struct xrt_pose new_poses[XRT_MAX_VIEWS], - VkImage target_image, -- VkImageView target_image_view, -+ VkImageView target_image_view_y, -+ VkImageView target_image_view_cbcr, - const struct render_viewport_data views[XRT_MAX_VIEWS]) - { - assert(render->r != NULL); -@@ -542,8 +576,10 @@ render_compute_projection_timewarp(struct render_compute *render, - r->compute.distortion_binding, // - distortion_samplers, // - r->distortion.image_views, // -- r->compute.target_binding, // -- target_image_view, // -+ r->compute.target_binding_y, // -+ r->compute.target_binding_cbcr, // -+ target_image_view_y, // -+ target_image_view_cbcr, // - r->compute.ubo_binding, // - r->compute.distortion.ubo.buffer, // - VK_WHOLE_SIZE, // -@@ -581,7 +617,7 @@ render_compute_projection_timewarp(struct render_compute *render, - .srcAccessMask = VK_ACCESS_SHADER_WRITE_BIT, - .dstAccessMask = VK_ACCESS_MEMORY_READ_BIT, - .oldLayout = VK_IMAGE_LAYOUT_GENERAL, -- .newLayout = VK_IMAGE_LAYOUT_PRESENT_SRC_KHR, -+ .newLayout = VK_IMAGE_LAYOUT_TRANSFER_SRC_OPTIMAL, - .srcQueueFamilyIndex = VK_QUEUE_FAMILY_IGNORED, - .dstQueueFamilyIndex = VK_QUEUE_FAMILY_IGNORED, - .image = target_image, -@@ -607,7 +643,8 @@ render_compute_projection(struct render_compute *render, - VkImageView src_image_views[XRT_MAX_VIEWS], - const struct xrt_normalized_rect src_norm_rects[XRT_MAX_VIEWS], - VkImage target_image, -- VkImageView target_image_view, -+ VkImageView target_image_view_y, -+ VkImageView target_image_view_cbcr, - const struct render_viewport_data views[XRT_MAX_VIEWS]) - { - assert(render->r != NULL); -@@ -666,8 +703,10 @@ render_compute_projection(struct render_compute *render, - r->compute.distortion_binding, // - distortion_samplers, // - r->distortion.image_views, // -- r->compute.target_binding, // -- target_image_view, // -+ r->compute.target_binding_y, // -+ r->compute.target_binding_cbcr, // -+ target_image_view_y, // -+ target_image_view_cbcr, // - r->compute.ubo_binding, // - r->compute.distortion.ubo.buffer, // - VK_WHOLE_SIZE, // -@@ -705,7 +744,7 @@ render_compute_projection(struct render_compute *render, - .srcAccessMask = VK_ACCESS_SHADER_WRITE_BIT, - .dstAccessMask = VK_ACCESS_MEMORY_READ_BIT, - .oldLayout = VK_IMAGE_LAYOUT_GENERAL, -- .newLayout = VK_IMAGE_LAYOUT_PRESENT_SRC_KHR, -+ .newLayout = VK_IMAGE_LAYOUT_TRANSFER_SRC_OPTIMAL, - .srcQueueFamilyIndex = VK_QUEUE_FAMILY_IGNORED, - .dstQueueFamilyIndex = VK_QUEUE_FAMILY_IGNORED, - .image = target_image, -@@ -728,7 +767,8 @@ render_compute_projection(struct render_compute *render, - void - render_compute_clear(struct render_compute *render, - VkImage target_image, -- VkImageView target_image_view, -+ VkImageView target_image_view_y, -+ VkImageView target_image_view_cbcr, - const struct render_viewport_data views[XRT_MAX_VIEWS]) - { - assert(render->r != NULL); -@@ -795,8 +835,10 @@ render_compute_clear(struct render_compute *render, - r->compute.distortion_binding, // - distortion_samplers, // - r->distortion.image_views, // -- r->compute.target_binding, // -- target_image_view, // -+ r->compute.target_binding_y, // -+ r->compute.target_binding_cbcr, // -+ target_image_view_y, // -+ target_image_view_cbcr, // - r->compute.ubo_binding, // - r->compute.clear.ubo.buffer, // - VK_WHOLE_SIZE, // ubo_size -@@ -834,7 +876,7 @@ render_compute_clear(struct render_compute *render, - .srcAccessMask = VK_ACCESS_SHADER_WRITE_BIT, - .dstAccessMask = VK_ACCESS_MEMORY_READ_BIT, - .oldLayout = VK_IMAGE_LAYOUT_GENERAL, -- .newLayout = VK_IMAGE_LAYOUT_PRESENT_SRC_KHR, -+ .newLayout = VK_IMAGE_LAYOUT_TRANSFER_SRC_OPTIMAL, - .srcQueueFamilyIndex = VK_QUEUE_FAMILY_IGNORED, - .dstQueueFamilyIndex = VK_QUEUE_FAMILY_IGNORED, - .image = target_image, -diff --git a/src/xrt/compositor/render/render_interface.h b/src/xrt/compositor/render/render_interface.h -index aab965e0c..b0e568401 100644 ---- a/src/xrt/compositor/render/render_interface.h -+++ b/src/xrt/compositor/render/render_interface.h -@@ -488,7 +488,8 @@ struct render_resources - uint32_t distortion_binding; - - //! Writing the image out too. -- uint32_t target_binding; -+ uint32_t target_binding_y; -+ uint32_t target_binding_cbcr; - - //! Uniform data binding. - uint32_t ubo_binding; -@@ -1374,7 +1375,8 @@ render_compute_layers(struct render_compute *render, - VkSampler src_samplers[RENDER_MAX_IMAGES_SIZE], - VkImageView src_image_views[RENDER_MAX_IMAGES_SIZE], - uint32_t num_srcs, -- VkImageView target_image_view, -+ VkImageView target_image_view_y, -+ VkImageView target_image_view_cbcr, - const struct render_viewport_data *view, - bool timewarp); - -@@ -1390,7 +1392,8 @@ render_compute_projection_timewarp(struct render_compute *render, - const struct xrt_fov src_fovs[XRT_MAX_VIEWS], - const struct xrt_pose new_poses[XRT_MAX_VIEWS], - VkImage target_image, -- VkImageView target_image_view, -+ VkImageView target_image_view_y, -+ VkImageView target_image_view_cbcr, - const struct render_viewport_data views[XRT_MAX_VIEWS]); - - /*! -@@ -1402,7 +1405,8 @@ render_compute_projection(struct render_compute *render, - VkImageView src_image_views[XRT_MAX_VIEWS], - const struct xrt_normalized_rect src_rects[XRT_MAX_VIEWS], - VkImage target_image, -- VkImageView target_image_view, -+ VkImageView target_image_view_y, -+ VkImageView target_image_view_cbcr, - const struct render_viewport_data views[XRT_MAX_VIEWS]); - - /*! -@@ -1411,7 +1415,8 @@ render_compute_projection(struct render_compute *render, - void - render_compute_clear(struct render_compute *render, - VkImage target_image, -- VkImageView target_image_view, -+ VkImageView target_image_view_y, -+ VkImageView target_image_view_cbcr, - const struct render_viewport_data views[XRT_MAX_VIEWS]); - - -diff --git a/src/xrt/compositor/render/render_resources.c b/src/xrt/compositor/render/render_resources.c -index b9b8d4a22..cd9d82072 100644 ---- a/src/xrt/compositor/render/render_resources.c -+++ b/src/xrt/compositor/render/render_resources.c -@@ -184,14 +184,15 @@ init_mesh_ubo_buffers(struct vk_bundle *vk, struct render_buffer ubo[XRT_MAX_VIE - XRT_CHECK_RESULT static VkResult - create_compute_layer_descriptor_set_layout(struct vk_bundle *vk, - uint32_t src_binding, -- uint32_t target_binding, -+ uint32_t target_binding_y, -+ uint32_t target_binding_cbcr, - uint32_t ubo_binding, - uint32_t source_images_count, - VkDescriptorSetLayout *out_descriptor_set_layout) - { - VkResult ret; - -- VkDescriptorSetLayoutBinding set_layout_bindings[3] = { -+ VkDescriptorSetLayoutBinding set_layout_bindings[4] = { - { - .binding = src_binding, - .descriptorType = VK_DESCRIPTOR_TYPE_COMBINED_IMAGE_SAMPLER, -@@ -199,7 +200,7 @@ create_compute_layer_descriptor_set_layout(struct vk_bundle *vk, - .stageFlags = VK_SHADER_STAGE_COMPUTE_BIT, - }, - { -- .binding = target_binding, -+ .binding = target_binding_y, - .descriptorType = VK_DESCRIPTOR_TYPE_STORAGE_IMAGE, - .descriptorCount = 1, - .stageFlags = VK_SHADER_STAGE_COMPUTE_BIT, -@@ -210,6 +211,12 @@ create_compute_layer_descriptor_set_layout(struct vk_bundle *vk, - .descriptorCount = 1, - .stageFlags = VK_SHADER_STAGE_COMPUTE_BIT, - }, -+ { -+ .binding = target_binding_cbcr, -+ .descriptorType = VK_DESCRIPTOR_TYPE_STORAGE_IMAGE, -+ .descriptorCount = 1, -+ .stageFlags = VK_SHADER_STAGE_COMPUTE_BIT, -+ }, - }; - - VkDescriptorSetLayoutCreateInfo set_layout_info = { -@@ -235,13 +242,14 @@ XRT_CHECK_RESULT static VkResult - create_compute_distortion_descriptor_set_layout(struct vk_bundle *vk, - uint32_t src_binding, - uint32_t distortion_binding, -- uint32_t target_binding, -+ uint32_t target_binding_y, -+ uint32_t target_binding_cbcr, - uint32_t ubo_binding, - VkDescriptorSetLayout *out_descriptor_set_layout) - { - VkResult ret; - -- VkDescriptorSetLayoutBinding set_layout_bindings[4] = { -+ VkDescriptorSetLayoutBinding set_layout_bindings[5] = { - { - .binding = src_binding, - .descriptorType = VK_DESCRIPTOR_TYPE_COMBINED_IMAGE_SAMPLER, -@@ -255,7 +263,7 @@ create_compute_distortion_descriptor_set_layout(struct vk_bundle *vk, - .stageFlags = VK_SHADER_STAGE_COMPUTE_BIT, - }, - { -- .binding = target_binding, -+ .binding = target_binding_y, - .descriptorType = VK_DESCRIPTOR_TYPE_STORAGE_IMAGE, - .descriptorCount = 1, - .stageFlags = VK_SHADER_STAGE_COMPUTE_BIT, -@@ -266,6 +274,12 @@ create_compute_distortion_descriptor_set_layout(struct vk_bundle *vk, - .descriptorCount = 1, - .stageFlags = VK_SHADER_STAGE_COMPUTE_BIT, - }, -+ { -+ .binding = target_binding_cbcr, -+ .descriptorType = VK_DESCRIPTOR_TYPE_STORAGE_IMAGE, -+ .descriptorCount = 1, -+ .stageFlags = VK_SHADER_STAGE_COMPUTE_BIT, -+ }, - }; - - VkDescriptorSetLayoutCreateInfo set_layout_info = { -@@ -601,8 +615,9 @@ render_resources_init(struct render_resources *r, - } - r->compute.src_binding = 0; - r->compute.distortion_binding = 1; -- r->compute.target_binding = 2; -+ r->compute.target_binding_y = 2; - r->compute.ubo_binding = 3; -+ r->compute.target_binding_cbcr = 4; - - r->compute.layer.image_array_size = - MIN(vk->features.max_per_stage_descriptor_sampled_images, RENDER_MAX_IMAGES_COUNT(r)); -@@ -919,7 +934,8 @@ render_resources_init(struct render_resources *r, - ret = create_compute_layer_descriptor_set_layout( // - vk, // vk_bundle - r->compute.src_binding, // src_binding, -- r->compute.target_binding, // target_binding, -+ r->compute.target_binding_y, // target_binding_y, -+ r->compute.target_binding_cbcr, // target_binding_cbcr, - r->compute.ubo_binding, // ubo_binding, - r->compute.layer.image_array_size, // source_images_count, - &r->compute.layer.descriptor_set_layout); // out_descriptor_set_layout -@@ -1000,7 +1016,8 @@ render_resources_init(struct render_resources *r, - vk, // vk_bundle - r->compute.src_binding, // src_binding, - r->compute.distortion_binding, // distortion_binding, -- r->compute.target_binding, // target_binding, -+ r->compute.target_binding_y, // target_binding_y, -+ r->compute.target_binding_cbcr, // target_binding_cbcr, - r->compute.ubo_binding, // ubo_binding, - &r->compute.distortion.descriptor_set_layout); // out_descriptor_set_layout - VK_CHK_WITH_RET(ret, "create_compute_distortion_descriptor_set_layout", false); -diff --git a/src/xrt/compositor/shaders/distortion.comp b/src/xrt/compositor/shaders/distortion.comp -index 9a09c03db..461aeb4ec 100644 ---- a/src/xrt/compositor/shaders/distortion.comp -+++ b/src/xrt/compositor/shaders/distortion.comp -@@ -18,7 +18,7 @@ layout(local_size_x = 8, local_size_y = 8, local_size_z = 1) in; - - layout(set = 0, binding = 0) uniform sampler2D source[2]; - layout(set = 0, binding = 1) uniform sampler2D distortion[6]; --layout(set = 0, binding = 2) uniform writeonly restrict image2D target; -+layout(set = 0, binding = 2) uniform writeonly restrict image2D luma; - layout(set = 0, binding = 3, std140) uniform restrict Config - { - ivec4 views[2]; -@@ -26,7 +26,20 @@ layout(set = 0, binding = 3, std140) uniform restrict Config - vec4 post_transform[2]; - mat4 transform[2]; - } ubo; -+layout(set = 0, binding = 4) uniform writeonly restrict image2D chroma; - -+const mat3 color_space = mat3( -+// clang-format off -+// R G B -+/* Y */ 0.2126, 0.7152, 0.0722, -+/* Cb*/-0.1146, -0.3854, 0.5, -+/* Cr*/ 0.5 , -0.4542, -0.0458); -+//clang-format: on -+ -+vec3 rgb_to_ycbcr(vec3 color) -+{ -+ return transpose(color_space) * color + vec3(0, 0.5, 0.5); -+} - - vec2 position_to_uv(ivec2 extent, uint ix, uint iy) - { -@@ -124,30 +137,40 @@ mat3x2 source_coord(ivec3 xyv, ivec2 extent) - - void main() - { -- uint ix = gl_GlobalInvocationID.x; -- uint iy = gl_GlobalInvocationID.y; - uint iz = gl_GlobalInvocationID.z; - - ivec2 offset = ivec2(ubo.views[iz].xy); - ivec2 extent = ivec2(ubo.views[iz].zw); -+ vec2 cbcrs[4]; -+ for (int k = 0; k < 2; k += 1) { -+ uint iy = gl_GlobalInvocationID.y * 2 + k; -+ for (int j = 0; j < 2; j += 1) { -+ uint ix = gl_GlobalInvocationID.x * 2 + j; - -- if (ix >= extent.x || iy >= extent.y) { -- return; -- } -+ vec2 dist_uv = position_to_uv(extent, ix, iy); - -- vec2 dist_uv = position_to_uv(extent, ix, iy); -+ vec2 uv = texture(distortion[iz + 0], dist_uv).xy; -+ vec2 lod = texture(distortion[iz + 2], dist_uv).xy; - -- vec2 uv = texture(distortion[iz + 0], dist_uv).xy; -- vec2 lod = texture(distortion[iz + 2], dist_uv).xy; -+ // Do any transformation needed. -+ uv = transform_uv(uv, iz); - -- // Do any transformation needed. -- uv = transform_uv(uv, iz); -+ // Sample the source -+ vec3 colour = textureLod(source[iz], uv, mix(lod.x, lod.y, 0.5)).rgb; - -- // Sample the source -- vec4 colour = textureLod(source[iz], uv, mix(lod.x, lod.y, 0.5)); -+ colour = rgb_to_ycbcr(from_linear_to_srgb(colour)); - -- // Do colour correction here since there are no automatic conversion in hardware available. -- colour = vec4(from_linear_to_srgb(colour.rgb), 1); -+ int i = k * 2 + j; -+ cbcrs[i] = colour.yz; - -- imageStore(target, ivec2(offset.x + ix, offset.y + iy), colour); -+ if (ix < extent.x && iy < extent.y) { -+ imageStore(luma, ivec2(offset.x + ix, offset.y + iy), vec4(colour.x)); -+ } -+ } -+ } -+ if (gl_GlobalInvocationID.x >= extent.x || gl_GlobalInvocationID.y >= extent.y) { -+ return; -+ } -+ vec2 cbcr = mix(mix(cbcrs[0], cbcrs[1], 0.5), mix(cbcrs[2], cbcrs[3], 0.5), 0.5); -+ imageStore(chroma, ivec2(offset.xy/2 + gl_GlobalInvocationID.xy), vec4(cbcr.x, cbcr.y, 0, 0)); - } -diff --git a/src/xrt/compositor/util/comp_render.h b/src/xrt/compositor/util/comp_render.h -index e6fd072be..d0733b650 100644 ---- a/src/xrt/compositor/util/comp_render.h -+++ b/src/xrt/compositor/util/comp_render.h -@@ -166,7 +166,8 @@ struct comp_render_dispatch_data - VkImage target_image; - - //! Target image view for distortion. -- VkImageView target_unorm_view; -+ VkImageView target_unorm_view_y; -+ VkImageView target_unorm_view_cbcr; - } cs; - }; - -@@ -432,7 +433,8 @@ comp_render_gfx_dispatch(struct render_gfx *render, - static inline void - comp_render_cs_initial_init(struct comp_render_dispatch_data *data, - VkImage target_image, -- VkImageView target_unorm_view, -+ VkImageView target_unorm_view_y, -+ VkImageView target_unorm_view_cbcr, - bool fast_path, - bool do_timewarp) - { -@@ -442,7 +444,8 @@ comp_render_cs_initial_init(struct comp_render_dispatch_data *data, - data->do_timewarp = do_timewarp; - - data->cs.target_image = target_image; -- data->cs.target_unorm_view = target_unorm_view; -+ data->cs.target_unorm_view_y = target_unorm_view_y; -+ data->cs.target_unorm_view_cbcr = target_unorm_view_cbcr; - } - - /*! -@@ -534,7 +537,8 @@ comp_render_cs_layer(struct render_compute *render, - const struct xrt_pose *world_pose, - const struct xrt_pose *eye_pose, - const VkImage target_image, -- const VkImageView target_image_view, -+ const VkImageView target_image_view_y, -+ const VkImageView target_image_view_cbcr, - const struct render_viewport_data *target_view, - bool do_timewarp); - -diff --git a/src/xrt/compositor/util/comp_render_cs.c b/src/xrt/compositor/util/comp_render_cs.c -index fc11a0521..3db6c8d2e 100644 ---- a/src/xrt/compositor/util/comp_render_cs.c -+++ b/src/xrt/compositor/util/comp_render_cs.c -@@ -430,11 +430,12 @@ crc_clear_output(struct render_compute *render, const struct comp_render_dispatc - } - - -- render_compute_clear( // -- render, // -- d->cs.target_image, // -- d->cs.target_unorm_view, // target_image_view -- target_viewport_datas); // views -+ render_compute_clear( // -+ render, // -+ d->cs.target_image, // -+ d->cs.target_unorm_view_y, // target_image_view_y -+ d->cs.target_unorm_view_cbcr, // target_image_view_cbcr -+ target_viewport_datas); // views - } - - /* -@@ -480,14 +481,15 @@ crc_distortion_after_squash(struct render_compute *render, const struct comp_ren - target_viewport_datas[i] = viewport_data; - } - -- render_compute_projection( // -- render, // -- src_samplers, // -- src_image_views, // -- src_norm_rects, // -- d->cs.target_image, // -- d->cs.target_unorm_view, // target_image_view -- target_viewport_datas); // views -+ render_compute_projection( // -+ render, // -+ src_samplers, // -+ src_image_views, // -+ src_norm_rects, // -+ d->cs.target_image, // -+ d->cs.target_unorm_view_y, // target_image_view_y -+ d->cs.target_unorm_view_cbcr, // target_image_view_cbcr -+ target_viewport_datas); // views - } - - /// Fast path -@@ -559,7 +561,8 @@ crc_distortion_fast_path(struct render_compute *render, - src_image_views, // - src_norm_rects, // - d->cs.target_image, // -- d->cs.target_unorm_view, // -+ d->cs.target_unorm_view_y, // -+ d->cs.target_unorm_view_cbcr, // - target_viewport_datas); // - } else { - render_compute_projection_timewarp( // -@@ -571,7 +574,8 @@ crc_distortion_fast_path(struct render_compute *render, - src_fovs, // - world_poses, // - d->cs.target_image, // -- d->cs.target_unorm_view, // -+ d->cs.target_unorm_view_y, // -+ d->cs.target_unorm_view_cbcr, // - target_viewport_datas); // - } - } -@@ -592,7 +596,8 @@ comp_render_cs_layer(struct render_compute *render, - const struct xrt_pose *world_pose, - const struct xrt_pose *eye_pose, - const VkImage target_image, -- const VkImageView target_image_view, -+ const VkImageView target_image_view_y, -+ const VkImageView target_image_view_cbcr, - const struct render_viewport_data *target_view, - bool do_timewarp) - { -@@ -749,7 +754,8 @@ comp_render_cs_layer(struct render_compute *render, - src_samplers, // - src_image_views, // - cur_image, // -- target_image_view, // -+ target_image_view_y, // -+ target_image_view_cbcr, // - target_view, // - do_timewarp); // - } -@@ -785,6 +791,7 @@ comp_render_cs_layers(struct render_compute *render, - &view->eye_pose, // - view->image, // - view->cs.unorm_view, // -+ view->cs.unorm_view, // - &view->layer_viewport_data, // - d->do_timewarp); // - } --- -2.48.0 - diff --git a/0007-don-t-verify-GL-stuff.patch b/0007-don-t-verify-GL-stuff.patch new file mode 100644 index 0000000..d3b28c4 --- /dev/null +++ b/0007-don-t-verify-GL-stuff.patch @@ -0,0 +1,33 @@ +From 0832cc9a2ccd0f67b3bd3a11ec1b1d1398dfebda Mon Sep 17 00:00:00 2001 +From: no name +Date: Sun, 25 Jan 2026 10:09:10 +0100 +Subject: [PATCH 7/8] don't verify GL stuff + +--- + src/xrt/state_trackers/oxr/oxr_verify.c | 10 ---------- + 1 file changed, 10 deletions(-) + +diff --git a/src/xrt/state_trackers/oxr/oxr_verify.c b/src/xrt/state_trackers/oxr/oxr_verify.c +index 8f35222cf..1418a8231 100644 +--- a/src/xrt/state_trackers/oxr/oxr_verify.c ++++ b/src/xrt/state_trackers/oxr/oxr_verify.c +@@ -610,16 +610,6 @@ oxr_verify_XrGraphicsBindingOpenGLXlibKHR(struct oxr_logger *log, const XrGraphi + "XrGraphicsBindingOpenGLXlibKHR::glxDrawable is NULL"); + } + +- if (next->glxFBConfig == NULL) { +- return oxr_error(log, XR_ERROR_GRAPHICS_DEVICE_INVALID, +- "XrGraphicsBindingOpenGLXlibKHR::glxFBConfig is NULL"); +- } +- +- if (next->visualid == 0) { +- return oxr_error(log, XR_ERROR_GRAPHICS_DEVICE_INVALID, +- "XrGraphicsBindingOpenGLXlibKHR::visualid is 0"); +- } +- + return XR_SUCCESS; + } + +-- +2.54.0 + diff --git a/0008-configure-u_git_tag-in-WiVRn.patch b/0008-configure-u_git_tag-in-WiVRn.patch new file mode 100644 index 0000000..2ba5f58 --- /dev/null +++ b/0008-configure-u_git_tag-in-WiVRn.patch @@ -0,0 +1,37 @@ +From d79de5d82def9641484b8d88212d3c2e97e38734 Mon Sep 17 00:00:00 2001 +From: no name +Date: Fri, 20 Mar 2026 16:51:18 +0100 +Subject: [PATCH 8/8] configure u_git_tag in WiVRn + +--- + src/xrt/auxiliary/util/CMakeLists.txt | 7 +------ + 1 file changed, 1 insertion(+), 6 deletions(-) + +diff --git a/src/xrt/auxiliary/util/CMakeLists.txt b/src/xrt/auxiliary/util/CMakeLists.txt +index 79646b181..21adcd446 100644 +--- a/src/xrt/auxiliary/util/CMakeLists.txt ++++ b/src/xrt/auxiliary/util/CMakeLists.txt +@@ -10,11 +10,6 @@ + # by applications like the OpenXR runtime library. + # + +-configure_file( +- "${CMAKE_CURRENT_SOURCE_DIR}/u_git_tag.c.in" "${CMAKE_CURRENT_BINARY_DIR}/u_git_tag.c" +- @ONLY +- ) +- + add_library( + aux_util STATIC + u_autoexpgain.c +@@ -120,7 +115,7 @@ add_library( + u_worker.cpp + u_worker.h + u_worker.hpp +- "${CMAKE_CURRENT_BINARY_DIR}/u_git_tag.c" ++ "${U_GIT_TAG_CPP}" + ) + target_link_libraries( + aux_util +-- +2.54.0 + diff --git a/0009-Revert-a-bindings-improve-reproducibility-of-binding.patch b/0009-Revert-a-bindings-improve-reproducibility-of-binding.patch deleted file mode 100644 index 6d966c1..0000000 --- a/0009-Revert-a-bindings-improve-reproducibility-of-binding.patch +++ /dev/null @@ -1,186 +0,0 @@ -From 9e0dc33f7b1895e084e70b0b18d84510eb6e0219 Mon Sep 17 00:00:00 2001 -From: Patrick Nicolas -Date: Tue, 31 Dec 2024 11:01:12 +0100 -Subject: [PATCH 09/10] Revert "a/bindings: improve reproducibility of bindings - generation" - -This reverts commit d2fbd3ce8d962e4a46351df0f34f14de71871e00. ---- - src/xrt/auxiliary/bindings/bindings.py | 41 +++++++++++-------- - .../auxiliary/bindings/steamvr_profiles.py | 2 +- - 2 files changed, 24 insertions(+), 19 deletions(-) - -diff --git a/src/xrt/auxiliary/bindings/bindings.py b/src/xrt/auxiliary/bindings/bindings.py -index 3039629ef..28d4395ff 100755 ---- a/src/xrt/auxiliary/bindings/bindings.py -+++ b/src/xrt/auxiliary/bindings/bindings.py -@@ -7,7 +7,8 @@ bindings.""" - import argparse - import json - import copy --from operator import attrgetter -+import itertools -+ - - def find_component_in_list_by_name(name, component_list, subaction_path=None, identifier_json_path=None): - """Find a component with the given name in a list of components.""" -@@ -123,6 +124,7 @@ class Component: - Turn a Identifier's component paths into a list of Component objects. - """ - -+ monado_bindings = json_subpath["monado_bindings"] - component_list = [] - for component_name in json_subpath["components"]: # click, touch, ... - matched_dpad_emulation = None -@@ -130,7 +132,9 @@ class Component: - json_subpath["dpad_emulation"]["position"] == component_name): - matched_dpad_emulation = json_subpath["dpad_emulation"] - -- monado_binding = json_subpath["monado_bindings"].get(component_name, None) -+ monado_binding = None -+ if component_name in monado_bindings: -+ monado_binding = monado_bindings[component_name] - - steamvr_path = steamvr_subpath_name(identifier_json_path, json_subpath["type"]) - if "steamvr_path" in json_subpath: -@@ -220,9 +224,10 @@ class Identifier: - - identifier_list = [] - for subaction_path in json_subaction_paths: # /user/hand/* -- for json_path in sorted(json_subpaths.keys()): # /input/*, /output/* -+ for json_sub_path_itm in json_subpaths.items(): # /input/*, /output/* -+ json_path = json_sub_path_itm[0] # /input/trackpad - # json object associated with a subpath (type, localized_name, ...) -- json_subpath = json_subpaths[json_path] -+ json_subpath = json_sub_path_itm[1] - - # Oculus Touch a,b/x,y components only exist on one controller - if "side" in json_subpath and "/user/hand/" + json_subpath["side"] != subaction_path: -@@ -262,7 +267,7 @@ class Identifier: - self.components = component_list - self.dpad = dpad - return -- -+ - - class FeatureSet: - """An AND of requirements (versions and/or extensions) under which a binding becomes available""" -@@ -317,7 +322,7 @@ class Availability: - if not optimize: - self.feature_sets = set(feature_sets) - return -- -+ - self.feature_sets = set() - for feature_set in feature_sets: - self.add_in_place(feature_set) -@@ -460,8 +465,8 @@ class Profile: - def __update_component_list(self): - self.components = [] - for identifier in self.identifiers: -- self.components += identifier.components -- self.components = sorted(self.components, key=attrgetter("steamvr_path")) -+ for component in identifier.components: -+ self.components.append(component) - - def availability(self): - result = Availability(feature_sets=[]) -@@ -499,8 +504,8 @@ class Bindings: - - def __init__(self, json_root): - """Construct a bindings from a dictionary of profiles.""" -- self.profiles = [Profile(profile_name, json_root["profiles"][profile_name]) for -- profile_name in sorted(json_root["profiles"].keys())] -+ self.profiles = [Profile(profile_name, json_profile) for -+ profile_name, json_profile in json_root["profiles"].items()] - self.__set_parent_profile_refs() - self.__mine_for_diamond_errors() - -@@ -526,7 +531,7 @@ class Bindings: - if profile.name in parent_path_set: - return True - parent_path_set.append(profile.name) -- for parent in sorted(profile.parent_profiles, key=attrgetter("name")): -+ for parent in profile.parent_profiles: - if self.__has_diamonds(parent, parent_path_set): - return True - return False -@@ -572,7 +577,7 @@ def write_verify_switch_body(f, dict_of_lists, profile, profile_name, tab_char): - Input is a file to write the code into, a dict where keys are length and - the values are lists of strings of that length. And a suffix if any.""" - f.write(f"{tab_char}\tswitch (length) {{\n") -- for length in sorted(dict_of_lists.keys()): -+ for length in dict_of_lists.keys(): - f.write(f"{tab_char}\tcase {str(length)}:\n\t\t{tab_char}") - for path in sorted(dict_of_lists[length]): - f.write(if_strcmp.format(exttab=tab_char, check=path)) -@@ -613,7 +618,7 @@ def write_verify_func_switch(f, dict_of_lists, profile, profile_name, availabili - closing.append(f'{tab_char}}}\n#endif // {ext_defines}\n') - - write_verify_switch_body(f, dict_of_lists, profile, profile_name, tab_char) -- -+ - for closer in reversed(closing): - f.write(closer) - -@@ -624,7 +629,7 @@ def write_verify_func_body(f, profile, dict_name, availability): - profile, dict_name), profile, profile.name, availability) - if profile.parent_profiles is None: - return -- for pp in sorted(profile.parent_profiles, key=attrgetter("name")): -+ for pp in profile.parent_profiles: - write_verify_func_body(f, pp, dict_name, availability.intersection(pp.availability())) - - -@@ -832,7 +837,7 @@ def generate_bindings_c(file, b): - f.write('{\n') - f.write('\tswitch(input)\n') - f.write('\t{\n') -- for input in sorted(inputs): -+ for input in inputs: - f.write(f'\tcase {input}: return "{input}";\n') - f.write(f'\tdefault: return "UNKNOWN";\n') - f.write('\t}\n') -@@ -841,7 +846,7 @@ def generate_bindings_c(file, b): - f.write('enum xrt_input_name\n') - f.write('xrt_input_name_enum(const char *input)\n') - f.write('{\n') -- for input in sorted(inputs): -+ for input in inputs: - f.write(f'\tif(strcmp("{input}", input) == 0) return {input};\n') - f.write(f'\treturn XRT_INPUT_GENERIC_TRACKER_POSE;\n') - f.write('}\n') -@@ -851,7 +856,7 @@ def generate_bindings_c(file, b): - f.write('{\n') - f.write('\tswitch(output)\n') - f.write('\t{\n') -- for output in sorted(outputs): -+ for output in outputs: - f.write(f'\tcase {output}: return "{output}";\n') - f.write(f'\tdefault: return "UNKNOWN";\n') - f.write('\t}\n') -@@ -860,7 +865,7 @@ def generate_bindings_c(file, b): - f.write('enum xrt_output_name\n') - f.write('xrt_output_name_enum(const char *output)\n') - f.write('{\n') -- for output in sorted(outputs): -+ for output in outputs: - f.write(f'\tif(strcmp("{output}", output) == 0) return {output};\n') - f.write(f'\treturn XRT_OUTPUT_NAME_SIMPLE_VIBRATION;\n') - f.write('}\n') -diff --git a/src/xrt/auxiliary/bindings/steamvr_profiles.py b/src/xrt/auxiliary/bindings/steamvr_profiles.py -index f54a303f9..abcbfabe6 100644 ---- a/src/xrt/auxiliary/bindings/steamvr_profiles.py -+++ b/src/xrt/auxiliary/bindings/steamvr_profiles.py -@@ -114,7 +114,7 @@ def main(): - - # print("Creating SteamVR input profile", f.name) - -- json.dump(j, f, indent=2, sort_keys=True) -+ json.dump(j, f, indent=2) - - f.close() - --- -2.48.0 - diff --git a/0010-store-alpha-channel-in-layer-1.patch b/0010-store-alpha-channel-in-layer-1.patch deleted file mode 100644 index eb04c29..0000000 --- a/0010-store-alpha-channel-in-layer-1.patch +++ /dev/null @@ -1,94 +0,0 @@ -From e17739a88a15ac26dfe9ccb9ffc97ff029fa7764 Mon Sep 17 00:00:00 2001 -From: Patrick Nicolas -Date: Sun, 3 Nov 2024 13:40:00 +0100 -Subject: [PATCH 10/10] store alpha channel in layer 1 - ---- - src/xrt/compositor/shaders/clear.comp | 4 ++-- - src/xrt/compositor/shaders/distortion.comp | 18 ++++++++++++------ - 2 files changed, 14 insertions(+), 8 deletions(-) - -diff --git a/src/xrt/compositor/shaders/clear.comp b/src/xrt/compositor/shaders/clear.comp -index 036da401a..db8e5178e 100644 ---- a/src/xrt/compositor/shaders/clear.comp -+++ b/src/xrt/compositor/shaders/clear.comp -@@ -10,7 +10,7 @@ - - layout(local_size_x = 8, local_size_y = 8, local_size_z = 1) in; - --layout(set = 0, binding = 2) uniform writeonly restrict image2D target; -+layout(set = 0, binding = 2) uniform writeonly restrict image2DArray target; - layout(set = 0, binding = 3) uniform restrict Config - { - ivec4 views[2]; -@@ -35,5 +35,5 @@ void main() - - vec4 colour = vec4(from_linear_to_srgb(vec3(0.1)), 1.0); - -- imageStore(target, ivec2(offset.x + ix, offset.y + iy), colour); -+ imageStore(target, ivec3(offset.x + ix, offset.y + iy, 0), colour); - } -diff --git a/src/xrt/compositor/shaders/distortion.comp b/src/xrt/compositor/shaders/distortion.comp -index 461aeb4ec..237221a54 100644 ---- a/src/xrt/compositor/shaders/distortion.comp -+++ b/src/xrt/compositor/shaders/distortion.comp -@@ -18,7 +18,7 @@ layout(local_size_x = 8, local_size_y = 8, local_size_z = 1) in; - - layout(set = 0, binding = 0) uniform sampler2D source[2]; - layout(set = 0, binding = 1) uniform sampler2D distortion[6]; --layout(set = 0, binding = 2) uniform writeonly restrict image2D luma; -+layout(set = 0, binding = 2) uniform writeonly restrict image2DArray luma; - layout(set = 0, binding = 3, std140) uniform restrict Config - { - ivec4 views[2]; -@@ -26,7 +26,7 @@ layout(set = 0, binding = 3, std140) uniform restrict Config - vec4 post_transform[2]; - mat4 transform[2]; - } ubo; --layout(set = 0, binding = 4) uniform writeonly restrict image2D chroma; -+layout(set = 0, binding = 4) uniform writeonly restrict image2DArray chroma; - - const mat3 color_space = mat3( - // clang-format off -@@ -142,6 +142,7 @@ void main() - ivec2 offset = ivec2(ubo.views[iz].xy); - ivec2 extent = ivec2(ubo.views[iz].zw); - vec2 cbcrs[4]; -+ vec4 alpha; - for (int k = 0; k < 2; k += 1) { - uint iy = gl_GlobalInvocationID.y * 2 + k; - for (int j = 0; j < 2; j += 1) { -@@ -156,15 +157,16 @@ void main() - uv = transform_uv(uv, iz); - - // Sample the source -- vec3 colour = textureLod(source[iz], uv, mix(lod.x, lod.y, 0.5)).rgb; -+ vec4 colour = textureLod(source[iz], uv, mix(lod.x, lod.y, 0.5)); - -- colour = rgb_to_ycbcr(from_linear_to_srgb(colour)); -+ colour.rgb = rgb_to_ycbcr(from_linear_to_srgb(colour.rgb)); - - int i = k * 2 + j; - cbcrs[i] = colour.yz; -+ alpha[i] = colour.a; - - if (ix < extent.x && iy < extent.y) { -- imageStore(luma, ivec2(offset.x + ix, offset.y + iy), vec4(colour.x)); -+ imageStore(luma, ivec3(offset.x + ix, offset.y + iy, 0), vec4(colour.x)); - } - } - } -@@ -172,5 +174,9 @@ void main() - return; - } - vec2 cbcr = mix(mix(cbcrs[0], cbcrs[1], 0.5), mix(cbcrs[2], cbcrs[3], 0.5), 0.5); -- imageStore(chroma, ivec2(offset.xy/2 + gl_GlobalInvocationID.xy), vec4(cbcr.x, cbcr.y, 0, 0)); -+ imageStore(chroma, ivec3(offset.xy/2 + gl_GlobalInvocationID.xy, 0), vec4(cbcr.x, cbcr.y, 0, 0)); -+ -+ imageStore(chroma, ivec3(offset.xy/2 + gl_GlobalInvocationID.xy, 1), vec4(0.5, 0.5, 0, 0)); -+ alpha = vec4(mix(mix(alpha[0], alpha[1], 0.5), mix(alpha[2], alpha[3], 0.5), 0.5)); -+ imageStore(luma, ivec3(offset.xy/2 + gl_GlobalInvocationID.xy, 1), alpha); - } --- -2.48.0 - diff --git a/0011-st-oxr-bind-to-dynamic-role-s-profile.patch b/0011-st-oxr-bind-to-dynamic-role-s-profile.patch deleted file mode 100644 index 32aaad9..0000000 --- a/0011-st-oxr-bind-to-dynamic-role-s-profile.patch +++ /dev/null @@ -1,40 +0,0 @@ -From 6321ad3eee7d4a8b304a36de008d8fb8359f3dfe Mon Sep 17 00:00:00 2001 -From: Patrick Nicolas -Date: Wed, 19 Feb 2025 10:13:36 +0100 -Subject: [PATCH] st/oxr: bind to dynamic role's profile - ---- - src/xrt/state_trackers/oxr/oxr_binding.c | 17 +++++++++++++++++ - 1 file changed, 17 insertions(+) - -diff --git a/src/xrt/state_trackers/oxr/oxr_binding.c b/src/xrt/state_trackers/oxr/oxr_binding.c -index eb8bb5489..b04977a8b 100644 ---- a/src/xrt/state_trackers/oxr/oxr_binding.c -+++ b/src/xrt/state_trackers/oxr/oxr_binding.c -@@ -380,6 +380,23 @@ oxr_find_profile_for_device(struct oxr_logger *log, - return; - } - -+ // Have bindings for the dynamic role's profile been suggested? -+#define FIND_PROFILE(X) \ -+ { \ -+ struct xrt_device *role_xdev = GET_XDEV_BY_ROLE(sess->sys, X); \ -+ if (role_xdev == xdev) { \ -+ enum xrt_device_name profile = GET_PROFILE_NAME_BY_ROLE(sess->sys, X); \ -+ oxr_get_profile_for_device_name(log, sess, profile, out_p); \ -+ if (*out_p != NULL) { \ -+ return; \ -+ } \ -+ } \ -+ } -+ FIND_PROFILE(left); -+ FIND_PROFILE(right); -+ FIND_PROFILE(gamepad); -+#undef FIND_PROFILE -+ - // Have bindings for this device's interaction profile been suggested? - oxr_get_profile_for_device_name(log, sess, xdev->name, out_p); - if (*out_p != NULL) { --- -2.48.1 - diff --git a/0012-ipc-Initialize-hmd-data-from-static-role.patch b/0012-ipc-Initialize-hmd-data-from-static-role.patch deleted file mode 100644 index 57ffffd..0000000 --- a/0012-ipc-Initialize-hmd-data-from-static-role.patch +++ /dev/null @@ -1,65 +0,0 @@ -From a2f80c1c92619ad57516e24539047d4d30d8cca7 Mon Sep 17 00:00:00 2001 -From: Patrick Nicolas -Date: Sun, 23 Feb 2025 11:12:14 +0100 -Subject: [PATCH] ipc: Initialize hmd data from static role - -Make sure the xdev used to read the hmd part is the one from head static -role. This fixes issues in case more than one hmd is available. ---- - src/xrt/ipc/server/ipc_server_process.c | 33 ++++++++++++------------- - 1 file changed, 16 insertions(+), 17 deletions(-) - -diff --git a/src/xrt/ipc/server/ipc_server_process.c b/src/xrt/ipc/server/ipc_server_process.c -index 6b6da45a4..21796fb08 100644 ---- a/src/xrt/ipc/server/ipc_server_process.c -+++ b/src/xrt/ipc/server/ipc_server_process.c -@@ -357,23 +357,6 @@ init_shm(struct ipc_server *s) - isdev->stage_supported = xdev->stage_supported; - isdev->battery_status_supported = xdev->battery_status_supported; - -- // Is this a HMD? -- if (xdev->hmd != NULL) { -- // set view count -- ism->hmd.view_count = xdev->hmd->view_count; -- for (uint32_t view = 0; view < xdev->hmd->view_count; ++view) { -- ism->hmd.views[view].display.w_pixels = xdev->hmd->views[view].display.w_pixels; -- ism->hmd.views[view].display.h_pixels = xdev->hmd->views[view].display.h_pixels; -- } -- -- for (size_t i = 0; i < xdev->hmd->blend_mode_count; i++) { -- // Not super necessary, we also do this assert in oxr_system.c -- assert(u_verify_blend_mode_valid(xdev->hmd->blend_modes[i])); -- ism->hmd.blend_modes[i] = xdev->hmd->blend_modes[i]; -- } -- ism->hmd.blend_mode_count = xdev->hmd->blend_mode_count; -- } -- - // Setup the tracking origin. - isdev->tracking_origin_index = (uint32_t)-1; - for (uint32_t k = 0; k < XRT_SYSTEM_MAX_DEVICES; k++) { -@@ -428,6 +411,22 @@ init_shm(struct ipc_server *s) - } - } - -+ // Setup the HMD -+ // set view count -+ assert(s->xsysd->static_roles.head->hmd); -+ ism->hmd.view_count = s->xsysd->static_roles.head->hmd->view_count; -+ for (uint32_t view = 0; view < s->xsysd->static_roles.head->hmd->view_count; ++view) { -+ ism->hmd.views[view].display.w_pixels = s->xsysd->static_roles.head->hmd->views[view].display.w_pixels; -+ ism->hmd.views[view].display.h_pixels = s->xsysd->static_roles.head->hmd->views[view].display.h_pixels; -+ } -+ -+ for (size_t i = 0; i < s->xsysd->static_roles.head->hmd->blend_mode_count; i++) { -+ // Not super necessary, we also do this assert in oxr_system.c -+ assert(u_verify_blend_mode_valid(s->xsysd->static_roles.head->hmd->blend_modes[i])); -+ ism->hmd.blend_modes[i] = s->xsysd->static_roles.head->hmd->blend_modes[i]; -+ } -+ ism->hmd.blend_mode_count = s->xsysd->static_roles.head->hmd->blend_mode_count; -+ - // Finally tell the client how many devices we have. - s->ism->isdev_count = count; - --- -2.48.1 - diff --git a/0013-c-main-allow-custom-pacing-app-factory.patch b/0013-c-main-allow-custom-pacing-app-factory.patch deleted file mode 100644 index a30f8ed..0000000 --- a/0013-c-main-allow-custom-pacing-app-factory.patch +++ /dev/null @@ -1,82 +0,0 @@ -From edd3447b25387908acbe005f1cbee5f17bae0260 Mon Sep 17 00:00:00 2001 -From: Patrick Nicolas -Date: Thu, 27 Feb 2025 22:45:36 +0100 -Subject: [PATCH] c/main: allow custom pacing app factory - ---- - src/xrt/compositor/main/comp_compositor.c | 10 ++++++---- - src/xrt/compositor/main/comp_main_interface.h | 4 ++++ - src/xrt/targets/common/target_instance.c | 2 +- - 3 files changed, 11 insertions(+), 5 deletions(-) - -diff --git a/src/xrt/compositor/main/comp_compositor.c b/src/xrt/compositor/main/comp_compositor.c -index 26c341385..36e3297f7 100644 ---- a/src/xrt/compositor/main/comp_compositor.c -+++ b/src/xrt/compositor/main/comp_compositor.c -@@ -983,6 +983,7 @@ compositor_init_renderer(struct comp_compositor *c) - xrt_result_t - comp_main_create_system_compositor(struct xrt_device *xdev, - const struct comp_target_factory *ctf, -+ struct u_pacing_app_factory *upaf, - struct xrt_system_compositor **out_xsysc) - { - COMP_TRACE_MARKER(); -@@ -1183,10 +1184,11 @@ comp_main_create_system_compositor(struct xrt_device *xdev, - } - - // Standard app pacer. -- struct u_pacing_app_factory *upaf = NULL; -- xrt_result_t xret = u_pa_factory_create(&upaf); -- assert(xret == XRT_SUCCESS && upaf != NULL); -- (void)xret; -+ if (upaf == NULL) { -+ xrt_result_t xret = u_pa_factory_create(&upaf); -+ assert(xret == XRT_SUCCESS && upaf != NULL); -+ (void)xret; -+ } - - return comp_multi_create_system_compositor(&c->base.base, upaf, sys_info, !c->deferred_surface, out_xsysc); - } -diff --git a/src/xrt/compositor/main/comp_main_interface.h b/src/xrt/compositor/main/comp_main_interface.h -index 0f0678358..f60568b43 100644 ---- a/src/xrt/compositor/main/comp_main_interface.h -+++ b/src/xrt/compositor/main/comp_main_interface.h -@@ -15,6 +15,7 @@ - #include "xrt/xrt_compositor.h" - - struct comp_target_factory; -+struct u_pacing_app_factory; - - #ifdef __cplusplus - extern "C" { -@@ -31,11 +32,14 @@ extern "C" { - * @param xdev The head device - * @param ctf A compositor target factory to force the output device, must remain valid for the lifetime of the - * compositor. If NULL, factory is automatically selected -+ * @param upaf A pacing app factory to customize the application pacer, compositor takes ownership. -+ * If NULL, a default factory is used - * @param out_xsysc The output compositor - */ - xrt_result_t - comp_main_create_system_compositor(struct xrt_device *xdev, - const struct comp_target_factory *ctf, -+ struct u_pacing_app_factory *upaf, - struct xrt_system_compositor **out_xsysc); - - -diff --git a/src/xrt/targets/common/target_instance.c b/src/xrt/targets/common/target_instance.c -index 617561c37..e10ba8358 100644 ---- a/src/xrt/targets/common/target_instance.c -+++ b/src/xrt/targets/common/target_instance.c -@@ -109,7 +109,7 @@ t_instance_create_system(struct xrt_instance *xinst, - - #ifdef XRT_MODULE_COMPOSITOR_MAIN - if (xret == XRT_SUCCESS && xsysc == NULL) { -- xret = comp_main_create_system_compositor(head, NULL, &xsysc); -+ xret = comp_main_create_system_compositor(head, NULL, NULL, &xsysc); - } - #else - if (!use_null) { --- -2.48.1 - diff --git a/0014-st-oxr-forward-0-refresh-rate.patch b/0014-st-oxr-forward-0-refresh-rate.patch deleted file mode 100644 index a2318f5..0000000 --- a/0014-st-oxr-forward-0-refresh-rate.patch +++ /dev/null @@ -1,33 +0,0 @@ -From b9bef34f0362677d2a58c0b2b331c98e2f74f9b2 Mon Sep 17 00:00:00 2001 -From: Patrick Nicolas -Date: Sat, 15 Mar 2025 22:19:48 +0100 -Subject: [PATCH] st/oxr: forward 0 refresh rate - ---- - src/xrt/state_trackers/oxr/oxr_api_session.c | 6 +----- - 1 file changed, 1 insertion(+), 5 deletions(-) - -diff --git a/src/xrt/state_trackers/oxr/oxr_api_session.c b/src/xrt/state_trackers/oxr/oxr_api_session.c -index c84806bde..955019957 100644 ---- a/src/xrt/state_trackers/oxr/oxr_api_session.c -+++ b/src/xrt/state_trackers/oxr/oxr_api_session.c -@@ -644,15 +644,11 @@ oxr_xrRequestDisplayRefreshRateFB(XrSession session, float displayRefreshRate) - OXR_VERIFY_SESSION_AND_INIT_LOG(&log, session, sess, "xrRequestDisplayRefreshRateFB"); - OXR_VERIFY_SESSION_NOT_LOST(&log, sess); - -- if (displayRefreshRate == 0.0f) { -- return XR_SUCCESS; -- } -- - /* - * For the requested display refresh rate, truncating to two decimal - * places and checking if it's in the supported refresh rates. - */ -- bool found = false; -+ bool found = displayRefreshRate == 0.0f; - for (int i = 0; i < (int)sess->sys->xsysc->info.refresh_rate_count; ++i) { - if ((int)(displayRefreshRate * 100.0f) == (int)(sess->sys->xsysc->info.refresh_rates_hz[i] * 100.0f)) { - found = true; --- -2.48.1 - diff --git a/LicenseBreakdown b/LicenseBreakdown new file mode 100644 index 0000000..cd275ad --- /dev/null +++ b/LicenseBreakdown @@ -0,0 +1,67 @@ +CECILL-C + _deps/monado-src/src/external/imgui/imgui/imconfig.h + +CC0-1.0 AND Apache-2.0 + _deps/monado-src/src/external/glad/* + +Unlicense + _deps/monado-src/src/external/imgui/imgui/backends/imgui_impl_opengl3_loader.h + +Apache-2.0 + _deps/monado-src/gradle* + _deps/monado-src/src/external/{flexkalman,glad,mermaid,nvpro_pyramid,openxr_includes}/* + _deps/monado-src/src/xrt/auxiliary/math/m_quatexpmap.cpp + gradle* + resources/values/ic_wivrn_launcher_background.xml + +Apache-2.0 OR MIT + _deps/monado-src/doc/doxygen-awesome-css/doxygen-awesome-* + +BSD-2-Clause + _deps/monado-src/cmake/FindEigen3.cmake + _deps/monado-src/src/external/hungarian/* + _deps/monado-src/src/external/tracy/{client,common}/* + _deps/monado-src/cmake/{FindEGL.cmake,FindSystemd.cmake,OptionWithDeps.cmake} + _deps/monado-src/src/external/openvr_includes/* + _deps/monado-src/src/external/tinyceres/* + _deps/monado-src/src/external/tracy/libbacktrace/* + _deps/monado-src/src/xrt/drivers/north_star/distortion_3d/* + _deps/monado-src/src/xrt/tracking/hand/mercury/kine_lm/lm_rotations_ceres.inl + +BSD-3-Clause AND BSL-1.0 + _deps/monado-src/src/xrt/targets/steamvr_drv/* + +BSL-1.0 + _deps/monado-src/{CMakeLists.txt,CompilerFlags.cmake,build.gradel} + _deps/monado-src/cmake/{CleanDirectoryList.cmake,FindHIDAPI.cmake,FindLeapV2.cmake,FindLibcheck.cmake,FindLibusb1.cmake,FindONNXRuntime.cmake,FindOpenGLES.cmake,FindOpenHMD.cmake,FindPercetto.cmake,Findbluetooth.cmake,FindcJSON.cmake,Findudev.cmake,GenerateKhrManifest.cmake,GenerateKhrManifestInternals.cmake.in,GenerateOpenXRRuntimeManifest.cmake,GenerateVulkanApiLayerManifest.cmake,GetGitRevisionDescription.cmake,GetGitRevisionDescription.cmake.in,PrefixListGlob.cmake,ProgramFilesGlob.cmake,SPIR-V.cmake,openxr_manifest.in.json.license} + _deps/monado-src/src/external/{Catch2,android-jni-wrap,flexkalman,imgui,nvpro_pyramid,openxr_includes,solarxr,util-headers,vit_includes} + _deps/monado-src/src/xrt/auxiliary/{android,bindings,d3d,gsteamer,math,ogl,os,tracking,util,vive,vk}/* + _deps/monado-src/src/xrt/compositor/* + _deps/monado-src/src/xrt/drivers/{android,arduino,daydream,depthai,euroc,hdk,ht,ht_ctrl_emu,hydra,illixr,multi_wrapper,north_star,ohmd,opengloves,psmv,qwerty,realsense,remote,rift_s,rokid,sample,simula,simulated,solarxr,steamvr_lh,survive,twrap,ultraleap_v2,v4l2,vf,vive,wmr,xreal_air}/* + _deps/monado-src/src/xrt/include/tracking/t_hand_tracking.h + _deps/monado-src/src/xrt/include/xrt/* + _deps/monado-src/src/xrt/{ipc,state_trackers,targets,tracking}/* + _deps/monado-src/tests/* + server/{main.cpp,start_application.cpp,target_instance_wivrn.cpp} + +BSL-1.0 AND CC0-1.0 + _deps/monado-src/{doc,scripts}/* + +GPL-2.0-or-later + tools/wireshark/dissector.cpp + +GPL-3.0-or-later + {client,common,dashboard,server}/* + +MIT-Khronos-old + _deps/monado-src/src/external/glad/include/KHR/khrplatform.h + +MIT + _deps/monado-src/cmake/sanitizers/{FindASan.cmake,FindMSan.cmake,FindSanitizers.cmake,FindTSan.cmake,FindUBSan.cmake,asan-wrapper,sanitize-helpers.cmake} + _deps/monado-src/doc/doxygen-awesome-css/* + _deps/monado-src/src/external/{cjson,imgui,jnipp,mermaid,renderdoc_api,solarxr,stb,tracy,valve-file-vdf}/* + _deps/monado-src/src/xrt/targets/android_common/src/main/res/raw/* + external/{magic_enum.hpp,magic_enum_containers.hpp,vk_mem_alloc.h} + +Zlib + _deps/monado-src/src/external/nanopb/* diff --git a/sources b/sources index a59dadb..a0f8e38 100644 --- a/sources +++ b/sources @@ -1,2 +1,2 @@ -SHA512 (WiVRn-0.24.1.tar.gz) = f946f815568bf757cb069b247e00c9d9a116829c64a18cebd95a6989a5a573a1b49afcfa78323befa641f34f553497fa88cd24bad686c22f3a10b15ddd1ef857 -SHA512 (monado-src-848a24aa106758fd6c7afcab6d95880c57dbe450.tar.bz2) = c11b883dc216864b7265a76ebab212503e20246ac2895b145d9017a861220a8091b4a637fa2b7cd62ad4248688791ff6d6ef080360e2c9a9b63394f6e9d9d3c0 +SHA512 (WiVRn-v26.6.2.tar.gz) = ce210881d43a09a4fc8a6c6fe6e40b6925a110d715a3f15ad38d5210b9ef06361e62ee6e267ffcb8fbc0f50ff9a71254c6bcd3a3e13c7bff2b09eb2532fc0db8 +SHA512 (monado-src-1b526bb3a0ff326ecd05af4c2c541407f53c6d4b.tar.bz2) = 662bf90d5a38e05c5909ed8d1a5c099979ae49ec6f7a71552a9d0c625ddda29f6b631e0feca8a01a6bb6eefcd6aacfbc81207f2852466165c561973df1cddf97 diff --git a/wivrn.spec b/wivrn.spec index 5d57142..73be60b 100644 --- a/wivrn.spec +++ b/wivrn.spec @@ -2,113 +2,65 @@ %bcond_with x264 %global forgeurl0 https://github.com/WiVRn/WiVRn -%global wivrn_version 0.24.1 -%global tag0 v%{wivrn_version} -%global date 20250410 +%global tag0 v%{version} # WiVRn is based on Monado, we need the full source -# Monado base source (find in CMakeLists.txt FetchContent_Declare(monado)) +# Monado base source (find in monado-rev file) %global forgeurl1 https://gitlab.freedesktop.org/monado/monado -%global commit1 848a24aa106758fd6c7afcab6d95880c57dbe450 -%global monado_version 24.0.0 +%global commit1 1b526bb3a0ff326ecd05af4c2c541407f53c6d4b +%global monado_version 25.1.0 %forgemeta Name: wivrn -Version: %{wivrn_version} +Version: 26.6.2 Release: %autorelease Summary: An OpenXR streaming application to a standalone headset -License: GPL-3.0-or-later AND Apache-2.0 AND BSD-2-Clause AND BSD-3-Clause AND BSL-1.0 AND CECILL-C AND CC0-1.0 AND CC-BY-4.0 AND MIT-Khronos-old AND Unlicense AND Zlib AND GPL-2.0-or-later AND MIT AND OFL-1.1 -# CECILL-C -# _deps/monado-src/src/external/imgui/imgui/imconfig.h -# CC0-1.0 AND Apache-2.0 -# _deps/monado-src/src/external/glad/* -# Unlicense -# _deps/monado-src/src/external/imgui/imgui/backends/imgui_impl_opengl3_loader.h -# Apache-2.0 -# _deps/monado-src/gradle* -# _deps/monado-src/src/external/{flexkalman,glad,mermaid,nvpro_pyramid,openxr_includes}/* -# _deps/monado-src/src/xrt/auxiliary/math/m_quatexpmap.cpp -# gradle* -# resources/values/ic_wivrn_launcher_background.xml -# Apache-2.0 OR MIT -# _deps/monado-src/doc/doxygen-awesome-css/doxygen-awesome-* -# BSD-2-Clause -# _deps/monado-src/cmake/FindEigen3.cmake -# _deps/monado-src/src/external/hungarian/* -# _deps/monado-src/src/external/tracy/{client,common}/* -# _deps/monado-src/cmake/{FindEGL.cmake,FindSystemd.cmake,OptionWithDeps.cmake} -# _deps/monado-src/src/external/openvr_includes/* -# _deps/monado-src/src/external/tinyceres/* -# _deps/monado-src/src/external/tracy/libbacktrace/* -# _deps/monado-src/src/xrt/drivers/north_star/distortion_3d/* -# _deps/monado-src/src/xrt/tracking/hand/mercury/kine_lm/lm_rotations_ceres.inl -# BSD-3-Clause AND BSL-1.0 -# _deps/monado-src/src/xrt/targets/steamvr_drv/* -# BSL-1.0 -# _deps/monado-src/{CMakeLists.txt,CompilerFlags.cmake,build.gradel} -# _deps/monado-src/cmake/{CleanDirectoryList.cmake,FindHIDAPI.cmake,FindLeapV2.cmake,FindLibcheck.cmake,FindLibusb1.cmake,FindONNXRuntime.cmake,FindOpenGLES.cmake,FindOpenHMD.cmake,FindPercetto.cmake,Findbluetooth.cmake,FindcJSON.cmake,Findudev.cmake,GenerateKhrManifest.cmake,GenerateKhrManifestInternals.cmake.in,GenerateOpenXRRuntimeManifest.cmake,GenerateVulkanApiLayerManifest.cmake,GetGitRevisionDescription.cmake,GetGitRevisionDescription.cmake.in,PrefixListGlob.cmake,ProgramFilesGlob.cmake,SPIR-V.cmake,openxr_manifest.in.json.license} -# _deps/monado-src/src/external/{Catch2,android-jni-wrap,flexkalman,imgui,nvpro_pyramid,openxr_includes,solarxr,util-headers,vit_includes} -# _deps/monado-src/src/xrt/auxiliary/{android,bindings,d3d,gsteamer,math,ogl,os,tracking,util,vive,vk}/* -# _deps/monado-src/src/xrt/compositor/* -# _deps/monado-src/src/xrt/drivers/{android,arduino,daydream,depthai,euroc,hdk,ht,ht_ctrl_emu,hydra,illixr,multi_wrapper,north_star,ohmd,opengloves,psmv,qwerty,realsense,remote,rift_s,rokid,sample,simula,simulated,solarxr,steamvr_lh,survive,twrap,ultraleap_v2,v4l2,vf,vive,wmr,xreal_air}/* -# _deps/monado-src/src/xrt/include/tracking/t_hand_tracking.h -# _deps/monado-src/src/xrt/include/xrt/* -# _deps/monado-src/src/xrt/{ipc,state_trackers,targets,tracking}/* -# _deps/monado-src/tests/* -# server/{main.cpp,start_application.cpp,target_instance_wivrn.cpp} -# BSL-1.0 AND CC0-1.0 -# _deps/monado-src/{doc,scripts}/* -# GPL-2.0-or-later -# tools/wireshark/dissector.cpp -# GPL-3.0-or-later -# {client,common,dashboard,server}/* -# MIT-Khronos-old -# _deps/monado-src/src/external/glad/include/KHR/khrplatform.h -# MIT -# _deps/monado-src/cmake/sanitizers/{FindASan.cmake,FindMSan.cmake,FindSanitizers.cmake,FindTSan.cmake,FindUBSan.cmake,asan-wrapper,sanitize-helpers.cmake} -# _deps/monado-src/doc/doxygen-awesome-css/* -# _deps/monado-src/src/external/{cjson,imgui,jnipp,mermaid,renderdoc_api,solarxr,stb,tracy,valve-file-vdf}/* -# _deps/monado-src/src/xrt/targets/android_common/src/main/res/raw/* -# external/{magic_enum.hpp,magic_enum_containers.hpp,vk_mem_alloc.h} -# Zlib -# _deps/monado-src/src/external/nanopb/* +License: %{shrink: + Apache-2.0 + AND BSD-2-Clause + AND BSD-3-Clause + AND BSL-1.0 + AND CC-BY-4.0 + AND CC0-1.0 + AND CECILL-C + AND GPL-2.0-or-later + AND GPL-3.0-or-later + AND MIT + AND MIT-Khronos-old + AND OFL-1.1 + AND Unlicense + AND Zlib +} +# For a per-file license breakdown see LicenseBreakdown. URL: %{forgeurl0} Source0: %{forgesource0} # License: GPL-3.0-or-later Source1: %{forgeurl1}/-/archive/%{commit1}/monado-src-%{commit1}.tar.bz2 # Check for new/removed patches when updating: https://github.com/WiVRn/WiVRn/tree/master/patches/monado/ (check the tag) -# For tagged releases: https://raw.githubusercontent.com/WiVRn/WiVRn/refs/tags/%%{tag0}/patches/monado/ -# For commit releases: https://raw.githubusercontent.com/WiVRn/WiVRn/%%{commit0}/patches/monado/ +# Make sure to re-pull the patches every release. They are rebased and need to be updated! # downstream-only - WiVRn specific Monado patches Patch0001: https://raw.githubusercontent.com/WiVRn/WiVRn/refs/tags/%{tag0}/patches/monado/0001-c-multi-early-wake-of-compositor.patch # downstream-only - WiVRn specific Monado patches -Patch0002: https://raw.githubusercontent.com/WiVRn/WiVRn/refs/tags/%{tag0}/patches/monado/0002-ipc-server-Always-listen-to-stdin.patch +Patch0002: https://raw.githubusercontent.com/WiVRn/WiVRn/refs/tags/%{tag0}/patches/monado/0002-Use-extern-socket-fd.patch # downstream-only - WiVRn specific Monado patches -Patch0003: https://raw.githubusercontent.com/WiVRn/WiVRn/refs/tags/%{tag0}/patches/monado/0003-Use-extern-socket-fd.patch +Patch0003: https://raw.githubusercontent.com/WiVRn/WiVRn/refs/tags/%{tag0}/patches/monado/0003-change-environment-blend-mode-selection-logic.patch # downstream-only - WiVRn specific Monado patches -Patch0004: https://raw.githubusercontent.com/WiVRn/WiVRn/refs/tags/%{tag0}/patches/monado/0004-c-render-Add-storage-usage-to-distortion-mesh.patch +Patch0004: https://raw.githubusercontent.com/WiVRn/WiVRn/refs/tags/%{tag0}/patches/monado/0004-st-oxr-forward-0-refresh-rate.patch # downstream-only - WiVRn specific Monado patches -Patch0005: https://raw.githubusercontent.com/WiVRn/WiVRn/refs/tags/%{tag0}/patches/monado/0005-change-environment-blend-mode-selection-logic.patch +Patch0005: https://raw.githubusercontent.com/WiVRn/WiVRn/refs/tags/%{tag0}/patches/monado/0005-d-steamvr_lh-prevent-crash-on-vive-pro2-WiVRn.patch # downstream-only - WiVRn specific Monado patches -Patch0006: https://raw.githubusercontent.com/WiVRn/WiVRn/refs/tags/%{tag0}/patches/monado/0006-Use-mipmaps-for-distortion-shader.patch +Patch0006: https://raw.githubusercontent.com/WiVRn/WiVRn/refs/tags/%{tag0}/patches/monado/0006-st-oxr-push-XrEventDataInteractionProfileChanged-whe.patch # downstream-only - WiVRn specific Monado patches -Patch0007: https://raw.githubusercontent.com/WiVRn/WiVRn/refs/tags/%{tag0}/patches/monado/0007-Convert-to-YCbCr-in-Monado.patch +Patch0007: https://raw.githubusercontent.com/WiVRn/WiVRn/refs/tags/%{tag0}/patches/monado/0007-don-t-verify-GL-stuff.patch # downstream-only - WiVRn specific Monado patches -Patch0009: https://raw.githubusercontent.com/WiVRn/WiVRn/refs/tags/%{tag0}/patches/monado/0009-Revert-a-bindings-improve-reproducibility-of-binding.patch -# downstream-only - WiVRn specific Monado patches -Patch0010: https://raw.githubusercontent.com/WiVRn/WiVRn/refs/tags/%{tag0}/patches/monado/0010-store-alpha-channel-in-layer-1.patch -# downstream-only - WiVRn specific Monado patches -Patch0011: https://raw.githubusercontent.com/WiVRn/WiVRn/refs/tags/%{tag0}/patches/monado/0011-st-oxr-bind-to-dynamic-role-s-profile.patch -# downstream-only - WiVRn specific Monado patches -Patch0012: https://raw.githubusercontent.com/WiVRn/WiVRn/refs/tags/%{tag0}/patches/monado/0012-ipc-Initialize-hmd-data-from-static-role.patch -# downstream-only - WiVRn specific Monado patches -Patch0013: https://raw.githubusercontent.com/WiVRn/WiVRn/refs/tags/%{tag0}/patches/monado/0013-c-main-allow-custom-pacing-app-factory.patch -# downstream-only - WiVRn specific Monado patches -Patch0014: https://raw.githubusercontent.com/WiVRn/WiVRn/refs/tags/%{tag0}/patches/monado/0014-st-oxr-forward-0-refresh-rate.patch +Patch0008: https://raw.githubusercontent.com/WiVRn/WiVRn/refs/tags/%{tag0}/patches/monado/0008-configure-u_git_tag-in-WiVRn.patch + +# If BuildRequires change, be sure to update envision-wivrn Requires +# https://src.fedoraproject.org/rpms/envision/blob/rawhide/f/envision.spec BuildRequires: boost-devel BuildRequires: cmake BuildRequires: desktop-file-utils @@ -121,6 +73,7 @@ BuildRequires: kf6-kcoreaddons-devel BuildRequires: kf6-ki18n-devel BuildRequires: kf6-kiconthemes-devel BuildRequires: kf6-kirigami-devel +BuildRequires: kf6-kirigami-addons-devel BuildRequires: libappstream-glib BuildRequires: librsvg2-tools BuildRequires: ninja-build @@ -137,6 +90,7 @@ BuildRequires: pkgconfig(gstreamer-1.0) BuildRequires: pkgconfig(gstreamer-app-1.0) BuildRequires: pkgconfig(gstreamer-video-1.0) BuildRequires: pkgconfig(hidapi-libusb) +BuildRequires: pkgconfig(libarchive) BuildRequires: pkgconfig(libavcodec) BuildRequires: pkgconfig(libavfilter) BuildRequires: pkgconfig(libavutil) @@ -150,6 +104,7 @@ BuildRequires: pkgconfig(libpipewire-0.3) BuildRequires: pkgconfig(libudev) BuildRequires: pkgconfig(libusb-1.0) BuildRequires: pkgconfig(libuvc) +BuildRequires: pkgconfig(librsvg-2.0) BuildRequires: pkgconfig(libswscale) BuildRequires: pkgconfig(libva) BuildRequires: pkgconfig(nlohmann_json) @@ -176,7 +131,10 @@ BuildRequires: pkgconfig(x264) %endif BuildRequires: pkgconfig(xrandr) BuildRequires: qcoro-qt6-devel +BuildRequires: qt6-qtbase-private-devel +BuildRequires: qt6-qtdeclarative-devel BuildRequires: qt6qml(org.kde.desktop) +BuildRequires: spirv-tools BuildRequires: systemd-rpm-macros Requires: android-tools @@ -188,9 +146,10 @@ Recommends: %{name}-dashboard Provides: bundled(monado) = %{monado_version} -# no big-endian support, vulkan 32bit headers broken +# no big-endian support, vulkan 32bit headers broken, android-tools not available on ppc64le # https://github.com/WiVRn/WiVRn/issues/271 -ExcludeArch: s390x %{ix86} +# https://src.fedoraproject.org/rpms/android-tools/c/38eea7154cb5930259f7a3ae59cdb123fb7d2862?branch=rawhide +ExcludeArch: s390x %{ix86} ppc64le %description WiVRn wirelessly connects a standalone VR headset to a Linux computer. @@ -199,9 +158,10 @@ on the computer. Supports a wide range of headsets such as: -Quest 1 / 2 / Pro / 3 / 3S +Meta Quest 1, 2, 3, 3S, pro Pico Neo 4 -HTC Vive Focus 3 / HTC Vive XR elite +HTC Vive Focus 3, XR Elite +Samsung Galaxy XR and most other Android based headsets @@ -209,6 +169,7 @@ and most other Android based headsets Summary: WiVRn dashboard Requires: %{name}%{?_isa} = %{version}-%{release} Requires: hicolor-icon-theme +Requires: kf6-kirigami-addons Requires: qt6qml(org.kde.desktop) %description -n %{name}-dashboard @@ -219,12 +180,12 @@ and to assist in pairing the headset with the server. %prep -%forgesetup +%setup -q -n WiVRn-%{version} # Extract libraries that are bundled mkdir -p _deps/monado-src tar -xvf %{SOURCE1} --strip-components 1 -C _deps/monado-src -# Apply WiVRn downstream patches +# Apply WiVRn downstream Monado patches pushd _deps/monado-src %patch -P0001 -p1 %patch -P0002 -p1 @@ -233,12 +194,7 @@ pushd _deps/monado-src %patch -P0005 -p1 %patch -P0006 -p1 %patch -P0007 -p1 -%patch -P0009 -p1 -%patch -P0010 -p1 -%patch -P0011 -p1 -%patch -P0012 -p1 -%patch -P0013 -p1 -%patch -P0014 -p1 +%patch -P0008 -p1 popd @@ -251,12 +207,14 @@ popd -DFETCHCONTENT_BASE_DIR="_deps" \ -DFETCHCONTENT_FULLY_DISCONNECTED=ON \ -DGIT_DESC=v%{version} \ - -DOVR_COMPAT_SEARCH_PATH=%{_libdir}/opencomposite/runtime \ + -DGIT_COMMIT=v%{version} \ + -DOVR_COMPAT_SEARCH_PATH=%{_libdir}/opencomposite/runtime:%{_libdir}/xrizer/runtime:/opt/opencomposite:/opt/xrizer \ -DWIVRN_BUILD_CLIENT=OFF \ -DWIVRN_BUILD_DASHBOARD=ON \ -DWIVRN_BUILD_DISSECTOR=OFF \ -DWIVRN_BUILD_SERVER=ON \ -DWIVRN_BUILD_WIVRNCTL=ON \ + -DWIVRN_FEATURE_STEAMVR_LIGHTHOUSE=ON \ -DWIVRN_USE_NVENC=ON \ -DWIVRN_USE_PIPEWIRE=ON \ -DWIVRN_USE_PULSEAUDIO=OFF \ @@ -279,6 +237,10 @@ popd install -m 0755 -vd %{buildroot}%{_sysconfdir}/ld.so.conf.d echo "%{_libdir}/%{name}" > %{buildroot}%{_sysconfdir}/ld.so.conf.d/%{name}.conf +# Import the WiVRn OpenXR runtime into Steam's pressure-vessel container +install -m 0755 -vd %{buildroot}%{_environmentdir} +echo "PRESSURE_VESSEL_IMPORT_OPENXR_1_RUNTIMES=1" > %{buildroot}%{_environmentdir}/%{name}.conf + %find_lang %{name}-dashboard %check @@ -289,19 +251,19 @@ appstream-util validate-relax --nonet %{buildroot}%{_metainfodir}/*.metainfo.xml %files -f %{name}-dashboard.lang %license COPYING LICENSE-OFL-1.1 _deps/monado-src/LICENSES/* %doc README.md docs/ -%caps(cap_sys_nice=ep) %{_bindir}/wivrn-server +%{_bindir}/wivrn-server %{_bindir}/wivrnctl %dir %{_libdir}/%{name} %{_libdir}/%{name}/libopenxr_wivrn.so %{_libdir}/%{name}/libmonado_wivrn.so -%{_libdir}/%{name}/libmonado_wivrn.so.24 -%{_libdir}/%{name}/libmonado_wivrn.so.24.0.0 +%{_libdir}/%{name}/libmonado_wivrn.so.25 +%{_libdir}/%{name}/libmonado_wivrn.so.25.1.0 %{_datarootdir}/openxr/1/openxr_wivrn.json %{_userunitdir}/wivrn.service -%{_userunitdir}/wivrn-application.service %{_prefix}/lib/firewalld/services/wivrn.xml %{_metainfodir}/io.github.wivrn.wivrn.metainfo.xml %{_sysconfdir}/ld.so.conf.d/%{name}.conf +%{_environmentdir}/%{name}.conf %files -n %{name}-dashboard %{_bindir}/wivrn-dashboard @@ -311,15 +273,12 @@ appstream-util validate-relax --nonet %{buildroot}%{_metainfodir}/*.metainfo.xml %post %systemd_user_post %{name}.service -%systemd_user_post %{name}-application.service %preun %systemd_user_preun %{name}.service -%systemd_user_preun %{name}-application.service %postun %systemd_user_postun %{name}.service -%systemd_user_postun %{name}-application.service %changelog