+ eventqueue, libkernel

This commit is contained in:
Martin Baliet 2024-02-29 17:00:13 +01:00
parent 6ca4243524
commit 2970417f87
26 changed files with 2823 additions and 22 deletions

View File

@ -67,6 +67,7 @@ Cpp11BracedListStyle: true
DeriveLineEnding: true
DerivePointerAlignment: false
DisableFormat: false
SeparateDefinitionBlocks: Always
EmptyLineBeforeAccessModifier: LogicalBlock
ExperimentalAutoDetectBinPacking: false
FixNamespaceComments: true

View File

@ -9,5 +9,5 @@
"asciidoc.preview.useEditorStyle": false,
"asciidoc.use_asciidoctorpdf": true,
"editor.tabSize": 2,
"git.ignoredRepositories": ["third_party/boost"]
"git.ignoredRepositories": ["third_party/boost", "third_party/optick"]
}

View File

@ -52,6 +52,7 @@ if(NOT DEFINED EMU_THIRDPARTY_BUILD_DIR)
include("${CMAKE_SOURCE_DIR}/third_party/third_party.cmake")
else()
add_library(third_party INTERFACE)
# submodule
include_directories(BEFORE
${EMU_THIRDPARTY_BUILD_DIR}/include
@ -64,6 +65,7 @@ endif()
# Internal Projects
add_subdirectory(modules)
add_subdirectory(core)
# #- Projects

7
core/CMakeLists.txt Normal file
View File

@ -0,0 +1,7 @@
#add_subdirectory(kernel)
# add_subdirectory(videoout)
# add_library(core SHARED
#$<TARGET_OBJECTS:kernel>
# $<TARGET_OBJECTS:videoout>
#)

View File

@ -0,0 +1,3 @@
add_library(kernel OBJECT
eventqueue.cpp
)

298
core/kernel/eventqueue.cpp Normal file
View File

@ -0,0 +1,298 @@
#define __APICALL_EXTERN
#include "eventqueue.h"
#undef __APICALL_EXTERN
#include "modules_include/common.h"
#include <algorithm>
#include <boost/chrono.hpp>
#include <boost/thread/condition.hpp>
#include <boost/thread/mutex.hpp>
#include <chrono>
#include <list>
#include <logging.h>
#include <memory>
#include <mutex>
#include <string>
#include <unordered_map>
namespace Kernel {
namespace EventQueue {
LOG_DEFINE_MODULE(EventQueue);
class KernelEqueue: public IKernelEqueue {
public:
KernelEqueue() = default;
virtual ~KernelEqueue();
[[nodiscard]] std::string const& getName() const { return m_name; }
void setName(std::string&& name) { m_name.swap(name); }
int waitForEvents(KernelEvent_t ev, int num, SceKernelUseconds const* micros);
int pollEvents(KernelEvent_t ev, int num) {
std::unique_lock lock(m_mutex_cond);
auto ret = getTriggeredEvents(ev, num);
return ret;
}
int wait(KernelEvent_t ev, int num, int* out, const SceKernelUseconds* time) final;
int addUserEvent(int ident) final;
int addUserEventEdge(int ident) final;
int triggerUserEvent(uintptr_t ident, void* udata) final;
int addEvent(const KernelEqueueEvent& event) final;
int triggerEvent(uintptr_t ident, int16_t filter, void* trigger_data) final;
int deleteEvent(uintptr_t ident, int16_t filter) final;
private:
int getTriggeredEvents(KernelEvent_t ev, int num);
std::list<KernelEqueueEvent> m_events; // We dont ecpext many events ~5
boost::mutex m_mutex_cond;
std::mutex m_mutex_int;
boost::condition_variable m_cond_var;
std::string m_name;
bool m_closed = false;
};
KernelEqueue::~KernelEqueue() {
std::unique_lock const lock(m_mutex_cond);
/*
for (auto& ev: m_events) {
if (ev.filter.delete_event_func != nullptr) {
ev.filter.delete_event_func(this, &ev);
}
}*/
m_closed = true;
m_cond_var.notify_all();
}
int KernelEqueue::waitForEvents(KernelEvent_t ev, int num, SceKernelUseconds const* micros) {
LOG_USE_MODULE(EventQueue);
// LOG_TRACE(L"->waitForEvents: ident:0x%08llx num:%d", ev->ident, num);
boost::unique_lock lock(m_mutex_cond);
int ret = 0;
if (micros == nullptr) {
ret = getTriggeredEvents(ev, num);
} else if (*micros > 0) {
using namespace std::chrono_literals;
if (m_cond_var.wait_for(lock, boost::chrono::microseconds(*(decltype(micros))micros), [&] {
ret = getTriggeredEvents(ev, num);
return (ret > 0) | m_closed;
}) == 0) {
// LOG_TRACE(L"waitForEvents timeout");
}
} else {
m_cond_var.wait(lock, [&] {
ret = getTriggeredEvents(ev, num);
return (ret > 0) | m_closed;
});
}
// LOG_TRACE(L"<-waitForEvents: ident:0x%08llx ret:%d", ev->ident, ret);
return ret;
}
int KernelEqueue::getTriggeredEvents(KernelEvent_t eventList, int num) {
LOG_USE_MODULE(EventQueue);
int countTriggered = 0;
for (auto& ev: m_events) {
if (ev.triggered) {
eventList[countTriggered++] = ev.event;
if ((((uint32_t)ev.event.flags & (uint32_t)EventFlags::EV_CLEAR) != 0) && ev.filter.reset_func != nullptr) {
ev.filter.reset_func(&ev);
}
LOG_TRACE(L"Event Triggered: ident:0x%08llx, filter:%d", (uint64_t)ev.event.ident, ev.event.filter);
if (countTriggered >= num) {
break;
}
}
}
return countTriggered;
}
void event_trigger_func(Kernel::EventQueue::KernelEqueueEvent* event, void* trigger_data) {
event->triggered = true;
event->event.fflags++;
event->event.udata = trigger_data;
}
void event_reset_func(Kernel::EventQueue::KernelEqueueEvent* event) {
event->triggered = false;
event->event.fflags = 0;
event->event.udata = 0;
}
void event_delete_func(Kernel::EventQueue::IKernelEqueue_t eq, Kernel::EventQueue::KernelEqueueEvent* event) {}
int createEqueue(IKernelEqueue_t* eq, const char* name) {
LOG_USE_MODULE(EventQueue);
if (eq == nullptr || name == nullptr) {
return getErr(ErrCode::_EINVAL);
}
auto item = std::make_unique<KernelEqueue>();
item->setName(std::string(name));
LOG_INFO(L"+EventQueue (%S)", item->getName().c_str());
*eq = item.release();
return Ok;
}
int deleteEqueue(IKernelEqueue_t eq) {
LOG_USE_MODULE(EventQueue);
if (eq == nullptr) {
return getErr(ErrCode::_EBADF);
}
LOG_INFO(L"-EventQueue (%S)", ((KernelEqueue*)eq)->getName().c_str());
delete (eq);
return Ok;
}
int KernelEqueue::wait(KernelEvent_t ev, int num, int* out, const SceKernelUseconds* time) {
if (ev == nullptr) {
return getErr(ErrCode::_EFAULT);
}
if (num < 1) {
return getErr(ErrCode::_EINVAL);
}
if (time == nullptr) {
*out = waitForEvents(ev, num, time);
if (*out == 0) {
return getErr(ErrCode::_ETIMEDOUT);
}
} else {
*out = waitForEvents(ev, num, time);
if (*out == 0 && *time > 0) {
return getErr(ErrCode::_ETIMEDOUT);
}
}
return Ok;
}
int KernelEqueue::addUserEvent(int ident) {
return addEvent(Kernel::EventQueue::KernelEqueueEvent {.triggered = false,
.event =
{
.ident = ident,
.filter = Kernel::EventQueue::KERNEL_EVFILT_USER,
.flags = Kernel::EventQueue::EventFlags::EV_NONE,
.fflags = 0,
.data = 0,
.udata = 0,
},
.filter {
.data = nullptr,
.trigger_func = event_trigger_func,
.reset_func = event_reset_func,
.delete_event_func = event_delete_func,
}});
}
int KernelEqueue::addUserEventEdge(int ident) {
return addEvent(Kernel::EventQueue::KernelEqueueEvent {.triggered = false,
.event =
{
.ident = ident,
.filter = Kernel::EventQueue::KERNEL_EVFILT_USER,
.flags = Kernel::EventQueue::EventFlags::EV_CLEAR,
.fflags = 0,
.data = 0,
.udata = 0,
},
.filter {
.data = nullptr,
.trigger_func = event_trigger_func,
.reset_func = event_reset_func,
.delete_event_func = event_delete_func,
}});
}
int KernelEqueue::triggerUserEvent(uintptr_t ident, void* udata) {
LOG_USE_MODULE(EventQueue);
LOG_TRACE(L"triggerUserEvent: ident:0x%08llx", (uint64_t)ident);
return triggerEvent(ident, Kernel::EventQueue::KERNEL_EVFILT_USER, udata);
}
int KernelEqueue::addEvent(const KernelEqueueEvent& event) {
LOG_USE_MODULE(EventQueue);
LOG_INFO(L"(%S) Add Event: ident:0x%08llx, filter:%d", m_name.c_str(), (uint64_t)event.event.ident, event.event.filter);
std::unique_lock const lock(m_mutex_cond);
auto it = std::find_if(m_events.begin(), m_events.end(),
[=](KernelEqueueEvent const& ev) { return ev.event.ident == event.event.ident && ev.event.filter == event.event.filter; });
if (it != m_events.end()) {
*it = event;
} else {
m_events.push_back(event);
}
if (event.triggered) {
m_cond_var.notify_all();
}
return Ok;
}
int KernelEqueue::triggerEvent(uintptr_t ident, int16_t filter, void* trigger_data) {
LOG_USE_MODULE(EventQueue);
LOG_TRACE(L"triggerEvent: ident:0x%08llx, filter:%d", (uint64_t)ident, filter);
std::unique_lock const lock(m_mutex_cond);
auto it = std::find_if(m_events.begin(), m_events.end(), [=](KernelEqueueEvent const& ev) { return ev.event.ident == ident && ev.event.filter == filter; });
if (it != m_events.end()) {
if (it->filter.trigger_func != nullptr) {
it->filter.trigger_func(&(*it), trigger_data);
} else {
it->triggered = true;
}
m_cond_var.notify_all();
return Ok;
}
return getErr(ErrCode::_ENOENT);
}
int KernelEqueue::deleteEvent(uintptr_t ident, int16_t filter) {
LOG_USE_MODULE(EventQueue);
LOG_INFO(L"deleteEvent: ident:0x%08llx, filter:%llu", (uint64_t)ident, filter);
std::unique_lock const lock(m_mutex_cond);
auto it = std::find_if(m_events.begin(), m_events.end(), [=](KernelEqueueEvent const& ev) { return ev.event.ident == ident && ev.event.filter == filter; });
if (it != m_events.end()) {
if (it->filter.delete_event_func != nullptr) {
it->filter.delete_event_func(this, &(*it));
}
m_events.erase(it);
return Ok;
}
return getErr(ErrCode::_ENOENT);
}
} // namespace EventQueue
} // namespace Kernel

43
core/kernel/eventqueue.h Normal file
View File

@ -0,0 +1,43 @@
#pragma once
#include "eventqueue_types.h"
#include "modules_include/common.h"
#include "utility/utility.h"
namespace Kernel {
namespace EventQueue {
class IKernelEqueue {
CLASS_NO_COPY(IKernelEqueue);
CLASS_NO_MOVE(IKernelEqueue);
protected:
IKernelEqueue() = default;
public:
virtual ~IKernelEqueue() = default;
virtual int wait(KernelEvent_t ev, int num, int* out, const SceKernelUseconds* timo) = 0;
virtual int addUserEvent(int ident) = 0;
virtual int addUserEventEdge(int ident) = 0;
virtual int triggerUserEvent(uintptr_t ident, void* udata) = 0;
virtual int addEvent(const KernelEqueueEvent& event) = 0;
virtual int triggerEvent(uintptr_t ident, int16_t filter, void* trigger_data) = 0;
virtual int deleteEvent(uintptr_t ident, int16_t filter) = 0;
};
#if defined(__APICALL_EXTERN)
#define __APICALL __declspec(dllexport)
#elif defined(__APICALL_IMPORT)
#define __APICALL __declspec(dllimport)
#else
#define __APICALL
#endif
__APICALL int createEqueue(IKernelEqueue_t* eq, const char* name);
__APICALL int deleteEqueue(IKernelEqueue_t eq);
#undef __APICALL
} // namespace EventQueue
} // namespace Kernel

View File

@ -0,0 +1,55 @@
#pragma once
#include <stdint.h>
namespace Kernel::EventQueue {
constexpr int16_t KERNEL_EVFILT_TIMER = -7;
constexpr int16_t KERNEL_EVFILT_READ = -1;
constexpr int16_t KERNEL_EVFILT_WRITE = -2;
constexpr int16_t KERNEL_EVFILT_USER = -11;
constexpr int16_t KERNEL_EVFILT_FILE = -4;
constexpr int16_t KERNEL_EVFILT_GRAPHICS = -14;
constexpr int16_t KERNEL_EVFILT_VIDEO_OUT = -13;
constexpr int16_t KERNEL_EVFILT_HRTIMER = -15;
enum class EventFlags : uint32_t {
EV_NONE = 0,
EV_ONESHOT = 0x10, // only report one occurrence
EV_CLEAR = 0x20, // clear event state after reporting
EV_RECEIPT = 0x40, // force EV_ERROR on success, data=0
EV_DISPATCH = 0x80, // disable event after reporting
EV_SYSFLAGS = 0xF000, // reserved by system
EV_FLAG1 = 0x2000, // filter-specific flag
};
struct KernelEvent {
int ident = 0;
int16_t filter = 0;
EventFlags flags = EventFlags::EV_NONE;
uint32_t fflags = 0;
intptr_t data = 0;
void* udata = nullptr;
};
struct KernelEqueueEvent;
class IKernelEqueue;
using IKernelEqueue_t = IKernelEqueue*;
using KernelEvent_t = KernelEvent*;
using trigger_func_t = void (*)(KernelEqueueEvent* event, void* trigger_data);
using reset_func_t = void (*)(KernelEqueueEvent* event);
using delete_func_t = void (*)(IKernelEqueue_t eq, KernelEqueueEvent* event);
struct KernelFilter {
void* data = nullptr;
trigger_func_t trigger_func = nullptr;
reset_func_t reset_func = nullptr;
delete_func_t delete_event_func = nullptr;
};
struct KernelEqueueEvent {
bool triggered = false;
KernelEvent event;
KernelFilter filter;
};
} // namespace Kernel::EventQueue

View File

@ -0,0 +1,10 @@
add_library(videoout OBJECT
videoout.cpp
vulkan/vulkanSetup.cpp
vulkan/vulkanHelper.cpp
)
target_include_directories(videoout PRIVATE
${Vulkan_INCLUDE_DIRS}
${CMAKE_SOURCE_DIR}/third_party/optick/src
)

118
core/videoout/graphics.h Normal file
View File

@ -0,0 +1,118 @@
#pragma once
#include "utility/utility.h"
#include "vulkan/vulkan_core.h"
#include <memory>
enum class GRAPHICS_EVENTS : int {
Compute0RelMem = 0,
Compute1RelMem,
Compute2RelMem,
Compute3RelMem,
Compute4RelMem,
Compute5RelMem,
Compute6RelMem,
EOP = 0x40,
};
namespace vulkan {
struct DeviceInfo;
enum class QueueType : uint8_t;
} // namespace vulkan
class IEventsGraphics {
public:
virtual void eventDoFlip(int handle, int index, int64_t flipArg, VkSemaphore waitSema, size_t waitValue) = 0;
virtual vulkan::DeviceInfo* getDeviceInfo() = 0;
virtual std::pair<VkQueue, uint32_t> getQueue(vulkan::QueueType type) = 0;
};
namespace Kernel::EventQueue {
struct KernelEqueueEvent;
class KernelEqueue;
} // namespace Kernel::EventQueue
class IGraphics {
CLASS_NO_COPY(IGraphics);
CLASS_NO_MOVE(IGraphics);
protected:
IGraphics() = default;
public:
virtual ~IGraphics() = default;
/**
* @brief Adds a GRAPHICS_EVENTS event
*
* @param event
* @param eq
* @return int result
*/
virtual int addEvent(Kernel::EventQueue::KernelEqueueEvent& event, Kernel::EventQueue::KernelEqueue* eq) = 0;
/**
* @brief Removes a GRAPHICS_EVENTS event
*
* @param eq
* @param ident
*/
virtual void removeEvent(Kernel::EventQueue::KernelEqueue* eq, int const ident) = 0;
/**
* @brief Currently used to preprocess the command buffer.
* todo: will be hooked in the future. and replaced
*
* @param curEndAddr end address of the command buffer
*/
virtual void updateCmdBuffer(uint64_t curEndAddr) = 0;
/**
* @brief submits the command buffer to the command processor
*
* @param count
* @param drawBuffers
* @param numDrawDw
* @param constBuffers
* @param numConstDw
* @param handle
* @param index
* @param flipMode
* @param flipArg
* @param flip
*/
virtual void submitCmdBuffer(uint32_t count, uint32_t const* drawBuffers[], uint32_t const numDrawDw[], uint32_t const* constBuffers[],
uint32_t const numConstDw[], int const handle, int const index, int const flipMode, int64_t const flipArg, bool flip) = 0;
/**
* @brief Wait for all submits (and currently flips todo: check if needed)
*
*/
virtual void waitSubmitDone() = 0;
/**
* @brief Increase the counter in waitSubmitDone(). Adds a wait condition.
*
*/
virtual void submited() = 0;
/**
* @brief Notify waitSubmitDone(). Decreases the counter
*
*/
virtual void submitDone() = 0;
/**
* @brief Check if command processor is running (handling submits)
*
* @return true
* @return false
*/
virtual bool isRunning() const = 0;
};
std::unique_ptr<IGraphics> createGraphics(IEventsGraphics& listener);

641
core/videoout/videoout.cpp Normal file
View File

@ -0,0 +1,641 @@
#define __APICALL_EXTERN
#include "videoout.h"
#undef __APICALL_EXTERN
#include "core/graphics/objects/colorAttachment.h"
#include "core/manager/graphics/managerInit.h"
#include "modules/libSceVideoOut/codes.h"
#include "modules/libSceVideoOut/types.h"
#include "modules_include/common.h"
#include "vulkan/vulkanHelper.h"
#include <queue>
#include <GLFW/glfw3.h>
#include <algorithm>
#include <array>
#include <assert.h>
#include <eventqueue_types.h>
#include <format>
#include <graphics.h>
#include <initParams.h>
#include <list>
#include <logging.h>
#include <memory>
#include <mutex>
#include <optick.h>
#include <systemContent.h>
#include <thread>
#include <timer.h>
LOG_DEFINE_MODULE(VideoOut);
using namespace Kernel;
namespace {
size_t constexpr WindowsMAX = 2;
std::string getTitle(int handle, uint64_t frame, size_t fps) {
static auto title = [] {
auto title = accessSystemContent().getString("TITLE");
if (title) return title.value().data();
return "psOFF";
}();
return std::format("{}({}): frame={} fps={}", title, handle, frame, fps);
}
std::pair<uint32_t, uint32_t> getDisplayBufferSize(uint32_t width, uint32_t height, uint32_t pitch, bool tile, bool neo) {
if (pitch == 3840) {
if (width == 3840 && height == 2160 && tile && !neo) {
return {33423360, 32768};
}
if (width == 3840 && height == 2160 && tile && neo) {
return {33423360, 65536};
}
if (width == 3840 && height == 2160 && !tile && !neo) {
return {33177600, 256};
}
if (width == 3840 && height == 2160 && !tile && neo) {
return {33177600, 256};
}
}
if (pitch == 1920) {
if (width == 1920 && height == 1080 && tile && !neo) {
return {8355840, 32768};
}
if (width == 1920 && height == 1080 && tile && neo) {
return {8847360, 65536};
}
if (width == 1920 && height == 1080 && !tile && !neo) {
return {8294400, 256};
}
if (width == 1920 && height == 1080 && !tile && neo) {
return {8294400, 256};
}
}
if (pitch == 1280) {
if (width == 1280 && height == 720 && tile && !neo) {
return {3932160, 32768};
}
if (width == 1280 && height == 720 && tile && neo) {
return {3932160, 65536};
}
if (width == 1280 && height == 720 && !tile && !neo) {
return {3686400, 256};
}
if (width == 1280 && height == 720 && !tile && neo) {
return {3686400, 256};
}
}
return {0, 0};
}
struct VideoOutConfig {
SceVideoOutFlipStatus flipStatus;
SceVideoOutVblankStatus vblankStatus;
SceVideoOutResolutionStatus resolution;
std::array<vulkan::SwapchainData, 16> bufferSets;
std::array<int32_t, 16> buffers; // index to bufferSets
std::array<std::weak_ptr<Objects::ColorAttachment>, 16> displayBuffers;
uint8_t buffersSetsCount = 0;
double fps = 0.0;
VideoOutConfig() { std::fill(buffers.begin(), buffers.end(), -1); }
};
struct Context {
int userId = -1;
int fliprate = 3;
VideoOutConfig config;
GLFWwindow* window;
VkSurfaceKHR surface;
std::list<EventQueue::KernelEqueue*> eventFlip;
std::list<EventQueue::KernelEqueue*> eventVblank;
};
enum class MessageType { open, close, flip };
struct Message {
MessageType type;
int windowIndex = -1;
bool* done = nullptr;
uint32_t index = 0;
};
} // namespace
class VideoOut: public IVideoOut, private IEventsGraphics {
std::array<Context, WindowsMAX> m_windows;
uint32_t const m_widthTotal = 1920, m_heightTotal = 1080; // todo: make config
mutable std::mutex m_mutexInt;
vulkan::VulkanObj* m_vulkanObj = nullptr;
std::unique_ptr<IGraphics> m_graphics;
std::thread m_threadGlfw;
std::condition_variable m_condGlfw;
std::condition_variable m_condDone;
bool m_stop = false;
std::queue<Message> m_messages;
void vblankEnd(int handle, uint64_t curTime, uint64_t curProcTime);
// Callback Graphics
void eventDoFlip(int handle, int index, int64_t flipArg, VkSemaphore waitSema, size_t waitValue) final {
OPTICK_EVENT();
m_graphics->submited();
transferDisplay(handle, index, waitSema, waitValue);
std::unique_lock lock(m_mutexInt);
auto& window = m_windows[handle - 1];
uint32_t const setIndex = window.config.buffers[index];
auto& flipStatus = m_windows[index].config.flipStatus;
++flipStatus.gcQueueNum;
flipStatus.flipArg = flipArg;
auto& timer = accessTimer();
auto const curTime = (uint64_t)(1e9 * timer.getTimeS());
flipStatus.submitTsc = curTime;
// window.config.flipStatus.currentBuffer = index; // set after flip, before vblank
m_messages.push({MessageType::flip, handle - 1, nullptr, setIndex});
lock.unlock();
m_condGlfw.notify_one();
}
vulkan::DeviceInfo* getDeviceInfo() final { return &m_vulkanObj->deviceInfo; }
std::pair<VkQueue, uint32_t> getQueue(vulkan::QueueType type) final;
// -
void transferDisplay(int handle, int index, VkSemaphore waitSema, size_t waitValue); // -> Renderer
std::thread createGlfwThread();
public:
VideoOut() = default;
/**
* @brief Preinit handle 1(index 0) with main window
* Needed for early vulkan init and with it all the managers
*/
void init() final;
int open(int userId) final;
void close(int handle) final;
void setFliprate(int handle, int rate) final {
LOG_USE_MODULE(VideoOut);
LOG_INFO(L"Fliprate:%d", rate);
std::unique_lock const lock(m_mutexInt);
m_windows[handle - 1].fliprate = rate;
}
int addEvent(int handle, EventQueue::KernelEqueueEvent const& event, EventQueue::KernelEqueue* eq) final;
void removeEvent(int handle, Kernel::EventQueue::KernelEqueue* eq, int const ident) final;
void submitFlip(int handle, int index, int64_t flipArg) final; // -> Renderer
void getFlipStatus(int handle, void* status) final;
void getVBlankStatus(int handle, void* status) final;
void getResolution(int handle, void* status) final;
void getBufferAttribute(void* attribute, uint32_t pixel_format, int32_t tiling_mode, int32_t aspect_ratio, uint32_t width, uint32_t height,
uint32_t pitch_in_pixel) final;
int registerBuffers(int handle, int startIndex, void* const* addresses, int numBuffer, const void* attribute) final;
int getPendingFlips(int handle) final {
std::unique_lock const lock(m_mutexInt);
auto& flipStatus = m_windows[handle - 1].config.flipStatus;
return flipStatus.gcQueueNum;
}
IGraphics* getGraphics() final {
assert(m_graphics);
return m_graphics.get();
};
std::unique_lock<std::mutex> getGlfwLock() const final { return std::unique_lock(m_mutexInt); }
};
IVideoOut& accessVideoOut() {
static VideoOut inst;
return inst;
}
void VideoOut::init() {
LOG_USE_MODULE(VideoOut);
LOG_DEBUG(L"createGlfwThread()");
m_threadGlfw = createGlfwThread();
std::unique_lock lock(m_mutexInt);
static bool done = false;
m_messages.push(Message {MessageType::open, 0, &done});
lock.unlock();
m_condGlfw.notify_one();
lock.lock();
m_condDone.wait(lock, [=] { return done; });
}
int VideoOut::open(int userId) {
OPTICK_THREAD("libVideOut");
LOG_USE_MODULE(VideoOut);
std::unique_lock lock(m_mutexInt);
// Get Free handle
int const windowIndex = [&] {
for (int n = 0; n < m_windows.size(); ++n) {
if (m_windows[n].userId < 0) {
m_windows[n].userId = userId;
m_windows[n].config.flipStatus = {};
m_windows[n].config.vblankStatus = {};
return n;
}
}
return -1;
// -
}();
// -
if (windowIndex == 0) return 1; // Special case windows[0] is mainWindow
// Create new window
static bool done = false;
m_messages.push(Message {MessageType::open, windowIndex, &done});
lock.unlock();
m_condGlfw.notify_one();
lock.lock();
m_condDone.wait(lock, [=] { return done; });
return 1 + windowIndex; // handle starts with 1
}
void VideoOut::close(int handle) {
LOG_USE_MODULE(VideoOut);
LOG_INFO(L"<-- VideoOut Close(%d)", handle);
std::unique_lock lock(m_mutexInt);
auto& window = m_windows[handle - 1];
window.userId = -1;
for (auto& item: window.eventFlip) {
if (item != nullptr) {
(void)EventQueue::KernelDeleteEvent(item, VIDEO_OUT_EVENT_FLIP, EventQueue::KERNEL_EVFILT_VIDEO_OUT);
}
}
for (auto& item: window.eventVblank) {
if (item != nullptr) {
(void)EventQueue::KernelDeleteEvent(item, VIDEO_OUT_EVENT_VBLANK, EventQueue::KERNEL_EVFILT_VIDEO_OUT);
}
}
window.eventFlip.clear();
window.eventVblank.clear();
static bool done = false;
m_messages.push(Message {MessageType::close, handle, &done});
lock.unlock();
m_condGlfw.notify_one();
lock.lock();
m_condDone.wait(lock, [=] { return done; });
}
int VideoOut::addEvent(int handle, EventQueue::KernelEqueueEvent const& event, EventQueue::KernelEqueue* eq) {
LOG_USE_MODULE(VideoOut);
if (eq == nullptr) return getErr(ErrCode::_EINVAL);
int result = EventQueue::KernelAddEvent(eq, event);
switch (event.event.ident) {
case VIDEO_OUT_EVENT_FLIP: {
LOG_DEBUG(L"+VIDEO_OUT_EVENT_FLIP(%d)", handle);
std::unique_lock const lock(m_mutexInt);
m_windows[handle - 1].eventFlip.push_back(eq);
} break;
case VIDEO_OUT_EVENT_VBLANK: {
LOG_DEBUG(L"+VIDEO_OUT_EVENT_VBLANK(%d)", handle);
std::unique_lock const lock(m_mutexInt);
m_windows[handle - 1].eventVblank.push_back(eq);
} break;
default: LOG_ERR(L"undefinded:%d", event.event.ident); result = -1;
}
return result;
}
void VideoOut::removeEvent(int handle, Kernel::EventQueue::KernelEqueue* eq, int const ident) {
LOG_USE_MODULE(VideoOut);
switch (ident) {
case VIDEO_OUT_EVENT_FLIP: {
LOG_DEBUG(L"-VIDEO_OUT_EVENT_FLIP(%d)", handle);
std::unique_lock const lock(m_mutexInt);
m_windows[handle - 1].eventFlip.remove(eq);
} break;
case VIDEO_OUT_EVENT_VBLANK: {
LOG_DEBUG(L"-VIDEO_OUT_EVENT_VBLANK(%d)", handle);
std::unique_lock const lock(m_mutexInt);
m_windows[handle - 1].eventVblank.remove(eq);
} break;
default: LOG_CRIT(L"undefinded:%d", ident);
}
}
void VideoOut::transferDisplay(int handle, int index, VkSemaphore waitSema, size_t waitValue) {
LOG_USE_MODULE(VideoOut);
auto& window = m_windows[handle - 1];
uint32_t const setIndex = window.config.buffers[index];
auto& swapchain = window.config.bufferSets[setIndex];
auto& displayBufferMeta = swapchain.buffers[index];
if (window.config.displayBuffers[index].expired()) {
auto image = accessGpuMemory().getDisplayBuffer(displayBufferMeta.bufferVaddr);
if (image) {
window.config.displayBuffers[index] = image;
vulkan::transfer2Display(displayBufferMeta.transferBuffer, m_vulkanObj, swapchain, image->getImage(), image.get(), index);
vulkan::submitDisplayTransfer(displayBufferMeta.transferBuffer, m_vulkanObj, displayBufferMeta.semPresentReady, displayBufferMeta.semDisplayReady,
waitSema, waitValue);
} else {
LOG_ERR(L"No Display for 0x%08llx:%u", displayBufferMeta.bufferVaddr, displayBufferMeta.bufferSize);
}
} else {
auto image = window.config.displayBuffers[index].lock();
vulkan::transfer2Display(displayBufferMeta.transferBuffer, m_vulkanObj, swapchain, image->getImage(), image.get(), index);
vulkan::submitDisplayTransfer(displayBufferMeta.transferBuffer, m_vulkanObj, displayBufferMeta.semPresentReady, displayBufferMeta.semDisplayReady, waitSema,
waitValue);
}
}
void VideoOut::submitFlip(int handle, int index, int64_t flipArg) {
OPTICK_EVENT();
LOG_USE_MODULE(VideoOut);
m_graphics->submited(); // increase internal counter (wait for flip)
auto& window = m_windows[handle - 1];
uint32_t const setIndex = window.config.buffers[index];
LOG_TRACE(L"submitFlip(%d):%u %d", handle, setIndex, index);
std::unique_lock lock(m_mutexInt);
transferDisplay(handle, index, nullptr, 0);
auto& flipStatus = m_windows[index].config.flipStatus;
++flipStatus.gcQueueNum;
flipStatus.flipArg = flipArg;
auto& timer = accessTimer();
auto const curTime = (uint64_t)(1e9 * timer.getTimeS());
flipStatus.submitTsc = curTime;
// window.config.flipStatus.currentBuffer = index; // set after flip, before vblank
m_messages.push({MessageType::flip, handle - 1, nullptr, setIndex});
lock.unlock();
m_condGlfw.notify_one();
}
void VideoOut::getFlipStatus(int handle, void* status) {
LOG_USE_MODULE(VideoOut);
LOG_TRACE(L"%S(%d)", __FUNCTION__, handle);
std::unique_lock const lock(m_mutexInt);
*(SceVideoOutFlipStatus*)status = m_windows[handle - 1].config.flipStatus;
}
void VideoOut::getVBlankStatus(int handle, void* status) {
LOG_USE_MODULE(VideoOut);
LOG_TRACE(L"%S(%d)", __FUNCTION__, handle);
std::unique_lock const lock(m_mutexInt);
auto& vblank = m_windows[handle - 1].config.vblankStatus;
*(SceVideoOutVblankStatus*)status = vblank;
}
void VideoOut::vblankEnd(int handle, uint64_t curTime, uint64_t curProcTime) {
OPTICK_EVENT();
auto& window = m_windows[handle - 1];
auto& vblank = window.config.vblankStatus;
// vblank.processTime = 0;
vblank.tsc = curProcTime;
vblank.processTime = curTime;
++vblank.count;
for (auto& item: window.eventFlip) {
(void)EventQueue::KernelTriggerEvent(item, VIDEO_OUT_EVENT_VBLANK, EventQueue::KERNEL_EVFILT_VIDEO_OUT,
reinterpret_cast<void*>(window.config.vblankStatus.count));
}
}
void VideoOut::getResolution(int handle, void* status) {
LOG_USE_MODULE(VideoOut);
LOG_TRACE(L"%S(%d)", __FUNCTION__, handle);
std::unique_lock const lock(m_mutexInt);
*(SceVideoOutResolutionStatus*)status = m_windows[handle - 1].config.resolution;
}
void VideoOut::getBufferAttribute(void* attribute, uint32_t pixel_format, int32_t tiling_mode, int32_t aspect_ratio, uint32_t width, uint32_t height,
uint32_t pitchInPixel) {
LOG_USE_MODULE(VideoOut);
LOG_TRACE(L"%S", __FUNCTION__);
auto [displayFormat, _] = vulkan::getDisplayFormat(m_vulkanObj);
*(SceVideoOutBufferAttribute*)attribute = SceVideoOutBufferAttribute {
.pixelFormat = SceVideoOutPixelFormat::PIXEL_FORMAT_A8R8G8B8_SRGB, // todo get vulkan pixel_format?
.tilingMode = tiling_mode,
.aspectRatio = aspect_ratio,
.width = width,
.height = height,
.pitchInPixel = pitchInPixel,
};
}
int VideoOut::registerBuffers(int handle, int startIndex, void* const* addresses, int numBuffer, const void* attribute) {
LOG_USE_MODULE(VideoOut);
LOG_TRACE(L"%S(%d)", __FUNCTION__, handle);
auto& config = m_windows[handle - 1].config;
auto const setIndex = config.buffersSetsCount++;
[[unlikely]] if (setIndex > 15) {
LOG_CRIT(L"buffersSetsCount > 15");
return ::Err::VIDEO_OUT_ERROR_NO_EMPTY_SLOT;
}
auto& bufferSet = config.bufferSets[setIndex];
bufferSet.buffers.resize(numBuffer);
for (int i = startIndex; i < startIndex + numBuffer; ++i) {
if (config.buffers[i] >= 0) return ::Err::VIDEO_OUT_ERROR_SLOT_OCCUPIED;
config.buffers[i] = setIndex;
}
auto const* _att = (SceVideoOutBufferAttribute const*)attribute;
for (size_t n = 0; n < numBuffer; ++n) {
bufferSet.buffers[n].bufferVaddr = (uintptr_t)addresses[n];
auto const [displaySize, displaySizeAlign] = getDisplayBufferSize(_att->width, _att->height, _att->pitchInPixel, _att->tilingMode, false);
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]);
auto [format, colorSpace] = vulkan::getDisplayFormat(m_vulkanObj);
if (!accessGpuMemory().registerDisplayBuffer(bufferSet.buffers[n].bufferVaddr, VkExtent2D {.width = _att->width, .height = _att->height},
_att->pitchInPixel, format))
return -1;
}
config.flipStatus.currentBuffer =
createData(m_vulkanObj, m_windows[handle - 1].surface, bufferSet, _att->width, _att->height, accessInitParams()->useVSYNC());
return setIndex;
}
std::pair<VkQueue, uint32_t> VideoOut::getQueue(vulkan::QueueType type) {
std::unique_lock const lock(m_mutexInt);
auto& queueInfo = m_vulkanObj->queues.items[vulkan::getIndex(type)];
auto bestIt = queueInfo.begin();
// Search for least used
for (auto it = queueInfo.begin()++; it != queueInfo.end(); ++it) {
if (it->useCount < bestIt->useCount) {
bestIt = it;
}
}
// -
++bestIt->useCount;
return std::make_pair(bestIt->queue, bestIt->family);
}
std::thread VideoOut::createGlfwThread() {
return std::thread([this] {
util::setThreadName("VideoOut");
OPTICK_THREAD("VideoOut");
LOG_USE_MODULE(VideoOut);
LOG_DEBUG(L"Init glfw");
glfwInit();
auto const vblankTime = (uint64_t)(1e6 / 59.0);
while (!m_stop) {
std::unique_lock lock(m_mutexInt);
m_condGlfw.wait_for(lock, std::chrono::microseconds(vblankTime), [=] { return m_stop || !m_messages.empty(); });
if (m_stop) break;
if (m_messages.empty()) {
using namespace std::chrono;
auto& timer = accessTimer();
auto const curTime = (uint64_t)(1e6 * timer.getTimeS());
auto const procTime = timer.queryPerformance();
for (size_t n = 0; n < m_windows.size(); ++n) {
vblankEnd(1 + n, curTime, procTime);
}
continue;
}
auto item = m_messages.front();
auto const index = item.windowIndex;
switch (item.type) {
case MessageType::open: {
auto const title = getTitle(index, 0, 0);
glfwWindowHint(GLFW_CLIENT_API, GLFW_NO_API);
glfwWindowHint(GLFW_RESIZABLE, GLFW_FALSE);
glfwWindowHint(GLFW_VISIBLE, GLFW_FALSE);
m_windows[index].window = glfwCreateWindow(m_widthTotal, m_heightTotal, title.c_str(), nullptr, nullptr);
glfwMakeContextCurrent(m_windows[index].window);
glfwShowWindow(m_windows[index].window);
glfwGetWindowSize(m_windows[index].window, (int*)(&m_windows[index].config.resolution.paneWidth),
(int*)(&m_windows[index].config.resolution.paneHeight));
LOG_INFO(L"--> VideoOut Open(%S)| %d:%d", title.c_str(), m_windows[index].config.resolution.paneWidth, m_windows[index].config.resolution.paneHeight);
if (m_vulkanObj == nullptr) {
m_vulkanObj = vulkan::initVulkan(m_windows[index].window, m_windows[index].surface, accessInitParams()->enableValidation());
auto& info = m_vulkanObj->deviceInfo;
initManager(info.device, info.physicalDevice, info.instance);
m_graphics = createGraphics(*this);
} else {
vulkan::createSurface(m_vulkanObj, m_windows[index].window, m_windows[index].surface);
}
*item.done = true;
m_condDone.notify_one();
} break;
case MessageType::close: {
glfwDestroyWindow(m_windows[index].window);
*item.done = true;
m_condDone.notify_one();
} break;
case MessageType::flip: {
LOG_TRACE(L"-> flip(%d) set:%u buffer:%u", index, item.index, m_windows[index].config.flipStatus.currentBuffer);
OPTICK_FRAME("VideoOut");
auto& flipStatus = m_windows[index].config.flipStatus;
using namespace std::chrono;
lock.unlock();
{
OPTICK_EVENT("Present");
presentImage(m_vulkanObj, m_windows[index].config.bufferSets[item.index], (uint32_t&)m_windows[index].config.flipStatus.currentBuffer);
}
m_graphics->submitDone();
lock.lock();
auto& timer = accessTimer();
auto const curTime = (uint64_t)(1e6 * timer.getTimeS());
auto const procTime = timer.queryPerformance();
auto elapsed_us = curTime - flipStatus.processTime;
flipStatus.tsc = procTime;
flipStatus.processTime = curTime;
++flipStatus.count;
--flipStatus.gcQueueNum;
vblankEnd(1 + index, curTime, procTime);
// Trigger Event Flip
for (auto& item: m_windows[index].eventFlip) {
(void)EventQueue::KernelTriggerEvent(item, VIDEO_OUT_EVENT_FLIP, EventQueue::KERNEL_EVFILT_VIDEO_OUT,
reinterpret_cast<void*>(m_windows[index].config.flipStatus.flipArg));
}
// - Flip event
double const fps = (m_windows[index].config.fps * 5.0 + (1e6 / (double)elapsed_us)) / 6.0;
auto title = getTitle(index + 1, flipStatus.count, round(fps));
m_windows[index].config.fps = fps;
glfwSetWindowTitle(m_windows[index].window, title.c_str());
glfwPollEvents();
LOG_TRACE(L"<- flip(%d) set:%u buffer:%u", index, item.index, m_windows[index].config.flipStatus.currentBuffer);
} break;
}
m_messages.pop();
}
glfwTerminate();
});
}

