gpu_bundle; gpu_bundle_pool; layouts;

This commit is contained in:
bjorn 2022-05-23 21:40:57 -07:00
parent a0db01959d
commit e054218713
3 changed files with 271 additions and 12 deletions

View File

@ -7,6 +7,8 @@ typedef struct gpu_texture gpu_texture;
typedef struct gpu_sampler gpu_sampler;
typedef struct gpu_layout gpu_layout;
typedef struct gpu_shader gpu_shader;
typedef struct gpu_bundle_pool gpu_bundle_pool;
typedef struct gpu_bundle gpu_bundle;
typedef struct gpu_pipeline gpu_pipeline;
typedef struct gpu_stream gpu_stream;
@ -15,6 +17,8 @@ size_t gpu_sizeof_texture(void);
size_t gpu_sizeof_sampler(void);
size_t gpu_sizeof_layout(void);
size_t gpu_sizeof_shader(void);
size_t gpu_sizeof_bundle_pool(void);
size_t gpu_sizeof_bundle(void);
size_t gpu_sizeof_pipeline(void);
// Buffer
@ -192,10 +196,9 @@ enum {
};
typedef struct {
uint8_t number;
uint8_t type;
uint8_t stage;
uint8_t count;
uint32_t number;
gpu_slot_type type;
uint32_t stages;
} gpu_slot;
typedef struct {
@ -223,6 +226,41 @@ typedef struct {
bool gpu_shader_init(gpu_shader* shader, gpu_shader_info* info);
void gpu_shader_destroy(gpu_shader* shader);
// Bundles
typedef struct {
gpu_buffer* object;
uint32_t offset;
uint32_t extent;
} gpu_buffer_binding;
typedef struct {
uint32_t number;
gpu_slot_type type;
union {
gpu_buffer_binding buffer;
gpu_texture* texture;
gpu_sampler* sampler;
};
} gpu_binding;
typedef struct {
gpu_layout* layout;
gpu_binding* bindings;
uint32_t count;
} gpu_bundle_info;
typedef struct {
gpu_bundle* bundles;
gpu_bundle_info* contents;
gpu_layout* layout;
uint32_t count;
} gpu_bundle_pool_info;
bool gpu_bundle_pool_init(gpu_bundle_pool* pool, gpu_bundle_pool_info* info);
void gpu_bundle_pool_destroy(gpu_bundle_pool* pool);
void gpu_bundle_write(gpu_bundle** bundles, gpu_bundle_info* info, uint32_t count);
// Pipeline
typedef enum {

View File

@ -43,6 +43,14 @@ struct gpu_shader {
VkPipelineLayout pipelineLayout;
};
struct gpu_bundle_pool {
VkDescriptorPool handle;
};
struct gpu_bundle {
VkDescriptorSet handle;
};
struct gpu_pipeline {
VkPipeline handle;
VkPipelineLayout layout;
@ -57,6 +65,8 @@ size_t gpu_sizeof_texture() { return sizeof(gpu_texture); }
size_t gpu_sizeof_sampler() { return sizeof(gpu_sampler); }
size_t gpu_sizeof_layout() { return sizeof(gpu_layout); }
size_t gpu_sizeof_shader() { return sizeof(gpu_shader); }
size_t gpu_sizeof_bundle_pool() { return sizeof(gpu_bundle_pool); }
size_t gpu_sizeof_bundle() { return sizeof(gpu_bundle); }
size_t gpu_sizeof_pipeline() { return sizeof(gpu_pipeline); }
// Internals
@ -763,11 +773,11 @@ bool gpu_layout_init(gpu_layout* layout, gpu_layout_info* info) {
bindings[i] = (VkDescriptorSetLayoutBinding) {
.binding = info->slots[i].number,
.descriptorType = types[info->slots[i].type],
.descriptorCount = info->slots[i].count,
.stageFlags = info->slots[i].stage == GPU_STAGE_ALL ? VK_SHADER_STAGE_ALL :
(((info->slots[i].stage & GPU_STAGE_VERTEX) ? VK_SHADER_STAGE_VERTEX_BIT : 0) |
((info->slots[i].stage & GPU_STAGE_FRAGMENT) ? VK_SHADER_STAGE_FRAGMENT_BIT : 0) |
((info->slots[i].stage & GPU_STAGE_COMPUTE) ? VK_SHADER_STAGE_COMPUTE_BIT : 0))
.descriptorCount = 1,
.stageFlags = info->slots[i].stages == GPU_STAGE_ALL ? VK_SHADER_STAGE_ALL :
(((info->slots[i].stages & GPU_STAGE_VERTEX) ? VK_SHADER_STAGE_VERTEX_BIT : 0) |
((info->slots[i].stages & GPU_STAGE_FRAGMENT) ? VK_SHADER_STAGE_FRAGMENT_BIT : 0) |
((info->slots[i].stages & GPU_STAGE_COMPUTE) ? VK_SHADER_STAGE_COMPUTE_BIT : 0))
};
}
@ -784,7 +794,7 @@ bool gpu_layout_init(gpu_layout* layout, gpu_layout_info* info) {
memset(layout->descriptorCounts, 0, sizeof(layout->descriptorCounts));
for (uint32_t i = 0; i < info->count; i++) {
layout->descriptorCounts[info->slots[i].type] += MAX(info->slots[i].count, 1);
layout->descriptorCounts[info->slots[i].type]++;
}
return true;
@ -843,6 +853,149 @@ void gpu_shader_destroy(gpu_shader* shader) {
condemn(shader->pipelineLayout, VK_OBJECT_TYPE_PIPELINE_LAYOUT);
}
// Bundles
bool gpu_bundle_pool_init(gpu_bundle_pool* pool, gpu_bundle_pool_info* info) {
VkDescriptorPoolSize sizes[7] = {
[GPU_SLOT_UNIFORM_BUFFER] = { VK_DESCRIPTOR_TYPE_UNIFORM_BUFFER, 0 },
[GPU_SLOT_STORAGE_BUFFER] = { VK_DESCRIPTOR_TYPE_STORAGE_BUFFER, 0 },
[GPU_SLOT_UNIFORM_BUFFER_DYNAMIC] = { VK_DESCRIPTOR_TYPE_UNIFORM_BUFFER_DYNAMIC, 0 },
[GPU_SLOT_STORAGE_BUFFER_DYNAMIC] = { VK_DESCRIPTOR_TYPE_STORAGE_BUFFER_DYNAMIC, 0 },
[GPU_SLOT_SAMPLED_TEXTURE] = { VK_DESCRIPTOR_TYPE_SAMPLED_IMAGE, 0 },
[GPU_SLOT_STORAGE_TEXTURE] = { VK_DESCRIPTOR_TYPE_STORAGE_IMAGE, 0 },
[GPU_SLOT_SAMPLER] = { VK_DESCRIPTOR_TYPE_SAMPLER, 0 }
};
if (info->layout) {
for (uint32_t i = 0; i < COUNTOF(sizes); i++) {
sizes[i].descriptorCount = info->layout->descriptorCounts[i] * info->count;
}
} else {
for (uint32_t i = 0; i < info->count; i++) {
for (uint32_t j = 0; j < COUNTOF(sizes); j++) {
sizes[j].descriptorCount += info->contents[i].layout->descriptorCounts[j];
}
}
}
// Descriptor counts of zero are forbidden, so swap any zero-sized sizes with the last entry
uint32_t poolSizeCount = COUNTOF(sizes);
for (uint32_t i = 0; i < poolSizeCount; i++) {
if (sizes[i].descriptorCount == 0) {
VkDescriptorPoolSize last = sizes[poolSizeCount - 1];
sizes[poolSizeCount - 1] = sizes[i];
sizes[i] = last;
poolSizeCount--;
i--;
}
}
VkDescriptorPoolCreateInfo poolInfo = {
.sType = VK_STRUCTURE_TYPE_DESCRIPTOR_POOL_CREATE_INFO,
.maxSets = info->count,
.poolSizeCount = poolSizeCount,
.pPoolSizes = sizes
};
VK(vkCreateDescriptorPool(state.device, &poolInfo, NULL, &pool->handle), "Could not create bundle pool") {
return false;
}
VkDescriptorSetLayout layouts[256];
for (uint32_t i = 0; i < info->count; i+= COUNTOF(layouts)) {
uint32_t chunk = MIN(info->count - i, COUNTOF(layouts));
for (uint32_t j = 0; j < chunk; j++) {
layouts[j] = info->layout ? info->layout->handle : info->contents[i + j].layout->handle;
}
VkDescriptorSetAllocateInfo allocateInfo = {
.sType = VK_STRUCTURE_TYPE_DESCRIPTOR_SET_ALLOCATE_INFO,
.descriptorPool = pool->handle,
.descriptorSetCount = chunk,
.pSetLayouts = layouts
};
VK(vkAllocateDescriptorSets(state.device, &allocateInfo, &info->bundles[i].handle), "Could not allocate descriptor sets") {
gpu_bundle_pool_destroy(pool);
return false;
}
}
return true;
}
void gpu_bundle_pool_destroy(gpu_bundle_pool* pool) {
condemn(pool->handle, VK_OBJECT_TYPE_DESCRIPTOR_POOL);
}
void gpu_bundle_write(gpu_bundle** bundles, gpu_bundle_info* infos, uint32_t count) {
VkDescriptorBufferInfo buffers[256];
VkDescriptorImageInfo images[256];
VkWriteDescriptorSet writes[256];
uint32_t bufferCount = 0;
uint32_t imageCount = 0;
uint32_t writeCount = 0;
static const VkDescriptorType types[] = {
[GPU_SLOT_UNIFORM_BUFFER] = VK_DESCRIPTOR_TYPE_UNIFORM_BUFFER,
[GPU_SLOT_STORAGE_BUFFER] = VK_DESCRIPTOR_TYPE_STORAGE_BUFFER,
[GPU_SLOT_UNIFORM_BUFFER_DYNAMIC] = VK_DESCRIPTOR_TYPE_UNIFORM_BUFFER_DYNAMIC,
[GPU_SLOT_STORAGE_BUFFER_DYNAMIC] = VK_DESCRIPTOR_TYPE_STORAGE_BUFFER_DYNAMIC,
[GPU_SLOT_SAMPLED_TEXTURE] = VK_DESCRIPTOR_TYPE_SAMPLED_IMAGE,
[GPU_SLOT_STORAGE_TEXTURE] = VK_DESCRIPTOR_TYPE_STORAGE_IMAGE,
[GPU_SLOT_SAMPLER] = VK_DESCRIPTOR_TYPE_SAMPLER
};
for (uint32_t i = 0; i < count; i++) {
gpu_bundle_info* info = &infos[i];
for (uint32_t j = 0; j < info->count; j++) {
gpu_binding* binding = &info->bindings[j];
VkDescriptorType type = types[binding->type];
bool texture = type == VK_DESCRIPTOR_TYPE_SAMPLED_IMAGE || type == VK_DESCRIPTOR_TYPE_STORAGE_IMAGE;
bool sampler = type == VK_DESCRIPTOR_TYPE_SAMPLER;
bool image = texture || sampler;
writes[writeCount++] = (VkWriteDescriptorSet) {
.sType = VK_STRUCTURE_TYPE_WRITE_DESCRIPTOR_SET,
.dstSet = bundles[i]->handle,
.dstBinding = binding->number,
.dstArrayElement = 0,
.descriptorCount = 1,
.descriptorType = type,
.pBufferInfo = &buffers[bufferCount],
.pImageInfo = &images[imageCount]
};
if (sampler) {
images[imageCount++] = (VkDescriptorImageInfo) {
.sampler = binding->sampler->handle
};
} else if (texture) {
images[imageCount++] = (VkDescriptorImageInfo) {
.imageView = binding->texture->view,
.imageLayout = binding->texture->layout
};
} else {
buffers[bufferCount++] = (VkDescriptorBufferInfo) {
.buffer = binding->buffer.object->handle,
.offset = binding->buffer.offset,
.range = binding->buffer.extent
};
}
if ((image ? imageCount >= COUNTOF(images) : bufferCount >= COUNTOF(buffers)) || writeCount >= COUNTOF(writes)) {
vkUpdateDescriptorSets(state.device, writeCount, writes, 0, NULL);
bufferCount = imageCount = writeCount = 0;
}
}
}
if (writeCount > 0) {
vkUpdateDescriptorSets(state.device, writeCount, writes, 0, NULL);
}
}
// Pipeline
bool gpu_pipeline_init_graphics(gpu_pipeline* pipeline, gpu_pipeline_info* info) {

View File

@ -92,6 +92,21 @@ struct Pass {
uint32_t pipelineIndex;
};
typedef struct {
void* next;
gpu_bundle_pool* gpu;
gpu_bundle* bundles;
uint32_t cursor;
uint32_t tick;
} BundlePool;
typedef struct {
uint64_t hash;
gpu_layout* gpu;
BundlePool* head;
BundlePool* tail;
} Layout;
typedef struct {
gpu_texture* texture;
uint32_t hash;
@ -115,6 +130,8 @@ static struct {
float background[4];
Texture* window;
Attachment attachments[16];
arr_t(Layout) layouts;
uint32_t builtinLayout;
Allocator allocator;
} state;
@ -123,6 +140,7 @@ static struct {
static void* tempAlloc(size_t size);
static void beginFrame(void);
static gpu_stream* getTransfers(void);
static uint32_t getLayout(gpu_slot* slots, uint32_t count);
static gpu_texture* getAttachment(uint32_t size[2], uint32_t layers, TextureFormat format, bool srgb, uint32_t samples);
static size_t measureTexture(TextureFormat format, uint16_t w, uint16_t h, uint16_t d);
static void checkShaderFeatures(uint32_t* features, uint32_t count);
@ -159,6 +177,16 @@ bool lovrGraphicsInit(bool debug, bool vsync) {
lovrThrow("Failed to initialize GPU");
}
arr_init(&state.layouts, realloc);
gpu_slot builtins[] = {
{ 0, GPU_SLOT_UNIFORM_BUFFER_DYNAMIC, GPU_STAGE_ALL },
{ 1, GPU_SLOT_UNIFORM_BUFFER_DYNAMIC, GPU_STAGE_ALL },
{ 2, GPU_SLOT_SAMPLER, GPU_STAGE_ALL }
};
state.builtinLayout = getLayout(builtins, COUNTOF(builtins));
// Temporary frame memory uses a large 1GB virtual memory allocation, committing pages as needed
state.allocator.length = 1 << 14;
state.allocator.memory = os_vm_init(MAX_FRAME_MEMORY);
@ -176,6 +204,11 @@ void lovrGraphicsDestroy() {
free(state.attachments[i].texture);
}
}
for (uint32_t i = 0; i < state.layouts.length; i++) {
gpu_layout_destroy(state.layouts.data[i].gpu);
free(state.layouts.data[i].gpu);
}
arr_free(&state.layouts);
lovrRelease(state.window, lovrTextureDestroy);
gpu_destroy();
glslang_finalize_process();
@ -850,8 +883,7 @@ Shader* lovrShaderCreate(ShaderInfo* info) {
slots[index] = (gpu_slot) {
.number = resource->binding,
.type = resourceTypes[resource->type],
.stage = stage,
.count = resource->arraySize
.stages = stage
};
shader->resources[index] = (ShaderResource) {
@ -905,6 +937,7 @@ Shader* lovrShaderCreate(ShaderInfo* info) {
shader->ref = 1;
shader->gpu = (gpu_shader*) (shader + 1);
shader->info = *info;
shader->layout = getLayout(slots, shader->resourceCount);
gpu_shader_info gpu = {
.stages[0] = { info->stages[0]->data, info->stages[0]->size },
@ -913,6 +946,12 @@ Shader* lovrShaderCreate(ShaderInfo* info) {
.label = info->label
};
if (info->type == SHADER_GRAPHICS) {
gpu.layouts[0] = state.layouts.data[state.builtinLayout].gpu;
}
gpu.layouts[userSet] = shader->resourceCount > 0 ? state.layouts.data[shader->layout].gpu : NULL;
gpu_shader_init(shader->gpu, &gpu);
lovrShaderInit(shader);
return shader;
@ -1295,6 +1334,35 @@ static gpu_stream* getTransfers(void) {
return state.transfers->stream;
}
static uint32_t getLayout(gpu_slot* slots, uint32_t count) {
uint64_t hash = hash64(slots, count * sizeof(gpu_slot));
uint32_t index;
for (uint32_t index = 0; index < state.layouts.length; index++) {
if (state.layouts.data[index].hash == hash) {
return index;
}
}
gpu_layout_info info = {
.slots = slots,
.count = count
};
gpu_layout* handle = malloc(gpu_sizeof_layout());
lovrAssert(handle, "Out of memory");
gpu_layout_init(handle, &info);
Layout layout = {
.hash = hash,
.gpu = handle
};
index = state.layouts.length;
arr_push(&state.layouts, layout);
return index;
}
static gpu_texture* getAttachment(uint32_t size[2], uint32_t layers, TextureFormat format, bool srgb, uint32_t samples) {
uint16_t key[] = { size[0], size[1], layers, format, srgb, samples };
uint32_t hash = hash64(key, sizeof(key));