Exemplo n.º 1
0
/** Start building context buffer.
 * Subsequent etna_reserve and other state setting commands will go to
 * the context buffer instead of the command buffer.
 * initial_pipe is the pipe as it has to be at the beginning of the context
 * buffer.
 *
 * @return ETNA_OK if succesful (can start building context)
 *         or an error code otherwise.
 */
static int gpu_context_build_start(struct etna_ctx *ctx)
{
    if(ctx->cur_buf == ETNA_CTX_BUFFER)
        return ETNA_INTERNAL_ERROR;
    /* Save current buffer id and position */
    ctx->cmdbuf[ctx->cur_buf]->offset = ctx->offset * 4;
    ctx->stored_buf = ctx->cur_buf;

    /* Switch to context buffer */
    ctx->cur_buf = ETNA_CTX_BUFFER;
    ctx->buf = GCCTX(ctx)->logical;
    ctx->offset = GCCTX(ctx)->bufferSize / 4;

    return ETNA_OK;
}
Exemplo n.º 2
0
int etna_set_pipe(struct etna_ctx *ctx, enum etna_pipe pipe)
{
    int status;
    if(ctx == NULL)
        return ETNA_INVALID_ADDR;

    if((status = etna_reserve(ctx, 2)) != ETNA_OK)
        return status;
    ETNA_EMIT_LOAD_STATE(ctx, VIVS_GL_FLUSH_CACHE>>2, 1, 0);
    switch(pipe)
    {
    case ETNA_PIPE_2D: ETNA_EMIT(ctx, VIVS_GL_FLUSH_CACHE_PE2D); break;
    case ETNA_PIPE_3D: ETNA_EMIT(ctx, VIVS_GL_FLUSH_CACHE_DEPTH | VIVS_GL_FLUSH_CACHE_COLOR); break;
    default: return ETNA_INVALID_VALUE;
    }

    etna_stall(ctx, SYNC_RECIPIENT_FE, SYNC_RECIPIENT_PE);

    if((status = etna_reserve(ctx, 2)) != ETNA_OK)
        return status;
    ETNA_EMIT_LOAD_STATE(ctx, VIVS_GL_PIPE_SELECT>>2, 1, 0);
    ETNA_EMIT(ctx, pipe);

#ifdef GCABI_HAS_CONTEXT
    if(ctx->cur_buf != ETNA_CTX_BUFFER)
    {
        GCCTX(ctx)->currentPipe = pipe;
    }
#endif
    return ETNA_OK;
}
Exemplo n.º 3
0
/** Finish building context buffer.
 * final_pipe is the current pipe at the end of the context buffer.
 */
