Skip to content

Commit

Permalink
vulkan: platform swapchain API (#6830)
Browse files Browse the repository at this point in the history
 - Introduce new custom swapchain API for VulkanPlatform.h
 - Implement the API for the base VulkanPlatform by refactoring
   the existing swapchain code.
 - VulkanSwapChain is now a wrapper for VulkanPlatform's
   swap chain API.
 - Actual implementation is in
   vulkan/platform/VulkanPlatformSwapChainImpl.{h,cpp}
  • Loading branch information
poweifeng committed May 25, 2023
1 parent 25708e8 commit f953488
Show file tree
Hide file tree
Showing 21 changed files with 1,115 additions and 632 deletions.
1 change: 1 addition & 0 deletions NEW_RELEASE_NOTES.md
Original file line number Diff line number Diff line change
Expand Up @@ -13,3 +13,4 @@ appropriate header in [RELEASE_NOTES.md](./RELEASE_NOTES.md).
- engine: improve froxelizer resource efficiency [⚠️ **Recompile materials**]
- matc: better accounting and validation of used samplers in user materials
- engine: add support for sampling fog color from a custom texture [⚠️ **Recompile materials**]
- vulkan: introduce new custom swapchain API
4 changes: 3 additions & 1 deletion filament/backend/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -160,6 +160,9 @@ endif()
if (FILAMENT_SUPPORTS_VULKAN)
list(APPEND SRCS
include/backend/platforms/VulkanPlatform.h
src/vulkan/platform/VulkanPlatform.cpp
src/vulkan/platform/VulkanPlatformSwapChainImpl.cpp
src/vulkan/platform/VulkanPlatformSwapChainImpl.h
src/vulkan/VulkanBlitter.cpp
src/vulkan/VulkanBlitter.h
src/vulkan/VulkanBuffer.cpp
Expand All @@ -184,7 +187,6 @@ if (FILAMENT_SUPPORTS_VULKAN)
src/vulkan/VulkanMemory.cpp
src/vulkan/VulkanPipelineCache.cpp
src/vulkan/VulkanPipelineCache.h
src/vulkan/VulkanPlatform.cpp
src/vulkan/VulkanSamplerCache.cpp
src/vulkan/VulkanSamplerCache.h
src/vulkan/VulkanStagePool.cpp
Expand Down
141 changes: 109 additions & 32 deletions filament/backend/include/backend/platforms/VulkanPlatform.h
Original file line number Diff line number Diff line change
Expand Up @@ -21,24 +21,118 @@

#include <bluevk/BlueVK.h>
#include <utils/FixedCapacityVector.h>
#include <utils/PrivateImplementation.h>

#include <tuple>
#include <unordered_set>

namespace filament::backend {

using SwapChain = Platform::SwapChain;

/**
* A Platform interface that creates a Vulkan backend.
* Private implementation details for the provided vulkan platform.
*/
struct VulkanPlatformPrivate;

class VulkanPlatform : public Platform {
/**
* A Platform interface that creates a Vulkan backend.
*/
class VulkanPlatform : public Platform, utils::PrivateImplementation<VulkanPlatformPrivate> {
public:
/**
*
* Shorthand for the pointer to the Platform SwapChain struct, we use it also as a handle (i.e.
* identifier for the swapchain).
*/
using SwapChainPtr = Platform::SwapChain*;

/**
* Collection of images, formats, and extent (width/height) that defines the swapchain.
*/
struct SwapChainBundle {
utils::FixedCapacityVector<VkImage> colors;
VkImage depth = VK_NULL_HANDLE;
VkFormat colorFormat = VK_FORMAT_UNDEFINED;
VkFormat depthFormat = VK_FORMAT_UNDEFINED;
VkExtent2D extent = {0, 0};
};

VulkanPlatform();

~VulkanPlatform() override;

Driver* createDriver(void* const sharedContext,
Platform::DriverConfig const& driverConfig) noexcept override;

int getOSVersion() const noexcept override { return 0; }
int getOSVersion() const noexcept override {
return 0;
}

/**
* Get the images handles and format of the memory backing the swapchain. This should be called
* after createSwapChain() or after recreateIfResized().
* @param swapchain The handle returned by createSwapChain()
* @return An array of VkImages
*/
virtual SwapChainBundle getSwapChainBundle(SwapChainPtr handle);

/**
* Acquire the next image for rendering. The `index` will be written with an non-negative
* integer that the backend can use to index into the `SwapChainBundle.colors` array. The
* corresponding VkImage will be used as the output color attachment. The client should signal
* the `clientSignal` semaphore when the image is ready to be used by the backend.
* @param handle The handle returned by createSwapChain()
* @param clientSignal The semaphore that the client will signal to indicate that the backend
* may render into the image.
* @param index Pointer to memory that will be filled with the index that corresponding
* to an image in the `SwapChainBundle.colors` array.
* @return Result of acquire
*/
virtual VkResult acquire(SwapChainPtr handle, VkSemaphore clientSignal, uint32_t* index);

/**
* Present the image corresponding to `index` to the display. The client should wait on
* `finishedDrawing` before presenting.
* @param handle The handle returned by createSwapChain()
* @param index Index that corresponding to an image in the
* `SwapChainBundle.colors` array.
* @param finishedDrawing Backend passes in a semaphore that the client will signal to
* indicate that the client may render into the image.
* @return Result of present
*/
virtual VkResult present(SwapChainPtr handle, uint32_t index, VkSemaphore finishedDrawing);

/**
* Check if the surface size has changed.
* @param handle The handle returned by createSwapChain()
* @return Whether the swapchain has been resized
*/
virtual bool hasResized(SwapChainPtr handle);

/**
* Carry out a recreation of the swapchain.
* @param handle The handle returned by createSwapChain()
* @return Result of the recreation
*/
virtual VkResult recreate(SwapChainPtr handle);

/**
* Create a swapchain given a platform window, or if given a null `nativeWindow`, then we
* try to create a headless swapchain with the given `extent`.
* @param flags Optional parameters passed to the client as defined in
* Filament::SwapChain.h.
* @param extent Optional width and height that indicates the size of the headless swapchain.
* @return Result of the operation
*/
virtual SwapChainPtr createSwapChain(void* nativeWindow, uint64_t flags = 0,
VkExtent2D extent = {0, 0});

/**
* Destroy the swapchain.
* @param handle The handle returned by createSwapChain()
*/
virtual void destroy(SwapChainPtr handle);

/**
* Clean up any resources owned by the Platform. For example, if the Vulkan instance handle was
Expand All @@ -49,63 +143,46 @@ class VulkanPlatform : public Platform {
/**
* @return The instance (VkInstance) for the Vulkan backend.
*/
inline VkInstance getInstance() const noexcept { return mInstance; }
VkInstance getInstance() const noexcept;

/**
* @return The logical device (VkDevice) that was selected as the backend device.
*/
inline VkDevice getDevice() const noexcept { return mDevice; }
VkDevice getDevice() const noexcept;

/**
* @return The physical device (i.e gpu) that was selected as the backend physical device.
*/
inline VkPhysicalDevice getPhysicalDevice() const noexcept { return mPhysicalDevice; }
VkPhysicalDevice getPhysicalDevice() const noexcept;

/**
* @return The family index of the graphics queue selected for the Vulkan backend.
*/
inline uint32_t getGraphicsQueueFamilyIndex() const noexcept {
return mGraphicsQueueFamilyIndex;
}
uint32_t getGraphicsQueueFamilyIndex() const noexcept;

/**
* @return The index of the graphics queue (if there are multiple graphics queues)
* selected for the Vulkan backend.
*/
inline uint32_t getGraphicsQueueIndex() const noexcept { return mGraphicsQueueIndex; }
uint32_t getGraphicsQueueIndex() const noexcept;

/**
* @return The queue that was selected for the Vulkan backend.
*/
inline VkQueue getGraphicsQueue() const noexcept { return mGraphicsQueue; }

// TODO: move this to private when swapchains are created via platform.
struct SurfaceBundle {
void* surface;
// On certain platforms, the extent of the surface cannot be queried from Vulkan. In those
// situations, we allow the frontend to pass in the extent to use in creating the swap
// chains. Platform implementation should set extent to 0 if they do not expect to set the
// swap chain extent.
uint32_t width;
uint32_t height;
};
static SurfaceBundle createVkSurfaceKHR(void* nativeWindow, VkInstance instance,
uint64_t flags) noexcept;
VkQueue getGraphicsQueue() const noexcept;

private:
// Platform dependent helper methods
using ExtensionSet = std::unordered_set<std::string_view>;
static ExtensionSet getRequiredInstanceExtensions();

protected:
VkInstance mInstance = VK_NULL_HANDLE;
VkPhysicalDevice mPhysicalDevice = VK_NULL_HANDLE;
VkDevice mDevice = VK_NULL_HANDLE;
uint32_t mGraphicsQueueFamilyIndex = 0xFFFFFFFF;
uint32_t mGraphicsQueueIndex = 0;
using SurfaceBundle = std::tuple<VkSurfaceKHR, VkExtent2D>;
static SurfaceBundle createVkSurfaceKHR(void* nativeWindow, VkInstance instance,
uint64_t flags) noexcept;

VkQueue mGraphicsQueue = VK_NULL_HANDLE;
friend struct VulkanPlatformPrivate;
};

}// namespace filament::backend

#endif//TNT_FILAMENT_BACKEND_PLATFORMS_VULKANPLATFORM_H
#endif// TNT_FILAMENT_BACKEND_PLATFORMS_VULKANPLATFORM_H
1 change: 1 addition & 0 deletions filament/backend/src/vulkan/VulkanBuffer.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -51,6 +51,7 @@ void VulkanBuffer::terminate() {
vmaDestroyBuffer(mAllocator, mGpuBuffer, mGpuMemory);
mGpuMemory = VK_NULL_HANDLE;
mGpuBuffer = VK_NULL_HANDLE;
mCommands.reset();
}

void VulkanBuffer::loadFromCpu(const void* cpuData, uint32_t byteOffset, uint32_t numBytes) const {
Expand Down
5 changes: 3 additions & 2 deletions filament/backend/src/vulkan/VulkanContext.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -55,7 +55,7 @@ VkExtent2D VulkanAttachment::getExtent2D() const {
return { std::max(1u, texture->width >> level), std::max(1u, texture->height >> level) };
}

VkImageView VulkanAttachment::getImageView(VkImageAspectFlags aspect) const {
VkImageView VulkanAttachment::getImageView(VkImageAspectFlags aspect) {
assert_invariant(texture);
return texture->getAttachmentView(getSubresourceRange(aspect));
}
Expand Down Expand Up @@ -136,7 +136,8 @@ VulkanTimestamps::QueryResult VulkanTimestamps::getResult(VulkanTimerQuery const
VkResult vkresult =
vkGetQueryPoolResults(mDevice, mPool, index, 2, dataSize, (void*) result.data(),
stride, VK_QUERY_RESULT_64_BIT | VK_QUERY_RESULT_WITH_AVAILABILITY_BIT);
ASSERT_POSTCONDITION(vkresult == VK_SUCCESS, "vkGetQueryPoolResults error.");
ASSERT_POSTCONDITION(vkresult == VK_SUCCESS || vkresult == VK_NOT_READY,
"vkGetQueryPoolResults error: %d", static_cast<int32_t>(vkresult));
if (vkresult == VK_NOT_READY) {
return {0, 0, 0, 0};
}
Expand Down
71 changes: 69 additions & 2 deletions filament/backend/src/vulkan/VulkanContext.h
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,8 @@
#include <utils/Slice.h>
#include <utils/bitset.h>

#include <memory>

VK_DEFINE_HANDLE(VmaAllocator)
VK_DEFINE_HANDLE(VmaPool)

Expand All @@ -37,15 +39,80 @@ struct VulkanTexture;
class VulkanStagePool;
struct VulkanTimerQuery;

// TODO: We used std::shared_ptr in various places across the vulkan backend, but VulkanTexture*
// also maps to HwTexture so it's not possible to switch in VulkanAttachment. Hence we introduce
// this temporary class. We need to revisit the ownership pattern and use of smart pointer in the
// vulkan backend, in particular std::shared_ptr uses atomic, which is overhead we do not need.
struct TexturePointer {
TexturePointer() = default;
explicit TexturePointer(VulkanTexture* tex) :mTexture(tex) {}
explicit TexturePointer(std::shared_ptr<VulkanTexture> tex) :mTexture(tex) {}

inline TexturePointer& operator=(VulkanTexture* tex) {
mTexture = tex;
return *this;
}

inline TexturePointer& operator=(std::shared_ptr<VulkanTexture> tex) {
mTexture = tex;
return *this;
}

// Be careful not to leak here. Should only be used in local scope.
explicit operator VulkanTexture*() const {
if (mTexture.index() == 0) {
return std::get<0>(mTexture);
}
return std::get<1>(mTexture).get();
}

// Be careful not to leak here. Should only be used in local scope.
explicit operator VulkanTexture const*() const {
if (mTexture.index() == 0) {
return std::get<0>(mTexture);
}
return std::get<1>(mTexture).get();
}

explicit operator std::shared_ptr<VulkanTexture>() const {
assert_invariant(mTexture.index() == 1);
return std::get<1>(mTexture);
}

inline operator bool() const {
if (mTexture.index() == 0) {
return std::get<0>(mTexture) != nullptr;
}
return (bool) std::get<1>(mTexture);
}

inline VulkanTexture* operator->() {
if (mTexture.index() == 0) {
return std::get<0>(mTexture);
}
return std::get<1>(mTexture).get();
}

inline VulkanTexture const* operator->() const {
if (mTexture.index() == 0) {
return std::get<0>(mTexture);
}
return std::get<1>(mTexture).get();
}

private:
std::variant<VulkanTexture*, std::shared_ptr<VulkanTexture>> mTexture;
};

struct VulkanAttachment {
VulkanTexture* texture = nullptr;
TexturePointer texture;
uint8_t level = 0;
uint16_t layer = 0;
VkImage getImage() const;
VkFormat getFormat() const;
VulkanLayout getLayout() const;
VkExtent2D getExtent2D() const;
VkImageView getImageView(VkImageAspectFlags aspect) const;
VkImageView getImageView(VkImageAspectFlags aspect);
// TODO: maybe embed aspect into the attachment or texture itself.
VkImageSubresourceRange getSubresourceRange(VkImageAspectFlags aspect) const;
};
Expand Down
Loading

0 comments on commit f953488

Please sign in to comment.