diff options
| -rw-r--r-- | Makefile | 26 | ||||
| -rw-r--r-- | c2.cpp | 161 | ||||
| -rw-r--r-- | configure.lua | 9 | ||||
| -rw-r--r-- | intermediate/debug.glsl | 53 | ||||
| -rw-r--r-- | model.cpp | 78 | ||||
| -rw-r--r-- | model.hpp | 17 | ||||
| -rw-r--r-- | pipeline.cpp | 5 | ||||
| -rw-r--r-- | ui.cpp | 2 | ||||
| -rw-r--r-- | video.cpp | 17 | ||||
| -rw-r--r-- | video.hpp | 9 | 
10 files changed, 346 insertions, 31 deletions
| @@ -81,20 +81,22 @@ packer.o: packer.c  packer: packer.o libqstd.a   	gcc $(opt_lnk) $(lflags) -o packer packer.o libqstd.a  -data/triangle.csh: intermediate/triangle.glsl shadercompiler | data -	./shadercompiler intermediate/triangle.glsl data/triangle.csh -data/ui.csh: intermediate/ui.glsl shadercompiler | data -	./shadercompiler intermediate/ui.glsl data/ui.csh +data/debug.csh: intermediate/debug.glsl shadercompiler | data +	./shadercompiler intermediate/debug.glsl data/debug.csh +data/mip_spec.csh: intermediate/mip_spec.glsl shadercompiler | data +	./shadercompiler intermediate/mip_spec.glsl data/mip_spec.csh +data/sky.csh: intermediate/sky.glsl shadercompiler | data +	./shadercompiler intermediate/sky.glsl data/sky.csh  data/surface.csh: intermediate/surface.glsl shadercompiler | data  	./shadercompiler intermediate/surface.glsl data/surface.csh  data/surface_depthonly.csh: intermediate/surface_depthonly.glsl shadercompiler | data  	./shadercompiler intermediate/surface_depthonly.glsl data/surface_depthonly.csh -data/sky.csh: intermediate/sky.glsl shadercompiler | data -	./shadercompiler intermediate/sky.glsl data/sky.csh -data/mip_spec.csh: intermediate/mip_spec.glsl shadercompiler | data -	./shadercompiler intermediate/mip_spec.glsl data/mip_spec.csh +data/triangle.csh: intermediate/triangle.glsl shadercompiler | data +	./shadercompiler intermediate/triangle.glsl data/triangle.csh +data/ui.csh: intermediate/ui.glsl shadercompiler | data +	./shadercompiler intermediate/ui.glsl data/ui.csh -data/monkey.mdl: convmodel intermediate/monkey.glb data/triangle.csh data/ui.csh data/surface.csh data/surface_depthonly.csh data/sky.csh data/mip_spec.csh | data +data/monkey.mdl: convmodel intermediate/monkey.glb data/debug.csh data/mip_spec.csh data/sky.csh data/surface.csh data/surface_depthonly.csh data/triangle.csh data/ui.csh | data  	./convmodel data intermediate/monkey.glb data/monkey.mdl  data/22.tex: convtexture intermediate/22.bmp | data @@ -115,8 +117,8 @@ data/bricks.mat: convmaterial intermediate/bricks.mat | data  data/plastic.mat: convmaterial intermediate/plastic.mat | data  	./convmaterial intermediate/plastic.mat data/plastic.mat -pack: packer data/triangle.csh data/ui.csh data/surface.csh data/surface_depthonly.csh data/sky.csh data/mip_spec.csh data/monkey.mdl data/22.tex data/kita.tex data/brick_albedo.tex data/brick_ao.tex data/brick_normal.tex data/sky.tex data/bricks.mat data/plastic.mat  -	./packer pack data triangle.csh ui.csh surface.csh surface_depthonly.csh sky.csh mip_spec.csh monkey.mdl 22.tex kita.tex brick_albedo.tex brick_ao.tex brick_normal.tex sky.tex bricks.mat plastic.mat  +pack: packer data/debug.csh data/mip_spec.csh data/sky.csh data/surface.csh data/surface_depthonly.csh data/triangle.csh data/ui.csh data/monkey.mdl data/22.tex data/kita.tex data/brick_albedo.tex data/brick_ao.tex data/brick_normal.tex data/sky.tex data/bricks.mat data/plastic.mat  +	./packer pack data debug.csh mip_spec.csh sky.csh surface.csh surface_depthonly.csh triangle.csh ui.csh monkey.mdl 22.tex kita.tex brick_albedo.tex brick_ao.tex brick_normal.tex sky.tex bricks.mat plastic.mat   data:  	mkdir -p data @@ -124,7 +126,7 @@ data:  -include qstd/memory.d qstd/plat.d qstd/str.d qstd/pack.d cfg/cfgparse.d sc/sc.d sc/includer.d app.d c2.d video.d pipeline.d asset.d ui.d maths.d model.d convtexture.d convmodel.d convmaterial.d packer.d   clean: -	rm -f qstd/memory.o qstd/plat.o qstd/str.o qstd/pack.o cfg/cfgparse.o sc/sc.o sc/includer.o app.o c2.o video.o pipeline.o asset.o ui.o maths.o model.o convtexture.o convmodel.o convmaterial.o packer.o qstd/memory.d qstd/plat.d qstd/str.d qstd/pack.d cfg/cfgparse.d sc/sc.d sc/includer.d app.d c2.d video.d pipeline.d asset.d ui.d maths.d model.d convtexture.d convmodel.d convmaterial.d packer.d data/triangle.csh data/ui.csh data/surface.csh data/surface_depthonly.csh data/sky.csh data/mip_spec.csh data/monkey.mdl data/22.tex data/kita.tex data/brick_albedo.tex data/brick_ao.tex data/brick_normal.tex data/sky.tex data/bricks.mat data/plastic.mat  +	rm -f qstd/memory.o qstd/plat.o qstd/str.o qstd/pack.o cfg/cfgparse.o sc/sc.o sc/includer.o app.o c2.o video.o pipeline.o asset.o ui.o maths.o model.o convtexture.o convmodel.o convmaterial.o packer.o qstd/memory.d qstd/plat.d qstd/str.d qstd/pack.d cfg/cfgparse.d sc/sc.d sc/includer.d app.d c2.d video.d pipeline.d asset.d ui.d maths.d model.d convtexture.d convmodel.d convmaterial.d packer.d data/debug.csh data/mip_spec.csh data/sky.csh data/surface.csh data/surface_depthonly.csh data/triangle.csh data/ui.csh data/monkey.mdl data/22.tex data/kita.tex data/brick_albedo.tex data/brick_ao.tex data/brick_normal.tex data/sky.tex data/bricks.mat data/plastic.mat   	rm -f shadercompiler  	rmdir data  	rm -f c2 @@ -13,6 +13,7 @@ extern "C" {  #define video_arena_size (1024 * 1024 * 16)  #define asset_arena_size (1024 * 1024 * 4)  #define ui_arena_size    (1024 * 16) +#define scene_arena_size (1024)  #define per_frame_memory_size (1024 * 1024)  static float verts[] = { @@ -533,6 +534,134 @@ struct Env_Probe {  	}  }; +struct Line_Renderer { +	static constexpr int max_lines = 1024; +	Staged_Buffer vb, cb; +	Shader* shader; +	int vert_binding, cbuffer_binding; +	int cur; +	int w, h; +	v3f cur_col; + +	struct Vertex { +		float x, y, z; +		float r, g, b; +	}* verts; + +	struct CBuffer { +		m4f vp; +	}; + +	void init(Device* dev, Asset_Arena* assets) { +		vb.init( +			dev, +			"Line Renderer mesh", +			max_lines * sizeof(Vertex) * 2, +			Buffer_Flags::vertex_buffer +		); +		cb.init( +			dev, +			"Line Renderer cbuffer", +			sizeof(CBuffer), +			Buffer_Flags::constant_buffer +		); +		shader = (Shader*)assets->load("debug.csh"); +		vert_binding = shader->binding_index("verts"); +		cbuffer_binding = shader->descriptor_binding("cbuf"); +		assert(vert_binding >= 0); +		assert(cbuffer_binding >= 0); +		verts = (Vertex*)vb.map(dev); +		cur = 0; +	} + +	void destroy(Device* dev) { +		vb.unmap(dev); +		vb.destroy(dev); +		cb.destroy(dev); +	} + +	void begin(int wi, int he) { +		cur = 0; +		w = wi; +		h = he; +		cur_col = v3f(1.0f, 1.0f, 1.0f); +	} + +	void colour(const v3f& c) { +		cur_col = c; +	} + +	void add_line(const v3f& s, const v3f& e) { +		if (cur >= max_lines) { +			assert(0); +			return; +		} +		Vertex& a = verts[cur * 2]; +		Vertex& b = verts[cur * 2 + 1]; +		a.x = s.x; +		a.y = s.y; +		a.z = s.z; +		b.x = e.x; +		b.y = e.y; +		b.z = e.z; +		a.r = cur_col.x; +		a.g = cur_col.y; +		a.b = cur_col.z; +		b.r = cur_col.x; +		b.g = cur_col.y; +		b.b = cur_col.z; +		cur++; +	} + +	void add_box(const AABB& b) { +		add_line(v3f(b.max.x, b.max.y, b.max.z), v3f(b.max.x, b.max.y, b.min.z)); +		add_line(v3f(b.max.x, b.max.y, b.max.z), v3f(b.max.x, b.min.y, b.max.z)); +		add_line(v3f(b.max.x, b.max.y, b.max.z), v3f(b.min.x, b.max.y, b.max.z)); +		add_line(v3f(b.max.x, b.min.y, b.min.z), v3f(b.max.x, b.max.y, b.min.z)); +		add_line(v3f(b.max.x, b.min.y, b.min.z), v3f(b.max.x, b.min.y, b.max.z)); +		add_line(v3f(b.min.x, b.max.y, b.max.z), v3f(b.min.x, b.min.y, b.max.z)); +		add_line(v3f(b.min.x, b.max.y, b.min.z), v3f(b.max.x, b.max.y, b.min.z)); +		add_line(v3f(b.min.x, b.max.y, b.min.z), v3f(b.min.x, b.max.y, b.max.z)); +		add_line(v3f(b.min.x, b.min.y, b.max.z), v3f(b.max.x, b.min.y, b.max.z)); +		add_line(v3f(b.min.x, b.min.y, b.min.z), v3f(b.max.x, b.min.y, b.min.z)); +		add_line(v3f(b.min.x, b.min.y, b.min.z), v3f(b.min.x, b.max.y, b.min.z)); +		add_line(v3f(b.min.x, b.min.y, b.min.z), v3f(b.min.x, b.min.y, b.max.z)); +	} + +	void flush( +		const Camera& cam, +		Device* dev, +		Arena* a, +		Render_Pass& rp +	) { +		if (!cur) return; +		CBuffer* c = (CBuffer*)cb.map(dev); +		c->vp = cam.get_proj() * cam.get_view(); +		cb.unmap(dev); +		Pipeline_Builder pb(a, dev); +		Context& ctx = dev->get_ctx(); +		cb.update(ctx); +		vb.update(ctx); +		pb.begin(); +		pb.shader(shader->id); +		pb.cbuffer(cbuffer_binding, cb.gpuonly); +		pb.vertex_format(shader->vf); +		pb.geo(Geo_Type::lines); +		Pipeline& pip = pb.build(); +		Vertex_Buffer_Binding binding[] = {{ +			.id = vb.gpuonly, +			.offset = 0, +			.target = vert_binding +		}, {}}; +		Draw d{}; +		d.verts = binding; +		d.vertex_count = cur * 2; +		d.instance_count = 1; +		ctx.submit(d, pip, rp); +		cur = 0; +	} +}; +  struct Config_Buffer {  	float offset[2];  }; @@ -543,7 +672,7 @@ struct Config_Buffer2 {  };  struct C2 : public App { -	Arena video_arena, asset_arena, ui_arena; +	Arena video_arena, asset_arena, ui_arena, scene_arena;  	Model_Loader model_loader;  	Material_Loader mat_loader;  	Asset_Arena assets; @@ -559,6 +688,7 @@ struct C2 : public App {  	Env_Probe eprobe;  	Buffer_Id vbo, cbuf;  	Sampler_Id clamped_linear; +	Line_Renderer lr;  	UI* ui;  	UI::Label* fps_label;  	void* per_frame; @@ -566,6 +696,8 @@ struct C2 : public App {  	uint8_t r = 0;  	float rot = 0.0f;  	v3f raxis = v3f(0.0f, 1.0f, 0.0); +	Model_Instance* selected_model; +	int selected_mesh;  	void on_init() override {  		running = 1; @@ -584,6 +716,11 @@ struct C2 : public App {  			arena_alloc(arena, ui_arena_size),  			ui_arena_size  		); +		init_arena( +			&scene_arena, +			arena_alloc(arena, scene_arena_size), +			scene_arena_size +		);  		assets.init(&asset_arena, "pack", 128);  		dev = Device::create(&video_arena, this);   		default_texture = make_default_texture(dev); @@ -607,11 +744,12 @@ struct C2 : public App {  		);  		clamped_linear = create_clamped_linear(dev);  		ui = UI::create(dev, this, &ui_arena, ui_shader->id); +		lr.init(dev, &assets);  		assert(per_frame != 0);  		vbo = upload_verts(dev);  		ui->layout(w, h);  		fps_label = ui->create_element<UI::Label>(ui->root, ""); -		scene.init(arena, 32, clamped_linear); +		scene.init(&scene_arena, 32, clamped_linear);  		monkey = scene.instantiate(  			dev,  			(Model*)assets.load("monkey.mdl") @@ -630,6 +768,7 @@ struct C2 : public App {  		init_arena(&frame_arena, per_frame, per_frame_memory_size);  		dev->begin_frame(); +		lr.begin(w, h);  		if (frame % 10 == 0) {  			char buf[32]; @@ -748,6 +887,23 @@ struct C2 : public App {  		ui->render(&frame_arena, dev->get_backbuffer());  		ctx.debug_pop(); +		if (mjp(mbtn_left)) { +			std::tie(selected_model, selected_mesh) = +				scene.pick(camera, w, h, mx, my); +		} + +		lr.colour(v3f(1.0f, 0.0f, 0.0f)); + +		if (selected_model) { +			lr.add_box(selected_model->bounds[selected_mesh]); +		} + +		ctx.debug_push("debug"); +		pb.begin_rp(); +		pb.rp_target(dev->get_backbuffer(), Clear_Mode::restore); +		lr.flush(camera, dev, &frame_arena, pb.build_rp()); +		ctx.debug_pop(); +  		r += 10;  		rot += 1.0f * dt;  		frame++; @@ -757,6 +913,7 @@ struct C2 : public App {  	void on_destroy() override {  		scene.destroy(dev);  		sky.destroy(dev); +		lr.destroy(dev);  		eprobe.destroy(dev);  		ui->destroy();  		assets.destroy(); diff --git a/configure.lua b/configure.lua index 0509346..2c5672b 100644 --- a/configure.lua +++ b/configure.lua @@ -35,12 +35,13 @@ config = {  		"convmaterial"  	},  	shaders = { -		"triangle", -		"ui", +		"debug", +		"mip_spec", +		"sky",  		"surface",  		"surface_depthonly", -		"sky", -		"mip_spec" +		"triangle", +		"ui",  	},  	materials = {  		"bricks", diff --git a/intermediate/debug.glsl b/intermediate/debug.glsl new file mode 100644 index 0000000..135511b --- /dev/null +++ b/intermediate/debug.glsl @@ -0,0 +1,53 @@ +#ifdef DESC +[program] +type: graphics +vertex: main +fragment: main + +[binding] +name: verts +rate: vertex +[attribute] +name: position +type: vec3 +[attribute] +name: colour +type: vec3 + +[struct] +name: CBuffer +[variable] +name: view_proj +type: mat4 + +[cbuffer] +type: CBuffer +name: cbuf +stage: vertex + +[interpolator] +name: colour +type: vec3 + +[target] +name: colour +type: vec4 + +#endif + +#ifdef VERTEX_SHADER + +void main() { +	interpolator.colour = colour; +	gl_Position = cbuf.view_proj * vec4(position, 1.0); +} + +#endif + +#ifdef FRAGMENT_SHADER + +void main() { +	colour = vec4(interpolator.colour, 1.0); +} + +#endif @@ -9,6 +9,8 @@ extern "C" {  }  #include <string.h> +#include <stdio.h> +#include <algorithm>  struct MVP_Cbuffer {  	m4f model; @@ -273,7 +275,7 @@ m4f Camera::get_proj() const {  	return m4f::pers(fov, asp, near, far);  } -void Model_Instance::init(Device* dev, Model* model) { +void Model_Instance::init(Device* dev, Heap* h, Model* model) {  	m = model;  	mvp = dev->create_buffer(  		"Model instance MVP", @@ -287,11 +289,26 @@ void Model_Instance::init(Device* dev, Model* model) {  		Buffer_Flags::constant_buffer |  		Buffer_Flags::cpu_readwrite  	); +	bounds = (AABB*)heap_alloc( +		h, +		sizeof *bounds * model->mesh_count +	);  } -void Model_Instance::destroy(Device* dev) { +void Model_Instance::destroy(Device* dev, Heap* h) {  	dev->destroy_buffer(mat);  	dev->destroy_buffer(mvp); +	heap_free(h, bounds); +} + +void Model_Instance::update() { +	Mesh* meshes = m->get_meshes(); +	int i, c = m->mesh_count; +	for (i = 0; i < c; i++) +		bounds[i] = m4f::transform( +			transform * meshes[i].world, +			meshes[i].bound +		);  }  void Model_Instance::update_cbuffers( @@ -400,12 +417,34 @@ void Model_Instance::render(  	}  } +int Model_Instance::pick(const v3f& o, const v3f& d) { +	int i, c = m->mesh_count; +	for (i = 0; i < c; i++) { +		AABB& b = bounds[i]; +		v3f id = 1.0f / d; +		v3f t1 = (b.min - o) * id; +		v3f t2 = (b.max - o) * id; +		float tmin = std::max(std::max(std::min(t1.x, t2.x), std::min(t1.y, t2.y)), std::min(t1.z, t2.z)); +		float tmax = std::min(std::min(std::max(t1.x, t2.x), std::max(t1.y, t2.y)), std::max(t1.z, t2.z)); +		if (tmax < 0.0f || tmin > tmax) +		{ +			continue; +		} +		return i; +	} +	return -1; +} +  void Model_Scene::init(  	Arena* arena,  	int max_instances,  	Sampler_Id s  ) { -	instances = (Model_Instance*)arena_alloc(arena, max_instances); +	int hs; +	h = (Heap*)arena_alloc(arena, sizeof *h); +	hs = arena->size - arena->ptr - allocation_default_alignment - 1; +	init_heap(h, arena_alloc(arena, hs), hs); +	instances = (Model_Instance*)heap_alloc(h, max_instances);  	count = 0;  	max = max_instances;  	sampler = s; @@ -417,7 +456,7 @@ Model_Instance* Model_Scene::instantiate(  ) {  	Model_Instance* instance = &instances[count++];  	assert(count <= max); -	instance->init(dev, model); +	instance->init(dev, h, model);  	return instance;  } @@ -427,7 +466,7 @@ void Model_Scene::uninstantiate(  ) {  	int idx = model - instances;  	int last = count - 1; -	model->destroy(dev); +	model->destroy(dev, h);  	instances[idx] = instances[last];  	count = last;  } @@ -435,8 +474,10 @@ void Model_Scene::uninstantiate(  void Model_Scene::update(const Camera& cam, Device* dev) {  	int i;  	Model_Instance* instance = instances; -	for (i = 0; i < count; i++, instance++) +	for (i = 0; i < count; i++, instance++) { +		instance->update();  		instance->update_cbuffers(dev, cam); +	}  }  void Model_Scene::render( @@ -456,5 +497,28 @@ void Model_Scene::destroy(Device* dev) {  	int i;  	Model_Instance* instance = instances;  	for (i = 0; i < count; i++, instance++) -		instance->destroy(dev); +		instance->destroy(dev, h); +} + +std::pair<Model_Instance*, int> Model_Scene::pick( +	const Camera& cam, +	int w, +	int h, +	int mx, +	int my +) { +	int i, c = count; +	v2f uv = (2.0f * v2f(mx, my) - v2f(w, h)) / (float)h; +	v4f e = +		cam.get_proj().inverse() * +		v4f(uv.x, uv.y, -1.0f, 1.0f); +	e.z = -1.0f; e.w = 0.0f; +	v4f d4 = cam.get_view().inverse() * e; +	v3f d = v3f::normalised(v3f(d4.x, d4.y, d4.z)); +	for (i = 0; i < c; i++) { +		int m = instances[i].pick(cam.position, d); +		if (m >= 0) return { &instances[i], m }; +	} +	return { 0, -1 };  } + @@ -5,6 +5,8 @@  #include "maths.hpp"  #include "video.hpp" +#include <tuple> +  struct Material : public Asset {  	float metalness, roughness, ao;  	v3f albedo; @@ -93,9 +95,11 @@ struct Model_Instance {  	Buffer_Id mat;  	m4f transform;  	Model* m; +	AABB* bounds; -	void init(Device* dev, Model* model); -	void destroy(Device* dev); +	void init(Device* dev, Heap* h, Model* model); +	void destroy(Device* dev, Heap* h); +	void update();  	void update_cbuffers(Device* dev, const Camera& cam);  	void render(  		Device* dev, @@ -104,9 +108,11 @@ struct Model_Instance {  		Texture_Id env_cubemap,  		Sampler_Id sampler  	); +	int pick(const v3f& o, const v3f& d);  };  struct Model_Scene { +	Heap* h;  	Model_Instance* instances;  	int count, max;  	Sampler_Id sampler; @@ -124,6 +130,13 @@ struct Model_Scene {  		Texture_Id env_cubemap,  		Sampler_Id sampler  	); +	std::pair<Model_Instance*, int> pick( +		const Camera& cam, +		int w, +		int h, +		int mx, +		int my +	);  };  #endif diff --git a/pipeline.cpp b/pipeline.cpp index 1b55966..30c110c 100644 --- a/pipeline.cpp +++ b/pipeline.cpp @@ -165,6 +165,10 @@ void Pipeline_Builder::depth(bool test, bool write, Depth_Mode mode) {  	pip->depth_mode = mode;  } +void Pipeline_Builder::geo(Geo_Type type) { +	pip->geo = type; +} +  void Pipeline_Builder::blend(  	Blend_Mode mode,  	Blend_Factor src, @@ -267,6 +271,7 @@ void Pipeline::hash() {  	h(pipeline_hash, depth_test);  	h(pipeline_hash, depth_write);  	h(pipeline_hash, depth_mode); +	h(pipeline_hash, geo);  	h(pipeline_hash, blend_enable);  	h(pipeline_hash, blend_mode);  	h(pipeline_hash, blend_mode_alpha); @@ -515,9 +515,9 @@ void UI::render(Arena* s, Texture_Id target) {  	pb.texture(shader_info.atlas_binding, atlas, sampler);  	pipeline = &pb.build(); -	mesh.reset(Rect(0, 0, t.w, t.h));  	root->render();  	mesh.draw(this, ctx, *pipeline, *render_pass); +	mesh.reset(Rect(0, 0, t.w, t.h));  }  void UI::draw_container(const Rect& bound, Colour c) { @@ -274,6 +274,19 @@ static VkCullModeFlags get_vk_cull_mode(Cull_Mode mode) {  	return VK_CULL_MODE_NONE;  } +static VkPrimitiveTopology get_topology(Geo_Type type) { +	switch (type) { +		case Geo_Type::triangles: +			return VK_PRIMITIVE_TOPOLOGY_TRIANGLE_LIST; +		case Geo_Type::lines: +			return VK_PRIMITIVE_TOPOLOGY_LINE_LIST; +		case Geo_Type::points: +			return VK_PRIMITIVE_TOPOLOGY_POINT_LIST; +	} +	assert(0); +	return VK_PRIMITIVE_TOPOLOGY_TRIANGLE_LIST; +} +  static VkFormat get_vk_format(Texture_Format fmt) {  	switch (fmt) {  		case texture_format_r8i:     return VK_FORMAT_R8_UNORM; @@ -2847,11 +2860,9 @@ void Pipeline_Vk::init_input_assembly(  			sizeof ia  		);  	(void)dev; -	(void)desc; -	(void)info;  	zero(&ia, sizeof ia);  	ia.sType = VK_STRUCTURE_TYPE_PIPELINE_INPUT_ASSEMBLY_STATE_CREATE_INFO; -	ia.topology = VK_PRIMITIVE_TOPOLOGY_TRIANGLE_LIST; +	ia.topology = get_topology(desc.geo);  	info.pInputAssemblyState = &ia;  } @@ -110,6 +110,12 @@ enum class Cull_Mode {  	front  }; +enum class Geo_Type { +	triangles, +	lines, +	points +}; +  struct Pipeline {  	uint64_t pipeline_hash;  	uint64_t descriptor_resource_hash; @@ -118,6 +124,7 @@ struct Pipeline {  	bool depth_test, depth_write;  	bool blend_enable;  	Depth_Mode depth_mode; +	Geo_Type geo;  	Blend_Factor blend_src, blend_dst;  	Blend_Mode blend_mode;  	Blend_Factor blend_src_alpha, blend_dst_alpha; @@ -145,6 +152,7 @@ struct Pipeline {  			depth_test == other.depth_test &&  			depth_write == other.depth_write &&  			depth_mode == other.depth_mode && +			geo == other.geo &&  			blend_enable == other.blend_enable &&  			blend_src == other.blend_src &&  			blend_dst == other.blend_dst && @@ -300,6 +308,7 @@ struct Pipeline_Builder {  	void viewport(int x, int y, int w, int h);  	void scissor(int x, int y, int w, int h);  	void depth(bool test, bool write, Depth_Mode mode); +	void geo(Geo_Type type);  	void blend(Blend_Mode mode, Blend_Factor src, Blend_Factor dst);  	void blend(  		Blend_Mode mode_col, |