diff options
| -rw-r--r-- | c2.cpp | 45 | ||||
| -rw-r--r-- | intermediate/triangle.glsl | 13 | ||||
| -rw-r--r-- | pipeline.cpp | 24 | ||||
| -rw-r--r-- | qstd/str.c | 9 | ||||
| -rw-r--r-- | qstd/str.h | 1 | ||||
| -rw-r--r-- | sc/Makefile | 2 | ||||
| -rw-r--r-- | sc/sc.cpp | 138 | ||||
| -rw-r--r-- | sc/sh_enums.h | 11 | ||||
| -rw-r--r-- | video.cpp | 1189 | ||||
| -rw-r--r-- | video.hpp | 83 | 
10 files changed, 1412 insertions, 103 deletions
| @@ -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); +} @@ -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; +} @@ -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 @@ -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 @@ -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, ©, 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);  } @@ -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 |