diff options
author | quou <quou@disroot.org> | 2024-12-23 14:41:01 +1100 |
---|---|---|
committer | quou <quou@disroot.org> | 2024-12-23 14:41:01 +1100 |
commit | b293168cc158d65f1a5146f155921ff82119d1bc (patch) | |
tree | 08cc5ee235b4b7ca6ead45c43f9b407664971613 | |
parent | 58245585cbe77e6c03ebe13f29e10393ff3c45b4 (diff) |
Texture conversion and loading
-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 |