videoout| handle swapchain with imageHandler

This commit is contained in:
Martin Baliet 2024-03-29 16:00:25 +01:00
parent 8e459d7576
commit ed0112d595
14 changed files with 423 additions and 289 deletions

View File

@ -117,6 +117,12 @@ class IGraphics {
*/
virtual void deinit() = 0;
/**
* @brief Call before deinit, stops all incoming requests
*
*/
virtual void stop() = 0;
/**
* @brief Register a display buffer
*

View File

@ -2,9 +2,10 @@ add_library(videoout OBJECT
videoout.cpp
vulkan/vulkanSetup.cpp
vulkan/vulkanHelper.cpp
imageHandler.cpp
)
add_dependencies(videoout third_party psOff_utility initParams imports)
add_dependencies(videoout third_party psOff_utility initParams imports boost)
target_include_directories(videoout PRIVATE
${Vulkan_INCLUDE_DIRS}
${PRJ_SRC_DIR}/third_party/optick/src

View File

@ -0,0 +1,238 @@
#include "imageHandler.h"
#include "logging.h"
#include "vulkan/vulkanSetup.h"
#include <boost/thread/thread.hpp>
#include <vulkan/vk_enum_string_helper.h>
LOG_DEFINE_MODULE(ImageHandler);
constexpr uint32_t NUM_DISPLAY_BUFFERS = 3;
class ImageHandler: public IImageHandler {
uint32_t m_maxImages = 1;
VkDevice m_device;
// Vulkan internal
VkSwapchainKHR m_swapchain = nullptr;
VkCommandPool m_commandPool = nullptr;
std::vector<VkSemaphore> m_semsImageReady;
std::vector<VkSemaphore> m_semsImageCopied;
std::vector<VkCommandBuffer> m_commandBuffer;
size_t m_nextIndex = 0;
std::vector<VkImage> m_scImages;
// - vulkan
boost::mutex m_mutexInt;
size_t m_waitId = 0;
size_t m_presentCount = 1;
boost::condition_variable m_present_condVar; /// regulates number of images
bool m_stop = false;
public:
ImageHandler(VkDevice vkDevice, VkExtent2D extentWindow, vulkan::QueueInfo* queue): IImageHandler(extentWindow, queue), m_device(vkDevice) {};
virtual ~ImageHandler() = default;
// ### Interace
void init(vulkan::VulkanObj* obj, VkSurfaceKHR surface) final;
void deinit() final;
void stop() final {
m_stop = true;
m_present_condVar.notify_all();
}
std::optional<ImageData> getImage_blocking() final;
void notify_done(ImageData const&) final;
VkSwapchainKHR getSwapchain() const final { return m_swapchain; }
};
std::unique_ptr<IImageHandler> createImageHandler(VkDevice vkDevice, VkExtent2D extentWindow, vulkan::QueueInfo* queue) {
return std::make_unique<ImageHandler>(vkDevice, extentWindow, queue);
}
std::optional<ImageData> ImageHandler::getImage_blocking() {
LOG_USE_MODULE(ImageHandler);
boost::unique_lock lock(m_mutexInt);
// Regulate max images
auto waitId = ++m_waitId;
LOG_DEBUG(L"waitId:%llu presentCount:%llu", waitId, m_presentCount);
m_present_condVar.wait(lock, [this, &waitId] { return m_stop || waitId == m_presentCount; });
if (m_stop) return {};
// -
ImageData imageData;
imageData.semImageReady = m_semsImageReady[m_nextIndex];
imageData.semImageCopied = m_semsImageCopied[m_nextIndex];
imageData.cmdBuffer = m_commandBuffer[m_nextIndex];
// Get swapchain image
{
int numTries = 2;
VkResult result = VK_SUCCESS;
for (; numTries >= 0; --numTries) {
if (result = vkAcquireNextImageKHR(m_device, m_swapchain, UINT64_MAX, imageData.semImageReady, VK_NULL_HANDLE, &imageData.index); result != VK_SUCCESS) {
if (result == VK_NOT_READY) {
std::this_thread::sleep_for(std::chrono::milliseconds(100));
}
} else
break;
}
if (numTries <= 0) {
LOG_ERR(L"vkAcquireNextImageKHR %S", string_VkResult(result));
++m_presentCount; // fake present
lock.unlock();
m_present_condVar.notify_all();
return {};
}
}
// - swapchain image
m_nextIndex = (++m_nextIndex) % m_maxImages;
imageData.swapchainImage = m_scImages[imageData.index];
imageData.extent = m_extentWindow;
{ // Begin commandbuffer| end is done by user
VkCommandBufferBeginInfo const beginInfo {
.sType = VK_STRUCTURE_TYPE_COMMAND_BUFFER_BEGIN_INFO,
.flags = 0,
.pInheritanceInfo = nullptr,
};
if (auto result = vkBeginCommandBuffer(imageData.cmdBuffer, &beginInfo); result != VK_SUCCESS) {
LOG_CRIT(L"Error vkBeginCommandBuffer: %S", string_VkResult(result));
}
}
return imageData;
}
void ImageHandler::notify_done(ImageData const& imageData) {
LOG_USE_MODULE(ImageHandler);
boost::unique_lock lock(m_mutexInt);
vkResetCommandBuffer(imageData.cmdBuffer, VK_COMMAND_BUFFER_RESET_RELEASE_RESOURCES_BIT);
++m_presentCount;
lock.unlock();
m_present_condVar.notify_all();
}
void ImageHandler::init(vulkan::VulkanObj* obj, VkSurfaceKHR surface) {
LOG_USE_MODULE(ImageHandler);
{ // Create Display ready sems
VkSemaphoreCreateInfo const semCreateInfo {
.sType = VK_STRUCTURE_TYPE_SEMAPHORE_CREATE_INFO,
.pNext = 0,
.flags = 0,
};
m_semsImageReady.resize(m_maxImages);
m_semsImageCopied.resize(m_maxImages);
for (size_t n = 0; n < m_maxImages; ++n) {
vkCreateSemaphore(m_device, &semCreateInfo, nullptr, &m_semsImageReady[n]);
vkCreateSemaphore(m_device, &semCreateInfo, nullptr, &m_semsImageCopied[n]);
}
}
auto numBuffers = NUM_DISPLAY_BUFFERS;
{ // swapchain
numBuffers = std::clamp(numBuffers, obj->surfaceCapabilities.capabilities.minImageCount, obj->surfaceCapabilities.capabilities.maxImageCount);
m_extentWindow.width = std::clamp(m_extentWindow.width, obj->surfaceCapabilities.capabilities.minImageExtent.width,
obj->surfaceCapabilities.capabilities.maxImageExtent.width);
m_extentWindow.height = std::clamp(m_extentWindow.height, obj->surfaceCapabilities.capabilities.minImageExtent.height,
obj->surfaceCapabilities.capabilities.maxImageExtent.height);
auto [displayFormat, displayColorSpace] = getDisplayFormat(obj);
m_imageFormat = displayFormat;
VkSwapchainCreateInfoKHR const createInfo {
.sType = VK_STRUCTURE_TYPE_SWAPCHAIN_CREATE_INFO_KHR,
.pNext = nullptr,
.flags = 0,
.surface = surface,
.minImageCount = numBuffers,
.imageFormat = m_imageFormat,
.imageColorSpace = displayColorSpace,
.imageExtent = m_extentWindow,
.imageArrayLayers = 1,
.imageUsage = VK_IMAGE_USAGE_COLOR_ATTACHMENT_BIT | VK_IMAGE_USAGE_TRANSFER_DST_BIT,
.imageSharingMode = VK_SHARING_MODE_EXCLUSIVE,
.queueFamilyIndexCount = 0,
.pQueueFamilyIndices = nullptr,
.preTransform = obj->surfaceCapabilities.capabilities.currentTransform,
.compositeAlpha = VK_COMPOSITE_ALPHA_OPAQUE_BIT_KHR,
.presentMode = VK_PRESENT_MODE_FIFO_KHR, // todo config vsync
.clipped = VK_TRUE,
.oldSwapchain = nullptr,
};
vkCreateSwapchainKHR(obj->deviceInfo.device, &createInfo, nullptr, &m_swapchain);
}
{ // swapchain images
uint32_t numImages = 0;
vkGetSwapchainImagesKHR(m_device, m_swapchain, &numImages, nullptr);
m_scImages.resize(numImages);
vkGetSwapchainImagesKHR(m_device, m_swapchain, &numImages, m_scImages.data());
}
{ // Command buffer
VkCommandPoolCreateInfo const poolInfo {
.sType = VK_STRUCTURE_TYPE_COMMAND_POOL_CREATE_INFO,
.flags = VK_COMMAND_POOL_CREATE_RESET_COMMAND_BUFFER_BIT,
.queueFamilyIndex = m_queue->family,
};
if (auto result = vkCreateCommandPool(obj->deviceInfo.device, &poolInfo, nullptr, &m_commandPool); result != VK_SUCCESS) {
LOG_CRIT(L"Couldn't create commandpool(graphics): %d", result);
}
m_commandBuffer.resize(m_maxImages);
VkCommandBufferAllocateInfo const allocInfo {
.sType = VK_STRUCTURE_TYPE_COMMAND_BUFFER_ALLOCATE_INFO,
.commandPool = m_commandPool,
.level = VK_COMMAND_BUFFER_LEVEL_PRIMARY,
.commandBufferCount = m_maxImages,
};
if (auto result = vkAllocateCommandBuffers(obj->deviceInfo.device, &allocInfo, m_commandBuffer.data()); result != VK_SUCCESS) {
LOG_CRIT(L"Couldn't create commandbuffers(graphics): %d", result);
}
}
}
void ImageHandler::deinit() {
printf("deinit ImageHandler\n");
for (auto& sem: m_semsImageReady) {
vkDestroySemaphore(m_device, sem, nullptr);
}
if (m_commandPool != nullptr) vkDestroyCommandPool(m_device, m_commandPool, nullptr);
if (m_swapchain != nullptr) vkDestroySwapchainKHR(m_device, m_swapchain, nullptr);
printf("deinit ImageHandler| done\n");
}

View File

@ -0,0 +1,54 @@
#pragma once
#include "utility/utility.h"
#include <optional>
#include <vulkan/vulkan_core.h>
struct ImageData {
VkImage swapchainImage = nullptr;
VkSemaphore semImageReady = nullptr; /// image is ready for operation
VkSemaphore semImageCopied = nullptr; /// Image has been copied, ready to swap
VkCommandBuffer cmdBuffer = nullptr; /// Commandbuffer to be used for transer etc. Already called begin!
VkExtent2D extent;
uint32_t index = 0;
};
namespace vulkan {
struct VulkanObj;
struct QueueInfo;
} // namespace vulkan
class IImageHandler {
CLASS_NO_COPY(IImageHandler);
CLASS_NO_MOVE(IImageHandler);
protected:
IImageHandler(VkExtent2D extentWindow, vulkan::QueueInfo* queue): m_extentWindow(extentWindow), m_queue(queue) {}
VkExtent2D m_extentWindow;
VkFormat m_imageFormat;
vulkan::QueueInfo* m_queue;
public:
virtual ~IImageHandler() = default;
auto getExtent() const { return m_extentWindow; }
auto getFormat() const { return m_imageFormat; }
auto getQueue() const { return m_queue; }
virtual void init(vulkan::VulkanObj* obj, VkSurfaceKHR surface) = 0;
virtual void deinit() = 0;
virtual void stop() = 0;
virtual std::optional<ImageData> getImage_blocking() = 0;
virtual void notify_done(ImageData const&) = 0;
virtual VkSwapchainKHR getSwapchain() const = 0;
};
std::unique_ptr<IImageHandler> createImageHandler(VkDevice vkDevice, VkExtent2D extentWindow, vulkan::QueueInfo* queue);

View File

@ -8,3 +8,16 @@
![](../../out/docs/uml/modules/videoout_class.svg)
</div>
### Swapchain image handling
User calls getImage_blocking() to retrieve the display image. After present notify_done() is called.
getImage_blocking() releases users FIFO. The whole swapchain images are handled by ImageHandler
<div align="center">
![](../../out/docs/uml/modules/videout_swapchain.svg)
</div>

View File

@ -10,6 +10,7 @@
#include "core/kernel/eventqueue.h"
#include "core/systemContent/systemContent.h"
#include "core/timer/timer.h"
#include "imageHandler.h"
#include "logging.h"
#include "modules/libSceVideoOut/codes.h"
#include "modules/libSceVideoOut/types.h"
@ -129,7 +130,7 @@ struct Message {
int index = 0;
uint32_t setIndex = 0;
vulkan::PresentData presentData = {};
ImageData imageData = {};
};
std::string getTitle(int handle, uint64_t frame, size_t fps, FlipRate maxFPS) {
@ -152,12 +153,14 @@ class VideoOut: public IVideoOut, private IEventsGraphics {
mutable std::mutex m_mutexInt;
vulkan::VulkanObj* m_vulkanObj = nullptr;
std::unique_ptr<IGraphics> m_graphics;
std::thread m_threadSDL2;
std::condition_variable m_condSDL2;
std::condition_variable m_condDone;
bool m_stop = false;
std::queue<Message> m_messages;
std::unique_ptr<IGraphics> m_graphics;
std::unique_ptr<IImageHandler> m_imageHandler;
std::thread m_threadSDL2;
std::condition_variable m_condSDL2;
std::condition_variable m_condDone;
bool m_stop = false;
std::queue<Message> m_messages;
uint64_t m_vblankTime = (uint64_t)(1e6 / 59.0); // in us
@ -178,15 +181,7 @@ class VideoOut: public IVideoOut, private IEventsGraphics {
auto& swapchain = window.config.bufferSets[setIndex];
auto& displayBufferMeta = swapchain.buffers[index];
LOG_DEBUG(L"-> eventDoFlip(%d):%u %d", handle, setIndex, index);
auto const presentData = transferDisplay(swapchain, displayBufferMeta, waitSema, waitValue);
if (presentData.displayReady == nullptr) {
LOG_ERR(L"<- submitFlip(%d):%u %d error", handle, setIndex, index);
m_graphics->submitDone();
doFlip(window, handle);
return;
}
auto imageData = m_imageHandler->getImage_blocking();
auto& flipStatus = window.config.flipStatus;
++flipStatus.gcQueueNum;
@ -196,9 +191,20 @@ class VideoOut: public IVideoOut, private IEventsGraphics {
auto const curTime = (uint64_t)(1e9 * timer.getTimeS());
flipStatus.submitTsc = curTime;
if (!imageData) {
LOG_ERR(L"<- submitFlip(%d):%u %d error", handle, setIndex, index);
m_graphics->submitDone();
doFlip(window, handle);
return;
}
LOG_DEBUG(L"-> eventDoFlip(%d):%u %d", handle, setIndex, index);
transferDisplay(*imageData, displayBufferMeta, waitSema, waitValue);
// window.config.flipStatus.currentBuffer = index; // set after flip, before vblank
m_messages.push({MessageType::flip, handle - 1, nullptr, index, setIndex, presentData});
m_messages.push({MessageType::flip, handle - 1, nullptr, index, setIndex, *imageData});
lock.unlock();
m_condSDL2.notify_one();
@ -208,8 +214,7 @@ class VideoOut: public IVideoOut, private IEventsGraphics {
vulkan::QueueInfo* getQueue(vulkan::QueueType type) final;
// -
vulkan::PresentData transferDisplay(vulkan::SwapchainData& swapchain, vulkan::SwapchainData::DisplayBuffers& displayBufferMeta, VkSemaphore waitSema,
size_t waitValue);
void transferDisplay(ImageData const& imageData, vulkan::SwapchainData::DisplayBuffers& displayBufferMeta, VkSemaphore waitSema, size_t waitValue);
std::thread createSDLThread();
@ -290,27 +295,24 @@ VideoOut::~VideoOut() {
m_stop = true;
m_condSDL2.notify_one();
for (auto& window: m_windows) {
if (window.userId < 0) continue;
for (size_t n = 0; n < window.config.buffersSetsCount; ++n) {
auto& bufferSet = window.config.bufferSets[n];
destroySwapchain(m_vulkanObj, bufferSet);
}
}
// printf("VideoOut| waiting on gpu idle\n");
m_imageHandler->stop();
m_graphics->stop();
vkQueueWaitIdle(m_imageHandler->getQueue()->queue);
// shutdown graphics first (uses vulkan)
m_graphics->deinit();
m_graphics.reset();
printf("VideoOut| waiting on gpu idle\n");
vkQueueWaitIdle(m_vulkanObj->queues.items[vulkan::getIndex(vulkan::QueueType::present)][0]->queue);
// printf("VideoOut| Destroy surface\n");
// for (auto& window: m_windows) {
// if (window.userId < 0) continue;
// if (window.surface != nullptr) vkDestroySurfaceKHR(m_vulkanObj->deviceInfo.instance, window.surface, nullptr);
// }
m_imageHandler->deinit();
m_imageHandler.reset();
printf("VideoOut| Destroy vulkan\n");
deinitVulkan(m_vulkanObj);
@ -438,11 +440,10 @@ void VideoOut::removeEvent(int handle, Kernel::EventQueue::IKernelEqueue_t eq, i
}
}
vulkan::PresentData VideoOut::transferDisplay(vulkan::SwapchainData& swapchain, vulkan::SwapchainData::DisplayBuffers& displayBufferMeta, VkSemaphore waitSema,
size_t waitValue) {
auto presentData = vulkan::transfer2Display(&displayBufferMeta, m_vulkanObj, swapchain, m_graphics.get());
if (presentData.displayReady != nullptr) vulkan::submitDisplayTransfer(m_vulkanObj, &displayBufferMeta, &presentData, waitSema, waitValue);
return presentData;
void VideoOut::transferDisplay(ImageData const& imageData, vulkan::SwapchainData::DisplayBuffers& displayBufferMeta, VkSemaphore waitSema, size_t waitValue) {
vulkan::transfer2Display(&displayBufferMeta, imageData, m_graphics.get());
vulkan::submitDisplayTransfer(&displayBufferMeta, imageData, m_imageHandler->getQueue(), waitSema, waitValue);
}
void VideoOut::submitFlip(int handle, int index, int64_t flipArg) {
@ -461,13 +462,7 @@ void VideoOut::submitFlip(int handle, int index, int64_t flipArg) {
LOG_DEBUG(L"-> submitFlip(%d):%u %d", handle, setIndex, index);
auto const presentData = transferDisplay(swapchain, displayBufferMeta, nullptr, 0);
if (presentData.displayReady == nullptr) {
LOG_ERR(L"<- submitFlip(%d):%u %d error", handle, setIndex, index);
m_graphics->submitDone();
doFlip(window, handle);
return;
}
auto imageData = m_imageHandler->getImage_blocking();
auto& flipStatus = window.config.flipStatus;
++flipStatus.gcQueueNum;
@ -477,9 +472,18 @@ void VideoOut::submitFlip(int handle, int index, int64_t flipArg) {
auto const curTime = (uint64_t)(1e9 * timer.getTimeS());
flipStatus.submitTsc = curTime;
if (!imageData) {
LOG_ERR(L"<- submitFlip(%d):%u %d error", handle, setIndex, index);
m_graphics->submitDone();
doFlip(window, handle);
return;
}
transferDisplay(*imageData, displayBufferMeta, nullptr, 0);
// window.config.flipStatus.currentBuffer = index; // set after flip, before vblank
m_messages.push({MessageType::flip, handle - 1, nullptr, index, setIndex, presentData});
m_messages.push({MessageType::flip, handle - 1, nullptr, index, setIndex, *imageData});
lock.unlock();
m_condSDL2.notify_one();
@ -596,7 +600,7 @@ int VideoOut::registerBuffers(int handle, int startIndex, void* const* addresses
bufferSet.buffers[n].bufferSize = displaySize;
bufferSet.buffers[n].bufferAlign = displaySizeAlign;
LOG_INFO(L"+bufferset[%d] buffer:%d vaddr:0x%08llx", setIndex, n, (uint64_t)addresses[n]);
LOG_INFO(L"+bufferset[%d] buffer:%d vaddr:0x%08llx-0x%08llx", setIndex, n, (uint64_t)addresses[n], (uint64_t)addresses[n] + displaySizeAlign);
auto [format, colorSpace] = vulkan::getDisplayFormat(m_vulkanObj);
if (!m_graphics->registerDisplayBuffer(bufferSet.buffers[n].bufferVaddr, VkExtent2D {.width = _att->width, .height = _att->height}, _att->pitchInPixel,
@ -604,7 +608,6 @@ int VideoOut::registerBuffers(int handle, int startIndex, void* const* addresses
return -1;
}
createData(m_vulkanObj, m_windows[handle - 1].surface, bufferSet, _att->width, _att->height, accessInitParams()->useVSYNC());
return setIndex;
}
@ -724,11 +727,16 @@ std::thread VideoOut::createSDLThread() {
auto& info = m_vulkanObj->deviceInfo;
m_graphics = createGraphics(*this, info.device, info.physicalDevice, info.instance);
auto queue = m_vulkanObj->queues.items[getIndex(vulkan::QueueType::present)][0].get(); // todo use getQeueu
m_imageHandler = createImageHandler(info.device, VkExtent2D {window.config.resolution.paneWidth, window.config.resolution.paneHeight}, queue);
m_imageHandler->init(m_vulkanObj, window.surface);
*item.done = true;
} else {
vulkan::createSurface(m_vulkanObj, window.window, window.surface);
}
*item.done = true;
m_condDone.notify_one();
} break;
case MessageType::close: {
@ -737,15 +745,14 @@ std::thread VideoOut::createSDLThread() {
m_condDone.notify_one();
} break;
case MessageType::flip: {
LOG_DEBUG(L"-> flip(%d) set:%u buffer:%u", item.index, item.setIndex, item.presentData.index);
LOG_DEBUG(L"-> flip(%d) set:%u buffer:%u", item.index, item.setIndex, item.imageData.index);
OPTICK_FRAME("VideoOut");
auto& flipStatus = window.config.flipStatus;
{
OPTICK_EVENT("Present");
if (!presentImage(m_vulkanObj, window.config.bufferSets[item.setIndex], item.presentData)) {
exit(1);
}
presentImage(item.imageData, m_imageHandler->getSwapchain(), m_imageHandler->getQueue());
m_imageHandler->notify_done(item.imageData);
}
m_graphics->submitDone();
@ -764,7 +771,7 @@ std::thread VideoOut::createSDLThread() {
SDL_SetWindowTitle(window.window, title.c_str());
func_pollSDL(window.window);
LOG_DEBUG(L"<- flip(%d) set:%u buffer:%u", handleIndex, item.setIndex, item.presentData.index);
LOG_DEBUG(L"<- flip(%d) set:%u buffer:%u", handleIndex, item.setIndex, item.imageData.index);
} break;
}
m_messages.pop();

View File

@ -1,5 +1,6 @@
#include "vulkanHelper.h"
#include "../imageHandler.h"
#include "core/imports/exports/graphics.h"
#include "logging.h"
#include "utility/utility.h"
@ -46,108 +47,7 @@ std::pair<VkFormat, VkColorSpaceKHR> getDisplayFormat(VulkanObj* obj) {
return {format, imageColorSpace};
}
void createData(VulkanObj* obj, VkSurfaceKHR surface, vulkan::SwapchainData& swapchainData, uint32_t width, uint32_t height, bool enableVsync) {
LOG_USE_MODULE(vulkanHelper);
uint32_t const numBuffers = swapchainData.buffers.size();
if (numBuffers > obj->surfaceCapabilities.capabilities.maxImageCount || numBuffers < obj->surfaceCapabilities.capabilities.minImageCount) {
LOG_CRIT(L"numBuffers:%d outside %d:%d", numBuffers, obj->surfaceCapabilities.capabilities.minImageCount,
obj->surfaceCapabilities.capabilities.maxImageCount);
}
swapchainData.extent2d.width =
std::clamp(width, obj->surfaceCapabilities.capabilities.minImageExtent.width, obj->surfaceCapabilities.capabilities.maxImageExtent.width);
swapchainData.extent2d.height =
std::clamp(height, obj->surfaceCapabilities.capabilities.minImageExtent.height, obj->surfaceCapabilities.capabilities.maxImageExtent.height);
auto [displayFormat, displayColorSpace] = getDisplayFormat(obj);
swapchainData.format = displayFormat;
VkSwapchainCreateInfoKHR const createInfo {
.sType = VK_STRUCTURE_TYPE_SWAPCHAIN_CREATE_INFO_KHR,
.pNext = nullptr,
.flags = 0,
.surface = surface,
.minImageCount = numBuffers,
.imageFormat = swapchainData.format,
.imageColorSpace = displayColorSpace,
.imageExtent = swapchainData.extent2d,
.imageArrayLayers = 1,
.imageUsage = VK_IMAGE_USAGE_COLOR_ATTACHMENT_BIT | VK_IMAGE_USAGE_TRANSFER_DST_BIT,
.imageSharingMode = VK_SHARING_MODE_EXCLUSIVE,
.queueFamilyIndexCount = 0,
.pQueueFamilyIndices = nullptr,
.preTransform = obj->surfaceCapabilities.capabilities.currentTransform,
.compositeAlpha = VK_COMPOSITE_ALPHA_OPAQUE_BIT_KHR,
.presentMode = enableVsync ? VK_PRESENT_MODE_FIFO_KHR : VK_PRESENT_MODE_IMMEDIATE_KHR,
.clipped = VK_TRUE,
.oldSwapchain = nullptr,
};
vkCreateSwapchainKHR(obj->deviceInfo.device, &createInfo, nullptr, &swapchainData.swapchain);
uint32_t numImages = numBuffers;
vkGetSwapchainImagesKHR(obj->deviceInfo.device, swapchainData.swapchain, &numImages, nullptr);
BOOST_ASSERT_MSG(numImages == numBuffers, "Swapchain created more images");
{ // Create Semaphore + imageView
std::vector<VkImage> images(numImages);
vkGetSwapchainImagesKHR(obj->deviceInfo.device, swapchainData.swapchain, &numImages, images.data());
VkSemaphoreCreateInfo const semCreateInfo {
.sType = VK_STRUCTURE_TYPE_SEMAPHORE_CREATE_INFO,
.pNext = 0,
.flags = 0,
};
VkFenceCreateInfo const fenceCreateInfo {
.sType = VK_STRUCTURE_TYPE_FENCE_CREATE_INFO,
.pNext = 0,
.flags = VK_FENCE_CREATE_SIGNALED_BIT,
};
for (uint8_t i = 0; i < numImages; ++i) {
auto& buffer = swapchainData.buffers[i];
buffer.image = images[i];
vkCreateSemaphore(obj->deviceInfo.device, &semCreateInfo, nullptr, &buffer.semDisplayReady);
vkCreateSemaphore(obj->deviceInfo.device, &semCreateInfo, nullptr, &buffer.semPresentReady);
vkCreateFence(obj->deviceInfo.device, &fenceCreateInfo, nullptr, &buffer.bufferFence);
}
}
// Flip data
{
VkCommandPoolCreateInfo const poolInfo {
.sType = VK_STRUCTURE_TYPE_COMMAND_POOL_CREATE_INFO,
.flags = VK_COMMAND_POOL_CREATE_RESET_COMMAND_BUFFER_BIT,
.queueFamilyIndex = obj->queues.items[getIndex(QueueType::graphics)][0]->family,
};
if (auto result = vkCreateCommandPool(obj->deviceInfo.device, &poolInfo, nullptr, &swapchainData.commandPool); result != VK_SUCCESS) {
LOG_CRIT(L"Couldn't create commandpool(graphics): %d", result);
}
}
{
VkCommandBufferAllocateInfo const allocInfo {
.sType = VK_STRUCTURE_TYPE_COMMAND_BUFFER_ALLOCATE_INFO,
.commandPool = swapchainData.commandPool,
.level = VK_COMMAND_BUFFER_LEVEL_PRIMARY,
.commandBufferCount = 1,
};
for (uint8_t i = 0; i < numImages; ++i) {
if (auto result = vkAllocateCommandBuffers(obj->deviceInfo.device, &allocInfo, &swapchainData.buffers[i].transferBuffer); result != VK_SUCCESS) {
LOG_CRIT(L"Couldn't create commandbuffers(graphics): %d", result);
}
}
}
// - Flip Data
}
void submitDisplayTransfer(VulkanObj* obj, SwapchainData::DisplayBuffers const* displayBuffer, PresentData* presentData, VkSemaphore waitSema,
void submitDisplayTransfer(SwapchainData::DisplayBuffers const* displayBuffer, ImageData const& imageData, QueueInfo const* queue, VkSemaphore waitSema,
size_t waitValue) {
LOG_USE_MODULE(vulkanHelper);
@ -161,9 +61,7 @@ void submitDisplayTransfer(VulkanObj* obj, SwapchainData::DisplayBuffers const*
};
VkPipelineStageFlags waitStage[] = {VK_PIPELINE_STAGE_TRANSFER_BIT, VK_PIPELINE_STAGE_TRANSFER_BIT};
VkSemaphore sems[] = {presentData->displayReady, waitSema};
presentData->presentReady = displayBuffer->semPresentReady;
VkSemaphore sems[] = {imageData.semImageReady, waitSema};
VkSubmitInfo const submitInfo {
.sType = VK_STRUCTURE_TYPE_SUBMIT_INFO,
@ -174,78 +72,23 @@ void submitDisplayTransfer(VulkanObj* obj, SwapchainData::DisplayBuffers const*
.pWaitDstStageMask = waitStage,
.commandBufferCount = 1,
.pCommandBuffers = &displayBuffer->transferBuffer,
.pCommandBuffers = &imageData.cmdBuffer,
.signalSemaphoreCount = 1,
.pSignalSemaphores = &presentData->presentReady,
.pSignalSemaphores = &imageData.semImageCopied,
};
{
auto& queue = obj->queues.items[getIndex(QueueType::present)][0];
std::unique_lock lock(queue->mutex);
if (VkResult result = vkQueueSubmit(queue->queue, 1, &submitInfo, displayBuffer->bufferFence); result != VK_SUCCESS) {
if (VkResult result = vkQueueSubmit(queue->queue, 1, &submitInfo, nullptr); result != VK_SUCCESS) {
LOG_CRIT(L"Couldn't vkQueueSubmit Transfer %S", string_VkResult(result));
}
}
}
PresentData transfer2Display(SwapchainData::DisplayBuffers const* displayBuffer, VulkanObj* obj, vulkan::SwapchainData& swapchain, IGraphics* graphics) {
void transfer2Display(SwapchainData::DisplayBuffers const* displayBuffer, ImageData const& imageData, IGraphics* graphics) {
LOG_USE_MODULE(vulkanHelper);
// Wait on prev present (only one image is allowed )
{
boost::unique_lock lock(swapchain.mutexPresent);
swapchain.condPresent.wait(lock, [&swapchain] { return swapchain.waitId == swapchain.presentId; });
++swapchain.waitId;
}
// -
PresentData presentData;
// Get swapchain image
presentData.displayReady = displayBuffer->semDisplayReady;
{
int n = 2;
VkResult result = VK_SUCCESS;
for (; n >= 0; --n) {
if (result = vkAcquireNextImageKHR(obj->deviceInfo.device, swapchain.swapchain, UINT64_MAX, presentData.displayReady, VK_NULL_HANDLE, &presentData.index);
result != VK_SUCCESS) {
if (result == VK_NOT_READY) {
std::this_thread::sleep_for(std::chrono::milliseconds(100));
}
} else
break;
}
if (n <= 0) {
LOG_ERR(L"vkAcquireNextImageKHR %S", string_VkResult(result));
swapchain.waitId--; // would deadlock on next call
return {};
}
}
presentData.swapchainImage = swapchain.buffers[presentData.index].image;
// -
vkWaitForFences(obj->deviceInfo.device, 1, &displayBuffer->bufferFence, VK_TRUE, UINT64_MAX);
vkResetFences(obj->deviceInfo.device, 1, &displayBuffer->bufferFence);
auto cmdBuffer = displayBuffer->transferBuffer;
// Wait and begin command buffer
vkResetCommandBuffer(cmdBuffer, VK_COMMAND_BUFFER_RESET_RELEASE_RESOURCES_BIT);
// Transfer
VkCommandBufferBeginInfo const beginInfo {
.sType = VK_STRUCTURE_TYPE_COMMAND_BUFFER_BEGIN_INFO,
.flags = VK_COMMAND_BUFFER_USAGE_SIMULTANEOUS_USE_BIT,
.pInheritanceInfo = nullptr,
};
if (vkBeginCommandBuffer(cmdBuffer, &beginInfo) != VK_SUCCESS) {
LOG_CRIT(L"Error vkBeginCommandBuffer");
}
// - begin command buffer
{
VkImageMemoryBarrier const barrier {
.sType = VK_STRUCTURE_TYPE_IMAGE_MEMORY_BARRIER,
@ -257,13 +100,13 @@ PresentData transfer2Display(SwapchainData::DisplayBuffers const* displayBuffer,
.srcQueueFamilyIndex = VK_QUEUE_FAMILY_IGNORED,
.dstQueueFamilyIndex = VK_QUEUE_FAMILY_IGNORED,
.image = presentData.swapchainImage,
.image = imageData.swapchainImage,
.subresourceRange = {.aspectMask = VK_IMAGE_ASPECT_COLOR_BIT, .baseMipLevel = 0, .levelCount = 1, .baseArrayLayer = 0, .layerCount = 1}};
vkCmdPipelineBarrier(cmdBuffer, VK_PIPELINE_STAGE_TRANSFER_BIT, VK_PIPELINE_STAGE_TRANSFER_BIT, 0, 0, nullptr, 0, nullptr, 1, &barrier);
vkCmdPipelineBarrier(imageData.cmdBuffer, VK_PIPELINE_STAGE_TRANSFER_BIT, VK_PIPELINE_STAGE_TRANSFER_BIT, 0, 0, nullptr, 0, nullptr, 1, &barrier);
}
graphics->copyDisplayBuffer(displayBuffer->bufferVaddr, cmdBuffer, presentData.swapchainImage, swapchain.extent2d); // let gpumemorymanager decide
graphics->copyDisplayBuffer(displayBuffer->bufferVaddr, imageData.cmdBuffer, imageData.swapchainImage, imageData.extent); // let gpumemorymanager decide
{
// Change to Present Layout
@ -277,50 +120,41 @@ PresentData transfer2Display(SwapchainData::DisplayBuffers const* displayBuffer,
.srcQueueFamilyIndex = VK_QUEUE_FAMILY_IGNORED,
.dstQueueFamilyIndex = VK_QUEUE_FAMILY_IGNORED,
.image = presentData.swapchainImage,
.image = imageData.swapchainImage,
.subresourceRange = {.aspectMask = VK_IMAGE_ASPECT_COLOR_BIT, .baseMipLevel = 0, .levelCount = 1, .baseArrayLayer = 0, .layerCount = 1}};
vkCmdPipelineBarrier(cmdBuffer, VK_PIPELINE_STAGE_TRANSFER_BIT, VK_PIPELINE_STAGE_TRANSFER_BIT, 0, 0, nullptr, 0, nullptr, 1, &barrier);
vkCmdPipelineBarrier(imageData.cmdBuffer, VK_PIPELINE_STAGE_TRANSFER_BIT, VK_PIPELINE_STAGE_TRANSFER_BIT, 0, 0, nullptr, 0, nullptr, 1, &barrier);
}
// - Present layout
// End CmdBuffer -> Submit
if (vkEndCommandBuffer(cmdBuffer) != VK_SUCCESS) {
if (vkEndCommandBuffer(imageData.cmdBuffer) != VK_SUCCESS) {
LOG_CRIT(L"Couldn't end commandbuffer");
}
// -
return presentData;
}
bool presentImage(VulkanObj* obj, vulkan::SwapchainData& swapchain, vulkan::PresentData const& presentData) {
void presentImage(ImageData const& imageData, VkSwapchainKHR swapchain, QueueInfo const* queue) {
LOG_USE_MODULE(vulkanHelper);
VkPresentInfoKHR const presentInfo {
.sType = VK_STRUCTURE_TYPE_PRESENT_INFO_KHR,
.pNext = nullptr,
.waitSemaphoreCount = 1,
.pWaitSemaphores = &presentData.presentReady,
.pWaitSemaphores = &imageData.semImageCopied,
.swapchainCount = 1,
.pSwapchains = &swapchain.swapchain,
.pImageIndices = &presentData.index,
.pSwapchains = &swapchain,
.pImageIndices = &imageData.index,
.pResults = nullptr,
};
{
OPTICK_GPU_FLIP(&swapchain.swapchain);
OPTICK_GPU_FLIP(&swapchain);
OPTICK_CATEGORY("Present", Optick::Category::Wait);
auto& queue = obj->queues.items[getIndex(QueueType::present)][0];
std::unique_lock lock(queue->mutex);
vkQueuePresentKHR(queue->queue, &presentInfo);
}
++swapchain.presentId;
swapchain.condPresent.notify_all();
return true;
}
} // namespace vulkan

View File

@ -5,6 +5,7 @@
#include <functional>
class IGraphics;
struct ImageData;
namespace vulkan {
struct PresentData {
@ -14,12 +15,13 @@ struct PresentData {
uint32_t index = 0;
};
void submitDisplayTransfer(VulkanObj* obj, SwapchainData::DisplayBuffers const* displayBuffer, PresentData* presentData, VkSemaphore waitSema,
std::pair<VkFormat, VkColorSpaceKHR> getDisplayFormat(VulkanObj* obj);
void submitDisplayTransfer(SwapchainData::DisplayBuffers const* displayBuffer, ImageData const& imageData, QueueInfo const* queue, VkSemaphore waitSema,
size_t waitValue);
PresentData transfer2Display(SwapchainData::DisplayBuffers const* displayBuffer, VulkanObj* obj, vulkan::SwapchainData& swapchain, IGraphics* graphics);
void transfer2Display(SwapchainData::DisplayBuffers const* displayBuffer, ImageData const& imageData, IGraphics* graphics);
bool presentImage(VulkanObj* obj, SwapchainData& swapchain, vulkan::PresentData const& presentData);
void presentImage(ImageData const& imageData, VkSwapchainKHR swapchain, QueueInfo const* queue);
void waitFlipped(VulkanObj* obj); /// Call before submit
} // namespace vulkan

View File

@ -774,16 +774,4 @@ void deinitVulkan(VulkanObj* obj) {
delete obj;
}
void destroySwapchain(VulkanObj* obj, SwapchainData& swapchainData) {
for (auto& item: swapchainData.buffers) {
vkDestroySemaphore(obj->deviceInfo.device, item.semDisplayReady, nullptr);
vkDestroySemaphore(obj->deviceInfo.device, item.semPresentReady, nullptr);
vkDestroyFence(obj->deviceInfo.device, item.bufferFence, nullptr);
// vkDestroyImage(obj->deviceInfo.device, item.image, nullptr); // swapchain does that
}
vkDestroyCommandPool(obj->deviceInfo.device, swapchainData.commandPool, nullptr);
// vkDestroySwapchainKHR(obj->deviceInfo.device, swapchainData.swapchain, nullptr); // still chrashed despite waiting for idle
}
} // namespace vulkan

View File

@ -37,7 +37,7 @@ struct QueueInfo {
uint32_t family = 0;
size_t useCount = 0;
std::mutex mutex; // sync queue submit access
mutable std::mutex mutex; // sync queue submit access
QueueInfo(VkQueue queue_, uint32_t family_): queue(queue_), family(family_) {}
};
@ -47,34 +47,13 @@ struct Queues {
};
struct SwapchainData {
VkSwapchainKHR swapchain = nullptr;
VkFormat format = VK_FORMAT_UNDEFINED;
VkExtent2D extent2d = {};
// present sync
uint64_t presentId = 0;
uint64_t waitId = 0;
boost::mutex mutexPresent;
boost::condition_variable condPresent;
// -
struct DisplayBuffers {
uint64_t bufferVaddr = 0;
uint32_t bufferSize = 0;
uint32_t bufferAlign = 0;
VkImage image;
VkSemaphore semDisplayReady;
VkCommandBuffer transferBuffer;
VkSemaphore semPresentReady;
VkFence bufferFence;
};
VkCommandPool commandPool;
std::vector<DisplayBuffers> buffers;
};
@ -107,7 +86,4 @@ VkInstance const getVkInstance();
std::string_view const getGPUName();
std::pair<VkFormat, VkColorSpaceKHR> getDisplayFormat(VulkanObj* obj);
void createData(VulkanObj* obj, VkSurfaceKHR surface, SwapchainData& swapchainData, uint32_t width, uint32_t height, bool enableVsync); // Swapchain
void destroySwapchain(VulkanObj* obj, SwapchainData& swapchainData);
} // namespace vulkan

View File

@ -0,0 +1,22 @@
@startuml
skinparam classAttributeIconSize 0
interface IImageHandler{
+ {abstract} ImageData getImage_blocking()
+ {abstract} void notify_done(ImageData)
}
class ImageHandler{
}
class ImageData
IImageHandler <|-- ImageHandler
IImageHandler - ImageData
class VideoOut << (S,#FF7700) Singleton >> {}
VideoOut *-ImageHandler
user ()- IImageHandler
@enduml

View File

@ -202,13 +202,6 @@ int SYSV_ABI sceGnmDrawIndexAuto(uint32_t* cmdOut, uint64_t size, uint32_t index
int32_t SYSV_ABI sceGnmValidateDrawCommandBuffers(uint32_t count, void* dcbGpuAddrs[], uint32_t* dcbSizesInBytes, void* ccbGpuAddrs[],
uint32_t* ccbSizesInBytes) {
#if DEBUG
LOG_USE_MODULE(libSceGraphicsDriver);
for (uint32_t n = 0; n < count; ++n) {
if (dcbGpuAddrs != nullptr) LOG_DEBUG(L"Validate DCB[%d] 0x%08llx(0x%u)", n, (uint64_t)dcbGpuAddrs[n], dcbSizesInBytes[n]);
if (ccbGpuAddrs != nullptr) LOG_DEBUG(L"Validate CCB[%d] 0x%08llx(0x%u)", n, (uint64_t)ccbGpuAddrs[n], ccbSizesInBytes[n]);
}
#endif
return Err::VALIDATION_NOT_ENABLED;
}
@ -415,8 +408,8 @@ uint64_t SYSV_ABI sceGnmGetGpuCoreClockFrequency() {
return 0x800000000;
}
int SYSV_ABI sceGnmIsUserPaEnabled() {
return 0;
bool SYSV_ABI sceGnmIsUserPaEnabled() {
return false;
}
void* SYSV_ABI sceGnmGetTheTessellationFactorRingBufferBaseAddress() {
@ -512,7 +505,7 @@ SceWorkloadStatus SYSV_ABI sceGnmCreateWorkloadStream(const char* name, SceWorkl
SceWorkloadStatus SYSV_ABI sceGnmBeginWorkload(SceWorkloadStream stream, uint64_t* workload) {
static int32_t count = 0;
*workload = ++count;
return SceWorkloadStatus::StatusOk;
return SceWorkloadStatus::StatusInvalidPointer;
}
SceWorkloadStatus SYSV_ABI sceGnmEndWorkload(uint64_t workload) {

View File

@ -100,7 +100,6 @@ EXPORT SYSV_ABI int32_t sceVideoOutGetVblankStatus(int32_t handle, SceVideoOutVb
EXPORT SYSV_ABI int32_t sceVideoOutIsFlipPending(int32_t handle) {
return accessVideoOut().getPendingFlips(handle);
;
}
EXPORT SYSV_ABI int32_t sceVideoOutGetResolutionStatus(int32_t handle, SceVideoOutResolutionStatus* status) {
@ -190,7 +189,7 @@ EXPORT SYSV_ABI int32_t sceVideoOutGetEventData(Kernel::EventQueue::KernelEvent_
}
EXPORT SYSV_ABI int32_t sceVideoOutGetEventCount(Kernel::EventQueue::KernelEvent_t ev) {
return Ok;
return ev->fflags;
}
EXPORT SYSV_ABI int32_t sceVideoOutWaitVblank(int32_t handle) {

File diff suppressed because one or more lines are too long

After

Width:  |  Height:  |  Size: 9.8 KiB