diff --git a/include/libtransistor/display/fence.h b/include/libtransistor/display/fence.h index c1d0cd27..10cf228c 100644 --- a/include/libtransistor/display/fence.h +++ b/include/libtransistor/display/fence.h @@ -12,11 +12,12 @@ extern "C" { #include /** -* @struct fence_t -* @brief Description here... -*/ + * @struct fence_t + * @brief Represents conditions for the completion of an asynchronous graphics operation + */ typedef struct { - uint32_t unknown[11]; + uint32_t is_valid; + gpu_fence_t sync[4]; } fence_t; #ifdef __cplusplus diff --git a/include/libtransistor/display/graphic_buffer_queue.h b/include/libtransistor/display/graphic_buffer_queue.h index ebfcae5b..e9eb328a 100644 --- a/include/libtransistor/display/graphic_buffer_queue.h +++ b/include/libtransistor/display/graphic_buffer_queue.h @@ -81,14 +81,17 @@ typedef enum { * https://source.android.com/reference/hidl/android/hardware/graphics/bufferqueue/1.0/IGraphicBufferProducer#queuebufferinput */ typedef struct { + uint32_t size; + uint32_t num_fds; int64_t timestamp; - bool is_auto_timestamp; + int32_t is_auto_timestamp; rect_t crop; int32_t scaling_mode; uint32_t transform; + uint32_t sticky_transform; + uint32_t unknown[2]; fence_t fence; - // and a few more unknown fields -} queue_buffer_input_t; +} __attribute__((packed)) queue_buffer_input_t; /** * @struct queue_buffer_output_t diff --git a/include/libtransistor/display/surface.h b/include/libtransistor/display/surface.h index 86a592cb..2ed08ab1 100644 --- a/include/libtransistor/display/surface.h +++ b/include/libtransistor/display/surface.h @@ -33,12 +33,14 @@ typedef struct { surface_state_t state; bool has_requested[2]; uint32_t current_slot; - + gpu_buffer_t gpu_buffer; uint32_t *gpu_buffer_memory; uint32_t *gpu_buffer_memory_alloc; graphic_buffer_t graphic_buffers[3]; + + fence_t current_fence; } surface_t; /** @@ -59,9 +61,15 @@ result_t surface_create(surface_t *surface, uint64_t layer_id, igbp_t igbp); * Using \ref gfx_slow_swizzling_blit is recommended. Call \ref surface_queue_buffer * when you're done rendering to submit it to be displayed. */ -// for now, we ignore fences result_t surface_dequeue_buffer(surface_t *surface, uint32_t **image); +/** + * @brief Wait for any asynchronous operations on the current buffer to complete + * + * @param surface Surface + */ +result_t surface_wait_buffer(surface_t *surface); + /** * @brief Submit the current buffer to be displayed * diff --git a/include/libtransistor/err.h b/include/libtransistor/err.h index c7201598..0e523137 100644 --- a/include/libtransistor/err.h +++ b/include/libtransistor/err.h @@ -78,7 +78,8 @@ extern "C" { #define LIBTRANSISTOR_ERR_SURFACE_DEQUEUE_BUFFER_FAILED LIBTRANSISTOR_RESULT(5007) #define LIBTRANSISTOR_ERR_SURFACE_QUEUE_BUFFER_FAILED LIBTRANSISTOR_RESULT(5008) #define LIBTRANSISTOR_ERR_SURFACE_INVALID_STATE LIBTRANSISTOR_RESULT(5009) - +#define LIBTRANSISTOR_ERR_DISPLAY_INVALID_FENCE LIBTRANSISTOR_RESULT(5010) + // GPU #define LIBTRANSISTOR_ERR_GPU_BUFFER_UNALIGNED LIBTRANSISTOR_RESULT(6001) diff --git a/include/libtransistor/gpu/gpu.h b/include/libtransistor/gpu/gpu.h index 48db4efd..1b4cc7f7 100644 --- a/include/libtransistor/gpu/gpu.h +++ b/include/libtransistor/gpu/gpu.h @@ -22,6 +22,11 @@ typedef struct { uint8_t kind; } gpu_buffer_t; +typedef struct { + uint32_t syncpt_id; + uint32_t syncpt_value; +} gpu_fence_t; + /** * @brief Initialize GPU */ @@ -63,6 +68,14 @@ result_t gpu_buffer_get_id(gpu_buffer_t *gpu_b, uint32_t *id); */ result_t gpu_buffer_initialize_from_id(gpu_buffer_t *gpu_b, uint32_t id); + +/** + * @brief Waits on a fence to complete + * + * @param fence Fence to wait for completion on + */ +result_t gpu_wait_fence(gpu_fence_t *fence, uint32_t timeout); + /** * @brief Finalize GPU */ diff --git a/include/libtransistor/gpu/nv_ioc.h b/include/libtransistor/gpu/nv_ioc.h index 4245e081..c11f3f6b 100644 --- a/include/libtransistor/gpu/nv_ioc.h +++ b/include/libtransistor/gpu/nv_ioc.h @@ -9,20 +9,20 @@ extern "C" { #endif -#define NVHOST_IOCTL_CTRL_SYNCPT_READ 0xC0080014 -#define NVHOST_IOCTL_CTRL_SYNCPT_INCR 0x40040015 -#define NVHOST_IOCTL_CTRL_SYNCPT_WAIT 0xC00C0016 -#define NVHOST_IOCTL_CTRL_MODULE_MUTEX 0x40080017 -#define NVHOST_IOCTL_CTRL_MODULE_REGRDWR 0xC0180018 -#define NVHOST_IOCTL_CTRL_SYNCPT_WAITEX 0xC0100019 -#define NVHOST_IOCTL_CTRL_SYNCPT_READ_MAX 0xC008001A -#define NVHOST_IOCTL_CTRL_GET_CONFIG 0xC183001B -#define NVHOST_IOCTL_CTRL_EVENT_SIGNAL 0xC004001C -#define NVHOST_IOCTL_CTRL_EVENT_WAIT 0xC010001D -#define NVHOST_IOCTL_CTRL_EVENT_WAIT_ASYNC 0xC010001E -#define NVHOST_IOCTL_CTRL_EVENT_REGISTER 0xC004001F -#define NVHOST_IOCTL_CTRL_EVENT_UNREGISTER 0xC0040020 -#define NVHOST_IOCTL_CTRL_EVENT_KILL 0x40080021 +#define NVHOST_IOC_CTRL_SYNCPT_READ 0xC0080014 +#define NVHOST_IOC_CTRL_SYNCPT_INCR 0x40040015 +#define NVHOST_IOC_CTRL_SYNCPT_WAIT 0xC00C0016 +#define NVHOST_IOC_CTRL_MODULE_MUTEX 0x40080017 +#define NVHOST_IOC_CTRL_MODULE_REGRDWR 0xC0180018 +#define NVHOST_IOC_CTRL_SYNCPT_WAITEX 0xC0100019 +#define NVHOST_IOC_CTRL_SYNCPT_READ_MAX 0xC008001A +#define NVHOST_IOC_CTRL_GET_CONFIG 0xC183001B +#define NVHOST_IOC_CTRL_EVENT_SIGNAL 0xC004001C +#define NVHOST_IOC_CTRL_EVENT_WAIT 0xC010001D +#define NVHOST_IOC_CTRL_EVENT_WAIT_ASYNC 0xC010001E +#define NVHOST_IOC_CTRL_EVENT_REGISTER 0xC004001F +#define NVHOST_IOC_CTRL_EVENT_UNREGISTER 0xC0040020 +#define NVHOST_IOC_CTRL_EVENT_KILL 0x40080021 #define NVMAP_IOC_CREATE 0xC0080101 #define NVMAP_IOC_FROM_ID 0xC0080103 @@ -32,15 +32,25 @@ extern "C" { #define NVMAP_IOC_GET_ID 0xC008010E /** - * @struct nvhost_ioctl_ctrl_event_wait_args + * @struct nvhost_ioctl_ctrl_syncpt_wait_args * @brief Arguments to wait on a syncpt */ +typedef struct { + uint32_t syncpt_id; ///< In + uint32_t threshold; ///< In + int32_t timeout; ///< In +} nvhost_ioc_ctrl_syncpt_wait_args; + +/** + * @struct nvhost_ioctl_ctrl_event_wait_args + * @brief Arguments to wait on a syncpt event + */ typedef struct { uint32_t syncpt_id; ///< In uint32_t threshold; ///< In int32_t timeout; ///< In uint32_t value; ///< Inout -} nvhost_ioctl_ctrl_event_wait_args; +} nvhost_ioc_ctrl_event_wait_args; /** * @struct nvmap_ioc_create_args diff --git a/lib/display/graphic_buffer_queue.c b/lib/display/graphic_buffer_queue.c index ca991cd1..6818bf88 100644 --- a/lib/display/graphic_buffer_queue.c +++ b/lib/display/graphic_buffer_queue.c @@ -41,26 +41,7 @@ static result_t queue_buffer_output_unflatten(parcel_t *parcel, queue_buffer_out } static result_t queue_buffer_input_flatten(parcel_t *parcel, queue_buffer_input_t *qbi) { - static uint32_t ts32 = 0x588bbba9; - uint32_t template[] = { - 0x54, 0, // unknown, but always these values - ts32, 0x0, //u64 timestamp - 1, 0, 0, // unknown, but always these values - 0, 0, // sometimes zero - 0, - 0, // also seen 2 - - 0, 0, - - // fence? - 1, 1, 0xa3, 0x0, - -1, 0, -1, 0, -1, 0 - }; - - ts32++; - //ts32+= 0x2e45f00; - - memcpy(parcel_write_inplace(parcel, sizeof(template)), &template, sizeof(template)); + memcpy(parcel_write_inplace(parcel, sizeof(*qbi)), qbi, sizeof(*qbi)); return RESULT_OK; } @@ -127,6 +108,20 @@ static result_t graphic_buffer_flatten(parcel_t *parcel, graphic_buffer_t *gb) { } static result_t fence_unflatten(parcel_t *parcel, fence_t *fence) { + struct { + uint32_t size; + uint32_t num_fds; + } *header; + header = parcel_read_inplace(parcel, sizeof(*header)); + if(header->size != sizeof(fence_t)) { + return LIBTRANSISTOR_ERR_DISPLAY_INVALID_FENCE; + } + if(header->num_fds != 0) { + return LIBTRANSISTOR_ERR_DISPLAY_FENCE_TOO_MANY_FDS; + } + for(size_t i = 0; i < ARRAY_LENGTH(fence->sync); i++) { + fence->sync[i].syncpt_id = 0xffffffff; + } memcpy(fence, parcel_read_inplace(parcel, sizeof(*fence)), sizeof(*fence)); return RESULT_OK; } @@ -182,11 +177,17 @@ result_t igbp_dequeue_buffer(igbp_t *igbp, uint32_t width, uint32_t height, pixe } *slot = parcel_read_u32(&response); - if((r = fence_unflatten(&response, fence)) != RESULT_OK) { - return r; + + int32_t has_fence = parcel_read_u32(&response); + if(has_fence) { + if((r = fence_unflatten(&response, fence)) != RESULT_OK) { + return r; + } + } else { + fence->is_valid = false; } *status = parcel_read_u32(&response); - + return RESULT_OK; } diff --git a/lib/display/surface.c b/lib/display/surface.c index 56b251c8..10b3e69f 100644 --- a/lib/display/surface.c +++ b/lib/display/surface.c @@ -104,12 +104,11 @@ result_t surface_dequeue_buffer(surface_t *surface, uint32_t **image) { } int status; - fence_t fence; if((r = igbp_dequeue_buffer( &surface->igbp, 1280, 720, 1, 0xb00, false, &status, &surface->current_slot, - &fence, NULL)) != RESULT_OK) { + &surface->current_fence, NULL)) != RESULT_OK) { return r; } @@ -144,11 +143,31 @@ result_t surface_queue_buffer(surface_t *surface) { if(surface->state != SURFACE_STATE_DEQUEUED) { return LIBTRANSISTOR_ERR_SURFACE_INVALID_STATE; } + + queue_buffer_input_t qbi; + qbi.size = sizeof(qbi) - 0x8; + qbi.num_fds = 0; + qbi.timestamp = 0; + qbi.is_auto_timestamp = 0; + qbi.crop.left = 0; + qbi.crop.top = 0; + qbi.crop.right = 0; + qbi.crop.bottom = 0; + qbi.scaling_mode = 0; + qbi.transform = 0; + qbi.sticky_transform = 0; + qbi.unknown[0] = 0; + qbi.unknown[1] = 1; + qbi.fence.is_valid = 1; + for(size_t i = 0; i < ARRAY_LENGTH(qbi.fence.sync); i++) { + qbi.fence.sync[i].syncpt_id = 0xffffffff; + qbi.fence.sync[i].syncpt_value = 0; + } queue_buffer_output_t qbo; int status; if((r = igbp_queue_buffer(&surface->igbp, surface->current_slot, - NULL, &qbo, &status)) != RESULT_OK) { + &qbi, &qbo, &status)) != RESULT_OK) { return r; } @@ -160,3 +179,26 @@ result_t surface_queue_buffer(surface_t *surface) { return RESULT_OK; } + +result_t surface_wait_buffer(surface_t *surface) { + if(surface->state != SURFACE_STATE_DEQUEUED) { + return LIBTRANSISTOR_ERR_SURFACE_INVALID_STATE; + } + + if(!surface->current_fence.is_valid) { + return RESULT_OK; + } + + for(size_t i = 0; i < ARRAY_LENGTH(surface->current_fence.sync); i++) { + result_t r; + gpu_fence_t *f = &surface->current_fence.sync[i]; + if(f->syncpt_id == 0xffffffff) { + continue; + } + if((r = gpu_wait_fence(f, -1)) != RESULT_OK) { + return r; + } + } + + return RESULT_OK; +} diff --git a/lib/gpu/gpu.c b/lib/gpu/gpu.c index 36fdf05f..ae8f5aae 100644 --- a/lib/gpu/gpu.c +++ b/lib/gpu/gpu.c @@ -7,6 +7,7 @@ static int nvas_fd; static int nvmap_fd; +static int nvhost_ctrl_fd; static int gpu_initializations = 0; result_t gpu_initialize() { @@ -29,8 +30,15 @@ result_t gpu_initialize() { goto fail_nvas; } + if((nvhost_ctrl_fd = nv_open("/dev/nvhost-ctrl")) < 0) { + r = nv_result; + goto fail_nvmap; + } + return RESULT_OK; +fail_nvhost_ctrl: + nv_close(nvhost_ctrl_fd); fail_nvmap: nv_close(nvmap_fd); fail_nvas: @@ -140,7 +148,23 @@ result_t gpu_buffer_initialize_from_id(gpu_buffer_t *gpu_b, uint32_t id) { return RESULT_OK; } +result_t gpu_wait_fence(gpu_fence_t *fence, uint32_t timeout) { + INITIALIZATION_GUARD(gpu); + + nvhost_ioc_ctrl_syncpt_wait_args nvh_wait; + nvh_wait.syncpt_id = fence->syncpt_id; + nvh_wait.threshold = fence->syncpt_value; + nvh_wait.timeout = timeout; + + if(nv_ioctl(nvhost_ctrl_fd, NVHOST_IOC_CTRL_SYNCPT_WAIT, &nvh_wait, sizeof(nvh_wait)) != 0) { + return nv_result; + } + + return RESULT_OK; +} + static void gpu_force_finalize() { + nv_close(nvhost_ctrl_fd); nv_close(nvmap_fd); nv_close(nvas_fd); nv_finalize(); diff --git a/test/test_display.c b/test/test_display.c index 399698ed..4d3f5a7c 100644 --- a/test/test_display.c +++ b/test/test_display.c @@ -38,9 +38,12 @@ int main() { printf("begin frame %d\n", i); uint32_t *out_buffer; ASSERT_OK(fail_display_event, surface_dequeue_buffer(&surf, &out_buffer)); - + if(i < 300) { + ASSERT_OK(fail_display_event, surface_wait_buffer(&surf)); + } + for(size_t p = 0; p < (0x3c0000/sizeof(uint32_t)); p++) { - out_buffer[p] = 0xFF0000FF; + out_buffer[p] = (i < 300) ? 0xFFFF0000 : 0xFF0000FF; } int x = (cos((double) i * 6.28 / 60.0) * 300.0 + 350.0); int y = (sin((double) i * 6.28 / 60.0) * 300.0 + 350.0);