From 7fa078995b3ed62925788ce2460441bc3d373392 Mon Sep 17 00:00:00 2001 From: quou Date: Thu, 26 Dec 2024 22:06:57 +1100 Subject: constant buffers --- c2.cpp | 28 +++++++++- intermediate/triangle.glsl | 13 ++++- pipeline.cpp | 15 ++++++ sc/sc.cpp | 127 ++++++++++++++++++++++++++++++++++++++++++++- video.cpp | 34 ++++++++---- video.hpp | 34 ++++++++---- 6 files changed, 226 insertions(+), 25 deletions(-) diff --git a/c2.cpp b/c2.cpp index 3b46920..052ebf0 100644 --- a/c2.cpp +++ b/c2.cpp @@ -5,6 +5,7 @@ extern "C" { #include "memory.h" } #include +#include #define video_arena_size (1024 * 1024 * 16) #define asset_arena_size (1024 * 1024 * 4) @@ -58,6 +59,10 @@ static Sampler_Id create_clamped_linear(Device* dev) { return dev->create_sampler(s); } +struct Config_Buffer { + float offset[2]; +}; + int main() { Arena video_arena; Arena asset_arena; @@ -66,7 +71,7 @@ int main() { Shader* shader; Texture* texture; Texture* texture2; - Buffer_Id vbo; + Buffer_Id vbo, cbuf; Sampler_Id clamped_linear; C2* app = App::create("c2"); void* per_frame; @@ -88,6 +93,11 @@ int main() { shader = (Shader*)assets.load("triangle.csh"); texture = (Texture*)assets.load("22.tex"); texture2 = (Texture*)assets.load("kita.tex"); + cbuf = dev->create_buffer( + sizeof verts, + Buffer_Flags::constant_buffer | + Buffer_Flags::cpu_readwrite + ); per_frame = heap_alloc( dev->heap, per_frame_memory_size @@ -103,6 +113,15 @@ int main() { app->begin(); dev->begin_frame(); + { + void* mem; + mem = dev->map_buffer(cbuf, 0, sizeof(Config_Buffer)); + Config_Buffer* c = (Config_Buffer*)mem; + c->offset[0] = sinf((float)frame / 10.0f); + c->offset[1] = cosf((float)frame / 30.0f); + dev->unmap_buffer(cbuf); + } + Pipeline_Builder pb(&frame_arena); pb.begin_rp(); pb.rp_target(dev->get_backbuffer(), { r, 0x00, 0xff, 0xff }); @@ -112,10 +131,14 @@ int main() { pb.shader(shader->id); pb.vertex_format(shader->vf); pb.texture( - shader->descriptor_index("colour_texture"), + shader->descriptor_binding("colour_texture"), frame % 2? texture->id: texture2->id, clamped_linear ); + pb.cbuffer( + shader->descriptor_binding("config_buffer"), + cbuf + ); Pipeline& pip = pb.build(); Vertex_Buffer_Binding binding[] = {{ @@ -139,6 +162,7 @@ int main() { assets.destroy(); dev->destroy_sampler(clamped_linear); dev->destroy_buffer(vbo); + dev->destroy_buffer(cbuf); dev->destroy(); app->destroy(); } diff --git a/intermediate/triangle.glsl b/intermediate/triangle.glsl index 8f3b586..1e883f5 100644 --- a/intermediate/triangle.glsl +++ b/intermediate/triangle.glsl @@ -33,6 +33,17 @@ name: colour_texture stage: fragment dimension: 2 +[struct] +name: Config +[variable] +name: offset +type: vec2 + +[cbuffer] +name: config_buffer +type: Config +stage: vertex + #endif #ifdef VERTEX_SHADER @@ -40,7 +51,7 @@ dimension: 2 void main() { interpolator.colour = colour; interpolator.uv = uv; - gl_Position = vec4(position, 0.0, 1.0); + gl_Position = vec4(position + config_buffer.offset, 0.0, 1.0); } #endif diff --git a/pipeline.cpp b/pipeline.cpp index 527c1e7..89f606d 100644 --- a/pipeline.cpp +++ b/pipeline.cpp @@ -56,6 +56,17 @@ void Pipeline_Builder::texture( td->texture = t; } +void Pipeline_Builder::cbuffer(int binding, Buffer_Id id) { + Descriptor* d; + Constant_Buffer_Descriptor* cd; + assert(pip->descriptor_count < pipeline_max_descriptors); + d = &pip->descriptors[pip->descriptor_count++]; + cd = (Constant_Buffer_Descriptor*)d->payload; + d->slot = binding; + d->type = Descriptor::Type::constant_buffer; + cd->buffer = id; +} + void Pipeline_Builder::vertex_format(Vertex_Format_Id vf) { pip->vertex_format = vf; } @@ -83,6 +94,10 @@ Pipeline& Pipeline_Builder::build() { h(pip->descriptor_resource_hash, td->sampler); h(pip->descriptor_resource_hash, td->texture); } break; + case Descriptor::Type::constant_buffer: { + auto cd = (Constant_Buffer_Descriptor*)d->payload; + h(pip->descriptor_resource_hash, cd->buffer); + } break; } } } diff --git a/sc/sc.cpp b/sc/sc.cpp index f7acc1a..e806dd4 100644 --- a/sc/sc.cpp +++ b/sc/sc.cpp @@ -156,6 +156,27 @@ struct Desc { int type; std::string name; std::string tname; + + std::pair size() const { + switch (type) { + case svariable_type_float: + return { 4, 4 }; + case svariable_type_vec2: + return { 8, 8 }; + case svariable_type_vec3: + return { 12, 16 }; + case svariable_type_vec4: + return { 16, 16 }; + } + assert(0); + return { 0, 0 }; + } + + int size(int coff) const { + auto [desired, align] = size(); + int pad = align_size(coff, align) - coff; + return desired + pad; + } }; struct Binding { SBinding_Rate rate; @@ -171,11 +192,24 @@ struct Desc { int slot; int stage; }; + struct Struct { + std::string name; + std::vector vars; + }; + struct CBuffer { + int size; + int stage; + int var_count; + std::string strct; + std::unordered_map offsets; + }; int type; std::vector bindings; std::vector trgts; std::vector interp; + std::unordered_map structs; std::unordered_map textures; + std::unordered_map cbuffers; std::vector descriptors; std::string entrypoints[shader_type_count]; void read_var(Variable& d, cfg_Object* desc) { @@ -243,6 +277,66 @@ struct Desc { t.stage |= 1 << stage_from_string(sstage); t.dimension = find_int_default(desc, "dimension", 2); } + cfg_Object* read_struct(cfg_Object* desc) { + const char* sname = find_string_default(desc, "name", 0); + if (!sname) { + print_err("%s must have a name.\n", desc->name); + pbreak(901); + } + std::string n = std::string(sname); + if (structs.contains(n)) { + print_err("Already a struct with the name %s.\n", sname); + pbreak(903); + } + desc = desc->next; + Struct& s = structs[n]; + while (desc && !strcmp(desc->name, "variable")) { + Variable v; + read_var(v, desc); + s.vars.push_back(v); + desc = desc->next; + } + return desc; + } + + void read_cbuffer(cfg_Object* desc) { + const char* sname = find_string_default(desc, "name", 0); + if (!sname) { + print_err("%s must have a name.\n", desc->name); + pbreak(951); + } + const char* stype = find_string_default(desc, "type", 0); + if (!stype) { + print_err("%s must define a type.\n", desc->name); + } + std::string n = std::string(sname); + std::string t = std::string(stype); + if (n.size() > 23) { + print_err("CBuffer name too long (max 23 chars).\n"); + pbreak(952); + } + if (!structs.contains(t)) { + print_err("No such struct %s\n", stype); + pbreak(953); + } + const char* sstage = find_string_default(desc, "stage", 0); + if (!sstage) { + print_err("%s must define a stage.\n", sname); + pbreak(954); + } + Struct& strct = structs[t]; + CBuffer& buf = cbuffers[n]; + int offset = 0; + buf.strct = t; + buf.stage |= 1 << stage_from_string(sstage); + buf.var_count = strct.vars.size(); + for (const auto& v : strct.vars) { + buf.offsets[v.name] = offset; + offset += v.size(offset); + } + buf.size = offset; + } + void build(cfg_Object* desc) { int i; Binding* cur_binding = 0; @@ -289,6 +383,11 @@ struct Desc { interp.push_back(v); } else if (!strcmp(desc->name, "texture")) { read_texture(desc); + } else if (!strcmp(desc->name, "cbuffer")) { + read_cbuffer(desc); + } else if (!strcmp(desc->name, "struct")) { + desc = read_struct(desc); + continue; } desc = desc->next; } @@ -311,7 +410,7 @@ struct Desc { void build_descriptors() { int slot = 0; - descriptors.resize(textures.size()); + descriptors.resize(textures.size() + cbuffers.size()); for (auto& i : textures) { Descriptor* d = find_desc(i.first.c_str()); d->name = i.first; @@ -319,6 +418,13 @@ struct Desc { d->stage = i.second.stage; slot++; } + for (auto& i : cbuffers) { + Descriptor* d = find_desc(i.first.c_str()); + d->name = i.first; + d->slot = slot; + d->stage = i.second.stage; + slot++; + } } void add_textures(std::stringstream& ss, Shader_Type stage) { @@ -334,6 +440,23 @@ struct Desc { } } + void add_cbuffers(std::stringstream& ss, Shader_Type stage) { + for (const auto& it : cbuffers) { + const auto& cbuffer = it.second; + if (cbuffer.stage & (1 << stage)) { + const Struct& s = structs[cbuffer.strct]; + Descriptor* d = find_desc(it.first.c_str()); + assert(d != 0); + ss << "layout (std140, binding = " << d->slot << ") "; + ss << "uniform " << cbuffer.strct << "{\n"; + for (const auto& v : s.vars) { + ss << v.tname << " " << v.name << ";\n"; + } + ss << "} " << it.first << ";\n"; + } + } + } + std::string build_vs() { std::stringstream ss; size_t i, li = bindings.size(); @@ -353,6 +476,7 @@ struct Desc { ss << i.tname << " " << i.name << ";\n"; ss << "} interpolator;\n"; add_textures(ss, shader_type_vertex); + add_cbuffers(ss, shader_type_vertex); return ss.str(); } @@ -370,6 +494,7 @@ struct Desc { ss << i.tname << " " << i.name << ";\n"; ss << "} interpolator;\n"; add_textures(ss, shader_type_fragment); + add_cbuffers(ss, shader_type_fragment); return ss.str(); } }; diff --git a/video.cpp b/video.cpp index 7335a32..7c88b54 100644 --- a/video.cpp +++ b/video.cpp @@ -479,7 +479,7 @@ struct Buffer_Vk : public Buffer, public Late_Terminated { r |= VK_BUFFER_USAGE_INDEX_BUFFER_BIT; if (flags & Buffer_Flags::vertex_buffer) r |= VK_BUFFER_USAGE_VERTEX_BUFFER_BIT; - if (flags & Buffer_Flags::uniform_buffer) + if (flags & Buffer_Flags::constant_buffer) r |= VK_BUFFER_USAGE_UNIFORM_BUFFER_BIT; if (flags & Buffer_Flags::storage_buffer) r |= VK_BUFFER_USAGE_STORAGE_BUFFER_BIT; @@ -2266,8 +2266,8 @@ void Pipeline_Vk::init_descriptors( case Descriptor::Type::texture: dst.descriptorType = VK_DESCRIPTOR_TYPE_COMBINED_IMAGE_SAMPLER; break; - default: - assert(0); + case Descriptor::Type::constant_buffer: + dst.descriptorType = VK_DESCRIPTOR_TYPE_UNIFORM_BUFFER; break; } dst.binding = src.slot; @@ -2382,8 +2382,8 @@ void Descriptor_Set_Vk::init( case Descriptor::Type::texture: sampler_count++; break; - default: - assert(0); + case Descriptor::Type::constant_buffer: + cbuffer_count++; break; } } @@ -2428,6 +2428,7 @@ void Descriptor_Set_Vk::init( } for (i = 0; i < count; i++) { VkDescriptorImageInfo img{}; + VkDescriptorBufferInfo buf{}; VkWriteDescriptorSet wd{}; auto& src = desc.descriptors[i]; wd.sType = VK_STRUCTURE_TYPE_WRITE_DESCRIPTOR_SET; @@ -2439,16 +2440,27 @@ void Descriptor_Set_Vk::init( switch (src.type) { case Descriptor::Type::texture: { Texture_Descriptor* td = (Texture_Descriptor*)src.payload; - assert(td->texture); - assert(td->sampler); Texture_Vk& t = *(Texture_Vk*)&dev->get_texture(td->texture); Sampler_Vk& s = *(Sampler_Vk*)&dev->samplers[td->sampler]; + assert(td->texture); + assert(td->sampler); img.imageView = t.view; img.sampler = s.sampler; img.imageLayout = VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL; wd.descriptorType = VK_DESCRIPTOR_TYPE_COMBINED_IMAGE_SAMPLER; wd.pImageInfo = &img; } break; + case Descriptor::Type::constant_buffer: { + Constant_Buffer_Descriptor* cd = + (Constant_Buffer_Descriptor*)src.payload; + Buffer_Vk& b = *(Buffer_Vk*)&dev->get_buffer(cd->buffer); + assert(cd->buffer); + buf.buffer = b.buf; + buf.offset = 0; + buf.range = b.size; + wd.descriptorType = VK_DESCRIPTOR_TYPE_UNIFORM_BUFFER; + wd.pBufferInfo = &buf; + } break; } vkUpdateDescriptorSets(dev->dev, 1, &wd, 0, 0); } @@ -2667,7 +2679,7 @@ int Shader::binding_index(const char* name) { Shader_Vk* sh = (Shader_Vk*)this; idx = sh->vfd.find_binding(name); if (idx < 0 || !sh->vfd.bindings[idx].name[0]) return -1; - return idx; + return sh->vfd.bindings[idx].index; } int Shader::attribute_index(const char* name) { @@ -2675,15 +2687,15 @@ int Shader::attribute_index(const char* name) { Shader_Vk* sh = (Shader_Vk*)this; idx = sh->vfd.find_attribute(name); if (idx < 0 || !sh->vfd.attributes[idx].name[0]) return -1; - return idx; + return sh->vfd.attributes[idx].index; } -int Shader::descriptor_index(const char* name) { +int Shader::descriptor_binding(const char* name) { int idx; Shader_Vk* sh = (Shader_Vk*)this; idx = sh->find_descriptor(name); if (idx < 0 || !sh->descs[idx].name[0]) return -1; - return idx; + return sh->descs[idx].slot; } int Shader::descriptor_stage(int slot) { diff --git a/video.hpp b/video.hpp index 3e01ab9..7639b50 100644 --- a/video.hpp +++ b/video.hpp @@ -48,7 +48,8 @@ struct Sampler_Id : public Primitive_Id { struct Descriptor { enum class Type { - texture + texture, + constant_buffer } type; int slot; uint8_t payload[descriptor_payload_size]; @@ -59,7 +60,12 @@ struct Texture_Descriptor { Sampler_Id sampler; }; +struct Constant_Buffer_Descriptor { + Buffer_Id buffer; +}; + static_assert(sizeof(Texture_Descriptor) <= descriptor_payload_size); +static_assert(sizeof(Constant_Buffer_Descriptor) <= descriptor_payload_size); #define pipeline_max_descriptors 16 @@ -103,6 +109,13 @@ struct Pipeline { if (ta->texture != tb->texture) return false; if (ta->sampler != tb->sampler) return false; } break; + case Descriptor::Type::constant_buffer: { + Constant_Buffer_Descriptor* ca = + (Constant_Buffer_Descriptor*)a.payload; + Constant_Buffer_Descriptor* cb = + (Constant_Buffer_Descriptor*)b.payload; + if (ca->buffer != cb->buffer) return false; + } break; } } return true; @@ -157,6 +170,7 @@ struct Pipeline_Builder { void shader(Shader_Id s); void vertex_format(Vertex_Format_Id vf); void texture(int binding, Texture_Id t, Sampler_Id s); + void cbuffer(int binding, Buffer_Id id); Pipeline& build(); void validate(); }; @@ -176,14 +190,14 @@ struct Texture : public Asset { namespace Buffer_Flags { enum { - index_buffer = 1 << 0, - vertex_buffer = 1 << 1, - uniform_buffer = 1 << 2, - storage_buffer = 1 << 3, - cpu_read = 1 << 4, - cpu_readwrite = 1 << 5, - copy_src = 1 << 6, - copy_dst = 1 << 7 + index_buffer = 1 << 0, + vertex_buffer = 1 << 1, + constant_buffer = 1 << 2, + storage_buffer = 1 << 3, + cpu_read = 1 << 4, + cpu_readwrite = 1 << 5, + copy_src = 1 << 6, + copy_dst = 1 << 7 }; }; @@ -301,7 +315,7 @@ struct Shader : public Asset { int binding_index(const char* name); int attribute_index(const char* name); int target_index(const char* name); - int descriptor_index(const char* name); + int descriptor_binding(const char* name); int descriptor_stage(int slot); }; -- cgit v1.2.3-54-g00ecf