mirror of
https://github.com/switchbrew/libnx.git
synced 2025-06-21 12:32:40 +02:00
333 lines
9.0 KiB
C
333 lines
9.0 KiB
C
#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"
|
|
|
|
#define NWINDOW_MAGIC 0x6E69574E // NWin
|
|
|
|
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;
|
|
}
|
|
|
|
static Result _nwindowConnect(NWindow* nw)
|
|
{
|
|
BqBufferOutput bqoutput;
|
|
Result rc = bqConnect(&nw->bq, NATIVE_WINDOW_API_CPU, nw->producer_controlled_by_app, &bqoutput);
|
|
if (R_SUCCEEDED(rc)) {
|
|
nw->is_connected = true;
|
|
_nwindowUpdate(nw, &bqoutput);
|
|
}
|
|
return rc;
|
|
}
|
|
|
|
static Result _nwindowDisconnect(NWindow* nw)
|
|
{
|
|
Result rc = bqDisconnect(&nw->bq, NATIVE_WINDOW_API_CPU);
|
|
if (R_SUCCEEDED(rc)) {
|
|
nw->is_connected = false;
|
|
nw->slots_configured = 0;
|
|
nw->slots_requested = 0;
|
|
nw->cur_slot = -1;
|
|
nw->width = 0;
|
|
nw->height = 0;
|
|
nw->format = 0;
|
|
nw->usage = 0;
|
|
}
|
|
return rc;
|
|
}
|
|
|
|
bool nwindowIsValid(NWindow* nw)
|
|
{
|
|
return nw && nw->magic == NWINDOW_MAGIC;
|
|
}
|
|
|
|
Result nwindowCreate(NWindow* nw, Service* binder_session, s32 binder_id, bool producer_controlled_by_app)
|
|
{
|
|
Result rc;
|
|
|
|
memset(nw, 0, sizeof(*nw));
|
|
nw->magic = NWINDOW_MAGIC;
|
|
nw->swap_interval = 1;
|
|
nw->cur_slot = -1;
|
|
nw->format = ~0U;
|
|
nw->producer_controlled_by_app = producer_controlled_by_app;
|
|
|
|
binderCreate(&nw->bq, binder_id);
|
|
rc = binderInitSession(&nw->bq, binder_session);
|
|
|
|
if (R_SUCCEEDED(rc))
|
|
binderGetNativeHandle(&nw->bq, 0x0f, &nw->event);
|
|
|
|
if (R_SUCCEEDED(rc))
|
|
rc = _nwindowConnect(nw);
|
|
|
|
if (R_FAILED(rc))
|
|
nwindowClose(nw);
|
|
|
|
return rc;
|
|
}
|
|
|
|
Result nwindowCreateFromLayer(NWindow* nw, const ViLayer* layer)
|
|
{
|
|
return nwindowCreate(nw, viGetSession_IHOSBinderDriverRelay(), layer->igbp_binder_obj_id, false);
|
|
}
|
|
|
|
void nwindowClose(NWindow* nw)
|
|
{
|
|
if (!nwindowIsValid(nw))
|
|
return;
|
|
|
|
if (nw->is_connected)
|
|
_nwindowDisconnect(nw);
|
|
|
|
eventClose(&nw->event);
|
|
binderClose(&nw->bq);
|
|
|
|
memset(nw, 0, sizeof(*nw));
|
|
}
|
|
|
|
Result nwindowGetDimensions(NWindow* nw, u32* out_width, u32* out_height)
|
|
{
|
|
if (!nwindowIsValid(nw))
|
|
return MAKERESULT(Module_Libnx, LibnxError_NotInitialized);
|
|
if (!out_width || !out_height)
|
|
return MAKERESULT(Module_Libnx, LibnxError_BadInput);
|
|
|
|
*out_width = nw->width ? nw->width : nw->default_width;
|
|
*out_height = nw->height ? nw->height : nw->default_height;
|
|
return 0;
|
|
}
|
|
|
|
Result nwindowSetDimensions(NWindow* nw, u32 width, u32 height)
|
|
{
|
|
if (!nwindowIsValid(nw))
|
|
return MAKERESULT(Module_Libnx, LibnxError_NotInitialized);
|
|
if ((nw->width || nw->height) && nw->slots_configured)
|
|
return MAKERESULT(Module_Libnx, LibnxError_AlreadyInitialized);
|
|
|
|
nw->width = width;
|
|
nw->height = height;
|
|
memset(&nw->crop, 0, sizeof(nw->crop));
|
|
return 0;
|
|
}
|
|
|
|
Result nwindowSetCrop(NWindow* nw, s32 left, s32 top, s32 right, s32 bottom)
|
|
{
|
|
if (right < left || bottom < top)
|
|
return MAKERESULT(Module_Libnx, LibnxError_BadInput);
|
|
|
|
u32 width, height;
|
|
Result rc = nwindowGetDimensions(nw, &width, &height);
|
|
if (R_SUCCEEDED(rc)) {
|
|
nw->crop.left = left < 0 ? 0 : left;
|
|
nw->crop.top = top < 0 ? 0 : top;
|
|
nw->crop.right = right > width ? width : right;
|
|
nw->crop.bottom = bottom > height ? height : bottom;
|
|
}
|
|
return rc;
|
|
}
|
|
|
|
Result nwindowSetTransform(NWindow* nw, u32 transform)
|
|
{
|
|
if (!nwindowIsValid(nw))
|
|
return MAKERESULT(Module_Libnx, LibnxError_NotInitialized);
|
|
if (transform & ~(HAL_TRANSFORM_FLIP_H|HAL_TRANSFORM_FLIP_V|HAL_TRANSFORM_ROT_90))
|
|
return MAKERESULT(Module_Libnx, LibnxError_BadInput);
|
|
|
|
nw->transform = transform;
|
|
return 0;
|
|
}
|
|
|
|
Result nwindowSetSwapInterval(NWindow* nw, u32 swap_interval)
|
|
{
|
|
if (!nwindowIsValid(nw))
|
|
return MAKERESULT(Module_Libnx, LibnxError_NotInitialized);
|
|
|
|
nw->swap_interval = swap_interval;
|
|
return 0;
|
|
}
|
|
|
|
Result nwindowConfigureBuffer(NWindow* nw, s32 slot, NvGraphicBuffer* buf)
|
|
{
|
|
if (!nw || !buf || slot < 0 || slot >= 64)
|
|
return MAKERESULT(Module_Libnx, LibnxError_BadInput);
|
|
if (!nwindowIsValid(nw))
|
|
return MAKERESULT(Module_Libnx, LibnxError_NotInitialized);
|
|
|
|
mutexLock(&nw->mutex);
|
|
|
|
if (nw->slots_configured & (1UL << slot)) {
|
|
mutexUnlock(&nw->mutex);
|
|
return MAKERESULT(Module_Libnx, LibnxError_AlreadyInitialized);
|
|
}
|
|
|
|
if (!nw->is_connected) {
|
|
Result rc = _nwindowConnect(nw);
|
|
if (R_FAILED(rc)) {
|
|
mutexUnlock(&nw->mutex);
|
|
return rc;
|
|
}
|
|
}
|
|
|
|
if (!nw->width)
|
|
nw->width = buf->planes[0].width;
|
|
if (!nw->height)
|
|
nw->height = buf->planes[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->planes[0].pitch / (u8)(buf->planes[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);
|
|
if (R_SUCCEEDED(rc))
|
|
nw->slots_configured |= 1UL << slot;
|
|
|
|
mutexUnlock(&nw->mutex);
|
|
return rc;
|
|
}
|
|
|
|
Result nwindowDequeueBuffer(NWindow* nw, s32* out_slot, NvMultiFence* out_fence)
|
|
{
|
|
if (!nw)
|
|
return MAKERESULT(Module_Libnx, LibnxError_BadInput);
|
|
if (!nwindowIsValid(nw))
|
|
return MAKERESULT(Module_Libnx, LibnxError_NotInitialized);
|
|
|
|
mutexLock(&nw->mutex);
|
|
|
|
if (!nw->slots_configured || nw->cur_slot >= 0) {
|
|
mutexUnlock(&nw->mutex);
|
|
return MAKERESULT(Module_Libnx, LibnxError_BadGfxDequeueBuffer);
|
|
}
|
|
|
|
NvMultiFence fence;
|
|
s32 slot;
|
|
Result rc;
|
|
|
|
if (eventActive(&nw->event)) {
|
|
do {
|
|
eventWait(&nw->event, UINT64_MAX);
|
|
rc = bqDequeueBuffer(&nw->bq, true, nw->width, nw->height, nw->format, nw->usage, &slot, &fence);
|
|
} while (R_VALUE(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)) {
|
|
nw->cur_slot = slot;
|
|
if (out_slot)
|
|
*out_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 (!nwindowIsValid(nw))
|
|
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 (!nwindowIsValid(nw))
|
|
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;
|
|
}
|
|
|
|
Result nwindowReleaseBuffers(NWindow* nw)
|
|
{
|
|
if (!nwindowIsValid(nw))
|
|
return MAKERESULT(Module_Libnx, LibnxError_NotInitialized);
|
|
|
|
Result rc = 0;
|
|
mutexLock(&nw->mutex);
|
|
|
|
if (nw->cur_slot >= 0)
|
|
rc = MAKERESULT(Module_Libnx, LibnxError_BadInput);
|
|
else if (nw->is_connected && nw->slots_configured)
|
|
rc = _nwindowDisconnect(nw);
|
|
|
|
mutexUnlock(&nw->mutex);
|
|
return rc;
|
|
}
|