mirror of
https://github.com/SysRay/psOff_public.git
synced 2024-11-27 00:20:54 +00:00
+ eventqueue, libkernel
This commit is contained in:
parent
6ca4243524
commit
2970417f87
@ -67,6 +67,7 @@ Cpp11BracedListStyle: true
|
||||
DeriveLineEnding: true
|
||||
DerivePointerAlignment: false
|
||||
DisableFormat: false
|
||||
SeparateDefinitionBlocks: Always
|
||||
EmptyLineBeforeAccessModifier: LogicalBlock
|
||||
ExperimentalAutoDetectBinPacking: false
|
||||
FixNamespaceComments: true
|
||||
|
2
.vscode/settings.json
vendored
2
.vscode/settings.json
vendored
@ -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"]
|
||||
}
|
@ -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
7
core/CMakeLists.txt
Normal file
@ -0,0 +1,7 @@
|
||||
#add_subdirectory(kernel)
|
||||
|
||||
# add_subdirectory(videoout)
|
||||
# add_library(core SHARED
|
||||
#$<TARGET_OBJECTS:kernel>
|
||||
# $<TARGET_OBJECTS:videoout>
|
||||
#)
|
3
core/kernel/CMakeLists.txt
Normal file
3
core/kernel/CMakeLists.txt
Normal file
@ -0,0 +1,3 @@
|
||||
add_library(kernel OBJECT
|
||||
eventqueue.cpp
|
||||
)
|
298
core/kernel/eventqueue.cpp
Normal file
298
core/kernel/eventqueue.cpp
Normal 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
43
core/kernel/eventqueue.h
Normal 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
|
55
core/kernel/eventqueue_types.h
Normal file
55
core/kernel/eventqueue_types.h
Normal 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
|
10
core/videoout/CMakeLists.txt
Normal file
10
core/videoout/CMakeLists.txt
Normal 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
118
core/videoout/graphics.h
Normal 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
641
core/videoout/videoout.cpp
Normal 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
170
core/videoout/videoout.h
Normal 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
|
361
core/videoout/vulkanHelper.cpp
Normal file
361
core/videoout/vulkanHelper.cpp
Normal 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);
|
||||
}
|
19
core/videoout/vulkanHelper.h
Normal file
19
core/videoout/vulkanHelper.h
Normal 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
|
741
core/videoout/vulkanSetup.cpp
Normal file
741
core/videoout/vulkanSetup.cpp
Normal 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
|
95
core/videoout/vulkanSetup.h
Normal file
95
core/videoout/vulkanSetup.h
Normal 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
|
@ -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);
|
||||
|
@ -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;
|
||||
}
|
||||
|
@ -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;
|
||||
}
|
||||
|
@ -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,
|
||||
|
11
modules/libkernel/CMakeLists.txt
Normal file
11
modules/libkernel/CMakeLists.txt
Normal 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})
|
4
modules/libkernel/codes.h
Normal file
4
modules/libkernel/codes.h
Normal file
@ -0,0 +1,4 @@
|
||||
#pragma once
|
||||
#include <stdint.h>
|
||||
|
||||
namespace Err {} // namespace Err
|
13
modules/libkernel/entry.cpp
Normal file
13
modules/libkernel/entry.cpp
Normal 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";
|
||||
}
|
124
modules/libkernel/equeue.cpp
Normal file
124
modules/libkernel/equeue.cpp
Normal 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;
|
||||
}
|
||||
}
|
2
modules/libkernel/types.h
Normal file
2
modules/libkernel/types.h
Normal file
@ -0,0 +1,2 @@
|
||||
#pragma once
|
||||
#include "codes.h"
|
22
third_party/CMakeLists.txt
vendored
22
third_party/CMakeLists.txt
vendored
@ -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"
|
||||
)
|
Loading…
Reference in New Issue
Block a user