170
core/videoout/videoout.h Normal file
View File

@ -0,0 +1,170 @@
#pragma once
#include <mutex>
#include <utility/utility.h>
namespace Kernel::EventQueue {
struct KernelEqueueEvent;
class KernelEqueue;
} // namespace Kernel::EventQueue
namespace vulkan {
struct SwapchainData;
}
constexpr int VIDEO_OUT_EVENT_FLIP = 0;
constexpr int VIDEO_OUT_EVENT_VBLANK = 1;
class IGLFW {};
class IGraphics;
class IVideoOut {
CLASS_NO_COPY(IVideoOut);
CLASS_NO_MOVE(IVideoOut);
protected:
IVideoOut() = default;
public:
virtual ~IVideoOut() = default;
/**
* @brief init interals
*
*/
virtual void init() = 0;
/**
* @brief Open a window
*
* @param userId
* @return int internal handle of the window
*/
virtual int open(int userId) = 0;
/**
* @brief Closes the window
*
* @param handle
*/
virtual void close(int handle) = 0;
/**
* @brief Set the fps to use
*
* @param handle
* @param rate
*/
virtual void setFliprate(int handle, int rate) = 0;
/**
* @brief Add a VIDEO_OUT_EVENT for the window
*
* @param handle
* @param event
* @param eq
* @return int
*/
virtual int addEvent(int handle, Kernel::EventQueue::KernelEqueueEvent const& event, Kernel::EventQueue::KernelEqueue* eq) = 0;
/**
* @brief Removes a VIDEO_OUT_EVENT for the window
*
* @param handle
* @param eq
* @param ident
*/
virtual void removeEvent(int handle, Kernel::EventQueue::KernelEqueue* eq, int const ident) = 0;
/**
* @brief Submit flip video buffers to the queue
*
* @param index
* @param flipArg used by the flip event
*/
virtual void submitFlip(int handle, int index, int64_t flipArg) = 0;
/**
* @brief Get the Flip Status
*
* @param handle
* @param status
*/
virtual void getFlipStatus(int handle, void* status) = 0;
/**
* @brief Get the VBlank Status (currently faked by a timer)
*
* @param handle
* @param status
*/
virtual void getVBlankStatus(int handle, void* status) = 0;
/**
* @brief Get the current resolution
*
* @param handle
* @param status
*/
virtual void getResolution(int handle, void* status) = 0;
/**
* @brief Get the number of unhandled flip submits
*
* @param handle
* @return int
*/
virtual int getPendingFlips(int handle) = 0;
/**
* @brief Get the video Buffer Attributes
*
* @param attribute
* @param pixel_format
* @param tiling_mode
* @param aspect_ratio
* @param width
* @param height
* @param pitch_in_pixel
*/
virtual void getBufferAttribute(void* attribute, uint32_t pixel_format, int32_t tiling_mode, int32_t aspect_ratio, uint32_t width, uint32_t height,
uint32_t pitch_in_pixel) = 0;
/**
* @brief Registers a video buffer
*
* @param handle
* @param startIndex
* @param addresses
* @param numBuffer
* @param attribute
* @return int
*/
virtual int registerBuffers(int handle, int startIndex, void* const* addresses, int numBuffer, const void* attribute) = 0;
/**
* @brief Access the graphics interface
*
* @return IGraphics*
*/
virtual IGraphics* getGraphics() = 0;
/**
* @brief locks the glfwpoll
*
* @return std::unique_lock<std::mutex>
*/
virtual std::unique_lock<std::mutex> getGlfwLock() const = 0;
};
#if defined(__APICALL_EXTERN)
#define __APICALL __declspec(dllexport)
#elif defined(__APICALL_IMPORT)
#define __APICALL __declspec(dllimport)
#else
#define __APICALL
#endif
__APICALL IVideoOut& accessVideoOut();
#undef __APICALL

