8#include "evan/openxr/XrSwapchainContext.hpp"
10#include "evan/CheckedCast.hpp"
14#include <glm/gtc/quaternion.hpp>
25 VkSampleCountFlagBits toVkSampleCountFlagBits(uint32_t sampleCount)
27 switch (sampleCount) {
29 return VK_SAMPLE_COUNT_2_BIT;
31 return VK_SAMPLE_COUNT_4_BIT;
33 return VK_SAMPLE_COUNT_8_BIT;
35 return VK_SAMPLE_COUNT_16_BIT;
37 return VK_SAMPLE_COUNT_32_BIT;
39 return VK_SAMPLE_COUNT_64_BIT;
41 return VK_SAMPLE_COUNT_1_BIT;
48 this->
getLogger().info() <<
"Initializing XrSwapchainContext";
64 selectMsaaSamples(deviceContext);
72 && multiviewMode() != MultiviewMode::Off
75 <=
static_cast<std::size_t
>(MAX_MULTIVIEW_VIEWS)
76 && viewConfigurationsShareLayout();
82 std::vector<SwapchainHandle>
handles;
86 _multiviewEnabled =
true;
89 <<
"Using a single layered swapchain with "
90 <<
handles.back().createInfo.arraySize
91 <<
" array layers to render all views in one pass.";
94 <<
"The runtime did not provide a layered swapchain; falling "
95 "back to one swapchain per view.";
98 if (!_multiviewEnabled) {
101 if (multiviewMode() == MultiviewMode::On && !_multiviewEnabled) {
103 <<
"EVAN_MULTIVIEW=on but multiview could not be enabled; "
104 "falling back to the two-pass path.";
110 for (std::size_t
i = 0;
i < _viewSet.
size(); ++
i) {
111 _viewSet[
i].swapchainIndex = _multiviewEnabled ? 0 :
i;
116 _multiviewEnabled ? multiviewViewMask(_viewSet.
size()) : 0
u);
118 buildSwapchainImages(deviceContext,
handles);
121std::optional<evan::XrSwapchainContext::SwapchainHandle>
122 evan::XrSwapchainContext::createMultiviewSwapchain(
130 const auto &
viewConfig = _viewsConfigurations.front();
132 SwapchainHandle
handle {};
133 handle.createInfo.type = XR_TYPE_SWAPCHAIN_CREATE_INFO;
134 handle.createInfo.arraySize =
static_cast<uint32_t
>(_viewSet.size());
135 handle.createInfo.format = swapchainFormat;
136 handle.createInfo.width = viewConfig.recommendedImageRectWidth;
137 handle.createInfo.height = viewConfig.recommendedImageRectHeight;
138 handle.createInfo.mipCount = 1;
139 handle.createInfo.faceCount = 1;
143 handle.createInfo.sampleCount = viewConfig.recommendedSwapchainSampleCount;
144 handle.createInfo.usageFlags =
145 backend.getOpenXrOptions().swapchainUsageFlags;
147 this->getLogger().info()
148 <<
"Creating layered swapchain for multiview rendering...";
150 XrResult result = xrCreateSwapchain(backend.getSession(),
151 &handle.createInfo, &handle.swapchain);
152 if (result != XR_SUCCESS) {
153 this->getLogger().warning()
154 <<
"Failed to create a swapchain with arraySize "
155 << handle.createInfo.arraySize <<
" with error code: " << result;
161std::vector<evan::XrSwapchainContext::SwapchainHandle>
162 evan::XrSwapchainContext::createPerViewSwapchains(
163 const DeviceContext &deviceContext, VkFormat swapchainFormat)
166 *deviceContext.getDeviceBackend());
168 std::vector<SwapchainHandle> handles;
169 for (
const auto &viewConfig: _viewsConfigurations) {
170 this->getLogger().info() <<
"Creating swapchain for view configuration";
172 SwapchainHandle handle {};
173 handle.createInfo.type = XR_TYPE_SWAPCHAIN_CREATE_INFO;
174 handle.createInfo.arraySize = 1;
175 handle.createInfo.format = swapchainFormat;
176 handle.createInfo.width = viewConfig.recommendedImageRectWidth;
177 handle.createInfo.height = viewConfig.recommendedImageRectHeight;
178 handle.createInfo.mipCount = 1;
179 handle.createInfo.faceCount = 1;
183 handle.createInfo.sampleCount =
184 viewConfig.recommendedSwapchainSampleCount;
185 handle.createInfo.usageFlags =
186 backend.getOpenXrOptions().swapchainUsageFlags;
188 XrResult result = xrCreateSwapchain(
189 backend.getSession(), &handle.createInfo, &handle.swapchain);
190 if (result != XR_SUCCESS) {
191 this->getLogger().error()
192 <<
"Failed to create swapchain for view configuration "
197 handles.push_back(handle);
202void evan::XrSwapchainContext::buildSwapchainImages(
203 const DeviceContext &deviceContext,
204 const std::vector<SwapchainHandle> &handles)
206 for (
const auto &handle: handles) {
209 .createInfo = handle.createInfo,
210 .renderPass = _renderPass,
211 .deviceContext = deviceContext,
212 .msaaSamples = _msaaSamples,
213 .colorAttachmentMode = _colorAttachmentMode
215 _swapchainImages.push_back(
216 std::make_shared<XrSwapchainImage>(properties));
220bool evan::XrSwapchainContext::viewConfigurationsShareLayout()
const
222 if (_viewsConfigurations.empty()) {
226 const auto &reference = _viewsConfigurations.front();
227 for (
const auto &viewConfig: _viewsConfigurations) {
228 if (viewConfig.recommendedImageRectWidth
229 != reference.recommendedImageRectWidth
230 || viewConfig.recommendedImageRectHeight
231 != reference.recommendedImageRectHeight
232 || viewConfig.recommendedSwapchainSampleCount
233 != reference.recommendedSwapchainSampleCount) {
240void evan::XrSwapchainContext::selectMsaaSamples(
241 const DeviceContext &deviceContext)
248 const uint32_t recommended = _viewsConfigurations.empty()
250 : _viewsConfigurations.front().recommendedSwapchainSampleCount;
251 const VkSampleCountFlagBits runtimeSamples =
252 toVkSampleCountFlagBits(recommended);
253 const VkSampleCountFlagBits requested = deviceContext.getMsaaSamples();
255 if (_viewsConfigurations.empty()) {
256 this->getLogger().warning()
257 <<
"No OpenXR view configuration available. Using " << requested
259 _msaaSamples = requested;
260 _colorAttachmentMode = requested == VK_SAMPLE_COUNT_1_BIT
261 ? ColorAttachmentMode::SwapchainImageAttachment
262 : ColorAttachmentMode::ResolveToSwapchain;
266 if (runtimeSamples != VK_SAMPLE_COUNT_1_BIT) {
269 _msaaSamples = runtimeSamples;
270 _colorAttachmentMode = ColorAttachmentMode::SwapchainImageAttachment;
271 if (requested != runtimeSamples) {
272 this->getLogger().warning()
273 <<
"The OpenXR runtime recommends a " << recommended
274 <<
"-sample swapchain, so the application request of "
275 << requested <<
" sample(s) is ignored.";
277 }
else if (requested == VK_SAMPLE_COUNT_1_BIT) {
280 _msaaSamples = VK_SAMPLE_COUNT_1_BIT;
281 _colorAttachmentMode = ColorAttachmentMode::SwapchainImageAttachment;
286 _msaaSamples = requested;
287 _colorAttachmentMode = ColorAttachmentMode::ResolveToSwapchain;
290 this->getLogger().info()
291 <<
"OpenXR recommended swapchain sample count: " << recommended
292 <<
", using MSAA samples: " << _msaaSamples
293 <<
", resolve to swapchain: "
294 << (_colorAttachmentMode == ColorAttachmentMode::ResolveToSwapchain
306 <<
"Destroying XrSwapchainContext and releasing resources";
310 <<
"Destroying swapchain image and releasing resources";
313 _swapchainImages.clear();
320 this->
getLogger().info() <<
"Recreating swapchain and associated resources "
321 "for XrSwapchainContext";
325 <<
"Destroying swapchain image and releasing resources";
328 _swapchainImages.clear();
338 std::vector<SwapchainHandle>
handles;
339 if (_multiviewEnabled) {
345 <<
"Failed to recreate the layered swapchain; leaving the "
346 "context without swapchain images.";
352 buildSwapchainImages(deviceContext,
handles);
354 this->
getLogger().info() <<
"Finished recreating swapchain and associated "
355 "resources for XrSwapchainContext";
362 this->
getLogger().info() <<
"Acquiring swapchain image";
373 <<
"Failed to acquire swapchain image with error code: " <<
result;
381 this->
getLogger().info() <<
"Waiting for swapchain image";
392 <<
"Failed to wait for swapchain image with error code: " <<
result;
395 this->
getLogger().info() <<
"Successfully waited for swapchain image";
401 <<
"Updating projection layer views based on current swapchain images "
402 "and view configurations";
404 _projectionLayerViews.resize(_views.size());
405 for (
size_t i = 0;
i < _views.size(); ++
i) {
407 <<
"Updating projection layer view for index: " <<
i;
412 if (
i >= _viewSet.size()) {
415 const std::size_t swapchainIndex = _viewSet[
i].swapchainIndex;
416 if (swapchainIndex >= _swapchainImages.size()) {
420 _swapchainImages[swapchainIndex].get());
426 _projectionLayerViews[
i] = {};
427 _projectionLayerViews[
i].type =
429 _projectionLayerViews[
i].pose = _views[
i].pose;
430 _projectionLayerViews[
i].fov = _views[
i].fov;
431 _projectionLayerViews[
i].subImage.swapchain =
viewSwapchain->_swapchain;
435 _projectionLayerViews[
i].subImage.imageArrayIndex =
436 _multiviewEnabled ?
static_cast<uint32_t>(
i) : 0
u;
437 _projectionLayerViews[
i].subImage.imageRect.offset = { 0, 0 };
438 _projectionLayerViews[
i].subImage.imageRect.extent = {
443 this->
getLogger().info() <<
"Finished updating projection layer views";
450const std::vector<XrCompositionLayerProjectionView> &
453 return _projectionLayerViews;
473 return _multiviewEnabled;
478 this->
getLogger().info() <<
"Syncing ViewSet from OpenXR view state";
480 if (_viewSet.size() != _views.size()) {
481 _viewSet.resize(_views.size());
486 if (!_views.empty()) {
488 for (
const auto &
xrView: _views) {
490 glm::vec3(
xrView.pose.position.x,
xrView.pose.position.y,
496 if (_views.size() == 1) {
497 const auto &
orientation = _views[0].pose.orientation;
501 const auto &
left = _views[0].pose.orientation;
502 const auto &
right = _views[1].pose.orientation;
508 _baseCenterPose = utility::graphic::PoseF(
511 utility::graphic::OrientationF(
514 _hasBaseCenter =
true;
517 const auto &
offsetPose = _viewOffset.getPosition();
518 const auto &
offsetOrient = _viewOffset.getOrientation();
524 for (std::size_t
i = 0;
i < _views.size(); ++
i) {
528 _viewSet[
i].swapchainIndex = _multiviewEnabled ? 0 :
i;
530 utility::graphic::ViewF view;
531 const auto &
xrPose = _views[
i].pose;
541 utility::graphic::PoseF
pose(
546 utility::graphic::FieldOfViewF
fov(
547 _views[
i].
fov.angleUp, _views[
i].fov.angleDown,
548 _views[
i].fov.angleLeft, _views[
i].fov.angleRight);
551 view.setFieldOfView(
fov);
552 view.setClippingPlanes(_nearPlane, _farPlane);
554 _viewSet[
i].view = view;
559 const utility::graphic::ViewF &view)
564 _nearPlane = view.getNearPlane();
565 _farPlane = view.getFarPlane();
567 if (!_hasBaseCenter) {
576 const auto basePosition = _baseCenterPose.getPosition();
577 const auto baseOrient = _baseCenterPose.getOrientation();
585 _viewOffset = utility::graphic::PoseF(
586 utility::graphic::PositionF(
VkFormat selectSwapchainFormat(VkPhysicalDevice physicalDevice, const std::vector< int64_t > &swapchainFormats)
Selects the appropriate swapchain format from the available formats.
void createRenderPass(const std::shared_ptr< ADeviceBackend > &deviceBackend, VkSampleCountFlagBits msaaSamples, ColorAttachmentMode mode, uint32_t viewMask=0)
Creates the Vulkan render pass for the swapchain context.
Represents the device context for the engine, managing Vulkan-related resources and operations.
std::shared_ptr< ADeviceBackend > getDeviceBackend() const
Retrieves a shared pointer to the ADeviceBackend instance associated with this device context.
Describes a set of views to render, independent of the swapchain image sets.
void resize(std::size_t count)
Resizes the view set to contain exactly count views.
void updateProjectionLayerViews()
Updates the projection layer views based on current swapchain images and view data.
void syncViewSet()
Rebuilds the ViewSet from the latest OpenXR view state.
std::vector< XrView > _views
void recreateSwapchain(const DeviceContext &deviceContext, VkRenderPass renderpass) override
Recreates the swapchain and associated resources.
bool usesMultiview() const override
Indicates whether all views render into one layered swapchain.
void destroy(VkDevice device) override
Destroys all swapchain images and clears the swapchain image collection.
VkResult aquireImage(uint32_t index, VkDevice device, VkSemaphore imageAvailableSemaphore, VkFence inFlightFence, uint32_t &imageIndex) override
Acquires an image from the swapchain for rendering.
VkSampleCountFlagBits getMsaaSamples() const override
Retrieves the MSAA sample count used by this swapchain context.
XrSwapchainContext(const DeviceContext &deviceContext)
Constructs an XrSwapchainContext instance using the provided DeviceContext.
std::vector< XrViewConfigurationView > _viewsConfigurations
ViewSet & getViewSet() override
Retrieves the set of views to render (mutable).
const std::vector< XrCompositionLayerProjectionView > & getProjectionLayerViews() const
Retrieves the projection layer views for the current view configurations.
void setView(std::size_t index, const utility::graphic::ViewF &view) override
Sets the view matrix for a specific view index.
void waitForImage(uint32_t index) override
Waits for a swapchain image to become available.
Represents a swapchain image for OpenXR rendering.
Small result wrapper used by helpers that must return both a status and a value (e....
Encapsulates properties for creating an XrSwapchainImage instance.
XrSwapchain swapchain
The OpenXR swapchain handle associated with the swapchain images.