├── README.md ├── chromium ├── README.md └── webxr-linux-vulkan-2copy.patch └── gecko ├── README.md ├── screenshot.png ├── webxr-linux-dmabuf.patch ├── webxr-linux-glx.patch └── webxr-linux.patch /README.md: -------------------------------------------------------------------------------- 1 | # webxr-linux 2 | Repository containing efforts in getting usable WebXR under Linux 3 | 4 | ## chromium 5 | The `chromium/` folder contains a patch to get Chromium's OpenXR based WebXR implementation working under Linux. See the README at [chromium/](chromium/) for more details. 6 | 7 | ## gecko 8 | The `gecko/` folder contains a patch to get WebXR working in Firefox under Linux (X11) using OpenVR (SteamVR). See the README at [gecko/](gecko/) for more details. 9 | 10 | ![Firefox running 'Hello WebXR' under Linux with SteamVR](./gecko/screenshot.png) -------------------------------------------------------------------------------- /chromium/README.md: -------------------------------------------------------------------------------- 1 | # Chromium 2 | This folder contains a patch for Chromium to compile with OpenXR support under Linux, loosely based on [this PR](https://github.com/chromium/chromium/pull/95). 3 | 4 | # How to compile 5 | In order to apply and compile this patch, it's important that you are able to compile Chromium yourself. For this follow the steps over at: https://chromium.googlesource.com/chromium/src/+/main/docs/linux/build_instructions.md 6 | 7 | Once you have the Chromium code base locally, apply the patch using the the following command (or use `git apply`): 8 | 9 | ``` 10 | git am /path/to/webxr-linux/chromium/webxr-linux-vulkan-2copy.patch 11 | ``` 12 | 13 | > **Note** These patches were made on top of Chromium 117, commit `d84f1a0ebf4e90528407c3e8a606a7b57f336a36`, and hopefully apply cleanly to more recent commits as well. 14 | 15 | Once applied, build using `autoninja`. In order for chromium to find your configured OpenXR runtime make sure to launch it with the `--no-sandbox` flag 16 | ``` 17 | ./chrome --no-sandbox 18 | ``` 19 | 20 | ## Other relevant things to mention 21 | * A copy of [this patch](https://chromium-review.googlesource.com/c/chromium/src/+/4361799) is included to fix an issue preventing texture sharing. 22 | * Two texture copies are involved in the frame transfer: an ANGLE->Vulkan copy in the GPU process and a Vulkan->OpenXR copy in the XR process. 23 | * A call to `GLES2Interface::Finish()` is used to synchronize these two copies. A GPU-side semaphore would offer significantly reduced frametimes, but Chromium doesn't seem to offer a working IPC interface for them under Linux (the provided `gfx::GpuFence` interface is only implemented under Windows and Android). 24 | * The second copy is needed due to OpenXR's swapchain textures not being suitable for inter-process sharing. 25 | * There are different ways for the transport to take place, either `SUBMIT_AS_TEXTURE_HANDLE`, `SUBMIT_AS_MAILBOX_HOLDER` or `DRAW_INTO_TEXTURE_MAILBOX`. 26 | * This patch exclusively uses the `SUBMIT_AS_MAILBOX_HOLDER` mode. A future patch adding `DRAW_INTO_TEXTURE_MAILBOX` support could allow omitting a texture copy, improving performance. 27 | * As of July 2023, SteamVR's OpenXR implementation has a bug which deadlocks the process on instance destroy or exit, preventing subsequent WebXR sessions from starting after the first one finishes. See [this issue](https://github.com/ValveSoftware/SteamVR-for-Linux/issues/422) for more details. 28 | -------------------------------------------------------------------------------- /chromium/webxr-linux-vulkan-2copy.patch: -------------------------------------------------------------------------------- 1 | diff --git a/DEPS b/DEPS 2 | index c24c48a3d2..a0f3bee2ae 100644 3 | --- a/DEPS 4 | +++ b/DEPS 5 | @@ -173,7 +173,7 @@ vars = { 6 | # By default checkout the OpenXR loader library only on Windows and Android. 7 | # The OpenXR backend for VR in Chromium is currently only supported for these 8 | # platforms, but support for other platforms may be added in the future. 9 | - 'checkout_openxr' : 'checkout_win or checkout_android', 10 | + 'checkout_openxr' : 'checkout_win or checkout_linux or checkout_android', 11 | 12 | 'checkout_instrumented_libraries': 'checkout_linux and checkout_configuration != "small"', 13 | 14 | diff --git a/content/services/isolated_xr_device/xr_runtime_provider.cc b/content/services/isolated_xr_device/xr_runtime_provider.cc 15 | index 27c89a1464..1c6f6e4d1b 100644 16 | --- a/content/services/isolated_xr_device/xr_runtime_provider.cc 17 | +++ b/content/services/isolated_xr_device/xr_runtime_provider.cc 18 | @@ -13,10 +13,14 @@ 19 | #include "device/vr/buildflags/buildflags.h" 20 | #include "device/vr/public/cpp/features.h" 21 | 22 | -#if BUILDFLAG(ENABLE_OPENXR) && BUILDFLAG(IS_WIN) 23 | +#if BUILDFLAG(ENABLE_OPENXR) && (BUILDFLAG(IS_WIN) || BUILDFLAG(IS_LINUX)) 24 | #include "content/public/common/gpu_stream_constants.h" 25 | #include "device/vr/openxr/openxr_device.h" 26 | +#if BUILDFLAG(IS_WIN) 27 | #include "device/vr/openxr/windows/openxr_platform_helper_windows.h" 28 | +#else 29 | +#include "device/vr/openxr/linux/openxr_platform_helper_linux.h" 30 | +#endif 31 | #include "services/viz/public/cpp/gpu/context_provider_command_buffer.h" 32 | #endif 33 | 34 | @@ -97,7 +101,7 @@ void IsolatedXRRuntimeProvider::PollForDeviceChanges() { 35 | // 'preferred_device_enabled' being unused, thus [[maybe_unused]]. 36 | [[maybe_unused]] bool preferred_device_enabled = false; 37 | 38 | -#if BUILDFLAG(ENABLE_OPENXR) && BUILDFLAG(IS_WIN) 39 | +#if BUILDFLAG(ENABLE_OPENXR) && (BUILDFLAG(IS_WIN) || BUILDFLAG(IS_LINUX)) 40 | if (!preferred_device_enabled && IsOpenXrHardwareAvailable()) { 41 | SetOpenXrRuntimeStatus(RuntimeStatus::kEnable); 42 | preferred_device_enabled = true; 43 | @@ -121,11 +125,16 @@ void IsolatedXRRuntimeProvider::SetupPollingForDeviceChanges() { 44 | // If none of the following runtimes are enabled, we'll get an error for 45 | // 'command_line' being unused, thus [[maybe_unused]]. 46 | 47 | -#if BUILDFLAG(ENABLE_OPENXR) && BUILDFLAG(IS_WIN) 48 | +#if BUILDFLAG(ENABLE_OPENXR) && (BUILDFLAG(IS_WIN) || BUILDFLAG(IS_LINUX)) 49 | if (IsEnabled(command_line, device::features::kOpenXR, 50 | switches::kWebXrRuntimeOpenXr)) { 51 | + #if BUILDFLAG(IS_WIN) 52 | openxr_platform_helper_ = 53 | std::make_unique(); 54 | + #else 55 | + openxr_platform_helper_ = 56 | + std::make_unique(); 57 | + #endif 58 | should_check_openxr_ = openxr_platform_helper_->EnsureInitialized() && 59 | openxr_platform_helper_->IsApiAvailable(); 60 | any_runtimes_available |= should_check_openxr_; 61 | @@ -147,7 +156,7 @@ void IsolatedXRRuntimeProvider::RequestDevices( 62 | client_->OnDevicesEnumerated(); 63 | } 64 | 65 | -#if BUILDFLAG(ENABLE_OPENXR) && BUILDFLAG(IS_WIN) 66 | +#if BUILDFLAG(ENABLE_OPENXR) && (BUILDFLAG(IS_WIN) || BUILDFLAG(IS_LINUX)) 67 | bool IsolatedXRRuntimeProvider::IsOpenXrHardwareAvailable() { 68 | return should_check_openxr_ && openxr_platform_helper_->IsHardwareAvailable(); 69 | } 70 | diff --git a/content/services/isolated_xr_device/xr_runtime_provider.h b/content/services/isolated_xr_device/xr_runtime_provider.h 71 | index 0e7880f97e..323b21c7ec 100644 72 | --- a/content/services/isolated_xr_device/xr_runtime_provider.h 73 | +++ b/content/services/isolated_xr_device/xr_runtime_provider.h 74 | @@ -21,6 +21,13 @@ 75 | #include "services/viz/public/cpp/gpu/gpu.h" 76 | #endif 77 | 78 | +#if BUILDFLAG(ENABLE_OPENXR) && BUILDFLAG(IS_LINUX) 79 | +#include "components/viz/common/gpu/context_provider.h" 80 | +#include "device/vr/openxr/context_provider_callbacks.h" 81 | +#include "device/vr/openxr/linux/openxr_platform_helper_linux.h" 82 | +#include "services/viz/public/cpp/gpu/gpu.h" 83 | +#endif 84 | + 85 | namespace device { 86 | class OpenXrDevice; 87 | } // namespace device 88 | @@ -48,7 +55,7 @@ class IsolatedXRRuntimeProvider final 89 | void PollForDeviceChanges(); 90 | void SetupPollingForDeviceChanges(); 91 | 92 | -#if BUILDFLAG(ENABLE_OPENXR) && BUILDFLAG(IS_WIN) 93 | +#if BUILDFLAG(ENABLE_OPENXR) && (BUILDFLAG(IS_WIN) || BUILDFLAG(IS_LINUX)) 94 | bool IsOpenXrHardwareAvailable(); 95 | void SetOpenXrRuntimeStatus(RuntimeStatus status); 96 | void CreateContextProviderAsync( 97 | @@ -57,7 +64,11 @@ class IsolatedXRRuntimeProvider final 98 | bool should_check_openxr_ = false; 99 | 100 | // Must outlive OpenXrDevice 101 | + #if BUILDFLAG(IS_WIN) 102 | std::unique_ptr openxr_platform_helper_; 103 | + #else 104 | + std::unique_ptr openxr_platform_helper_; 105 | + #endif 106 | 107 | std::unique_ptr openxr_device_; 108 | 109 | diff --git a/device/vr/BUILD.gn b/device/vr/BUILD.gn 110 | index 7893093074..98494902b6 100644 111 | --- a/device/vr/BUILD.gn 112 | +++ b/device/vr/BUILD.gn 113 | @@ -244,6 +244,19 @@ if (enable_vr) { 114 | deps += [ ":directx_helpers" ] 115 | } 116 | 117 | + if (is_linux) { 118 | + libs = [ ] 119 | + 120 | + sources += [ 121 | + "openxr/linux/openxr_graphics_binding_vulkan.cc", 122 | + "openxr/linux/openxr_graphics_binding_vulkan.h", 123 | + "openxr/linux/openxr_platform_helper_linux.cc", 124 | + "openxr/linux/openxr_platform_helper_linux.h", 125 | + ] 126 | + 127 | + configs += [ "//gpu/vulkan:vulkan_config" ] 128 | + } 129 | + 130 | if (is_android) { 131 | sources += [ 132 | "openxr/android/openxr_graphics_binding_open_gles.cc", 133 | diff --git a/device/vr/buildflags/buildflags.gni b/device/vr/buildflags/buildflags.gni 134 | index 6f69f9ee24..78ad912411 100644 135 | --- a/device/vr/buildflags/buildflags.gni 136 | +++ b/device/vr/buildflags/buildflags.gni 137 | @@ -26,7 +26,7 @@ declare_args() { 138 | 139 | # To build with OpenXR support, the OpenXR Loader needs to be pulled to 140 | # third_party/openxr. 141 | - enable_openxr = checkout_openxr && (is_win || _is_xr_supported_android) 142 | + enable_openxr = checkout_openxr && (is_win || is_linux || _is_xr_supported_android) 143 | } 144 | 145 | declare_args() { 146 | diff --git a/device/vr/openxr/linux/openxr_graphics_binding_vulkan.cc b/device/vr/openxr/linux/openxr_graphics_binding_vulkan.cc 147 | new file mode 100644 148 | index 0000000000..0285bbac23 149 | --- /dev/null 150 | +++ b/device/vr/openxr/linux/openxr_graphics_binding_vulkan.cc 151 | @@ -0,0 +1,343 @@ 152 | +#include "device/vr/openxr/linux/openxr_graphics_binding_vulkan.h" 153 | +#include "base/containers/contains.h" 154 | +#include "base/native_library.h" 155 | +#include "device/vr/openxr/openxr_util.h" 156 | +#include "gpu/command_buffer/common/shared_image_usage.h" 157 | +#include "gpu/ipc/common/gpu_memory_buffer_support.h" 158 | +#include "gpu/vulkan/vulkan_function_pointers.h" 159 | +#include "ui/gfx/color_space.h" 160 | + 161 | +namespace device { 162 | + 163 | +namespace { 164 | + [[maybe_unused]] int next_memory_buffer_id = 0; 165 | +} 166 | + 167 | +void OpenXrGraphicsBinding::GetRequiredExtensions(std::vector& extensions) { 168 | + extensions.push_back(XR_KHR_VULKAN_ENABLE2_EXTENSION_NAME); 169 | +} 170 | + 171 | +OpenXrGraphicsBindingVulkan::OpenXrGraphicsBindingVulkan() = default; 172 | +OpenXrGraphicsBindingVulkan::~OpenXrGraphicsBindingVulkan() { 173 | + if (initialized_) 174 | + vkDeviceWaitIdle(binding_.device); 175 | + if (gpuTransferImage_) 176 | + gpuTransferImage_->Destroy(); 177 | + gpuTransferImage_.reset(); 178 | + if (commandBuffer_) 179 | + commandBuffer_->Destroy(); 180 | + commandBuffer_.reset(); 181 | + if (commandPool_) 182 | + commandPool_->Destroy(); 183 | + commandPool_.reset(); 184 | + if (copyQueue_) 185 | + copyQueue_->Destroy(); 186 | + copyQueue_.reset(); 187 | + if (binding_.device) 188 | + vkDestroyDevice(binding_.device, nullptr); 189 | +} 190 | + 191 | +struct StaticLibrary { 192 | + base::NativeLibrary handle; 193 | + StaticLibrary(base::NativeLibrary library) : handle(library) {} 194 | + ~StaticLibrary() {base::UnloadNativeLibrary(handle);} 195 | + operator base::NativeLibrary() const {return handle;} 196 | +}; 197 | + 198 | +static base::NativeLibrary LoadNativeLibrary(std::string_view filePath) { 199 | + base::NativeLibraryLoadError error; 200 | + base::NativeLibrary library = base::LoadNativeLibrary(base::FilePath(filePath), &error); 201 | + if (!library) 202 | + LOG(ERROR) << "Failed to load '" << filePath << "': " << error.ToString(); 203 | + return library; 204 | +} 205 | + 206 | +XrResult OpenXrGraphicsBindingVulkan::SetupBinding(XrInstance instance, XrSystemId system) { 207 | + static StaticLibrary libvulkan = LoadNativeLibrary("libvulkan.so.1"); 208 | + gpu::VulkanFunctionPointers *const vulkan_function_pointers = gpu::GetVulkanFunctionPointers(); 209 | + vulkan_function_pointers->ResetForTesting(); 210 | + if (!vulkan_function_pointers->BindUnassociatedFunctionPointersFromLoaderLib(libvulkan)) 211 | + return XR_ERROR_RUNTIME_FAILURE; 212 | + 213 | + PFN_xrGetVulkanGraphicsRequirements2KHR pfn_xrGetVulkanGraphicsRequirements2KHR = nullptr; 214 | + RETURN_IF_XR_FAILED(xrGetInstanceProcAddr(instance, "xrGetVulkanGraphicsRequirements2KHR", (PFN_xrVoidFunction*)(&pfn_xrGetVulkanGraphicsRequirements2KHR))); 215 | + PFN_xrCreateVulkanInstanceKHR pfn_xrCreateVulkanInstanceKHR = nullptr; 216 | + RETURN_IF_XR_FAILED(xrGetInstanceProcAddr(instance, "xrCreateVulkanInstanceKHR", (PFN_xrVoidFunction*)(&pfn_xrCreateVulkanInstanceKHR))); 217 | + PFN_xrGetVulkanGraphicsDevice2KHR pfn_xrGetVulkanGraphicsDevice2KHR = nullptr; 218 | + RETURN_IF_XR_FAILED(xrGetInstanceProcAddr(instance, "xrGetVulkanGraphicsDevice2KHR", (PFN_xrVoidFunction*)(&pfn_xrGetVulkanGraphicsDevice2KHR))); 219 | + PFN_xrCreateVulkanDeviceKHR pfn_xrCreateVulkanDeviceKHR = nullptr; 220 | + RETURN_IF_XR_FAILED(xrGetInstanceProcAddr(instance, "xrCreateVulkanDeviceKHR", (PFN_xrVoidFunction*)(&pfn_xrCreateVulkanDeviceKHR))); 221 | + 222 | + XrGraphicsRequirementsVulkan2KHR requirements = {XR_TYPE_GRAPHICS_REQUIREMENTS_VULKAN2_KHR}; 223 | + RETURN_IF_XR_FAILED(pfn_xrGetVulkanGraphicsRequirements2KHR(instance, system, &requirements)); 224 | + VkResult result; 225 | + RETURN_IF_XR_FAILED(pfn_xrCreateVulkanInstanceKHR(instance, (const XrVulkanInstanceCreateInfoKHR[1]){{ 226 | + .type = XR_TYPE_VULKAN_INSTANCE_CREATE_INFO_KHR, 227 | + .systemId = system, 228 | + .pfnGetInstanceProcAddr = vulkan_function_pointers->vkGetInstanceProcAddr.get(), 229 | + .vulkanCreateInfo = (const VkInstanceCreateInfo[1]){{ 230 | + .sType = VK_STRUCTURE_TYPE_INSTANCE_CREATE_INFO, 231 | + .pApplicationInfo = (const VkApplicationInfo[1]){{ 232 | + .sType = VK_STRUCTURE_TYPE_APPLICATION_INFO, 233 | + .pApplicationName = "Chromium", 234 | + .apiVersion = gpu::kVulkanRequiredApiVersion, 235 | + }}, 236 | + /*.enabledLayerCount = 1, 237 | + .ppEnabledLayerNames = (const char *const[1]){ 238 | + "VK_LAYER_KHRONOS_validation", 239 | + },*/ 240 | + }}, 241 | + }}, &binding_.instance, &result)); 242 | + RETURN_IF(result < VK_SUCCESS, XR_ERROR_RUNTIME_FAILURE, "xrCreateVulkanInstanceKHR() failed"); 243 | + 244 | + std::vector extensions = { 245 | + VK_KHR_EXTERNAL_MEMORY_EXTENSION_NAME, 246 | + VK_KHR_EXTERNAL_MEMORY_FD_EXTENSION_NAME, 247 | + VK_KHR_EXTERNAL_SEMAPHORE_EXTENSION_NAME, 248 | + VK_KHR_EXTERNAL_SEMAPHORE_FD_EXTENSION_NAME, 249 | + VK_EXT_EXTERNAL_MEMORY_DMA_BUF_EXTENSION_NAME, 250 | + }; 251 | + if (haveDrmFormatModifiers_) // TODO: set `haveDrmFormatModifiers_` if supported 252 | + extensions.push_back(VK_EXT_IMAGE_DRM_FORMAT_MODIFIER_EXTENSION_NAME); 253 | + gfx::ExtensionSet extension_set(std::begin(extensions), std::end(extensions)); 254 | + 255 | + if (!vulkan_function_pointers->BindInstanceFunctionPointers(binding_.instance, gpu::kVulkanRequiredApiVersion, extension_set)) 256 | + return XR_ERROR_RUNTIME_FAILURE; 257 | + 258 | + RETURN_IF_XR_FAILED(pfn_xrGetVulkanGraphicsDevice2KHR(instance, (const XrVulkanGraphicsDeviceGetInfoKHR[1]){{ 259 | + .type = XR_TYPE_VULKAN_GRAPHICS_DEVICE_GET_INFO_KHR, 260 | + .systemId = system, 261 | + .vulkanInstance = binding_.instance, 262 | + }}, &binding_.physicalDevice)); 263 | + 264 | + const float queuePriority = 0; 265 | + VkDeviceQueueCreateInfo queueInfo = { 266 | + .sType = VK_STRUCTURE_TYPE_DEVICE_QUEUE_CREATE_INFO, 267 | + .queueCount = 1, 268 | + .pQueuePriorities = &queuePriority, 269 | + }; 270 | + uint32_t queueFamilyCount = 0; 271 | + vkGetPhysicalDeviceQueueFamilyProperties(binding_.physicalDevice, &queueFamilyCount, nullptr); 272 | + std::vector queueFamilyProps(queueFamilyCount); 273 | + vkGetPhysicalDeviceQueueFamilyProperties(binding_.physicalDevice, &queueFamilyCount, &queueFamilyProps[0]); 274 | + 275 | + for (uint32_t i = 0; i < queueFamilyCount; ++i) { 276 | + if (queueFamilyProps[i].queueFlags & VK_QUEUE_GRAPHICS_BIT) { 277 | + binding_.queueFamilyIndex = i; 278 | + break; 279 | + } 280 | + } 281 | + queueInfo.queueFamilyIndex = binding_.queueFamilyIndex; 282 | + 283 | + RETURN_IF_XR_FAILED(pfn_xrCreateVulkanDeviceKHR(instance, (const XrVulkanDeviceCreateInfoKHR[1]){{ 284 | + .type = XR_TYPE_VULKAN_DEVICE_CREATE_INFO_KHR, 285 | + .systemId = system, 286 | + .pfnGetInstanceProcAddr = vulkan_function_pointers->vkGetInstanceProcAddr.get(), 287 | + .vulkanPhysicalDevice = binding_.physicalDevice, 288 | + .vulkanCreateInfo = (const VkDeviceCreateInfo[1]){{ 289 | + .sType = VK_STRUCTURE_TYPE_DEVICE_CREATE_INFO, 290 | + .queueCreateInfoCount = 1, 291 | + .pQueueCreateInfos = &queueInfo, 292 | + .enabledExtensionCount = (uint32_t)extensions.size(), 293 | + .ppEnabledExtensionNames = &extensions[0], 294 | + .pEnabledFeatures = (const VkPhysicalDeviceFeatures[1]){}, 295 | + }}, 296 | + }}, &binding_.device, &result)); 297 | + RETURN_IF(result < VK_SUCCESS, XR_ERROR_RUNTIME_FAILURE, "xrCreateVulkanDeviceKHR() failed"); 298 | + if (!vulkan_function_pointers->BindDeviceFunctionPointers(binding_.device, gpu::kVulkanRequiredApiVersion, extension_set)) 299 | + return XR_ERROR_RUNTIME_FAILURE; 300 | + vkCmdCopyImage_ = reinterpret_cast(vkGetDeviceProcAddr(binding_.device, "vkCmdCopyImage")); 301 | + if (!vkCmdCopyImage_) { 302 | + DLOG(WARNING) << "Failed to bind vulkan entrypoint: vkCmdCopyImage"; 303 | + return XR_ERROR_RUNTIME_FAILURE; 304 | + } 305 | + 306 | + binding_.queueIndex = 0; 307 | + VkQueue queue = VK_NULL_HANDLE; 308 | + vkGetDeviceQueue(binding_.device, binding_.queueFamilyIndex, binding_.queueIndex, &queue); 309 | + copyQueue_ = std::make_unique(binding_.instance); 310 | + if (!copyQueue_->InitializeForWebView(binding_.physicalDevice, binding_.device, queue, binding_.queueFamilyIndex, std::move(extension_set))) 311 | + return XR_ERROR_RUNTIME_FAILURE; 312 | + commandPool_ = std::make_unique(copyQueue_.get()); 313 | + if (!commandPool_->Initialize()) // vkCreateCommandPool 314 | + return XR_ERROR_RUNTIME_FAILURE; 315 | + commandBuffer_ = commandPool_->CreatePrimaryCommandBuffer(); // vkAllocateCommandBuffers 316 | + if (!commandBuffer_) 317 | + return XR_ERROR_RUNTIME_FAILURE; 318 | + return XR_SUCCESS; 319 | +} 320 | + 321 | +bool OpenXrGraphicsBindingVulkan::Initialize(XrInstance instance, XrSystemId system) { 322 | + if (initialized_) { 323 | + return true; 324 | + } 325 | + 326 | + if (XR_FAILED(SetupBinding(instance, system))) 327 | + return false; 328 | + initialized_ = true; 329 | + OnSwapchainImageSizeChanged(); 330 | + return true; 331 | +} 332 | + 333 | +const void* OpenXrGraphicsBindingVulkan::GetSessionCreateInfo() const { 334 | + CHECK(initialized_); 335 | + return &binding_; 336 | +} 337 | + 338 | +int64_t OpenXrGraphicsBindingVulkan::GetSwapchainFormat(XrSession session) const { 339 | + uint32_t format_length = 0; 340 | + RETURN_IF_XR_FAILED(xrEnumerateSwapchainFormats(session, 0, &format_length, nullptr)); 341 | + std::vector swapchain_formats(format_length); 342 | + RETURN_IF_XR_FAILED(xrEnumerateSwapchainFormats(session, (uint32_t)swapchain_formats.size(), &format_length, swapchain_formats.data())); 343 | + DCHECK(!swapchain_formats.empty()); 344 | + 345 | + if (base::Contains(swapchain_formats, VK_FORMAT_R8G8B8A8_SRGB)) 346 | + return VK_FORMAT_R8G8B8A8_SRGB; 347 | + 348 | + if (!base::Contains(swapchain_formats, VK_FORMAT_R8G8B8A8_UNORM)) { 349 | + LOG(ERROR) << "No matching supported swapchain formats with OpenXr Runtime:"; 350 | + for(int64_t format : swapchain_formats) 351 | + LOG(ERROR) << " " << format; 352 | + } 353 | + 354 | + return VK_FORMAT_R8G8B8A8_UNORM; 355 | +} 356 | + 357 | +XrResult OpenXrGraphicsBindingVulkan::EnumerateSwapchainImages(const XrSwapchain& color_swapchain) { 358 | + CHECK(color_swapchain != XR_NULL_HANDLE); 359 | + CHECK(color_swapchain_images_.empty()); 360 | + 361 | + uint32_t chain_length; 362 | + RETURN_IF_XR_FAILED(xrEnumerateSwapchainImages(color_swapchain, 0, &chain_length, nullptr)); 363 | + std::vector xr_swapchain_images(chain_length, {XR_TYPE_SWAPCHAIN_IMAGE_VULKAN2_KHR}); 364 | + 365 | + RETURN_IF_XR_FAILED(xrEnumerateSwapchainImages(color_swapchain, xr_swapchain_images.size(), &chain_length, reinterpret_cast(xr_swapchain_images.data()))); 366 | + 367 | + color_swapchain_images_.reserve(xr_swapchain_images.size()); 368 | + for (const XrSwapchainImageVulkan2KHR& swapchain_image : xr_swapchain_images) { 369 | + color_swapchain_images_.emplace_back(swapchain_image.image); 370 | + } 371 | + 372 | + return XR_SUCCESS; 373 | +} 374 | + 375 | +void OpenXrGraphicsBindingVulkan::ClearSwapChainImages() { 376 | + color_swapchain_images_.clear(); 377 | +} 378 | + 379 | +base::span OpenXrGraphicsBindingVulkan::GetSwapChainImages() { 380 | + return color_swapchain_images_; 381 | +} 382 | + 383 | +bool OpenXrGraphicsBindingVulkan::CanUseSharedImages() const { 384 | + return false; 385 | +} 386 | + 387 | +void OpenXrGraphicsBindingVulkan::CreateSharedImages(gpu::SharedImageInterface* sii) {} 388 | + 389 | +const SwapChainInfo& OpenXrGraphicsBindingVulkan::GetActiveSwapchainImage() { 390 | + CHECK(has_active_swapchain_image()); 391 | + CHECK(active_swapchain_index() < color_swapchain_images_.size()); 392 | + return color_swapchain_images_[active_swapchain_index()]; 393 | +} 394 | + 395 | +bool OpenXrGraphicsBindingVulkan::WaitOnFence(gfx::GpuFence& gpu_fence) { 396 | + return false; 397 | +} 398 | + 399 | +void OpenXrGraphicsBindingVulkan::OnSwapchainImageSizeChanged() { 400 | + gpuTransferMailbox_ = {}; 401 | + gpuTransferBuffer_ = nullptr; 402 | + if (!initialized_) 403 | + return; 404 | + 405 | + const gfx::Size size = GetSwapchainImageSize(); // TODO: return early if size hasn't changed 406 | + 407 | + vkDeviceWaitIdle(binding_.device); 408 | + if (gpuTransferImage_) 409 | + gpuTransferImage_->Destroy(); 410 | + gpuTransferImage_.reset(); 411 | + 412 | + const VkExternalMemoryImageCreateInfo extra_image_create_info = { 413 | + .sType = VK_STRUCTURE_TYPE_EXTERNAL_MEMORY_IMAGE_CREATE_INFO, 414 | + .handleTypes = VK_EXTERNAL_MEMORY_HANDLE_TYPE_DMA_BUF_BIT_EXT, 415 | + }; 416 | + const VkExportMemoryAllocateInfo extra_memory_allocation_info = { 417 | + .sType = VK_STRUCTURE_TYPE_EXPORT_MEMORY_ALLOCATE_INFO, 418 | + .handleTypes = VK_EXTERNAL_MEMORY_HANDLE_TYPE_DMA_BUF_BIT_EXT, 419 | + }; 420 | + gpuTransferImage_ = gpu::VulkanImage::Create(copyQueue_.get(), size, VK_FORMAT_R8G8B8A8_UNORM, VK_IMAGE_USAGE_TRANSFER_SRC_BIT | VK_IMAGE_USAGE_TRANSFER_DST_BIT, 0, 421 | + haveDrmFormatModifiers_ ? VK_IMAGE_TILING_DRM_FORMAT_MODIFIER_EXT : VK_IMAGE_TILING_OPTIMAL, &extra_image_create_info, &extra_memory_allocation_info); 422 | + if (!gpuTransferImage_) 423 | + return; 424 | + 425 | + base::ScopedFD fd = gpuTransferImage_->GetMemoryFd(VK_EXTERNAL_MEMORY_HANDLE_TYPE_DMA_BUF_BIT_EXT); 426 | + if (!fd.is_valid()) { 427 | + LOG(ERROR) << "vkGetMemoryFdKHR() failed"; 428 | + return; 429 | + } 430 | + 431 | + VkImageDrmFormatModifierPropertiesEXT modifier_props = { 432 | + .sType = VK_STRUCTURE_TYPE_IMAGE_DRM_FORMAT_MODIFIER_PROPERTIES_EXT, 433 | + .drmFormatModifier = gfx::NativePixmapHandle::kNoModifier, 434 | + }; 435 | + if (haveDrmFormatModifiers_) { 436 | + const VkResult result = vkGetImageDrmFormatModifierPropertiesEXT(binding_.device, gpuTransferImage_->image(), &modifier_props); 437 | + if (result != VK_SUCCESS) { 438 | + LOG(ERROR) << "vkGetImageDrmFormatModifierPropertiesEXT failed result:" << result; 439 | + return; 440 | + } 441 | + } 442 | + 443 | + gfx::GpuMemoryBufferHandle handle; 444 | + handle.id = gfx::GpuMemoryBufferId(next_memory_buffer_id++); 445 | + handle.type = gfx::GpuMemoryBufferType::NATIVE_PIXMAP; 446 | + handle.native_pixmap_handle.planes.emplace_back(0, 0, gpuTransferImage_->device_size(), std::move(fd)); 447 | + handle.native_pixmap_handle.modifier = modifier_props.drmFormatModifier; 448 | + gpu::GpuMemoryBufferSupport support; 449 | + gpuTransferBuffer_ = support.CreateGpuMemoryBufferImplFromHandle(std::move(handle), size, gfx::BufferFormat::BGRA_8888, gfx::BufferUsage::SCANOUT, base::NullCallback()); 450 | +} 451 | + 452 | +void OpenXrGraphicsBindingVulkan::BlitFrom(const gpu::MailboxHolder& holder, gpu::gles2::GLES2Interface *gl, gpu::SharedImageInterface *sii) { 453 | + const gfx::Size imageSize = GetTransferSize(); 454 | + if (imageSize != GetSwapchainImageSize() || !gpuTransferBuffer_) 455 | + return; 456 | + 457 | + commandBuffer_->Wait(UINT64_MAX); 458 | + if (gpuTransferMailbox_.mailbox.IsZero()) { 459 | + gpuTransferMailbox_.mailbox = sii->CreateSharedImage(viz::SinglePlaneFormat::kBGRA_8888, gpuTransferBuffer_->GetSize(), gfx::ColorSpace(), kBottomLeft_GrSurfaceOrigin, kPremul_SkAlphaType, gpu::SHARED_IMAGE_USAGE_GLES2, "OpenXrGraphicsBindingVulkan", gpuTransferBuffer_->CloneHandle()); 460 | + gpuTransferMailbox_.sync_token = sii->GenVerifiedSyncToken(); 461 | + gpuTransferMailbox_.texture_target = GL_TEXTURE_2D; 462 | + gl->WaitSyncTokenCHROMIUM(sii->GenUnverifiedSyncToken().GetConstData()); 463 | + } 464 | + 465 | + gpu::Mailbox::Name mailboxes[2]; 466 | + memcpy(mailboxes[0], holder.mailbox.name, sizeof(gpu::Mailbox::Name)); 467 | + memcpy(mailboxes[1], gpuTransferMailbox_.mailbox.name, sizeof(gpu::Mailbox::Name)); 468 | + gl->WaitSyncTokenCHROMIUM(holder.sync_token.GetConstData()); 469 | + gl->CopySharedImageINTERNAL(0, 0, 0, 0, imageSize.width(), imageSize.height(), true, *mailboxes); 470 | + gl->Finish(); // CHROMIUM_gpu_fence isn't available on Linux and I'm not sure how else to synchronize this 471 | + 472 | + SwapChainInfo &image = color_swapchain_images_[active_swapchain_index()]; 473 | + { 474 | + gpu::ScopedSingleUseCommandBufferRecorder recorder(*commandBuffer_); // vkBeginCommandBuffer 475 | + commandBuffer_->TransitionImageLayout(gpuTransferImage_->image(), VK_IMAGE_LAYOUT_UNDEFINED, VK_IMAGE_LAYOUT_TRANSFER_SRC_OPTIMAL); 476 | + commandBuffer_->TransitionImageLayout(image.openxr_image, VK_IMAGE_LAYOUT_UNDEFINED, VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL); 477 | + vkCmdCopyImage_(recorder.handle(), gpuTransferImage_->image(), VK_IMAGE_LAYOUT_TRANSFER_SRC_OPTIMAL, image.openxr_image, VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL, 1, (const VkImageCopy[1]){{ 478 | + .srcSubresource = { 479 | + .aspectMask = VK_IMAGE_ASPECT_COLOR_BIT, 480 | + .layerCount = 1, 481 | + }, 482 | + .dstSubresource = { 483 | + .aspectMask = VK_IMAGE_ASPECT_COLOR_BIT, 484 | + .layerCount = 1, 485 | + }, 486 | + .extent = {(uint32_t)imageSize.width(), (uint32_t)imageSize.height(), 1}, 487 | + }}); 488 | + commandBuffer_->TransitionImageLayout(gpuTransferImage_->image(), VK_IMAGE_LAYOUT_TRANSFER_SRC_OPTIMAL, VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL); 489 | + commandBuffer_->TransitionImageLayout(image.openxr_image, VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL, VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL); 490 | + } // ~CommandBufferRecorderBase() - vkEndCommandBuffer 491 | + commandBuffer_->Submit(0, nullptr, 0, nullptr); 492 | +} 493 | + 494 | +} // namespace device 495 | diff --git a/device/vr/openxr/linux/openxr_graphics_binding_vulkan.h b/device/vr/openxr/linux/openxr_graphics_binding_vulkan.h 496 | new file mode 100644 497 | index 0000000000..a17ae30605 498 | --- /dev/null 499 | +++ b/device/vr/openxr/linux/openxr_graphics_binding_vulkan.h 500 | @@ -0,0 +1,54 @@ 501 | +#pragma once 502 | +#include 503 | + 504 | +#include "device/vr/openxr/openxr_graphics_binding.h" 505 | +#include "device/vr/openxr/openxr_platform.h" 506 | +#include "device/vr/vr_export.h" 507 | +#include "gpu/command_buffer/client/gles2_interface.h" 508 | +#include "gpu/command_buffer/client/shared_image_interface.h" 509 | +#include "gpu/vulkan/vulkan_command_buffer.h" 510 | +#include "gpu/vulkan/vulkan_command_pool.h" 511 | +#include "gpu/vulkan/vulkan_device_queue.h" 512 | +#include "gpu/vulkan/vulkan_image.h" 513 | +#include "ui/gfx/gpu_memory_buffer.h" 514 | + 515 | +namespace device { 516 | + 517 | +class DEVICE_VR_EXPORT OpenXrGraphicsBindingVulkan : public OpenXrGraphicsBinding { 518 | + public: 519 | + OpenXrGraphicsBindingVulkan(); 520 | + ~OpenXrGraphicsBindingVulkan() override; 521 | + 522 | + bool Initialize(XrInstance instance, XrSystemId system) override; 523 | + const void* GetSessionCreateInfo() const override; 524 | + int64_t GetSwapchainFormat(XrSession session) const override; 525 | + XrResult EnumerateSwapchainImages( 526 | + const XrSwapchain& color_swapchain) override; 527 | + void ClearSwapChainImages() override; 528 | + base::span GetSwapChainImages() override; 529 | + bool CanUseSharedImages() const override; 530 | + void CreateSharedImages(gpu::SharedImageInterface* sii) override; 531 | + const SwapChainInfo& GetActiveSwapchainImage() override; 532 | + bool WaitOnFence(gfx::GpuFence& gpu_fence) override; 533 | + void BlitFrom(const gpu::MailboxHolder& holder, gpu::gles2::GLES2Interface *gl, gpu::SharedImageInterface *sii); 534 | + 535 | +private: 536 | + XrResult SetupBinding(XrInstance instance, XrSystemId system); 537 | + void OnSwapchainImageSizeChanged() override; 538 | + bool initialized_ = false; 539 | + bool haveDrmFormatModifiers_ = false; 540 | + XrGraphicsBindingVulkan2KHR binding_{XR_TYPE_GRAPHICS_BINDING_VULKAN2_KHR, nullptr}; 541 | + std::vector color_swapchain_images_; 542 | + 543 | + std::unique_ptr copyQueue_ = nullptr; 544 | + std::unique_ptr commandPool_ = nullptr; 545 | + std::unique_ptr commandBuffer_ = nullptr; 546 | + 547 | + std::unique_ptr gpuTransferImage_ = nullptr; 548 | + std::unique_ptr gpuTransferBuffer_ = nullptr; 549 | + gpu::MailboxHolder gpuTransferMailbox_; 550 | + 551 | + PFN_vkCmdCopyImage vkCmdCopyImage_ = nullptr; 552 | +}; 553 | + 554 | +} // namespace device 555 | diff --git a/device/vr/openxr/linux/openxr_platform_helper_linux.cc b/device/vr/openxr/linux/openxr_platform_helper_linux.cc 556 | new file mode 100644 557 | index 0000000000..6edd66879d 558 | --- /dev/null 559 | +++ b/device/vr/openxr/linux/openxr_platform_helper_linux.cc 560 | @@ -0,0 +1,63 @@ 561 | +#include "device/vr/openxr/linux/openxr_platform_helper_linux.h" 562 | +#include "device/vr/openxr/openxr_api_wrapper.h" 563 | +#include "device/vr/openxr/linux/openxr_graphics_binding_vulkan.h" 564 | +#include "device/vr/public/mojom/isolated_xr_service.mojom.h" 565 | + 566 | +namespace device { 567 | + 568 | +void OpenXrPlatformHelper::GetRequiredExtensions(std::vector& extensions) {} 569 | +std::vector OpenXrPlatformHelper::GetOptionalExtensions() {return {};} 570 | + 571 | +OpenXrPlatformHelperLinux::OpenXrPlatformHelperLinux() {} 572 | +OpenXrPlatformHelperLinux::~OpenXrPlatformHelperLinux() = default; 573 | + 574 | +std::unique_ptr OpenXrPlatformHelperLinux::GetGraphicsBinding() { 575 | + return std::make_unique(); 576 | +} 577 | + 578 | +void OpenXrPlatformHelperLinux::GetPlatformCreateInfo(const device::OpenXrCreateInfo& create_info, PlatformCreateInfoReadyCallback callback) { 579 | + std::move(callback).Run(nullptr); 580 | +} 581 | + 582 | +device::mojom::XRDeviceData OpenXrPlatformHelperLinux::GetXRDeviceData() { 583 | + return {}; 584 | +} 585 | + 586 | +bool OpenXrPlatformHelperLinux::IsHardwareAvailable() { 587 | + XrInstance instance = GetOrCreateXrInstance(); 588 | + if (instance == XR_NULL_HANDLE) { 589 | + return false; 590 | + } 591 | + 592 | + XrSystemId system; 593 | + return XR_SUCCEEDED(OpenXrApiWrapper::GetSystem(instance, &system)); 594 | +} 595 | + 596 | +bool OpenXrPlatformHelperLinux::IsApiAvailable() { 597 | + return GetOrCreateXrInstance() != XR_NULL_HANDLE; 598 | +} 599 | + 600 | +bool OpenXrPlatformHelperLinux::Initialize() { 601 | + return true; 602 | +} 603 | + 604 | +XrInstance OpenXrPlatformHelperLinux::GetOrCreateXrInstance() { 605 | + XrInstance instance = XR_NULL_HANDLE; 606 | + (void)CreateInstance(&instance, nullptr); 607 | + return instance; 608 | +} 609 | + 610 | +XrResult OpenXrPlatformHelperLinux::CreateInstance(XrInstance* instance, void* create_info) { 611 | + CHECK(instance); 612 | + if (xr_instance_ != XR_NULL_HANDLE) { 613 | + *instance = xr_instance_; 614 | + return XR_SUCCESS; 615 | + } 616 | + return OpenXrPlatformHelper::CreateInstance(instance, create_info); 617 | +} 618 | + 619 | +XrResult OpenXrPlatformHelperLinux::DestroyInstance(XrInstance& instance) { 620 | + return OpenXrPlatformHelper::DestroyInstance(instance); 621 | +} 622 | + 623 | +} // namespace device 624 | diff --git a/device/vr/openxr/linux/openxr_platform_helper_linux.h b/device/vr/openxr/linux/openxr_platform_helper_linux.h 625 | new file mode 100644 626 | index 0000000000..a922dbf431 627 | --- /dev/null 628 | +++ b/device/vr/openxr/linux/openxr_platform_helper_linux.h 629 | @@ -0,0 +1,27 @@ 630 | +#pragma once 631 | +#include "device/vr/openxr/openxr_platform_helper.h" 632 | +#include "device/vr/openxr/openxr_platform.h" 633 | + 634 | +namespace device { 635 | + 636 | +class DEVICE_VR_EXPORT OpenXrPlatformHelperLinux : public OpenXrPlatformHelper { 637 | + public: 638 | + OpenXrPlatformHelperLinux(); 639 | + ~OpenXrPlatformHelperLinux() override; 640 | + 641 | + std::unique_ptr GetGraphicsBinding() override; 642 | + void GetPlatformCreateInfo(const device::OpenXrCreateInfo& create_info, PlatformCreateInfoReadyCallback) override; 643 | + device::mojom::XRDeviceData GetXRDeviceData() override; 644 | + bool Initialize() override; 645 | + 646 | + XrResult CreateInstance(XrInstance* instance, void* create_info) override; 647 | + XrResult DestroyInstance(XrInstance& instance) override; 648 | + 649 | + bool IsHardwareAvailable(); 650 | + bool IsApiAvailable(); 651 | + 652 | +private: 653 | + XrInstance GetOrCreateXrInstance(); 654 | +}; 655 | + 656 | +} 657 | diff --git a/device/vr/openxr/openxr_graphics_binding.cc b/device/vr/openxr/openxr_graphics_binding.cc 658 | index df54dce45a..f2c996aeba 100644 659 | --- a/device/vr/openxr/openxr_graphics_binding.cc 660 | +++ b/device/vr/openxr/openxr_graphics_binding.cc 661 | @@ -11,6 +11,8 @@ namespace device { 662 | #if BUILDFLAG(IS_WIN) 663 | SwapChainInfo::SwapChainInfo(ID3D11Texture2D* d3d11_texture) 664 | : d3d11_texture(d3d11_texture) {} 665 | +#elif BUILDFLAG(IS_LINUX) 666 | +SwapChainInfo::SwapChainInfo(VkImage image) : openxr_image(image) {} 667 | #elif BUILDFLAG(IS_ANDROID) 668 | SwapChainInfo::SwapChainInfo(uint32_t texture) : openxr_texture(texture) {} 669 | #endif 670 | diff --git a/device/vr/openxr/openxr_graphics_binding.h b/device/vr/openxr/openxr_graphics_binding.h 671 | index 39e8062136..a40265c8a8 100644 672 | --- a/device/vr/openxr/openxr_graphics_binding.h 673 | +++ b/device/vr/openxr/openxr_graphics_binding.h 674 | @@ -19,6 +19,10 @@ 675 | #include 676 | #endif 677 | 678 | +#if BUILDFLAG(IS_LINUX) 679 | +#include 680 | +#endif 681 | + 682 | #if BUILDFLAG(IS_ANDROID) 683 | #include "gpu/ipc/common/gpu_memory_buffer_impl_android_hardware_buffer.h" 684 | #include "ui/gl/scoped_egl_image.h" 685 | @@ -39,6 +43,8 @@ struct SwapChainInfo { 686 | public: 687 | #if BUILDFLAG(IS_WIN) 688 | explicit SwapChainInfo(ID3D11Texture2D*); 689 | +#elif BUILDFLAG(IS_LINUX) 690 | + explicit SwapChainInfo(VkImage image); 691 | #elif BUILDFLAG(IS_ANDROID) 692 | explicit SwapChainInfo(uint32_t texture); 693 | #endif 694 | @@ -56,6 +62,8 @@ struct SwapChainInfo { 695 | // and D3D11Fence for each D3D11 texture in the vector. 696 | raw_ptr d3d11_texture = nullptr; 697 | Microsoft::WRL::ComPtr d3d11_fence; 698 | +#elif BUILDFLAG(IS_LINUX) 699 | + VkImage openxr_image; 700 | #elif BUILDFLAG(IS_ANDROID) 701 | // Ideally this would be a gluint, but there are conflicting headers for GL 702 | // depending on *how* you want to use it; so we can't use it at the moment. 703 | diff --git a/device/vr/openxr/openxr_platform.h b/device/vr/openxr/openxr_platform.h 704 | index a0158be7f9..65f0f32df7 100644 705 | --- a/device/vr/openxr/openxr_platform.h 706 | +++ b/device/vr/openxr/openxr_platform.h 707 | @@ -18,6 +18,8 @@ 708 | 709 | #if BUILDFLAG(IS_WIN) 710 | #include 711 | +#elif BUILDFLAG(IS_LINUX) 712 | +#include 713 | #elif BUILDFLAG(IS_ANDROID) 714 | #include 715 | #include 716 | diff --git a/device/vr/openxr/openxr_render_loop.cc b/device/vr/openxr/openxr_render_loop.cc 717 | index 31e956c757..358ef492df 100644 718 | --- a/device/vr/openxr/openxr_render_loop.cc 719 | +++ b/device/vr/openxr/openxr_render_loop.cc 720 | @@ -26,6 +26,10 @@ 721 | #include 722 | #endif 723 | 724 | +#if BUILDFLAG(IS_LINUX) 725 | +#include "device/vr/openxr/linux/openxr_graphics_binding_vulkan.h" 726 | +#endif 727 | + 728 | namespace device { 729 | 730 | OpenXrRenderLoop::OpenXrRenderLoop( 731 | @@ -125,7 +129,7 @@ void OpenXrRenderLoop::StartRuntime( 732 | // texture helper rather than need it passed in. 733 | #if BUILDFLAG(IS_WIN) 734 | graphics_binding_ = platform_helper_->GetGraphicsBinding(&texture_helper_); 735 | -#elif BUILDFLAG(IS_ANDROID) 736 | +#elif BUILDFLAG(IS_LINUX) || BUILDFLAG(IS_ANDROID) 737 | graphics_binding_ = platform_helper_->GetGraphicsBinding(); 738 | #endif 739 | 740 | @@ -312,9 +316,15 @@ void OpenXrRenderLoop::SubmitFrame(int16_t frame_index, 741 | base::TimeDelta time_waited) { 742 | DVLOG(3) << __func__ << " frame_index=" << frame_index; 743 | CHECK(!IsUsingSharedImages()); 744 | + #if BUILDFLAG(IS_LINUX) 745 | + static_cast(graphics_binding_.get())->BlitFrom(mailbox, context_provider_->ContextGL(), context_provider_->SharedImageInterface()); 746 | + MarkFrameSubmitted(frame_index); 747 | + MaybeCompositeAndSubmit(); 748 | + #else 749 | DCHECK(BUILDFLAG(IS_ANDROID)); 750 | // TODO(https://crbug.com/1454942): Support non-shared buffer mode. 751 | SubmitFrameMissing(frame_index, mailbox.sync_token); 752 | + #endif 753 | } 754 | 755 | void OpenXrRenderLoop::SubmitFrameDrawnIntoTexture( 756 | @@ -349,7 +359,7 @@ void OpenXrRenderLoop::OnWebXrTokenSignaled( 757 | #if BUILDFLAG(IS_WIN) 758 | SubmitFrameWithTextureHandle(frame_index, mojo::PlatformHandle(), 759 | gpu::SyncToken()); 760 | -#elif BUILDFLAG(IS_ANDROID) 761 | +#elif BUILDFLAG(IS_ANDROID) || BUILDFLAG(IS_LINUX) 762 | MarkFrameSubmitted(frame_index); 763 | graphics_binding_->Render(); 764 | MaybeCompositeAndSubmit(); 765 | diff --git a/device/vr/public/cpp/features.cc b/device/vr/public/cpp/features.cc 766 | index d1230c3f38..3fdcac0d92 100644 767 | --- a/device/vr/public/cpp/features.cc 768 | +++ b/device/vr/public/cpp/features.cc 769 | @@ -54,8 +54,8 @@ BASE_FEATURE(kEnableCardboard, 770 | // Controls WebXR support for the OpenXR Runtime. 771 | BASE_FEATURE(kOpenXR, 772 | "OpenXR", 773 | - BUILDFLAG(IS_WIN) ? base::FEATURE_ENABLED_BY_DEFAULT 774 | - : base::FEATURE_DISABLED_BY_DEFAULT); 775 | + (BUILDFLAG(IS_WIN) || BUILDFLAG(IS_LINUX)) ? base::FEATURE_ENABLED_BY_DEFAULT 776 | + : base::FEATURE_DISABLED_BY_DEFAULT); 777 | 778 | // Some WebXR features may have been enabled for ARCore, but are not yet ready 779 | // to be plumbed up from the OpenXR backend. This feature provides a mechanism 780 | diff --git a/device/vr/windows/compositor_base.cc b/device/vr/windows/compositor_base.cc 781 | index 198a8b0d28..3d1c4f85e6 100644 782 | --- a/device/vr/windows/compositor_base.cc 783 | +++ b/device/vr/windows/compositor_base.cc 784 | @@ -623,7 +623,7 @@ void XRCompositorCommon::MaybeCompositeAndSubmit() { 785 | } else { 786 | texture_helper_.CleanupNoSubmit(); 787 | } 788 | -#elif BUILDFLAG(IS_ANDROID) 789 | +#elif BUILDFLAG(IS_LINUX) || BUILDFLAG(IS_ANDROID) 790 | bool copy_successful = true; 791 | #endif 792 | 793 | diff --git a/gpu/command_buffer/service/shared_image/external_vk_image_gl_representation.cc b/gpu/command_buffer/service/shared_image/external_vk_image_gl_representation.cc 794 | index 4d17707c2f..df8ea50208 100644 795 | --- a/gpu/command_buffer/service/shared_image/external_vk_image_gl_representation.cc 796 | +++ b/gpu/command_buffer/service/shared_image/external_vk_image_gl_representation.cc 797 | @@ -7,6 +7,7 @@ 798 | #include 799 | 800 | #include "base/logging.h" 801 | +#include "gpu/command_buffer/service/skia_utils.h" 802 | #include "gpu/vulkan/vulkan_util.h" 803 | #include "ui/gl/gl_bindings.h" 804 | 805 | @@ -146,6 +147,13 @@ void ExternalVkImageGLRepresentationShared::EndAccess() { 806 | backing_impl()->context_state()->MarkContextLost(); 807 | return; 808 | } 809 | + 810 | + { // HACK: prevents FD exhaustion due to WebXR semaphores not getting cleaned up in some cases 811 | + GrFlushInfo flush_info = {}; 812 | + gpu::AddVulkanCleanupTaskForSkiaFlush(context_provider(), &flush_info); 813 | + backing_impl()->context_state()->gr_context()->flush(flush_info); 814 | + backing_impl()->context_state()->gr_context()->submit(); 815 | + } 816 | } 817 | backing_impl()->EndAccess(readonly, std::move(external_semaphore), 818 | /*is_gl=*/true); 819 | diff --git a/third_party/openxr/BUILD.gn b/third_party/openxr/BUILD.gn 820 | index ed731bd432..b7c3568c68 100644 821 | --- a/third_party/openxr/BUILD.gn 822 | +++ b/third_party/openxr/BUILD.gn 823 | @@ -166,6 +166,13 @@ if (enable_openxr) { 824 | "XR_USE_PLATFORM_WIN32", 825 | "XR_USE_GRAPHICS_API_D3D11", 826 | ] 827 | + } else if (is_linux) { 828 | + defines = [ 829 | + "XRLOADER_DISABLE_EXCEPTION_HANDLING", 830 | + "XR_OS_LINUX", 831 | + "XR_USE_GRAPHICS_API_VULKAN", 832 | + "HAVE_SECURE_GETENV", 833 | + ] 834 | } else { 835 | defines = [ 836 | "XRLOADER_DISABLE_EXCEPTION_HANDLING", 837 | diff --git a/ui/gfx/linux/gbm_wrapper.cc b/ui/gfx/linux/gbm_wrapper.cc 838 | index c1bf39fc53..70b70d0b44 100644 839 | --- a/ui/gfx/linux/gbm_wrapper.cc 840 | +++ b/ui/gfx/linux/gbm_wrapper.cc 841 | @@ -392,8 +392,16 @@ class Device final : public ui::GbmDevice { 842 | struct gbm_bo* bo = 843 | gbm_bo_import(device_, GBM_BO_IMPORT_FD_MODIFIER, &fd_data, gbm_flags); 844 | if (!bo) { 845 | - LOG(ERROR) << "nullptr returned from gbm_bo_import"; 846 | - return nullptr; 847 | + if (gbm_flags & GBM_BO_USE_SCANOUT) { 848 | + gbm_flags &= ~GBM_BO_USE_SCANOUT; 849 | + bo = gbm_bo_import(device_, GBM_BO_IMPORT_FD_MODIFIER, &fd_data, 850 | + gbm_flags); 851 | + } 852 | + 853 | + if (!bo) { 854 | + LOG(ERROR) << "nullptr returned from gbm_bo_import"; 855 | + return nullptr; 856 | + } 857 | } 858 | 859 | return std::make_unique(bo, format, gbm_flags, handle.modifier, 860 | -------------------------------------------------------------------------------- /gecko/README.md: -------------------------------------------------------------------------------- 1 | # Firefox 2 | This folder contains various patch files for firefox to allow WebXR content to work under Linux. It is more a hack than a proper implementation, and is only tested under X11, using a Valve Index as HMD. 3 | 4 | ![Firefox running 'Hello WebXR' under Linux with SteamVR](./screenshot.png) 5 | 6 | # How to compile 7 | In order to apply and compile this patch, it's important that you are able to compile Firefox yourself. For this follow the steps over at: https://firefox-source-docs.mozilla.org/setup/linux_build.html 8 | 9 | > **Note:** don't make use of 'Artifact Mode' as the patch modifies the C++ code 10 | 11 | Once you have the Firefox code base locally, apply **one** of the patch files. The following command is for the `webxr-linux-dmabuf.patch` file (recommended): 12 | 13 | ``` 14 | hg patch --no-commit path/to/webxr-linux-dmabuf.patch 15 | ``` 16 | 17 | Once applied, build using `./mach build` and if everything goes well, you should now have a built version with WebXR support. Run it using `./mach run`. There are some settings you will have to enable in `about:config`: 18 | | Preference name | Value | 19 | |-----------------|-------| 20 | | `dom.vr.enabled` | true | 21 | | `dom.vr.openvr.enabled` | true | 22 | | `dom.vr.webxr.enabled` | true | 23 | 24 | After configuring the above preferences, be sure to restart the firefox instance, as some of these only take effect on startup of Firefox. 25 | 26 | You are now ready to go and visit any WebXR experience on the web. Examples: 27 | * https://mixedreality.mozilla.org/hello-webxr/ 28 | * https://immersive-web.github.io/webxr-samples/report/ 29 | 30 | # How it works 31 | Firefox already contains all the relevant logic for handling the JS side of the WebXR specification and supports the OpenVR (Steam specific) runtime. Since SteamVR also works under Linux, the only missing part is actually submitting the frame data to the VRCompositor. 32 | 33 | ## The DMABUF version (`webxr-linux-dmabuf.patch`) 34 | This patch let's the WebGL rendering use EGL and creates its own (E)GL context on the compositor side. The textures are shared between the two using DMABuf. In theory DMABuf also allows for it to take place over IPC, but the way this patch is written, that isn't possible (yet). 35 | 36 | ## The GLX version (`webxr-linux-glx.patch`) 37 | The biggest drawback of the initial version was the need to copy the framebuffer contents to RAM and reupload it in a different GL context. This new patch actually makes use of the fact that Firefox does support using GLX for WebGL rendering. This means that the framebuffer textures it produces can be directly consumed by OpenVR. This removes the bottleneck of the initial version and grealy improves performance. 38 | 39 | With this patch practically all WebXR experiences performed at a stable 90fps at >100% render resolution on my machine. 40 | 41 | > **Note:** when using the `webxr-linux-glx.patch` it's important that EGL isn't used. So in addition to the preferences mentioned above, the `gfx.x11-egl.force-disabled` preference should also be set to `true`. 42 | 43 | 44 | ## The initial version (`webxr-linux.patch`) 45 | The initial version let Firefox use EGL (OpenGL ES 2.0) for WebGL rendering. OpenVR doesn't support this, but only supports 'Desktop GL'. 46 | 47 | To workaround this, the `webxr-linux.patch` basically makes use of `glReadPixels` to load the raw pixel data from the OpenGL ES framebuffer and reuploads it as a new texture in a GLX context _every frame_. As a result, this solution isn't very performant, but when reducing the render resolution it's possible to hit 90HZ with relative ease. -------------------------------------------------------------------------------- /gecko/screenshot.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mrxz/webxr-linux/e5c2290d61913726ff6c8c56c73cc36fe08a5cd3/gecko/screenshot.png -------------------------------------------------------------------------------- /gecko/webxr-linux-dmabuf.patch: -------------------------------------------------------------------------------- 1 | diff --git a/gfx/vr/VRManager.cpp b/gfx/vr/VRManager.cpp 2 | --- a/gfx/vr/VRManager.cpp 3 | +++ b/gfx/vr/VRManager.cpp 4 | @@ -46,6 +46,8 @@ 5 | # include "GeckoVRManager.h" 6 | # include "mozilla/java/GeckoSurfaceTextureWrappers.h" 7 | # include "mozilla/layers/CompositorThread.h" 8 | +#elif defined(XP_LINUX) 9 | +# include "DMABufSurface.h" 10 | #endif // defined(MOZ_WIDGET_ANDROID) 11 | 12 | using namespace mozilla; 13 | @@ -1378,7 +1380,8 @@ bool VRManager::SubmitFrame(const layers 14 | if (mState != VRManagerState::Active) { 15 | return false; 16 | } 17 | -#if defined(XP_WIN) || defined(XP_MACOSX) || defined(MOZ_WIDGET_ANDROID) 18 | +#if defined(XP_WIN) || defined(XP_MACOSX) || defined(MOZ_WIDGET_ANDROID) || \ 19 | + defined(XP_LINUX) 20 | MOZ_ASSERT(mBrowserState.layerState[0].type == 21 | VRLayerType::LayerType_Stereo_Immersive); 22 | VRLayer_Stereo_Immersive& layer = 23 | @@ -1427,6 +1430,16 @@ bool VRManager::SubmitFrame(const layers 24 | layer.textureSize.width = desc.size().width; 25 | layer.textureSize.height = desc.size().height; 26 | } break; 27 | +# elif defined(XP_LINUX) 28 | + case SurfaceDescriptor::TSurfaceDescriptorDMABuf: { 29 | + const SurfaceDescriptorDMABuf& desc = 30 | + aTexture.get_SurfaceDescriptorDMABuf(); 31 | + layer.textureType = VRLayerTextureType::LayerTextureType_DMABuf; 32 | + DMABufSurface* surface = DMABufSurface::CreateDMABufSurface(desc).take(); 33 | + layer.textureHandle = (void*)surface; 34 | + layer.textureSize.width = surface->GetWidth(); 35 | + layer.textureSize.height = surface->GetHeight(); 36 | + } break; 37 | # endif 38 | default: { 39 | MOZ_ASSERT(false); 40 | @@ -1489,7 +1502,7 @@ void VRManager::SubmitFrameInternal(cons 41 | mCurrentSubmitTask = nullptr; 42 | } 43 | 44 | -#if defined(XP_WIN) || defined(XP_MACOSX) 45 | +#if defined(XP_WIN) || defined(XP_MACOSX) || defined(XP_LINUX) 46 | 47 | /** 48 | * Trigger the next VSync immediately after we are successfully 49 | diff --git a/gfx/vr/VRShMem.cpp b/gfx/vr/VRShMem.cpp 50 | --- a/gfx/vr/VRShMem.cpp 51 | +++ b/gfx/vr/VRShMem.cpp 52 | @@ -247,12 +247,20 @@ void VRShMem::CreateShMemForAndroid() { 53 | 54 | void VRShMem::ClearShMem() { 55 | if (mExternalShmem != nullptr) { 56 | + // Note: If the generation is reset, the communication will fail. 57 | + // The other instance of VRShMem would still think to have read 58 | + // generation 1, meaning the first (important) update is ignored. 59 | + long generation = mExternalShmem->geckoGenerationA; 60 | #ifdef MOZILLA_INTERNAL_API 61 | // VRExternalShmem is asserted to be POD 62 | mExternalShmem->Clear(); 63 | #else 64 | memset((void*)mExternalShmem, 0, sizeof(VRExternalShmem)); 65 | #endif 66 | + 67 | + // Continue counting generations where we left off. 68 | + mExternalShmem->geckoGenerationA = mExternalShmem->geckoGenerationB = 69 | + generation + 1; 70 | } 71 | } 72 | 73 | @@ -418,11 +426,12 @@ void VRShMem::PushBrowserState(VRBrowser 74 | status = lock.GetStatus(); 75 | # endif // defined(XP_WIN) 76 | 77 | - if (status) { 78 | + if (status && pthread_mutex_lock((pthread_mutex_t*)&(mExternalShmem->geckoMutex)) == 0) { 79 | mExternalShmem->geckoGenerationA = mExternalShmem->geckoGenerationA + 1; 80 | memcpy((void*)&(mExternalShmem->geckoState), (void*)&aBrowserState, 81 | sizeof(VRBrowserState)); 82 | mExternalShmem->geckoGenerationB = mExternalShmem->geckoGenerationB + 1; 83 | + pthread_mutex_unlock((pthread_mutex_t*)&(mExternalShmem->geckoMutex)); 84 | } 85 | #endif // defined(MOZ_WIDGET_ANDROID) 86 | } 87 | @@ -462,17 +471,15 @@ void VRShMem::PullBrowserState(mozilla:: 88 | } 89 | # endif // defined(XP_WIN) 90 | if (status) { 91 | - VRExternalShmem tmp; 92 | if (mExternalShmem->geckoGenerationA != mBrowserGeneration) { 93 | // TODO - (void *) cast removes volatile semantics. 94 | // The memcpy is not likely to be optimized out, but is theoretically 95 | // possible. Suggest refactoring to either explicitly enforce memory 96 | // order or to use locks. 97 | - memcpy(&tmp, (void*)mExternalShmem, sizeof(VRExternalShmem)); 98 | - if (tmp.geckoGenerationA == tmp.geckoGenerationB && 99 | - tmp.geckoGenerationA != 0) { 100 | - memcpy(&aState, &tmp.geckoState, sizeof(VRBrowserState)); 101 | - mBrowserGeneration = tmp.geckoGenerationA; 102 | + if (pthread_mutex_lock((pthread_mutex_t*)&(mExternalShmem->geckoMutex)) == 0) { 103 | + memcpy(&aState, (void*)&(mExternalShmem->geckoState), sizeof(VRBrowserState)); 104 | + mBrowserGeneration = mExternalShmem->geckoGenerationA; 105 | + pthread_mutex_unlock((pthread_mutex_t*)&(mExternalShmem->geckoMutex)); 106 | } 107 | } 108 | } 109 | diff --git a/gfx/vr/external_api/moz_external_vr.h b/gfx/vr/external_api/moz_external_vr.h 110 | --- a/gfx/vr/external_api/moz_external_vr.h 111 | +++ b/gfx/vr/external_api/moz_external_vr.h 112 | @@ -452,7 +452,8 @@ enum class VRLayerTextureType : uint16_t 113 | LayerTextureType_None = 0, 114 | LayerTextureType_D3D10SurfaceDescriptor = 1, 115 | LayerTextureType_MacIOSurface = 2, 116 | - LayerTextureType_GeckoSurfaceTexture = 3 117 | + LayerTextureType_GeckoSurfaceTexture = 3, 118 | + LayerTextureType_DMABuf = 4 119 | }; 120 | 121 | struct VRLayer_2D_Content { 122 | @@ -619,6 +620,7 @@ struct VRExternalShmem { 123 | pthread_cond_t servoCond; 124 | #else 125 | int64_t generationA; 126 | + pthread_mutex_t geckoMutex; 127 | #endif // defined(__ANDROID__) 128 | VRSystemState state; 129 | #if !defined(__ANDROID__) 130 | diff --git a/gfx/vr/ipc/VRLayerChild.cpp b/gfx/vr/ipc/VRLayerChild.cpp 131 | --- a/gfx/vr/ipc/VRLayerChild.cpp 132 | +++ b/gfx/vr/ipc/VRLayerChild.cpp 133 | @@ -92,6 +92,8 @@ void VRLayerChild::SubmitFrame(const VRD 134 | if (kIsAndroid && StaticPrefs::webgl_enable_surface_texture()) { 135 | texType = layers::TextureType::AndroidNativeWindow; 136 | } 137 | + // HACK: always pick DMABUF 138 | + texType = layers::TextureType::DMABUF; 139 | 140 | webgl->Present(mFramebuffer, texType, true); 141 | mThisFrameTextureDesc = webgl->GetFrontBuffer(mFramebuffer, true); 142 | diff --git a/gfx/vr/moz.build b/gfx/vr/moz.build 143 | --- a/gfx/vr/moz.build 144 | +++ b/gfx/vr/moz.build 145 | @@ -71,6 +71,9 @@ else: 146 | "VRServiceHost.cpp", 147 | ] 148 | 149 | +if CONFIG["OS_TARGET"] == "Linux": 150 | + LOCAL_INCLUDES += ["/widget/gtk"] 151 | + 152 | IPDL_SOURCES = [ 153 | "ipc/PVR.ipdl", 154 | "ipc/PVRGPU.ipdl", 155 | diff --git a/gfx/vr/service/OSVRSession.cpp b/gfx/vr/service/OSVRSession.cpp 156 | --- a/gfx/vr/service/OSVRSession.cpp 157 | +++ b/gfx/vr/service/OSVRSession.cpp 158 | @@ -496,7 +496,7 @@ bool OSVRSession::SubmitFrame( 159 | return false; 160 | // TODO Implement 161 | } 162 | -#elif defined(XP_MACOSX) 163 | +#elif defined(XP_MACOSX) || defined(XP_LINUX) 164 | bool OSVRSession::SubmitFrame( 165 | const mozilla::gfx::VRLayer_Stereo_Immersive& aLayer, 166 | const VRLayerTextureHandle& aTexture) { 167 | diff --git a/gfx/vr/service/OSVRSession.h b/gfx/vr/service/OSVRSession.h 168 | --- a/gfx/vr/service/OSVRSession.h 169 | +++ b/gfx/vr/service/OSVRSession.h 170 | @@ -44,7 +44,7 @@ class OSVRSession : public VRSession { 171 | #if defined(XP_WIN) 172 | bool SubmitFrame(const mozilla::gfx::VRLayer_Stereo_Immersive& aLayer, 173 | ID3D11Texture2D* aTexture) override; 174 | -#elif defined(XP_MACOSX) 175 | +#elif defined(XP_MACOSX) || defined(XP_LINUX) 176 | bool SubmitFrame(const mozilla::gfx::VRLayer_Stereo_Immersive& aLayer, 177 | const VRLayerTextureHandle& aTexture) override; 178 | #endif 179 | diff --git a/gfx/vr/service/OpenVRSession.cpp b/gfx/vr/service/OpenVRSession.cpp 180 | --- a/gfx/vr/service/OpenVRSession.cpp 181 | +++ b/gfx/vr/service/OpenVRSession.cpp 182 | @@ -1273,6 +1273,13 @@ bool OpenVRSession::SubmitFrame( 183 | return SubmitFrame(aTexture, ::vr::ETextureType::TextureType_IOSurface, 184 | aLayer.leftEyeRect, aLayer.rightEyeRect); 185 | } 186 | +#elif defined(XP_LINUX) 187 | +bool OpenVRSession::SubmitFrame( 188 | + const mozilla::gfx::VRLayer_Stereo_Immersive& aLayer, 189 | + const VRLayerTextureHandle& aTexture) { 190 | + return SubmitFrame(aTexture, ::vr::ETextureType::TextureType_OpenGL, 191 | + aLayer.leftEyeRect, aLayer.rightEyeRect); 192 | +} 193 | #endif 194 | 195 | bool OpenVRSession::SubmitFrame(const VRLayerTextureHandle& aTextureHandle, 196 | @@ -1292,32 +1299,32 @@ bool OpenVRSession::SubmitFrame(const VR 197 | 198 | CFTypeRefPtr ioSurface = surf->GetIOSurfaceRef(); 199 | tex.handle = (void*)ioSurface.get(); 200 | -#else 201 | +#endif 202 | + tex.eColorSpace = ::vr::EColorSpace::ColorSpace_Gamma; 203 | + tex.eType = aTextureType; 204 | + 205 | tex.handle = aTextureHandle; 206 | -#endif 207 | - tex.eType = aTextureType; 208 | - tex.eColorSpace = ::vr::EColorSpace::ColorSpace_Auto; 209 | 210 | ::vr::VRTextureBounds_t bounds; 211 | bounds.uMin = aLeftEyeRect.x; 212 | - bounds.vMin = 1.0 - aLeftEyeRect.y; 213 | + bounds.vMin = 1.0 - (aLeftEyeRect.y + aLeftEyeRect.height); 214 | bounds.uMax = aLeftEyeRect.x + aLeftEyeRect.width; 215 | - bounds.vMax = 1.0 - (aLeftEyeRect.y + aLeftEyeRect.height); 216 | + bounds.vMax = 1.0 - aLeftEyeRect.y; 217 | 218 | ::vr::EVRCompositorError err; 219 | err = mVRCompositor->Submit(::vr::EVREye::Eye_Left, &tex, &bounds); 220 | if (err != ::vr::EVRCompositorError::VRCompositorError_None) { 221 | - printf_stderr("OpenVR Compositor Submit() failed.\n"); 222 | + printf_stderr("OpenVR Compositor Submit() failed: err=%d.\n", err); 223 | } 224 | 225 | bounds.uMin = aRightEyeRect.x; 226 | - bounds.vMin = 1.0 - aRightEyeRect.y; 227 | + bounds.vMin = 1.0 - (aRightEyeRect.y + aRightEyeRect.height); 228 | bounds.uMax = aRightEyeRect.x + aRightEyeRect.width; 229 | - bounds.vMax = 1.0 - (aRightEyeRect.y + aRightEyeRect.height); 230 | + bounds.vMax = 1.0 - aRightEyeRect.y; 231 | 232 | err = mVRCompositor->Submit(::vr::EVREye::Eye_Right, &tex, &bounds); 233 | if (err != ::vr::EVRCompositorError::VRCompositorError_None) { 234 | - printf_stderr("OpenVR Compositor Submit() failed.\n"); 235 | + printf_stderr("OpenVR Compositor Submit() failed: err=%d\n", err); 236 | } 237 | 238 | mVRCompositor->PostPresentHandoff(); 239 | diff --git a/gfx/vr/service/OpenVRSession.h b/gfx/vr/service/OpenVRSession.h 240 | --- a/gfx/vr/service/OpenVRSession.h 241 | +++ b/gfx/vr/service/OpenVRSession.h 242 | @@ -58,6 +58,9 @@ class OpenVRSession : public VRSession { 243 | #elif defined(XP_MACOSX) 244 | bool SubmitFrame(const mozilla::gfx::VRLayer_Stereo_Immersive& aLayer, 245 | const VRLayerTextureHandle& aTexture) override; 246 | +#elif defined(XP_LINUX) 247 | + bool SubmitFrame(const mozilla::gfx::VRLayer_Stereo_Immersive& aLayer, 248 | + const VRLayerTextureHandle& aTexture) override; 249 | #endif 250 | 251 | private: 252 | diff --git a/gfx/vr/service/PuppetSession.cpp b/gfx/vr/service/PuppetSession.cpp 253 | --- a/gfx/vr/service/PuppetSession.cpp 254 | +++ b/gfx/vr/service/PuppetSession.cpp 255 | @@ -89,7 +89,7 @@ bool PuppetSession::SubmitFrame( 256 | ID3D11Texture2D* aTexture) { 257 | return VRPuppetCommandBuffer::Get().SubmitFrame(); 258 | } 259 | -#elif defined(XP_MACOSX) 260 | +#elif defined(XP_MACOSX) || defined(XP_LINUX) 261 | bool PuppetSession::SubmitFrame( 262 | const mozilla::gfx::VRLayer_Stereo_Immersive& aLayer, 263 | const VRLayerTextureHandle& aTexture) { 264 | diff --git a/gfx/vr/service/PuppetSession.h b/gfx/vr/service/PuppetSession.h 265 | --- a/gfx/vr/service/PuppetSession.h 266 | +++ b/gfx/vr/service/PuppetSession.h 267 | @@ -41,7 +41,7 @@ class PuppetSession : public VRSession { 268 | #if defined(XP_WIN) 269 | bool SubmitFrame(const mozilla::gfx::VRLayer_Stereo_Immersive& aLayer, 270 | ID3D11Texture2D* aTexture) override; 271 | -#elif defined(XP_MACOSX) 272 | +#elif defined(XP_MACOSX) || defined(XP_LINUX) 273 | bool SubmitFrame(const mozilla::gfx::VRLayer_Stereo_Immersive& aLayer, 274 | const VRLayerTextureHandle& aTexture) override; 275 | #endif 276 | diff --git a/gfx/vr/service/VRSession.cpp b/gfx/vr/service/VRSession.cpp 277 | --- a/gfx/vr/service/VRSession.cpp 278 | +++ b/gfx/vr/service/VRSession.cpp 279 | @@ -14,6 +14,10 @@ 280 | # include 281 | #endif // defined(XP_WIN) 282 | 283 | +#if defined(XP_LINUX) 284 | +# include "DMABufSurface.h" 285 | +#endif 286 | + 287 | #if defined(MOZILLA_INTERNAL_API) 288 | # if defined(XP_WIN) 289 | # include "mozilla/gfx/Logging.h" 290 | @@ -152,6 +156,30 @@ bool VRSession::SubmitFrame( 291 | return SubmitFrame(aLayer, aLayer.textureHandle); 292 | } 293 | 294 | +#elif defined(XP_LINUX) 295 | + 296 | + if (aLayer.textureType == 297 | + VRLayerTextureType::LayerTextureType_DMABuf) { 298 | + if (!mGLContext) { 299 | + nsCString discard; 300 | + mGLContext = gl::GLContextProviderEGL::CreateHeadless({}, &discard); 301 | + } 302 | + 303 | + if (!mGLContext || !mGLContext->MakeCurrent(true)) { 304 | + return false; 305 | + } 306 | + 307 | + DMABufSurface* surface = (DMABufSurface*) aLayer.textureHandle; 308 | + surface->CreateTexture(mGLContext); 309 | + 310 | + bool result = SubmitFrame(aLayer, (VRLayerTextureHandle) surface->GetTexture()); 311 | + 312 | + surface->ReleaseTextures(); 313 | + surface->ReleaseSurface(); 314 | + 315 | + return result; 316 | + } 317 | + 318 | #endif 319 | 320 | return false; 321 | diff --git a/gfx/vr/service/VRSession.h b/gfx/vr/service/VRSession.h 322 | --- a/gfx/vr/service/VRSession.h 323 | +++ b/gfx/vr/service/VRSession.h 324 | @@ -13,6 +13,9 @@ 325 | # include 326 | #elif defined(XP_MACOSX) 327 | class MacIOSurface; 328 | +#elif defined(XP_LINUX) 329 | +# include "GLContext.h" 330 | +# include "GLContextProvider.h" 331 | #endif 332 | 333 | namespace mozilla { 334 | @@ -80,10 +83,13 @@ class VRSession { 335 | ID3D11DeviceContext1* mContext; 336 | ID3DDeviceContextState* mDeviceContextState; 337 | 338 | -#elif defined(XP_MACOSX) 339 | +#elif defined(XP_MACOSX) || defined(XP_LINUX) 340 | virtual bool SubmitFrame(const mozilla::gfx::VRLayer_Stereo_Immersive& aLayer, 341 | const VRLayerTextureHandle& aTexture) = 0; 342 | #endif 343 | +#if defined(XP_LINUX) 344 | + RefPtr mGLContext; 345 | +#endif 346 | void SetControllerSelectionAndSqueezeFrameId( 347 | VRControllerState& controllerState, uint64_t aFrameId); 348 | }; 349 | diff --git a/gfx/vr/service/moz.build b/gfx/vr/service/moz.build 350 | --- a/gfx/vr/service/moz.build 351 | +++ b/gfx/vr/service/moz.build 352 | @@ -35,6 +35,8 @@ if CONFIG["TARGET_OS"] in ("WINNT", "OSX 353 | "openvr", 354 | ] 355 | LOCAL_INCLUDES += ["/dom/base", "/gfx/layers/d3d11"] 356 | + if CONFIG["OS_TARGET"] == "Linux": 357 | + LOCAL_INCLUDES += ["/widget/gtk"] 358 | 359 | # OpenVRSession includes MacIOSurface.h which includes Mac headers 360 | # which define Size and Points types in the root namespace that 361 | @@ -48,4 +50,8 @@ if CONFIG["TARGET_OS"] in ("WINNT", "OSX 362 | "OpenVRViveMapper.cpp", 363 | ] 364 | 365 | +if CONFIG["MOZ_WIDGET_TOOLKIT"] == "gtk": 366 | + CXXFLAGS += CONFIG["MOZ_GTK3_CFLAGS"] 367 | + CFLAGS += CONFIG["MOZ_GTK3_CFLAGS"] 368 | + 369 | FINAL_LIBRARY = "xul" 370 | -------------------------------------------------------------------------------- /gecko/webxr-linux-glx.patch: -------------------------------------------------------------------------------- 1 | diff --git a/dom/canvas/WebGLContext.cpp b/dom/canvas/WebGLContext.cpp 2 | --- a/dom/canvas/WebGLContext.cpp 3 | +++ b/dom/canvas/WebGLContext.cpp 4 | @@ -1031,6 +1031,11 @@ Maybe WebGLCo 5 | const auto& front = swapChain->FrontBuffer(); 6 | if (!front) return {}; 7 | 8 | + // HACK: Only for WebVR do we want to return the surface descriptor. 9 | + // In all other cases, we want to old behaviour of returning Nothing. 10 | + if(!webvr) { 11 | + return Nothing(); 12 | + } 13 | return front->ToSurfaceDescriptor(); 14 | } 15 | 16 | diff --git a/gfx/gl/SharedSurfaceGL.cpp b/gfx/gl/SharedSurfaceGL.cpp 17 | --- a/gfx/gl/SharedSurfaceGL.cpp 18 | +++ b/gfx/gl/SharedSurfaceGL.cpp 19 | @@ -29,7 +29,9 @@ SharedSurface_Basic::SharedSurface_Basic 20 | : SharedSurface(desc, std::move(fb)) {} 21 | 22 | Maybe SharedSurface_Basic::ToSurfaceDescriptor() { 23 | - return Nothing(); 24 | + // Smuggle textureId and GLContext (as fence) 25 | + return Some(layers::SurfaceDescriptorSharedGLTexture( 26 | + mFb->ColorTex(), -1, (uintptr_t)mDesc.gl.get(), mFb->mSize, true)); 27 | } 28 | 29 | //////////////////////////////////////////////////////////////////////// 30 | diff --git a/gfx/vr/VRManager.cpp b/gfx/vr/VRManager.cpp 31 | --- a/gfx/vr/VRManager.cpp 32 | +++ b/gfx/vr/VRManager.cpp 33 | @@ -1378,7 +1378,8 @@ bool VRManager::SubmitFrame(const layers 34 | if (mState != VRManagerState::Active) { 35 | return false; 36 | } 37 | -#if defined(XP_WIN) || defined(XP_MACOSX) || defined(MOZ_WIDGET_ANDROID) 38 | +#if defined(XP_WIN) || defined(XP_MACOSX) || defined(MOZ_WIDGET_ANDROID) || \ 39 | + defined(XP_LINUX) 40 | MOZ_ASSERT(mBrowserState.layerState[0].type == 41 | VRLayerType::LayerType_Stereo_Immersive); 42 | VRLayer_Stereo_Immersive& layer = 43 | @@ -1426,6 +1427,17 @@ bool VRManager::SubmitFrame(const layers 44 | layer.textureSize.width = desc.size().width; 45 | layer.textureSize.height = desc.size().height; 46 | } break; 47 | +# elif defined(XP_LINUX) 48 | + case SurfaceDescriptor::TSurfaceDescriptorSharedGLTexture: { 49 | + const SurfaceDescriptorSharedGLTexture& desc = 50 | + aTexture.get_SurfaceDescriptorSharedGLTexture(); 51 | + layer.textureType = VRLayerTextureType::LayerTextureType_SharedGLTexture; 52 | + // HACK: Retrieve the pointer to the GLContext from the fence field. 53 | + layer.gl = (gl::GLContext*)desc.fence(); 54 | + layer.textureHandle = (void*)desc.texture(); 55 | + layer.textureSize.width = desc.size().width; 56 | + layer.textureSize.height = desc.size().height; 57 | + } break; 58 | # endif 59 | default: { 60 | MOZ_ASSERT(false); 61 | @@ -1488,7 +1500,7 @@ void VRManager::SubmitFrameInternal(cons 62 | mCurrentSubmitTask = nullptr; 63 | } 64 | 65 | -#if defined(XP_WIN) || defined(XP_MACOSX) 66 | +#if defined(XP_WIN) || defined(XP_MACOSX) || defined(XP_LINUX) 67 | 68 | /** 69 | * Trigger the next VSync immediately after we are successfully 70 | diff --git a/gfx/vr/VRShMem.cpp b/gfx/vr/VRShMem.cpp 71 | --- a/gfx/vr/VRShMem.cpp 72 | +++ b/gfx/vr/VRShMem.cpp 73 | @@ -247,12 +247,20 @@ void VRShMem::CreateShMemForAndroid() { 74 | 75 | void VRShMem::ClearShMem() { 76 | if (mExternalShmem != nullptr) { 77 | + // Note: If the generation is reset, the communication will fail. 78 | + // The other instance of VRShMem would still think to have read 79 | + // generation 1, meaning the first (important) update is ignored. 80 | + long generation = mExternalShmem->geckoGenerationA; 81 | #ifdef MOZILLA_INTERNAL_API 82 | // VRExternalShmem is asserted to be POD 83 | mExternalShmem->Clear(); 84 | #else 85 | memset((void*)mExternalShmem, 0, sizeof(VRExternalShmem)); 86 | #endif 87 | + 88 | + // Continue counting generations where we left off. 89 | + mExternalShmem->geckoGenerationA = mExternalShmem->geckoGenerationB = 90 | + generation + 1; 91 | } 92 | } 93 | 94 | diff --git a/gfx/vr/external_api/moz_external_vr.h b/gfx/vr/external_api/moz_external_vr.h 95 | --- a/gfx/vr/external_api/moz_external_vr.h 96 | +++ b/gfx/vr/external_api/moz_external_vr.h 97 | @@ -451,7 +451,8 @@ enum class VRLayerTextureType : uint16_t 98 | LayerTextureType_None = 0, 99 | LayerTextureType_D3D10SurfaceDescriptor = 1, 100 | LayerTextureType_MacIOSurface = 2, 101 | - LayerTextureType_GeckoSurfaceTexture = 3 102 | + LayerTextureType_GeckoSurfaceTexture = 3, 103 | + LayerTextureType_SharedGLTexture = 4 104 | }; 105 | 106 | struct VRLayer_2D_Content { 107 | @@ -468,6 +469,7 @@ struct VRLayer_Stereo_Immersive { 108 | VRLayerEyeRect leftEyeRect; 109 | VRLayerEyeRect rightEyeRect; 110 | IntSize_POD textureSize; 111 | + void* gl; 112 | }; 113 | 114 | struct VRLayerState { 115 | diff --git a/gfx/vr/service/OSVRSession.cpp b/gfx/vr/service/OSVRSession.cpp 116 | --- a/gfx/vr/service/OSVRSession.cpp 117 | +++ b/gfx/vr/service/OSVRSession.cpp 118 | @@ -497,7 +497,7 @@ bool OSVRSession::SubmitFrame( 119 | return false; 120 | // TODO Implement 121 | } 122 | -#elif defined(XP_MACOSX) 123 | +#elif defined(XP_MACOSX) || defined(XP_LINUX) 124 | bool OSVRSession::SubmitFrame( 125 | const mozilla::gfx::VRLayer_Stereo_Immersive& aLayer, 126 | const VRLayerTextureHandle& aTexture) { 127 | diff --git a/gfx/vr/service/OSVRSession.h b/gfx/vr/service/OSVRSession.h 128 | --- a/gfx/vr/service/OSVRSession.h 129 | +++ b/gfx/vr/service/OSVRSession.h 130 | @@ -44,7 +44,7 @@ class OSVRSession : public VRSession { 131 | #if defined(XP_WIN) 132 | bool SubmitFrame(const mozilla::gfx::VRLayer_Stereo_Immersive& aLayer, 133 | ID3D11Texture2D* aTexture) override; 134 | -#elif defined(XP_MACOSX) 135 | +#elif defined(XP_MACOSX) || defined(XP_LINUX) 136 | bool SubmitFrame(const mozilla::gfx::VRLayer_Stereo_Immersive& aLayer, 137 | const VRLayerTextureHandle& aTexture) override; 138 | #endif 139 | diff --git a/gfx/vr/service/OpenVRSession.cpp b/gfx/vr/service/OpenVRSession.cpp 140 | --- a/gfx/vr/service/OpenVRSession.cpp 141 | +++ b/gfx/vr/service/OpenVRSession.cpp 142 | @@ -18,6 +18,8 @@ 143 | # include "mozilla/gfx/DeviceManagerDx.h" 144 | #elif defined(XP_MACOSX) 145 | # include "mozilla/gfx/MacIOSurface.h" 146 | +#elif defined(XP_LINUX) 147 | +# include "GLContextProvider.h" 148 | #endif 149 | 150 | #if !defined(XP_WIN) 151 | @@ -1284,6 +1286,14 @@ bool OpenVRSession::SubmitFrame( 152 | return SubmitFrame(aTexture, ::vr::ETextureType::TextureType_IOSurface, 153 | aLayer.leftEyeRect, aLayer.rightEyeRect); 154 | } 155 | +#elif defined(XP_LINUX) 156 | +bool OpenVRSession::SubmitFrame( 157 | + const mozilla::gfx::VRLayer_Stereo_Immersive& aLayer, 158 | + const VRLayerTextureHandle& aTexture) { 159 | + mGLContext = (gl::GLContext*)aLayer.gl; 160 | + return SubmitFrame(aTexture, ::vr::ETextureType::TextureType_OpenGL, 161 | + aLayer.leftEyeRect, aLayer.rightEyeRect); 162 | +} 163 | #endif 164 | 165 | bool OpenVRSession::SubmitFrame(const VRLayerTextureHandle& aTextureHandle, 166 | @@ -1303,32 +1313,33 @@ bool OpenVRSession::SubmitFrame(const VR 167 | 168 | CFTypeRefPtr ioSurface = surf->GetIOSurfaceRef(); 169 | tex.handle = (void*)ioSurface.get(); 170 | -#else 171 | +#endif 172 | + tex.eColorSpace = ::vr::EColorSpace::ColorSpace_Gamma; 173 | + tex.eType = aTextureType; 174 | + 175 | + mGLContext->MakeCurrent(true); 176 | tex.handle = aTextureHandle; 177 | -#endif 178 | - tex.eType = aTextureType; 179 | - tex.eColorSpace = ::vr::EColorSpace::ColorSpace_Auto; 180 | 181 | ::vr::VRTextureBounds_t bounds; 182 | bounds.uMin = aLeftEyeRect.x; 183 | - bounds.vMin = 1.0 - aLeftEyeRect.y; 184 | + bounds.vMin = 1.0 - (aLeftEyeRect.y + aLeftEyeRect.height); 185 | bounds.uMax = aLeftEyeRect.x + aLeftEyeRect.width; 186 | - bounds.vMax = 1.0 - (aLeftEyeRect.y + aLeftEyeRect.height); 187 | + bounds.vMax = 1.0 - aLeftEyeRect.y; 188 | 189 | ::vr::EVRCompositorError err; 190 | err = mVRCompositor->Submit(::vr::EVREye::Eye_Left, &tex, &bounds); 191 | if (err != ::vr::EVRCompositorError::VRCompositorError_None) { 192 | - printf_stderr("OpenVR Compositor Submit() failed.\n"); 193 | + printf_stderr("OpenVR Compositor Submit() failed: err=%d.\n", err); 194 | } 195 | 196 | bounds.uMin = aRightEyeRect.x; 197 | - bounds.vMin = 1.0 - aRightEyeRect.y; 198 | + bounds.vMin = 1.0 - (aRightEyeRect.y + aRightEyeRect.height); 199 | bounds.uMax = aRightEyeRect.x + aRightEyeRect.width; 200 | - bounds.vMax = 1.0 - (aRightEyeRect.y + aRightEyeRect.height); 201 | + bounds.vMax = 1.0 - aRightEyeRect.y; 202 | 203 | err = mVRCompositor->Submit(::vr::EVREye::Eye_Right, &tex, &bounds); 204 | if (err != ::vr::EVRCompositorError::VRCompositorError_None) { 205 | - printf_stderr("OpenVR Compositor Submit() failed.\n"); 206 | + printf_stderr("OpenVR Compositor Submit() failed: err=%d\n", err); 207 | } 208 | 209 | mVRCompositor->PostPresentHandoff(); 210 | diff --git a/gfx/vr/service/OpenVRSession.h b/gfx/vr/service/OpenVRSession.h 211 | --- a/gfx/vr/service/OpenVRSession.h 212 | +++ b/gfx/vr/service/OpenVRSession.h 213 | @@ -16,6 +16,8 @@ 214 | 215 | #if defined(XP_WIN) 216 | # include 217 | +#elif defined(XP_LINUX) 218 | +# include "GLContext.h" 219 | #endif 220 | class nsITimer; 221 | 222 | @@ -58,6 +60,9 @@ class OpenVRSession : public VRSession { 223 | #elif defined(XP_MACOSX) 224 | bool SubmitFrame(const mozilla::gfx::VRLayer_Stereo_Immersive& aLayer, 225 | const VRLayerTextureHandle& aTexture) override; 226 | +#elif defined(XP_LINUX) 227 | + bool SubmitFrame(const mozilla::gfx::VRLayer_Stereo_Immersive& aLayer, 228 | + const VRLayerTextureHandle& aTexture) override; 229 | #endif 230 | 231 | private: 232 | @@ -73,6 +78,10 @@ class OpenVRSession : public VRSession { 233 | bool mIsWindowsMR; 234 | TimeStamp mLastHapticUpdate; 235 | 236 | +#if defined(XP_LINUX) 237 | + RefPtr mGLContext; 238 | +#endif 239 | + 240 | static void HapticTimerCallback(nsITimer* aTimer, void* aClosure); 241 | bool InitState(mozilla::gfx::VRSystemState& aSystemState); 242 | void UpdateStageParameters(mozilla::gfx::VRDisplayState& aState); 243 | diff --git a/gfx/vr/service/PuppetSession.cpp b/gfx/vr/service/PuppetSession.cpp 244 | --- a/gfx/vr/service/PuppetSession.cpp 245 | +++ b/gfx/vr/service/PuppetSession.cpp 246 | @@ -89,7 +89,7 @@ bool PuppetSession::SubmitFrame( 247 | ID3D11Texture2D* aTexture) { 248 | return VRPuppetCommandBuffer::Get().SubmitFrame(); 249 | } 250 | -#elif defined(XP_MACOSX) 251 | +#elif defined(XP_MACOSX) || defined(XP_LINUX) 252 | bool PuppetSession::SubmitFrame( 253 | const mozilla::gfx::VRLayer_Stereo_Immersive& aLayer, 254 | const VRLayerTextureHandle& aTexture) { 255 | diff --git a/gfx/vr/service/PuppetSession.h b/gfx/vr/service/PuppetSession.h 256 | --- a/gfx/vr/service/PuppetSession.h 257 | +++ b/gfx/vr/service/PuppetSession.h 258 | @@ -41,7 +41,7 @@ class PuppetSession : public VRSession { 259 | #if defined(XP_WIN) 260 | bool SubmitFrame(const mozilla::gfx::VRLayer_Stereo_Immersive& aLayer, 261 | ID3D11Texture2D* aTexture) override; 262 | -#elif defined(XP_MACOSX) 263 | +#elif defined(XP_MACOSX) || defined(XP_LINUX) 264 | bool SubmitFrame(const mozilla::gfx::VRLayer_Stereo_Immersive& aLayer, 265 | const VRLayerTextureHandle& aTexture) override; 266 | #endif 267 | diff --git a/gfx/vr/service/VRSession.cpp b/gfx/vr/service/VRSession.cpp 268 | --- a/gfx/vr/service/VRSession.cpp 269 | +++ b/gfx/vr/service/VRSession.cpp 270 | @@ -148,6 +148,13 @@ bool VRSession::SubmitFrame( 271 | return SubmitFrame(aLayer, aLayer.textureHandle); 272 | } 273 | 274 | +#elif defined(XP_LINUX) 275 | + 276 | + if (aLayer.textureType == 277 | + VRLayerTextureType::LayerTextureType_SharedGLTexture) { 278 | + return SubmitFrame(aLayer, aLayer.textureHandle); 279 | + } 280 | + 281 | #endif 282 | 283 | return false; 284 | diff --git a/gfx/vr/service/VRSession.h b/gfx/vr/service/VRSession.h 285 | --- a/gfx/vr/service/VRSession.h 286 | +++ b/gfx/vr/service/VRSession.h 287 | @@ -80,7 +80,7 @@ class VRSession { 288 | ID3D11DeviceContext1* mContext; 289 | ID3DDeviceContextState* mDeviceContextState; 290 | 291 | -#elif defined(XP_MACOSX) 292 | +#elif defined(XP_MACOSX) || defined(XP_LINUX) 293 | virtual bool SubmitFrame(const mozilla::gfx::VRLayer_Stereo_Immersive& aLayer, 294 | const VRLayerTextureHandle& aTexture) = 0; 295 | #endif 296 | -------------------------------------------------------------------------------- /gecko/webxr-linux.patch: -------------------------------------------------------------------------------- 1 | diff --git a/gfx/gl/SharedSurfaceGL.cpp b/gfx/gl/SharedSurfaceGL.cpp 2 | --- a/gfx/gl/SharedSurfaceGL.cpp 3 | +++ b/gfx/gl/SharedSurfaceGL.cpp 4 | @@ -6,7 +6,9 @@ 5 | #include "SharedSurfaceGL.h" 6 | 7 | #include "GLBlitHelper.h" 8 | +#include "GLConsts.h" 9 | #include "GLContext.h" 10 | +#include "GLContextEGL.h" 11 | #include "GLReadTexImageHelper.h" 12 | #include "mozilla/gfx/2D.h" 13 | #include "mozilla/layers/TextureForwarder.h" 14 | @@ -16,6 +18,8 @@ namespace mozilla { 15 | namespace gl { 16 | 17 | /*static*/ 18 | +uint8_t* SharedSurface_Basic::pixelsBuffer = nullptr; 19 | + 20 | UniquePtr SharedSurface_Basic::Create( 21 | const SharedSurfaceDesc& desc) { 22 | auto fb = MozFramebuffer::Create(desc.gl, desc.size, 0, false); 23 | @@ -29,7 +33,25 @@ SharedSurface_Basic::SharedSurface_Basic 24 | : SharedSurface(desc, std::move(fb)) {} 25 | 26 | Maybe SharedSurface_Basic::ToSurfaceDescriptor() { 27 | - return Nothing(); 28 | + int width = mFb->mSize.width; 29 | + int height = mFb->mSize.height; 30 | + if (!pixelsBuffer) { 31 | + // Allocate the pixels buffer once. 32 | + pixelsBuffer = (uint8_t*)malloc(width * height * 4); 33 | + } 34 | + 35 | + // Read the pixels from the frame buffer. 36 | + mDesc.gl->fBindFramebuffer(LOCAL_GL_FRAMEBUFFER, mFb->mFB); 37 | + mDesc.gl->fReadPixels(0, 0, width, height, LOCAL_GL_RGBA, 38 | + LOCAL_GL_UNSIGNED_BYTE, pixelsBuffer); 39 | + 40 | + // HACK: pass along the pointer to the pixelsBuffer in the 'fence' field. 41 | + return Some(layers::SurfaceDescriptorSharedGLTexture( 42 | + -1, 43 | + -1, 44 | + (uintptr_t)pixelsBuffer, 45 | + mFb->mSize, 46 | + true)); 47 | } 48 | 49 | //////////////////////////////////////////////////////////////////////// 50 | diff --git a/gfx/gl/SharedSurfaceGL.h b/gfx/gl/SharedSurfaceGL.h 51 | --- a/gfx/gl/SharedSurfaceGL.h 52 | +++ b/gfx/gl/SharedSurfaceGL.h 53 | @@ -19,6 +19,10 @@ class SharedSurface_Basic final : public 54 | static UniquePtr Create(const SharedSurfaceDesc&); 55 | 56 | private: 57 | + // HACK: single static buffer for storing the pixel data. 58 | + // Image is uploaded and submitted to the compositor in OpenVRSession. 59 | + static uint8_t* pixelsBuffer; 60 | + 61 | SharedSurface_Basic(const SharedSurfaceDesc& desc, 62 | UniquePtr&& fb); 63 | 64 | diff --git a/gfx/vr/VRManager.cpp b/gfx/vr/VRManager.cpp 65 | --- a/gfx/vr/VRManager.cpp 66 | +++ b/gfx/vr/VRManager.cpp 67 | @@ -1378,7 +1378,8 @@ bool VRManager::SubmitFrame(const layers 68 | if (mState != VRManagerState::Active) { 69 | return false; 70 | } 71 | -#if defined(XP_WIN) || defined(XP_MACOSX) || defined(MOZ_WIDGET_ANDROID) 72 | +#if defined(XP_WIN) || defined(XP_MACOSX) || defined(MOZ_WIDGET_ANDROID) || \ 73 | + defined(XP_LINUX) 74 | MOZ_ASSERT(mBrowserState.layerState[0].type == 75 | VRLayerType::LayerType_Stereo_Immersive); 76 | VRLayer_Stereo_Immersive& layer = 77 | @@ -1426,6 +1427,16 @@ bool VRManager::SubmitFrame(const layers 78 | layer.textureSize.width = desc.size().width; 79 | layer.textureSize.height = desc.size().height; 80 | } break; 81 | +# elif defined(XP_LINUX) 82 | + case SurfaceDescriptor::TSurfaceDescriptorSharedGLTexture: { 83 | + const SurfaceDescriptorSharedGLTexture& desc = 84 | + aTexture.get_SurfaceDescriptorSharedGLTexture(); 85 | + layer.textureType = VRLayerTextureType::LayerTextureType_SharedGLTexture; 86 | + // HACK: Retrieve the pointer to the pixel data from the fence field. 87 | + layer.textureHandle = (void*)desc.fence(); 88 | + layer.textureSize.width = desc.size().width; 89 | + layer.textureSize.height = desc.size().height; 90 | + } break; 91 | # endif 92 | default: { 93 | MOZ_ASSERT(false); 94 | @@ -1488,7 +1499,7 @@ void VRManager::SubmitFrameInternal(cons 95 | mCurrentSubmitTask = nullptr; 96 | } 97 | 98 | -#if defined(XP_WIN) || defined(XP_MACOSX) 99 | +#if defined(XP_WIN) || defined(XP_MACOSX) || defined(XP_LINUX) 100 | 101 | /** 102 | * Trigger the next VSync immediately after we are successfully 103 | diff --git a/gfx/vr/VRShMem.cpp b/gfx/vr/VRShMem.cpp 104 | --- a/gfx/vr/VRShMem.cpp 105 | +++ b/gfx/vr/VRShMem.cpp 106 | @@ -247,12 +247,20 @@ void VRShMem::CreateShMemForAndroid() { 107 | 108 | void VRShMem::ClearShMem() { 109 | if (mExternalShmem != nullptr) { 110 | + // Note: If the generation is reset, the communication will fail. 111 | + // The other instance of VRShMem would still think to have read 112 | + // generation 1, meaning the first (important) update is ignored. 113 | + long generation = mExternalShmem->geckoGenerationA; 114 | #ifdef MOZILLA_INTERNAL_API 115 | // VRExternalShmem is asserted to be POD 116 | mExternalShmem->Clear(); 117 | #else 118 | memset((void*)mExternalShmem, 0, sizeof(VRExternalShmem)); 119 | #endif 120 | + 121 | + // Continue counting generations where we left off. 122 | + mExternalShmem->geckoGenerationA = mExternalShmem->geckoGenerationB = 123 | + generation + 1; 124 | } 125 | } 126 | 127 | diff --git a/gfx/vr/external_api/moz_external_vr.h b/gfx/vr/external_api/moz_external_vr.h 128 | --- a/gfx/vr/external_api/moz_external_vr.h 129 | +++ b/gfx/vr/external_api/moz_external_vr.h 130 | @@ -451,7 +451,8 @@ enum class VRLayerTextureType : uint16_t 131 | LayerTextureType_None = 0, 132 | LayerTextureType_D3D10SurfaceDescriptor = 1, 133 | LayerTextureType_MacIOSurface = 2, 134 | - LayerTextureType_GeckoSurfaceTexture = 3 135 | + LayerTextureType_GeckoSurfaceTexture = 3, 136 | + LayerTextureType_SharedGLTexture = 4 137 | }; 138 | 139 | struct VRLayer_2D_Content { 140 | diff --git a/gfx/vr/service/OSVRSession.cpp b/gfx/vr/service/OSVRSession.cpp 141 | --- a/gfx/vr/service/OSVRSession.cpp 142 | +++ b/gfx/vr/service/OSVRSession.cpp 143 | @@ -497,7 +497,7 @@ bool OSVRSession::SubmitFrame( 144 | return false; 145 | // TODO Implement 146 | } 147 | -#elif defined(XP_MACOSX) 148 | +#elif defined(XP_MACOSX) || defined(XP_LINUX) 149 | bool OSVRSession::SubmitFrame( 150 | const mozilla::gfx::VRLayer_Stereo_Immersive& aLayer, 151 | const VRLayerTextureHandle& aTexture) { 152 | diff --git a/gfx/vr/service/OSVRSession.h b/gfx/vr/service/OSVRSession.h 153 | --- a/gfx/vr/service/OSVRSession.h 154 | +++ b/gfx/vr/service/OSVRSession.h 155 | @@ -44,7 +44,7 @@ class OSVRSession : public VRSession { 156 | #if defined(XP_WIN) 157 | bool SubmitFrame(const mozilla::gfx::VRLayer_Stereo_Immersive& aLayer, 158 | ID3D11Texture2D* aTexture) override; 159 | -#elif defined(XP_MACOSX) 160 | +#elif defined(XP_MACOSX) || defined(XP_LINUX) 161 | bool SubmitFrame(const mozilla::gfx::VRLayer_Stereo_Immersive& aLayer, 162 | const VRLayerTextureHandle& aTexture) override; 163 | #endif 164 | diff --git a/gfx/vr/service/OpenVRSession.cpp b/gfx/vr/service/OpenVRSession.cpp 165 | --- a/gfx/vr/service/OpenVRSession.cpp 166 | +++ b/gfx/vr/service/OpenVRSession.cpp 167 | @@ -18,6 +18,8 @@ 168 | # include "mozilla/gfx/DeviceManagerDx.h" 169 | #elif defined(XP_MACOSX) 170 | # include "mozilla/gfx/MacIOSurface.h" 171 | +#elif defined(XP_LINUX) 172 | +# include "GLContextProvider.h" 173 | #endif 174 | 175 | #if !defined(XP_WIN) 176 | @@ -1284,6 +1286,14 @@ bool OpenVRSession::SubmitFrame( 177 | return SubmitFrame(aTexture, ::vr::ETextureType::TextureType_IOSurface, 178 | aLayer.leftEyeRect, aLayer.rightEyeRect); 179 | } 180 | +#elif defined(XP_LINUX) 181 | +bool OpenVRSession::SubmitFrame( 182 | + const mozilla::gfx::VRLayer_Stereo_Immersive& aLayer, 183 | + const VRLayerTextureHandle& aTexture) { 184 | + mTextureSize = aLayer.textureSize; 185 | + return SubmitFrame(aTexture, ::vr::ETextureType::TextureType_OpenGL, 186 | + aLayer.leftEyeRect, aLayer.rightEyeRect); 187 | +} 188 | #endif 189 | 190 | bool OpenVRSession::SubmitFrame(const VRLayerTextureHandle& aTextureHandle, 191 | @@ -1303,32 +1313,46 @@ bool OpenVRSession::SubmitFrame(const VR 192 | 193 | CFTypeRefPtr ioSurface = surf->GetIOSurfaceRef(); 194 | tex.handle = (void*)ioSurface.get(); 195 | -#else 196 | - tex.handle = aTextureHandle; 197 | #endif 198 | + tex.eColorSpace = ::vr::EColorSpace::ColorSpace_Gamma; 199 | tex.eType = aTextureType; 200 | - tex.eColorSpace = ::vr::EColorSpace::ColorSpace_Auto; 201 | + 202 | + if (!mGLContext) { 203 | + // Create a new GLX context for creating textures for the compositor. 204 | + nsCString discardFailureId; 205 | + mGLContext = gl::GLContextProviderGLX::CreateHeadless({}, &discardFailureId); 206 | + } 207 | + 208 | + if (mTempTexture == 0) { 209 | + // Create texture to upload pixel data to. 210 | + mGLContext->fGenTextures(1, &mTempTexture); 211 | + } 212 | + mGLContext->fBindTexture(LOCAL_GL_TEXTURE_2D, mTempTexture); 213 | + mGLContext->fTexImage2D(LOCAL_GL_TEXTURE_2D, 0, LOCAL_GL_RGBA8, 214 | + mTextureSize.width, mTextureSize.height, 0, 215 | + LOCAL_GL_RGBA, LOCAL_GL_UNSIGNED_BYTE, (void*)aTextureHandle); 216 | + tex.handle = (void*)mTempTexture; 217 | 218 | ::vr::VRTextureBounds_t bounds; 219 | bounds.uMin = aLeftEyeRect.x; 220 | - bounds.vMin = 1.0 - aLeftEyeRect.y; 221 | + bounds.vMin = 1.0 - (aLeftEyeRect.y + aLeftEyeRect.height); 222 | bounds.uMax = aLeftEyeRect.x + aLeftEyeRect.width; 223 | - bounds.vMax = 1.0 - (aLeftEyeRect.y + aLeftEyeRect.height); 224 | + bounds.vMax = 1.0 - aLeftEyeRect.y; 225 | 226 | ::vr::EVRCompositorError err; 227 | err = mVRCompositor->Submit(::vr::EVREye::Eye_Left, &tex, &bounds); 228 | if (err != ::vr::EVRCompositorError::VRCompositorError_None) { 229 | - printf_stderr("OpenVR Compositor Submit() failed.\n"); 230 | + printf_stderr("OpenVR Compositor Submit() failed: err=%d.\n", err); 231 | } 232 | 233 | bounds.uMin = aRightEyeRect.x; 234 | - bounds.vMin = 1.0 - aRightEyeRect.y; 235 | + bounds.vMin = 1.0 - (aRightEyeRect.y + aRightEyeRect.height); 236 | bounds.uMax = aRightEyeRect.x + aRightEyeRect.width; 237 | - bounds.vMax = 1.0 - (aRightEyeRect.y + aRightEyeRect.height); 238 | + bounds.vMax = 1.0 - aRightEyeRect.y; 239 | 240 | err = mVRCompositor->Submit(::vr::EVREye::Eye_Right, &tex, &bounds); 241 | if (err != ::vr::EVRCompositorError::VRCompositorError_None) { 242 | - printf_stderr("OpenVR Compositor Submit() failed.\n"); 243 | + printf_stderr("OpenVR Compositor Submit() failed: err=%d\n", err); 244 | } 245 | 246 | mVRCompositor->PostPresentHandoff(); 247 | diff --git a/gfx/vr/service/OpenVRSession.h b/gfx/vr/service/OpenVRSession.h 248 | --- a/gfx/vr/service/OpenVRSession.h 249 | +++ b/gfx/vr/service/OpenVRSession.h 250 | @@ -16,6 +16,8 @@ 251 | 252 | #if defined(XP_WIN) 253 | # include 254 | +#elif defined(XP_LINUX) 255 | +# include "GLContext.h" 256 | #endif 257 | class nsITimer; 258 | 259 | @@ -58,6 +60,9 @@ class OpenVRSession : public VRSession { 260 | #elif defined(XP_MACOSX) 261 | bool SubmitFrame(const mozilla::gfx::VRLayer_Stereo_Immersive& aLayer, 262 | const VRLayerTextureHandle& aTexture) override; 263 | +#elif defined(XP_LINUX) 264 | + bool SubmitFrame(const mozilla::gfx::VRLayer_Stereo_Immersive& aLayer, 265 | + const VRLayerTextureHandle& aTexture) override; 266 | #endif 267 | 268 | private: 269 | @@ -73,6 +78,12 @@ class OpenVRSession : public VRSession { 270 | bool mIsWindowsMR; 271 | TimeStamp mLastHapticUpdate; 272 | 273 | +#if defined(XP_LINUX) 274 | + RefPtr mGLContext; 275 | + GLuint mTempTexture = 0; 276 | + IntSize_POD mTextureSize; 277 | +#endif 278 | + 279 | static void HapticTimerCallback(nsITimer* aTimer, void* aClosure); 280 | bool InitState(mozilla::gfx::VRSystemState& aSystemState); 281 | void UpdateStageParameters(mozilla::gfx::VRDisplayState& aState); 282 | diff --git a/gfx/vr/service/PuppetSession.cpp b/gfx/vr/service/PuppetSession.cpp 283 | --- a/gfx/vr/service/PuppetSession.cpp 284 | +++ b/gfx/vr/service/PuppetSession.cpp 285 | @@ -89,7 +89,7 @@ bool PuppetSession::SubmitFrame( 286 | ID3D11Texture2D* aTexture) { 287 | return VRPuppetCommandBuffer::Get().SubmitFrame(); 288 | } 289 | -#elif defined(XP_MACOSX) 290 | +#elif defined(XP_MACOSX) || defined(XP_LINUX) 291 | bool PuppetSession::SubmitFrame( 292 | const mozilla::gfx::VRLayer_Stereo_Immersive& aLayer, 293 | const VRLayerTextureHandle& aTexture) { 294 | diff --git a/gfx/vr/service/PuppetSession.h b/gfx/vr/service/PuppetSession.h 295 | --- a/gfx/vr/service/PuppetSession.h 296 | +++ b/gfx/vr/service/PuppetSession.h 297 | @@ -41,7 +41,7 @@ class PuppetSession : public VRSession { 298 | #if defined(XP_WIN) 299 | bool SubmitFrame(const mozilla::gfx::VRLayer_Stereo_Immersive& aLayer, 300 | ID3D11Texture2D* aTexture) override; 301 | -#elif defined(XP_MACOSX) 302 | +#elif defined(XP_MACOSX) || defined(XP_LINUX) 303 | bool SubmitFrame(const mozilla::gfx::VRLayer_Stereo_Immersive& aLayer, 304 | const VRLayerTextureHandle& aTexture) override; 305 | #endif 306 | diff --git a/gfx/vr/service/VRSession.cpp b/gfx/vr/service/VRSession.cpp 307 | --- a/gfx/vr/service/VRSession.cpp 308 | +++ b/gfx/vr/service/VRSession.cpp 309 | @@ -148,6 +148,13 @@ bool VRSession::SubmitFrame( 310 | return SubmitFrame(aLayer, aLayer.textureHandle); 311 | } 312 | 313 | +#elif defined(XP_LINUX) 314 | + 315 | + if (aLayer.textureType == 316 | + VRLayerTextureType::LayerTextureType_SharedGLTexture) { 317 | + return SubmitFrame(aLayer, aLayer.textureHandle); 318 | + } 319 | + 320 | #endif 321 | 322 | return false; 323 | diff --git a/gfx/vr/service/VRSession.h b/gfx/vr/service/VRSession.h 324 | --- a/gfx/vr/service/VRSession.h 325 | +++ b/gfx/vr/service/VRSession.h 326 | @@ -80,7 +80,7 @@ class VRSession { 327 | ID3D11DeviceContext1* mContext; 328 | ID3DDeviceContextState* mDeviceContextState; 329 | 330 | -#elif defined(XP_MACOSX) 331 | +#elif defined(XP_MACOSX) || defined(XP_LINUX) 332 | virtual bool SubmitFrame(const mozilla::gfx::VRLayer_Stereo_Immersive& aLayer, 333 | const VRLayerTextureHandle& aTexture) = 0; 334 | #endif 335 | --------------------------------------------------------------------------------