diff options
| -rw-r--r-- | .gitignore | 1 | ||||
| -rw-r--r-- | Makefile | 13 | ||||
| -rw-r--r-- | asset.cpp | 18 | ||||
| -rw-r--r-- | asset.hpp | 11 | ||||
| -rw-r--r-- | c2.cpp | 42 | ||||
| -rw-r--r-- | convtexture.c | 217 | ||||
| -rw-r--r-- | intermediate/22.bmp | bin | 0 -> 4194442 bytes | |||
| -rw-r--r-- | todo.txt | 2 | ||||
| -rw-r--r-- | video.cpp | 584 | ||||
| -rw-r--r-- | video.hpp | 30 | 
10 files changed, 769 insertions, 149 deletions
@@ -9,3 +9,4 @@ tags  /c2  /pack  /packer +/convtexture @@ -3,8 +3,9 @@  target = c2  data_dir = data  shaders = $(data_dir)/triangle.csh -packed_files = $(shaders) -tools = qstd cfg sc packer +textures = $(data_dir)/22.tex +packed_files = $(shaders) $(textures) +tools = qstd cfg sc  objects = app.o c2.o video.o pipeline.o asset.o  includes = -Iqstd  defines = -Dplat_x86 -Dplat_posix -Dplat_x11 -Dallocation_default_alignment=8 @@ -13,7 +14,7 @@ libs = -lX11 -lm  lflags = $(libs) $(DEBUG_LINK_FLAG)  .PHONY: all clean $(tools) -all: $(target) $(shaders) $(tools) $(objects) +all: $(target) $(shaders) $(tools) $(objects) pack  qstd:  	$(MAKE) -C qstd @@ -27,12 +28,18 @@ sc: qstd cfg  packer: packer.c | qstd  	$(CC) $(cflags) packer.c $(lflags) -Lqstd -lqstd -o packer +convtexture: convtexture.c | qstd +	$(CC) $(cflags) convtexture.c $(lflags) -Lqstd -lqstd -o convtexture +  pack: $(packed_files) packer  	./packer pack $(data_dir) $(packed_files)  data/triangle.csh: intermediate/triangle.glsl | $(data_dir) sc  	./sc/sc intermediate/triangle.glsl $(data_dir)/triangle.csh +data/22.tex: intermediate/22.bmp | $(data_dir) convtexture +	./convtexture intermediate/22.bmp $(data_dir)/22.tex bc1 +  app.o:  	$(CXX) -c $(cflags) app.cpp -o app.o @@ -8,6 +8,7 @@ extern "C" {  }  #define max_asset_types 32 +#define asset_scratch_size (1024 * 1024)  struct RLoader {  	char magic[4]; @@ -68,13 +69,19 @@ void register_asset_loader(  void Asset_Arena::init(Arena* arena, const char* pack_name) {  	p = pack_open(pack_name, arena);  	a = arena; +	s = (Arena*)arena_alloc(a, sizeof *s); +	init_arena( +		s, +		arena_alloc(a, asset_scratch_size), +		asset_scratch_size +	);  	assets = 0;  }  void Asset_Arena::destroy() {  	Asset* a;  	for (a = assets; a; a = a->next) { -		a->unload(a->loader); +		a->loader->unload(a);  	}  	pack_close(p);  } @@ -93,9 +100,12 @@ Asset* Asset_Arena::load(const char* name) {  	pack_read(f, magic, 4);  	pack_seek(f, 0, seek_rel_start);  	Asset_Loader& loader = manager.get_loader(magic); -	Asset* asset = loader.load(a, f); -	asset->loader = &loader; +	clear_arena(s); +	Asset* asset = loader.load(a, s, f); +	if (asset) { +		asset->loader = &loader; +		claim(asset); +	}  	pack_close_file(f); -	claim(asset);  	return asset;  } @@ -9,21 +9,16 @@ struct Pack;  struct Asset {  	Asset* next;  	Asset_Loader* loader; -	virtual bool load( -		Asset_Loader* loader, -		Arena* a, -		Pack_File* f -	) = 0; -	virtual void unload(Asset_Loader* loader) = 0;  };  struct Asset_Loader { -	virtual Asset* load(Arena* a, Pack_File* f) = 0; +	virtual Asset* load(Arena* a, Arena* s, Pack_File* f) = 0; +	virtual void unload(Asset* a) = 0;  };  struct Asset_Arena {  	Pack* p; -	Arena* a; +	Arena* a, * s;  	Asset* assets;  	void init(Arena* arena, const char* pack_name); @@ -23,12 +23,39 @@ struct C2 : public App {  	}  }; +static Buffer_Id upload_verts(Device* dev) { +	Buffer_Id stage = dev->create_buffer( +		sizeof verts, +		Buffer_Flags::copy_src | +		Buffer_Flags::cpu_readwrite +	); +	Buffer_Id vbo = dev->create_buffer( +		sizeof verts, +		Buffer_Flags::vertex_buffer | +		Buffer_Flags::copy_dst +	); +	{ +		void* mem; +		mem = dev->map_buffer(stage, 0, sizeof verts); +		memcpy(mem, verts, sizeof verts); +		dev->unmap_buffer(stage); +	} +	{ +		Context& ctx = dev->acquire(); +		ctx.copy(vbo, stage); +		dev->submit(ctx); +	} +	dev->destroy_buffer(stage); +	return vbo; +} +  int main() {  	Arena video_arena;  	Arena asset_arena;  	Asset_Arena assets;  	Device* dev;  	Shader* shader; +	Texture* texture;  	Buffer_Id vbo;  	C2* app = App::create<C2>("c2");  	void* per_frame; @@ -47,23 +74,14 @@ int main() {  	dev = Device::create(&video_arena, app);   	app->dev = dev;  	shader = (Shader*)assets.load("triangle.csh"); +	texture = (Texture*)assets.load("22.tex");  	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); -	} +	vbo = upload_verts(dev);  	while (app->running) {  		Arena frame_arena;  		init_arena(&frame_arena, per_frame, per_frame_memory_size); @@ -92,7 +110,7 @@ int main() {  		draw.vertex_count = 3;  		draw.instance_count = 1; -		dev->get_ctx().submit(*dev, draw, pip, pass); +		dev->get_ctx().submit(draw, pip, pass);  	/*	dev->get_ctx().submit(*dev, pass);*/  		r += 10; diff --git a/convtexture.c b/convtexture.c new file mode 100644 index 0000000..a04e3c9 --- /dev/null +++ b/convtexture.c @@ -0,0 +1,217 @@ +#include <stdio.h> +#include <stdlib.h> + +#include "plat.h" +#include "str.h" +#include "vid_enums.h" + +typedef struct { +	float r, g, b; +} vec3; + +typedef struct { +	unsigned char r, g, b, a; +} Colour; + +typedef struct { +	Colour* pixels; +	int w, h; +} Image; + +typedef struct { +	vec3 start, end; +	unsigned char indices[4 * 4]; +} Block; + +const char* format_names[] = { +#define x(n) #n, +	texture_format_xmacro() +#undef x +}; + +void get_block(Block* block, Colour* pixels, int stride) { +	int x, y, i; +	vec3 cols[4 * 4]; +	vec3 s, e; +	float cd; +	for (y = 0; y < 4; y++) { +		for (x = 0; x < 4; x++) { +			Colour pixel = pixels[x + y * stride]; +			vec3 vec; +			vec.r = (float)pixel.r / 255.0f; +			vec.g = (float)pixel.g / 255.0f; +			vec.b = (float)pixel.b / 255.0f; +			cols[x + y * 4] = vec; +		} +	} +	cd = -1.0f; +	for (i = 0; i < 4 * 4; i++) { +		int j; +		vec3* a = &cols[i]; +		for (j = i + 1; j < 4 * 4; j++) { +			vec3* b = &cols[j]; +			float d = +				(b->r - a->r) * (b->r - a->r) + +				(b->g - a->g) * (b->g - a->g) + +				(b->b - a->b) * (b->b - a->b); +			if (d > cd) { +				cd = d; +				s = *a; +				e = *b; +			} +		} +	} +	for (i = 0; i < 4 * 4; i++) { +		vec3* col = &cols[i]; +		float d; +		d = +			(e.r - col->r) * (e.r - col->r) + +			(e.g - col->g) * (e.g - col->g) + +			(e.b - col->b) * (e.b - col->b); +		if (cd < 0.0001f) +			block->indices[i] = 0; +		else { +			d = d / cd; +			block->indices[i] = (int)((1.0f - d) * 3.1f); +		} +	} +	block->start = s; +	block->end = e; +} + +void compress_block(const Block* block, FILE* f) { +	int i; +	unsigned start = +		((unsigned)(block->start.r * 31.9999f) << 11) | +		((unsigned)(block->start.g * 63.9999f) << 5) | +		((unsigned)(block->start.b * 31.9999f)); +	unsigned end = +		((unsigned)(block->end.r * 31.9999f) << 11) | +		((unsigned)(block->end.g * 63.9999f) << 5) | +		((unsigned)(block->end.b * 31.9999f)); +	unsigned palette = (start << 16) | end; +	unsigned indices = 0; +	fwrite(&palette, 1, 4, f); +	for (i = 0; i < 4 * 4; i++) { +		indices |= block->indices[i] << (i * 2); +	} +	fwrite(&indices, 1, 4, f); +} + +void compress_bc1(Colour* pixels, int w, int h, FILE* f) { +	int x, y, cw, ch; +	Block block; +	cw = w / 4; +	ch = h / 4; +	for (y = 0; y < ch; y++) { +		for (x = 0; x < cw; x++) { +			get_block(&block, &pixels[x * 4 + y * 4 * w], w); +			compress_block(&block, f); +		} +	} +} + +void convert(Image* image, Texture_Format target, FILE* f) { +	fwrite("TXTR", 1, 4, f); +	fwrite(&image->w, 1, 4, f); +	fwrite(&image->h, 1, 4, f); +	fwrite(&target, 1, 4, f); +	switch (target) { +		case texture_format_bc1: +			compress_bc1(image->pixels, image->w, image->h, f); +			break; +		default: +			print_err("Unsupported target format.\n"); +			pbreak(40); +	} +} + +Texture_Format texture_format_from_string(const char* s) { +	int i, e = sizeof format_names / sizeof *format_names; +	for (i = 0; i < e; i++) +		if (string_equal(s, format_names[i])) +			return (Texture_Format)i; +	print_err("Invalid texture format %s\n", s); +	pbreak(10); +	return (Texture_Format)0; +} + +int main(int argc, const char** argv) { +	FILE* outfile, * infile; +	char bmp_magic[2]; +	unsigned bmp_offset; +	int bmp_w, bmp_h, s, x, y; +	unsigned short bmp_bits; +	Colour* buffer; +	Image img; +	Colour pixel; +	Texture_Format target; + +	if (argc < 4) { +		print_err("Usage: %s infile outfile format.\n", argv[0]); +		return 1; +	} + +	infile = fopen(argv[1], "rb"); +	if (!infile) { +		print_err("Failed to open %s.\n", argv[1]); +		return 2; +	} +	target = texture_format_from_string(argv[3]); + +	fread(bmp_magic, 2, 1, infile); +	if (bmp_magic[0] != 'B' || bmp_magic[1] != 'M') { +		print_err("Not a valid bitmap file.\n"); +		return 3; +	} + +	fseek(infile, 10, SEEK_SET); +	fread(&bmp_offset, 4, 1, infile); +	fseek(infile, 18, SEEK_SET); +	fread(&bmp_w,    4, 1, infile); +	fread(&bmp_h,    4, 1, infile); +	fseek(infile, 28, SEEK_SET); +	fread(&bmp_bits, 2, 1, infile); + +	if (bmp_bits != 32) { +		print_err("Bitmap must have 32 bit pixels. Instead has %d bit pixels.\n", bmp_bits); +		return 4; +	} + +	if (bmp_w % 4 != 0 || bmp_h % 4 != 0) { +		print_err("Bitmap must have a size divisible by four.\n"); +		return 5; +	} + +	fseek(infile, bmp_offset, SEEK_SET); +	s = bmp_w * bmp_h; +	buffer = malloc(s * 4); +	/* Flip & read. */ +	for (y = 0; y < bmp_h; y++) { +		for (x = 0; x < bmp_w; x++) { +			unsigned char t; +			fread(&pixel, 1, 4, infile); +			t = pixel.r; +			pixel.r = pixel.b; +			pixel.b = t; +			if (pixel.a == 0) { +				pixel.r = 0; +				pixel.g = 0; +				pixel.b = 0; +			} +			buffer[x + (bmp_h - y - 1) * bmp_w] = pixel; +		} +	} +	outfile = fopen(argv[2], "wb"); +	if (!outfile) { +		print_err("Failed to open %s.\n", argv[2]); +		return 6; +	} +	img.pixels = buffer; +	img.w = bmp_w; +	img.h = bmp_h; +	convert(&img, target, outfile); +	fclose(outfile); +	return 0; +} + diff --git a/intermediate/22.bmp b/intermediate/22.bmp Binary files differnew file mode 100644 index 0000000..22570e5 --- /dev/null +++ b/intermediate/22.bmp @@ -3,7 +3,7 @@ todo list   - [x] windowing + input   - [x] vulkan device and swapchain, etc   - [x] draw a triangle - - [ ] texture conversion + compression, etc + - [x] texture conversion + compression, etc   - [ ] texture the triangle   - [ ] simple text rendering   - [ ] 3D maths library @@ -176,6 +176,35 @@ struct Hash_Map {  	}  }; +static VkFormat get_vk_format(Texture_Format fmt) { +	switch (fmt) { +		case texture_format_r8i:     return VK_FORMAT_R8_UNORM; +		case texture_format_r16f:    return VK_FORMAT_R16_SFLOAT; +		case texture_format_r32f:    return VK_FORMAT_R32_SFLOAT; +		case texture_format_rg8i:    return VK_FORMAT_R8G8_UNORM; +		case texture_format_rg16f:   return VK_FORMAT_R16G16_SFLOAT; +		case texture_format_rg32f:   return VK_FORMAT_R32G32_SFLOAT; +		case texture_format_rgb8i:   return VK_FORMAT_R8G8B8_UNORM; +		case texture_format_rgb16f:  return VK_FORMAT_R16G16B16_SFLOAT; +		case texture_format_rgb32f:  return VK_FORMAT_R32G32B32_SFLOAT; +		case texture_format_rgba8i:  return VK_FORMAT_R8G8B8A8_UNORM; +		case texture_format_rgba16f: return VK_FORMAT_R16G16B16A16_SFLOAT; +		case texture_format_rgba32f: return VK_FORMAT_R32G32B32A32_SFLOAT; +		case texture_format_bc1:     return VK_FORMAT_BC1_RGB_UNORM_BLOCK; +		default: assert(0); return VK_FORMAT_UNDEFINED; +	} +} + +VkImageLayout state_to_image_layout(Resource_State s) { +	switch (s) { +		case undefined:    return VK_IMAGE_LAYOUT_UNDEFINED; +		case copy_dst:     return VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL; +		case copy_src:     return VK_IMAGE_LAYOUT_TRANSFER_SRC_OPTIMAL; +		case shader_read:  return VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL; +		default: assert(0); return VK_IMAGE_LAYOUT_UNDEFINED; +	} +} +  static void* vk_alloc(  	void* uptr,  	size_t size, @@ -295,6 +324,12 @@ static void deinit_swap_cap(  	if (cap->pms)  heap_free(d->heap, cap->pms);  } +struct Late_Terminated { +	Late_Terminated* next; + +	virtual void destroy(Device_Vk* dev) = 0; +}; +  struct Swapchain {  	VkSwapchainKHR swapchain;  	Texture_Id* textures; @@ -308,6 +343,14 @@ struct Swapchain {  	void recreate(const App& app, Device_Vk* dev);  	void get_images(Device_Vk* dev);  	void destroy(Device_Vk* dev); + +	Texture_Id create_image( +		Device_Vk* dev, +		VkImage image, +		VkImageView view, +		int w, +		int h +	);  };  #define max_contexts 16 @@ -317,7 +360,7 @@ enum {  	context_state_init  = 1 << 1  }; -struct Shader_Vk : public Shader { +struct Shader_Vk : public Shader, public Late_Terminated {  	struct Attribute {  		char name[28];  		SVariable_Type type; @@ -361,7 +404,7 @@ struct Shader_Vk : public Shader {  		Device_Vk* dev,  		FILE* f  	); -	void destroy_internal(Device_Vk* dev); +	void destroy(Device_Vk* dev) override;  	static VkShaderStageFlagBits stage(Shader_Type type) {  		switch (type) { @@ -379,37 +422,42 @@ struct Shader_Vk : public Shader {  struct Renderpass_Vk;  struct Context_Vk : public Context {  	int state; +	Device_Vk* dev;  	VkCommandBuffer cb;  	VkCommandPool pool;  	VkFence fence;  	VkSemaphore semaphore; -	void init_pool(Device_Vk* dev); -	void init_cb(Device_Vk* dev); -	void init_sync(Device_Vk* dev); -	void init(Device_Vk* dev); -	void begin_record(Device_Vk* dev); -	Context_Vk& acquire(Device_Vk* dev); +	void init_pool(); +	void init_cb(); +	void init_sync(); +	void init(Device_Vk* device); +	void begin_record(); +	Context_Vk& acquire(Device_Vk* device);  	void release(); -	void destroy(Device_Vk* dev); +	void destroy(); -	Renderpass_Vk& begin_rp(Device& d, const Render_Pass& rp); +	Renderpass_Vk& begin_rp(const Render_Pass& rp);  	void end_rp(Renderpass_Vk& rpo);  }; -struct Texture_Vk : public Texture { +struct Texture_Vk : public Texture, public Late_Terminated {  	VkImage image;  	VkImageView view; +	VkDeviceMemory memory; +	Resource_State state; + +	void destroy(Device_Vk*) override;  }; -struct Buffer_Vk : public Buffer { +struct Buffer_Vk : public Buffer, public Late_Terminated {  	VkBuffer buf;  	VkDeviceMemory memory;  	VkDeviceSize size;  	int flags;  	void init(Device_Vk* dev, int flags, VkDeviceSize size); -	void destroy(Device_Vk* dev); +	void destroy(Device_Vk* dev) override;  	static VkBufferUsageFlags get_usage(int flags) {  		VkBufferUsageFlags r = 0; @@ -600,7 +648,44 @@ struct std::hash<Render_Pass> {  struct Shader_Loader : public Asset_Loader {  	Device_Vk* dev;  	void init(Device_Vk* d); -	Asset* load(Arena* a, Pack_File* f) override; +	Asset* load(Arena* a, Arena* s, Pack_File* f) override; +	void unload(Asset* a) override; +}; + +struct Texture_Loader : public Asset_Loader { +	Device_Vk* dev; +	static size_t calc_size(Texture_Format fmt, int w, int h); +	void init(Device_Vk* d); +	Asset* load(Arena* a, Arena* s, Pack_File* f) override; +	void unload(Asset* a) override; + +	Buffer_Id upload(void* buf, size_t size); +	Texture_Id create_tex( +		Texture_Format fmt, +		int w, +		int h +	); +}; + +struct Terminator { +	Late_Terminated* queue; + +	void execute(Device_Vk* dev) { +		Late_Terminated* obj = queue; +		for (; obj; obj = obj->next) +			obj->destroy(dev); +		queue = 0; +	} + +	void add(Late_Terminated* obj) { +		if (queue) { +			obj->next = queue; +			queue = obj; +		} else { +			obj->next = 0; +			queue = obj; +		} +	}  };  struct Device_Vk : public Device { @@ -619,6 +704,7 @@ struct Device_Vk : public Device {  	Context_Vk contexts[max_contexts];  	Context_Vk* current_ctx;  	Shader_Loader shader_loader; +	Texture_Loader texture_loader;  #ifdef DEBUG  	VkDebugUtilsMessengerEXT msg;  #endif @@ -639,11 +725,10 @@ struct Device_Vk : public Device {  	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 -	); +	Terminator* terminators; +	uint32_t terminator_index; + +	Texture_Id alloc_texture();  	Buffer_Id alloc_buffer();  	Vertex_Format_Id alloc_vf();  	Vertex_Format_Id create_vf(Shader_Vk& shader); @@ -669,6 +754,8 @@ struct Device_Vk : public Device {  	Pipeline_Vk& get_pso(const Pso_Key& pop);  	void collect_garbage(); +	void queue_destroy(Late_Terminated* obj); +	void create_terminators();  	int find_memory_type(  		uint32_t filter, @@ -980,7 +1067,9 @@ void Device_Vk::init_internal() {  	rpo_cache.init();  	pso_cache.init();  	shader_loader.init(this); +	texture_loader.init(this);  	register_asset_loader("CSH2", &shader_loader); +	register_asset_loader("TXTR", &texture_loader);  	find_exts(exts, ext_count);  	init_ac();  	create_inst(exts, ext_count); @@ -993,12 +1082,31 @@ void Device_Vk::init_internal() {  	gladLoaderLoadVulkan(inst, phys_dev, dev);  	vkGetDeviceQueue(dev, (uint32_t)queue_index, 0, &queue);  	swapchain.init(*app, this); +	terminators = 0; +	terminator_index = 0; +	create_terminators();  	for (i = 0; i < max_contexts; i++)  		contexts[i].state = context_state_avail;  } +void Device_Vk::create_terminators() { +	int i, count = swapchain.image_count; +	if (terminators) { +		for (i = 0; i < count; i++) +			terminators[i].execute(this); +		heap_free(heap, terminators); +	} +	terminators = (Terminator*)heap_alloc( +		heap, +		count * sizeof *terminators +	); +	for (i = 0; i < count; i++) { +		terminators[i].queue = 0; +	} +} +  void Device_Vk::deinit_internal() { -	int i; +	int i, image_count = swapchain.image_count;  	vkDeviceWaitIdle(dev);  	swapchain.destroy(this);  	deinit_swap_cap(this, &swap_cap); @@ -1010,7 +1118,10 @@ void Device_Vk::deinit_internal() {  	for (i = 0; i < max_contexts; i++) {  		auto& context = contexts[i];  		if (context.state & context_state_init) -			context.destroy(this); +			context.destroy(); +	} +	for (i = 0; i < image_count; i++) { +		terminators[i].execute(this);  	}  	vkDestroyDevice(dev, &ac);  #ifdef DEBUG @@ -1030,6 +1141,7 @@ void Device_Vk::on_resize_internal(int w, int h) {  	deinit_swap_cap(this, &swap_cap);  	get_swap_cap(this, phys_dev, surf, &swap_cap);  	swapchain.recreate(*app, this); +	create_terminators();  }  Renderpass_Vk& Device_Vk::create_rpo(const Render_Pass& rp) { @@ -1073,10 +1185,11 @@ void Renderpass_Vk::destroy(Device_Vk* dev) {  }  void Device_Vk::collect_garbage() { +	int max_age = swapchain.image_count + 3;  	for (const auto& i: rpo_cache) {  		auto& rp = i.second;  		rp.age++; -		if (rp.age > 3) { +		if (rp.age > max_age) {  			rp.destroy(this);  			rpo_cache.remove(i.first);  		} @@ -1084,13 +1197,17 @@ void Device_Vk::collect_garbage() {  	for (const auto& i: pso_cache) {  		auto& pip = i.second;  		pip.age++; -		if (pip.age > 3) { +		if (pip.age > max_age) {  			pip.destroy(this);  			pso_cache.remove(i.first);  		}  	}  } +void Device_Vk::queue_destroy(Late_Terminated* obj) { +	terminators[terminator_index].add(obj); +} +  int Device_Vk::find_memory_type(  	uint32_t filter,  	VkMemoryPropertyFlags flags @@ -1271,14 +1388,27 @@ void Swapchain::recreate(const App& app, Device_Vk* dev) {  	old.destroy(dev);  } +Texture_Id Swapchain::create_image( +	Device_Vk* dev, +	VkImage image, +	VkImageView view, +	int w, +	int h +) { +	Texture_Id id = dev->alloc_texture(); +	Texture_Vk& tex = *(Texture_Vk*)&dev->get_texture(id); +	tex.image = image; +	tex.view = view; +	tex.w = w; +	tex.h = h; +	tex.alias = true; +	return id; +} +  void Swapchain::get_images(Device_Vk* dev) {  	unsigned count;  	int i;  	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( @@ -1296,7 +1426,13 @@ void Swapchain::get_images(Device_Vk* dev) {  			format.format,  			VK_IMAGE_ASPECT_COLOR_BIT  		); -		textures[i] = dev->alloc_texture(images[i], view, info); +		textures[i] = create_image( +			dev, +			images[i], +			view, +			size.width, +			size.height +		);  	}  	heap_free(dev->heap, images);  } @@ -1339,6 +1475,9 @@ void Device::begin_frame() {  	Device_Vk* dev = (Device_Vk*)this;  	dev->collect_garbage();  	dev->current_ctx = (Context_Vk*)&acquire(); +	dev->terminator_index++; +	dev->terminator_index %= dev->swapchain.image_count; +	dev->terminators[dev->terminator_index].execute(dev);  	vkAcquireNextImageKHR(  		dev->dev,  		dev->swapchain.swapchain, @@ -1353,20 +1492,18 @@ void Device::begin_frame() {  void Device::submit(Context& ctx_) {  	Context_Vk* ctx = (Context_Vk*)&ctx_;  	Device_Vk* dev = (Device_Vk*)this; -	VkPipelineStageFlags stage = -		VK_PIPELINE_STAGE_COLOR_ATTACHMENT_OUTPUT_BIT;  	VkSubmitInfo si{};  	si.sType = VK_STRUCTURE_TYPE_SUBMIT_INFO; -	si.waitSemaphoreCount = 1; +/*	si.waitSemaphoreCount = 1;  	si.pWaitSemaphores = &ctx->semaphore;  	si.pWaitDstStageMask = &stage;  	si.signalSemaphoreCount = 1; -	si.pSignalSemaphores = &ctx->semaphore; +	si.pSignalSemaphores = &ctx->semaphore;*/  	si.commandBufferCount = 1;  	si.pCommandBuffers = &ctx->cb;  	vkEndCommandBuffer(ctx->cb);  	vkQueueSubmit(dev->queue, 1, &si, ctx->fence); -	ctx->wait(*dev); +	ctx->wait();  	ctx->release();  } @@ -1374,18 +1511,27 @@ void Device::present() {  	Device_Vk* dev = (Device_Vk*)this;  	Context_Vk* ctx = dev->current_ctx;  	VkPresentInfoKHR pi{}; -	VkSemaphore s[1]; -	VkSwapchainKHR sw[1]; -	submit(*ctx); -	s[0] = ctx->semaphore; -	sw[0] = dev->swapchain.swapchain; +	VkSubmitInfo si{}; +	VkPipelineStageFlags stage = +		VK_PIPELINE_STAGE_COLOR_ATTACHMENT_OUTPUT_BIT; +	si.sType = VK_STRUCTURE_TYPE_SUBMIT_INFO; +	si.waitSemaphoreCount = 1; +	si.pWaitSemaphores = &ctx->semaphore; +	si.pWaitDstStageMask = &stage; +	si.signalSemaphoreCount = 1; +	si.pSignalSemaphores = &ctx->semaphore; +	si.commandBufferCount = 1; +	si.pCommandBuffers = &ctx->cb; +	vkEndCommandBuffer(ctx->cb); +	vkQueueSubmit(dev->queue, 1, &si, ctx->fence);  	pi.sType = VK_STRUCTURE_TYPE_PRESENT_INFO_KHR;  	pi.waitSemaphoreCount = 1; -	pi.pWaitSemaphores = s; +	pi.pWaitSemaphores = &ctx->semaphore;  	pi.swapchainCount = 1; -	pi.pSwapchains = sw; +	pi.pSwapchains = &dev->swapchain.swapchain;  	pi.pImageIndices = &dev->backbuffer_index;  	vkQueuePresentKHR(dev->queue, &pi); +	ctx->release();  }  Texture_Id Device::get_backbuffer() { @@ -1396,16 +1542,9 @@ Texture& Device::get_texture(Texture_Id id) {  	return ((Device_Vk*)this)->textures[id];  } -Texture_Id Device_Vk::alloc_texture( -	VkImage img, -	VkImageView view,  -	const Texture& copy -) { +Texture_Id Device_Vk::alloc_texture() {  	Texture_Vk tex{};  	Texture_Id id(texture_count++); -	memcpy(&tex, ©, sizeof(Texture)); -	tex.image = img; -	tex.view = view;  	textures.set(id, tex);  	return id;  } @@ -1445,16 +1584,12 @@ Shader_Id Device_Vk::alloc_shader() {  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); +	dev->queue_destroy((Texture_Vk*)&dev->get_texture(id));  } -void Context::wait(Device& d) { +void Context::wait() {  	Context_Vk* ctx = (Context_Vk*)this; -	Device_Vk* dev = (Device_Vk*)&d; +	Device_Vk* dev = ctx->dev;  	vkWaitForFences(  		dev->dev,  		1, @@ -1465,17 +1600,16 @@ void Context::wait(Device& d) {  }  void Context::submit( -	Device& d,  	const Draw& draw,  	const Pipeline& p,  	const Render_Pass& rp  ) { -	Device_Vk* dev = (Device_Vk*)&d;  	Context_Vk* ctx = (Context_Vk*)this; +	Device_Vk* dev = ctx->dev;  	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); +	auto& rpo = ctx->begin_rp(rp);  	vkCmdBindPipeline(  		ctx->cb,  		VK_PIPELINE_BIND_POINT_GRAPHICS, @@ -1498,13 +1632,13 @@ void Context::submit(  }  void Context::submit( -	Device& d,  	const Draw* draws,  	int count,  	const Pipeline& p,  	const Render_Pass& rp  ) { -	Device_Vk* dev = (Device_Vk*)&d; +	Context_Vk* ctx = (Context_Vk*)this; +	Device_Vk* dev = ctx->dev;  	(void)draws;  	(void)count;  	(void)p; @@ -1514,8 +1648,115 @@ void Context::submit(  	/* todo */  } -Renderpass_Vk& Context_Vk::begin_rp(Device& d, const Render_Pass& rp) { -	Device_Vk* dev = (Device_Vk*)&d; +void Context::copy(Buffer_Id dst, Buffer_Id src) { +	Context_Vk* ctx = (Context_Vk*)this; +	Device_Vk* dev = ctx->dev; +	Buffer_Vk& a = *(Buffer_Vk*)&dev->get_buffer(dst); +	Buffer_Vk& b = *(Buffer_Vk*)&dev->get_buffer(src); +	VkBufferCopy region{}; +	region.srcOffset = 0; +	region.dstOffset = 0; +	region.size = b.size; +	vkCmdCopyBuffer( +		ctx->cb, +		b.buf, +		a.buf, +		1, +		®ion +	); +} + +void Context::copy(Texture_Id dst, Buffer_Id src) { +	Context_Vk* ctx = (Context_Vk*)this; +	Device_Vk* dev = ctx->dev; +	Texture_Vk& a = *(Texture_Vk*)&dev->get_texture(dst); +	Buffer_Vk& b = *(Buffer_Vk*)&dev->get_buffer(src); +	VkBufferImageCopy c{}; +	c.imageSubresource.aspectMask = VK_IMAGE_ASPECT_COLOR_BIT; +	c.imageSubresource.layerCount = 1; +	c.imageExtent.width = a.w; +	c.imageExtent.height = a.h; +	c.imageExtent.depth = 1; +	vkCmdCopyBufferToImage( +		ctx->cb, +		b.buf, +		a.image, +		VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL, +		1, +		&c +	); +} + +void Context::transition(Texture_Id id, Resource_State state) { +	Context_Vk* ctx = (Context_Vk*)this; +	Device_Vk* dev = ctx->dev; +	Texture_Vk& tex = *(Texture_Vk*)&dev->get_texture(id); +	VkImageMemoryBarrier b{}; +	VkImageLayout src_layout = state_to_image_layout(tex.state); +	VkImageLayout dst_layout = state_to_image_layout(state); +	VkPipelineStageFlags src_stage, dst_stage; +	b.sType = VK_STRUCTURE_TYPE_IMAGE_MEMORY_BARRIER; +	b.oldLayout = src_layout; +	b.newLayout = dst_layout; +	b.srcQueueFamilyIndex = VK_QUEUE_FAMILY_IGNORED; +	b.dstQueueFamilyIndex = VK_QUEUE_FAMILY_IGNORED; +	b.image = tex.image; +	b.subresourceRange.aspectMask = VK_IMAGE_ASPECT_COLOR_BIT; +	b.subresourceRange.baseMipLevel = 0; +	b.subresourceRange.levelCount = 1; +	b.subresourceRange.baseArrayLayer = 0; +	b.subresourceRange.layerCount = 1; +	if ( +		src_layout == VK_IMAGE_LAYOUT_UNDEFINED && +		dst_layout == VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL +	) { +		b.srcAccessMask = 0; +		b.dstAccessMask = VK_ACCESS_TRANSFER_WRITE_BIT; +		src_stage = VK_PIPELINE_STAGE_TOP_OF_PIPE_BIT; +		dst_stage = VK_PIPELINE_STAGE_TRANSFER_BIT; +	} else if ( +		src_layout == VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL && +		dst_layout == VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL +	) { +		b.srcAccessMask = VK_ACCESS_TRANSFER_WRITE_BIT; +		b.dstAccessMask = VK_ACCESS_SHADER_READ_BIT; +		src_stage = VK_PIPELINE_STAGE_TRANSFER_BIT; +		dst_stage = VK_PIPELINE_STAGE_FRAGMENT_SHADER_BIT; +	} else if ( +		src_layout == VK_IMAGE_LAYOUT_UNDEFINED && +		dst_layout == VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL +	) { +		b.srcAccessMask = 0; +		b.dstAccessMask = VK_ACCESS_COLOR_ATTACHMENT_WRITE_BIT; +		src_stage = VK_PIPELINE_STAGE_COLOR_ATTACHMENT_OUTPUT_BIT; +		dst_stage = VK_PIPELINE_STAGE_COLOR_ATTACHMENT_OUTPUT_BIT; +	} else if ( +		src_layout == VK_IMAGE_LAYOUT_UNDEFINED && +		dst_layout == VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL +	) { +		b.srcAccessMask = 0; +		b.dstAccessMask = VK_ACCESS_SHADER_READ_BIT; +		src_stage = VK_PIPELINE_STAGE_FRAGMENT_SHADER_BIT; +		dst_stage = VK_PIPELINE_STAGE_FRAGMENT_SHADER_BIT; +	} else { +		print_err("Bad resource transition.\n"); +		pbreak(389); +	} +	vkCmdPipelineBarrier( +		ctx->cb, +		src_stage, +		dst_stage, +		0, +		0, +		0, +		0, +		0, +		1, +		&b +	); +} + +Renderpass_Vk& Context_Vk::begin_rp(const Render_Pass& rp) {  	Renderpass_Vk& rpo = dev->get_rpo(rp);  	VkRenderPassBeginInfo rpbi{};  	rpbi.sType = VK_STRUCTURE_TYPE_RENDER_PASS_BEGIN_INFO; @@ -1537,13 +1778,13 @@ void Context_Vk::end_rp(Renderpass_Vk& rpo) {  	rpo.on_submit();  } -void Context::submit(Device& d, const Render_Pass& rp) { +void Context::submit(const Render_Pass& rp) {  	Context_Vk* ctx = (Context_Vk*)this; -	auto& rpo = ctx->begin_rp(d, rp); +	auto& rpo = ctx->begin_rp(rp);  	ctx->end_rp(rpo);  } -void Context_Vk::init_pool(Device_Vk* dev) { +void Context_Vk::init_pool() {  	VkCommandPoolCreateInfo pi{};  	VkResult r;  	pi.sType = VK_STRUCTURE_TYPE_COMMAND_POOL_CREATE_INFO; @@ -1556,7 +1797,7 @@ void Context_Vk::init_pool(Device_Vk* dev) {  	}  } -void Context_Vk::init_cb(Device_Vk* dev) { +void Context_Vk::init_cb() {  	VkCommandBufferAllocateInfo ci{};  	VkResult r;  	ci.sType = VK_STRUCTURE_TYPE_COMMAND_BUFFER_ALLOCATE_INFO; @@ -1570,7 +1811,7 @@ void Context_Vk::init_cb(Device_Vk* dev) {  	}  } -void Context_Vk::init_sync(Device_Vk* dev) { +void Context_Vk::init_sync() {  	VkFenceCreateInfo fi{};  	VkSemaphoreCreateInfo si{};  	VkResult r; @@ -1589,26 +1830,28 @@ void Context_Vk::init_sync(Device_Vk* dev) {  	}  } -void Context_Vk::init(Device_Vk* dev) { -	init_pool(dev); -	init_cb(dev); -	init_sync(dev); +void Context_Vk::init(Device_Vk* device) { +	dev = device; +	init_pool(); +	init_cb(); +	init_sync();  	state |= context_state_init;  } -void Context_Vk::begin_record(Device_Vk* dev) { +void Context_Vk::begin_record() {  	VkCommandBufferBeginInfo bi{};  	bi.sType = VK_STRUCTURE_TYPE_COMMAND_BUFFER_BEGIN_INFO; +	wait();  	vkResetFences(dev->dev, 1, &fence);  	vkResetCommandBuffer(cb, 0);  	vkBeginCommandBuffer(cb, &bi);  } -Context_Vk& Context_Vk::acquire(Device_Vk* dev) { +Context_Vk& Context_Vk::acquire(Device_Vk* device) {  	if (~state & context_state_init) -		init(dev); +		init(device);  	state &= ~context_state_avail; -	begin_record(dev); +	begin_record();  	return *this;  } @@ -1616,7 +1859,7 @@ void Context_Vk::release() {  	state |= context_state_avail;  } -void Context_Vk::destroy(Device_Vk* dev) { +void Context_Vk::destroy() {  	state &= ~context_state_init;  	vkDestroyCommandPool(dev->dev, pool, &dev->ac);  	vkDestroySemaphore(dev->dev, semaphore, &dev->ac); @@ -2116,17 +2359,14 @@ void Shader_Vk::Vertex_Format::destroy(Device_Vk* dev) {  	heap_free(dev->heap, attributes);  } -void Shader_Vk::destroy_internal(Device_Vk* dev) { +void Shader_Vk::destroy(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); +	dev->shaders.remove(id);  }  int Shader::binding_index(const char* name) { @@ -2148,11 +2388,12 @@ int Shader::attribute_index(const char* name) {  void Buffer_Vk::init(  	Device_Vk* dev,  	int flags, -	VkDeviceSize size +	VkDeviceSize s  ) {  	VkBufferCreateInfo bi{};  	VkMemoryRequirements req;  	VkResult r; +	size = s;  	bi.sType = VK_STRUCTURE_TYPE_BUFFER_CREATE_INFO;  	bi.size = size;  	bi.usage = get_usage(flags); @@ -2190,6 +2431,7 @@ void Buffer_Vk::init(  void Buffer_Vk::destroy(Device_Vk* dev) {  	vkDestroyBuffer(dev->dev, buf, &dev->ac);  	vkFreeMemory(dev->dev, memory, &dev->ac); +	dev->buffers.remove(id);  }  Buffer_Id Device::create_buffer(size_t size, int flags) { @@ -2202,9 +2444,8 @@ Buffer_Id Device::create_buffer(size_t size, int flags) {  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); +	Buffer_Vk* buf = (Buffer_Vk*)&get_buffer(id); +	dev->queue_destroy(buf);  }  void* Device::map_buffer( @@ -2240,42 +2481,32 @@ Shader& Device::get_shader(Shader_Id id) {  	return ((Device_Vk*)this)->shaders[id];  } -bool Shader::load( -	Asset_Loader* loader, -	Arena* a, -	Pack_File* f -) { -	Device_Vk* dev = ((Shader_Loader*)loader)->dev; -	Shader_Vk& sh = *(Shader_Vk*)this; -	return sh.init(dev, f); -} - -void Shader::unload(Asset_Loader* loader_) { -	Shader_Loader* loader = (Shader_Loader*)loader_; -	Device_Vk* dev = loader->dev; -	Shader_Vk& sh = *(Shader_Vk*)this; -	sh.destroy(dev); -	dev->shaders.remove(id); -} -  void Shader_Loader::init(Device_Vk* d) {  	dev = d;  }  Asset* Shader_Loader::load(  	Arena* a, +	Arena* s,  	Pack_File* f  ) { -	Shader_Vk* s; +	Shader_Vk* shader;  	Shader_Id id; +	(void)s; +	(void)a;  	id = dev->alloc_shader(); -	s = (Shader_Vk*)&dev->get_shader(id); -	s->id = id; -	if (!s->load(this, a, f)) { +	shader = (Shader_Vk*)&dev->get_shader(id); +	shader->id = id; +	if (!shader->init(dev, f)) {  		dev->shaders.remove(id);  		return 0;  	} -	return s; +	return shader; +} + +void Shader_Loader::unload(Asset* a) { +	Shader_Vk* sh = (Shader_Vk*)a; +	dev->queue_destroy(sh);  }  bool Shader_Vk::init(Device_Vk* dev, Pack_File* f) { @@ -2323,3 +2554,140 @@ bool Shader_Vk::init(Device_Vk* dev, Pack_File* f) {  	return true;  } +void Texture_Loader::init(Device_Vk* d) { +	dev = d; +} + +size_t Texture_Loader::calc_size( +	Texture_Format fmt, +	int w, +	int h +) { +	switch (fmt) { +		case texture_format_bc1: +			return (w / 4) * (h / 4) * 8; +		default: +			print_err("Can't load this texture format.\n");  +			pbreak(45498); +			return 0; +	} +} + +Buffer_Id Texture_Loader::upload(void* buf, size_t size) { +	void* mem; +	Buffer_Id id = dev->create_buffer( +		size, +		Buffer_Flags::copy_src | +		Buffer_Flags::cpu_readwrite +	); +	mem = dev->map_buffer(id, 0, size); +	memcpy(mem, buf, size); +	dev->unmap_buffer(id); +	return id; +} + +Texture_Id Texture_Loader::create_tex( +	Texture_Format fmt, +	int w, +	int h +) { +	VkImageCreateInfo ii{}; +	VkResult r; +	Texture_Id id = dev->alloc_texture(); +	Texture_Vk& tex = *(Texture_Vk*)&dev->get_texture(id); +	VkMemoryRequirements req; +	ii.sType = VK_STRUCTURE_TYPE_IMAGE_CREATE_INFO; +	ii.imageType = VK_IMAGE_TYPE_2D; +	ii.extent.width = w; +	ii.extent.height = h; +	ii.extent.depth = 1; +	ii.mipLevels = 1; +	ii.arrayLayers = 1; +	ii.format = get_vk_format(fmt); +	ii.tiling = VK_IMAGE_TILING_OPTIMAL; +	ii.initialLayout = VK_IMAGE_LAYOUT_UNDEFINED; +	ii.usage = VK_IMAGE_USAGE_TRANSFER_DST_BIT | VK_IMAGE_USAGE_SAMPLED_BIT; +	ii.sharingMode = VK_SHARING_MODE_EXCLUSIVE; +	ii.samples = VK_SAMPLE_COUNT_1_BIT; +	r = vkCreateImage(dev->dev, &ii, &dev->ac, &tex.image); +	if (r != VK_SUCCESS) { +		print_err("Failed to create an image.\n"); +	} +	vkGetImageMemoryRequirements(dev->dev, tex.image, &req); +	{ +		VkMemoryPropertyFlags props = +			VK_MEMORY_PROPERTY_DEVICE_LOCAL_BIT; +		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, &tex.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); +		} +	} +	vkBindImageMemory(dev->dev, tex.image, tex.memory, 0); +	tex.w = w; +	tex.h = h; +	tex.alias = false; +	tex.view = make_view( +		dev, +		tex.image, +		ii.format, +		VK_IMAGE_ASPECT_COLOR_BIT +	); +	return id; +} + +Asset* Texture_Loader::load(Arena* a, Arena* s, Pack_File* f) { +	char magic[4]; +	void* data; +	int w, h; +	size_t size; +	Texture_Format fmt; +	pack_read(f, magic, 4); +	pack_read(f, &w, 4); +	pack_read(f, &h, 4); +	pack_read(f, &fmt, 4); +	size = calc_size(fmt, w, h); +	data = arena_alloc(s, size); +	pack_read(f, data, size); +	{ +		Buffer_Id buf = upload(data, size); +		Texture_Id tex = create_tex(fmt, w, h); +		{ +			Context& ctx = dev->acquire(); +			ctx.transition(tex, Resource_State::copy_dst); +			ctx.copy(tex, buf); +			ctx.transition(tex, Resource_State::shader_read); +			dev->submit(ctx); +			dev->destroy_buffer(buf); +		} +		return &dev->get_texture(tex); +	} +} + +void Texture_Loader::unload(Asset* a) { +	Texture_Vk* tex = (Texture_Vk*)a; +	tex->destroy(dev); +} + +void Texture_Vk::destroy(Device_Vk* dev) { +	if (!alias) { +		vkDestroyImage(dev->dev, image, &dev->ac); +		vkFreeMemory(dev->dev, memory, &dev->ac); +	} +	vkDestroyImageView(dev->dev, view, &dev->ac); +	dev->textures.remove(id); +} + @@ -96,7 +96,15 @@ struct Pipeline_Builder {  	void validate();  }; -struct Texture { +enum Resource_State { +	undefined, +	copy_dst, +	copy_src, +	shader_read +}; + +struct Texture : public Asset { +	Texture_Id id;  	int w, h;  	bool alias;  }; @@ -114,7 +122,9 @@ namespace Buffer_Flags {  	};  }; -struct Buffer {}; +struct Buffer { +	Buffer_Id id; +};  struct Context;  struct Shader; @@ -149,21 +159,22 @@ struct Device {  };  struct Context { -	void wait(Device& d); +	void wait();  	void submit( -		Device& d,  		const Draw& draw,  		const Pipeline& p,  		const Render_Pass& rp  	);  	void submit( -		Device& d,  		const Draw* draws,  		int count,  		const Pipeline& p,  		const Render_Pass& rp  	); -	void submit(Device& d, const Render_Pass& rp); +	void submit(const Render_Pass& rp); +	void copy(Buffer_Id dst, Buffer_Id src); +	void copy(Texture_Id dst, Buffer_Id src); +	void transition(Texture_Id id, Resource_State state);  };  struct Shader : public Asset { @@ -175,13 +186,6 @@ struct Shader : public Asset {  	int binding_index(const char* name);  	int attribute_index(const char* name);  	int target_index(const char* name); - -	bool load( -		Asset_Loader* loader, -		Arena* a, -		Pack_File* f -	) override; -	void unload(Asset_Loader* loader) override;  };  #endif  |