static int gpu_context_build_end(struct etna_ctx *ctx, enum etna_pipe final_pipe)
{
    if(ctx->cur_buf != ETNA_CTX_BUFFER)
        return ETNA_INTERNAL_ERROR;
    /* If closing pipe of context is different from entry pipe, add a switch
     * command, as we want the context to end in the entry pipe. The kernel
     * will handle switching to the entry pipe only when this is a new context.
     */
    if(final_pipe != GCCTX(ctx)->entryPipe)
    {
        etna_set_pipe(ctx, GCCTX(ctx)->entryPipe);
    }
    /* Set current size -- finishing up context before flush
     * will add space for a LINK command and inUse flag.
     */
    GCCTX(ctx)->bufferSize = ctx->offset * 4;

    /* Switch back to stored buffer */
    ctx->cur_buf = ctx->stored_buf;
    ctx->buf = VIV_TO_PTR(ctx->cmdbuf[ctx->cur_buf]->logical);
    ctx->offset = ctx->cmdbuf[ctx->cur_buf]->offset / 4;
    return ETNA_OK;
}
Exemplo n.º 4
0
/* Clear GPU context, to rebuild it for next flush */
static int gpu_context_clear(struct etna_ctx *ctx)
{
    /* If context was used, queue free it and allocate new buffer to prevent
     * overwriting it while being used by the GPU.  Otherwise we can just
     * re-use it.
     */
    int rv;
#ifdef DEBUG
    fprintf(stderr, "gpu_context_clear (context %i)\n", (int)GCCTX(ctx)->id);
#endif
    if(GCCTX(ctx)->inUse != NULL &&
       *GCCTX(ctx)->inUse)
    {
#ifdef DEBUG
        fprintf(stderr, "gpu_context_clear: context was in use, deferred freeing and reallocating it\n");
#endif
        if((rv = etna_bo_del(ctx->conn, ctx->ctx_bo, ctx->queue)) != ETNA_OK)
        {
            return rv;
        }
        if((ctx->ctx_bo = etna_bo_new(ctx->conn, COMMAND_BUFFER_SIZE, DRM_ETNA_GEM_TYPE_CMD)) == NULL)
        {
            return ETNA_OUT_OF_MEMORY;
        }
    }
    /* Leave space at beginning of buffer for PIPE switch */
    GCCTX(ctx)->bufferSize = BEGIN_COMMIT_CLEARANCE;
    GCCTX(ctx)->logical = etna_bo_map(ctx->ctx_bo);
#ifdef GCABI_CONTEXT_HAS_PHYSICAL
    GCCTX(ctx)->bytes = etna_bo_size(ctx->ctx_bo); /* actual size of buffer */
    GCCTX(ctx)->physical = HANDLE_TO_VIV(etna_bo_gpu_address(ctx->ctx_bo));
#endif
    /* When context is empty, initial pipe should default to entry pipe so that
     * no pipe switch is needed within the context and the kernel does the
     * right thing.
     */
    GCCTX(ctx)->initialPipe = GCCTX(ctx)->entryPipe;
    return ETNA_OK;
}
Exemplo n.º 5
0
/* Clear GPU context, to rebuild it for next flush */
static int gpu_context_clear(struct etna_ctx *ctx)
{
    /* If context was used, queue free it and allocate new buffer to prevent
     * overwriting it while being used by the GPU.  Otherwise we can just
     * re-use it.
     */
    int rv;
#ifdef DEBUG
    printf("gpu_context_clear (context %i)\n", (int)GCCTX(ctx)->id);
#endif
    if(GCCTX(ctx)->inUse != NULL &&
       *GCCTX(ctx)->inUse)
    {
#ifdef DEBUG
        printf("gpu_context_clear: context was in use, deferred freeing and reallocating it\n");
#endif
        if((rv = gpu_context_free_buffer(ctx, &ctx->ctx_info, true)) != ETNA_OK)
        {
            return rv;
        }
        if((rv = gpu_context_allocate_buffer(ctx, &ctx->ctx_info)) != ETNA_OK)
        {
            return rv;
        }
    }
    /* Leave space at beginning of buffer for PIPE switch */
    GCCTX(ctx)->bufferSize = BEGIN_COMMIT_CLEARANCE;
    GCCTX(ctx)->logical = ctx->ctx_info.logical;
#ifdef GCABI_CONTEXT_HAS_PHYSICAL
    GCCTX(ctx)->bytes = ctx->ctx_info.bytes; /* actual size of buffer */
    GCCTX(ctx)->physical = HANDLE_TO_VIV(ctx->ctx_info.physical);
#endif
    /* When context is empty, initial pipe should default to entry pipe so that
     * no pipe switch is needed within the context and the kernel does the
     * right thing.
     */
    GCCTX(ctx)->initialPipe = GCCTX(ctx)->entryPipe;
    return ETNA_OK;
}
Exemplo n.º 6
0
/** Finish up GPU context, make it ready for submission to kernel.
   Append space for LINK and inUse flag.
 */
