From 5bc8f90c38981045515bab04d26687f929f62ec1 Mon Sep 17 00:00:00 2001 From: quou Date: Sat, 21 Dec 2024 21:25:22 +1100 Subject: render a triangle --- c2.cpp | 45 +- intermediate/triangle.glsl | 13 +- pipeline.cpp | 24 + qstd/str.c | 9 + qstd/str.h | 1 + sc/Makefile | 2 +- sc/sc.cpp | 138 ++++- sc/sh_enums.h | 11 + video.cpp | 1189 +++++++++++++++++++++++++++++++++++++++++--- video.hpp | 83 +++- 10 files changed, 1412 insertions(+), 103 deletions(-) diff --git a/c2.cpp b/c2.cpp index bb1b2e1..92cff0b 100644 --- a/c2.cpp +++ b/c2.cpp @@ -4,10 +4,17 @@ extern "C" { #include "plat.h" #include "memory.h" } +#include #define video_arena_size (1024 * 1024 * 16) #define per_frame_memory_size (1024 * 1024) +static float verts[] = { + 0.5f, 0.5f, 1.0f, 0.0f, 0.0f, + -0.5f, 0.5f, 0.0f, 1.0f, 0.0f, + 0.0f, -0.5f, 0.0f, 0.0f, 1.0f +}; + struct C2 : public App { Device* dev; void on_resize() override { @@ -18,7 +25,8 @@ struct C2 : public App { int main() { Arena video_arena; Device* dev; - Shader* shader; + Shader_Id shader; + Buffer_Id vbo; C2* app = App::create("c2"); void* per_frame; app->running = 1; @@ -30,12 +38,23 @@ int main() { dev = Device::create(&video_arena, app); app->dev = dev; shader = dev->load_shader("data/triangle.csh"); - per_frame = heap_alloc( dev->heap, per_frame_memory_size ); + assert(per_frame != 0); uint8_t r = 0; + vbo = dev->create_buffer( + sizeof verts, + Buffer_Flags::vertex_buffer | + Buffer_Flags::cpu_readwrite + ); + { + void* mem; + mem = dev->map_buffer(vbo, 0, sizeof verts); + memcpy(mem, verts, sizeof verts); + dev->unmap_buffer(vbo); + } while (app->running) { Arena frame_arena; init_arena(&frame_arena, per_frame, per_frame_memory_size); @@ -48,13 +67,31 @@ int main() { pb.rp_target(dev->get_backbuffer(), { r, 0x00, 0xff, 0xff }); Render_Pass& pass = pb.build_rp(); - dev->get_ctx().submit(*dev, pass); + pb.begin(); + pb.shader(shader); + pb.vertex_format(dev->get_shader(shader).vf); + Pipeline& pip = pb.build(); + + Vertex_Buffer_Binding binding[] = {{ + .id = vbo, + .offset = 0, + .target = dev->get_shader(shader).binding_index("verts") + }, {}}; + + Draw draw{}; + draw.verts = binding; + draw.vertex_count = 3; + draw.instance_count = 1; + + dev->get_ctx().submit(*dev, draw, pip, pass); + /* dev->get_ctx().submit(*dev, pass);*/ r += 10; dev->present(); app->end(); } - shader->destroy(dev); + dev->destroy_buffer(vbo); + dev->destroy_shader(shader); dev->destroy(); app->destroy(); } diff --git a/intermediate/triangle.glsl b/intermediate/triangle.glsl index ed6052f..04f66d3 100644 --- a/intermediate/triangle.glsl +++ b/intermediate/triangle.glsl @@ -4,9 +4,19 @@ type: graphics vertex: main fragment: main +[binding] +name: verts +rate: vertex [attribute] name: position type: vec2 +[attribute] +name: colour +type: vec3 + +[interpolator] +name: colour +type: vec3 [target] name: colour @@ -16,6 +26,7 @@ type: vec4 #ifdef VERTEX_SHADER void main() { + interpolator.colour = vec3(colour); gl_Position = vec4(position, 0.0, 1.0); } @@ -24,7 +35,7 @@ void main() { #ifdef FRAGMENT_SHADER void main() { - colour = 1.0.xxxx; + colour = vec4(interpolator.colour, 1.0); } #endif diff --git a/pipeline.cpp b/pipeline.cpp index 30bf442..ce95672 100644 --- a/pipeline.cpp +++ b/pipeline.cpp @@ -27,3 +27,27 @@ Render_Pass& Pipeline_Builder::build_rp() { validate_rp(); return *pass; } + +void Pipeline_Builder::begin() { + pip = (Pipeline*)arena_alloc(arena, sizeof *pip); + pip->vertex_format = 0; + pip->shader = 0; +} + +void Pipeline_Builder::shader(Shader_Id s) { + pip->shader = s; +} + +void Pipeline_Builder::vertex_format(Vertex_Format_Id vf) { + pip->vertex_format = vf; +} + +Pipeline& Pipeline_Builder::build() { + validate(); + return *pip; +} + +void Pipeline_Builder::validate() { + assert(pip->vertex_format); + assert(pip->shader); +} diff --git a/qstd/str.c b/qstd/str.c index a1c1aa1..b6b0eb7 100644 --- a/qstd/str.c +++ b/qstd/str.c @@ -9,3 +9,12 @@ uint64_t fnv1a64(uint8_t* buf, size_t size) { } return hash; } + +uint32_t hash_string(const char* s) { + uint32_t h = 2166136261u; + for (; *s; s++) { + h ^= *(uint8_t*)s; + h *= 16777619; + } + return h; +} diff --git a/qstd/str.h b/qstd/str.h index f55333e..da9bf36 100644 --- a/qstd/str.h +++ b/qstd/str.h @@ -5,5 +5,6 @@ #include uint64_t fnv1a64(uint8_t* buf, size_t size); +uint32_t hash_string(const char* s); #endif diff --git a/sc/Makefile b/sc/Makefile index ce8a24d..6ec1cc3 100644 --- a/sc/Makefile +++ b/sc/Makefile @@ -31,7 +31,7 @@ objects = sc.o includer.o all: $(target) -sc.o: sc.cpp includer.hpp +sc.o: sc.cpp includer.hpp sh_enums.h $(CXX) -c $(cflags) sc.cpp -o sc.o includer.o: includer.cpp includer.hpp diff --git a/sc/sc.cpp b/sc/sc.cpp index 617d5ce..6218f7a 100644 --- a/sc/sc.cpp +++ b/sc/sc.cpp @@ -88,6 +88,12 @@ static const char* type_strings[] = { #undef x }; +static const char* rate_strings[] = { +#define x(n) #n, + sbinding_rate_xmacro() +#undef x +}; + int get_program_type(cfg_Object* desc) { int i; const char* s = find_string_default(desc, "type", 0); @@ -114,36 +120,75 @@ int type_from_string(const char* s) { return 0; } +SBinding_Rate rate_from_string(const char* s) { + int i; + for (i = 0; i < sbinding_rate_count; i++) { + if (!strcmp(rate_strings[i], s)) + return (SBinding_Rate)i; + } + print_err("Invalid binding rate %s\n", s); + pbreak(315); + return (SBinding_Rate)0; +} + struct Desc { struct Variable { int type; std::string name; std::string tname; }; + struct Binding { + SBinding_Rate rate; + std::string name; + std::vector attrs; + }; int type; - std::vector attrs; + std::vector bindings; std::vector trgts; + std::vector interp; std::string entrypoints[shader_type_count]; void read_var(Variable& d, cfg_Object* desc) { - const char* stype = find_string_default(desc, "type", 0); - if (!stype) { - print_err("%s needs to have a type\n", desc->name); - pbreak(303); - } const char* sname = find_string_default(desc, "name", 0); if (!sname) { print_err("%s needs to have a name\n", desc->name); + pbreak(303); + } + const char* stype = find_string_default(desc, "type", 0); + if (!stype) { + print_err("%s needs to have a type\n", sname); pbreak(304); } d.type = type_from_string(stype); d.tname = std::string(stype); d.name = std::string(sname); - if (d.name.size() > 28) { - print_err("variable name %s is too long (max 28 chars).\n", sname); + if (d.name.size() > 27) { + print_err("variable name %s is too long (max 27 chars).\n", sname); pbreak(305); } } + Binding* read_binding(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(310); + } + const char* srate = find_string_default(desc, "rate", 0); + if (!srate) { + print_err("%s must specify a rate.\n", sname); + pbreak(311); + } + Binding& b = bindings.emplace_back(); + b.rate = rate_from_string(srate); + b.name = std::string(sname); + if (b.name.size() > 23) { + print_err("Binding name %s is too long (max 23 chars).\n", sname); + pbreak(312); + } + return &b; + } void build(cfg_Object* desc) { + int i; + Binding* cur_binding = 0; type = get_program_type(desc); if (type != sprogram_type_graphics) { assert(0); /* todo */ @@ -161,15 +206,30 @@ struct Desc { pbreak(302); } entrypoints[shader_type_fragment] = sf; + for (i = 0; i < (int)shader_type_count; i++) { + if (entrypoints[i].size() > 23) { + print_err("Entry point name is too long.\n"); + pbreak(3000); + } + } desc = desc->next; while (desc) { Variable v; - if (!strcmp(desc->name, "attribute")) { + if (!strcmp(desc->name, "binding")) { + cur_binding = read_binding(desc); + } else if (!strcmp(desc->name, "attribute")) { + if (!cur_binding) { + print_err("Can't specify a vertex attribute with no binding.\n"); + pbreak(303); + } read_var(v, desc); - attrs.push_back(v); + cur_binding->attrs.push_back(v); } else if (!strcmp(desc->name, "target")) { read_var(v, desc); trgts.push_back(v); + } else if (!strcmp(desc->name, "interpolator")) { + read_var(v, desc); + interp.push_back(v); } desc = desc->next; } @@ -177,13 +237,22 @@ struct Desc { std::string build_vs() const { std::stringstream ss; - size_t i, l = attrs.size(); - for (i = 0; i < l; i++) { - auto& attr = attrs[i]; - ss << "layout (location = 0) in "; - ss << attr.tname << " "; - ss << attr.name << ";\n"; + size_t i, li = bindings.size(); + size_t l = 0; + for (i = 0; i < li; i++) { + auto& binding = bindings[i]; + size_t j, lj = binding.attrs.size(); + for (j = 0; j < lj; j++, l++) { + auto& attr = binding.attrs[j]; + ss << "layout (location = " << l << ") in "; + ss << attr.tname << " "; + ss << attr.name << ";\n"; + } } + ss << "layout (location = 0) out _Interpolator {\n"; + for (const auto& i : interp) + ss << i.tname << " " << i.name << ";\n"; + ss << "} interpolator;\n"; return ss.str(); } @@ -196,6 +265,10 @@ struct Desc { ss << attr.tname << " "; ss << attr.name << ";\n"; } + ss << "layout (location = 0) in _Interpolator {\n"; + for (const auto& i : interp) + ss << i.tname << " " << i.name << ";\n"; + ss << "} interpolator;\n"; return ss.str(); } }; @@ -223,6 +296,13 @@ std::vector compile_shader( Includer inc; std::string prepr; std::vector spv; +#ifdef DEBUG + options.disableOptimizer = true; + options.generateDebugInfo = true; +#else + options.disableOptimizer = false; + options.stripDebugInfo = true; +#endif EShMessages msg = (EShMessages)(EShMsgSpvRules | EShMsgVulkanRules); shader.setStrings(srcs, sizeof srcs / sizeof *srcs); shader.setEnvClient(glslang::EShClientVulkan, client_version); @@ -323,14 +403,24 @@ void write_csh( int c; fwrite("CSH2", 4, 1, f); fwrite(&d.type, 4, 1, f); - c = d.attrs.size(); fwrite(&c, 4, 1, f); + c = d.bindings.size(); fwrite(&c, 4, 1, f); c = d.trgts.size(); fwrite(&c, 4, 1, f); - for (const auto& a : d.attrs) { - char buf[28]; + for (const auto& b : d.bindings) { + char buf[24]; + int count = b.attrs.size(); memset(buf, 0, sizeof buf); - strcpy(buf, a.name.c_str()); + strcpy(buf, b.name.c_str()); fwrite(buf, 1, sizeof buf, f); - fwrite(&a.type, 4, 1, f); + fwrite(&b.rate, 4, 1, f); + fwrite(&count, 4, 1, f); + for (const auto& a : b.attrs) { + char buf[28]; + memset(buf, 0, sizeof buf); + strcpy(buf, a.name.c_str()); + fwrite(buf, 1, sizeof buf, f); + fwrite(&a.type, 4, 1, f); + hsize += 32; + } hsize += 32; } for (const auto& t : d.trgts) { @@ -341,19 +431,23 @@ void write_csh( fwrite(&t.type, 4, 1, f); hsize += 32; } - hsize += shader_type_count * 8; + hsize += shader_type_count * 32; for (i = 0, coff = 0; i < shader_type_count; i++) { int o = 0; + char buf[24]; + memset(buf, 0, sizeof buf); if (d.entrypoints[i].empty()) { fwrite(&o, 4, 1, f); fwrite(&o, 4, 1, f); } else { int size = stages[i].size() * sizeof(uint32_t); + strcpy(buf, d.entrypoints[i].c_str()); o = hsize + coff; fwrite(&o, 4, 1, f); fwrite(&size, 4, 1, f); coff += size; } + fwrite(buf, 1, sizeof buf, f); } for (i = 0; i < shader_type_count; i++) if (!d.entrypoints[i].empty()) { diff --git a/sc/sh_enums.h b/sc/sh_enums.h index 8f3c2a5..9e62a9d 100644 --- a/sc/sh_enums.h +++ b/sc/sh_enums.h @@ -38,4 +38,15 @@ svariable_type_xmacro() svariable_type_count } SVariable_Type; +#define sbinding_rate_xmacro() \ + x(vertex) \ + x(instance) + +typedef enum { +#define x(n) sbinding_rate_ ## n, + sbinding_rate_xmacro() +#undef x + sbinding_rate_count +} SBinding_Rate; + #endif diff --git a/video.cpp b/video.cpp index c68df60..9e2ca05 100644 --- a/video.cpp +++ b/video.cpp @@ -1,8 +1,13 @@ #include "app.hpp" #include "video.hpp" -#define device_heap_size (1024 * 1024 * 4) +#define device_heap_size (1024 * 1024 * 8) #define max_textures 1024 +#define max_buffers 1024 +#define max_vertex_formats 64 +#define max_rpos 64 +#define max_pipelines 64 +#define max_shaders 32 extern "C" { #include "memory.h" @@ -33,42 +38,140 @@ void app_destroy_vk_surface( ); } -template -struct ID_Map -{ - T storage[size]; - ID keys[size]; +struct Device_Vk; + +template +struct Hash_Function {}; + +template +struct Hash_Map { + enum { + flags_tombstone = 1 << 0, + flags_null = 1 << 1 + }; + + Key keys[size]; + Value values[size]; + uint8_t flags[size]; void init() { int i; + for (i = 0; i < size; i++) flags[i] = flags_null; + } + + int find(const Key& to_find) { + int tombstone = -1, i; + int bucket = (int)(Hash_Function{}(to_find) % (size_t)size); for (i = 0; i < size; i++) { - keys[i] = 0; + Key& k = keys[bucket]; + uint8_t flag = flags[bucket]; + if (flag & flags_null) { + if (flag & flags_tombstone) { + if (tombstone < 0) tombstone = bucket; + } else return tombstone >= 0? tombstone: bucket; + } else if (k == to_find) return bucket; + bucket = (bucket + 1) % size; } + if (tombstone >= 0) return tombstone; + return -1; } - std::pair bucket(ID id) { - int index = (int)id % size, i; - for (i = 0; i < size; i++) { - ID key = keys[index]; - if (!key || key == id) return { &storage[index], index }; - index = (index + 1) % size; + Value& set(const Key& k, const Value& v) { + int bucket = find(k); + assert(bucket >= 0); /* full */ + flags[bucket] = 0; + keys[bucket] = k; + values[bucket] = v; + return values[bucket]; + } + + Value* get(const Key& k) { + int bucket = find(k); + if (bucket < 0 || flags[bucket] & flags_null) return 0; + return &values[bucket]; + } + + Value& operator[](const Key& k) { + int bucket = find(k); + assert(bucket >= 0); + return values[bucket]; + } + + void remove(const Key& k) { + int bucket = find(k); + assert(bucket >= 0); + flags[bucket] = flags_null | flags_tombstone; + } + + int has(const Key& k) { + int bucket = find(k); + return bucket >= 0 && ~flags[bucket] & flags_null; + } + + template + struct iterator { + Table* table; + int bucket; + + void init_begin(Table* t) { + bucket = 0; + table = t; + while ( + bucket < size && + table->flags[bucket] & flags_null + ) bucket++; + } + void init_end(Table* t) { + bucket = size; + table = t; + } + bool equals(const iterator& other) { + return bucket == other.bucket && table == other.table; + } + bool operator==(const iterator
& other) { + return equals(other); } - return { 0, 0 }; + bool operator!=(const iterator
& other) { + return !equals(other); + } + iterator
operator++() { + bucket++; + while ( + bucket < size && + table->flags[bucket] & flags_null + ) bucket++; + return *this; + } + std::pair operator*() { + return { table->keys[bucket], table->values[bucket] }; + } + std::pair operator*() const { + return { table->keys[bucket], table->values[bucket] }; + } + }; + + iterator> begin() { + iterator> r; + r.init_begin(this); + return r; } - T& set(ID id, const T& v) { - auto [b, index] = bucket(id); - assert(b != 0); - assert(!keys[index]); - keys[index] = id; - *b = v; - return *b; + iterator> end() { + iterator> r; + r.init_end(this); + return r; } - T& operator[](ID id) { - T* b = bucket(id).first; - assert(b != 0); - return *b; + iterator> begin() const { + iterator> r; + r.init_begin(this); + return r; + } + + iterator> end() const { + iterator> r; + r.init_end(this); + return r; } }; @@ -191,7 +294,6 @@ static void deinit_swap_cap( if (cap->pms) heap_free(d->heap, cap->pms); } -struct Device_Vk; struct Swapchain { VkSwapchainKHR swapchain; Texture_Id* textures; @@ -215,8 +317,37 @@ enum { }; struct Shader_Vk : public Shader { + struct Attribute { + char name[28]; + SVariable_Type type; + int index; + }; + + struct Binding { + char name[24]; + SBinding_Rate rate; + int attr_count; + int index; + int* attributes; + }; + + struct Vertex_Format { + Binding* bindings; + Attribute* attributes; + int attr_count; + int binding_count; + + bool init(Device_Vk* dev, FILE* f); + void destroy(Device_Vk* dev); + + int find_binding(const char* name); + int find_attribute(const char* name); + }; + SProgram_Type type; VkShaderModule modules[shader_type_count]; + char entrypoints[shader_type_count][24]; + Vertex_Format vfd; bool init(Device_Vk* dev, FILE* f); bool init_module( @@ -225,9 +356,26 @@ struct Shader_Vk : public Shader { char* buf, int size ); + bool init_vertex_format( + Device_Vk* dev, + FILE* f + ); void destroy_internal(Device_Vk* dev); + + static VkShaderStageFlagBits stage(Shader_Type type) { + switch (type) { + case shader_type_vertex: + return VK_SHADER_STAGE_VERTEX_BIT; + case shader_type_fragment: + return VK_SHADER_STAGE_FRAGMENT_BIT; + default: + assert(0); + return (VkShaderStageFlagBits)0; + } + } }; +struct Renderpass_Vk; struct Context_Vk : public Context { int state; VkCommandBuffer cb; @@ -243,6 +391,9 @@ struct Context_Vk : public Context { Context_Vk& acquire(Device_Vk* dev); void release(); void destroy(Device_Vk* dev); + + Renderpass_Vk& begin_rp(Device& d, const Render_Pass& rp); + void end_rp(Renderpass_Vk& rpo); }; struct Texture_Vk : public Texture { @@ -250,6 +401,43 @@ struct Texture_Vk : public Texture { VkImageView view; }; +struct Buffer_Vk : public Buffer { + VkBuffer buf; + VkDeviceMemory memory; + VkDeviceSize size; + int flags; + + void init(Device_Vk* dev, int flags, VkDeviceSize size); + void destroy(Device_Vk* dev); + + static VkBufferUsageFlags get_usage(int flags) { + VkBufferUsageFlags r = 0; + if (flags & Buffer_Flags::index_buffer) + 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) + r |= VK_BUFFER_USAGE_UNIFORM_BUFFER_BIT; + if (flags & Buffer_Flags::storage_buffer) + r |= VK_BUFFER_USAGE_STORAGE_BUFFER_BIT; + if (flags & Buffer_Flags::copy_src) + r |= VK_BUFFER_USAGE_TRANSFER_SRC_BIT; + if (flags & Buffer_Flags::copy_dst) + r |= VK_BUFFER_USAGE_TRANSFER_DST_BIT; + return r; + } + + static VkMemoryPropertyFlags get_memory_flags(int flags) { + VkMemoryPropertyFlags r = 0; + r |= VK_MEMORY_PROPERTY_DEVICE_LOCAL_BIT; + if (flags & Buffer_Flags::cpu_read) + r |= VK_MEMORY_PROPERTY_HOST_VISIBLE_BIT; + if (flags & Buffer_Flags::cpu_readwrite) + r |= VK_MEMORY_PROPERTY_HOST_COHERENT_BIT; + return r; + } +}; + struct Renderpass_Vk { VkRenderPass rpo; VkFramebuffer fbo; @@ -265,11 +453,146 @@ struct Renderpass_Vk { void destroy(Device_Vk* dev); }; +struct Pso_Key { + Pipeline pip; + Render_Pass rpo; + + bool operator==(const Pso_Key& other) const { + int size = sizeof *this, i; + uint8_t* bba = (uint8_t*)this; + uint8_t* bbb = (uint8_t*)&other; + for (i = 0; i < size; i++, bba++, bbb++) + if (*bba != *bbb) return false; + return true; + } +}; + +struct Pipeline_Vk { + VkPipeline pip; + VkPipelineLayout lay; + int age; + + void init(Device_Vk* dev, const Pso_Key& desc); + void destroy(Device_Vk* dev); + + void init_stages( + Arena& scope, + Device_Vk* dev, + VkGraphicsPipelineCreateInfo& info, + const Pipeline& desc + ); + void init_vertex_input( + Arena& scope, + Device_Vk* dev, + VkGraphicsPipelineCreateInfo& info, + const Pipeline& desc + ); + void init_input_assembly( + Arena& scope, + Device_Vk* dev, + VkGraphicsPipelineCreateInfo& info, + const Pipeline& desc + ); + void init_viewport( + Arena& scope, + Device_Vk* dev, + VkGraphicsPipelineCreateInfo& info, + const Render_Pass& desc + ); + void init_rasterisation( + Arena& scope, + Device_Vk* dev, + VkGraphicsPipelineCreateInfo& info, + const Pipeline& desc + ); + void init_msaa( + Arena& scope, + Device_Vk* dev, + VkGraphicsPipelineCreateInfo& info, + const Pipeline& desc + ); + void init_depthstencil( + Arena& scope, + Device_Vk* dev, + VkGraphicsPipelineCreateInfo& info, + const Pipeline& desc + ); + void init_blending( + Arena& scope, + Device_Vk* dev, + VkGraphicsPipelineCreateInfo& info, + const Pipeline& desc + ); + void init_layout( + Device_Vk* dev, + const Pipeline& desc + ); + + void on_submit() { + age = 0; + } +}; + +struct Vertex_Format_Vk { + VkVertexInputBindingDescription* bindings; + int binding_count; + VkVertexInputAttributeDescription* attrs; + int attr_count; + + void from_shader(Device_Vk* dev, Shader_Vk& s); + void destroy(Device_Vk* dev); + static int svariable_type_size(SVariable_Type type); + static VkFormat format_from_svar_type(SVariable_Type type); +}; + template<> -struct std::hash +struct Hash_Function { size_t operator()(const Render_Pass& rp) const { - return fnv1a64((uint8_t*)&rp, sizeof rp); + return (size_t)fnv1a64((uint8_t*)&rp, sizeof rp); + } +}; + +template<> +struct Hash_Function +{ + size_t operator()(const Pso_Key& rp) const { + return (size_t)fnv1a64((uint8_t*)&rp, sizeof rp); + } +}; + +template<> +struct Hash_Function { + size_t operator()(Texture_Id id) const { + return id.index; + } +}; + +template<> +struct Hash_Function { + size_t operator()(Buffer_Id id) const { + return id.index; + } +}; + +template<> +struct Hash_Function { + size_t operator()(Shader_Id id) const { + return id.index; + } +}; + +template<> +struct Hash_Function { + size_t operator()(Vertex_Format_Id id) const { + return id.index; + } +}; + +template<> +struct std::hash { + size_t operator()(const Render_Pass& rp) const { + return (size_t)fnv1a64((uint8_t*)&rp, sizeof rp); } }; @@ -282,6 +605,7 @@ struct Device_Vk : public Device { uint32_t backbuffer_index; Texture_Id backbuffer_id; Swap_Cap swap_cap; + VkPhysicalDeviceMemoryProperties mem_props; int queue_index; VkQueue queue; Swapchain swapchain; @@ -291,16 +615,32 @@ struct Device_Vk : public Device { VkDebugUtilsMessengerEXT msg; #endif - ID_Map textures; - Texture_Id texture_count; - - std::unordered_map rpo_cache; + Hash_Map textures; + Hash_Map buffers; + Hash_Map< + Vertex_Format_Id, + Vertex_Format_Vk, + max_vertex_formats + > vertex_formats; + Hash_Map shaders; + uint32_t texture_count; + uint32_t buffer_count; + uint32_t vertex_format_count; + uint32_t shader_count; + + Hash_Map rpo_cache; + Hash_Map pso_cache; Texture_Id alloc_texture( VkImage img, VkImageView view, const Texture& copy ); + Buffer_Id alloc_buffer(); + Vertex_Format_Id alloc_vf(); + Vertex_Format_Id create_vf(Shader_Vk& shader); + void destroy_vf(Vertex_Format_Id id); + Shader_Id alloc_shader(); void init_internal(); void deinit_internal(); @@ -317,8 +657,15 @@ struct Device_Vk : public Device { Renderpass_Vk& create_rpo(const Render_Pass& rp); Renderpass_Vk& get_rpo(const Render_Pass& rp); + Pipeline_Vk& create_pso(const Pso_Key& pip); + Pipeline_Vk& get_pso(const Pso_Key& pop); void collect_garbage(); + + int find_memory_type( + uint32_t filter, + VkMemoryPropertyFlags flags + ); }; #ifdef DEBUG @@ -585,6 +932,7 @@ void Device_Vk::create_dev(Swap_Cap* swap_cap) { VkPhysicalDeviceFeatures pdf{}; VkResult r; phys_dev = get_phys_dev(this, swap_cap); + vkGetPhysicalDeviceMemoryProperties(phys_dev, &mem_props); qi.sType = VK_STRUCTURE_TYPE_DEVICE_QUEUE_CREATE_INFO; qi.queueFamilyIndex = queue_index; qi.queueCount = 1; @@ -615,6 +963,14 @@ void Device_Vk::init_internal() { gladLoaderLoadVulkan(0, 0, 0); textures.init(); texture_count = 1; + buffers.init(); + buffer_count = 1; + vertex_formats.init(); + vertex_format_count = 1; + shaders.init(); + shader_count = 1; + rpo_cache.init(); + pso_cache.init(); find_exts(exts, ext_count); init_ac(); create_inst(exts, ext_count); @@ -637,7 +993,9 @@ void Device_Vk::deinit_internal() { swapchain.destroy(this); deinit_swap_cap(this, &swap_cap); app_destroy_vk_surface(app, inst, surf); - for (auto& i : rpo_cache) + for (auto i : rpo_cache) + i.second.destroy(this); + for (auto i : pso_cache) i.second.destroy(this); for (i = 0; i < max_contexts; i++) { auto& context = contexts[i]; @@ -675,15 +1033,28 @@ Renderpass_Vk& Device_Vk::create_rpo(const Render_Pass& rp) { rpo.init_fb(this, rp); rpo.age = 0; rpo.clear = clear; - rpo_cache[rp] = rpo; - return rpo_cache[rp]; + return rpo_cache.set(rp, rpo); } Renderpass_Vk& Device_Vk::get_rpo(const Render_Pass& rp) { - auto rpo_index = rpo_cache.find(rp); - if (rpo_index == rpo_cache.end()) + Renderpass_Vk* rpo = rpo_cache.get(rp); + if (!rpo) return create_rpo(rp); - return rpo_index->second; + return *rpo; +} + +Pipeline_Vk& Device_Vk::create_pso(const Pso_Key& pip) { + Pipeline_Vk pso; + pso.age = 0; + pso.init(this, pip); + return pso_cache.set(pip, pso); +} + +Pipeline_Vk& Device_Vk::get_pso(const Pso_Key& pip) { + Pipeline_Vk* pso = pso_cache.get(pip); + if (!pso) + return create_pso(pip); + return *pso; } void Renderpass_Vk::destroy(Device_Vk* dev) { @@ -692,14 +1063,37 @@ void Renderpass_Vk::destroy(Device_Vk* dev) { } void Device_Vk::collect_garbage() { - for (auto i = rpo_cache.begin(); i != rpo_cache.end();) { - auto& rp = i->second; + for (const auto& i: rpo_cache) { + auto& rp = i.second; rp.age++; if (rp.age > 3) { rp.destroy(this); - i = rpo_cache.erase(i); - } else ++i; + rpo_cache.remove(i.first); + } + } + for (const auto& i: pso_cache) { + auto& pip = i.second; + pip.age++; + if (pip.age > 3) { + pip.destroy(this); + pso_cache.remove(i.first); + } + } +} + +int Device_Vk::find_memory_type( + uint32_t filter, + VkMemoryPropertyFlags flags +) { + int i, e = mem_props.memoryTypeCount; + auto* types = mem_props.memoryTypes; + for (i = 0; i < e; i++) { + if ( + (filter & (1 << i)) && + (types[i].propertyFlags & flags) == flags + ) return i; } + return -1; } void Renderpass_Vk::init_rp( @@ -711,6 +1105,7 @@ void Renderpass_Vk::init_rp( VkAttachmentReference ar{}; VkSubpassDescription sd{}; VkResult r; + (void)rp; ad.format = dev->swapchain.format.format; ad.samples = VK_SAMPLE_COUNT_1_BIT; @@ -856,7 +1251,6 @@ void Swapchain::initr(const App& app, Device_Vk* dev) { pbreak(r); } } - textures = (Texture_Id*)heap_alloc(dev->heap, sizeof *textures * image_count); get_images(dev); } @@ -870,16 +1264,22 @@ void Swapchain::recreate(const App& app, Device_Vk* dev) { void Swapchain::get_images(Device_Vk* dev) { unsigned count; int i; - VkImage* images = (VkImage*)heap_alloc( - dev->heap, - sizeof *images * image_count - ); + VkImage* images; Texture info{}; info.w = size.width; info.h = size.height; info.alias = true; + vkGetSwapchainImagesKHR(dev->dev, swapchain, &count, 0); + image_count = count; + images = (VkImage*)heap_alloc( + dev->heap, + sizeof *images * image_count + ); + textures = (Texture_Id*)heap_alloc( + dev->heap, + sizeof *textures * image_count + ); vkGetSwapchainImagesKHR(dev->dev, swapchain, &count, images); - assert(count == (unsigned)image_count); for (i = 0; i < image_count; i++) { VkImageView view = make_view(dev, images[i], @@ -991,9 +1391,8 @@ Texture_Id Device_Vk::alloc_texture( VkImageView view, const Texture& copy ) { - Texture_Id id = texture_count++; - Texture_Vk tex; - assert(id < max_textures); + Texture_Vk tex{}; + Texture_Id id(texture_count++); memcpy(&tex, ©, sizeof(Texture)); tex.image = img; tex.view = view; @@ -1001,12 +1400,46 @@ Texture_Id Device_Vk::alloc_texture( return id; } +Buffer_Id Device_Vk::alloc_buffer() { + Buffer_Vk buf{}; + Buffer_Id id(buffer_count++); + buffers.set(id, buf); + return id; +} + +Vertex_Format_Id Device_Vk::alloc_vf() { + Vertex_Format_Vk vf{}; + Vertex_Format_Id id(vertex_format_count++); + vertex_formats.set(id, vf); + return id; +} +Vertex_Format_Id Device_Vk::create_vf( + Shader_Vk& shader +) { + Vertex_Format_Id id = alloc_vf(); + vertex_formats[id].from_shader(this, shader); + return id; +} +void Device_Vk::destroy_vf(Vertex_Format_Id id) { + Vertex_Format_Vk& vf = vertex_formats[id]; + vf.destroy(this); +} + +Shader_Id Device_Vk::alloc_shader() { + Shader_Vk buf{}; + Shader_Id id(shader_count++); + assert(id.index < max_shaders); + shaders.set(id, buf); + return id; +} + void Device::destroy_texture(Texture_Id id) { Device_Vk* dev = (Device_Vk*)this; Texture_Vk& tex = dev->textures[id]; if (!tex.alias) vkDestroyImage(dev->dev, tex.image, &dev->ac); vkDestroyImageView(dev->dev, tex.view, &dev->ac); + dev->textures.remove(id); } void Context::wait(Device& d) { @@ -1028,12 +1461,30 @@ void Context::submit( const Render_Pass& rp ) { Device_Vk* dev = (Device_Vk*)&d; - (void)draw; - (void)p; - (void)rp; - (void)dev; - assert(0); - /* todo */ + Context_Vk* ctx = (Context_Vk*)this; + Vertex_Buffer_Binding* binding; + Pso_Key pso_key = { p, rp }; + Pipeline_Vk& pso = dev->get_pso(pso_key); + auto& rpo = ctx->begin_rp(d, rp); + vkCmdBindPipeline( + ctx->cb, + VK_PIPELINE_BIND_POINT_GRAPHICS, + pso.pip + ); + for (binding = draw.verts; binding->id; binding++) { + VkBuffer buf = ((Buffer_Vk*)&dev->get_buffer(binding->id))->buf; + VkDeviceSize offset = (VkDeviceSize)binding->offset; + vkCmdBindVertexBuffers(ctx->cb, 0, 1, &buf, &offset); + } + vkCmdDraw( + ctx->cb, + draw.vertex_count, + draw.instance_count, + draw.first_vertex, + draw.first_instance + ); + ctx->end_rp(rpo); + pso.on_submit(); } void Context::submit( @@ -1053,9 +1504,8 @@ void Context::submit( /* todo */ } -void Context::submit(Device& d, const Render_Pass& rp) { +Renderpass_Vk& Context_Vk::begin_rp(Device& d, const Render_Pass& rp) { Device_Vk* dev = (Device_Vk*)&d; - Context_Vk* ctx = (Context_Vk*)this; Renderpass_Vk& rpo = dev->get_rpo(rp); VkRenderPassBeginInfo rpbi{}; rpbi.sType = VK_STRUCTURE_TYPE_RENDER_PASS_BEGIN_INFO; @@ -1065,14 +1515,24 @@ void Context::submit(Device& d, const Render_Pass& rp) { rpbi.clearValueCount = 1; rpbi.pClearValues = &rpo.clear; vkCmdBeginRenderPass( - ctx->cb, + cb, &rpbi, VK_SUBPASS_CONTENTS_INLINE ); - vkCmdEndRenderPass(ctx->cb); + return rpo; +} + +void Context_Vk::end_rp(Renderpass_Vk& rpo) { + vkCmdEndRenderPass(cb); rpo.on_submit(); } +void Context::submit(Device& d, const Render_Pass& rp) { + Context_Vk* ctx = (Context_Vk*)this; + auto& rpo = ctx->begin_rp(d, rp); + ctx->end_rp(rpo); +} + void Context_Vk::init_pool(Device_Vk* dev) { VkCommandPoolCreateInfo pi{}; VkResult r; @@ -1171,10 +1631,362 @@ Context& Device::get_ctx() { return *vk->current_ctx; } +void Pipeline_Vk::init_stages( + Arena& scope, + Device_Vk* dev, + VkGraphicsPipelineCreateInfo& info, + const Pipeline& desc +) { + int count = 0, i; + Shader_Vk& shader = *(Shader_Vk*)&dev->get_shader(desc.shader); + for (i = 0; i < shader_type_count; i++) { + if (shader.modules[i]) + count++; + } + VkPipelineShaderStageCreateInfo* sis = + (VkPipelineShaderStageCreateInfo*)arena_alloc( + &scope, + sizeof *sis * count + ); + memset(sis, 0, sizeof *sis * count); + for (i = 0, count = 0; i < shader_type_count; i++) { + if (shader.modules[i]) { + auto& si = sis[i]; + si.sType = VK_STRUCTURE_TYPE_PIPELINE_SHADER_STAGE_CREATE_INFO; + si.flags = 0; + si.stage = Shader_Vk::stage((Shader_Type)i); + si.module = shader.modules[i]; + si.pName = shader.entrypoints[i]; + count++; + } + } + info.stageCount = count; + info.pStages = sis; +} + +void Pipeline_Vk::init_vertex_input( + Arena& scope, + Device_Vk* dev, + VkGraphicsPipelineCreateInfo& info, + const Pipeline& desc +) { + Vertex_Format_Vk& vf = dev->vertex_formats[desc.vertex_format]; + VkPipelineVertexInputStateCreateInfo& vi = + *(VkPipelineVertexInputStateCreateInfo*)arena_alloc( + &scope, + sizeof vi + ); + memset(&vi, 0, sizeof vi); + vi.sType = VK_STRUCTURE_TYPE_PIPELINE_VERTEX_INPUT_STATE_CREATE_INFO; + vi.vertexBindingDescriptionCount = vf.binding_count; + vi.pVertexBindingDescriptions = vf.bindings; + vi.vertexAttributeDescriptionCount = vf.attr_count; + vi.pVertexAttributeDescriptions = vf.attrs; + info.pVertexInputState = &vi; +} + +void Pipeline_Vk::init_input_assembly( + Arena& scope, + Device_Vk* dev, + VkGraphicsPipelineCreateInfo& info, + const Pipeline& desc +) { + VkPipelineInputAssemblyStateCreateInfo& ia = + *(VkPipelineInputAssemblyStateCreateInfo*)arena_alloc( + &scope, + sizeof ia + ); + (void)dev; + (void)desc; + (void)info; + memset(&ia, 0, sizeof ia); + ia.sType = VK_STRUCTURE_TYPE_PIPELINE_INPUT_ASSEMBLY_STATE_CREATE_INFO; + ia.topology = VK_PRIMITIVE_TOPOLOGY_TRIANGLE_LIST; + info.pInputAssemblyState = &ia; +} + +void Pipeline_Vk::init_viewport( + Arena& scope, + Device_Vk* dev, + VkGraphicsPipelineCreateInfo& info, + const Render_Pass& desc +) { + Texture& texture = dev->get_texture(desc.target); + VkPipelineViewportStateCreateInfo& vi = + *(VkPipelineViewportStateCreateInfo*)arena_alloc( + &scope, + sizeof vi + ); + VkRect2D& scissor = *(VkRect2D*)arena_alloc( + &scope, + sizeof scissor + ); + VkViewport& viewport = *(VkViewport*)arena_alloc( + &scope, + sizeof viewport + ); + memset(&vi, 0, sizeof vi); + memset(&scissor, 0, sizeof scissor); + memset(&viewport, 0, sizeof viewport); + scissor.offset.x = 0; + scissor.offset.y = 0; + scissor.extent.width = texture.w; + scissor.extent.height = texture.h; + viewport.x = 0; + viewport.y = 0; + viewport.width = texture.w; + viewport.height = texture.h; + viewport.minDepth = 0.0f; + viewport.maxDepth = 1.0f; + vi.sType = VK_STRUCTURE_TYPE_PIPELINE_VIEWPORT_STATE_CREATE_INFO; + vi.viewportCount = 1; + vi.pViewports = &viewport; + vi.scissorCount = 1; + vi.pScissors = &scissor; + info.pViewportState = &vi; +} + +void Pipeline_Vk::init_rasterisation( + Arena& scope, + Device_Vk* dev, + VkGraphicsPipelineCreateInfo& info, + const Pipeline& desc +) { + VkPipelineRasterizationStateCreateInfo& ri = + *(VkPipelineRasterizationStateCreateInfo*)arena_alloc( + &scope, + sizeof ri + ); + (void)dev; + (void)desc; + memset(&ri, 0, sizeof ri); + ri.sType = VK_STRUCTURE_TYPE_PIPELINE_RASTERIZATION_STATE_CREATE_INFO; + ri.depthClampEnable = VK_FALSE; + ri.rasterizerDiscardEnable = VK_FALSE; + ri.polygonMode = VK_POLYGON_MODE_FILL; + ri.lineWidth = 1.0f; + ri.cullMode = VK_CULL_MODE_NONE; + ri.frontFace = VK_FRONT_FACE_COUNTER_CLOCKWISE; + ri.depthBiasEnable = VK_FALSE; + info.pRasterizationState = &ri; +} + +void Pipeline_Vk::init_msaa( + Arena& scope, + Device_Vk* dev, + VkGraphicsPipelineCreateInfo& info, + const Pipeline& desc +) { + VkPipelineMultisampleStateCreateInfo& mi = + *(VkPipelineMultisampleStateCreateInfo*)arena_alloc( + &scope, + sizeof mi + ); + (void)dev; + (void)desc; + memset(&mi, 0, sizeof mi); + mi.sType = VK_STRUCTURE_TYPE_PIPELINE_MULTISAMPLE_STATE_CREATE_INFO; + mi.sampleShadingEnable = VK_FALSE; + mi.rasterizationSamples = VK_SAMPLE_COUNT_1_BIT; + info.pMultisampleState = &mi; +} + +void Pipeline_Vk::init_depthstencil( + Arena& scope, + Device_Vk* dev, + VkGraphicsPipelineCreateInfo& info, + const Pipeline& desc +) { + VkPipelineDepthStencilStateCreateInfo& ds = + *(VkPipelineDepthStencilStateCreateInfo*)arena_alloc( + &scope, + sizeof ds + ); + (void)dev; + (void)desc; + memset(&ds, 0, sizeof ds); + ds.sType = VK_STRUCTURE_TYPE_PIPELINE_DEPTH_STENCIL_STATE_CREATE_INFO; + ds.depthTestEnable = VK_FALSE; + ds.depthWriteEnable = VK_FALSE; + ds.depthCompareOp = VK_COMPARE_OP_LESS; + ds.depthBoundsTestEnable = VK_FALSE; + ds.stencilTestEnable = VK_FALSE; + info.pDepthStencilState = &ds; +} + +void Pipeline_Vk::init_blending( + Arena& scope, + Device_Vk* dev, + VkGraphicsPipelineCreateInfo& info, + const Pipeline& desc +) { + VkPipelineColorBlendStateCreateInfo& bi = + *(VkPipelineColorBlendStateCreateInfo*)arena_alloc( + &scope, + sizeof bi + ); + VkPipelineColorBlendAttachmentState& abs = + *(VkPipelineColorBlendAttachmentState*)arena_alloc( + &scope, + sizeof abs + ); + (void)dev; + (void)desc; + memset(&bi, 0, sizeof bi); + memset(&abs, 0, sizeof abs); + abs.colorWriteMask = + VK_COLOR_COMPONENT_R_BIT | + VK_COLOR_COMPONENT_G_BIT | + VK_COLOR_COMPONENT_B_BIT | + VK_COLOR_COMPONENT_A_BIT; + abs.blendEnable = VK_FALSE; + bi.sType = VK_STRUCTURE_TYPE_PIPELINE_COLOR_BLEND_STATE_CREATE_INFO; + bi.flags = 0; + bi.logicOpEnable = VK_FALSE; + bi.attachmentCount = 1; + bi.pAttachments = &abs; + info.pColorBlendState = &bi; +} + +void Pipeline_Vk::init_layout( + Device_Vk* dev, + const Pipeline& desc +) { + VkResult r; + VkPipelineLayoutCreateInfo li{}; + (void)desc; + li.sType = VK_STRUCTURE_TYPE_PIPELINE_LAYOUT_CREATE_INFO; + li.setLayoutCount = 0; + li.pushConstantRangeCount = 0; /* todo */ + r = vkCreatePipelineLayout( + dev->dev, + &li, + &dev->ac, + &lay + ); + if (r != VK_SUCCESS) { + print_err("Failed to create a pipeline layout.\n"); + pbreak(r); + } +} + +void Pipeline_Vk::init(Device_Vk* dev, const Pso_Key& key) { + char buffer[1024]; + Arena scope; + VkResult r; + const auto& desc = key.pip; + VkGraphicsPipelineCreateInfo info{}; + init_arena(&scope, buffer, sizeof buffer); + init_layout(dev, desc); + init_stages(scope, dev, info, desc); + init_vertex_input(scope, dev, info, desc); + init_input_assembly(scope, dev, info, desc); + init_viewport(scope, dev, info, key.rpo); + init_rasterisation(scope, dev, info, desc); + init_msaa(scope, dev, info, desc); + init_depthstencil(scope, dev, info, desc); + init_blending(scope, dev, info, desc); + info.sType = VK_STRUCTURE_TYPE_GRAPHICS_PIPELINE_CREATE_INFO; + info.flags = 0; + info.renderPass = dev->get_rpo(key.rpo).rpo; + info.subpass = 0; + info.layout = lay; + r = vkCreateGraphicsPipelines( + dev->dev, + VK_NULL_HANDLE, + 1, + &info, + &dev->ac, + &pip + ); + if (r != VK_SUCCESS) { + print_err("Failed to create a pipeline.\n"); + pbreak(r); + } +} + +void Pipeline_Vk::destroy(Device_Vk* dev) { + vkDestroyPipeline(dev->dev, pip, &dev->ac); + vkDestroyPipelineLayout(dev->dev, lay, &dev->ac); +} + +int Vertex_Format_Vk::svariable_type_size(SVariable_Type type) { + switch (type) { + case svariable_type_float: return 4; + case svariable_type_vec2: return 8; + case svariable_type_vec3: return 12; + case svariable_type_vec4: return 16; + default: assert(0); /* todo */ + } + return 0; +} + +VkFormat Vertex_Format_Vk::format_from_svar_type( + SVariable_Type type +) { + switch (type) { + case svariable_type_float: + return VK_FORMAT_R32_SFLOAT; + case svariable_type_vec2: + return VK_FORMAT_R32G32_SFLOAT; + case svariable_type_vec3: + return VK_FORMAT_R32G32B32_SFLOAT; + case svariable_type_vec4: + return VK_FORMAT_R32G32B32A32_SFLOAT; + default: assert(0); /* todo */ + } + return (VkFormat)0; +} + +void Vertex_Format_Vk::from_shader( + Device_Vk* dev, + Shader_Vk& s +) { + Shader_Vk::Vertex_Format& vfd = s.vfd; + int i; + binding_count = vfd.binding_count; + attr_count = vfd.attr_count; + bindings = (VkVertexInputBindingDescription*)heap_alloc( + dev->heap, + vfd.binding_count * sizeof *bindings + ); + attrs = (VkVertexInputAttributeDescription*)heap_alloc( + dev->heap, + vfd.attr_count * sizeof *attrs + ); + memset(bindings, 0, vfd.binding_count * sizeof *bindings); + memset(attrs, 0, vfd.attr_count * sizeof *attrs); + for (i = 0; i < vfd.binding_count; i++) { + int j, stride = 0; + auto& src = vfd.bindings[i]; + auto& dst = bindings[src.index]; + for (j = 0; j < src.attr_count; j++) { + auto& src_attr = vfd.attributes[src.attributes[j]]; + auto& dst_attr = attrs[src.attributes[j]]; + dst_attr.binding = src.index; + dst_attr.location = j; + dst_attr.format = format_from_svar_type(src_attr.type); + dst_attr.offset = stride; + stride += svariable_type_size(src_attr.type); + } + dst.binding = src.index; + dst.stride = stride; + dst.inputRate = + src.rate == sbinding_rate_instance? + VK_VERTEX_INPUT_RATE_INSTANCE: + VK_VERTEX_INPUT_RATE_VERTEX; + } +} + +void Vertex_Format_Vk::destroy(Device_Vk* dev) { + heap_free(dev->heap, attrs); + heap_free(dev->heap, bindings); +} + /* todo proper asset manager which will load this stuff */ bool Shader_Vk::init(Device_Vk* dev, FILE* f) { char magic[4]; - int attr_count, target_count, i; + int binding_count, target_count, i; fread(magic, 4, 1, f); if ( magic[0] != 'C' || @@ -1183,9 +1995,18 @@ bool Shader_Vk::init(Device_Vk* dev, FILE* f) { magic[3] != '2' ) return false; fread(&type, 4, 1, f); - fread(&attr_count, 4, 1, f); + fread(&binding_count, 4, 1, f); fread(&target_count, 4, 1, f); - fseek(f, 32 * attr_count + 32 * target_count, SEEK_CUR); + vfd.binding_count = binding_count; + assert(binding_count); + if (!vfd.init(dev, f)) + return false; + vf = dev->create_vf(*this); + fseek( + f, + 32 * target_count, + SEEK_CUR + ); for (i = 0; i < shader_type_count; i++) { int o, s; fread(&o, 4, 1, f); @@ -1203,6 +2024,7 @@ bool Shader_Vk::init(Device_Vk* dev, FILE* f) { } else { modules[i] = VK_NULL_HANDLE; } + fread(entrypoints[i], 1, 24, f); } return true; } @@ -1224,28 +2046,257 @@ bool Shader_Vk::init_module( return r == VK_SUCCESS; } +int Shader_Vk::Vertex_Format::find_binding(const char* name) { + int i; + int bucket = (int)(hash_string(name) % binding_count); + for (i = 0; i < binding_count; i++) { + Binding& binding = bindings[bucket]; + if ( + !binding.name[0] || + !strcmp(binding.name, name) + ) return bucket; + bucket = (bucket + 1) % binding_count; + } + return -1; +} + +int Shader_Vk::Vertex_Format::find_attribute(const char* name) { + int i; + int bucket = (int)(hash_string(name) % attr_count); + for (i = 0; i < attr_count; i++) { + Attribute& attr = attributes[bucket]; + if ( + !attr.name[0] || + !strcmp(attr.name, name) + ) return bucket; + bucket = (bucket + 1) % attr_count; + } + return -1; +} + +bool Shader_Vk::Vertex_Format::init( + Device_Vk* dev, + FILE* f +) { + int i, attr_index = 0; + int start = ftell(f); + attr_count = 0; + for (i = 0; i < binding_count; i++) { + char name[24]; + int count, j; + SBinding_Rate rate; + fread(name, 1, sizeof name, f); + fread(&rate, 4, 1, f); + fread(&count, 4, 1, f); + for (j = 0; j < count; j++) { + char aname[28]; + SVariable_Type type; + fread(aname, 1, sizeof aname, f); + fread(&type, 4, 1, f); + attr_count++; + } + } + fseek(f, start, SEEK_SET); + bindings = (Binding*)heap_alloc( + dev->heap, + binding_count * sizeof *bindings + ); + attributes = (Attribute*)heap_alloc( + dev->heap, + attr_count * sizeof *attributes + ); + for (i = 0; i < binding_count; i++) + bindings[i].name[0] = 0; + for (i = 0; i < attr_count; i++) + attributes[i].name[0] = 0; + for (i = 0; i < binding_count; i++) { + Binding* binding; + char name[24]; + int count, j; + SBinding_Rate rate; + fread(name, 1, sizeof name, f); + fread(&rate, 4, 1, f); + fread(&count, 4, 1, f); + binding = &bindings[find_binding(name)]; + strcpy(binding->name, name); + binding->rate = rate; + binding->attr_count = count; + binding->attributes = (int*)heap_alloc( + dev->heap, + count * sizeof *binding->attributes + ); + binding->index = i; + for (j = 0; j < count; j++, attr_index++) { + int bucket; + Attribute* attr; + char aname[28]; + SVariable_Type type; + fread(aname, 1, sizeof aname, f); + fread(&type, 4, 1, f); + bucket = find_attribute(aname); + binding->attributes[j] = bucket; + attr = &attributes[bucket]; + strcpy(attr->name, aname); + attr->index = j; + attr->type = type; + } + } + return true; +} + +void Shader_Vk::Vertex_Format::destroy(Device_Vk* dev) { + int i; + for (i = 0; i < binding_count; i++) + heap_free(dev->heap, bindings[i].attributes); + heap_free(dev->heap, bindings); + heap_free(dev->heap, attributes); +} + void Shader_Vk::destroy_internal(Device_Vk* dev) { int i; for (i = 0; i < shader_type_count; i++) if (modules[i]) vkDestroyShaderModule(dev->dev, modules[i], &dev->ac); + vfd.destroy(dev); + dev->destroy_vf(vf); } void Shader::destroy(Device* dev) { ((Shader_Vk*)this)->destroy_internal((Device_Vk*)dev); } -Shader* Device::load_shader(const char* fname) { +int Shader::binding_index(const char* name) { + int idx; + 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; +} + +int Shader::attribute_index(const char* name) { + int idx; + 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; +} + +void Buffer_Vk::init( + Device_Vk* dev, + int flags, + VkDeviceSize size +) { + VkBufferCreateInfo bi{}; + VkMemoryRequirements req; + VkResult r; + bi.sType = VK_STRUCTURE_TYPE_BUFFER_CREATE_INFO; + bi.size = size; + bi.usage = get_usage(flags); + bi.sharingMode = VK_SHARING_MODE_EXCLUSIVE; + r = vkCreateBuffer(dev->dev, &bi, &dev->ac, &buf); + if (r != VK_SUCCESS) { + print_err("Failed to create a buffer.\n"); + pbreak(r); + } + vkGetBufferMemoryRequirements(dev->dev, buf, &req); + { + VkMemoryPropertyFlags props = get_memory_flags(flags); + int mt = dev->find_memory_type(req.memoryTypeBits, props); + VkMemoryAllocateInfo ai{}; + if (mt < 0) { + print("Failed to find a satisfying memory type index.\n"); + pbreak(mt); + } + ai.sType = VK_STRUCTURE_TYPE_MEMORY_ALLOCATE_INFO; + ai.allocationSize = req.size; + ai.memoryTypeIndex = mt; + r = vkAllocateMemory(dev->dev, &ai, &dev->ac, &memory); + if (r == VK_ERROR_OUT_OF_DEVICE_MEMORY) { + print_err("Out of video memory.\n"); + pbreak(r); + } + if (r != VK_SUCCESS) { + print_err("VRAM allocation failed.\n"); + pbreak(r); + } + } + vkBindBufferMemory(dev->dev, buf, memory, 0); +} + +void Buffer_Vk::destroy(Device_Vk* dev) { + vkDestroyBuffer(dev->dev, buf, &dev->ac); + vkFreeMemory(dev->dev, memory, &dev->ac); +} + +Buffer_Id Device::create_buffer(size_t size, int flags) { + Device_Vk* dev = (Device_Vk*)this; + Buffer_Id id = dev->alloc_buffer(); + Buffer_Vk& buf = *(Buffer_Vk*)&get_buffer(id); + buf.init(dev, flags, (VkDeviceSize)size); + return id; +} + +void Device::destroy_buffer(Buffer_Id id) { + Device_Vk* dev = (Device_Vk*)this; + Buffer_Vk& buf = *(Buffer_Vk*)&get_buffer(id); + buf.destroy(dev); + dev->buffers.remove(id); +} + +void* Device::map_buffer( + Buffer_Id id, + size_t offset, + size_t size +) { + Device_Vk* dev = (Device_Vk*)this; + Buffer_Vk& buf = *(Buffer_Vk*)&get_buffer(id); + void* ptr; + vkMapMemory( + dev->dev, + buf.memory, + (VkDeviceSize)offset, + (VkDeviceSize)size, + 0, + &ptr + ); + return ptr; +} + +void Device::unmap_buffer(Buffer_Id id) { + Device_Vk* dev = (Device_Vk*)this; + Buffer_Vk& buf = *(Buffer_Vk*)&get_buffer(id); + vkUnmapMemory(dev->dev, buf.memory); +} + +Buffer& Device::get_buffer(Buffer_Id id) { + return ((Device_Vk*)this)->buffers[id]; +} + +Shader& Device::get_shader(Shader_Id id) { + return ((Device_Vk*)this)->shaders[id]; +} + +Shader_Id Device::load_shader(const char* fname) { FILE* f = fopen(fname, "rb"); Shader_Vk* s; + Device_Vk* dev = (Device_Vk*)this; + Shader_Id id; bool r; - if (!f) return 0; - s = (Shader_Vk*)heap_alloc(heap, sizeof *s); - r = s->init((Device_Vk*)this, f); + if (!f) return Shader_Id(0); + id = dev->alloc_shader(); + s = (Shader_Vk*)&get_shader(id); + r = s->init(dev, f); fclose(f); if (!r) { heap_free(heap, s); return 0; } - return s; + return id; +} + +void Device::destroy_shader(Shader_Id id) { + Device_Vk* dev = (Device_Vk*)this; + Shader_Vk& buf = *(Shader_Vk*)&get_shader(id); + buf.destroy(dev); + dev->shaders.remove(id); } diff --git a/video.hpp b/video.hpp index 6878d77..b59fd5c 100644 --- a/video.hpp +++ b/video.hpp @@ -1,17 +1,42 @@ #ifndef video_hpp #define video_hpp +#include #include struct App; struct Arena; struct Heap; -typedef uint32_t Texture_Id; -typedef uint32_t Buffer_Id; +template +struct Primitive_Id { + T index; + Primitive_Id(): index(0) {} + Primitive_Id(T i): index(i) {} + bool operator==(Primitive_Id other) const { + return index == other.index; + } + operator bool() const { + return index != 0; + } +}; -struct Pipeline { +struct Texture_Id : public Primitive_Id { + using Primitive_Id::Primitive_Id; +}; +struct Buffer_Id : public Primitive_Id { + using Primitive_Id::Primitive_Id; +}; +struct Vertex_Format_Id : public Primitive_Id { + using Primitive_Id::Primitive_Id; +}; +struct Shader_Id : public Primitive_Id { + using Primitive_Id::Primitive_Id; +}; +struct Pipeline { + Vertex_Format_Id vertex_format; + Shader_Id shader; }; struct Colour { @@ -26,19 +51,30 @@ struct Render_Pass { int size = sizeof *this, i; uint8_t* bba = (uint8_t*)this; uint8_t* bbb = (uint8_t*)&other; - for (i = 0; i < size; i++) + for (i = 0; i < size; i++, bba++, bbb++) if (*bba != *bbb) return false; return true; } }; -struct Draw { +struct Vertex_Buffer_Binding { + Buffer_Id id; + size_t offset; + int target; +}; +struct Draw { + Vertex_Buffer_Binding* verts; + int vertex_count; + int instance_count; + int first_vertex; + int first_instance; }; struct Pipeline_Builder { Arena* arena; Render_Pass* pass; + Pipeline* pip; Pipeline_Builder(Arena* arena); @@ -46,6 +82,12 @@ struct Pipeline_Builder { void rp_target(Texture_Id id, Colour clear_colour); Render_Pass& build_rp(); void validate_rp(); + + void begin(); + void shader(Shader_Id s); + void vertex_format(Vertex_Format_Id vf); + Pipeline& build(); + void validate(); }; struct Texture { @@ -53,6 +95,21 @@ struct Texture { bool alias; }; +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 + }; +}; + +struct Buffer {}; + struct Context; struct Shader; struct Device { @@ -76,7 +133,15 @@ struct Device { Texture& get_texture(Texture_Id id); void destroy_texture(Texture_Id id); - Shader* load_shader(const char* fname); + Buffer_Id create_buffer(size_t size, int flags); + void* map_buffer(Buffer_Id id, size_t offset, size_t size); + void unmap_buffer(Buffer_Id id); + Buffer& get_buffer(Buffer_Id id); + void destroy_buffer(Buffer_Id id); + + Shader_Id load_shader(const char* fname); + Shader& get_shader(Shader_Id id); + void destroy_shader(Shader_Id id); }; struct Context { @@ -98,7 +163,13 @@ struct Context { }; struct Shader { + Vertex_Format_Id vf; void destroy(Device* dev); + + /* -1 on failure */ + int binding_index(const char* name); + int attribute_index(const char* name); + int target_index(const char* name); }; #endif -- cgit v1.2.3-54-g00ecf