Home | History | Annotate | Line # | Download | only in vulkan
      1 /*
      2  * Copyright 2019 Google LLC
      3  * SPDX-License-Identifier: MIT
      4  */
      5 
      6 #include "vn_cs.h"
      7 
      8 #include "vn_instance.h"
      9 #include "vn_renderer.h"
     10 
     11 static void
     12 vn_cs_encoder_sanity_check(struct vn_cs_encoder *enc)
     13 {
     14    assert(enc->buffer_count <= enc->buffer_max);
     15 
     16    size_t total_committed_size = 0;
     17    for (uint32_t i = 0; i < enc->buffer_count; i++)
     18       total_committed_size += enc->buffers[i].committed_size;
     19    assert(enc->total_committed_size == total_committed_size);
     20 
     21    if (enc->buffer_count) {
     22       const struct vn_cs_encoder_buffer *cur_buf =
     23          &enc->buffers[enc->buffer_count - 1];
     24       assert(cur_buf->base <= enc->cur && enc->cur <= enc->end &&
     25              enc->end <= cur_buf->base + enc->current_buffer_size);
     26       if (cur_buf->committed_size)
     27          assert(enc->cur == enc->end);
     28    } else {
     29       assert(!enc->current_buffer_size);
     30       assert(!enc->cur && !enc->end);
     31    }
     32 }
     33 
     34 static void
     35 vn_cs_encoder_add_buffer(struct vn_cs_encoder *enc,
     36                          struct vn_renderer_shmem *shmem,
     37                          size_t offset,
     38                          void *base,
     39                          size_t size)
     40 {
     41    /* add a buffer and make it current */
     42    assert(enc->buffer_count < enc->buffer_max);
     43    struct vn_cs_encoder_buffer *cur_buf = &enc->buffers[enc->buffer_count++];
     44    /* shmem ownership transferred */
     45    cur_buf->shmem = shmem;
     46    cur_buf->offset = offset;
     47    cur_buf->base = base;
     48    cur_buf->committed_size = 0;
     49 
     50    /* update the write pointer */
     51    enc->cur = base;
     52    enc->end = base + size;
     53 }
     54 
     55 static void
     56 vn_cs_encoder_commit_buffer(struct vn_cs_encoder *enc)
     57 {
     58    assert(enc->buffer_count);
     59    struct vn_cs_encoder_buffer *cur_buf =
     60       &enc->buffers[enc->buffer_count - 1];
     61    const size_t written_size = enc->cur - cur_buf->base;
     62    if (cur_buf->committed_size) {
     63       assert(cur_buf->committed_size == written_size);
     64    } else {
     65       cur_buf->committed_size = written_size;
     66       enc->total_committed_size += written_size;
     67    }
     68 }
     69 
     70 static void
     71 vn_cs_encoder_gc_buffers(struct vn_cs_encoder *enc)
     72 {
     73    /* free all but the current buffer */
     74    assert(enc->buffer_count);
     75    struct vn_cs_encoder_buffer *cur_buf =
     76       &enc->buffers[enc->buffer_count - 1];
     77    for (uint32_t i = 0; i < enc->buffer_count - 1; i++)
     78       vn_renderer_shmem_unref(enc->instance->renderer, enc->buffers[i].shmem);
     79 
     80    /* move the current buffer to the beginning, skipping the used part */
     81    const size_t used = cur_buf->offset + cur_buf->committed_size;
     82    enc->buffer_count = 0;
     83    vn_cs_encoder_add_buffer(enc, cur_buf->shmem, used,
     84                             cur_buf->base + cur_buf->committed_size,
     85                             enc->current_buffer_size - used);
     86 
     87    enc->total_committed_size = 0;
     88 }
     89 
     90 void
     91 vn_cs_encoder_init_indirect(struct vn_cs_encoder *enc,
     92                             struct vn_instance *instance,
     93                             size_t min_size)
     94 {
     95    memset(enc, 0, sizeof(*enc));
     96    enc->instance = instance;
     97    enc->min_buffer_size = min_size;
     98    enc->indirect = true;
     99 }
    100 
    101 void
    102 vn_cs_encoder_fini(struct vn_cs_encoder *enc)
    103 {
    104    if (unlikely(!enc->indirect))
    105       return;
    106 
    107    for (uint32_t i = 0; i < enc->buffer_count; i++)
    108       vn_renderer_shmem_unref(enc->instance->renderer, enc->buffers[i].shmem);
    109    if (enc->buffers)
    110       free(enc->buffers);
    111 }
    112 
    113 /**
    114  * Reset a cs for reuse.
    115  */
    116 void
    117 vn_cs_encoder_reset(struct vn_cs_encoder *enc)
    118 {
    119    /* enc->error is sticky */
    120    if (likely(enc->buffer_count))
    121       vn_cs_encoder_gc_buffers(enc);
    122 }
    123 
    124 static uint32_t
    125 next_array_size(uint32_t cur_size, uint32_t min_size)
    126 {
    127    const uint32_t next_size = cur_size ? cur_size * 2 : min_size;
    128    return next_size > cur_size ? next_size : 0;
    129 }
    130 
    131 static size_t
    132 next_buffer_size(size_t cur_size, size_t min_size, size_t need)
    133 {
    134    size_t next_size = cur_size ? cur_size * 2 : min_size;
    135    while (next_size < need) {
    136       next_size *= 2;
    137       if (!next_size)
    138          return 0;
    139    }
    140    return next_size;
    141 }
    142 
    143 static bool
    144 vn_cs_encoder_grow_buffer_array(struct vn_cs_encoder *enc)
    145 {
    146    const uint32_t buf_max = next_array_size(enc->buffer_max, 4);
    147    if (!buf_max)
    148       return false;
    149 
    150    void *bufs = realloc(enc->buffers, sizeof(*enc->buffers) * buf_max);
    151    if (!bufs)
    152       return false;
    153 
    154    enc->buffers = bufs;
    155    enc->buffer_max = buf_max;
    156 
    157    return true;
    158 }
    159 
    160 /**
    161  * Add a new vn_cs_encoder_buffer to a cs.
    162  */
    163 bool
    164 vn_cs_encoder_reserve_internal(struct vn_cs_encoder *enc, size_t size)
    165 {
    166    if (unlikely(!enc->indirect))
    167       return false;
    168 
    169    if (enc->buffer_count >= enc->buffer_max) {
    170       if (!vn_cs_encoder_grow_buffer_array(enc))
    171          return false;
    172       assert(enc->buffer_count < enc->buffer_max);
    173    }
    174 
    175    size_t buf_size = 0;
    176    if (likely(enc->buffer_count)) {
    177       vn_cs_encoder_commit_buffer(enc);
    178 
    179       /* TODO better strategy to grow buffer size */
    180       const struct vn_cs_encoder_buffer *cur_buf =
    181          &enc->buffers[enc->buffer_count - 1];
    182       if (cur_buf->offset)
    183          buf_size = next_buffer_size(0, enc->current_buffer_size, size);
    184    }
    185 
    186    if (!buf_size) {
    187       buf_size = next_buffer_size(enc->current_buffer_size,
    188                                   enc->min_buffer_size, size);
    189       if (!buf_size)
    190          return false;
    191    }
    192 
    193    struct vn_renderer_shmem *shmem =
    194       vn_renderer_shmem_create(enc->instance->renderer, buf_size);
    195    if (!shmem)
    196       return false;
    197 
    198    uint32_t roundtrip;
    199    VkResult result = vn_instance_submit_roundtrip(enc->instance, &roundtrip);
    200    if (result != VK_SUCCESS) {
    201       vn_renderer_shmem_unref(enc->instance->renderer, shmem);
    202       return false;
    203    }
    204 
    205    vn_cs_encoder_add_buffer(enc, shmem, 0, shmem->mmap_ptr, buf_size);
    206    enc->current_buffer_size = buf_size;
    207    enc->current_buffer_roundtrip = roundtrip;
    208 
    209    vn_cs_encoder_sanity_check(enc);
    210 
    211    return true;
    212 }
    213 
    214 /*
    215  * Commit written data.
    216  */
    217 void
    218 vn_cs_encoder_commit(struct vn_cs_encoder *enc)
    219 {
    220    if (likely(enc->buffer_count)) {
    221       vn_cs_encoder_commit_buffer(enc);
    222 
    223       /* trigger the slow path on next vn_cs_encoder_reserve */
    224       enc->end = enc->cur;
    225    }
    226 
    227    vn_cs_encoder_sanity_check(enc);
    228 }
    229