mirror of
https://github.com/switchbrew/libnx.git
synced 2025-06-21 12:32:40 +02:00
Introduce NWindow, an experimental replacement for the GFX API
This commit is contained in:
parent
b188f5fb33
commit
a9cbd9116a
@ -86,6 +86,7 @@ extern "C" {
|
||||
#include "switch/display/binder.h"
|
||||
#include "switch/display/parcel.h"
|
||||
#include "switch/display/buffer_producer.h"
|
||||
#include "switch/display/native_window.h"
|
||||
|
||||
#include "switch/nvidia/ioctl.h"
|
||||
#include "switch/nvidia/map.h"
|
||||
|
40
nx/include/switch/display/native_window.h
Normal file
40
nx/include/switch/display/native_window.h
Normal file
@ -0,0 +1,40 @@
|
||||
#pragma once
|
||||
#include "../kernel/mutex.h"
|
||||
#include "../kernel/event.h"
|
||||
#include "../services/vi.h"
|
||||
#include "../nvidia/graphic_buffer.h"
|
||||
#include "types.h"
|
||||
#include "binder.h"
|
||||
#include "buffer_producer.h"
|
||||
|
||||
typedef struct NWindow {
|
||||
Binder bq;
|
||||
Event event;
|
||||
Mutex mutex;
|
||||
u64 slots_requested;
|
||||
s32 cur_slot;
|
||||
u32 width;
|
||||
u32 height;
|
||||
u32 format;
|
||||
u32 usage;
|
||||
BqRect crop;
|
||||
u32 scaling_mode;
|
||||
u32 transform;
|
||||
u32 sticky_transform;
|
||||
u32 default_width;
|
||||
u32 default_height;
|
||||
u32 transform_hint;
|
||||
u32 swap_interval;
|
||||
bool has_init;
|
||||
bool is_connected;
|
||||
bool consumer_running_behind;
|
||||
} NWindow;
|
||||
|
||||
Result nwindowCreate(NWindow* nw, s32 binder_id, bool producer_controlled_by_app);
|
||||
Result nwindowCreateFromLayer(NWindow* nw, const ViLayer* layer);
|
||||
void nwindowClose(NWindow* nw);
|
||||
|
||||
Result nwindowConfigureBuffer(NWindow* nw, s32 slot, NvGraphicBuffer* buf);
|
||||
Result nwindowDequeueBuffer(NWindow* nw, s32* out_slot, NvMultiFence* out_fence);
|
||||
Result nwindowCancelBuffer(NWindow* nw, s32 slot, const NvMultiFence* fence);
|
||||
Result nwindowQueueBuffer(NWindow* nw, s32 slot, const NvMultiFence* fence);
|
209
nx/source/display/native_window.c
Normal file
209
nx/source/display/native_window.c
Normal file
@ -0,0 +1,209 @@
|
||||
#include <string.h>
|
||||
#include "types.h"
|
||||
#include "result.h"
|
||||
#include "services/vi.h"
|
||||
#include "display/binder.h"
|
||||
#include "display/buffer_producer.h"
|
||||
#include "display/native_window.h"
|
||||
#include "nvidia/graphic_buffer.h"
|
||||
|
||||
static void _nwindowUpdate(NWindow* nw, const BqBufferOutput* out)
|
||||
{
|
||||
nw->default_width = out->width;
|
||||
nw->default_height = out->height;
|
||||
nw->consumer_running_behind = out->numPendingBuffers > 1;
|
||||
}
|
||||
|
||||
Result nwindowCreate(NWindow* nw, s32 binder_id, bool producer_controlled_by_app)
|
||||
{
|
||||
Result rc;
|
||||
|
||||
memset(nw, 0, sizeof(*nw));
|
||||
nw->has_init = true;
|
||||
nw->swap_interval = 1;
|
||||
nw->cur_slot = -1;
|
||||
nw->format = ~0U;
|
||||
|
||||
binderCreate(&nw->bq, binder_id);
|
||||
rc = binderInitSession(&nw->bq);
|
||||
|
||||
if (R_SUCCEEDED(rc))
|
||||
binderGetNativeHandle(&nw->bq, 0x0f, &nw->event);
|
||||
|
||||
if (R_SUCCEEDED(rc)) {
|
||||
BqBufferOutput bqoutput;
|
||||
rc = bqConnect(&nw->bq, NATIVE_WINDOW_API_CPU, producer_controlled_by_app, &bqoutput);
|
||||
if (R_SUCCEEDED(rc)) {
|
||||
nw->is_connected = true;
|
||||
_nwindowUpdate(nw, &bqoutput);
|
||||
}
|
||||
}
|
||||
|
||||
if (R_FAILED(rc))
|
||||
nwindowClose(nw);
|
||||
|
||||
return rc;
|
||||
}
|
||||
|
||||
Result nwindowCreateFromLayer(NWindow* nw, const ViLayer* layer)
|
||||
{
|
||||
return nwindowCreate(nw, layer->igbp_binder_obj_id, false);
|
||||
}
|
||||
|
||||
void nwindowClose(NWindow* nw)
|
||||
{
|
||||
if (!nw || !nw->has_init)
|
||||
return;
|
||||
|
||||
if (nw->is_connected)
|
||||
bqDisconnect(&nw->bq, NATIVE_WINDOW_API_CPU);
|
||||
|
||||
eventClose(&nw->event);
|
||||
binderClose(&nw->bq);
|
||||
|
||||
memset(nw, 0, sizeof(*nw));
|
||||
}
|
||||
|
||||
Result nwindowConfigureBuffer(NWindow* nw, s32 slot, NvGraphicBuffer* buf)
|
||||
{
|
||||
if (!nw || !buf || slot < 0 || slot >= 64)
|
||||
return MAKERESULT(Module_Libnx, LibnxError_BadInput);
|
||||
if (!nw->has_init)
|
||||
return MAKERESULT(Module_Libnx, LibnxError_NotInitialized);
|
||||
|
||||
mutexLock(&nw->mutex);
|
||||
|
||||
if (!nw->width)
|
||||
nw->width = buf->layers[0].width;
|
||||
if (!nw->height)
|
||||
nw->height = buf->layers[0].height;
|
||||
if (nw->format == ~0U)
|
||||
nw->format = buf->format;
|
||||
if (!nw->usage)
|
||||
nw->usage = buf->usage;
|
||||
|
||||
BqGraphicBuffer bqbuf;
|
||||
bqbuf.width = nw->width;
|
||||
bqbuf.height = nw->height;
|
||||
bqbuf.stride = buf->stride;
|
||||
//bqbuf.stride = buf->layers[0].pitch / (u8)(buf->layers[0].color_format >> 3); // this also works
|
||||
bqbuf.format = nw->format;
|
||||
bqbuf.usage = nw->usage;
|
||||
bqbuf.native_handle = &buf->header;
|
||||
|
||||
Result rc = bqSetPreallocatedBuffer(&nw->bq, slot, &bqbuf);
|
||||
|
||||
mutexUnlock(&nw->mutex);
|
||||
return rc;
|
||||
}
|
||||
|
||||
Result nwindowDequeueBuffer(NWindow* nw, s32* out_slot, NvMultiFence* out_fence)
|
||||
{
|
||||
if (!nw || !out_slot)
|
||||
return MAKERESULT(Module_Libnx, LibnxError_BadInput);
|
||||
if (!nw->has_init)
|
||||
return MAKERESULT(Module_Libnx, LibnxError_NotInitialized);
|
||||
|
||||
mutexLock(&nw->mutex);
|
||||
|
||||
if (nw->cur_slot >= 0) {
|
||||
mutexUnlock(&nw->mutex);
|
||||
return MAKERESULT(Module_Libnx, LibnxError_BadGfxDequeueBuffer);
|
||||
}
|
||||
|
||||
NvMultiFence fence;
|
||||
s32 slot;
|
||||
Result rc;
|
||||
|
||||
if (nw->event.revent != INVALID_HANDLE) {
|
||||
do {
|
||||
eventWait(&nw->event, U64_MAX);
|
||||
rc = bqDequeueBuffer(&nw->bq, true, nw->width, nw->height, nw->format, nw->usage, &slot, &fence);
|
||||
} while (rc == MAKERESULT(Module_LibnxBinder, LibnxBinderError_WouldBlock));
|
||||
}
|
||||
else
|
||||
rc = bqDequeueBuffer(&nw->bq, false, nw->width, nw->height, nw->format, nw->usage, &slot, &fence);
|
||||
|
||||
if (R_SUCCEEDED(rc)) {
|
||||
if (!(nw->slots_requested & (1UL << slot))) {
|
||||
rc = bqRequestBuffer(&nw->bq, slot, NULL);
|
||||
if (R_FAILED(rc))
|
||||
bqCancelBuffer(&nw->bq, slot, &fence);
|
||||
else
|
||||
nw->slots_requested |= 1UL << slot;
|
||||
}
|
||||
}
|
||||
|
||||
if (R_SUCCEEDED(rc)) {
|
||||
*out_slot = slot;
|
||||
nw->cur_slot = slot;
|
||||
if (out_fence)
|
||||
*out_fence = fence;
|
||||
else
|
||||
nvMultiFenceWait(&fence, -1);
|
||||
}
|
||||
|
||||
mutexUnlock(&nw->mutex);
|
||||
return rc;
|
||||
}
|
||||
|
||||
Result nwindowCancelBuffer(NWindow* nw, s32 slot, const NvMultiFence* fence)
|
||||
{
|
||||
if (!nw || slot < 0 || slot >= 64)
|
||||
return MAKERESULT(Module_Libnx, LibnxError_BadInput);
|
||||
if (!nw->has_init)
|
||||
return MAKERESULT(Module_Libnx, LibnxError_NotInitialized);
|
||||
|
||||
mutexLock(&nw->mutex);
|
||||
|
||||
if (slot != nw->cur_slot) {
|
||||
mutexUnlock(&nw->mutex);
|
||||
return MAKERESULT(Module_Libnx, LibnxError_BadGfxQueueBuffer);
|
||||
}
|
||||
|
||||
static const NvMultiFence s_emptyFence = {0};
|
||||
if (!fence)
|
||||
fence = &s_emptyFence;
|
||||
|
||||
Result rc = bqCancelBuffer(&nw->bq, slot, fence);
|
||||
if (R_SUCCEEDED(rc))
|
||||
nw->cur_slot = -1;
|
||||
|
||||
mutexUnlock(&nw->mutex);
|
||||
return rc;
|
||||
}
|
||||
|
||||
Result nwindowQueueBuffer(NWindow* nw, s32 slot, const NvMultiFence* fence)
|
||||
{
|
||||
if (!nw || slot < 0 || slot >= 64)
|
||||
return MAKERESULT(Module_Libnx, LibnxError_BadInput);
|
||||
if (!nw->has_init)
|
||||
return MAKERESULT(Module_Libnx, LibnxError_NotInitialized);
|
||||
|
||||
mutexLock(&nw->mutex);
|
||||
|
||||
if (slot != nw->cur_slot) {
|
||||
mutexUnlock(&nw->mutex);
|
||||
return MAKERESULT(Module_Libnx, LibnxError_BadGfxQueueBuffer);
|
||||
}
|
||||
|
||||
BqBufferInput bqinput;
|
||||
memset(&bqinput, 0, sizeof(bqinput));
|
||||
bqinput.crop = nw->crop;
|
||||
bqinput.scalingMode = nw->scaling_mode;
|
||||
bqinput.transform = nw->transform;
|
||||
bqinput.stickyTransform = nw->sticky_transform;
|
||||
bqinput.swapInterval = nw->swap_interval;
|
||||
if (fence)
|
||||
bqinput.fence = *fence;
|
||||
|
||||
BqBufferOutput bqoutput;
|
||||
Result rc = bqQueueBuffer(&nw->bq, slot, &bqinput, &bqoutput);
|
||||
if (R_SUCCEEDED(rc)) {
|
||||
nw->cur_slot = -1;
|
||||
_nwindowUpdate(nw, &bqoutput);
|
||||
}
|
||||
|
||||
mutexUnlock(&nw->mutex);
|
||||
return rc;
|
||||
}
|
Loading…
Reference in New Issue
Block a user