Path: blob/21.2-virgl/src/gallium/frontends/nine/nine_queue.c
4561 views
/*1* Copyright 2016 Patrick Rudolph <[email protected]>2*3* Permission is hereby granted, free of charge, to any person obtaining a4* copy of this software and associated documentation files (the "Software"),5* to deal in the Software without restriction, including without limitation6* on the rights to use, copy, modify, merge, publish, distribute, sub7* license, and/or sell copies of the Software, and to permit persons to whom8* the Software is furnished to do so, subject to the following conditions:9*10* The above copyright notice and this permission notice (including the next11* paragraph) shall be included in all copies or substantial portions of the12* Software.13*14* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR15* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,16* FITNESS FOR A PARTICULAR PURPOSE AND NON-INFRINGEMENT. IN NO EVENT SHALL17* THE AUTHOR(S) AND/OR THEIR SUPPLIERS BE LIABLE FOR ANY CLAIM,18* DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR19* OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE20* USE OR OTHER DEALINGS IN THE SOFTWARE. */2122#include "nine_queue.h"23#include "os/os_thread.h"24#include "util/macros.h"25#include "nine_helpers.h"2627#define NINE_CMD_BUF_INSTR (256)2829#define NINE_CMD_BUFS (32)30#define NINE_CMD_BUFS_MASK (NINE_CMD_BUFS - 1)3132#define NINE_QUEUE_SIZE (8192 * 16 + 128)3334#define DBG_CHANNEL DBG_DEVICE3536/*37* Single producer - single consumer pool queue38*39* Producer:40* Calls nine_queue_alloc to get a slice of memory in current cmdbuf.41* Calls nine_queue_flush to flush the queue by request.42* The queue is flushed automatically on insufficient space or once the43* cmdbuf contains NINE_CMD_BUF_INSTR instructions.44*45* nine_queue_flush does block, while nine_queue_alloc doesn't block.46*47* nine_queue_alloc returns NULL on insufficent space.48*49* Consumer:50* Calls nine_queue_wait_flush to wait for a cmdbuf.51* After waiting for a cmdbuf it calls nine_queue_get until NULL is returned.52*53* nine_queue_wait_flush does block, while nine_queue_get doesn't block.54*55* Constrains:56* Only a single consumer and a single producer are supported.57*58*/5960struct nine_cmdbuf {61unsigned instr_size[NINE_CMD_BUF_INSTR];62unsigned num_instr;63unsigned offset;64void *mem_pool;65BOOL full;66};6768struct nine_queue_pool {69struct nine_cmdbuf pool[NINE_CMD_BUFS];70unsigned head;71unsigned tail;72unsigned cur_instr;73BOOL worker_wait;74cnd_t event_pop;75cnd_t event_push;76mtx_t mutex_pop;77mtx_t mutex_push;78};7980/* Consumer functions: */81void82nine_queue_wait_flush(struct nine_queue_pool* ctx)83{84struct nine_cmdbuf *cmdbuf = &ctx->pool[ctx->tail];8586/* wait for cmdbuf full */87mtx_lock(&ctx->mutex_push);88while (!cmdbuf->full)89{90DBG("waiting for full cmdbuf\n");91cnd_wait(&ctx->event_push, &ctx->mutex_push);92}93DBG("got cmdbuf=%p\n", cmdbuf);94mtx_unlock(&ctx->mutex_push);9596cmdbuf->offset = 0;97ctx->cur_instr = 0;98}99100/* Gets a pointer to the next memory slice.101* Does not block.102* Returns NULL on empty cmdbuf. */103void *104nine_queue_get(struct nine_queue_pool* ctx)105{106struct nine_cmdbuf *cmdbuf = &ctx->pool[ctx->tail];107unsigned offset;108109/* At this pointer there's always a cmdbuf. */110111if (ctx->cur_instr == cmdbuf->num_instr) {112/* signal waiting producer */113mtx_lock(&ctx->mutex_pop);114DBG("freeing cmdbuf=%p\n", cmdbuf);115cmdbuf->full = 0;116cnd_signal(&ctx->event_pop);117mtx_unlock(&ctx->mutex_pop);118119ctx->tail = (ctx->tail + 1) & NINE_CMD_BUFS_MASK;120121return NULL;122}123124/* At this pointer there's always a cmdbuf with instruction to process. */125offset = cmdbuf->offset;126cmdbuf->offset += cmdbuf->instr_size[ctx->cur_instr];127ctx->cur_instr ++;128129return cmdbuf->mem_pool + offset;130}131132/* Producer functions: */133134/* Flushes the queue.135* Moves the current cmdbuf to worker thread.136* Blocks until next cmdbuf is free. */137void138nine_queue_flush(struct nine_queue_pool* ctx)139{140struct nine_cmdbuf *cmdbuf = &ctx->pool[ctx->head];141142DBG("flushing cmdbuf=%p instr=%d size=%d\n",143cmdbuf, cmdbuf->num_instr, cmdbuf->offset);144145/* Nothing to flush */146if (!cmdbuf->num_instr)147return;148149/* signal waiting worker */150mtx_lock(&ctx->mutex_push);151cmdbuf->full = 1;152cnd_signal(&ctx->event_push);153mtx_unlock(&ctx->mutex_push);154155ctx->head = (ctx->head + 1) & NINE_CMD_BUFS_MASK;156157cmdbuf = &ctx->pool[ctx->head];158159/* wait for queue empty */160mtx_lock(&ctx->mutex_pop);161while (cmdbuf->full)162{163DBG("waiting for empty cmdbuf\n");164cnd_wait(&ctx->event_pop, &ctx->mutex_pop);165}166DBG("got empty cmdbuf=%p\n", cmdbuf);167mtx_unlock(&ctx->mutex_pop);168cmdbuf->offset = 0;169cmdbuf->num_instr = 0;170}171172/* Gets a a pointer to slice of memory with size @space.173* Does block if queue is full.174* Returns NULL on @space > NINE_QUEUE_SIZE. */175void *176nine_queue_alloc(struct nine_queue_pool* ctx, unsigned space)177{178unsigned offset;179struct nine_cmdbuf *cmdbuf = &ctx->pool[ctx->head];180181if (space > NINE_QUEUE_SIZE)182return NULL;183184/* at this pointer there's always a free queue available */185186if ((cmdbuf->offset + space > NINE_QUEUE_SIZE) ||187(cmdbuf->num_instr == NINE_CMD_BUF_INSTR)) {188189nine_queue_flush(ctx);190191cmdbuf = &ctx->pool[ctx->head];192}193194DBG("cmdbuf=%p space=%d\n", cmdbuf, space);195196/* at this pointer there's always a free queue with sufficient space available */197198offset = cmdbuf->offset;199cmdbuf->offset += space;200cmdbuf->instr_size[cmdbuf->num_instr] = space;201cmdbuf->num_instr ++;202203return cmdbuf->mem_pool + offset;204}205206/* Returns the current queue flush state.207* TRUE nothing flushed208* FALSE one ore more instructions queued flushed. */209bool210nine_queue_no_flushed_work(struct nine_queue_pool* ctx)211{212return (ctx->tail == ctx->head);213}214215/* Returns the current queue empty state.216* TRUE no instructions queued.217* FALSE one ore more instructions queued. */218bool219nine_queue_isempty(struct nine_queue_pool* ctx)220{221struct nine_cmdbuf *cmdbuf = &ctx->pool[ctx->head];222223return (ctx->tail == ctx->head) && !cmdbuf->num_instr;224}225226struct nine_queue_pool*227nine_queue_create(void)228{229unsigned i;230struct nine_queue_pool *ctx;231232ctx = CALLOC_STRUCT(nine_queue_pool);233if (!ctx)234goto failed;235236for (i = 0; i < NINE_CMD_BUFS; i++) {237ctx->pool[i].mem_pool = MALLOC(NINE_QUEUE_SIZE);238if (!ctx->pool[i].mem_pool)239goto failed;240}241242cnd_init(&ctx->event_pop);243(void) mtx_init(&ctx->mutex_pop, mtx_plain);244245cnd_init(&ctx->event_push);246(void) mtx_init(&ctx->mutex_push, mtx_plain);247248/* Block until first cmdbuf has been flushed. */249ctx->worker_wait = TRUE;250251return ctx;252failed:253if (ctx) {254for (i = 0; i < NINE_CMD_BUFS; i++) {255if (ctx->pool[i].mem_pool)256FREE(ctx->pool[i].mem_pool);257}258FREE(ctx);259}260return NULL;261}262263void264nine_queue_delete(struct nine_queue_pool *ctx)265{266unsigned i;267268mtx_destroy(&ctx->mutex_pop);269cnd_destroy(&ctx->event_pop);270271mtx_destroy(&ctx->mutex_push);272cnd_destroy(&ctx->event_push);273274for (i = 0; i < NINE_CMD_BUFS; i++)275FREE(ctx->pool[i].mem_pool);276277FREE(ctx);278}279280281