View File

@ -0,0 +1,361 @@
#include "vulkanHelper.h"
// #include "core/graphics/objects/colorAttachment.h"
#include "logging.h"
#include "utility/utility.h"
#include <GLFW/glfw3.h>
#include <algorithm>
#include <array>
#include <assert.h>
#include <boost/assert.hpp>
#include <chrono>
#include <format>
#include <mutex>
#include <optick.h>
#include <string>
#include <thread>
#include <vulkan/vk_enum_string_helper.h>
LOG_DEFINE_MODULE(vulkanHelper);
namespace vulkan {
void createSurface(VulkanObj* obj, GLFWwindow* window, VkSurfaceKHR& surfaceOut) {
LOG_USE_MODULE(vulkanHelper);
if (glfwCreateWindowSurface(obj->deviceInfo.instance, window, NULL, &surfaceOut)) {
LOG_CRIT(L"Couldn't create surface");
}
}
std::pair<VkFormat, VkColorSpaceKHR> getDisplayFormat(VulkanObj* obj) {
if (obj->surfaceCapabilities.formats.empty()) {
return {VK_FORMAT_B8G8R8A8_UNORM, VK_COLOR_SPACE_SRGB_NONLINEAR_KHR};
}
VkFormat format = obj->surfaceCapabilities.formats[0].format;
VkColorSpaceKHR imageColorSpace = obj->surfaceCapabilities.formats[0].colorSpace;
if (obj->surfaceCapabilities.format_unorm_bgra32) {
format = VK_FORMAT_B8G8R8A8_UNORM;
imageColorSpace = VK_COLOR_SPACE_SRGB_NONLINEAR_KHR;
} else if (obj->surfaceCapabilities.format_srgb_bgra32) {
format = VK_FORMAT_B8G8R8A8_SRGB;
imageColorSpace = VK_COLOR_SPACE_SRGB_NONLINEAR_KHR;
}
return {format, imageColorSpace};
}
uint32_t 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,
};
for (uint8_t i = 0; i < numImages; ++i) {
swapchainData.buffers[i].image = images[i];
vkCreateSemaphore(obj->deviceInfo.device, &semCreateInfo, nullptr, &swapchainData.buffers[i].semDisplayReady);
vkCreateSemaphore(obj->deviceInfo.device, &semCreateInfo, nullptr, &swapchainData.buffers[i].semPresentReady);
}
}
// 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
uint32_t curBufferIndex = 0;
// Get first display buffer
while (true) {
auto result = vkAcquireNextImageKHR(obj->deviceInfo.device, swapchainData.swapchain, UINT64_MAX, swapchainData.buffers[curBufferIndex].semDisplayReady,
VK_NULL_HANDLE, &curBufferIndex);
if (result != VK_SUCCESS) {
if (result == VK_NOT_READY) {
using namespace std::chrono_literals;
std::this_thread::sleep_for(100ms); // Not init yet or whatever
continue;
} else
LOG_CRIT(L"vkAcquireNextImageKHR err:%S", string_VkResult(result));
}
break;
}
// -
BOOST_ASSERT_MSG(curBufferIndex == 0, "AcquireNextImage init returned unexpected value");
return curBufferIndex;
}
void submitDisplayTransfer(VkCommandBuffer cmdBuffer, VulkanObj* obj, VkSemaphore semPresentReady, VkSemaphore displayReady, VkSemaphore waitSema,
size_t waitValue) {
LOG_USE_MODULE(vulkanHelper);
size_t waitValues[] = {0, waitValue};
uint32_t waitCount = waitSema != nullptr ? 2 : 1;
VkTimelineSemaphoreSubmitInfo const timelineInfo {
.sType = VK_STRUCTURE_TYPE_TIMELINE_SEMAPHORE_SUBMIT_INFO,
.waitSemaphoreValueCount = waitCount,
.pWaitSemaphoreValues = waitValues,
};
VkPipelineStageFlags waitStage[] = {VK_PIPELINE_STAGE_TRANSFER_BIT, VK_PIPELINE_STAGE_TRANSFER_BIT};
VkSemaphore sems[] = {displayReady, waitSema};
VkSubmitInfo const submitInfo {
.sType = VK_STRUCTURE_TYPE_SUBMIT_INFO,
.pNext = waitSema != nullptr ? &timelineInfo : nullptr,
.waitSemaphoreCount = waitCount,
.pWaitSemaphores = sems,
.pWaitDstStageMask = waitStage,
.commandBufferCount = 1,
.pCommandBuffers = &cmdBuffer,
.signalSemaphoreCount = 1,
.pSignalSemaphores = &semPresentReady,
};
if (VkResult result = vkQueueSubmit(obj->queues.items[getIndex(QueueType::graphics)][0].queue, 1, &submitInfo, VK_NULL_HANDLE); result != VK_SUCCESS) {
LOG_CRIT(L"Couldn't vkQueueSubmit Transfer %S", string_VkResult(result));
}
}
void transfer2Display(VkCommandBuffer cmdBuffer, VulkanObj* obj, vulkan::SwapchainData& swapchain, VkImage displayImage, Objects::ColorAttachment* image,
uint32_t index) {
LOG_USE_MODULE(vulkanHelper);
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");
}
image->displaySrcBarrier(cmdBuffer);
{
VkImageMemoryBarrier const barrier {
.sType = VK_STRUCTURE_TYPE_IMAGE_MEMORY_BARRIER,
.pNext = nullptr,
.srcAccessMask = 0,
.dstAccessMask = VK_ACCESS_TRANSFER_WRITE_BIT,
.oldLayout = VK_IMAGE_LAYOUT_UNDEFINED,
.newLayout = VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL,
.srcQueueFamilyIndex = VK_QUEUE_FAMILY_IGNORED,
.dstQueueFamilyIndex = VK_QUEUE_FAMILY_IGNORED,
.image = swapchain.buffers[index].image,
.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);
}
VkImageBlit const blit {
.srcSubresource =
{
.aspectMask = VK_IMAGE_ASPECT_COLOR_BIT,
.mipLevel = 0,
.baseArrayLayer = 0,
.layerCount = 1,
},
.srcOffsets = {{0, 0, 0}, {(int32_t)swapchain.extent2d.width, (int32_t)swapchain.extent2d.height, 1}},
.dstSubresource =
{
.aspectMask = VK_IMAGE_ASPECT_COLOR_BIT,
.mipLevel = 0,
.baseArrayLayer = 0,
.layerCount = 1,
},
.dstOffsets = {{0, 0, 0}, {(int32_t)swapchain.extent2d.width, (int32_t)swapchain.extent2d.height, 1}},
};
vkCmdBlitImage(cmdBuffer, displayImage, VK_IMAGE_LAYOUT_TRANSFER_SRC_OPTIMAL, swapchain.buffers[index].image, VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL, 1, &blit,
VK_FILTER_LINEAR);
{
// Change to Present Layout
VkImageMemoryBarrier const barrier {
.sType = VK_STRUCTURE_TYPE_IMAGE_MEMORY_BARRIER,
.pNext = nullptr,
.srcAccessMask = VK_ACCESS_TRANSFER_WRITE_BIT,
.dstAccessMask = 0,
.oldLayout = VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL,
.newLayout = VK_IMAGE_LAYOUT_PRESENT_SRC_KHR,
.srcQueueFamilyIndex = VK_QUEUE_FAMILY_IGNORED,
.dstQueueFamilyIndex = VK_QUEUE_FAMILY_IGNORED,
.image = swapchain.buffers[index].image,
.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);
}
// - Present layout
// End CmdBuffer -> Submit
if (vkEndCommandBuffer(cmdBuffer) != VK_SUCCESS) {
LOG_CRIT(L"Couldn't end commandbuffer");
}
// -
}
void presentImage(VulkanObj* obj, vulkan::SwapchainData& swapchain, uint32_t& index) {
LOG_USE_MODULE(vulkanHelper);
VkPresentInfoKHR const presentInfo {
.sType = VK_STRUCTURE_TYPE_PRESENT_INFO_KHR,
.pNext = nullptr,
.waitSemaphoreCount = 1,
.pWaitSemaphores = &swapchain.buffers[index].semPresentReady,
.swapchainCount = 1,
.pSwapchains = &swapchain.swapchain,
.pImageIndices = &index,
.pResults = nullptr,
};
{
OPTICK_GPU_FLIP(&swapchain.swapchain);
OPTICK_CATEGORY("Present", Optick::Category::Wait);
vkQueuePresentKHR(obj->queues.items[getIndex(QueueType::present)][0].queue, &presentInfo);
}
uint32_t swapchainIndex = ++index;
if (swapchainIndex >= swapchain.buffers.size()) swapchainIndex = 0;
auto result =
vkAcquireNextImageKHR(obj->deviceInfo.device, swapchain.swapchain, UINT64_MAX, swapchain.buffers[swapchainIndex].semDisplayReady, VK_NULL_HANDLE, &index);
BOOST_ASSERT_MSG(swapchainIndex == index, "AcquireNextImage returned unexpected value");
}
} // namespace vulkan
// ### Vulkan EXT Function Definitions
VKAPI_ATTR VkResult VKAPI_CALL vkCreateShadersEXT(VkDevice device, uint32_t createInfoCount, const VkShaderCreateInfoEXT* pCreateInfos,
const VkAllocationCallbacks* pAllocator, VkShaderEXT* pShaders) {
static auto fn = (PFN_vkCreateShadersEXT)vkGetInstanceProcAddr(vulkan::getVkInstance(), "vkCreateShadersEXT");
return fn(device, createInfoCount, pCreateInfos, pAllocator, pShaders);
}
VKAPI_ATTR void VKAPI_CALL vkCmdSetDepthClipNegativeOneToOneEXT(VkCommandBuffer commandBuffer, VkBool32 negativeOneToOne) {
static auto fn = (PFN_vkCmdSetDepthClipNegativeOneToOneEXT)vkGetInstanceProcAddr(vulkan::getVkInstance(), "vkCmdSetDepthClipNegativeOneToOneEXT");
fn(commandBuffer, negativeOneToOne);
}
VKAPI_ATTR void VKAPI_CALL vkCmdSetColorWriteEnableEXT(VkCommandBuffer commandBuffer, uint32_t attachmentCount, const VkBool32* pColorWriteEnables) {
static auto fn = (PFN_vkCmdSetColorWriteEnableEXT)vkGetInstanceProcAddr(vulkan::getVkInstance(), "vkCmdSetColorWriteEnableEXT");
fn(commandBuffer, attachmentCount, pColorWriteEnables);
}
VKAPI_ATTR void VKAPI_CALL vkCmdSetColorWriteMaskEXT(VkCommandBuffer commandBuffer, uint32_t firstAttachment, uint32_t attachmentCount,
const VkColorComponentFlags* pColorWriteMasks) {
static auto fn = (PFN_vkCmdSetColorWriteMaskEXT)vkGetInstanceProcAddr(vulkan::getVkInstance(), "vkCmdSetColorWriteMaskEXT");
fn(commandBuffer, firstAttachment, attachmentCount, pColorWriteMasks);
}
VKAPI_ATTR void VKAPI_CALL vkCmdSetColorBlendEnableEXT(VkCommandBuffer commandBuffer, uint32_t firstAttachment, uint32_t attachmentCount,
const VkBool32* pColorBlendEnables) {
static auto fn = (PFN_vkCmdSetColorBlendEnableEXT)vkGetInstanceProcAddr(vulkan::getVkInstance(), "vkCmdSetColorBlendEnableEXT");
fn(commandBuffer, firstAttachment, attachmentCount, pColorBlendEnables);
}
VKAPI_ATTR void VKAPI_CALL vkCmdSetColorBlendEquationEXT(VkCommandBuffer commandBuffer, uint32_t firstAttachment, uint32_t attachmentCount,
const VkColorBlendEquationEXT* pColorBlendEquations) {
static auto fn = (PFN_vkCmdSetColorBlendEquationEXT)vkGetInstanceProcAddr(vulkan::getVkInstance(), "vkCmdSetColorBlendEquationEXT");
fn(commandBuffer, firstAttachment, attachmentCount, pColorBlendEquations);
}
VKAPI_ATTR void VKAPI_CALL vkCmdSetPolygonModeEXT(VkCommandBuffer commandBuffer, VkPolygonMode polygonMode) {
static auto fn = (PFN_vkCmdSetPolygonModeEXT)vkGetInstanceProcAddr(vulkan::getVkInstance(), "vkCmdSetPolygonModeEXT");
fn(commandBuffer, polygonMode);
}
VKAPI_ATTR void VKAPI_CALL vkCmdSetRasterizationSamplesEXT(VkCommandBuffer commandBuffer, VkSampleCountFlagBits rasterizationSamples) {
static auto fn = (PFN_vkCmdSetRasterizationSamplesEXT)vkGetInstanceProcAddr(vulkan::getVkInstance(), "vkCmdSetRasterizationSamplesEXT");
fn(commandBuffer, rasterizationSamples);
}
VKAPI_ATTR void VKAPI_CALL vkCmdSetAlphaToCoverageEnableEXT(VkCommandBuffer commandBuffer, VkBool32 alphaToCoverageEnable) {
static auto fn = (PFN_vkCmdSetAlphaToCoverageEnableEXT)vkGetInstanceProcAddr(vulkan::getVkInstance(), "vkCmdSetAlphaToCoverageEnableEXT");
fn(commandBuffer, alphaToCoverageEnable);
}
VKAPI_ATTR void VKAPI_CALL vkCmdSetAlphaToOneEnableEXT(VkCommandBuffer commandBuffer, VkBool32 alphaToOneEnable) {
static auto fn = (PFN_vkCmdSetAlphaToOneEnableEXT)vkGetInstanceProcAddr(vulkan::getVkInstance(), "vkCmdSetAlphaToOneEnableEXT");
fn(commandBuffer, alphaToOneEnable);
}

