diff options
| -rw-r--r-- | c2.cpp | 1 | ||||
| -rw-r--r-- | ui.cpp | 329 | ||||
| -rw-r--r-- | ui.hpp | 89 | 
3 files changed, 272 insertions, 147 deletions
| @@ -126,7 +126,6 @@ extern "C" int entrypoint() {  	);  	clamped_linear = create_clamped_linear(dev);  	ui = UI::create(dev, &ui_arena, ui_shader->id); -	ui->create_label("Hello, world", 10, 15);  	assert(per_frame != 0);  	uint8_t r = 0;  	float rot = 0.0f; @@ -8,9 +8,9 @@ extern "C" {  #include <string.h> -#define mesh_size (1024 * 8) +#define vertex_buffer_count (256) -static int font_w = 960; +static constexpr int font_w = 960;  static unsigned font_data[] = {  	0x00000000, 0x00003000, 0x00000000, 0x00000000, 0x00000000,  	0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, @@ -74,9 +74,10 @@ static unsigned font_data[] = {  	0x0003c01e, 0x00000000, 0x00000000, 0x00000000, 0x00000000  }; -static Texture_Id create_atlas(Device* d, Arena* a) { +static Texture_Id create_atlas(Device* d) {  	int x, y; -	int size = 10 * font_w; +	int w = font_w + 10; +	int size = 10 * w;  	Buffer_Id buf = d->create_buffer(  		"font atlas stage",  		size, @@ -88,9 +89,16 @@ static Texture_Id create_atlas(Device* d, Arena* a) {  	for (y = 0; y < 10; y++) {  		for (x = 0; x < font_w; x++) {  			int si = x + y * font_w; +			int di = x + y * w;  			unsigned bits = font_data[si >> 5];  			int bit = bits & 1 << (si & 0x1f); -			pixels[si] = bit? 0xff: 0x00; +			pixels[di] = bit? 0xff: 0x00; +		} +	} +	/* for solid colours, one white square at the end */ +	for (y = 0; y < 10; y++) { +		for (x = font_w; x < w; x++) { +			pixels[x + y * w] = 0xff;  		}  	}  	d->unmap_buffer(buf); @@ -98,12 +106,11 @@ static Texture_Id create_atlas(Device* d, Arena* a) {  		"font atlas",  		texture_format_r8i,  		Texture_Flags::sampleable | Texture_Flags::copy_dst, -		font_w, +		w,  		10,  		buf  	);  	d->destroy_bufferi(buf); -	clear_arena(a);  	return tex;  } @@ -116,10 +123,176 @@ static Sampler_Id create_clamped_point(Device* dev) {  	return dev->create_sampler("clamped point", s);  } +UI::Colour::Colour(unsigned rgb, uint8_t a): +	r((rgb >> 16) & 0xff), +	g((rgb >> 8)  & 0xff), +	b((rgb)       & 0xff), +	a(a) {} + +void UI::Vertex_Buffer::init(Device* dev) { +	buf.init( +		dev, +		"UI mesh", +		vertex_buffer_count * sizeof(Vertex) * 4, +		Buffer_Flags::vertex_buffer +	); +	init_indices(dev); +	usage = 0; +	next = 0; +} + +void UI::Vertex_Buffer::init_indices(Device* dev) { +	auto create_ind = [](uint16_t* target){ +		int i, i2, e = vertex_buffer_count * 6; +		for (i = 0, i2 = 0; i < e; i += 6, i2 += 4) { +			target[i    ] = (uint16_t)(i2    ); +			target[i + 1] = (uint16_t)(i2 + 1); +			target[i + 2] = (uint16_t)(i2 + 2); +			target[i + 3] = (uint16_t)(i2 + 1); +			target[i + 4] = (uint16_t)(i2 + 3); +			target[i + 5] = (uint16_t)(i2 + 2); +		} +	}; +	int s = vertex_buffer_count * 6 * sizeof(uint16_t); +	Buffer_Id stage = dev->create_buffer( +		"UI mesh indices stage", +		s, +		Buffer_Flags::copy_src | +		Buffer_Flags::cpu_readwrite +	); +	void* mem = dev->map_buffer(stage, 0, s); +	create_ind((uint16_t*)mem); +	indices = dev->create_buffer( +		"UI indices", +		s, +		Buffer_Flags::copy_dst | +		Buffer_Flags::index_buffer +	); +	Context& ctx = dev->acquire(); +	ctx.copy(indices, stage); +	dev->submit(ctx); +	dev->unmap_buffer(stage); +	dev->destroy_bufferi(stage); +} + +void UI::Vertex_Buffer::destroy(UI* ui) { +	Device* dev = ui->device; +	if (next) { +		next->destroy(ui); +		heap_free(ui->heap, next); +	} +	dev->destroy_buffer(indices); +	buf.destroy(dev); +} + +void UI::Vertex_Buffer::add_quad( +	UI* ui, +	int x, +	int y, +	int w, +	int h, +	float u0, +	float v0, +	float u1, +	float v1, +	Colour col +) { +	Vertex* verts; +	int index = usage * 4; +	float r, g, b, a; +	if (usage >= vertex_buffer_count) { +		if (!next) { +			next = (Vertex_Buffer*)heap_alloc(ui->heap, sizeof *next); +			next->init(ui->device); +		} +		next->add_quad(ui, x, y, w, h, u0, v0, u1, v1, col); +		return; +	} +	r = col.r_f(); +	g = col.g_f(); +	b = col.b_f(); +	a = col.a_f(); +	verts = &((Vertex*)buf.map(ui->device))[index]; +	verts[0] = Vertex { (float)x,     (float)y,     u0, v0, r, g, b, a }; +	verts[1] = Vertex { (float)x + w, (float)y,     u1, v0, r, g, b, a }; +	verts[2] = Vertex { (float)x,     (float)y + h, u0, v1, r, g, b, a }; +	verts[3] = Vertex { (float)x + w, (float)y + h, u1, v1, r, g, b, a }; +	buf.unmap(ui->device); +	usage++; +} + +void UI::Vertex_Buffer::add_rect( +	UI* ui, +	int x, +	int y, +	int w, +	int h, +	Colour col +) { +	constexpr float white_uv_x = (float)font_w / (float)(font_w + 10); +	add_quad(ui, x, y, w, h, white_uv_x, 0.0f, 1.0f, 1.0f, col); +} + +void UI::Vertex_Buffer::add_char( +	UI* ui, +	int x, +	int y, +	char ch, +	Colour col +) { +	int off = (ch - ' ') * 10; +	float w = (float)(font_w + 10); +	float u0 = (float)off / w; +	float u1 = u0 + (10.0f / w); +	add_quad(ui, x, y, 10, 10, u0, 0.0f, u1, 1.0f, col); +} + +void UI::Vertex_Buffer::add_text( +	UI* ui, +	int x, +	int y, +	const char* txt, +	Colour col +) { +	for (; *txt; txt++) +		add_char(ui, x += 10, y, *txt, col); +} + +void UI::Vertex_Buffer::update_buffer(Context& ctx) { +	buf.update(ctx); +	if (next) +		next->update_buffer(ctx); +} + +void UI::Vertex_Buffer::draw(UI* ui, Context& ctx, Pipeline& pip, Render_Pass& rp) { +	Vertex_Buffer_Binding binding[] = {{ +		.id = buf.gpuonly, +		.offset = 0, +		.target = ui->shader_info.vert_binding +	}, {}}; +	Index_Buffer_Binding indb = { +		.id = indices, +		.offset = 0 +	}; +	Draw d{}; +	d.verts = binding; +	d.inds = indb; +	d.vertex_count = usage * 6; +	d.instance_count = 1; +	ctx.submit(d, pip, rp); +	if (next) +		next->draw(ui, ctx, pip, rp); +	usage = 0; +} +  UI* UI::create(Device* dev, Arena* a, Shader_Id sh) { -	Texture_Id atlas = create_atlas(dev, a); +	int hs; +	Texture_Id atlas = create_atlas(dev);  	UI* u = (UI*)arena_alloc(a, sizeof *u); -	u->init(dev, a, atlas, sh); +	Heap* h = (Heap*)arena_alloc(a, sizeof *h); +	hs = a->size - a->ptr - allocation_default_alignment - 1; +	init_heap(h, arena_alloc(a, hs), hs); +	u->init(dev, h, atlas, sh);  	return u;  } @@ -134,24 +307,23 @@ int UI::text_height(const char* t) {  	return 10;  } -void UI::init(Device* dev, Arena* a, Texture_Id at, Shader_Id sh) { +void UI::init( +	Device* dev, +	Heap* h, +	Texture_Id at, +	Shader_Id sh +) {  	Shader* sp; -	arena = a; +	heap = h;  	device = dev; -	tree = 0;  	atlas = at;  	shader = sh; -	mesh = device->create_buffer( -		"UI Mesh", -		mesh_size, -		Buffer_Flags::vertex_buffer | -		Buffer_Flags::cpu_readwrite -	); -	config_buf = device->create_buffer( -		"UI Cbuffer", +	mesh.init(device); +	cbuffer.init( +		device, +		"UI CBuffer",  		sizeof(UI_CBuffer), -		Buffer_Flags::constant_buffer | -		Buffer_Flags::cpu_readwrite +		Buffer_Flags::constant_buffer  	);  	sp = &dev->get_shader(sh);  	vertex_format = sp->vf; @@ -163,110 +335,30 @@ void UI::init(Device* dev, Arena* a, Texture_Id at, Shader_Id sh) {  void UI::destroy() {  	device->destroy_texture(atlas); -	device->destroy_buffer(mesh); -	device->destroy_buffer(config_buf);  	device->destroy_sampler(sampler); -} - -int UI::Element::size(Type t) { -	switch (t) { -		case Type::label: -			return sizeof(Label); -	} -	assert(0); -	return 0; -} - -UI::Element* UI::create_element(Element::Type t) { -	Element* e = (Element*)arena_alloc(arena, Element::size(t)); -	e->type = t; -	e->next = 0; -	if (tree) -		tree->next = e; -	else -		tree = e; -	return e; -} - -UI::Label* UI::create_label(const char* t, int x, int y) { -	Label* l = (Label*)create_element(Element::Type::label); -	l->text = dup_string(arena, t); -	l->len = string_len(t); -	l->x = x; -	l->y = y; -	l->w = text_width(t); -	l->h = text_height(t); -	return l; +	mesh.destroy(this); +	cbuffer.destroy(device);  }  void UI::update(Arena* s) {  	(void)s;  } -int UI::render_label(const Label* l, int off) { -	int vc = 6 * l->len, i; -	int x = l->x; -	int y = l->y; -	const char* c; -	float o = 10.0f; -	Vertex* verts = (Vertex*)device->map_buffer( -		mesh, -		off * sizeof(Vertex), -		vc * sizeof(Vertex) -	); -	for (i = 0, c = l->text; i < vc; i += 6, c++) { -		float uo = (float)((*c - ' ') * 10) / (float)font_w; -		float w = 10.0f / (float)font_w; -		float uv[2] = { -			uo, uo + w -		}; -		verts[i] = Vertex { -			(float)x, (float)y, uv[0], 0.0f, 1.0f, 1.0f, 1.0f, 1.0f -		}; -		verts[i + 1] = Vertex { -			(float)x, (float)y + o, uv[0], 1.0f, 1.0f, 1.0f, 1.0f, 1.0f -		}; -		verts[i + 2] = Vertex { -			(float)x + o, (float)y + o, uv[1], 1.0f, 1.0f, 1.0f, 1.0f, 1.0f -		}; -		verts[i + 3] = Vertex { -			(float)x, (float)y, uv[0], 0.0f, 1.0f, 1.0f, 1.0f, 1.0f -		}; -		verts[i + 4] = Vertex { -			(float)x + o, (float)y, uv[1], 0.0f, 1.0f, 1.0f, 1.0f, 1.0f -		}; -		verts[i + 5] = Vertex { -			(float)x + o, (float)y + o, uv[1], 1.0f, 1.0f, 1.0f, 1.0f, 1.0f -		}; -		x += 10; -	} -	device->unmap_buffer(mesh); -	return vc; -} -  void UI::render(Arena* s, Texture_Id target) { -	Element* e = tree; -	int vc = 0; +	int i, c = 2000; +	Context& ctx = device->get_ctx();  	Texture& t = device->get_texture(target); -	UI_CBuffer* config = (UI_CBuffer*)device->map_buffer( -		config_buf, -		0, -		sizeof(UI_CBuffer) -	); +	UI_CBuffer* config = (UI_CBuffer*)cbuffer.map(device);  	config->projection = m4f::orth(  		0.0f, (float)t.w,  		0.0f, (float)t.h,  		-1.0f,  		1.0f  	); -	device->unmap_buffer(config_buf); -	for (; e; e = e->next) { -		switch (e->type) { -			case Element::Type::label: -				vc += render_label((Label*)e, vc); -				break; -		} -	} +	cbuffer.unmap(device); +	cbuffer.update(ctx); + +	mesh.add_text(this, 10, 10, "Hello, world!", 0xff00ff);  	Pipeline_Builder pb(s, device);  	pb.begin_rp(); @@ -281,19 +373,10 @@ void UI::render(Arena* s, Texture_Id target) {  		Blend_Factor::src_alpha,  		Blend_Factor::inv_src_colour  	); -	pb.cbuffer(shader_info.config_binding, config_buf); +	pb.cbuffer(shader_info.config_binding, cbuffer.gpuonly);  	pb.texture(shader_info.atlas_binding, atlas, sampler);  	Pipeline& pip = pb.build(); -	Vertex_Buffer_Binding binding[] = {{ -		.id = mesh, -		.offset = 0, -		.target = shader_info.vert_binding -	}, {}}; - -	Draw draw{}; -	draw.verts = binding; -	draw.vertex_count = vc; -	draw.instance_count = 1; -	device->get_ctx().submit(draw, pip, pass); +	mesh.update_buffer(ctx); +	mesh.draw(this, ctx, pip, pass);  } @@ -6,36 +6,79 @@  struct Arena;  struct UI { -	struct Rect { -		int x, y, w, h; +	struct Vertex { +		float x, y, u, v, r, g, b, a;  	}; -	struct Element : Rect { -		enum class Type { -			label -		} type; -		Element* next; -		static int size(Type t); +	struct Colour { +		uint8_t r, g, b, a; +		Colour(unsigned rgb, uint8_t a = 0xff); +		float r_f() { return (float)r / 255.0f; }; +		float g_f() { return (float)g / 255.0f; }; +		float b_f() { return (float)b / 255.0f; }; +		float a_f() { return (float)a / 255.0f; };  	}; -	struct Label : Element { -		char* text; -		int len; -		int x, y; +	struct Vertex_Buffer { +		Staged_Buffer buf; +		Buffer_Id indices; +		int usage; + +		void init(Device* dev); +		void init_indices(Device* dev); +		void destroy(UI* ui); +		void update_buffer(Context& ctx); + +		void add_quad( +			UI* ui, +			int x, +			int y, +			int w, +			int h, +			float u0, +			float v0, +			float u1, +			float v1, +			Colour col +		); +		void add_rect( +			UI* ui, +			int x, +			int y, +			int w, +			int h, +			Colour col +		); +		void add_char(UI* ui, int x, int y, char ch, Colour col); +		void add_text( +			UI* ui, +			int x, +			int y, +			const char* txt, +			Colour col +		); +		void draw( +			UI* ui, +			Context& ctx, +			Pipeline& pip, +			Render_Pass& rp +		); +		Vertex_Buffer* next;  	}; -	struct Vertex { -		float x, y, u, v, r, g, b, a; +	struct Rect { +		int x, y, w, h;  	}; -	Arena* arena; +	Heap* heap;  	Device* device; -	Element* tree;  	Texture_Id atlas;  	Shader_Id shader;  	Vertex_Format_Id vertex_format; -	Buffer_Id mesh, config_buf;  	Sampler_Id sampler; +	Staged_Buffer cbuffer; +	Vertex_Buffer mesh; +  	struct UI_CBuffer {  		m4f projection;  	}; @@ -50,15 +93,15 @@ struct UI {  	static UI* create(Device* dev, Arena* a, Shader_Id sh); -	Element* create_element(Element::Type t); -	Label* create_label(const char* t, int x, int y); - -	void init(Device* dev, Arena* a, Texture_Id atlas, Shader_Id sh); +	void init( +		Device* dev, +		Heap* h, +		Texture_Id atlas, +		Shader_Id sh +	);  	void destroy();  	void update(Arena* s);  	void render(Arena* s, Texture_Id target); - -	int render_label(const Label* l, int off);  };  #endif |