mirror of https://gitlab.freedesktop.org/mesa/mesa
315 lines
8.8 KiB
C
315 lines
8.8 KiB
C
/*
|
|
* Copyright 2019 Google LLC
|
|
* SPDX-License-Identifier: MIT
|
|
*/
|
|
|
|
#include "vn_cs.h"
|
|
|
|
#include "vn_instance.h"
|
|
#include "vn_renderer.h"
|
|
|
|
struct vn_cs_renderer_protocol_info _vn_cs_renderer_protocol_info = {
|
|
.mutex = SIMPLE_MTX_INITIALIZER,
|
|
};
|
|
|
|
static void
|
|
vn_cs_renderer_protocol_info_init_once(struct vn_instance *instance)
|
|
{
|
|
const struct vn_renderer_info *renderer_info = &instance->renderer->info;
|
|
/* assume renderer protocol supports all extensions if bit 0 is not set */
|
|
const bool support_all_exts =
|
|
!vn_info_extension_mask_test(renderer_info->vk_extension_mask, 0);
|
|
|
|
_vn_cs_renderer_protocol_info.api_version = renderer_info->vk_xml_version;
|
|
|
|
STATIC_ASSERT(sizeof(renderer_info->vk_extension_mask) >=
|
|
sizeof(_vn_cs_renderer_protocol_info.extension_bitset));
|
|
|
|
for (uint32_t i = 1; i <= VN_INFO_EXTENSION_MAX_NUMBER; i++) {
|
|
/* use protocl helper to ensure mask decoding matches encoding */
|
|
if (support_all_exts ||
|
|
vn_info_extension_mask_test(renderer_info->vk_extension_mask, i))
|
|
BITSET_SET(_vn_cs_renderer_protocol_info.extension_bitset, i);
|
|
}
|
|
}
|
|
|
|
void
|
|
vn_cs_renderer_protocol_info_init(struct vn_instance *instance)
|
|
{
|
|
simple_mtx_lock(&_vn_cs_renderer_protocol_info.mutex);
|
|
if (_vn_cs_renderer_protocol_info.init_once) {
|
|
simple_mtx_unlock(&_vn_cs_renderer_protocol_info.mutex);
|
|
return;
|
|
}
|
|
|
|
vn_cs_renderer_protocol_info_init_once(instance);
|
|
|
|
_vn_cs_renderer_protocol_info.init_once = true;
|
|
simple_mtx_unlock(&_vn_cs_renderer_protocol_info.mutex);
|
|
}
|
|
|
|
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)
|
|
{
|
|
/* add a buffer and make it current */
|
|
assert(enc->buffer_count < enc->buffer_max);
|
|
struct vn_cs_encoder_buffer *cur_buf = &enc->buffers[enc->buffer_count++];
|
|
/* shmem ownership transferred */
|
|
cur_buf->shmem = shmem;
|
|
cur_buf->offset = offset;
|
|
cur_buf->base = base;
|
|
cur_buf->committed_size = 0;
|
|
|
|
/* update the write pointer */
|
|
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)
|
|
{
|
|
/* when the shmem pool is used, no need to cache the shmem in cs */
|
|
if (enc->storage_type == VN_CS_ENCODER_STORAGE_SHMEM_POOL) {
|
|
for (uint32_t i = 0; i < enc->buffer_count; i++) {
|
|
vn_renderer_shmem_unref(enc->instance->renderer,
|
|
enc->buffers[i].shmem);
|
|
}
|
|
|
|
enc->buffer_count = 0;
|
|
enc->total_committed_size = 0;
|
|
enc->current_buffer_size = 0;
|
|
|
|
enc->cur = NULL;
|
|
enc->end = NULL;
|
|
|
|
return;
|
|
}
|
|
|
|
/* free all but the current buffer */
|
|
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);
|
|
|
|
/* move the current buffer to the beginning, skipping the used part */
|
|
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(struct vn_cs_encoder *enc,
|
|
struct vn_instance *instance,
|
|
enum vn_cs_encoder_storage_type storage_type,
|
|
size_t min_size)
|
|
{
|
|
/* VN_CS_ENCODER_INITIALIZER* should be used instead */
|
|
assert(storage_type != VN_CS_ENCODER_STORAGE_POINTER);
|
|
|
|
memset(enc, 0, sizeof(*enc));
|
|
enc->instance = instance;
|
|
enc->storage_type = storage_type;
|
|
enc->min_buffer_size = min_size;
|
|
}
|
|
|
|
void
|
|
vn_cs_encoder_fini(struct vn_cs_encoder *enc)
|
|
{
|
|
if (unlikely(enc->storage_type == VN_CS_ENCODER_STORAGE_POINTER))
|
|
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);
|
|
}
|
|
|
|
/**
|
|
* Reset a cs for reuse.
|
|
*/
|
|
void
|
|
vn_cs_encoder_reset(struct vn_cs_encoder *enc)
|
|
{
|
|
/* enc->error is sticky */
|
|
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;
|
|
}
|
|
|
|
/**
|
|
* Add a new vn_cs_encoder_buffer to a cs.
|
|
*/
|
|
bool
|
|
vn_cs_encoder_reserve_internal(struct vn_cs_encoder *enc, size_t size)
|
|
{
|
|
VN_TRACE_FUNC();
|
|
if (unlikely(enc->storage_type == VN_CS_ENCODER_STORAGE_POINTER))
|
|
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);
|
|
|
|
if (enc->storage_type == VN_CS_ENCODER_STORAGE_SHMEM_ARRAY) {
|
|
/* if the current buffer is reused from the last vn_cs_encoder_reset
|
|
* (i.e., offset != 0), do not double the size
|
|
*
|
|
* TODO better strategy to grow buffer size
|
|
*/
|
|
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) {
|
|
/* double the 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;
|
|
size_t buf_offset;
|
|
if (enc->storage_type == VN_CS_ENCODER_STORAGE_SHMEM_ARRAY) {
|
|
shmem = vn_renderer_shmem_create(enc->instance->renderer, buf_size);
|
|
buf_offset = 0;
|
|
} else {
|
|
assert(enc->storage_type == VN_CS_ENCODER_STORAGE_SHMEM_POOL);
|
|
shmem =
|
|
vn_instance_cs_shmem_alloc(enc->instance, buf_size, &buf_offset);
|
|
}
|
|
if (!shmem)
|
|
return false;
|
|
|
|
vn_cs_encoder_add_buffer(enc, shmem, buf_offset,
|
|
shmem->mmap_ptr + buf_offset, buf_size);
|
|
enc->current_buffer_size = buf_size;
|
|
|
|
vn_cs_encoder_sanity_check(enc);
|
|
|
|
return true;
|
|
}
|
|
|
|
/*
|
|
* Commit written data.
|
|
*/
|
|
void
|
|
vn_cs_encoder_commit(struct vn_cs_encoder *enc)
|
|
{
|
|
if (likely(enc->buffer_count)) {
|
|
vn_cs_encoder_commit_buffer(enc);
|
|
|
|
/* trigger the slow path on next vn_cs_encoder_reserve */
|
|
enc->end = enc->cur;
|
|
}
|
|
|
|
vn_cs_encoder_sanity_check(enc);
|
|
}
|
|
|
|
bool
|
|
vn_cs_encoder_needs_roundtrip(struct vn_cs_encoder *enc)
|
|
{
|
|
if (!enc->instance->renderer->info.has_guest_vram)
|
|
return false;
|
|
|
|
/* check pinned shmems for new shmem allocs */
|
|
for (uint32_t i = 0; i < enc->buffer_count; i++) {
|
|
const struct vn_renderer_shmem *shmem = enc->buffers[i].shmem;
|
|
if (shmem && !shmem->cache_timestamp)
|
|
return true;
|
|
}
|
|
|
|
return false;
|
|
}
|