View File

@ -0,0 +1,19 @@
#pragma once
#include "vulkanSetup.h"
namespace Objects {
class ColorAttachment;
}
namespace vulkan {
void submitDisplayTransfer(VkCommandBuffer cmdBuffer, VulkanObj* obj, VkSemaphore semPresentReady, VkSemaphore displayReady, VkSemaphore waitSema,
size_t waitValue);
void transfer2Display(VkCommandBuffer cmdBuffer, VulkanObj* obj, vulkan::SwapchainData& swapchain, VkImage displayImage, Objects::ColorAttachment* image,
uint32_t index);
void presentImage(VulkanObj* obj, SwapchainData& swapchain, uint32_t& index);
void waitFlipped(VulkanObj* obj); /// Call before submit
} // namespace vulkan

View File

@ -0,0 +1,741 @@
#include "vulkanSetup.h"
#include <format>
#include <logging.h>
#include <utility/utility.h>
#include <vulkan/vk_enum_string_helper.h>
#define GLFW_INCLUDE_VULKAN
#include <GLFW/glfw3.h>
LOG_DEFINE_MODULE(vulkanSetup);
constexpr std::array<VkValidationFeatureDisableEXT, 0> disabledValidationFeatures {};
constexpr std::array enabledValidationFeatures {VK_VALIDATION_FEATURE_ENABLE_DEBUG_PRINTF_EXT};
constexpr std::array requiredExtensions {
VK_KHR_SWAPCHAIN_EXTENSION_NAME, VK_EXT_COLOR_WRITE_ENABLE_EXTENSION_NAME, VK_KHR_MAINTENANCE_2_EXTENSION_NAME, VK_KHR_TIMELINE_SEMAPHORE_EXTENSION_NAME,
VK_KHR_SHADER_NON_SEMANTIC_INFO_EXTENSION_NAME, VK_KHR_DYNAMIC_RENDERING_EXTENSION_NAME, VK_KHR_BUFFER_DEVICE_ADDRESS_EXTENSION_NAME,
VK_EXT_DESCRIPTOR_INDEXING_EXTENSION_NAME, VK_EXT_DEPTH_CLIP_CONTROL_EXTENSION_NAME, VK_KHR_SAMPLER_MIRROR_CLAMP_TO_EDGE_EXTENSION_NAME,
VK_EXT_DEPTH_RANGE_UNRESTRICTED_EXTENSION_NAME, VK_EXT_INLINE_UNIFORM_BLOCK_EXTENSION_NAME, VK_EXT_SEPARATE_STENCIL_USAGE_EXTENSION_NAME,
VK_EXT_EXTENDED_DYNAMIC_STATE_3_EXTENSION_NAME, VK_EXT_INLINE_UNIFORM_BLOCK_EXTENSION_NAME,
// VK_EXT_SHADER_OBJECT_EXTENSION_NAME,
"VK_KHR_external_memory_win32"};
namespace vulkan {
struct VulkanExtensions {
bool enableValidationLayers = true;
std::vector<const char*> requiredExtensions;
std::vector<VkExtensionProperties> availableExtensions;
std::vector<const char*> requiredLayers;
std::vector<VkLayerProperties> availableLayers;
};
struct QueueInfo {
uint32_t family = 0;
uint32_t index = 0;
uint32_t count = 0;
bool graphics = false;
bool compute = false;
bool transfer = false;
bool present = false;
};
struct VulkanQueues {
uint32_t familyCount = 0;
std::vector<uint32_t> familyUsed;
std::vector<QueueInfo> graphics;
std::vector<QueueInfo> compute;
std::vector<QueueInfo> transfer;
std::vector<QueueInfo> present;
};
VKAPI_ATTR VkResult VKAPI_CALL createDebugUtilsMessengerEXT(VkInstance instance, const VkDebugUtilsMessengerCreateInfoEXT* createInfo,
const VkAllocationCallbacks* allocator, VkDebugUtilsMessengerEXT* messenger) {
if (auto func = reinterpret_cast<PFN_vkCreateDebugUtilsMessengerEXT>(vkGetInstanceProcAddr(instance, "vkCreateDebugUtilsMessengerEXT")); func != nullptr) {
return func(instance, createInfo, allocator, messenger);
}
return VK_ERROR_EXTENSION_NOT_PRESENT;
}
VKAPI_ATTR VkBool32 VKAPI_CALL debugMessengerCallback(VkDebugUtilsMessageSeverityFlagBitsEXT message_severity, VkDebugUtilsMessageTypeFlagsEXT messageTypes,
const VkDebugUtilsMessengerCallbackDataEXT* callback_data, void* /*user_data*/) {
LOG_USE_MODULE(vulkanSetup);
const char* severityStr = nullptr;
bool skip = false;
bool error = false;
bool debugPrintf = false;
switch (message_severity) {
case VK_DEBUG_UTILS_MESSAGE_SEVERITY_VERBOSE_BIT_EXT:
severityStr = "V";
skip = true;
break;
case VK_DEBUG_UTILS_MESSAGE_SEVERITY_INFO_BIT_EXT:
if ((messageTypes & VK_DEBUG_UTILS_MESSAGE_TYPE_VALIDATION_BIT_EXT) != 0 && strcmp(callback_data->pMessageIdName, "UNASSIGNED-DEBUG-PRINTF") == 0) {
debugPrintf = true;
skip = true;
} else {
severityStr = "I";
}
break;
case VK_DEBUG_UTILS_MESSAGE_SEVERITY_WARNING_BIT_EXT: severityStr = "W"; break;
case VK_DEBUG_UTILS_MESSAGE_SEVERITY_ERROR_BIT_EXT:
severityStr = "E";
error = true;
break;
default: severityStr = "?";
}
if (error) {
LOG_WARN(L"[Vulkan][%S][%u]: %S", severityStr, static_cast<uint32_t>(messageTypes), callback_data->pMessage);
} else if (!skip) {
LOG_DEBUG(L"[Vulkan][%S][%u]: %S", severityStr, static_cast<uint32_t>(messageTypes), callback_data->pMessage);
}
if (debugPrintf) {
auto strs = util::splitString(std::string(callback_data->pMessage), U'|');
if (!strs.empty()) {
LOG_DEBUG(L"%S", std::string(strs.back()).data());
}
}
return VK_FALSE;
}
VulkanExtensions getExtensions(bool useValidation) {
LOG_USE_MODULE(vulkanSetup);
uint32_t countAvailableExtensions = 0;
uint32_t countAvailableLayers = 0;
uint32_t countRequiredExtensions = 0;
VulkanExtensions r = {.enableValidationLayers = useValidation};
auto res = glfwGetRequiredInstanceExtensions(&countRequiredExtensions);
for (size_t n = 0; n < countRequiredExtensions; n++) {
r.requiredExtensions.push_back(res[n]);
}
vkEnumerateInstanceExtensionProperties(nullptr, &countAvailableExtensions, nullptr);
r.availableExtensions = std::vector<VkExtensionProperties>(countAvailableExtensions, VkExtensionProperties {});
vkEnumerateInstanceExtensionProperties(nullptr, &countAvailableExtensions, r.availableExtensions.data());
if (std::find_if(r.availableExtensions.begin(), r.availableExtensions.end(),
[](VkExtensionProperties s) { return strcmp(s.extensionName, VK_EXT_DEBUG_UTILS_EXTENSION_NAME) == 0; }) != r.availableExtensions.end()) {
r.requiredExtensions.push_back(VK_EXT_DEBUG_UTILS_EXTENSION_NAME);
} else {
r.enableValidationLayers = false;
}
for (size_t n = 0; n < r.requiredExtensions.size(); ++n) {
LOG_INFO(L"GLFW required extension: %S", r.requiredExtensions[n]);
}
for (const auto& ext: r.availableExtensions) {
LOG_DEBUG(L"Vulkan available extension: %S specVer:%u", ext.extensionName, ext.specVersion);
}
vkEnumerateInstanceLayerProperties(&countAvailableLayers, nullptr);
r.availableLayers = std::vector<VkLayerProperties>(countAvailableLayers, VkLayerProperties {});
vkEnumerateInstanceLayerProperties(&countAvailableLayers, r.availableLayers.data());
for (const auto& l: r.availableLayers) {
LOG_DEBUG(L"Vulkan available layer:%S specVer:%u.%u implVer:%u (%S)", l.layerName, VK_API_VERSION_MAJOR(l.specVersion), VK_API_VERSION_MINOR(l.specVersion),
l.implementationVersion, l.description);
}
if (r.enableValidationLayers) {
r.requiredLayers.push_back("VK_LAYER_KHRONOS_validation");
for (auto const l: r.requiredLayers) {
if (std::find_if(r.availableLayers.begin(), r.availableLayers.end(), [&l](auto s) { return strcmp(s.layerName, l) == 0; }) == r.availableLayers.end()) {
LOG_INFO(L"no validation layer:%S", l);
r.enableValidationLayers = false;
break;
}
}
}
if (r.enableValidationLayers) {
vkEnumerateInstanceExtensionProperties("VK_LAYER_KHRONOS_validation", &countAvailableExtensions, nullptr);
std::vector<VkExtensionProperties> availableExtensions(countAvailableExtensions, VkExtensionProperties {});
vkEnumerateInstanceExtensionProperties("VK_LAYER_KHRONOS_validation", &countAvailableExtensions, availableExtensions.data());
for (const auto& ext: availableExtensions) {
LOG_DEBUG(L"VK_LAYER_KHRONOS_validation available extension: %S version:%u", ext.extensionName, ext.specVersion);
}
if (std::find_if(availableExtensions.begin(), availableExtensions.end(),
[](auto s) { return strcmp(s.extensionName, "VK_EXT_validation_features") == 0; }) != availableExtensions.end()) {
r.requiredExtensions.push_back("VK_EXT_validation_features");
} else {
r.enableValidationLayers = false;
}
}
return r;
}
VulkanQueues findQueues(VkPhysicalDevice device, VkSurfaceKHR surface) {
LOG_USE_MODULE(vulkanSetup);
VulkanQueues qs;
vkGetPhysicalDeviceQueueFamilyProperties(device, &qs.familyCount, nullptr);
std::vector<VkQueueFamilyProperties> queueFamilies(qs.familyCount, VkQueueFamilyProperties {});
vkGetPhysicalDeviceQueueFamilyProperties(device, &qs.familyCount, queueFamilies.data());
std::vector<QueueInfo> queueInfos;
qs.familyUsed.resize(queueFamilies.size());
for (uint32_t n = 0; n < queueFamilies.size(); ++n) {
VkBool32 presentationSupported = VK_FALSE;
vkGetPhysicalDeviceSurfaceSupportKHR(device, n, surface, &presentationSupported);
auto const& queue = queueFamilies[n];
LOG_DEBUG(L"queue family[%u]: %S [count:%u] [present=%S]", n, string_VkQueueFlags(queue.queueFlags).c_str(), queue.queueCount,
util::getBoolStr(presentationSupported == VK_TRUE));
QueueInfo info {
.family = n,
.count = queue.queueCount,
.graphics = (queue.queueFlags & VK_QUEUE_GRAPHICS_BIT) != 0,
.compute = (queue.queueFlags & VK_QUEUE_COMPUTE_BIT) != 0,
.transfer = (queue.queueFlags & VK_QUEUE_TRANSFER_BIT) != 0,
.present = (bool)presentationSupported,
};
queueInfos.push_back(info);
}
// Prio
auto gather = [&queueInfos, &qs](uint32_t count, std::vector<QueueInfo>& queue, auto const cmp) {
for (uint32_t i = queue.size(); i < count; i++) {
if (auto it = std::find_if(queueInfos.begin(), queueInfos.end(), cmp); it != queueInfos.end()) {
it->index = 0;
auto& item = queue.emplace_back(*it);
if (it->count > qs.familyUsed[it->family]) {
item.index = qs.familyUsed[it->family];
qs.familyUsed[it->family]++;
}
} else {
break;
}
}
};
gather(1, qs.graphics, [](QueueInfo& q) { return q.graphics && q.transfer && q.present; });
gather(1, qs.present, [](QueueInfo& q) { return q.graphics && q.transfer && q.present; });
gather(1, qs.transfer, [](QueueInfo& q) { return q.transfer && !q.graphics && !q.compute; });
gather(1, qs.compute, [](QueueInfo& q) { return q.compute && !q.graphics; });
//-
// Fill in whatever is left
gather(1, qs.graphics, [](QueueInfo& q) { return q.transfer == true; });
gather(1, qs.present, [](QueueInfo& q) { return q.present == true; });
gather(1, qs.transfer, [](QueueInfo& q) { return q.graphics == true; });
gather(1, qs.compute, [](QueueInfo& q) { return q.compute == true; });
//
//-
return qs;
}
void dumpQueues(const VulkanQueues& qs) {
LOG_USE_MODULE(vulkanSetup);
std::string familyString;
for (auto u: qs.familyUsed) {
familyString += std::to_string(u) + std::string(", ");
}
LOG_INFO(L"\t familyUsed = [%S]", familyString.c_str());
LOG_INFO(L"\t graphics:");
for (const auto& q: qs.graphics) {
LOG_INFO(L"\t\t family:%u index:%u", q.family, q.index);
}
LOG_INFO(L"\t compute:");
for (const auto& q: qs.compute) {
LOG_INFO(L"\t\t family:%u index:%u", q.family, q.index);
}
LOG_INFO(L"\t transfer:");
for (const auto& q: qs.transfer) {
LOG_INFO(L"\t\t family:%u, index:%u", q.family, q.index);
}
LOG_INFO(L"\t present:");
for (const auto& q: qs.present) {
LOG_INFO(L"\t\t family:%u, index:%u", q.family, q.index);
}
}
void getSurfaceCapabilities(VkPhysicalDevice physicalDevice, VkSurfaceKHR surface, SurfaceCapabilities& r) {
LOG_USE_MODULE(vulkanSetup);
vkGetPhysicalDeviceSurfaceCapabilitiesKHR(physicalDevice, surface, &r.capabilities);
uint32_t formats_count = 0;
vkGetPhysicalDeviceSurfaceFormatsKHR(physicalDevice, surface, &formats_count, nullptr);
r.formats = std::vector<VkSurfaceFormatKHR>(formats_count, VkSurfaceFormatKHR {});
vkGetPhysicalDeviceSurfaceFormatsKHR(physicalDevice, surface, &formats_count, r.formats.data());
uint32_t present_modes_count = 0;
vkGetPhysicalDeviceSurfacePresentModesKHR(physicalDevice, surface, &present_modes_count, nullptr);
r.presentModes = std::vector<VkPresentModeKHR>(present_modes_count, VkPresentModeKHR {});
vkGetPhysicalDeviceSurfacePresentModesKHR(physicalDevice, surface, &present_modes_count, r.presentModes.data());
r.format_srgb_bgra32 = false;
for (const auto& f: r.formats) {
if (f.format == VK_FORMAT_B8G8R8A8_SRGB && f.colorSpace == VK_COLOR_SPACE_SRGB_NONLINEAR_KHR) {
r.format_srgb_bgra32 = true;
break;
}
if (f.format == VK_FORMAT_B8G8R8A8_UNORM && f.colorSpace == VK_COLOR_SPACE_SRGB_NONLINEAR_KHR) {
r.format_unorm_bgra32 = true;
break;
}
}
}
bool checkFormat(VkPhysicalDevice device, VkFormat format, VkFormatFeatureFlags features) {
VkFormatProperties formatProps;
vkGetPhysicalDeviceFormatProperties(device, format, &formatProps);
if ((formatProps.optimalTilingFeatures & features) == features || (formatProps.linearTilingFeatures & features) == features) {
return true;
}
return false;
}
void findPhysicalDevice(VkInstance instance, VkSurfaceKHR surface, SurfaceCapabilities* outCapabilities, VkPhysicalDevice* outDevice, VulkanQueues* outQueues) {
LOG_USE_MODULE(vulkanSetup);
uint32_t devicesCount = 0;
vkEnumeratePhysicalDevices(instance, &devicesCount, nullptr);
if (devicesCount == 0) {
LOG_CRIT(L"No GPUs found");
}
std::vector<VkPhysicalDevice> devices(devicesCount);
vkEnumeratePhysicalDevices(instance, &devicesCount, devices.data());
// Find Best
VkPhysicalDevice bestDevice = nullptr;
VulkanQueues bestQueues;
bool skipDevice = false;
for (const auto& device: devices) {
skipDevice = false;
VkPhysicalDeviceShaderObjectFeaturesEXT shaderObj {.sType = VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_SHADER_OBJECT_FEATURES_EXT, .pNext = nullptr};
VkPhysicalDeviceDescriptorIndexingFeatures indexingFeature {.sType = VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_DESCRIPTOR_INDEXING_FEATURES_EXT,
.pNext = &shaderObj};
VkPhysicalDeviceColorWriteEnableFeaturesEXT colorWriteExt {
.sType = VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_COLOR_WRITE_ENABLE_FEATURES_EXT,
.pNext = &indexingFeature,
};
VkPhysicalDeviceFeatures2 deviceFeatures {
.sType = VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_FEATURES_2,
.pNext = &colorWriteExt,
};
VkPhysicalDeviceProperties deviceProperties;
vkGetPhysicalDeviceProperties(device, &deviceProperties);
vkGetPhysicalDeviceFeatures2(device, &deviceFeatures);
LOG_INFO(L"Vulkan device: %S api:%u.%u:%u", deviceProperties.deviceName, VK_API_VERSION_MAJOR(deviceProperties.apiVersion),
VK_API_VERSION_MINOR(deviceProperties.apiVersion), VK_API_VERSION_PATCH(deviceProperties.apiVersion));
auto qs = findQueues(device, surface);
dumpQueues(qs);
if (shaderObj.shaderObject == VK_FALSE) {
LOG_WARN(L"shaderObject is not supported");
skipDevice = true;
}
if (qs.graphics.empty() || qs.compute.empty() || qs.transfer.empty() || qs.present.empty()) {
LOG_WARN(L"Not enough queues");
skipDevice = true;
}
if (colorWriteExt.colorWriteEnable != VK_TRUE) {
LOG_WARN(L"colorWriteEnable is not supported");
skipDevice = true;
}
if (deviceFeatures.features.fragmentStoresAndAtomics != VK_TRUE) {
LOG_WARN(L"fragmentStoresAndAtomics is not supported");
skipDevice = true;
}
if (deviceFeatures.features.samplerAnisotropy != VK_TRUE) {
LOG_WARN(L"samplerAnisotropy is not supported");
skipDevice = true;
}
if (!skipDevice) {
uint32_t numExt = 0;
vkEnumerateDeviceExtensionProperties(device, nullptr, &numExt, nullptr);
if (numExt == 0) {
LOG_CRIT(L"no extensions found");
}
std::vector<VkExtensionProperties> availableExt(numExt, VkExtensionProperties {});
vkEnumerateDeviceExtensionProperties(device, nullptr, &numExt, availableExt.data());
for (const char* ext: requiredExtensions) {
if (std::find_if(availableExt.begin(), availableExt.end(), [&ext](auto p) { return strcmp(p.extensionName, ext) == 0; }) == availableExt.end()) {
LOG_ERR(L"%S not supported", ext);
skipDevice = true;
break;
}
}
if (skipDevice) {
for (const auto& ext: availableExt) {
LOG_TRACE(L"Vulkan available extension: %S, version:%u", ext.extensionName, ext.specVersion);
}
}
}
if (!skipDevice) {
getSurfaceCapabilities(device, surface, *outCapabilities);
if ((outCapabilities->capabilities.supportedUsageFlags & VK_IMAGE_USAGE_TRANSFER_DST_BIT) == 0) {
LOG_DEBUG(L"Surface cannot be destination of blit");
skipDevice = true;
}
}
if (!skipDevice && !checkFormat(device, VK_FORMAT_R8G8B8A8_SRGB, VK_FORMAT_FEATURE_BLIT_SRC_BIT)) {
LOG_DEBUG(L"Format VK_FORMAT_R8G8B8A8_SRGB cannot be used as transfer source");
skipDevice = true;
}
if (!skipDevice && !checkFormat(device, VK_FORMAT_D32_SFLOAT, VK_FORMAT_FEATURE_DEPTH_STENCIL_ATTACHMENT_BIT)) {
LOG_DEBUG(L"Format VK_FORMAT_D32_SFLOAT cannot be used as depth buffer");
skipDevice = true;
}
if (!skipDevice && !checkFormat(device, VK_FORMAT_D32_SFLOAT_S8_UINT, VK_FORMAT_FEATURE_DEPTH_STENCIL_ATTACHMENT_BIT)) {
LOG_DEBUG(L"Format VK_FORMAT_D32_SFLOAT_S8_UINT cannot be used as depth buffer");
skipDevice = true;
}
if (!skipDevice && !checkFormat(device, VK_FORMAT_D16_UNORM, VK_FORMAT_FEATURE_DEPTH_STENCIL_ATTACHMENT_BIT)) {
LOG_DEBUG(L"Format VK_FORMAT_D16_UNORM cannot be used as depth buffer");
skipDevice = true;
}
if (!skipDevice && !checkFormat(device, VK_FORMAT_D24_UNORM_S8_UINT, VK_FORMAT_FEATURE_DEPTH_STENCIL_ATTACHMENT_BIT)) {
LOG_DEBUG(L"Format VK_FORMAT_D24_UNORM_S8_UINT cannot be used as depth buffer");
// skipDevice = true;
}
if (!skipDevice && !checkFormat(device, VK_FORMAT_BC3_SRGB_BLOCK, VK_FORMAT_FEATURE_SAMPLED_IMAGE_BIT | VK_FORMAT_FEATURE_TRANSFER_DST_BIT)) {
LOG_DEBUG(L"Format VK_FORMAT_BC3_SRGB_BLOCK cannot be used as texture");
skipDevice = true;
}
if (!skipDevice && !checkFormat(device, VK_FORMAT_R8G8B8A8_SRGB, VK_FORMAT_FEATURE_SAMPLED_IMAGE_BIT | VK_FORMAT_FEATURE_TRANSFER_DST_BIT)) {
LOG_DEBUG(L"Format VK_FORMAT_R8G8B8A8_SRGB cannot be used as texture");
skipDevice = true;
}
if (!skipDevice && !checkFormat(device, VK_FORMAT_R8_UNORM, VK_FORMAT_FEATURE_SAMPLED_IMAGE_BIT | VK_FORMAT_FEATURE_TRANSFER_DST_BIT)) {
LOG_DEBUG(L"Format VK_FORMAT_R8_UNORM cannot be used as texture");
skipDevice = true;
}
if (!skipDevice && !checkFormat(device, VK_FORMAT_R8G8_UNORM, VK_FORMAT_FEATURE_SAMPLED_IMAGE_BIT | VK_FORMAT_FEATURE_TRANSFER_DST_BIT)) {
LOG_DEBUG(L"Format VK_FORMAT_R8G8_UNORM cannot be used as texture");
skipDevice = true;
}
if (!skipDevice && !checkFormat(device, VK_FORMAT_R8G8B8A8_SRGB, VK_FORMAT_FEATURE_STORAGE_IMAGE_BIT | VK_FORMAT_FEATURE_TRANSFER_DST_BIT)) {
LOG_DEBUG(L"Format VK_FORMAT_R8G8B8A8_SRGB cannot be used as texture");
if (!skipDevice && !checkFormat(device, VK_FORMAT_R8G8B8A8_UNORM, VK_FORMAT_FEATURE_STORAGE_IMAGE_BIT | VK_FORMAT_FEATURE_TRANSFER_DST_BIT)) {
LOG_DEBUG(L"Format VK_FORMAT_R8G8B8A8_UNORM cannot be used as texture");
skipDevice = true;
}
}
if (!skipDevice && !checkFormat(device, VK_FORMAT_B8G8R8A8_SRGB, VK_FORMAT_FEATURE_STORAGE_IMAGE_BIT | VK_FORMAT_FEATURE_TRANSFER_DST_BIT)) {
LOG_DEBUG(L"Format VK_FORMAT_B8G8R8A8_SRGB cannot be used as texture");
if (!skipDevice && !checkFormat(device, VK_FORMAT_B8G8R8A8_UNORM, VK_FORMAT_FEATURE_STORAGE_IMAGE_BIT | VK_FORMAT_FEATURE_TRANSFER_DST_BIT)) {
LOG_DEBUG(L"Format VK_FORMAT_B8G8R8A8_UNORM cannot be used as texture");
skipDevice = true;
}
}
if (!skipDevice && deviceProperties.limits.maxSamplerAnisotropy < 16.0f) {
LOG_DEBUG(L"maxSamplerAnisotropy < 16.0f");
skipDevice = true;
}
if (skipDevice) {
continue;
}
if (bestDevice == nullptr || deviceProperties.deviceType == VK_PHYSICAL_DEVICE_TYPE_DISCRETE_GPU) {
bestDevice = device;
bestQueues = qs;
}
}
// -Find best
*outDevice = bestDevice;
*outQueues = bestQueues;
}
VkDevice createDevice(VkPhysicalDevice physicalDevice, VkSurfaceKHR surface, VulkanExtensions const extensions, VulkanQueues const& queues,
bool useValidation) {
std::vector<VkDeviceQueueCreateInfo> queueCreateInfo(queues.familyCount);
std::vector<std::vector<float>> queuePrio(queues.familyCount);
uint32_t numQueueCreateInfo = 0;
for (uint32_t i = 0; i < queues.familyCount; i++) {
if (queues.familyUsed[i] != 0) {
for (uint32_t pi = 0; pi < queues.familyUsed[i]; pi++) {
queuePrio[numQueueCreateInfo].push_back(1.0f);
}
queueCreateInfo[numQueueCreateInfo].sType = VK_STRUCTURE_TYPE_DEVICE_QUEUE_CREATE_INFO;
queueCreateInfo[numQueueCreateInfo].pNext = nullptr;
queueCreateInfo[numQueueCreateInfo].flags = 0;
queueCreateInfo[numQueueCreateInfo].queueFamilyIndex = i;
queueCreateInfo[numQueueCreateInfo].queueCount = queues.familyUsed[i];
queueCreateInfo[numQueueCreateInfo].pQueuePriorities = queuePrio[numQueueCreateInfo].data();
numQueueCreateInfo++;
}
}
VkPhysicalDeviceFeatures deviceFeatures {
.geometryShader = VK_TRUE, .fillModeNonSolid = VK_TRUE, .wideLines = VK_TRUE, .samplerAnisotropy = VK_TRUE, .fragmentStoresAndAtomics = VK_TRUE,
// deviceFeatures.shaderImageGatherExtended = VK_TRUE;
};
VkPhysicalDeviceInlineUniformBlockFeaturesEXT uniBlock {
.sType = VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_INLINE_UNIFORM_BLOCK_FEATURES,
.pNext = nullptr,
.inlineUniformBlock = VK_TRUE,
.descriptorBindingInlineUniformBlockUpdateAfterBind = VK_TRUE,
};
VkPhysicalDeviceShaderObjectFeaturesEXT shaderObjectFeatures {.sType = VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_SHADER_OBJECT_FEATURES_EXT, .shaderObject = VK_TRUE};
VkPhysicalDeviceExtendedDynamicState3FeaturesEXT extendedDynamic3Features {
.sType = VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_EXTENDED_DYNAMIC_STATE_3_FEATURES_EXT,
.pNext = &uniBlock,
.extendedDynamicState3PolygonMode = VK_TRUE,
.extendedDynamicState3RasterizationSamples = VK_TRUE,
//.extendedDynamicState3AlphaToCoverageEnable = VK_TRUE,
//.extendedDynamicState3AlphaToOneEnable = VK_TRUE,
.extendedDynamicState3ColorBlendEnable = VK_TRUE,
.extendedDynamicState3ColorBlendEquation = VK_TRUE,
.extendedDynamicState3ColorWriteMask = VK_TRUE,
.extendedDynamicState3DepthClipNegativeOneToOne = VK_TRUE,
};
VkPhysicalDeviceDepthClipControlFeaturesEXT depthClipControlFeatures {
.sType = VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_DEPTH_CLIP_CONTROL_FEATURES_EXT,
.pNext = &extendedDynamic3Features,
.depthClipControl = VK_TRUE,
};
VkPhysicalDeviceDynamicRenderingFeatures dynamicRenderingFeature {
.sType = VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_DYNAMIC_RENDERING_FEATURES,
.pNext = &depthClipControlFeatures,
.dynamicRendering = VK_TRUE,
};
VkPhysicalDeviceTimelineSemaphoreFeatures timelineSemaphoreFeature {
.sType = VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_TIMELINE_SEMAPHORE_FEATURES,
.pNext = &dynamicRenderingFeature,
.timelineSemaphore = VK_TRUE,
};
VkPhysicalDeviceColorWriteEnableFeaturesEXT colorWriteExt {
.sType = VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_COLOR_WRITE_ENABLE_FEATURES_EXT,
.pNext = &timelineSemaphoreFeature,
.colorWriteEnable = VK_TRUE,
};
VkPhysicalDeviceBufferDeviceAddressFeatures bufferDeviceAddress {
.sType = VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_BUFFER_DEVICE_ADDRESS_FEATURES,
.pNext = &colorWriteExt,
.bufferDeviceAddress = VK_TRUE,
.bufferDeviceAddressCaptureReplay = useValidation ? VK_TRUE : VK_FALSE,
};
VkPhysicalDeviceDescriptorIndexingFeatures descIndexing {
.sType = VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_DESCRIPTOR_INDEXING_FEATURES,
.pNext = &bufferDeviceAddress,
.descriptorBindingUniformBufferUpdateAfterBind = VK_TRUE,
.descriptorBindingSampledImageUpdateAfterBind = VK_TRUE,
.descriptorBindingStorageImageUpdateAfterBind = VK_TRUE,
.descriptorBindingStorageBufferUpdateAfterBind = VK_TRUE,
.descriptorBindingPartiallyBound = VK_TRUE,
.descriptorBindingVariableDescriptorCount = VK_TRUE,
.runtimeDescriptorArray = VK_TRUE,
};
VkDeviceCreateInfo const createInfo {
.sType = VK_STRUCTURE_TYPE_DEVICE_CREATE_INFO,
.pNext = &descIndexing,
.flags = 0,
.queueCreateInfoCount = numQueueCreateInfo,
.pQueueCreateInfos = queueCreateInfo.data(),
.enabledLayerCount = (uint32_t)extensions.requiredLayers.size(),
.ppEnabledLayerNames = extensions.requiredLayers.data(),
.enabledExtensionCount = requiredExtensions.size(),
.ppEnabledExtensionNames = requiredExtensions.data(),
.pEnabledFeatures = &deviceFeatures,
};
VkDevice device = nullptr;
vkCreateDevice(physicalDevice, &createInfo, nullptr, &device);
return device;
}
static VkPhysicalDeviceProperties g_PhysicalDeviceProperties;
static VkInstance g_VkInstance;
VulkanObj* initVulkan(GLFWwindow* window, VkSurfaceKHR& surface, bool useValidation) {
LOG_USE_MODULE(vulkanSetup);
auto obj = new VulkanObj;
// Create Instance&Debug
VkDebugUtilsMessengerCreateInfoEXT dbgCreateInfo {
.sType = VK_STRUCTURE_TYPE_DEBUG_UTILS_MESSENGER_CREATE_INFO_EXT,
.pNext = nullptr,
.flags = 0,
.messageSeverity =
static_cast<uint32_t>(VK_DEBUG_UTILS_MESSAGE_SEVERITY_VERBOSE_BIT_EXT) | static_cast<uint32_t>(VK_DEBUG_UTILS_MESSAGE_SEVERITY_INFO_BIT_EXT) |
static_cast<uint32_t>(VK_DEBUG_UTILS_MESSAGE_SEVERITY_WARNING_BIT_EXT) | static_cast<uint32_t>(VK_DEBUG_UTILS_MESSAGE_SEVERITY_ERROR_BIT_EXT),
.messageType = static_cast<uint32_t>(VK_DEBUG_UTILS_MESSAGE_TYPE_GENERAL_BIT_EXT) |
static_cast<uint32_t>(VK_DEBUG_UTILS_MESSAGE_TYPE_VALIDATION_BIT_EXT) |
static_cast<uint32_t>(VK_DEBUG_UTILS_MESSAGE_TYPE_PERFORMANCE_BIT_EXT),
.pfnUserCallback = debugMessengerCallback,
.pUserData = nullptr,
};
VkApplicationInfo const appInfo {
.sType = VK_STRUCTURE_TYPE_APPLICATION_INFO,
.pNext = nullptr,
.pApplicationName = "psOff",
.applicationVersion = 1,
.pEngineName = "psEngine",
.engineVersion = 1,
.apiVersion = VK_API_VERSION_1_3,
};
auto extensions = getExtensions(useValidation);
VkInstanceCreateInfo const instInfo {
.sType = VK_STRUCTURE_TYPE_INSTANCE_CREATE_INFO,
.pNext = extensions.enableValidationLayers ? &dbgCreateInfo : nullptr,
.flags = 0,
.pApplicationInfo = &appInfo,
.enabledLayerCount = (uint32_t)extensions.requiredLayers.size(),
.ppEnabledLayerNames = extensions.requiredLayers.data(),
.enabledExtensionCount = (uint32_t)extensions.requiredExtensions.size(),
.ppEnabledExtensionNames = extensions.requiredExtensions.data(),
};
if (VkResult result = vkCreateInstance(&instInfo, nullptr, &obj->deviceInfo.instance); result != VK_SUCCESS) {
if (result == VK_ERROR_INCOMPATIBLE_DRIVER)
LOG_CRIT(L"vkCreateInstance() Error:VK_ERROR_INCOMPATIBLE_DRIVER");
else
LOG_CRIT(L"vkCreateInstance() Error:%S", string_VkResult(result));
}
g_VkInstance = obj->deviceInfo.instance;
if (extensions.enableValidationLayers) {
// dbgCreateInfo.pNext = nullptr;
if (auto result = createDebugUtilsMessengerEXT(obj->deviceInfo.instance, &dbgCreateInfo, nullptr, &obj->debugMessenger); result != VK_SUCCESS) {
LOG_CRIT(L"createDebugUtilsMessengerEXT() %S", string_VkResult(result));
}
}
// -
if (auto result = glfwCreateWindowSurface(obj->deviceInfo.instance, window, NULL, &surface); result != VK_SUCCESS) {
LOG_CRIT(L"glfwCreateWindowSurface() Error:%S", string_VkResult(result));
}
VulkanQueues queues;
findPhysicalDevice(obj->deviceInfo.instance, surface, &obj->surfaceCapabilities, &obj->deviceInfo.physicalDevice, &queues);
if (obj->deviceInfo.physicalDevice == nullptr) {
LOG_CRIT(L"Couldn't find a suitable device");
}
vkGetPhysicalDeviceProperties(obj->deviceInfo.physicalDevice, &g_PhysicalDeviceProperties);
{
auto const text = std::format("Selected GPU:{} api:{}.{}", g_PhysicalDeviceProperties.deviceName,
VK_API_VERSION_MAJOR(g_PhysicalDeviceProperties.apiVersion), VK_API_VERSION_MINOR(g_PhysicalDeviceProperties.apiVersion));
printf("%s\n", text.data());
LOG_INFO(L"%S", text.data());
}
obj->deviceInfo.device = createDevice(obj->deviceInfo.physicalDevice, surface, extensions, queues, useValidation);
if (obj->deviceInfo.device == nullptr) {
LOG_CRIT(L"Couldn't create vulkanDevice");
}
// Create queues
{
auto queueFunc = [](VkDevice device, std::vector<QueueInfo> const queueInfos, std::vector<Queues::Info>& out) {
for (auto& item: queueInfos) {
VkQueue queue;
vkGetDeviceQueue(device, item.family, item.index, &queue);
out.push_back({queue, item.family});
}
};
queueFunc(obj->deviceInfo.device, queues.compute, obj->queues.items[getIndex(QueueType::compute)]);
queueFunc(obj->deviceInfo.device, queues.present, obj->queues.items[getIndex(QueueType::present)]);
queueFunc(obj->deviceInfo.device, queues.transfer, obj->queues.items[getIndex(QueueType::transfer)]);
queueFunc(obj->deviceInfo.device, queues.graphics, obj->queues.items[getIndex(QueueType::graphics)]);
}
//-
return obj;
}
VkPhysicalDeviceLimits const* getPhysicalLimits() {
return &g_PhysicalDeviceProperties.limits;
}
VkInstance const getVkInstance() {
return g_VkInstance;
}
std::string_view const getGPUName() {
return g_PhysicalDeviceProperties.deviceName;
}
void deinitVulkan(VulkanObj* obj) {
delete obj;
}
} // namespace vulkan

