#include "vn_cs.h"
#include "vn_device.h"
#include "vn_renderer.h"
static void
vn_cs_encoder_sanity_check(struct vn_cs_encoder *enc)
{
assert(enc->buffer_count <= enc->buffer_max);
size_t total_committed_size = 0;
for (uint32_t i = 0; i < enc->buffer_count; i++)
total_committed_size += enc->buffers[i].committed_size;
assert(enc->total_committed_size == total_committed_size);
if (enc->buffer_count) {
const struct vn_cs_encoder_buffer *cur_buf =
&enc->buffers[enc->buffer_count - 1];
assert(cur_buf->base <= enc->cur && enc->cur <= enc->end &&
enc->end <= cur_buf->base + enc->current_buffer_size);
if (cur_buf->committed_size)
assert(enc->cur == enc->end);
} else {
assert(!enc->current_buffer_size);
assert(!enc->cur && !enc->end);
}
}
static void
vn_cs_encoder_add_buffer(struct vn_cs_encoder *enc,
struct vn_renderer_shmem *shmem,
size_t offset,
void *base,
size_t size)
{
assert(enc->buffer_count < enc->buffer_max);
struct vn_cs_encoder_buffer *cur_buf = &enc->buffers[enc->buffer_count++];
cur_buf->shmem = shmem;
cur_buf->offset = offset;
cur_buf->base = base;
cur_buf->committed_size = 0;
enc->cur = base;
enc->end = base + size;
}
static void
vn_cs_encoder_commit_buffer(struct vn_cs_encoder *enc)
{
assert(enc->buffer_count);
struct vn_cs_encoder_buffer *cur_buf =
&enc->buffers[enc->buffer_count - 1];
const size_t written_size = enc->cur - cur_buf->base;
if (cur_buf->committed_size) {
assert(cur_buf->committed_size == written_size);
} else {
cur_buf->committed_size = written_size;
enc->total_committed_size += written_size;
}
}
static void
vn_cs_encoder_gc_buffers(struct vn_cs_encoder *enc)
{
assert(enc->buffer_count);
struct vn_cs_encoder_buffer *cur_buf =
&enc->buffers[enc->buffer_count - 1];
for (uint32_t i = 0; i < enc->buffer_count - 1; i++)
vn_renderer_shmem_unref(enc->instance->renderer, enc->buffers[i].shmem);
const size_t used = cur_buf->offset + cur_buf->committed_size;
enc->buffer_count = 0;
vn_cs_encoder_add_buffer(enc, cur_buf->shmem, used,
cur_buf->base + cur_buf->committed_size,
enc->current_buffer_size - used);
enc->total_committed_size = 0;
}
void
vn_cs_encoder_init_indirect(struct vn_cs_encoder *enc,
struct vn_instance *instance,
size_t min_size)
{
memset(enc, 0, sizeof(*enc));
enc->instance = instance;
enc->min_buffer_size = min_size;
enc->indirect = true;
}
void
vn_cs_encoder_fini(struct vn_cs_encoder *enc)
{
if (unlikely(!enc->indirect))
return;
for (uint32_t i = 0; i < enc->buffer_count; i++)
vn_renderer_shmem_unref(enc->instance->renderer, enc->buffers[i].shmem);
if (enc->buffers)
free(enc->buffers);
}
void
vn_cs_encoder_reset(struct vn_cs_encoder *enc)
{
if (likely(enc->buffer_count))
vn_cs_encoder_gc_buffers(enc);
}
static uint32_t
next_array_size(uint32_t cur_size, uint32_t min_size)
{
const uint32_t next_size = cur_size ? cur_size * 2 : min_size;
return next_size > cur_size ? next_size : 0;
}
static size_t
next_buffer_size(size_t cur_size, size_t min_size, size_t need)
{
size_t next_size = cur_size ? cur_size * 2 : min_size;
while (next_size < need) {
next_size *= 2;
if (!next_size)
return 0;
}
return next_size;
}
static bool
vn_cs_encoder_grow_buffer_array(struct vn_cs_encoder *enc)
{
const uint32_t buf_max = next_array_size(enc->buffer_max, 4);
if (!buf_max)
return false;
void *bufs = realloc(enc->buffers, sizeof(*enc->buffers) * buf_max);
if (!bufs)
return false;
enc->buffers = bufs;
enc->buffer_max = buf_max;
return true;
}
bool
vn_cs_encoder_reserve_internal(struct vn_cs_encoder *enc, size_t size)
{
if (unlikely(!enc->indirect))
return false;
if (enc->buffer_count >= enc->buffer_max) {
if (!vn_cs_encoder_grow_buffer_array(enc))
return false;
assert(enc->buffer_count < enc->buffer_max);
}
size_t buf_size = 0;
if (likely(enc->buffer_count)) {
vn_cs_encoder_commit_buffer(enc);
const struct vn_cs_encoder_buffer *cur_buf =
&enc->buffers[enc->buffer_count - 1];
if (cur_buf->offset)
buf_size = next_buffer_size(0, enc->current_buffer_size, size);
}
if (!buf_size) {
buf_size = next_buffer_size(enc->current_buffer_size,
enc->min_buffer_size, size);
if (!buf_size)
return false;
}
struct vn_renderer_shmem *shmem =
vn_renderer_shmem_create(enc->instance->renderer, buf_size);
if (!shmem)
return false;
uint32_t roundtrip;
VkResult result = vn_instance_submit_roundtrip(enc->instance, &roundtrip);
if (result != VK_SUCCESS) {
vn_renderer_shmem_unref(enc->instance->renderer, shmem);
return false;
}
vn_cs_encoder_add_buffer(enc, shmem, 0, shmem->mmap_ptr, buf_size);
enc->current_buffer_size = buf_size;
enc->current_buffer_roundtrip = roundtrip;
vn_cs_encoder_sanity_check(enc);
return true;
}
void
vn_cs_encoder_commit(struct vn_cs_encoder *enc)
{
if (likely(enc->buffer_count)) {
vn_cs_encoder_commit_buffer(enc);
enc->end = enc->cur;
}
vn_cs_encoder_sanity_check(enc);
}