Refactor graphics #1

This commit is contained in:
nmzik
2026-07-19 00:54:31 +02:00
parent 7905f2d12f
commit cbc11d2eaf
71 changed files with 2051 additions and 3399 deletions
+255 -504
View File
@@ -57,6 +57,15 @@ constexpr int VIDEO_OUT_BUFFER_ATTRIBUTE_CATEGORY_UNCOMPRESSED = 0;
constexpr int VIDEO_OUT_BUFFER_ATTRIBUTE_CATEGORY_COMPRESSED = 1;
constexpr uint64_t VIDEO_OUT_BUFFER_ATTRIBUTE_OPTION_STRICT_COLORIMETRY = 8;
enum class VideoOutEventKind : uintptr_t {
Flip = VIDEO_OUT_EVENT_FLIP,
Vblank = VIDEO_OUT_EVENT_VBLANK,
PreVblankStart = VIDEO_OUT_EVENT_PRE_VBLANK_START,
OutputMode = VIDEO_OUT_EVENT_SET_MODE,
};
enum class FlipRequestSource { Cpu, GpuEop };
struct VideoOutBufferAttribute2 {
uint32_t reserved0;
uint32_t tiling_mode;
@@ -160,10 +169,10 @@ struct VideoOutConfig {
class FlipQueue {
public:
FlipQueue() { EXIT_NOT_IMPLEMENTED(!Common::Thread::IsMainThread()); }
virtual ~FlipQueue() { KYTY_NOT_IMPLEMENTED; }
~FlipQueue() { KYTY_NOT_IMPLEMENTED; }
KYTY_CLASS_NO_COPY(FlipQueue);
bool Reserve(VideoOutConfig* cfg, int index, int64_t flip_arg, bool gpu_eop,
bool Reserve(VideoOutConfig* cfg, int index, int64_t flip_arg, FlipRequestSource source,
uint64_t* request_id);
void Prepare(uint64_t request_id, Graphics::CommandBuffer* buffer);
uint64_t PrepareNextCpu(Graphics::CommandBuffer* buffer);
@@ -183,7 +192,7 @@ private:
int index;
int64_t flip_arg;
uint64_t submit_ptc;
bool gpu_eop;
FlipRequestSource source;
RequestState state;
Graphics::PreparedFrame* frame;
};
@@ -203,7 +212,7 @@ public:
static constexpr int VIDEO_OUT_NUM_MAX = 2;
VideoOutContext() { EXIT_NOT_IMPLEMENTED(!Common::Thread::IsMainThread()); }
virtual ~VideoOutContext() { KYTY_NOT_IMPLEMENTED; }
~VideoOutContext() { KYTY_NOT_IMPLEMENTED; }
KYTY_CLASS_NO_COPY(VideoOutContext);
@@ -240,6 +249,167 @@ private:
static VideoOutContext* g_video_out_context = nullptr;
using VideoOutEventQueues = std::vector<EventQueue::KernelEqueue>;
static uintptr_t VideoOutEventId(VideoOutEventKind kind) {
return static_cast<uintptr_t>(kind);
}
static VideoOutEventKind GetVideoOutEventKind(uintptr_t event_id) {
switch (event_id) {
case VIDEO_OUT_EVENT_FLIP: return VideoOutEventKind::Flip;
case VIDEO_OUT_EVENT_VBLANK: return VideoOutEventKind::Vblank;
case VIDEO_OUT_EVENT_PRE_VBLANK_START: return VideoOutEventKind::PreVblankStart;
case VIDEO_OUT_EVENT_SET_MODE: return VideoOutEventKind::OutputMode;
default: EXIT("unsupported video-out event id=%" PRIuPTR "\n", event_id);
}
return VideoOutEventKind::Flip;
}
static VideoOutEventQueues& VideoOutEventQueuesFor(VideoOutConfig& video_out,
VideoOutEventKind kind) {
switch (kind) {
case VideoOutEventKind::Flip: return video_out.flip_eqs;
case VideoOutEventKind::Vblank: return video_out.vblank_eqs;
case VideoOutEventKind::PreVblankStart: return video_out.pre_vblank_eqs;
case VideoOutEventKind::OutputMode: return video_out.output_mode_eqs;
}
EXIT("unsupported video-out event kind\n");
return video_out.flip_eqs;
}
static intptr_t MakeVideoOutEventData(intptr_t current_data, void* trigger_data) {
const uint64_t old_data = static_cast<uint64_t>(current_data);
uint64_t counter = (old_data >> 12u) & 0xfu;
if (counter != 0xfu) {
counter++;
}
const uint64_t time = LibKernel::KernelReadTsc() & 0xfffu;
const uint64_t payload = static_cast<uint64_t>(reinterpret_cast<intptr_t>(trigger_data));
return static_cast<intptr_t>(time | (counter << 12u) |
((payload & 0x0000ffffffffffffULL) << 16u));
}
static void ResetVideoOutEvent(EventQueue::KernelEqueueEvent* event) {
EXIT_IF(event == nullptr);
event->triggered = false;
event->event.fflags = 0;
event->event.data = 0;
}
static void TriggerVideoOutEvent(EventQueue::KernelEqueueEvent* event, void* trigger_data) {
EXIT_IF(event == nullptr);
auto triggered_event = event->event;
triggered_event.fflags =
triggered_event.fflags < 0xfu ? triggered_event.fflags + 1u : triggered_event.fflags;
triggered_event.data = MakeVideoOutEventData(triggered_event.data, trigger_data);
if (event->triggered) {
event->pending_events.push_back(triggered_event);
return;
}
event->event = triggered_event;
event->triggered = true;
}
static void RemoveVideoOutEventQueue(EventQueue::KernelEqueue eq,
EventQueue::KernelEqueueEvent* event) {
EXIT_IF(event == nullptr);
EXIT_IF(event->filter.data == nullptr);
EXIT_NOT_IMPLEMENTED(event->event.filter != EventQueue::KERNEL_EVFILT_VIDEO_OUT);
auto* video_out = static_cast<VideoOutConfig*>(event->filter.data);
auto& queues = VideoOutEventQueuesFor(*video_out, GetVideoOutEventKind(event->event.ident));
Common::LockGuard lock(video_out->mutex);
EXIT_IF(queues.empty());
const auto entry = std::find(queues.begin(), queues.end(), eq);
EXIT_NOT_IMPLEMENTED(entry == queues.end());
*entry = nullptr;
}
static void TriggerVideoOutEventsLocked(const VideoOutEventQueues& queues, VideoOutEventKind kind,
void* trigger_data) {
for (auto eq: queues) {
if (eq == nullptr) {
continue;
}
const auto result = EventQueue::KernelTriggerEvent(
eq, VideoOutEventId(kind), EventQueue::KERNEL_EVFILT_VIDEO_OUT, trigger_data);
EXIT_NOT_IMPLEMENTED(result != OK);
}
}
static void DeleteVideoOutEventsLocked(VideoOutEventQueues& queues, VideoOutEventKind kind) {
for (auto eq: queues) {
if (eq == nullptr) {
continue;
}
const auto result = EventQueue::KernelDeleteEvent(eq, VideoOutEventId(kind),
EventQueue::KERNEL_EVFILT_VIDEO_OUT);
EXIT_NOT_IMPLEMENTED(result != OK);
}
queues.clear();
}
static int RegisterVideoOutEvent(int handle, EventQueue::KernelEqueue eq, VideoOutEventKind kind,
void* udata) {
EXIT_IF(g_video_out_context == nullptr);
auto* video_out = g_video_out_context->Get(handle);
if (video_out == nullptr) {
return VIDEO_OUT_ERROR_INVALID_HANDLE;
}
Common::LockGuard lock(video_out->mutex);
if (kind == VideoOutEventKind::OutputMode) {
LOGF("\t eq = 0x%016" PRIx64 "\n"
"\t handle = %d\n"
"\t udata = 0x%016" PRIx64 "\n",
reinterpret_cast<uint64_t>(eq), handle, reinterpret_cast<uint64_t>(udata));
}
if (eq == nullptr) {
return VIDEO_OUT_ERROR_INVALID_EVENT_QUEUE;
}
auto& queues = VideoOutEventQueuesFor(*video_out, kind);
const bool add_queue = std::find(queues.begin(), queues.end(), eq) == queues.end();
const bool initially_triggered = kind == VideoOutEventKind::OutputMode;
void* const initial_trigger_data =
initially_triggered ? reinterpret_cast<void*>(video_out->output_mode) : nullptr;
EventQueue::KernelEqueueEvent event {};
event.triggered = initially_triggered;
event.event.ident = VideoOutEventId(kind);
event.event.filter = EventQueue::KERNEL_EVFILT_VIDEO_OUT;
event.event.udata = udata;
event.event.fflags = initially_triggered ? 1u : 0u;
event.event.data = initially_triggered ? MakeVideoOutEventData(0, initial_trigger_data) : 0;
event.filter.delete_event_func = RemoveVideoOutEventQueue;
event.filter.reset_func = ResetVideoOutEvent;
event.filter.trigger_func = TriggerVideoOutEvent;
event.filter.data = video_out;
const int result = EventQueue::KernelAddEvent(eq, event);
if (result == OK && add_queue) {
queues.push_back(eq);
}
return result;
}
static int DeleteVideoOutEvent(int handle, EventQueue::KernelEqueue eq, VideoOutEventKind kind) {
EXIT_IF(g_video_out_context == nullptr);
if (g_video_out_context->Get(handle) == nullptr) {
return VIDEO_OUT_ERROR_INVALID_HANDLE;
}
if (eq == nullptr) {
return VIDEO_OUT_ERROR_INVALID_EVENT_QUEUE;
}
return EventQueue::KernelDeleteEvent(eq, VideoOutEventId(kind),
EventQueue::KERNEL_EVFILT_VIDEO_OUT);
}
static void WaitForNextVblank() {
static uint64_t next_vblank_ticks = 0;
@@ -286,6 +456,38 @@ static bool IsSpecialBufferIndex(int index) {
return index == VIDEO_OUT_BUFFER_INDEX_BLANK || index == VIDEO_OUT_BUFFER_INDEX_BLACK;
}
static bool IsValidFlipMode(int mode) {
return mode >= VIDEO_OUT_FLIP_MODE_VSYNC && mode <= VIDEO_OUT_FLIP_MODE_VSYNC_MULTI;
}
static int ReserveFlipRequest(int handle, int index, int flip_mode, int64_t flip_arg,
FlipRequestSource source, uint64_t* request_id) {
EXIT_IF(g_video_out_context == nullptr || request_id == nullptr);
auto* video_out = g_video_out_context->Get(handle);
if (video_out == nullptr) {
return VIDEO_OUT_ERROR_INVALID_HANDLE;
}
if (!IsValidFlipMode(flip_mode)) {
return VIDEO_OUT_ERROR_INVALID_VALUE;
}
if (!IsValidBufferIndex(index)) {
return VIDEO_OUT_ERROR_INVALID_INDEX;
}
Common::LockGuard lock(video_out->mutex);
if (video_out->closing ||
(!IsSpecialBufferIndex(index) &&
(video_out->unregistering[index] || video_out->buffers[index].buffer_vulkan == nullptr))) {
return VIDEO_OUT_ERROR_INVALID_INDEX;
}
if (!g_video_out_context->GetFlipQueue().Reserve(video_out, index, flip_arg, source,
request_id)) {
return VIDEO_OUT_ERROR_FLIP_QUEUE_FULL;
}
return OK;
}
static Graphics::VideoOutInfo MakeVideoOutInfo(const VideoOutBufferAttribute2& attribute,
uint64_t address, uint64_t metadata_address,
Graphics::VideoOutCompression compression) {
@@ -396,54 +598,27 @@ void VideoOutContext::Close(int handle) {
EXIT_NOT_IMPLEMENTED(handle >= VIDEO_OUT_NUM_MAX);
EXIT_NOT_IMPLEMENTED(!m_video_out_ctx[handle].opened);
m_video_out_ctx[handle].opened = false;
auto& config = m_video_out_ctx[handle];
config.opened = false;
m_video_out_ctx[handle].mutex.Lock();
if (m_video_out_ctx[handle].closing) {
config.mutex.Lock();
if (config.closing) {
EXIT("video-out handle is already closing\n");
}
m_video_out_ctx[handle].closing = true;
if (m_flip_queue.HasPending(&m_video_out_ctx[handle], VIDEO_OUT_BUFFER_INDEX_BLACK,
config.closing = true;
if (m_flip_queue.HasPending(&config, VIDEO_OUT_BUFFER_INDEX_BLACK,
VIDEO_OUT_BUFFER_NUM_MAX - VIDEO_OUT_BUFFER_INDEX_BLACK)) {
EXIT("cannot close video-out handle with pending flips\n");
}
for (auto& flip_eq: m_video_out_ctx[handle].flip_eqs) {
if (flip_eq != nullptr) {
auto result = EventQueue::KernelDeleteEvent(flip_eq, VIDEO_OUT_EVENT_FLIP,
EventQueue::KERNEL_EVFILT_VIDEO_OUT);
EXIT_NOT_IMPLEMENTED(result != OK);
}
}
m_video_out_ctx[handle].flip_eqs.clear();
for (auto& vblank_eq: m_video_out_ctx[handle].pre_vblank_eqs) {
if (vblank_eq != nullptr) {
auto result = EventQueue::KernelDeleteEvent(vblank_eq, VIDEO_OUT_EVENT_PRE_VBLANK_START,
EventQueue::KERNEL_EVFILT_VIDEO_OUT);
EXIT_NOT_IMPLEMENTED(result != OK);
}
}
m_video_out_ctx[handle].pre_vblank_eqs.clear();
for (auto& vblank_eq: m_video_out_ctx[handle].vblank_eqs) {
if (vblank_eq != nullptr) {
auto result = EventQueue::KernelDeleteEvent(vblank_eq, VIDEO_OUT_EVENT_VBLANK,
EventQueue::KERNEL_EVFILT_VIDEO_OUT);
EXIT_NOT_IMPLEMENTED(result != OK);
}
}
m_video_out_ctx[handle].vblank_eqs.clear();
for (auto& output_mode_eq: m_video_out_ctx[handle].output_mode_eqs) {
if (output_mode_eq != nullptr) {
auto result = EventQueue::KernelDeleteEvent(output_mode_eq, VIDEO_OUT_EVENT_SET_MODE,
EventQueue::KERNEL_EVFILT_VIDEO_OUT);
EXIT_NOT_IMPLEMENTED(result != OK);
}
}
m_video_out_ctx[handle].output_mode_eqs.clear();
DeleteVideoOutEventsLocked(config.flip_eqs, VideoOutEventKind::Flip);
DeleteVideoOutEventsLocked(config.pre_vblank_eqs, VideoOutEventKind::PreVblankStart);
DeleteVideoOutEventsLocked(config.vblank_eqs, VideoOutEventKind::Vblank);
DeleteVideoOutEventsLocked(config.output_mode_eqs, VideoOutEventKind::OutputMode);
m_video_out_ctx[handle].flip_rate = 0;
config.flip_rate = 0;
std::vector<Graphics::VideoOutVulkanImage*> images;
for (const auto& buffer: m_video_out_ctx[handle].buffers) {
for (const auto& buffer: config.buffers) {
if ((buffer.buffer == nullptr) != (buffer.buffer_vulkan == nullptr) ||
(buffer.buffer_vulkan != nullptr &&
(buffer.buffer_size == 0 || buffer.buffer_pitch == 0))) {
@@ -459,21 +634,17 @@ void VideoOutContext::Close(int handle) {
}
Graphics::g_render_ctx->GetTextureCache()->UnregisterVideoOutSurfaces(images);
}
for (auto& buffer: m_video_out_ctx[handle].buffers) {
buffer.buffer = nullptr;
buffer.buffer_vulkan = nullptr;
buffer.buffer_size = 0;
buffer.buffer_pitch = 0;
buffer.set_id = 0;
for (auto& buffer: config.buffers) {
buffer = VideoOutBufferInfo {};
}
m_video_out_ctx[handle].buffers_sets.clear();
for (bool unregistering: m_video_out_ctx[handle].unregistering) {
config.buffers_sets.clear();
for (bool unregistering: config.unregistering) {
if (unregistering) {
EXIT("video-out close raced with buffer unregistration\n");
}
}
m_video_out_ctx[handle].mutex.Unlock();
config.mutex.Unlock();
}
VideoOutConfig* VideoOutContext::Get(int handle) {
@@ -503,15 +674,8 @@ void VideoOutContext::VblankBegin() {
ctx.pre_vblank_status.reserved = LibKernel::KernelReadTsc();
ctx.pre_vblank_status.processTimeCounter = LibKernel::KernelGetProcessTimeCounter();
for (auto& vblank_eq: ctx.pre_vblank_eqs) {
if (vblank_eq != nullptr) {
auto result = EventQueue::KernelTriggerEvent(
vblank_eq, VIDEO_OUT_EVENT_PRE_VBLANK_START,
EventQueue::KERNEL_EVFILT_VIDEO_OUT,
reinterpret_cast<void*>(ctx.pre_vblank_status.count));
EXIT_NOT_IMPLEMENTED(result != OK);
}
}
TriggerVideoOutEventsLocked(ctx.pre_vblank_eqs, VideoOutEventKind::PreVblankStart,
reinterpret_cast<void*>(ctx.pre_vblank_status.count));
ctx.mutex.Unlock();
}
}
@@ -529,14 +693,8 @@ void VideoOutContext::VblankEnd() {
ctx.vblank_status.reserved = LibKernel::KernelReadTsc();
ctx.vblank_status.processTimeCounter = LibKernel::KernelGetProcessTimeCounter();
for (auto& vblank_eq: ctx.vblank_eqs) {
if (vblank_eq != nullptr) {
auto result = EventQueue::KernelTriggerEvent(
vblank_eq, VIDEO_OUT_EVENT_VBLANK, EventQueue::KERNEL_EVFILT_VIDEO_OUT,
reinterpret_cast<void*>(ctx.vblank_status.count));
EXIT_NOT_IMPLEMENTED(result != OK);
}
}
TriggerVideoOutEventsLocked(ctx.vblank_eqs, VideoOutEventKind::Vblank,
reinterpret_cast<void*>(ctx.vblank_status.count));
ctx.mutex.Unlock();
}
}
@@ -574,7 +732,7 @@ Presentation::DisplayBufferImage VideoOutContext::FindImage(const void* buffer,
return ret;
}
bool FlipQueue::Reserve(VideoOutConfig* cfg, int index, int64_t flip_arg, bool gpu_eop,
bool FlipQueue::Reserve(VideoOutConfig* cfg, int index, int64_t flip_arg, FlipRequestSource source,
uint64_t* request_id) {
EXIT_IF(cfg == nullptr || request_id == nullptr);
Common::LockGuard lock(m_mutex);
@@ -582,7 +740,7 @@ bool FlipQueue::Reserve(VideoOutConfig* cfg, int index, int64_t flip_arg, bool g
if (m_requests.size() + m_cpu_requests.size() >= VIDEO_OUT_FLIP_QUEUE_CAPACITY) {
return false;
}
auto& pending = gpu_eop ? m_requests : m_cpu_requests;
auto& pending = source == FlipRequestSource::GpuEop ? m_requests : m_cpu_requests;
Request r {};
r.id = m_next_request_id++;
@@ -590,7 +748,7 @@ bool FlipQueue::Reserve(VideoOutConfig* cfg, int index, int64_t flip_arg, bool g
r.index = index;
r.flip_arg = flip_arg;
r.submit_ptc = LibKernel::KernelGetProcessTimeCounter();
r.gpu_eop = gpu_eop;
r.source = source;
r.state = RequestState::Reserved;
pending.push_back(r);
@@ -598,7 +756,7 @@ bool FlipQueue::Reserve(VideoOutConfig* cfg, int index, int64_t flip_arg, bool g
cfg->flip_status.flipPendingNum = static_cast<int>(m_requests.size() + m_cpu_requests.size());
cfg->flip_status.submitProcessTimeCounter = r.submit_ptc;
if (gpu_eop) {
if (source == FlipRequestSource::GpuEop) {
cfg->flip_status.gcQueueNum++;
}
@@ -782,7 +940,7 @@ bool FlipQueue::Flip(uint32_t micros) {
r.cfg->flip_status.flipArg = r.flip_arg;
r.cfg->flip_status.currentBuffer = r.index;
r.cfg->flip_status.flipPendingNum = static_cast<int>(m_requests.size() + m_cpu_requests.size());
if (r.gpu_eop && r.cfg->flip_status.gcQueueNum > 0) {
if (r.source == FlipRequestSource::GpuEop && r.cfg->flip_status.gcQueueNum > 0) {
r.cfg->flip_status.gcQueueNum--;
}
@@ -792,14 +950,8 @@ bool FlipQueue::Flip(uint32_t micros) {
m_mutex.Unlock();
r.cfg->mutex.Lock();
for (auto& flip_eq: r.cfg->flip_eqs) {
if (flip_eq != nullptr) {
auto result = EventQueue::KernelTriggerEvent(flip_eq, VIDEO_OUT_EVENT_FLIP,
EventQueue::KERNEL_EVFILT_VIDEO_OUT,
reinterpret_cast<void*>(r.flip_arg));
EXIT_NOT_IMPLEMENTED(result != OK);
}
}
TriggerVideoOutEventsLocked(r.cfg->flip_eqs, VideoOutEventKind::Flip,
reinterpret_cast<void*>(r.flip_arg));
r.cfg->mutex.Unlock();
if (Config::GraphicsDebugDumpEnabled() &&
@@ -926,398 +1078,42 @@ KYTY_SYSV_ABI int VideoOutSetFlipRate(int handle, int rate) {
return OK;
}
static void FlipEventResetFunc(LibKernel::EventQueue::KernelEqueueEvent* event) {
EXIT_IF(event == nullptr);
event->triggered = false;
event->event.fflags = 0;
event->event.data = 0;
}
static void FlipEventDeleteFunc(EventQueue::KernelEqueue eq,
LibKernel::EventQueue::KernelEqueueEvent* event) {
EXIT_IF(event == nullptr);
EXIT_IF(event->filter.data == nullptr);
EXIT_NOT_IMPLEMENTED(event->event.ident != VIDEO_OUT_EVENT_FLIP);
EXIT_NOT_IMPLEMENTED(event->event.filter != EventQueue::KERNEL_EVFILT_VIDEO_OUT);
if (event->filter.data != nullptr) {
auto* video_out = static_cast<VideoOutConfig*>(event->filter.data);
video_out->mutex.Lock();
EXIT_IF(video_out->flip_eqs.empty());
auto it = std::find(video_out->flip_eqs.begin(), video_out->flip_eqs.end(), eq);
EXIT_NOT_IMPLEMENTED(it == video_out->flip_eqs.end());
*it = nullptr;
video_out->mutex.Unlock();
}
}
static intptr_t MakeVideoOutEventData(intptr_t current_data, void* trigger_data) {
const uint64_t old_data = static_cast<uint64_t>(current_data);
uint64_t counter = (old_data >> 12u) & 0xfu;
if (counter != 0xfu) {
counter++;
}
const uint64_t time = LibKernel::KernelReadTsc() & 0xfffu;
const uint64_t payload = static_cast<uint64_t>(reinterpret_cast<intptr_t>(trigger_data));
return static_cast<intptr_t>(time | (counter << 12u) |
((payload & 0x0000ffffffffffffULL) << 16u));
}
static void FlipEventTriggerFunc(LibKernel::EventQueue::KernelEqueueEvent* event,
void* trigger_data) {
EXIT_IF(event == nullptr);
auto triggered_event = event->event;
triggered_event.fflags =
((triggered_event.fflags < 0xfu) ? triggered_event.fflags + 1u : triggered_event.fflags);
triggered_event.data = MakeVideoOutEventData(triggered_event.data, trigger_data);
if (event->triggered) {
event->pending_events.push_back(triggered_event);
} else {
event->event = triggered_event;
event->triggered = true;
}
}
static void VblankEventResetFunc(LibKernel::EventQueue::KernelEqueueEvent* event) {
EXIT_IF(event == nullptr);
event->triggered = false;
event->event.fflags = 0;
event->event.data = 0;
}
static void VblankEventDeleteFunc(EventQueue::KernelEqueue eq,
LibKernel::EventQueue::KernelEqueueEvent* event) {
EXIT_IF(event == nullptr);
EXIT_IF(event->filter.data == nullptr);
EXIT_NOT_IMPLEMENTED(event->event.ident != VIDEO_OUT_EVENT_VBLANK);
EXIT_NOT_IMPLEMENTED(event->event.filter != EventQueue::KERNEL_EVFILT_VIDEO_OUT);
if (event->filter.data != nullptr) {
auto* video_out = static_cast<VideoOutConfig*>(event->filter.data);
video_out->mutex.Lock();
EXIT_IF(video_out->vblank_eqs.empty());
auto it = std::find(video_out->vblank_eqs.begin(), video_out->vblank_eqs.end(), eq);
EXIT_NOT_IMPLEMENTED(it == video_out->vblank_eqs.end());
*it = nullptr;
video_out->mutex.Unlock();
}
}
static void PreVblankEventDeleteFunc(EventQueue::KernelEqueue eq,
LibKernel::EventQueue::KernelEqueueEvent* event) {
EXIT_IF(event == nullptr);
EXIT_IF(event->filter.data == nullptr);
EXIT_NOT_IMPLEMENTED(event->event.ident != VIDEO_OUT_EVENT_PRE_VBLANK_START);
EXIT_NOT_IMPLEMENTED(event->event.filter != EventQueue::KERNEL_EVFILT_VIDEO_OUT);
if (event->filter.data != nullptr) {
auto* video_out = static_cast<VideoOutConfig*>(event->filter.data);
video_out->mutex.Lock();
EXIT_IF(video_out->pre_vblank_eqs.empty());
auto it = std::find(video_out->pre_vblank_eqs.begin(), video_out->pre_vblank_eqs.end(), eq);
EXIT_NOT_IMPLEMENTED(it == video_out->pre_vblank_eqs.end());
*it = nullptr;
video_out->mutex.Unlock();
}
}
static void VblankEventTriggerFunc(LibKernel::EventQueue::KernelEqueueEvent* event,
void* trigger_data) {
EXIT_IF(event == nullptr);
auto triggered_event = event->event;
triggered_event.fflags =
((triggered_event.fflags < 0xfu) ? triggered_event.fflags + 1u : triggered_event.fflags);
triggered_event.data = MakeVideoOutEventData(triggered_event.data, trigger_data);
if (event->triggered) {
event->pending_events.push_back(triggered_event);
} else {
event->event = triggered_event;
event->triggered = true;
}
}
static void OutputModeEventResetFunc(LibKernel::EventQueue::KernelEqueueEvent* event) {
EXIT_IF(event == nullptr);
event->triggered = false;
event->event.fflags = 0;
event->event.data = 0;
}
static void OutputModeEventDeleteFunc(EventQueue::KernelEqueue eq,
LibKernel::EventQueue::KernelEqueueEvent* event) {
EXIT_IF(event == nullptr);
EXIT_IF(event->filter.data == nullptr);
EXIT_NOT_IMPLEMENTED(event->event.ident != VIDEO_OUT_EVENT_SET_MODE);
EXIT_NOT_IMPLEMENTED(event->event.filter != EventQueue::KERNEL_EVFILT_VIDEO_OUT);
if (event->filter.data != nullptr) {
auto* video_out = static_cast<VideoOutConfig*>(event->filter.data);
video_out->mutex.Lock();
EXIT_IF(video_out->output_mode_eqs.empty());
auto it =
std::find(video_out->output_mode_eqs.begin(), video_out->output_mode_eqs.end(), eq);
EXIT_NOT_IMPLEMENTED(it == video_out->output_mode_eqs.end());
*it = nullptr;
video_out->mutex.Unlock();
}
}
static void OutputModeEventTriggerFunc(LibKernel::EventQueue::KernelEqueueEvent* event,
void* trigger_data) {
EXIT_IF(event == nullptr);
auto triggered_event = event->event;
triggered_event.fflags =
((triggered_event.fflags < 0xfu) ? triggered_event.fflags + 1u : triggered_event.fflags);
triggered_event.data = MakeVideoOutEventData(triggered_event.data, trigger_data);
if (event->triggered) {
event->pending_events.push_back(triggered_event);
} else {
event->event = triggered_event;
event->triggered = true;
}
}
KYTY_SYSV_ABI int VideoOutDeleteFlipEvent(EventQueue::KernelEqueue eq, int handle) {
PRINT_NAME();
EXIT_IF(g_video_out_context == nullptr);
[[maybe_unused]] auto* ctx = g_video_out_context->Get(handle);
if (ctx == nullptr) {
return VIDEO_OUT_ERROR_INVALID_HANDLE;
}
if (eq == nullptr) {
return VIDEO_OUT_ERROR_INVALID_EVENT_QUEUE;
}
return EventQueue::KernelDeleteEvent(eq, VIDEO_OUT_EVENT_FLIP,
EventQueue::KERNEL_EVFILT_VIDEO_OUT);
return DeleteVideoOutEvent(handle, eq, VideoOutEventKind::Flip);
}
KYTY_SYSV_ABI int VideoOutAddFlipEvent(EventQueue::KernelEqueue eq, int handle, void* udata) {
PRINT_NAME();
EXIT_IF(g_video_out_context == nullptr);
auto* ctx = g_video_out_context->Get(handle);
if (ctx == nullptr) {
return VIDEO_OUT_ERROR_INVALID_HANDLE;
}
ctx->mutex.Lock();
if (eq == nullptr) {
ctx->mutex.Unlock();
return VIDEO_OUT_ERROR_INVALID_EVENT_QUEUE;
}
bool add_eq =
(std::find(ctx->flip_eqs.begin(), ctx->flip_eqs.end(), eq) == ctx->flip_eqs.end());
EventQueue::KernelEqueueEvent event;
event.triggered = false;
event.event.ident = VIDEO_OUT_EVENT_FLIP;
event.event.filter = EventQueue::KERNEL_EVFILT_VIDEO_OUT;
event.event.udata = udata;
event.event.fflags = 0;
event.event.data = 0;
event.filter.delete_event_func = FlipEventDeleteFunc;
event.filter.reset_func = FlipEventResetFunc;
event.filter.trigger_func = FlipEventTriggerFunc;
event.filter.data = ctx;
int result = EventQueue::KernelAddEvent(eq, event);
if (add_eq) {
ctx->flip_eqs.push_back(eq);
}
ctx->mutex.Unlock();
return result;
return RegisterVideoOutEvent(handle, eq, VideoOutEventKind::Flip, udata);
}
KYTY_SYSV_ABI int VideoOutDeleteVblankEvent(EventQueue::KernelEqueue eq, int handle) {
PRINT_NAME();
EXIT_IF(g_video_out_context == nullptr);
[[maybe_unused]] auto* ctx = g_video_out_context->Get(handle);
if (ctx == nullptr) {
return VIDEO_OUT_ERROR_INVALID_HANDLE;
}
if (eq == nullptr) {
return VIDEO_OUT_ERROR_INVALID_EVENT_QUEUE;
}
return EventQueue::KernelDeleteEvent(eq, VIDEO_OUT_EVENT_VBLANK,
EventQueue::KERNEL_EVFILT_VIDEO_OUT);
return DeleteVideoOutEvent(handle, eq, VideoOutEventKind::Vblank);
}
KYTY_SYSV_ABI int VideoOutDeletePreVblankStartEvent(EventQueue::KernelEqueue eq, int handle) {
PRINT_NAME();
EXIT_IF(g_video_out_context == nullptr);
[[maybe_unused]] auto* ctx = g_video_out_context->Get(handle);
if (ctx == nullptr) {
return VIDEO_OUT_ERROR_INVALID_HANDLE;
}
if (eq == nullptr) {
return VIDEO_OUT_ERROR_INVALID_EVENT_QUEUE;
}
return EventQueue::KernelDeleteEvent(eq, VIDEO_OUT_EVENT_PRE_VBLANK_START,
EventQueue::KERNEL_EVFILT_VIDEO_OUT);
return DeleteVideoOutEvent(handle, eq, VideoOutEventKind::PreVblankStart);
}
KYTY_SYSV_ABI int VideoOutAddVblankEvent(LibKernel::EventQueue::KernelEqueue eq, int handle,
void* udata) {
PRINT_NAME();
EXIT_IF(g_video_out_context == nullptr);
auto* ctx = g_video_out_context->Get(handle);
if (ctx == nullptr) {
return VIDEO_OUT_ERROR_INVALID_HANDLE;
}
ctx->mutex.Lock();
if (eq == nullptr) {
ctx->mutex.Unlock();
return VIDEO_OUT_ERROR_INVALID_EVENT_QUEUE;
}
bool add_eq =
(std::find(ctx->vblank_eqs.begin(), ctx->vblank_eqs.end(), eq) == ctx->vblank_eqs.end());
EventQueue::KernelEqueueEvent event;
event.triggered = false;
event.event.ident = VIDEO_OUT_EVENT_VBLANK;
event.event.filter = EventQueue::KERNEL_EVFILT_VIDEO_OUT;
event.event.udata = udata;
event.event.fflags = 0;
event.event.data = 0;
event.filter.delete_event_func = VblankEventDeleteFunc;
event.filter.reset_func = VblankEventResetFunc;
event.filter.trigger_func = VblankEventTriggerFunc;
event.filter.data = ctx;
int result = EventQueue::KernelAddEvent(eq, event);
if (add_eq) {
ctx->vblank_eqs.push_back(eq);
}
ctx->mutex.Unlock();
return result;
return RegisterVideoOutEvent(handle, eq, VideoOutEventKind::Vblank, udata);
}
KYTY_SYSV_ABI int VideoOutAddPreVblankStartEvent(LibKernel::EventQueue::KernelEqueue eq, int handle,
void* udata) {
PRINT_NAME();
EXIT_IF(g_video_out_context == nullptr);
auto* ctx = g_video_out_context->Get(handle);
if (ctx == nullptr) {
return VIDEO_OUT_ERROR_INVALID_HANDLE;
}
ctx->mutex.Lock();
if (eq == nullptr) {
ctx->mutex.Unlock();
return VIDEO_OUT_ERROR_INVALID_EVENT_QUEUE;
}
bool add_eq = (std::find(ctx->pre_vblank_eqs.begin(), ctx->pre_vblank_eqs.end(), eq) ==
ctx->pre_vblank_eqs.end());
EventQueue::KernelEqueueEvent event;
event.triggered = false;
event.event.ident = VIDEO_OUT_EVENT_PRE_VBLANK_START;
event.event.filter = EventQueue::KERNEL_EVFILT_VIDEO_OUT;
event.event.udata = udata;
event.event.fflags = 0;
event.event.data = 0;
event.filter.delete_event_func = PreVblankEventDeleteFunc;
event.filter.reset_func = VblankEventResetFunc;
event.filter.trigger_func = VblankEventTriggerFunc;
event.filter.data = ctx;
int result = EventQueue::KernelAddEvent(eq, event);
if (add_eq) {
ctx->pre_vblank_eqs.push_back(eq);
}
ctx->mutex.Unlock();
return result;
return RegisterVideoOutEvent(handle, eq, VideoOutEventKind::PreVblankStart, udata);
}
KYTY_SYSV_ABI int VideoOutAddOutputModeEvent(LibKernel::EventQueue::KernelEqueue eq, int handle,
void* udata) {
PRINT_NAME();
EXIT_IF(g_video_out_context == nullptr);
auto* ctx = g_video_out_context->Get(handle);
if (ctx == nullptr) {
return VIDEO_OUT_ERROR_INVALID_HANDLE;
}
ctx->mutex.Lock();
LOGF("\t eq = 0x%016" PRIx64 "\n"
"\t handle = %d\n"
"\t udata = 0x%016" PRIx64 "\n",
reinterpret_cast<uint64_t>(eq), handle, reinterpret_cast<uint64_t>(udata));
if (eq == nullptr) {
ctx->mutex.Unlock();
return VIDEO_OUT_ERROR_INVALID_EVENT_QUEUE;
}
bool add_eq = (std::find(ctx->output_mode_eqs.begin(), ctx->output_mode_eqs.end(), eq) ==
ctx->output_mode_eqs.end());
EventQueue::KernelEqueueEvent event;
event.triggered = true;
event.event.ident = VIDEO_OUT_EVENT_SET_MODE;
event.event.filter = EventQueue::KERNEL_EVFILT_VIDEO_OUT;
event.event.udata = udata;
event.event.fflags = 1;
event.event.data = MakeVideoOutEventData(0, reinterpret_cast<void*>(ctx->output_mode));
event.filter.delete_event_func = OutputModeEventDeleteFunc;
event.filter.reset_func = OutputModeEventResetFunc;
event.filter.trigger_func = OutputModeEventTriggerFunc;
event.filter.data = ctx;
int result = EventQueue::KernelAddEvent(eq, event);
if (add_eq) {
ctx->output_mode_eqs.push_back(eq);
}
ctx->mutex.Unlock();
return result;
return RegisterVideoOutEvent(handle, eq, VideoOutEventKind::OutputMode, udata);
}
static int RegisterBuffersInternal(VideoOutConfig* ctx, int set_id, int start_index,
@@ -1428,8 +1224,8 @@ KYTY_SYSV_ABI int VideoOutRegisterBuffers2(int handle, int set_index, int buffer
for (int i = 0; i < buffer_num; i++) {
LOGF("\t buffers[%d]: data=%p metadata=%p\n", i, buffers[i].data, buffers[i].metadata);
if (buffers[i].reserved[0] != nullptr || buffers[i].reserved[1] != nullptr) {
LOGF("\t buffers[%d]: ignoring reserved fields {%p, %p}\n", i,
buffers[i].reserved[0], buffers[i].reserved[1]);
LOGF("\t buffers[%d]: ignoring reserved fields {%p, %p}\n", i, buffers[i].reserved[0],
buffers[i].reserved[1]);
}
const auto data_address = reinterpret_cast<uint64_t>(buffers[i].data);
const auto metadata_address = reinterpret_cast<uint64_t>(buffers[i].metadata);
@@ -1543,33 +1339,14 @@ namespace Libs::VideoOut {
KYTY_SYSV_ABI int VideoOutSubmitFlip(int handle, int index, int flip_mode, int64_t flip_arg) {
PRINT_NAME();
EXIT_IF(g_video_out_context == nullptr);
auto* ctx = g_video_out_context->Get(handle);
if (ctx == nullptr) {
return VIDEO_OUT_ERROR_INVALID_HANDLE;
}
if (flip_mode < VIDEO_OUT_FLIP_MODE_VSYNC || flip_mode > VIDEO_OUT_FLIP_MODE_VSYNC_MULTI) {
uint64_t request_id = 0;
const int result =
ReserveFlipRequest(handle, index, flip_mode, flip_arg, FlipRequestSource::Cpu, &request_id);
if (result == VIDEO_OUT_ERROR_INVALID_VALUE) {
LOGF("\t unsupported flip_mode = %d\n", flip_mode);
return VIDEO_OUT_ERROR_INVALID_VALUE;
}
if (!IsValidBufferIndex(index)) {
return VIDEO_OUT_ERROR_INVALID_INDEX;
}
uint64_t request_id = 0;
{
Common::LockGuard lock(ctx->mutex);
const bool special = IsSpecialBufferIndex(index);
if (ctx->closing || (!special && (ctx->unregistering[index] ||
ctx->buffers[index].buffer_vulkan == nullptr))) {
return VIDEO_OUT_ERROR_INVALID_INDEX;
}
if (!g_video_out_context->GetFlipQueue().Reserve(ctx, index, flip_arg, false,
&request_id)) {
return VIDEO_OUT_ERROR_FLIP_QUEUE_FULL;
}
if (result != OK) {
return result;
}
Graphics::GraphicsRunSubmitFlipPreparation();
@@ -1585,30 +1362,10 @@ int DisplayBufferSubmitFlipFromGpu(Graphics::CommandBuffer* buffer, int handle,
EXIT_IF(VideoOut::g_video_out_context == nullptr || Graphics::g_render_ctx == nullptr ||
buffer == nullptr || request_id == nullptr);
auto* ctx = VideoOut::g_video_out_context->Get(handle);
if (ctx == nullptr) {
return VideoOut::VIDEO_OUT_ERROR_INVALID_HANDLE;
}
if (flip_mode < VideoOut::VIDEO_OUT_FLIP_MODE_VSYNC ||
flip_mode > VideoOut::VIDEO_OUT_FLIP_MODE_VSYNC_MULTI) {
return VideoOut::VIDEO_OUT_ERROR_INVALID_VALUE;
}
if (!VideoOut::IsValidBufferIndex(index)) {
return VideoOut::VIDEO_OUT_ERROR_INVALID_INDEX;
}
{
Common::LockGuard lock(ctx->mutex);
const bool special = VideoOut::IsSpecialBufferIndex(index);
if (ctx->closing || (!special && (ctx->unregistering[index] ||
ctx->buffers[index].buffer_vulkan == nullptr))) {
return VideoOut::VIDEO_OUT_ERROR_INVALID_INDEX;
}
if (!VideoOut::g_video_out_context->GetFlipQueue().Reserve(ctx, index, flip_arg, true,
request_id)) {
return VideoOut::VIDEO_OUT_ERROR_FLIP_QUEUE_FULL;
}
const int result = VideoOut::ReserveFlipRequest(
handle, index, flip_mode, flip_arg, VideoOut::FlipRequestSource::GpuEop, request_id);
if (result != OK) {
return result;
}
VideoOut::g_video_out_context->GetFlipQueue().Prepare(*request_id, buffer);
@@ -1901,14 +1658,8 @@ KYTY_SYSV_ABI int VideoOutConfigureOutput(int handle, uint64_t mode,
ctx->mutex.Lock();
ctx->output_mode = mode;
for (auto& output_mode_eq: ctx->output_mode_eqs) {
if (output_mode_eq != nullptr) {
auto trigger_result = EventQueue::KernelTriggerEvent(
output_mode_eq, VIDEO_OUT_EVENT_SET_MODE, EventQueue::KERNEL_EVFILT_VIDEO_OUT,
reinterpret_cast<void*>(ctx->output_mode));
EXIT_NOT_IMPLEMENTED(trigger_result != OK);
}
}
TriggerVideoOutEventsLocked(ctx->output_mode_eqs, VideoOutEventKind::OutputMode,
reinterpret_cast<void*>(ctx->output_mode));
ctx->mutex.Unlock();
return OK;