View File

@ -0,0 +1,95 @@
#pragma once
#include <array>
#include <string>
#include <type_traits>
#include <utility>
#include <vector>
#include <vulkan/vulkan_core.h>
struct GLFWwindow;
namespace vulkan {
struct DeviceInfo {
VkInstance instance = nullptr;
VkPhysicalDevice physicalDevice = nullptr;
VkDevice device = nullptr;
};
enum class QueueType : uint8_t {
graphics = 0,
compute,
transfer,
present,
numTypes,
};
constexpr std::underlying_type<QueueType>::type getIndex(QueueType type) {
return (std::underlying_type<QueueType>::type)type;
}
struct Queues {
struct Info {
VkQueue queue = nullptr;
uint32_t family = 0;
size_t useCount = 0;
Info(VkQueue queue_, uint32_t family_): queue(queue_), family(family_) {}
};
std::array<std::vector<Info>, getIndex(QueueType::numTypes)> items {}; /// first: VkQueue, second: familyindex
};
struct SwapchainData {
VkSwapchainKHR swapchain = nullptr;
VkFormat format = VK_FORMAT_UNDEFINED;
VkExtent2D extent2d = {};
struct DisplayBuffers {
uint64_t bufferVaddr = 0;
uint32_t bufferSize = 0;
uint32_t bufferAlign = 0;
VkImage image;
VkSemaphore semDisplayReady;
VkCommandBuffer transferBuffer;
VkSemaphore semPresentReady;
};
VkCommandPool commandPool;
std::vector<DisplayBuffers> buffers;
};
struct SurfaceCapabilities {
VkSurfaceCapabilitiesKHR capabilities {};
std::vector<VkSurfaceFormatKHR> formats;
std::vector<VkPresentModeKHR> presentModes;
bool format_srgb_bgra32 = false;
bool format_unorm_bgra32 = false;
};
struct VulkanObj {
DeviceInfo deviceInfo;
VkDebugUtilsMessengerEXT debugMessenger = nullptr;
SurfaceCapabilities surfaceCapabilities;
Queues queues;
};
VulkanObj* initVulkan(GLFWwindow* window, VkSurfaceKHR& surface, bool useValidation);
void deinitVulkan(VulkanObj* obj);
void createSurface(VulkanObj* obj, GLFWwindow* window, VkSurfaceKHR& surfaceOut);
VkPhysicalDeviceLimits const* getPhysicalLimits();
VkInstance const getVkInstance();
std::string_view const getGPUName();
std::pair<VkFormat, VkColorSpaceKHR> getDisplayFormat(VulkanObj* obj);
uint32_t createData(VulkanObj* obj, VkSurfaceKHR surface, SwapchainData& swapchainData, uint32_t width, uint32_t height, bool enableVsync); // Swapchain
} // namespace vulkan

