blob: fbd6d85e54be0de25a2588b8fd09ab35ae01ba90 [file] [log] [blame]
// Copyright 2019 The Fuchsia Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#include "vk_strings.h"
#include <stdarg.h>
#include <stdio.h>
#include <string.h>
#include "tests/common/utils.h"
//
//
//
// TODO(digit): Decide what to do to make ring buffer allocation thread-safe
// if this becomes an issue. There are essentially two ways to do it:
//
// A) Make |s_ring| below thread_local. Not supported by C99 by default but
// easy to add with compiler-specific extensions for GCC, Clang and MSVC.
// A bit wasteful though.
//
// B) Use atomic loads and compare-and-swaps to use a single global buffer
// with an atomically-managed |pos| pointer below. A little more difficult
// to implement due to the lack of <stdatomic.h> in C99. Again,
// compiler-specific extensions can be used.
//
// A global ring buffer for temporary string buffers used by these functions.
#define RING_SIZE (2048u - sizeof(size_t))
typedef struct
{
size_t pos;
char data[RING_SIZE];
} Ring;
static char *
ring_alloc(Ring * ring, size_t size)
{
ASSERT_MSG(size < RING_SIZE,
"String ring allocation too large %u > %u\n",
(uint32_t)size,
RING_SIZE);
size_t avail = RING_SIZE - ring->pos;
char * result = ring->data + ring->pos;
if (size <= avail)
{
ring->pos += size;
}
else
{
result = ring->data;
ring->pos = 0;
}
return result;
}
// TODO(digit): Make this thread_local eventually to make all functions here
// thread-safe. For now, just use a single static ring.
static Ring s_ring;
// Allocate |size_| bytes of data from the current thread's string ring.
#define DECLARE_TEMP_ARRAY(temp_, size_) char * temp_ = ring_alloc(&s_ring, size_)
//
//
//
// Helper type for a fixed-size buffer that can accept formatted data.
typedef struct
{
char * data;
size_t capacity;
size_t pos;
} Buffer;
void
buffer_init(Buffer * buffer, char * data, size_t data_size)
{
ASSERT_MSG(data_size > 0, "Cannot create buffer with size of 0\n");
buffer->data = data;
buffer->pos = 0;
buffer->capacity = data_size - 1;
buffer->data[0] = 0;
;
}
void
buffer_add(Buffer * buffer, const char * str, size_t size)
{
size_t avail = buffer->capacity;
if (size > avail)
size = avail;
memmove(buffer->data + buffer->pos, str, size);
buffer->pos += size;
buffer->data[buffer->pos] = 0;
}
void
buffer_add_s(Buffer * buffer, const char * str)
{
buffer_add(buffer, str, strlen(str));
}
void
buffer_add_formatv(Buffer * buffer, const char * fmt, va_list args)
{
size_t avail = buffer->capacity;
if (!avail)
return;
int len = vsnprintf(buffer->data + buffer->pos, avail, fmt, args);
ASSERT_MSG(len >= 0, "vsnprintf() error!\n");
if ((size_t)len >= avail)
buffer->pos = buffer->capacity;
else
buffer->pos += len;
buffer->data[buffer->pos] = 0;
}
void
buffer_add_format(Buffer * buffer, const char * fmt, ...)
{
va_list args;
va_start(args, fmt);
buffer_add_formatv(buffer, fmt, args);
va_end(args);
}
// Declare a static temporary buffer. This could become thread_local if we
// really need to make these functions thread-safe.
#define DECLARE_TEMP_BUFFER(temp_, size_) \
DECLARE_TEMP_ARRAY(MACRO_EXPAND(temp_##_data), size_); \
Buffer temp_[1]; \
buffer_init(temp_, temp_##_data, size_)
extern const char *
vk_device_size_to_string(VkDeviceSize size)
{
DECLARE_TEMP_ARRAY(temp, 16);
if (size < 65536)
{
snprintf(temp, sizeof(temp), "%u", (unsigned)size);
}
else if (size < 1024 * 1024)
{
snprintf(temp, sizeof(temp), "%.1f kiB", size / 1024.);
}
else if (size < 1024 * 1024 * 1024)
{
snprintf(temp, sizeof(temp), "%.1f MiB", size / (1024. * 1024.));
}
else
{
snprintf(temp, sizeof(temp), "%1.f GiB", size / (1024. * 1024. * 1024.));
}
return temp;
}
const char *
vk_queue_family_index_to_string(uint32_t queue_family_index)
{
if (queue_family_index == UINT32_MAX)
return "NONE";
DECLARE_TEMP_ARRAY(temp, 10);
snprintf(temp, sizeof(temp), "%u", queue_family_index);
return temp;
}
const char *
vk_memory_heap_to_string(const VkMemoryHeap * memory_heap)
{
DECLARE_TEMP_BUFFER(temp, 64);
uint32_t flags = memory_heap->flags;
buffer_add_format(temp,
"size=%-8s flags=0x%08X",
vk_device_size_to_string(memory_heap->size),
flags);
#define FLAG_BIT(name_) \
if ((flags & VK_MEMORY_HEAP_##name_##_BIT) != 0) \
buffer_add_format(temp, " %s", #name_);
FLAG_BIT(DEVICE_LOCAL);
FLAG_BIT(MULTI_INSTANCE);
#undef FLAG_BIT
return temp->data;
}
const char *
vk_memory_type_to_string(const VkMemoryType * memory_type)
{
DECLARE_TEMP_BUFFER(temp, 64);
uint32_t flags = memory_type->propertyFlags;
buffer_add_format(temp, "heap=%-2d flags=0x%08X", memory_type->heapIndex, flags);
#define FLAG_BIT(name_) \
if ((flags & VK_MEMORY_PROPERTY_##name_##_BIT)) \
buffer_add_format(temp, " %s", #name_);
FLAG_BIT(DEVICE_LOCAL);
FLAG_BIT(HOST_VISIBLE);
FLAG_BIT(HOST_COHERENT);
FLAG_BIT(HOST_CACHED);
FLAG_BIT(LAZILY_ALLOCATED);
FLAG_BIT(PROTECTED);
#undef FLAG_BIT
return temp->data;
}
const char *
vk_present_mode_khr_to_string(VkPresentModeKHR arg)
{
#define CASE(arg_) \
case VK_PRESENT_MODE_##arg_: \
return "VK_PRESENT_MODE_" #arg_
switch (arg)
{
CASE(IMMEDIATE_KHR);
CASE(MAILBOX_KHR);
CASE(FIFO_KHR);
CASE(FIFO_RELAXED_KHR);
default:;
}
#undef CASE
DECLARE_TEMP_ARRAY(temp, 16);
snprintf(temp, sizeof(temp), "UNKNOWN(%u)", (unsigned)arg);
return temp;
}
const char *
vk_format_to_string(VkFormat arg)
{
#define CASE(arg_) \
case VK_FORMAT_##arg_: \
return "VK_FORMAT_" #arg_
switch (arg)
{
CASE(UNDEFINED);
CASE(B8G8R8A8_UNORM);
CASE(B8G8R8A8_SRGB);
CASE(R8G8B8A8_UNORM);
CASE(R8G8B8A8_SRGB);
default:;
}
#undef CASE
DECLARE_TEMP_ARRAY(temp, 16);
snprintf(temp, sizeof(temp), "UNKNOWN(%u)", (unsigned)arg);
return temp;
}
const char *
vk_colorspace_khr_to_string(VkColorSpaceKHR arg)
{
#define CASE(arg_) \
case VK_COLOR_SPACE_##arg_: \
return "VK_COLOR_SPACE_" #arg_
switch (arg)
{
CASE(SRGB_NONLINEAR_KHR);
default:;
}
#undef CASE
DECLARE_TEMP_ARRAY(temp, 16);
snprintf(temp, sizeof(temp), "UNKNOWN(%u)", (unsigned)arg);
return temp;
}
const char *
vk_surface_format_khr_to_string(VkSurfaceFormatKHR format)
{
DECLARE_TEMP_ARRAY(temp, 32);
snprintf(temp,
sizeof(temp),
"%s(%s)",
vk_format_to_string(format.format),
vk_colorspace_khr_to_string(format.colorSpace));
return temp;
}
const char *
vk_format_feature_flags_to_string(VkFormatFeatureFlags flags)
{
DECLARE_TEMP_BUFFER(temp, 128);
#define CHECK_FLAG(flag_) \
if ((flags & VK_FORMAT_FEATURE_##flag_##_BIT) != 0) \
buffer_add_format(temp, " %s", #flag_);
CHECK_FLAG(SAMPLED_IMAGE)
CHECK_FLAG(STORAGE_IMAGE)
CHECK_FLAG(STORAGE_IMAGE_ATOMIC)
CHECK_FLAG(UNIFORM_TEXEL_BUFFER)
CHECK_FLAG(STORAGE_TEXEL_BUFFER)
CHECK_FLAG(STORAGE_TEXEL_BUFFER_ATOMIC)
CHECK_FLAG(VERTEX_BUFFER)
CHECK_FLAG(COLOR_ATTACHMENT)
CHECK_FLAG(COLOR_ATTACHMENT_BLEND)
CHECK_FLAG(DEPTH_STENCIL_ATTACHMENT)
CHECK_FLAG(BLIT_SRC)
CHECK_FLAG(BLIT_DST)
#undef CHECK_FLAG
return temp->data;
}
#define LIST_VK_IMAGE_USAGE_BITS(macro) \
macro(TRANSFER_SRC) macro(TRANSFER_DST) macro(SAMPLED) macro(STORAGE) macro(COLOR_ATTACHMENT) \
macro(DEPTH_STENCIL_ATTACHMENT) macro(TRANSIENT_ATTACHMENT) macro(INPUT_ATTACHMENT)
const char *
vk_image_usage_flags_to_string(VkImageUsageFlags flags)
{
uint32_t bits = (uint32_t)flags;
if (!bits)
return "NONE";
#define TEST_BIT(bit_) ((bits & VK_IMAGE_USAGE_##bit_##_BIT) != 0)
// First, count the number of known bits set.
#define COUNT_BIT(bit_) \
if (TEST_BIT(bit_)) \
count++;
int count = 0;
LIST_VK_IMAGE_USAGE_BITS(COUNT_BIT)
#undef COUNT_BIT
DECLARE_TEMP_BUFFER(temp, 64);
if (!count)
{
buffer_add_format(temp, "UNKNOWN(0x%X)", bits);
return temp->data;
}
#define NAME_BIT(bit_) #bit_,
static const char * const kBitNames[] = { LIST_VK_IMAGE_USAGE_BITS(NAME_BIT) };
#undef NAME_BIT
buffer_add_s(temp, "VK_IMAGE_USAGE_");
if (count > 1)
buffer_add(temp, "[", 1);
const char * separator = "";
unsigned bit_index = 0;
#define ADD_BIT(bit_) \
if (TEST_BIT(bit_)) \
{ \
buffer_add_s(temp, separator); \
buffer_add_s(temp, kBitNames[bit_index]); \
separator = "|"; \
} \
bit_index++;
LIST_VK_IMAGE_USAGE_BITS(ADD_BIT)
#undef ADD_BIT
if (count > 1)
buffer_add(temp, "]", 1);
buffer_add(temp, "_BIT", 4);
return temp->data;
#undef TEST_BIT
}
#define LIST_VK_BUFFER_USAGE_BITS(macro) \
macro(TRANSFER_SRC) macro(TRANSFER_DST) macro(UNIFORM_TEXEL_BUFFER) macro(STORAGE_TEXEL_BUFFER) \
macro(UNIFORM_BUFFER) macro(STORAGE_BUFFER) macro(INDEX_BUFFER) macro(VERTEX_BUFFER) \
macro(INDIRECT_BUFFER)
const char *
vk_buffer_usage_flags_to_string(VkBufferUsageFlags flags)
{
uint32_t bits = (uint32_t)flags;
if (!bits)
return "NONE";
#define TEST_BIT(bit_) ((bits & VK_BUFFER_USAGE_##bit_##_BIT) != 0)
// First, count the number of known bits set.
#define COUNT_BIT(bit_) \
if (TEST_BIT(bit_)) \
count++;
int count = 0;
LIST_VK_BUFFER_USAGE_BITS(COUNT_BIT)
#undef COUNT_BIT
// Special case if there are no known bits.
DECLARE_TEMP_BUFFER(temp, 64);
if (!count)
{
buffer_add_format(temp, "UNKNOWN(0x%X)", bits);
return temp->data;
}
#define NAME_BIT(bit_) #bit_,
static const char * const kBitNames[] = { LIST_VK_BUFFER_USAGE_BITS(NAME_BIT) };
#undef NAME_BIT
buffer_add_s(temp, "VK_BUFFER_USAGE_");
if (count > 1)
buffer_add(temp, "[", 1);
const char * separator = "";
unsigned bit_index = 0;
#define ADD_BIT(bit_) \
if (TEST_BIT(bit_)) \
{ \
buffer_add(temp, separator, 1); \
buffer_add_s(temp, kBitNames[bit_index]); \
separator = "|"; \
} \
bit_index++;
LIST_VK_BUFFER_USAGE_BITS(ADD_BIT)
#undef ADD_BIT
if (count > 1)
buffer_add(temp, "]", 1);
buffer_add(temp, "_BIT", 4);
return temp->data;
#undef TEST_BIT
}
const char *
vk_physical_device_type_to_string(VkPhysicalDeviceType device_type)
{
#define CASE(type_) case VK_PHYSICAL_DEVICE_TYPE_ ## type_: return #type_;
switch (device_type)
{
CASE(OTHER)
CASE(INTEGRATED_GPU)
CASE(DISCRETE_GPU)
CASE(VIRTUAL_GPU)
CASE(CPU)
default:;
}
#undef CASE
DECLARE_TEMP_ARRAY(temp, 16);
snprintf(temp, sizeof(temp), "UKNOWN(%u)", device_type);
return temp;
}