Files
2026-08-12 04:47:48 -04:00

295 lines
8.4 KiB
C++

/* SPDX-FileCopyrightText: 2022-2023 Blender Authors
*
* SPDX-License-Identifier: GPL-2.0-or-later */
/** \file
* \ingroup GHOST
*/
#pragma once
#ifndef WITH_VULKAN_BACKEND
# error "ContextVK requires WITH_VULKAN_BACKEND"
#endif
#include "GHOST_Context.hh"
#ifdef _WIN32
# include "GHOST_SystemWin32.hh"
#elif defined(__APPLE__)
# include "GHOST_SystemCocoa.hh"
#else
# ifdef WITH_GHOST_X11
# include "GHOST_SystemX11.hh"
# else
# define Display void
# define Window void *
# endif
# ifdef WITH_GHOST_WAYLAND
# include "GHOST_SystemWayland.hh"
# else
# define wl_surface void
# define wl_display void
# endif
#endif
#include <map>
#include <optional>
#include <vector>
namespace blender {
class StringRefNull;
}
#ifndef GHOST_OPENGL_VK_CONTEXT_FLAGS
/* leave as convenience define for the future */
# define GHOST_OPENGL_VK_CONTEXT_FLAGS 0
#endif
#ifndef GHOST_OPENGL_VK_RESET_NOTIFICATION_STRATEGY
# define GHOST_OPENGL_VK_RESET_NOTIFICATION_STRATEGY 0
#endif
enum GHOST_TVulkanPlatformType {
GHOST_kVulkanPlatformHeadless = 0,
#ifdef WITH_GHOST_X11
GHOST_kVulkanPlatformX11 = 1,
#endif
#ifdef WITH_GHOST_WAYLAND
GHOST_kVulkanPlatformWayland = 2,
#endif
};
struct GHOST_ContextVK_WindowInfo {
int size[2];
};
struct GHOST_FrameDiscard {
std::vector<VkSwapchainKHR> swapchains;
std::vector<VkSemaphore> semaphores;
void destroy(VkDevice vk_device);
};
struct GHOST_SwapchainImage {
/** Swap-chain image (owned by the swapchain). */
VkImage vk_image = VK_NULL_HANDLE;
/**
* Semaphore for presenting; being signaled when the swap-chain image is ready to be presented.
*/
VkSemaphore present_semaphore = VK_NULL_HANDLE;
void destroy(VkDevice vk_device);
};
struct GHOST_Frame {
/**
* Fence signaled when "previous" use of the frame has finished rendering. When signaled the
* frame can acquire a new image and the semaphores can be reused.
*/
VkFence submission_fence = VK_NULL_HANDLE;
/** Semaphore for acquiring; being signaled when the swap-chain image is ready to be updated. */
VkSemaphore acquire_semaphore = VK_NULL_HANDLE;
GHOST_FrameDiscard discard_pile;
void destroy(VkDevice vk_device);
};
class GHOST_ContextVK : public GHOST_Context {
friend class GHOST_XrGraphicsBindingVulkan;
friend class GHOST_XrGraphicsBindingVulkanD3D;
public:
/**
* Constructor.
*/
GHOST_ContextVK(const GHOST_ContextParams &context_params,
#ifdef _WIN32
HWND hwnd,
#elif defined(__APPLE__)
/* FIXME CAMetalLayer but have issue with linking. */
void *metal_layer,
#else
GHOST_TVulkanPlatformType platform,
/* X11 */
Window window,
Display *display,
/* Wayland */
wl_surface *wayland_surface,
wl_display *wayland_display,
const GHOST_ContextVK_WindowInfo *wayland_window_info,
#endif
int contextMajorVersion,
int contextMinorVersion,
const GHOST_GPUDevice &preferred_device,
const GHOST_WindowHDRInfo *hdr_info_ = nullptr);
/**
* Destructor.
*/
~GHOST_ContextVK() override;
/** \copydoc #GHOST_IContext::swapBuffersAcquire */
GHOST_TSuccess swapBufferAcquire() override;
/**
* Swaps front and back buffers of a window.
* \return A boolean success indicator.
*/
GHOST_TSuccess swapBufferRelease() override;
/**
* Activates the drawing context of this window.
* \return A boolean success indicator.
*/
GHOST_TSuccess activateDrawingContext() override;
/**
* Release the drawing context of the calling thread.
* \return A boolean success indicator.
*/
GHOST_TSuccess releaseDrawingContext() override;
/**
* Call immediately after new to initialize. If this fails then immediately delete the object.
* \return Indication as to whether initialization has succeeded.
*/
GHOST_TSuccess initializeDrawingContext() override;
/**
* Removes references to native handles from this context and then returns
* \return GHOST_kSuccess if it is OK for the parent to release the handles and
* GHOST_kFailure if releasing the handles will interfere with sharing
*/
GHOST_TSuccess releaseNativeHandles() override;
/**
* Gets the Vulkan context related resource handles.
* \return A boolean success indicator.
*/
GHOST_TSuccess getVulkanHandles(GHOST_VulkanHandles &r_handles) override;
GHOST_TSuccess getVulkanSwapChainFormat(GHOST_VulkanSwapChainData *r_swap_chain_data) override;
GHOST_TSuccess setVulkanSwapBuffersCallbacks(
std::function<void(const GHOST_VulkanSwapChainData *, bool)> swap_buffer_draw_callback,
std::function<void(void)> swap_buffer_acquired_callback,
std::function<void(GHOST_VulkanOpenXRData *)> openxr_acquire_framebuffer_image_callback,
std::function<void(GHOST_VulkanOpenXRData *)> openxr_release_framebuffer_image_callback)
override;
#ifdef WITH_GHOST_WAYLAND
/**
* \brief Check if the active driver supports wayland color management.
*
* NVIDIA driver before 595 don't support wayland color management protocol as expected resulting
* in to bright output.
*/
static GHOST_TSuccess supportsWaylandColorManagement();
#endif
/**
* Sets the swap interval for `swapBuffers`.
* \param interval: The swap interval to use.
* \return A boolean success indicator.
*/
GHOST_TSuccess setSwapInterval(int /*interval*/) override
{
return GHOST_kFailure;
}
/**
* Gets the current swap interval for swapBuffers.
* \param interval_out: Variable to store the swap interval if it can be read.
* \return Whether the swap interval can be read.
*/
GHOST_TSuccess getSwapInterval(int & /*interval_out*/) override
{
return GHOST_kFailure;
};
/**
* Returns if the context is rendered upside down compared to OpenGL.
*
* Vulkan is always rendered upside down.
*/
bool isUpsideDown() const override
{
return true;
}
/**
* \brief Is the given extension name enabled on instance level?
*
* \returns false, when extension isn't enabled on instance level or when no instance exists.
* Will return true when instance exists and extension name has been enabled on the instance.
*/
static bool is_instance_extension_enabled(blender::StringRefNull extension_name);
/**
* \brief Is the given extension name enabled on device level?
*
* \returns false, when extension isn't enabled on device level or when no instance or device
* exists. Will return true when instance exists and extension name has been enabled on the
* instance.
*/
static bool is_device_extension_enabled(blender::StringRefNull extension_name);
private:
#ifdef _WIN32
HWND hwnd_;
#elif defined(__APPLE__)
/* Is CAMetalLayer* */
void *metal_layer_;
#else /* Linux */
GHOST_TVulkanPlatformType platform_;
/* X11 */
Display *display_;
Window window_;
/* Wayland */
wl_surface *wayland_surface_;
wl_display *wayland_display_;
const GHOST_ContextVK_WindowInfo *wayland_window_info_;
#endif
const int context_major_version_;
const int context_minor_version_;
const GHOST_GPUDevice preferred_device_;
/* Optional HDR info updated by window. */
const GHOST_WindowHDRInfo *hdr_info_;
/* For display only. */
VkSurfaceKHR surface_;
VkSwapchainKHR swapchain_;
std::vector<GHOST_SwapchainImage> swapchain_images_;
std::vector<GHOST_Frame> frame_data_;
uint64_t render_frame_;
uint64_t image_count_;
VkExtent2D render_extent_;
VkExtent2D render_extent_min_;
VkSurfaceFormatKHR surface_format_;
bool use_hdr_swapchain_;
std::optional<uint32_t> acquired_swapchain_image_index_;
std::function<void(const GHOST_VulkanSwapChainData *, bool)> swap_buffer_draw_callback_;
std::function<void(void)> swap_buffer_acquired_callback_;
std::function<void(GHOST_VulkanOpenXRData *)> openxr_acquire_framebuffer_image_callback_;
std::function<void(GHOST_VulkanOpenXRData *)> openxr_release_framebuffer_image_callback_;
std::vector<VkFence> fence_pile_;
std::map<VkSwapchainKHR, std::vector<VkFence>> present_fences_;
const char *getPlatformSpecificSurfaceExtension() const;
GHOST_TSuccess recreateSwapchain(bool use_hdr_swapchain);
GHOST_TSuccess initializeFrameData();
GHOST_TSuccess destroySwapchain();
VkFence getFence();
void setPresentFence(VkSwapchainKHR swapchain, VkFence fence);
void destroySwapchainPresentFences(VkSwapchainKHR swapchain);
};