gpu_bind_bundles;

Now you can bind multiple bundles at once.
This commit is contained in:
bjorn 2022-08-08 20:36:22 -07:00
parent d88a7bdc8b
commit f310a7ad04
3 changed files with 202 additions and 170 deletions

View File

@ -585,7 +585,7 @@ void gpu_set_viewport(gpu_stream* stream, float viewport[4], float depthRange[4]
void gpu_set_scissor(gpu_stream* stream, uint32_t scissor[4]);
void gpu_push_constants(gpu_stream* stream, gpu_shader* shader, void* data, uint32_t size);
void gpu_bind_pipeline(gpu_stream* stream, gpu_pipeline* pipeline, bool compute);
void gpu_bind_bundle(gpu_stream* stream, gpu_shader* shader, uint32_t set, gpu_bundle* bundle, uint32_t* dynamicOffsets, uint32_t dynamicOffsetCount);
void gpu_bind_bundles(gpu_stream* stream, gpu_shader* shader, gpu_bundle** bundles, uint32_t first, uint32_t count, uint32_t* dynamicOffsets, uint32_t dynamicOffsetCount);
void gpu_bind_vertex_buffers(gpu_stream* stream, gpu_buffer** buffers, uint32_t* offsets, uint32_t first, uint32_t count);
void gpu_bind_index_buffer(gpu_stream* stream, gpu_buffer* buffer, uint32_t offset, gpu_index_type type);
void gpu_draw(gpu_stream* stream, uint32_t vertexCount, uint32_t instanceCount, uint32_t firstVertex, uint32_t baseInstance);

View File

@ -1625,9 +1625,13 @@ void gpu_bind_pipeline(gpu_stream* stream, gpu_pipeline* pipeline, bool compute)
vkCmdBindPipeline(stream->commands, bindPoint, pipeline->handle);
}
void gpu_bind_bundle(gpu_stream* stream, gpu_shader* shader, uint32_t set, gpu_bundle* bundle, uint32_t* dynamicOffsets, uint32_t dynamicOffsetCount) {
void gpu_bind_bundles(gpu_stream* stream, gpu_shader* shader, gpu_bundle** bundles, uint32_t first, uint32_t count, uint32_t* dynamicOffsets, uint32_t dynamicOffsetCount) {
VkDescriptorSet sets[COUNTOF(((gpu_shader_info*) NULL)->layouts)];
for (uint32_t i = 0; i < count; i++) {
sets[i] = bundles[i]->handle;
}
VkPipelineBindPoint bindPoint = shader->handles[1] ? VK_PIPELINE_BIND_POINT_GRAPHICS : VK_PIPELINE_BIND_POINT_COMPUTE;
vkCmdBindDescriptorSets(stream->commands, bindPoint, shader->pipelineLayout, set, 1, &bundle->handle, dynamicOffsetCount, dynamicOffsets);
vkCmdBindDescriptorSets(stream->commands, bindPoint, shader->pipelineLayout, first, count, sets, dynamicOffsetCount, dynamicOffsets);
}
void gpu_bind_vertex_buffers(gpu_stream* stream, gpu_buffer** buffers, uint32_t* offsets, uint32_t first, uint32_t count) {

View File

@ -13,6 +13,7 @@
#include "monkey.h"
#include "shaders.h"
#include <math.h>
#include <limits.h>
#include <stdlib.h>
#include <string.h>
#ifdef LOVR_USE_GLSLANG
@ -2891,7 +2892,7 @@ static void lovrModelReskin(Model* model) {
gpu_compute_begin(state.stream);
gpu_bind_pipeline(state.stream, pipeline, true);
gpu_bind_bundle(state.stream, shader, 0, bundle, NULL, 0);
gpu_bind_bundles(state.stream, shader, &bundle, 0, 1, NULL, 0);
gpu_push_constants(state.stream, shader, constants, sizeof(constants));
gpu_compute(state.stream, (skin->vertexCount + subgroupSize - 1) / subgroupSize, 1, 1);
gpu_compute_end(state.stream);
@ -3073,7 +3074,7 @@ static void lovrTallyResolve(Tally* tally, uint32_t index, uint32_t count, gpu_b
gpu_compute_begin(stream);
gpu_bind_pipeline(stream, pipeline, true);
gpu_bind_bundle(stream, shader, 0, bundle, NULL, 0);
gpu_bind_bundles(stream, shader, &bundle, 0, 1, NULL, 0);
gpu_push_constants(stream, shader, &constants, sizeof(constants));
gpu_compute(stream, (count + 31) / 32, 1, 1);
gpu_compute_end(stream);
@ -3985,7 +3986,7 @@ void lovrPassSendValue(Pass* pass, const char* name, size_t length, void** data,
lovrThrow("Shader has no push constant named '%s'", name);
}
static void flushPipeline(Pass* pass, Draw* draw, Shader* shader) {
static void bindPipeline(Pass* pass, Draw* draw, Shader* shader) {
Pipeline* pipeline = pass->pipeline;
if (pipeline->info.drawMode != (gpu_draw_mode) draw->mode) {
@ -4000,62 +4001,61 @@ static void flushPipeline(Pass* pass, Draw* draw, Shader* shader) {
pipeline->dirty = true;
}
// Builtin vertex format
if (!draw->vertex.buffer && pipeline->formatHash != 1 + draw->vertex.format) {
pipeline->formatHash = 1 + draw->vertex.format;
pipeline->info.vertex = state.vertexFormats[draw->vertex.format];
pipeline->dirty = true;
// Vertex formats
if (pipeline->formatHash != 1 + draw->vertex.format) {
if (!draw->vertex.buffer) {
pipeline->formatHash = 1 + draw->vertex.format;
pipeline->info.vertex = state.vertexFormats[draw->vertex.format];
pipeline->dirty = true;
if (shader->hasCustomAttributes) {
for (uint32_t i = 0; i < shader->attributeCount; i++) {
if (shader->attributes[i].location < 10) {
pipeline->info.vertex.attributes[pipeline->info.vertex.attributeCount++] = (gpu_attribute) {
.buffer = 1,
.location = shader->attributes[i].location,
.type = GPU_TYPE_F32x4,
.offset = shader->attributes[i].location == LOCATION_COLOR ? 16 : 0
};
}
}
}
} else {
pipeline->formatHash = draw->vertex.buffer->hash;
pipeline->info.vertex.bufferCount = 2;
pipeline->info.vertex.attributeCount = shader->attributeCount;
pipeline->info.vertex.bufferStrides[0] = draw->vertex.buffer->info.stride;
pipeline->info.vertex.bufferStrides[1] = 0;
pipeline->dirty = true;
if (shader->hasCustomAttributes) {
for (uint32_t i = 0; i < shader->attributeCount; i++) {
if (shader->attributes[i].location < 10) {
pipeline->info.vertex.attributes[pipeline->info.vertex.attributeCount++] = (gpu_attribute) {
.buffer = 1,
.location = shader->attributes[i].location,
.type = GPU_TYPE_F32x4,
.offset = shader->attributes[i].location == LOCATION_COLOR ? 16 : 0
};
ShaderAttribute* attribute = &shader->attributes[i];
bool found = false;
for (uint32_t j = 0; j < draw->vertex.buffer->info.fieldCount; j++) {
BufferField field = draw->vertex.buffer->info.fields[j];
lovrCheck(field.type < FIELD_MAT2, "Currently, matrix and index types can not be used in vertex buffers");
if (field.hash ? (field.hash == attribute->hash) : (field.location == attribute->location)) {
pipeline->info.vertex.attributes[i] = (gpu_attribute) {
.buffer = 0,
.location = attribute->location,
.offset = field.offset,
.type = field.type
};
found = true;
break;
}
}
}
}
}
// Custom vertex format
if (draw->vertex.buffer && pipeline->formatHash != draw->vertex.buffer->hash) {
pipeline->formatHash = draw->vertex.buffer->hash;
pipeline->info.vertex.bufferCount = 2;
pipeline->info.vertex.attributeCount = shader->attributeCount;
pipeline->info.vertex.bufferStrides[0] = draw->vertex.buffer->info.stride;
pipeline->info.vertex.bufferStrides[1] = 0;
pipeline->dirty = true;
for (uint32_t i = 0; i < shader->attributeCount; i++) {
ShaderAttribute* attribute = &shader->attributes[i];
bool found = false;
for (uint32_t j = 0; j < draw->vertex.buffer->info.fieldCount; j++) {
BufferField field = draw->vertex.buffer->info.fields[j];
lovrCheck(field.type < FIELD_MAT2, "Currently, matrix and index types can not be used in vertex buffers");
if (field.hash ? (field.hash == attribute->hash) : (field.location == attribute->location)) {
if (!found) {
pipeline->info.vertex.attributes[i] = (gpu_attribute) {
.buffer = 0,
.buffer = 1,
.location = attribute->location,
.offset = field.offset,
.type = field.type
.offset = attribute->location == LOCATION_COLOR ? 16 : 0,
.type = GPU_TYPE_F32x4
};
found = true;
break;
}
}
if (!found) {
pipeline->info.vertex.attributes[i] = (gpu_attribute) {
.buffer = 1,
.location = attribute->location,
.offset = attribute->location == LOCATION_COLOR ? 16 : 0,
.type = GPU_TYPE_F32x4
};
}
}
}
@ -4079,117 +4079,142 @@ static void flushPipeline(Pass* pass, Draw* draw, Shader* shader) {
pipeline->dirty = false;
}
static void flushConstants(Pass* pass, Shader* shader) {
if (pass->constantsDirty && shader->constantSize > 0) {
gpu_push_constants(pass->stream, shader->gpu, pass->constants, shader->constantSize);
pass->constantsDirty = false;
}
}
static void flushBindings(Pass* pass, Shader* shader) {
if (!pass->bindingsDirty || shader->resourceCount == 0) {
return;
}
static void bindBundles(Pass* pass, Draw* draw, Shader* shader) {
size_t stack = tempPush();
uint32_t set = pass->info.type == PASS_RENDER ? 2 : 0;
gpu_binding* bindings = tempAlloc(shader->resourceCount * sizeof(gpu_binding));
for (uint32_t i = 0; i < shader->resourceCount; i++) {
bindings[i] = pass->bindings[shader->resources[i].binding];
gpu_bundle* bundles[3];
uint32_t bundleMask = 0;
// Set 0 - Builtins
if (pass->info.type == PASS_RENDER) {
bool builtinsDirty = false;
if (pass->cameraDirty) {
for (uint32_t i = 0; i < pass->cameraCount; i++) {
mat4_init(pass->cameras[i].viewProjection, pass->cameras[i].projection);
mat4_init(pass->cameras[i].inverseProjection, pass->cameras[i].projection);
mat4_mul(pass->cameras[i].viewProjection, pass->cameras[i].view);
mat4_invert(pass->cameras[i].inverseProjection);
}
uint32_t size = pass->cameraCount * sizeof(Camera);
void* data = gpu_map(pass->builtins[1].buffer.object, size, state.limits.uniformBufferAlign, GPU_MAP_WRITE);
memcpy(data, pass->cameras, size);
pass->cameraDirty = false;
builtinsDirty = true;
}
if (pass->drawCount % 256 == 0) {
uint32_t size = 256 * sizeof(DrawData);
pass->drawData = gpu_map(pass->builtins[2].buffer.object, size, state.limits.uniformBufferAlign, GPU_MAP_WRITE);
builtinsDirty = true;
}
if (pass->samplerDirty) {
Sampler* sampler = pass->pipeline->sampler ? pass->pipeline->sampler : state.defaultSamplers[FILTER_LINEAR];
pass->builtins[3].sampler = sampler->gpu;
pass->samplerDirty = false;
builtinsDirty = true;
}
if (builtinsDirty) {
gpu_bundle_info bundleInfo = {
.layout = state.layouts.data[state.builtinLayout].gpu,
.bindings = pass->builtins,
.count = COUNTOF(pass->builtins)
};
bundles[0] = getBundle(state.builtinLayout);
gpu_bundle_write(&bundles[0], &bundleInfo, 1);
bundleMask |= (1 << 0);
}
// Draw data
float m[16];
float* transform;
if (draw->transform) {
transform = mat4_mul(mat4_init(m, pass->transform), draw->transform);
} else {
transform = pass->transform;
}
float cofactor[16];
mat4_init(cofactor, transform);
cofactor[12] = 0.f;
cofactor[13] = 0.f;
cofactor[14] = 0.f;
cofactor[15] = 1.f;
mat4_cofactor(cofactor);
memcpy(pass->drawData->transform, transform, 64);
memcpy(pass->drawData->cofactor, cofactor, 64);
memcpy(pass->drawData->color, pass->pipeline->color, 16);
pass->drawData++;
}
gpu_bundle_info info = {
.layout = state.layouts.data[shader->layout].gpu,
.bindings = bindings,
.count = shader->resourceCount
};
// Set 1 - Material
if (pass->info.type == PASS_RENDER) {
if (draw->material && draw->material != pass->pipeline->material) {
trackMaterial(pass, draw->material, GPU_PHASE_SHADER_VERTEX | GPU_PHASE_SHADER_FRAGMENT, GPU_CACHE_TEXTURE);
pass->materialDirty = true;
bundles[1] = draw->material->bundle;
bundleMask |= (1 << 1);
} else if (pass->materialDirty) {
Material* material = pass->pipeline->material ? pass->pipeline->material : state.defaultMaterial;
trackMaterial(pass, material, GPU_PHASE_SHADER_VERTEX | GPU_PHASE_SHADER_FRAGMENT, GPU_CACHE_TEXTURE);
pass->materialDirty = false;
bundles[1] = material->bundle;
bundleMask |= (1 << 1);
} else {
bundles[1] = (pass->pipeline->material ? pass->pipeline->material : state.defaultMaterial)->bundle;
}
}
// Set 2 - Resources
if (pass->bindingsDirty && shader->resourceCount > 0) {
gpu_binding* bindings = tempAlloc(shader->resourceCount * sizeof(gpu_binding));
for (uint32_t i = 0; i < shader->resourceCount; i++) {
bindings[i] = pass->bindings[shader->resources[i].binding];
}
gpu_bundle_info info = {
.layout = state.layouts.data[shader->layout].gpu,
.bindings = bindings,
.count = shader->resourceCount
};
gpu_bundle* bundle = getBundle(shader->layout);
gpu_bundle_write(&bundle, &info, 1);
pass->bindingsDirty = false;
uint32_t set = pass->info.type == PASS_RENDER ? 2 : 0;
bundleMask |= (1 << set);
bundles[set] = bundle;
}
// Bind
if (bundleMask) {
uint32_t first = 0;
while (~bundleMask & 0x1) {
bundleMask >>= 1;
first++;
}
uint32_t count = 0;
while (bundleMask) {
bundleMask >>= 1;
count++;
}
gpu_bind_bundles(pass->stream, shader->gpu, bundles + first, first, count, NULL, 0);
}
gpu_bundle* bundle = getBundle(shader->layout);
gpu_bundle_write(&bundle, &info, 1);
gpu_bind_bundle(pass->stream, shader->gpu, set, bundle, NULL, 0);
tempPop(stack);
}
static void flushBuiltins(Pass* pass, Draw* draw, Shader* shader) {
bool rebind = false;
if (pass->cameraDirty) {
for (uint32_t i = 0; i < pass->cameraCount; i++) {
mat4_init(pass->cameras[i].viewProjection, pass->cameras[i].projection);
mat4_init(pass->cameras[i].inverseProjection, pass->cameras[i].projection);
mat4_mul(pass->cameras[i].viewProjection, pass->cameras[i].view);
mat4_invert(pass->cameras[i].inverseProjection);
}
uint32_t size = pass->cameraCount * sizeof(Camera);
void* data = gpu_map(pass->builtins[1].buffer.object, size, state.limits.uniformBufferAlign, GPU_MAP_WRITE);
memcpy(data, pass->cameras, size);
pass->cameraDirty = false;
rebind = true;
}
if (pass->drawCount % 256 == 0) {
uint32_t size = 256 * sizeof(DrawData);
pass->drawData = gpu_map(pass->builtins[2].buffer.object, size, state.limits.uniformBufferAlign, GPU_MAP_WRITE);
rebind = true;
}
if (pass->samplerDirty) {
Sampler* sampler = pass->pipeline->sampler ? pass->pipeline->sampler : state.defaultSamplers[FILTER_LINEAR];
pass->builtins[3].sampler = sampler->gpu;
pass->samplerDirty = false;
rebind = true;
}
if (rebind) {
gpu_bundle_info bundleInfo = {
.layout = state.layouts.data[state.builtinLayout].gpu,
.bindings = pass->builtins,
.count = COUNTOF(pass->builtins)
};
gpu_bundle* bundle = getBundle(state.builtinLayout);
gpu_bundle_write(&bundle, &bundleInfo, 1);
gpu_bind_bundle(pass->stream, shader->gpu, 0, bundle, NULL, 0);
}
float m[16];
float* transform;
if (draw->transform) {
transform = mat4_mul(mat4_init(m, pass->transform), draw->transform);
} else {
transform = pass->transform;
}
float cofactor[16];
mat4_init(cofactor, transform);
cofactor[12] = 0.f;
cofactor[13] = 0.f;
cofactor[14] = 0.f;
cofactor[15] = 1.f;
mat4_cofactor(cofactor);
memcpy(pass->drawData->transform, transform, 64);
memcpy(pass->drawData->cofactor, cofactor, 64);
memcpy(pass->drawData->color, pass->pipeline->color, 16);
pass->drawData++;
}
static void flushMaterial(Pass* pass, Draw* draw, Shader* shader) {
if (draw->material && draw->material != pass->pipeline->material) {
gpu_bind_bundle(pass->stream, shader->gpu, 1, draw->material->bundle, NULL, 0);
trackMaterial(pass, draw->material, GPU_PHASE_SHADER_VERTEX | GPU_PHASE_SHADER_FRAGMENT, GPU_CACHE_TEXTURE);
pass->materialDirty = true;
} else if (pass->materialDirty) {
Material* material = pass->pipeline->material ? pass->pipeline->material : state.defaultMaterial;
gpu_bind_bundle(pass->stream, shader->gpu, 1, material->bundle, NULL, 0);
trackMaterial(pass, material, GPU_PHASE_SHADER_VERTEX | GPU_PHASE_SHADER_FRAGMENT, GPU_CACHE_TEXTURE);
pass->materialDirty = false;
}
}
static void flushBuffers(Pass* pass, Draw* draw) {
static void bindBuffers(Pass* pass, Draw* draw) {
Shape* cache = NULL;
if (draw->hash) {
@ -4250,16 +4275,21 @@ static void flushBuffers(Pass* pass, Draw* draw) {
}
}
static void pushConstants(Pass* pass, Shader* shader) {
if (pass->constantsDirty && shader->constantSize > 0) {
gpu_push_constants(pass->stream, shader->gpu, pass->constants, shader->constantSize);
pass->constantsDirty = false;
}
}
static void lovrPassDraw(Pass* pass, Draw* draw) {
lovrCheck(pass->info.type == PASS_RENDER, "This function can only be called on a render pass");
Shader* shader = pass->pipeline->shader ? pass->pipeline->shader : lovrGraphicsGetDefaultShader(draw->shader);
flushPipeline(pass, draw, shader);
flushConstants(pass, shader);
flushBindings(pass, shader);
flushBuiltins(pass, draw, shader);
flushMaterial(pass, draw, shader);
flushBuffers(pass, draw);
bindPipeline(pass, draw, shader);
bindBundles(pass, draw, shader);
bindBuffers(pass, draw);
pushConstants(pass, shader);
uint32_t defaultCount = draw->index.count > 0 ? draw->index.count : draw->vertex.count;
uint32_t count = draw->count > 0 ? draw->count : defaultCount;
@ -5100,12 +5130,10 @@ void lovrPassMeshIndirect(Pass* pass, Buffer* vertices, Buffer* indices, Buffer*
Shader* shader = pass->pipeline->shader;
lovrCheck(shader, "A custom Shader must be bound to source draws from a Buffer");
flushPipeline(pass, &draw, shader);
flushConstants(pass, shader);
flushBindings(pass, shader);
flushBuiltins(pass, &draw, shader);
flushMaterial(pass, &draw, shader);
flushBuffers(pass, &draw);
bindPipeline(pass, &draw, shader);
bindBundles(pass, &draw, shader);
bindBuffers(pass, &draw);
pushConstants(pass, shader);
if (indices) {
gpu_draw_indirect_indexed(pass->stream, draws->gpu, offset, count, stride);
@ -5132,8 +5160,8 @@ void lovrPassCompute(Pass* pass, uint32_t x, uint32_t y, uint32_t z, Buffer* ind
pass->pipeline->dirty = false;
}
flushConstants(pass, shader);
flushBindings(pass, shader);
bindBundles(pass, NULL, shader);
pushConstants(pass, shader);
if (indirect) {
lovrCheck(offset % 4 == 0, "Indirect compute offset must be a multiple of 4");