libnx/nx/source/display/buffer_producer.c

261 lines
7.0 KiB
C

#include <string.h>
#include "types.h"
#include "result.h"
#include "display/parcel.h"
#include "display/buffer_producer.h"
// This implements the version of Android IGraphicBufferProducer used by Switch.
// Hence names/params etc here are based on Android IGraphicBufferProducer.cpp.
// Based on an old version of the enum from the above .cpp.
// https://android.googlesource.com/platform/frameworks/native/+/29a3e90879fd96404c971e7187cd0e05927bbce0/libs/gui/IGraphicBufferProducer.cpp#35
enum {
/* 0x1 */ REQUEST_BUFFER = BINDER_FIRST_CALL_TRANSACTION,
/* 0x2 */ SET_BUFFER_COUNT,
/* 0x3 */ DEQUEUE_BUFFER,
/* 0x4 */ DETACH_BUFFER,
/* 0x5 */ DETACH_NEXT_BUFFER,
/* 0x6 */ ATTACH_BUFFER,
/* 0x7 */ QUEUE_BUFFER,
/* 0x8 */ CANCEL_BUFFER,
/* 0x9 */ QUERY,
/* 0xA */ CONNECT,
/* 0xB */ DISCONNECT,
/* 0xC */ SET_SIDEBAND_STREAM,
/* 0xD */ ALLOCATE_BUFFERS,
/* 0xE */ SET_PREALLOCATED_BUFFER, // Custom Switch-specific command
};
static const char g_bq_InterfaceDescriptor[] = "android.gui.IGraphicBufferProducer";
Result bqRequestBuffer(Binder *b, s32 bufferIdx, BqGraphicBuffer *buf)
{
Result rc;
Parcel parcel, parcel_reply;
parcelCreate(&parcel);
parcelCreate(&parcel_reply);
parcelWriteInterfaceToken(&parcel, g_bq_InterfaceDescriptor);
parcelWriteInt32(&parcel, bufferIdx);
rc = parcelTransact(b, REQUEST_BUFFER, &parcel, &parcel_reply);
if (R_SUCCEEDED(rc)) {
int nonNull = parcelReadInt32(&parcel_reply);
if (nonNull != 0) {
size_t tmp_size=0;
void* tmp_ptr;
tmp_ptr = parcelReadFlattenedObject(&parcel_reply, &tmp_size);
if (tmp_ptr == NULL || tmp_size != sizeof(BqGraphicBuffer))
return MAKERESULT(Module_Libnx, LibnxError_BadInput);
if (buf)
memcpy(buf, tmp_ptr, sizeof(BqGraphicBuffer));
}
int status = parcelReadInt32(&parcel_reply);
if (status != 0) {
rc = MAKERESULT(Module_Libnx, LibnxError_BufferProducerError);
}
}
return rc;
}
Result bqDequeueBuffer(Binder *b, bool async, u32 width, u32 height, s32 format, u32 usage, s32 *buf, NvMultiFence *fence)
{
Result rc;
Parcel parcel, parcel_reply;
parcelCreate(&parcel);
parcelCreate(&parcel_reply);
parcelWriteInterfaceToken(&parcel, g_bq_InterfaceDescriptor);
parcelWriteInt32(&parcel, async);
parcelWriteUInt32(&parcel, width);
parcelWriteUInt32(&parcel, height);
parcelWriteInt32(&parcel, format);
parcelWriteUInt32(&parcel, usage);
rc = parcelTransact(b, DEQUEUE_BUFFER, &parcel, &parcel_reply);
if (R_SUCCEEDED(rc)) {
*buf = parcelReadInt32(&parcel_reply);
if(parcelReadInt32(&parcel_reply)) {
size_t tmp_size=0;
void* tmp_ptr;
tmp_ptr = parcelReadFlattenedObject(&parcel_reply, &tmp_size);
if (tmp_ptr == NULL || tmp_size != sizeof(NvMultiFence))
return MAKERESULT(Module_Libnx, LibnxError_BadInput);
if (fence)
memcpy(fence, tmp_ptr, sizeof(NvMultiFence));
}
int result = parcelReadInt32(&parcel_reply);
if (result != 0)
rc = MAKERESULT(Module_Libnx, LibnxError_BufferProducerError);
}
return rc;
}
Result bqDetachBuffer(Binder *b, s32 slot)
{
Result rc;
Parcel parcel, parcel_reply;
parcelCreate(&parcel);
parcelCreate(&parcel_reply);
parcelWriteInterfaceToken(&parcel, g_bq_InterfaceDescriptor);
parcelWriteInt32(&parcel, slot);
rc = parcelTransact(b, DETACH_BUFFER, &parcel, &parcel_reply);
if (R_SUCCEEDED(rc)) {
//TODO: parse reply
}
return rc;
}
Result bqQueueBuffer(Binder *b, s32 buf, BqQueueBufferInput *input, BqQueueBufferOutput *output)
{
Result rc;
Parcel parcel, parcel_reply;
parcelCreate(&parcel);
parcelCreate(&parcel_reply);
parcelWriteInterfaceToken(&parcel, g_bq_InterfaceDescriptor);
parcelWriteInt32(&parcel, buf);
parcelWriteFlattenedObject(&parcel, input, sizeof(*input));
rc = parcelTransact(b, QUEUE_BUFFER, &parcel, &parcel_reply);
if (R_SUCCEEDED(rc)) {
if (parcelReadData(&parcel_reply, output, sizeof(*output)) == NULL)
return MAKERESULT(Module_Libnx, LibnxError_BadInput);
int result = parcelReadInt32(&parcel_reply);
if (result != 0)
rc = MAKERESULT(Module_Libnx, LibnxError_BufferProducerError);
}
return rc;
}
Result bqQuery(Binder *b, s32 what, s32* value)
{
Result rc;
Parcel parcel, parcel_reply;
parcelCreate(&parcel);
parcelCreate(&parcel_reply);
parcelWriteInterfaceToken(&parcel, g_bq_InterfaceDescriptor);
parcelWriteInt32(&parcel, what);
rc = parcelTransact(b, QUERY, &parcel, &parcel_reply);
if (R_SUCCEEDED(rc)) {
*value = parcelReadInt32(&parcel_reply);
int result = parcelReadInt32(&parcel_reply);
if (result != 0)
rc = MAKERESULT(Module_Libnx, LibnxError_BufferProducerError);
}
return rc;
}
Result bqConnect(Binder *b, s32 api, bool producerControlledByApp, BqQueueBufferOutput *output)
{
Result rc;
Parcel parcel, parcel_reply;
parcelCreate(&parcel);
parcelCreate(&parcel_reply);
parcelWriteInterfaceToken(&parcel, g_bq_InterfaceDescriptor);
// Hard-code this as if listener==NULL, since that's not known to be used officially.
parcelWriteInt32(&parcel, 0);
parcelWriteInt32(&parcel, api);
parcelWriteInt32(&parcel, producerControlledByApp);
rc = parcelTransact(b, CONNECT, &parcel, &parcel_reply);
if (R_SUCCEEDED(rc)) {
if (parcelReadData(&parcel_reply, output, sizeof(*output)) == NULL)
return MAKERESULT(Module_Libnx, LibnxError_BadInput);
int result = parcelReadInt32(&parcel_reply);
if (result != 0)
rc = MAKERESULT(Module_Libnx, LibnxError_BufferProducerError);
}
return rc;
}
Result bqDisconnect(Binder *b, s32 api)
{
Result rc;
Parcel parcel, parcel_reply;
parcelCreate(&parcel);
parcelCreate(&parcel_reply);
parcelWriteInterfaceToken(&parcel, g_bq_InterfaceDescriptor);
parcelWriteInt32(&parcel, api);
rc = parcelTransact(b, DISCONNECT, &parcel, &parcel_reply);
if (R_SUCCEEDED(rc)) {
// TODO: parse reply
}
return rc;
}
Result bqSetPreallocatedBuffer(Binder *b, s32 buf, BqGraphicBuffer *input)
{
Result rc;
Parcel parcel, parcel_reply;
bool flag = 0;
parcelCreate(&parcel);
parcelCreate(&parcel_reply);
parcelWriteInterfaceToken(&parcel, g_bq_InterfaceDescriptor);
parcelWriteInt32(&parcel, buf);
if (input != NULL)
flag = 1;
parcelWriteInt32(&parcel, flag);
if (flag)
parcelWriteFlattenedObject(&parcel, input, sizeof(BqGraphicBuffer));
rc = parcelTransact(b, SET_PREALLOCATED_BUFFER, &parcel, &parcel_reply);
if (R_SUCCEEDED(rc)) {
int result = parcelReadInt32(&parcel_reply);
if (result != 0)
rc = MAKERESULT(Module_Libnx, LibnxError_BufferProducerError);
}
return rc;
}