View File

@ -1,15 +1,12 @@
#include "common.h"
#include "../libSceNpManager/types.h"
#include <fileManager.h>
#include <systemContent.h>
#include <logging.h>
#include "common.h"
#include "types.h"
#include <fileManager.h>
#include <filesystem>
#include <logging.h>
#include <optional>
#include <systemContent.h>
LOG_DEFINE_MODULE(libSceAppContent);
@ -23,6 +20,7 @@ EXPORT SYSV_ABI int32_t sceAppContentInitialize(SceAppContentInitParam* initPara
bootParam->attr = 0;
return Ok;
}
EXPORT SYSV_ABI int32_t sceAppContentAppParamGetInt(SceAppContentAppParamId paramId, int32_t* value) {
LOG_USE_MODULE(libSceAppContent);
@ -44,40 +42,47 @@ EXPORT SYSV_ABI int32_t sceAppContentAppParamGetInt(SceAppContentAppParamId para
return retValue.has_value() ? Ok : Err::PARAMETER;
return Ok;
}
EXPORT SYSV_ABI int32_t sceAppContentGetAddcontInfoList(SceNpServiceLabel serviceLabel, SceAppContentAddcontInfo* list, uint32_t listNum, uint32_t* hitNum) {
LOG_USE_MODULE(libSceAppContent);
LOG_ERR(L"todo %S", __FUNCTION__);
*hitNum = 0;
return Ok;
}
EXPORT SYSV_ABI int32_t sceAppContentGetAddcontInfo(SceNpServiceLabel serviceLabel, const SceNpUnifiedEntitlementLabel* entitlementLabel,
SceAppContentAddcontInfo* info) {
LOG_USE_MODULE(libSceAppContent);
LOG_ERR(L"todo %S", __FUNCTION__);
return Ok;
}
EXPORT SYSV_ABI int32_t sceAppContentAddcontMount(SceNpServiceLabel serviceLabel, const SceNpUnifiedEntitlementLabel* entitlementLabel,
SceAppContentMountPoint* mountPoint) {
LOG_USE_MODULE(libSceAppContent);
LOG_ERR(L"todo %S", __FUNCTION__);
return Ok;
}
EXPORT SYSV_ABI int32_t sceAppContentAddcontUnmount(const SceAppContentMountPoint* mountPoint) {
LOG_USE_MODULE(libSceAppContent);
LOG_ERR(L"todo %S", __FUNCTION__);
return Ok;
}
EXPORT SYSV_ABI int32_t sceAppContentAddcontEnqueueDownload(SceNpServiceLabel serviceLabel, const SceNpUnifiedEntitlementLabel* entitlementLabel) {
LOG_USE_MODULE(libSceAppContent);
LOG_ERR(L"todo %S", __FUNCTION__);
return Ok;
}
EXPORT SYSV_ABI int32_t sceAppContentGetEntitlementKey(SceNpServiceLabel serviceLabel, const SceNpUnifiedEntitlementLabel* entitlementLabel,
SceAppContentEntitlementKey* key) {
LOG_USE_MODULE(libSceAppContent);
LOG_ERR(L"todo %S", __FUNCTION__);
return Ok;
}
EXPORT SYSV_ABI int32_t sceAppContentTemporaryDataMount2(SceAppContentTemporaryDataOption option, SceAppContentMountPoint* mountPoint) {
auto tempFolder = accessFileManager().getMountPoint(MountType::Temp, MOUNT_POINT_TEMP.data());
if ((option & 1) > 0) {
@ -89,11 +94,13 @@ EXPORT SYSV_ABI int32_t sceAppContentTemporaryDataMount2(SceAppContentTemporaryD
return Ok;
}
EXPORT SYSV_ABI int32_t sceAppContentTemporaryDataUnmount(const SceAppContentMountPoint* mountPoint) {
LOG_USE_MODULE(libSceAppContent);
LOG_ERR(L"todo %S", __FUNCTION__);
return Ok;
}
EXPORT SYSV_ABI int32_t sceAppContentTemporaryDataFormat(const SceAppContentMountPoint* mountPoint) {
LOG_USE_MODULE(libSceAppContent);
auto tempFolder = accessFileManager().getMappedPath(mountPoint->data);
@ -104,16 +111,19 @@ EXPORT SYSV_ABI int32_t sceAppContentTemporaryDataFormat(const SceAppContentMoun
}
return Ok;
}
EXPORT SYSV_ABI int32_t sceAppContentDownloadDataFormat(const SceAppContentMountPoint* mountPoint) {
LOG_USE_MODULE(libSceAppContent);
LOG_ERR(L"todo %S", __FUNCTION__);
return Ok;
}
EXPORT SYSV_ABI int32_t sceAppContentAddcontDelete(SceNpServiceLabel serviceLabel, const SceNpUnifiedEntitlementLabel* entitlementLabel) {
LOG_USE_MODULE(libSceAppContent);
LOG_ERR(L"todo %S", __FUNCTION__);
return Ok;
}
EXPORT SYSV_ABI int32_t sceAppContentGetAddcontDownloadProgress(SceNpServiceLabel serviceLabel, const SceNpUnifiedEntitlementLabel* entitlementLabel,
SceAppContentAddcontDownloadProgress* progress) {
LOG_USE_MODULE(libSceAppContent);

View File

@ -1,10 +1,10 @@
#include "common.h"
#include <logging.h>
#include "portaudio.h"
#include "types.h"
#include <array>
#include <chrono>
#include <logging.h>
#include <mutex>
LOG_DEFINE_MODULE(libSceAudioOut);
@ -53,6 +53,7 @@ EXPORT SYSV_ABI int32_t sceAudioOutInit(void) {
Pa_Initialize();
return Ok;
}
EXPORT SYSV_ABI int32_t sceAudioOutOpen(int32_t userId, SceAudioOutPortType type, int32_t index, uint32_t len, uint32_t freq, SceAudioOutParamFormat format) {
LOG_USE_MODULE(libSceAudioOut);
LOG_TRACE(L"%S", __FUNCTION__);
@ -128,6 +129,7 @@ EXPORT SYSV_ABI int32_t sceAudioOutOpen(int32_t userId, SceAudioOutPortType type
}
return Err::PORT_FULL;
}
EXPORT SYSV_ABI int32_t sceAudioOutClose(int32_t handle) {
LOG_USE_MODULE(libSceAudioOut);
LOG_TRACE(L"%S", __FUNCTION__);
@ -144,12 +146,14 @@ EXPORT SYSV_ABI int32_t sceAudioOutClose(int32_t handle) {
}
return Ok;
}
EXPORT SYSV_ABI int32_t sceAudioOutOutput(int32_t handle, const void* ptr) {
auto pimpl = getData();
// std::unique_lock const lock(pimpl->mutexInt);
return writeOut(pimpl, handle, ptr);
}
EXPORT SYSV_ABI int32_t sceAudioOutSetVolume(int32_t handle, int32_t flag, int32_t* vol) {
LOG_USE_MODULE(libSceAudioOut);
auto pimpl = getData();
@ -178,6 +182,7 @@ EXPORT SYSV_ABI int32_t sceAudioOutSetVolume(int32_t handle, int32_t flag, int32
}
return Ok;
}
EXPORT SYSV_ABI int32_t sceAudioOutOutputs(SceAudioOutOutputParam* param, uint32_t num) {
auto pimpl = getData();
@ -187,6 +192,7 @@ EXPORT SYSV_ABI int32_t sceAudioOutOutputs(SceAudioOutOutputParam* param, uint32
}
return Ok;
}
EXPORT SYSV_ABI int32_t sceAudioOutGetLastOutputTime(int32_t handle, uint64_t* outputTime) {
auto pimpl = getData();
@ -197,9 +203,11 @@ EXPORT SYSV_ABI int32_t sceAudioOutGetLastOutputTime(int32_t handle, uint64_t* o
*outputTime = port.lastOutputTime;
return Ok;
}
EXPORT SYSV_ABI int32_t sceAudioOutSetMixLevelPadSpk(int32_t handle, int32_t mixLevel) {
return Ok;
}
EXPORT SYSV_ABI int32_t sceAudioOutGetPortState(int32_t handle, SceAudioOutPortState* state) {
auto pimpl = getData();
@ -212,10 +220,12 @@ EXPORT SYSV_ABI int32_t sceAudioOutGetPortState(int32_t handle, SceAudioOutPortS
state->output = (uint16_t)SceAudioOutStateOutput::CONNECTED_PRIMARY;
return Ok;
}
EXPORT SYSV_ABI int32_t sceAudioOutGetSystemState(SceAudioOutSystemState* state) {
state->loudness = -70.0f;
return Ok;
}
EXPORT SYSV_ABI int32_t sceAudioOutSetSystemDebugState(SceAudioOutSystemDebugStateElement elem, SceAudioOutSystemDebugStateParam* param) {
return Ok;
}

View File

@ -1,8 +1,8 @@
#include "common.h"
#include "core/kernel/eventqueue_types.h"
#include "types.h"
#include <eventqueue_types.h>
#include <igraphics.h>
#include <graphics.h>
#include <logging.h>
#include <pm4_custom.h>
#include <timer.h>
@ -23,7 +23,7 @@ void event_reset_func(Kernel::EventQueue::KernelEqueueEvent* event) {
event->event.data = 0;
}
void event_delete_func(Kernel::EventQueue::KernelEqueue* eq, Kernel::EventQueue::KernelEqueueEvent* event) {
void event_delete_func(Kernel::EventQueue::IKernelEqueue_t eq, Kernel::EventQueue::KernelEqueueEvent* event) {
accessVideoOut().getGraphics()->removeEvent(eq, event->event.ident);
}
} // namespace
@ -198,6 +198,7 @@ int SYSV_ABI sceGnmSubmitAndFlipCommandBuffers(uint32_t count, void** dcb_gpu_ad
ccb_sizes_in_bytes, handle, index, flip_mode, flip_arg, true);
return Ok;
}
int SYSV_ABI sceGnmSubmitAndFlipCommandBuffersForWorkload(uint64_t workload, uint32_t count, void** dcb_gpu_addrs, const uint32_t* dcb_sizes_in_bytes,
void** ccb_gpu_addrs, const uint32_t* ccb_sizes_in_bytes, int handle, int index, int flip_mode,
int64_t flip_arg) {
@ -239,6 +240,7 @@ int SYSV_ABI sceGnmSetVgtControl(uint32_t* cmdOut, uint64_t size, uint32_t primG
}
return -1;
}
int SYSV_ABI sceGnmResetVgtControl(uint32_t* cmdOut, int32_t param) {
LOG_USE_MODULE(libSceGraphicsDriver);
LOG_TRACE(L"%S", __FUNCTION__);
@ -438,10 +440,11 @@ int SYSV_ABI sceGnmUnregisterOwnerAndResources(uint32_t owner_handle) {
int SYSV_ABI sceGnmUnregisterResource(uint32_t resource_handle) {
return Ok;
}
// - tracing
// #####################################
int SYSV_ABI sceGnmAddEqEvent(Kernel::EventQueue::KernelEqueue* eq, int id, void* udata) {
int SYSV_ABI sceGnmAddEqEvent(Kernel::EventQueue::IKernelEqueue_t eq, int id, void* udata) {
Kernel::EventQueue::KernelEqueueEvent event = {.triggered = false,
.event =
{
@ -463,12 +466,13 @@ int SYSV_ABI sceGnmAddEqEvent(Kernel::EventQueue::KernelEqueue* eq, int id, void
return accessVideoOut().getGraphics()->addEvent(event, eq);
}
int SYSV_ABI sceGnmDeleteEqEvent(Kernel::EventQueue::KernelEqueue* eq, int id) {
int SYSV_ABI sceGnmDeleteEqEvent(Kernel::EventQueue::IKernelEqueue_t eq, int id) {
accessVideoOut().getGraphics()->removeEvent(eq, id);
return Ok;
}
void SYSV_ABI sceGnmInsertSetMarker(const char* debugString) {}
void SYSV_ABI sceGnmInsertSetColorMarker(const char* debugString, uint32_t argbColor) {}
SceWorkloadStatus SYSV_ABI sceGnmCreateWorkloadStream(const char* name, SceWorkloadStream* stream) {
@ -484,9 +488,11 @@ SceWorkloadStatus SYSV_ABI sceGnmBeginWorkload(SceWorkloadStream stream, uint64_
*workload = ++count;
return SceWorkloadStatus::StatusOk;
}
SceWorkloadStatus SYSV_ABI sceGnmEndWorkload(uint64_t workload) {
return SceWorkloadStatus::StatusOk;
}
SceWorkloadStatus SYSV_ABI sceGnmDestroyWorkloadStream(SceWorkloadStream workloadStream) {
return SceWorkloadStatus::StatusOk;
}

View File

@ -1,7 +1,7 @@
#include "common.h"
#include "core/kernel/eventqueue_types.h"
#include "types.h"
#include <eventqueue_types.h>
#include <logging.h>
#include <videoOut.h>
@ -20,7 +20,7 @@ void event_reset_func(Kernel::EventQueue::KernelEqueueEvent* event) {
event->event.data = 0;
}
void event_delete_func(Kernel::EventQueue::KernelEqueue* eq, Kernel::EventQueue::KernelEqueueEvent* event) {
void event_delete_func(Kernel::EventQueue::IKernelEqueue_t eq, Kernel::EventQueue::KernelEqueueEvent* event) {
accessVideoOut().removeEvent(event->event.fflags, eq, event->event.ident);
}
} // namespace
@ -32,10 +32,12 @@ EXPORT const char* MODULE_NAME = "libSceVideoOut";
EXPORT SYSV_ABI int32_t sceVideoOutOpen(int32_t userId, int32_t type, int32_t index, const void* param) {
return accessVideoOut().open(userId);
}
EXPORT SYSV_ABI int32_t sceVideoOutClose(int32_t handle) {
accessVideoOut().close(handle);
return Ok;
}
EXPORT SYSV_ABI int32_t sceVideoOutRegisterBuffers(int32_t handle, int32_t startIndex, void* const* addresses, int32_t bufferNum,
const SceVideoOutBufferAttribute* attribute) {
[[unlikely]] if (addresses == nullptr) { return Err::VIDEO_OUT_ERROR_INVALID_ADDRESS; }
@ -48,23 +50,29 @@ EXPORT SYSV_ABI int32_t sceVideoOutRegisterBuffers(int32_t handle, int32_t start
return accessVideoOut().registerBuffers(handle, startIndex, addresses, bufferNum, attribute);
}
EXPORT SYSV_ABI int32_t sceVideoOutRegisterStereoBuffers(int32_t handle, int32_t startIndex, const SceVideoOutStereoBuffers* buffers, int32_t length,
const SceVideoOutBufferAttribute* attribute) {
return Ok;
}
EXPORT SYSV_ABI int32_t sceVideoOutUnregisterBuffers(int32_t handle, int32_t attributeIndex) {
return Ok;
}
EXPORT SYSV_ABI int32_t sceVideoOutSubmitChangeBufferAttribute(int32_t handle, int32_t index, const SceVideoOutBufferAttribute* attribute) {
return Ok;
}
EXPORT SYSV_ABI int32_t sceVideoOutSetFlipRate(int32_t handle, int32_t rate) {
accessVideoOut().setFliprate(handle, rate);
return Ok;
}
EXPORT SYSV_ABI int32_t sceVideoOutSetWindowModeMargins(int32_t handle, int top, int bottom) {
return Ok;
}
EXPORT SYSV_ABI int32_t sceVideoOutSubmitFlip(int32_t handle, int32_t bufferIndex, uint32_t flipMode, int64_t flipArg) {
if (bufferIndex < 0 || bufferIndex > 15) {
return Err::VIDEO_OUT_ERROR_INVALID_INDEX;
@ -72,23 +80,28 @@ EXPORT SYSV_ABI int32_t sceVideoOutSubmitFlip(int32_t handle, int32_t bufferInde
accessVideoOut().submitFlip(handle, bufferIndex, flipArg);
return Ok;
}
EXPORT SYSV_ABI int32_t sceVideoOutGetFlipStatus(int32_t handle, SceVideoOutFlipStatus* status) {
accessVideoOut().getFlipStatus(handle, status);
return Ok;
}
EXPORT SYSV_ABI int32_t sceVideoOutGetVblankStatus(int32_t handle, SceVideoOutVblankStatus* status) {
accessVideoOut().getVBlankStatus(handle, status);
return Ok;
}
EXPORT SYSV_ABI int32_t sceVideoOutIsFlipPending(int32_t handle) {
return accessVideoOut().getPendingFlips(handle);
;
}
EXPORT SYSV_ABI int32_t sceVideoOutGetResolutionStatus(int32_t handle, SceVideoOutResolutionStatus* status) {
accessVideoOut().getResolution(handle, status);
return Ok;
}
EXPORT SYSV_ABI int32_t sceVideoOutAddFlipEvent(Kernel::EventQueue::KernelEqueue* eq, int32_t handle, void* udata) {
EXPORT SYSV_ABI int32_t sceVideoOutAddFlipEvent(Kernel::EventQueue::IKernelEqueue_t eq, int32_t handle, void* udata) {
[[unlikely]] if (eq == nullptr) { return Err::VIDEO_OUT_ERROR_INVALID_EVENT_QUEUE; }
Kernel::EventQueue::KernelEqueueEvent const event = {.triggered = false,
@ -111,7 +124,8 @@ EXPORT SYSV_ABI int32_t sceVideoOutAddFlipEvent(Kernel::EventQueue::KernelEqueue
return accessVideoOut().addEvent(handle, event, eq);
}
EXPORT SYSV_ABI int32_t sceVideoOutAddVblankEvent(Kernel::EventQueue::KernelEqueue* eq, int32_t handle, void* udata) {
EXPORT SYSV_ABI int32_t sceVideoOutAddVblankEvent(Kernel::EventQueue::IKernelEqueue_t eq, int32_t handle, void* udata) {
[[unlikely]] if (eq == nullptr) { return Err::VIDEO_OUT_ERROR_INVALID_EVENT_QUEUE; }
Kernel::EventQueue::KernelEqueueEvent const event = {.triggered = false,
@ -134,61 +148,80 @@ EXPORT SYSV_ABI int32_t sceVideoOutAddVblankEvent(Kernel::EventQueue::KernelEque
return accessVideoOut().addEvent(handle, event, eq);
}
EXPORT SYSV_ABI int32_t sceVideoOutDeleteFlipEvent(Kernel::EventQueue::KernelEqueue* eq, int32_t handle) {
EXPORT SYSV_ABI int32_t sceVideoOutDeleteFlipEvent(Kernel::EventQueue::IKernelEqueue_t eq, int32_t handle) {
return Ok;
}
EXPORT SYSV_ABI int32_t sceVideoOutDeleteVblankEvent(Kernel::EventQueue::KernelEqueue* eq, int32_t handle) {
EXPORT SYSV_ABI int32_t sceVideoOutDeleteVblankEvent(Kernel::EventQueue::IKernelEqueue_t eq, int32_t handle) {
return Ok;
}
EXPORT SYSV_ABI int32_t sceVideoOutAddPreVblankStartEvent(Kernel::EventQueue::KernelEqueue* eq, int32_t handle, void* udata) {
EXPORT SYSV_ABI int32_t sceVideoOutAddPreVblankStartEvent(Kernel::EventQueue::IKernelEqueue_t eq, int32_t handle, void* udata) {
return Ok;
}
EXPORT SYSV_ABI int32_t sceVideoOutDeletePreVblankStartEvent(Kernel::EventQueue::KernelEqueue* eq, int32_t handle) {
EXPORT SYSV_ABI int32_t sceVideoOutDeletePreVblankStartEvent(Kernel::EventQueue::IKernelEqueue_t eq, int32_t handle) {
return Ok;
}
EXPORT SYSV_ABI int32_t sceVideoOutGetEventId(const void* ev) {
return Ok;
}
EXPORT SYSV_ABI int32_t sceVideoOutGetEventData(const void* ev, int64_t* data) {
return Ok;
}
EXPORT SYSV_ABI int32_t sceVideoOutGetEventCount(const void* ev) {
return Ok;
}
EXPORT SYSV_ABI int32_t sceVideoOutWaitVblank(int32_t handle) {
return Ok;
}
EXPORT SYSV_ABI void sceVideoOutSetBufferAttribute(SceVideoOutBufferAttribute* attribute, uint32_t pixelFormat, uint32_t tilingMode, uint32_t aspectRatio,
uint32_t width, uint32_t height, uint32_t pitchInPixel) {
accessVideoOut().getBufferAttribute(attribute, pixelFormat, tilingMode, aspectRatio, width, height, pitchInPixel);
}
EXPORT SYSV_ABI int32_t sceVideoOutColorSettingsSetGamma_(SceVideoOutColorSettings* p, float gamma, uint32_t sizeOfSettings) {
return Ok;
}
EXPORT SYSV_ABI int32_t sceVideoOutAdjustColor_(int32_t handle, SceVideoOutColorSettings* pSettings, uint32_t sizeOfSettings) {
return Ok;
}
EXPORT SYSV_ABI int32_t sceVideoOutCursorEnable(int32_t handle, int32_t index, const void* address) {
return Ok;
}
EXPORT SYSV_ABI int32_t sceVideoOutCursorDisable(int32_t handle, int32_t index) {
return Ok;
}
EXPORT SYSV_ABI int32_t sceVideoOutCursorSetHotSpot(int32_t handle, int32_t index, uint32_t hotX, uint32_t hotY) {
return Ok;
}
EXPORT SYSV_ABI int32_t sceVideoOutCursorSetPosition(int32_t handle, int32_t index, uint32_t posX, uint32_t posY) {
return Ok;
}
EXPORT SYSV_ABI int32_t sceVideoOutCursorSetPositionStereo(int32_t handle, int32_t index, uint32_t posX, uint32_t posY, uint32_t offset) {
return Ok;
}
EXPORT SYSV_ABI int32_t sceVideoOutCursorSet2xMagnify(int32_t handle, int32_t index, uint32_t enable) {
return Ok;
}
EXPORT SYSV_ABI int32_t sceVideoOutCursorSetImageAddress(int32_t handle, int32_t index, const void* address) {
return Ok;
}
EXPORT SYSV_ABI int32_t sceVideoOutCursorIsUpdatePending(int32_t handle, int32_t index, SceVideoOutCursorPendingType type) {
return Ok;
}
@ -197,7 +230,9 @@ EXPORT SYSV_ABI int32_t sceVideoOutGetDeviceCapabilityInfo_(int32_t handle, SceV
pInfo->capability = 0;
return Ok;
}
EXPORT SYSV_ABI void sceVideoOutModeSetAny_(SceVideoOutMode* pMode, uint32_t sizeOfMode) {}
EXPORT SYSV_ABI void sceVideoOutConfigureOptionsInitialize_(SceVideoOutConfigureOptions* pOptions, uint32_t sizeOfOptions) {}
EXPORT SYSV_ABI int32_t sceVideoOutConfigureOutputMode_(int32_t handle, uint32_t reserved, SceVideoOutMode const* pMode,

View File

@ -0,0 +1,11 @@
cmake_minimum_required(VERSION 3.24)
include(../setupModule.cmake)
set(libName libkernel)
project(${libName})
add_library(${libName} SHARED entry.cpp equeue.cpp)
target_link_libraries(${libName} PRIVATE emulator.lib)
setupModule(${libName})

View File

@ -0,0 +1,4 @@
#pragma once
#include <stdint.h>
namespace Err {} // namespace Err

View File

@ -0,0 +1,13 @@
#include "common.h"
#include "types.h"
#include <logging.h>
LOG_DEFINE_MODULE(libkernel);
namespace {} // namespace
extern "C" {
EXPORT const char* MODULE_NAME = "libkernel";
}

View File

@ -0,0 +1,124 @@
#include "common.h"
#include "core/kernel/eventqueue.h"
#include "types.h"
#include <logging.h>
LOG_DEFINE_MODULE(equeue);
extern "C" {
EXPORT SYSV_ABI int sceKernelGetEventFilter(const Kernel::EventQueue::KernelEvent_t ev) {
return ev->filter;
}
EXPORT SYSV_ABI uintptr_t sceKernelGetEventId(const Kernel::EventQueue::KernelEvent_t ev) {
return ev->ident;
}
EXPORT SYSV_ABI intptr_t sceKernelGetEventData(const Kernel::EventQueue::KernelEvent_t ev) {
return ev->data;
}
EXPORT SYSV_ABI unsigned int sceKernelGetEventFflags(const Kernel::EventQueue::KernelEvent_t ev) {
return ev->fflags;
}
EXPORT SYSV_ABI int sceKernelGetEventError(const Kernel::EventQueue::KernelEvent_t ev) {
LOG_USE_MODULE(equeue);
LOG_ERR(L"todo %S", __FUNCTION__);
return Ok;
}
EXPORT SYSV_ABI void* sceKernelGetEventUserData(const Kernel::EventQueue::KernelEvent_t ev) {
return ev->udata;
}
EXPORT SYSV_ABI int sceKernelCreateEqueue(Kernel::EventQueue::IKernelEqueue_t* eq, const char* name) {
return Kernel::EventQueue::createEqueue(eq, name);
}
EXPORT SYSV_ABI int sceKernelDeleteEqueue(Kernel::EventQueue::IKernelEqueue_t eq) {
return Kernel::EventQueue::deleteEqueue(eq);
}
EXPORT SYSV_ABI int sceKernelWaitEqueue(Kernel::EventQueue::IKernelEqueue_t eq, Kernel::EventQueue::KernelEvent_t ev, int num, int* out,
SceKernelUseconds* time) {
return eq->wait(ev, num, out, time);
}
EXPORT SYSV_ABI int sceKernelAddTimerEvent(Kernel::EventQueue::IKernelEqueue_t eq, int id, SceKernelUseconds usec, void* udata) {
LOG_USE_MODULE(equeue);
LOG_ERR(L"todo %S", __FUNCTION__);
return Ok;
}
EXPORT SYSV_ABI int sceKernelDeleteTimerEvent(Kernel::EventQueue::IKernelEqueue_t eq, int id) {
LOG_USE_MODULE(equeue);
LOG_ERR(L"todo %S", __FUNCTION__);
return Ok;
}
EXPORT SYSV_ABI int sceKernelAddReadEvent(Kernel::EventQueue::IKernelEqueue_t eq, int fd, size_t size, void* udata) {
LOG_USE_MODULE(equeue);
LOG_ERR(L"todo %S", __FUNCTION__);
return Ok;
}
EXPORT SYSV_ABI int sceKernelDeleteReadEvent(Kernel::EventQueue::IKernelEqueue_t eq, int fd) {
LOG_USE_MODULE(equeue);
LOG_ERR(L"todo %S", __FUNCTION__);
return Ok;
}
EXPORT SYSV_ABI int sceKernelAddWriteEvent(Kernel::EventQueue::IKernelEqueue_t eq, int fd, size_t size, void* udata) {
LOG_USE_MODULE(equeue);
LOG_ERR(L"todo %S", __FUNCTION__);
return Ok;
}
EXPORT SYSV_ABI int sceKernelDeleteWriteEvent(Kernel::EventQueue::IKernelEqueue_t eq, int fd) {
LOG_USE_MODULE(equeue);
LOG_ERR(L"todo %S", __FUNCTION__);
return Ok;
}
EXPORT SYSV_ABI int sceKernelAddFileEvent(Kernel::EventQueue::IKernelEqueue_t eq, int fd, int watch, void* udata) {
LOG_USE_MODULE(equeue);
LOG_ERR(L"todo %S", __FUNCTION__);
return Ok;
}
EXPORT SYSV_ABI int sceKernelDeleteFileEvent(Kernel::EventQueue::IKernelEqueue_t eq, int fd) {
LOG_USE_MODULE(equeue);
LOG_ERR(L"todo %S", __FUNCTION__);
return Ok;
}
EXPORT SYSV_ABI int sceKernelAddUserEvent(Kernel::EventQueue::IKernelEqueue_t eq, int id) {
return eq->addUserEvent(id);
}
EXPORT SYSV_ABI int sceKernelAddUserEventEdge(Kernel::EventQueue::IKernelEqueue_t eq, int id) {
return eq->addUserEventEdge(id);
}
EXPORT SYSV_ABI int sceKernelDeleteUserEvent(Kernel::EventQueue::IKernelEqueue_t eq, int id) {
return eq->deleteEvent(id, Kernel::EventQueue::KERNEL_EVFILT_USER);
}
EXPORT SYSV_ABI int sceKernelTriggerUserEvent(Kernel::EventQueue::IKernelEqueue_t eq, int id, void* udata) {
return eq->triggerUserEvent(id, udata);
}
EXPORT SYSV_ABI int sceKernelAddHRTimerEvent(Kernel::EventQueue::IKernelEqueue_t eq, int id, SceKernelTimespec* ts, void* udata) {
LOG_USE_MODULE(equeue);
LOG_ERR(L"todo %S", __FUNCTION__);
return Ok;
}
EXPORT SYSV_ABI int sceKernelDeleteHRTimerEvent(Kernel::EventQueue::IKernelEqueue_t eq, int id) {
LOG_USE_MODULE(equeue);
LOG_ERR(L"todo %S", __FUNCTION__);
return Ok;
}
}

View File

@ -0,0 +1,2 @@
#pragma once
#include "codes.h"

View File

@ -9,6 +9,7 @@ install(DIRECTORY "ffmpeg/bin/" DESTINATION "${CMAKE_BINARY_DIR}/bin"
FILES_MATCHING PATTERN "*.dll"
)
# Boost
set(BOOST_INCLUDE_LIBRARIES "program_options;date_time;interprocess;stacktrace;uuid;beast;signals2;thread")
ExternalProject_Add(boost_thirdParty
SOURCE_DIR ${CMAKE_CURRENT_SOURCE_DIR}/boost
@ -23,4 +24,25 @@ ExternalProject_Add(boost_thirdParty
-DBUILD_TESTING=OFF
-DBOOST_INSTALL_LAYOUT=system
CMAKE_CACHE_ARGS -DBOOST_INCLUDE_LIBRARIES:STRING=${BOOST_INCLUDE_LIBRARIES}
)
# Optick
add_library(Optick STATIC)
target_sources(Optick
PRIVATE
optick/src/optick_capi.cpp
optick/src/optick_core.cpp
optick/src/optick_gpu.cpp
optick/src/optick_gpu.d3d12.cpp
optick/src/optick_gpu.vulkan.cpp
optick/src/optick_message.cpp
optick/src/optick_miniz.cpp
optick/src/optick_serialization.cpp
optick/src/optick_server.cpp
)
set_target_properties(Optick
PROPERTIES
ARCHIVE_OUTPUT_DIRECTORY "${CMAKE_BINARY_DIR}/install/bin"
LIBRARY_OUTPUT_DIRECTORY "${CMAKE_BINARY_DIR}/install/lib"
)