static int gpu_context_finish_up(struct etna_ctx *ctx)
{
    uint32_t *logical = GCCTX(ctx)->logical;
    uint32_t ptr = GCCTX(ctx)->bufferSize/4;
    /* Append LINK (8 bytes) */
    GCCTX(ctx)->link = &logical[ptr];
    logical[ptr++] = VIV_FE_NOP_HEADER_OP_NOP;
    logical[ptr++] = VIV_FE_NOP_HEADER_OP_NOP;
    /* Append inUse (4 bytes) */
    GCCTX(ctx)->inUse = (int*)&logical[ptr];
    logical[ptr++] = 0;
    /* Update buffer size to final value */
    GCCTX(ctx)->bufferSize = ptr*4;
#ifdef DEBUG
    fprintf(stderr, "gpu_context_finish_up: bufferSize %i link %p inUse %p\n",
            (int)GCCTX(ctx)->bufferSize, GCCTX(ctx)->link, GCCTX(ctx)->inUse);
#endif
    return ETNA_OK;
}
Exemplo n.º 7
0
int etna_flush(struct etna_ctx *ctx, uint32_t *fence_out)
{
    int status = ETNA_OK;
    if(ctx == NULL)
        return ETNA_INVALID_ADDR;
    if(ctx->cur_buf == ETNA_CTX_BUFFER)
        /* Can never flush while building context buffer */
        return ETNA_INTERNAL_ERROR;

    if(fence_out) /* is a fence handle requested? */
    {
        uint32_t fence;
        int signal;
        /* Need to lock the fence mutex to make sure submits are ordered by
         * fence number.
         */
        pthread_mutex_lock(&ctx->conn->fence_mutex);
        do {
            /*   Get next fence ID */
            if((status = _viv_fence_new(ctx->conn, &fence, &signal)) != VIV_STATUS_OK)
            {
                fprintf(stderr, "%s: could not request fence\n", __func__);
                goto unlock_and_return_status;
            }
        } while(fence == 0); /* don't return fence handle 0 as it is interpreted as error value downstream */
        /*   Queue the signal. This can call in turn call this function (but
         * without fence) if the queue was full, so we should be able to handle
         * that. In that case, we will exit from this function with only
         * this fence in the queue and an empty command buffer.
         */
        if((status = etna_queue_signal(ctx->queue, signal, VIV_WHERE_PIXEL)) != ETNA_OK)
        {
            fprintf(stderr, "%s: error %i queueing fence signal %i\n", __func__, status, signal);
            goto unlock_and_return_status;
        }
        *fence_out = fence;
    }
    /***** Start fence mutex locked */
    /* Make sure to unlock the mutex before returning */
    struct _gcsQUEUE *queue_first = _etna_queue_first(ctx->queue);
    gcoCMDBUF cur_buf = (ctx->cur_buf != ETNA_NO_BUFFER) ? ctx->cmdbuf[ctx->cur_buf] : NULL;

    if(cur_buf == NULL || (ctx->offset*4 <= (cur_buf->startOffset + BEGIN_COMMIT_CLEARANCE)))
    {
        /* Nothing in command buffer; but if we end up here there may be kernel commands to submit. Do this seperately. */
        if(queue_first != NULL)
        {
            ctx->flushes = 0;
            if((status = viv_event_commit(ctx->conn, queue_first)) != 0)
            {
#ifdef DEBUG
                fprintf(stderr, "Error committing kernel commands\n");
#endif
                goto unlock_and_return_status;
            }
            if(fence_out) /* mark fence as submitted to kernel */
                _viv_fence_mark_pending(ctx->conn, *fence_out);
        }
        goto unlock_and_return_status;
    }

    cur_buf->offset = ctx->offset*4; /* Copy over current end offset into CMDBUF, for kernel */
#ifdef DEBUG
    fprintf(stderr, "Committing command buffer %i startOffset=%x offset=%x\n", ctx->cur_buf,
            cur_buf->startOffset, ctx->offset*4);
#endif
#ifdef DEBUG_CMDBUF
    etna_dump_cmd_buffer(ctx);
#endif
#ifdef GCABI_HAS_CONTEXT
    gpu_context_finish_up(ctx);
#endif
    if(!queue_first)
        ctx->flushes += 1;
    else
        ctx->flushes = 0;
    if((status = viv_commit(ctx->conn, cur_buf, ctx->ctx, queue_first)) != 0)
    {
#ifdef DEBUG
        fprintf(stderr, "Error committing command buffer\n");
#endif
        goto unlock_and_return_status;
    }
    if(fence_out)
    {
        _viv_fence_mark_pending(ctx->conn, *fence_out);
        pthread_mutex_unlock(&ctx->conn->fence_mutex);
    }
    /***** End fence mutex locked */
#ifdef GCABI_HAS_CONTEXT
    /* set context entryPipe to currentPipe (next commit will start with current pipe) */
    GCCTX(ctx)->entryPipe = GCCTX(ctx)->currentPipe;
    gpu_context_clear(ctx);
    if(ctx->ctx_cb)
    {
        enum etna_pipe initial_pipe, final_pipe;
        /* Start building GPU context */
        if((status = gpu_context_build_start(ctx)) != ETNA_OK)
        {
            fprintf(stderr, "%s: gpu_context_build_start failed with status %i\n", __func__, status);
            return status;
        }
        if((status = ctx->ctx_cb(ctx->ctx_cb_data, ctx, &initial_pipe, &final_pipe)) != ETNA_OK)
        {
            fprintf(stderr, "%s: Context callback failed with status %i\n", __func__, status);
            return status;
        }
        /* Set initial pipe in context */
        GCCTX(ctx)->initialPipe = initial_pipe;
        /* Finish building GPU context */
        if((status = gpu_context_build_end(ctx, final_pipe)) != ETNA_OK)
        {
            fprintf(stderr, "%s: gpu_context_build_end failed with status %i\n", __func__, status);
            return status;
        }
    }
#endif
    cur_buf->startOffset = cur_buf->offset + END_COMMIT_CLEARANCE;
    cur_buf->offset = cur_buf->startOffset + BEGIN_COMMIT_CLEARANCE;

    if((cur_buf->offset + END_COMMIT_CLEARANCE) >= COMMAND_BUFFER_SIZE ||
       ctx->flushes > ETNA_MAX_UNSIGNALED_FLUSHES)
    {
        /* nothing more fits in buffer, prevent warning about buffer overflow
           on next etna_reserve.
         */
        cur_buf->startOffset = cur_buf->offset = COMMAND_BUFFER_SIZE - END_COMMIT_CLEARANCE;
    }

    /* Set writing offset for next etna_reserve. For convenience this is
       stored as an index instead of a byte offset.  */
    ctx->offset = cur_buf->offset / 4;
#ifdef DEBUG
#ifdef GCABI_HAS_CONTEXT
    fprintf(stderr, "  New start offset: %x New offset: %x Contextbuffer used: %i\n", cur_buf->startOffset, cur_buf->offset, *(GCCTX(ctx)->inUse));
#else
    fprintf(stderr, "  New start offset: %x New offset: %x\n", cur_buf->startOffset, cur_buf->offset);
#endif
#endif
    return ETNA_OK;

unlock_and_return_status: /* Unlock fence mutex (if necessary) and return status */
    if(fence_out)
        pthread_mutex_unlock(&ctx->conn->fence_mutex);
    return status;
}
Exemplo n.º 8
0
int etna_flush(struct etna_ctx *ctx)
{
    int status;
    if(ctx == NULL)
        return ETNA_INVALID_ADDR;
    if(ctx->cur_buf == ETNA_CTX_BUFFER)
        /* Can never flush while building context buffer */
        return ETNA_INTERNAL_ERROR;
    struct _gcsQUEUE *queue_first = etna_queue_first(ctx->queue);
    if(ctx->cur_buf == ETNA_NO_BUFFER)
        goto nothing_to_do;
    gcoCMDBUF cur_buf = ctx->cmdbuf[ctx->cur_buf];

    ETNA_ALIGN(ctx); /* make sure end of submitted command buffer end is aligned */
#ifdef DEBUG
    printf("Committing command buffer %i startOffset=%x offset=%x\n", ctx->cur_buf,
            cur_buf->startOffset, ctx->offset*4);
#endif
    if(ctx->offset*4 <= (cur_buf->startOffset + BEGIN_COMMIT_CLEARANCE))
        goto nothing_to_do;
    cur_buf->offset = ctx->offset*4; /* Copy over current ending offset into CMDBUF, for kernel */
#ifdef DEBUG_CMDBUF
    etna_dump_cmd_buffer(ctx);
#endif
#ifdef GCABI_HAS_CONTEXT
    gpu_context_finish_up(ctx);
#endif
    if(!queue_first)
        ctx->flushes += 1;
    else
        ctx->flushes = 0;
    if((status = viv_commit(ctx->conn, cur_buf, ctx->ctx, queue_first)) != 0)
    {
#ifdef DEBUG
        fprintf(stderr, "Error committing command buffer\n");
#endif
        return status;
    }
    if((status = etna_queue_clear(ctx->queue)) != ETNA_OK)
        return status;
#ifdef GCABI_HAS_CONTEXT
    /* set context entryPipe to currentPipe (next commit will start with current pipe) */
    GCCTX(ctx)->entryPipe = GCCTX(ctx)->currentPipe;
    gpu_context_clear(ctx);
    if(ctx->ctx_cb)
    {
        enum etna_pipe initial_pipe, final_pipe;
        /* Start building GPU context */
        if((status = gpu_context_build_start(ctx)) != ETNA_OK)
        {
            printf("%s: gpu_context_build_start failed with status %i\n", __func__, status);
            return status;
        }
        if((status = ctx->ctx_cb(ctx->ctx_cb_data, ctx, &initial_pipe, &final_pipe)) != ETNA_OK)
        {
            printf("%s: Context callback failed with status %i\n", __func__, status);
            return status;
        }
        /* Set initial pipe in context */
        GCCTX(ctx)->initialPipe = initial_pipe;
        /* Finish building GPU context */
        if((status = gpu_context_build_end(ctx, final_pipe)) != ETNA_OK)
        {
            printf("%s: gpu_context_build_end failed with status %i\n", __func__, status);
            return status;
        }
    }
#endif
    cur_buf->startOffset = cur_buf->offset + END_COMMIT_CLEARANCE;
    cur_buf->offset = cur_buf->startOffset + BEGIN_COMMIT_CLEARANCE;

    if((cur_buf->offset + END_COMMIT_CLEARANCE) >= COMMAND_BUFFER_SIZE ||
       ctx->flushes > ETNA_MAX_UNSIGNALED_FLUSHES)
    {
        /* nothing more fits in buffer, prevent warning about buffer overflow
           on next etna_reserve.
         */
        cur_buf->startOffset = cur_buf->offset = COMMAND_BUFFER_SIZE - END_COMMIT_CLEARANCE;
    }

    /* Set writing offset for next etna_reserve. For convenience this is
       stored as an index instead of a byte offset.  */
    ctx->offset = cur_buf->offset / 4;
#ifdef DEBUG
#ifdef GCABI_HAS_CONTEXT
    printf("  New start offset: %x New offset: %x Contextbuffer used: %i\n", cur_buf->startOffset, cur_buf->offset, *(GCCTX(ctx)->inUse));
#else
    printf("  New start offset: %x New offset: %x\n", cur_buf->startOffset, cur_buf->offset);
#endif
#endif
    return ETNA_OK;
nothing_to_do:
    /* Nothing in command buffer; but there may be kernel commands to submit. Do this seperately. */
    if(queue_first != NULL)
    {
        ctx->flushes = 0;
        if((status = viv_event_commit(ctx->conn, queue_first)) != 0)
        {
#ifdef DEBUG
            fprintf(stderr, "Error committing kernel commands\n");
#endif
            return status;
        }
        if((status = etna_queue_clear(ctx->queue)) != ETNA_OK)
            return status;
    }
    return ETNA_OK;
}