Skip to content
Snippets Groups Projects
Commit 496120c3 authored by Christian Mattes's avatar Christian Mattes
Browse files

First version of lava-extras-display

parent a734a8da
No related branches found
No related tags found
No related merge requests found
......@@ -5,6 +5,7 @@ find_package(Boost)
add_subdirectory(pack)
add_subdirectory(glsl)
add_subdirectory(display)
if (TARGET imgui)
add_subdirectory(imgui)
......
cmake_minimum_required(VERSION 3.0)
file(GLOB_RECURSE SOURCE_FILES "*.cc")
file(GLOB_RECURSE HEADER_FILES "*.hh")
# X11 (optional, but useful to acquire displays)
add_library(lava-extras-display ${LAVA_LINK_TYPE} ${SOURCE_FILES} ${HEADER_FILES})
find_package(X11)
if(X11_FOUND)
target_compile_definitions(lava-extras-display PUBLIC -DLAVA_DISPLAY_X11_AVAILABLE)
target_include_directories(lava-extras-display PUBLIC "${X11_X11_INCLUDE_PATH}")
target_link_libraries(lava-extras-display PUBLIC "${X11_X11_LIB}")
endif()
target_include_directories(lava-extras-display PUBLIC ./)
target_compile_options(lava-extras-display PRIVATE ${LAVA_EXTRAS_DEF_OPTIONS})
target_link_libraries(lava-extras-display PUBLIC lava)
#include "DisplayOutput.hh"
#include "DisplayWindow.hh"
#include <lava/common/str_utils.hh>
namespace lava {
namespace display {
DisplayOutput::DisplayOutput() {}
std::shared_ptr<DisplayWindow>
DisplayOutput::openWindow(const SharedDevice &device, uint32_t index) {
return std::make_shared<DisplayWindow>(device, index);
}
std::vector<const char *>
DisplayOutput::instanceExtensions(const std::vector<const char *> &available) {
std::vector<const char *> result = {
VK_KHR_SURFACE_EXTENSION_NAME, VK_KHR_DISPLAY_EXTENSION_NAME,
VK_EXT_DIRECT_MODE_DISPLAY_EXTENSION_NAME};
bool has_direct_mode = util::contains(available, VK_EXT_DIRECT_MODE_DISPLAY_EXTENSION_NAME);
bool has_acquire = util::contains(available, "VK_EXT_acquire_xlib_display");
if (has_direct_mode && has_acquire) {
result.push_back(VK_EXT_DIRECT_MODE_DISPLAY_EXTENSION_NAME);
result.push_back("VK_EXT_acquire_xlib_display");
}
return result;
}
void DisplayOutput::onInstanceCreated(Instance *instance) {}
std::vector<const char *> DisplayOutput::deviceExtensions() {
return {VK_KHR_SWAPCHAIN_EXTENSION_NAME};
}
void DisplayOutput::onPhysicalDeviceSelected(vk::PhysicalDevice phy) {}
void DisplayOutput::onLogicalDeviceCreated(const SharedDevice &device) {}
} // namespace display
} // namespace lava
#pragma once
#include <lava/features/IFeature.hh>
namespace lava {
namespace display {
class DisplayWindow;
class DisplayOutput : public lava::features::IFeature {
public:
static std::shared_ptr<DisplayOutput> create() {
return std::make_shared<DisplayOutput>();
}
DisplayOutput();
std::shared_ptr<DisplayWindow> openWindow(SharedDevice const &device,
uint32_t index);
/* IFeature overrides */
std::vector<const char *> instanceExtensions(std::vector<const char *> const& available) override;
void onInstanceCreated(Instance *instance) override;
std::vector<const char *> deviceExtensions() override;
void onPhysicalDeviceSelected(vk::PhysicalDevice phy) override;
void onLogicalDeviceCreated(SharedDevice const &device) override;
protected:
lava::Instance *mInstance;
lava::Device *mDevice;
vk::PhysicalDevice mPhysicalDevice;
};
} // namespace display
} // namespace lava
#include "DisplayWindow.hh"
#include <lava/common/log.hh>
#include <lava/createinfos/Images.hh>
#include <lava/objects/Device.hh>
#include <lava/objects/Image.hh>
#include <lava/objects/Instance.hh>
namespace lava {
namespace display {
#ifdef LAVA_DISPLAY_X11_AVAILABLE
#include <X11/X.h>
void acquireDisplay(vk::Instance instance, vk::PhysicalDevice phy, vk::DisplayKHR disp)
{
typedef VkResult AcquireDisplayFunc(VkPhysicalDevice, Display*, VkDisplayKHR);
auto func = (AcquireDisplayFunc*)instance.getProcAddr("vkAcquireXlibDisplayEXT");
auto *xd = XOpenDisplay(nullptr);
if (xd) {
// Running inside an X server
auto res = func(phy, XOpenDisplay(nullptr), disp);
if (res != VK_SUCCESS) {
lava::error() << "Could not acquire display from XServer, is it in use?";
}
}
}
#else
void acquireDisplay(vk::Instance,vk::PhysicalDevice,vk::DisplayKHR) {}
#endif
void DisplayWindow::buildSwapchainWith(
const DisplayWindow::SwapchainBuildHandler &handler) {
mSwapchainHandler = handler;
buildSwapchain();
}
void DisplayWindow::buildSwapchain() {
auto caps = mDevice->physical().getSurfaceCapabilitiesKHR(mSurface);
auto pmodes = mDevice->physical().getSurfacePresentModesKHR(mSurface);
auto formats = mDevice->physical().getSurfaceFormatsKHR(mSurface);
vk::SwapchainCreateInfoKHR info;
info.surface = mSurface;
info.minImageCount = caps.minImageCount;
info.imageFormat = formats[0].format;
info.imageColorSpace = formats[0].colorSpace;
info.imageExtent = mSurfaceInfo.imageExtent;
info.imageArrayLayers = 1;
info.imageUsage = vk::ImageUsageFlagBits::eColorAttachment |
vk::ImageUsageFlagBits::eTransferDst;
info.preTransform = mSurfaceInfo.transform;
info.presentMode = vk::PresentModeKHR::eImmediate;
info.clipped = true;
auto width = info.imageExtent.width;
auto height = info.imageExtent.height;
mChain = mDevice->handle().createSwapchainKHR(info);
mChainViews.clear();
mChainImages.clear();
{
auto chainHandles = mDevice->handle().getSwapchainImagesKHR(mChain);
auto imgCreateInfo =
lava::attachment2D(width, height, info.imageFormat);
for (vk::Image handle : chainHandles) {
auto image = std::make_shared<lava::Image>(
mDevice, imgCreateInfo, handle, vk::ImageViewType::e2D);
mChainViews.push_back(image->createView());
mChainImages.push_back(move(image));
}
}
mSwapchainHandler(mChainViews);
mSwapchainInfo = info;
}
DisplayWindow::DisplayWindow(SharedDevice device,
uint32_t index)
: mDevice(device) {
auto const &instance = device->instance();
auto displays = device->physical().getDisplayPropertiesKHR();
mDisplay = displays[index].display;
auto planes = device->physical().getDisplayPlanePropertiesKHR();
auto plane_idx = std::find_if(begin(planes), end(planes),
[&](vk::DisplayPlanePropertiesKHR const &p) {
return !p.currentDisplay ||
p.currentDisplay == mDisplay;
}) -
begin(planes);
auto modes = device->physical().getDisplayModePropertiesKHR(mDisplay);
mMode = modes[0].displayMode;
auto dpcaps =
device->physical().getDisplayPlaneCapabilitiesKHR(mMode, plane_idx);
acquireDisplay(instance->handle(), device->physical(), mDisplay);
mSurfaceInfo = vk::DisplaySurfaceCreateInfoKHR()
.setDisplayMode(mMode)
.setPlaneIndex(plane_idx)
.setPlaneStackIndex(planes[plane_idx].currentStackIndex)
.setImageExtent(dpcaps.maxSrcExtent);
mSurface = instance->handle().createDisplayPlaneSurfaceKHR(mSurfaceInfo);
auto supported = mDevice->physical().getSurfaceSupportKHR(0, mSurface);
if (!supported)
throw std::runtime_error("Can't present to display surface.");
mRenderingComplete = device->handle().createSemaphore({});
mImageReady = device->handle().createSemaphore({});
mQueue = &device->graphicsQueue();
}
DisplayWindow::~DisplayWindow() {}
DisplayWindow::Frame DisplayWindow::startFrame() {
assert(mChain && "You need to provide a handler for swapchain creation.");
while (true) {
auto res = vkAcquireNextImageKHR(mDevice->handle(), mChain, 1e9,
mImageReady, {}, &mPresentIndex);
if (res == VK_TIMEOUT) {
lava::error()
<< "GlfwWindow::startFrame(): acquireNextImage timed out (>1s)";
continue;
}
if (res == VK_ERROR_OUT_OF_DATE_KHR || res == VK_SUBOPTIMAL_KHR) {
mDevice->handle().waitIdle();
buildSwapchain();
continue;
}
return {this};
}
}
DisplayWindow::Frame::Frame(DisplayWindow::Frame &&rhs) : window(rhs.window) {
rhs.window = nullptr;
}
DisplayWindow::Frame::~Frame() {
if (!window)
return;
vk::PresentInfoKHR info;
info.pImageIndices = &window->mPresentIndex;
info.pSwapchains = &window->mChain;
info.swapchainCount = 1;
info.pWaitSemaphores = &window->mRenderingComplete;
info.waitSemaphoreCount = 1;
try {
window->mQueue->handle().presentKHR(info);
} catch (vk::OutOfDateKHRError const &) {
window->mDevice->handle().waitIdle();
window->buildSwapchain();
}
}
DisplayWindow::Frame::Frame(DisplayWindow *parent) : window(parent) {}
} // namespace features
} // namespace lava
#pragma once
#include <functional>
#include <vector>
#include <lava/common/NoCopy.hh>
#include <lava/common/vulkan.hh>
#include <lava/fwd.hh>
namespace lava {
namespace display {
class DisplayWindow {
public:
class Frame;
using SwapchainBuildHandler =
std::function<void(std::vector<SharedImageView>)>;
void
buildSwapchainWith(const DisplayWindow::SwapchainBuildHandler &handler);
/// index of the image that should be drawn to next
uint32_t nextIndex();
/// wait for this semaphore before drawing to the current image
vk::Semaphore imageReady() const { return mImageReady; }
/// signal this semaphore when the image is ready to be presented
vk::Semaphore renderingComplete() const {
mRenderingCompleteCalled = true;
return mRenderingComplete;
}
/// the Format of the images in the swapchain.
/// Make sure the attachment format in your Framebuffers matches this.
vk::Format format() { return mChainFormat.format; }
uint32_t width() const { return mWidth; }
uint32_t height() const { return mHeight; }
/// Call this when window is resized
void onResize(uint32_t w, uint32_t h);
/// Don't use this directly, use openWindow in DisplayOutput instead
DisplayWindow(SharedDevice device, uint32_t index);
~DisplayWindow();
/// Call this before you start rendering to the window.
/// Use the included index to select the right FBO to render to.
/// Automatically presents the image when the return value goes out-of-scope
Frame startFrame();
class Frame {
public:
LAVA_RAII_CLASS(Frame);
Frame(Frame &&rhs);
~Frame();
uint32_t imageIndex() const { return window->mPresentIndex; }
vk::Semaphore imageReady() const { return window->mImageReady; }
vk::Semaphore renderingComplete() const {
return window->mRenderingComplete;
}
SharedImage const &image() const {
return window->mChainImages[window->mPresentIndex];
}
private:
Frame(DisplayWindow *parent);
DisplayWindow *window;
friend class DisplayWindow;
};
vk::DisplayKHR display() const { return mDisplay; }
vk::DisplayModeKHR mode() const { return mMode; }
vk::SurfaceKHR const &surface() const { return mSurface; }
vk::SurfaceFormatKHR const &surfaceFormat() const { return mChainFormat; }
vk::SwapchainCreateInfoKHR const &swapchainInfo() const {
return mSwapchainInfo;
}
size_t swapchainImageCount() const { return mChainImages.size(); }
std::vector<SharedImage> const &chainImages() const { return mChainImages; }
std::vector<SharedImageView> const &chainViews() const {
return mChainViews;
}
protected:
void buildSwapchain();
SharedDevice mDevice;
vk::SurfaceFormatKHR mChainFormat;
uint32_t mWidth;
uint32_t mHeight;
vk::DisplayKHR mDisplay;
vk::DisplayModeKHR mMode;
vk::SurfaceKHR mSurface;
vk::SwapchainKHR mChain;
vk::Semaphore mImageReady;
vk::Semaphore mRenderingComplete;
mutable bool mRenderingCompleteCalled = false;
uint32_t mPresentIndex;
lava::Queue *mQueue;
std::vector<SharedImage> mChainImages;
std::vector<SharedImageView> mChainViews;
SwapchainBuildHandler mSwapchainHandler;
vk::DisplaySurfaceCreateInfoKHR mSurfaceInfo;
vk::SwapchainCreateInfoKHR mSwapchainInfo;
friend class Frame;
};
} // namespace display
} // namespace lava
0% Loading or .
You are about to add 0 people to the discussion. Proceed with caution.
Please register or to comment