summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorquou <quou@disroot.org>2024-12-21 21:25:22 +1100
committerquou <quou@disroot.org>2024-12-21 21:25:22 +1100
commit5bc8f90c38981045515bab04d26687f929f62ec1 (patch)
treed1d603ded306ae218e75f7eff4b69807f2a14e26
parent3283c6c8c32f980bd01591e441acd9c712c650ef (diff)
render a triangle
-rw-r--r--c2.cpp45
-rw-r--r--intermediate/triangle.glsl13
-rw-r--r--pipeline.cpp24
-rw-r--r--qstd/str.c9
-rw-r--r--qstd/str.h1
-rw-r--r--sc/Makefile2
-rw-r--r--sc/sc.cpp138
-rw-r--r--sc/sh_enums.h11
-rw-r--r--video.cpp1189
-rw-r--r--video.hpp83
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 <string.h>
#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>("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 <stdint.h>
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<Variable> attrs;
+ };
int type;
- std::vector<Variable> attrs;
+ std::vector<Binding> bindings;
std::vector<Variable> trgts;
+ std::vector<Variable> 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<uint32_t> compile_shader(
Includer inc;
std::string prepr;
std::vector<uint32_t> 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 <typename ID, typename T, int size>
-struct ID_Map
-{
- T storage[size];
- ID keys[size];
+struct Device_Vk;
+
+template <typename T>
+struct Hash_Function {};
+
+template <typename Key, typename Value, int size>
+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<Key>{}(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<T*, uint32_t> 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 <typename Table>
+ 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<Table>& other) {
+ return bucket == other.bucket && table == other.table;
+ }
+ bool operator==(const iterator<Table>& other) {
+ return equals(other);
}
- return { 0, 0 };
+ bool operator!=(const iterator<Table>& other) {
+ return !equals(other);
+ }
+ iterator<Table> operator++() {
+ bucket++;
+ while (
+ bucket < size &&
+ table->flags[bucket] & flags_null
+ ) bucket++;
+ return *this;
+ }
+ std::pair<Key&, Value&> operator*() {
+ return { table->keys[bucket], table->values[bucket] };
+ }
+ std::pair<const Key&, const Value&> operator*() const {
+ return { table->keys[bucket], table->values[bucket] };
+ }
+ };
+
+ iterator<Hash_Map<Key, Value, size>> begin() {
+ iterator<Hash_Map<Key, Value, size>> 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<Hash_Map<Key, Value, size>> end() {
+ iterator<Hash_Map<Key, Value, size>> r;
+ r.init_end(this);
+ return r;
}
- T& operator[](ID id) {
- T* b = bucket(id).first;
- assert(b != 0);
- return *b;
+ iterator<const Hash_Map<Key, Value, size>> begin() const {
+ iterator<const Hash_Map<Key, Value, size>> r;
+ r.init_begin(this);
+ return r;
+ }
+
+ iterator<const Hash_Map<Key, Value, size>> end() const {
+ iterator<const Hash_Map<Key, Value, size>> 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<Render_Pass>
+struct Hash_Function<Render_Pass>
{
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<Pso_Key>
+{
+ size_t operator()(const Pso_Key& rp) const {
+ return (size_t)fnv1a64((uint8_t*)&rp, sizeof rp);
+ }
+};
+
+template<>
+struct Hash_Function<Texture_Id> {
+ size_t operator()(Texture_Id id) const {
+ return id.index;
+ }
+};
+
+template<>
+struct Hash_Function<Buffer_Id> {
+ size_t operator()(Buffer_Id id) const {
+ return id.index;
+ }
+};
+
+template<>
+struct Hash_Function<Shader_Id> {
+ size_t operator()(Shader_Id id) const {
+ return id.index;
+ }
+};
+
+template<>
+struct Hash_Function<Vertex_Format_Id> {
+ size_t operator()(Vertex_Format_Id id) const {
+ return id.index;
+ }
+};
+
+template<>
+struct std::hash<Render_Pass> {
+ 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<Texture_Id, Texture_Vk, max_textures> textures;
- Texture_Id texture_count;
-
- std::unordered_map<Render_Pass, Renderpass_Vk> rpo_cache;
+ Hash_Map<Texture_Id, Texture_Vk, max_textures> textures;
+ Hash_Map<Buffer_Id, Buffer_Vk, max_buffers> buffers;
+ Hash_Map<
+ Vertex_Format_Id,
+ Vertex_Format_Vk,
+ max_vertex_formats
+ > vertex_formats;
+ Hash_Map<Shader_Id, Shader_Vk, max_shaders> shaders;
+ uint32_t texture_count;
+ uint32_t buffer_count;
+ uint32_t vertex_format_count;
+ uint32_t shader_count;
+
+ Hash_Map<Render_Pass, Renderpass_Vk, max_rpos> rpo_cache;
+ Hash_Map<Pso_Key, Pipeline_Vk, max_pipelines> 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, &copy, 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 <stddef.h>
#include <stdint.h>
struct App;
struct Arena;
struct Heap;
-typedef uint32_t Texture_Id;
-typedef uint32_t Buffer_Id;
+template <typename T>
+struct Primitive_Id {
+ T index;
+ Primitive_Id(): index(0) {}
+ Primitive_Id(T i): index(i) {}
+ bool operator==(Primitive_Id<T> other) const {
+ return index == other.index;
+ }
+ operator bool() const {
+ return index != 0;
+ }
+};
-struct Pipeline {
+struct Texture_Id : public Primitive_Id<uint32_t> {
+ using Primitive_Id<uint32_t>::Primitive_Id;
+};
+struct Buffer_Id : public Primitive_Id<uint32_t> {
+ using Primitive_Id<uint32_t>::Primitive_Id;
+};
+struct Vertex_Format_Id : public Primitive_Id<uint32_t> {
+ using Primitive_Id<uint32_t>::Primitive_Id;
+};
+struct Shader_Id : public Primitive_Id<uint32_t> {
+ using Primitive_Id<uint32_t>::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