libnx/nx/source/display/native_window.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 (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;
}