diff options
-rw-r--r-- | c2.cpp | 5 | ||||
-rw-r--r-- | model.cpp | 5 | ||||
-rw-r--r-- | pipeline.cpp | 117 | ||||
-rw-r--r-- | todo.txt | 4 | ||||
-rw-r--r-- | ui.cpp | 5 | ||||
-rw-r--r-- | vid_enums.h | 7 | ||||
-rw-r--r-- | video.cpp | 363 | ||||
-rw-r--r-- | video.hpp | 89 |
8 files changed, 483 insertions, 112 deletions
@@ -174,12 +174,12 @@ extern "C" int entrypoint() { dev->unmap_buffer(cbuf2); } - Pipeline_Builder pb(&frame_arena); + Pipeline_Builder pb(&frame_arena, dev); pb.begin_rp(); pb.rp_target(dev->get_backbuffer(), { r, 0x00, 0xff, 0xff }); Render_Pass& pass = pb.build_rp(); - pb.begin(dev); + pb.begin(); pb.shader(shader->id); pb.vertex_format(shader->vf); pb.texture( @@ -208,6 +208,7 @@ extern "C" int entrypoint() { pb.begin_rp(); pb.rp_target(dev->get_backbuffer(), Clear_Mode::restore); + pb.rp_depth_target(dev->get_depth_target(), 1.0f); Render_Pass& pass2 = pb.build_rp(); monkey->render(dev, &frame_arena, pass2, cbuf2); @@ -127,14 +127,15 @@ void Model::render( .offset = 0 }; Draw draw{}; - Pipeline_Builder pb(a); + Pipeline_Builder pb(a, dev); draw.verts = vbb; draw.inds = ibb; draw.vertex_count = mesh.count; draw.instance_count = 1; draw.first_vertex = mesh.offset; draw.vertex_offset = mesh.vbo_offset; - pb.begin(dev); + pb.begin(); + pb.depth(true, true, Depth_Mode::less); pb.shader(mesh.shader); pb.cbuffer( shader.descriptor_binding("config_buffer"), diff --git a/pipeline.cpp b/pipeline.cpp index 88b2009..c31ef69 100644 --- a/pipeline.cpp +++ b/pipeline.cpp @@ -8,37 +8,115 @@ extern "C" { #include "str.h" } -Pipeline_Builder::Pipeline_Builder(Arena* arena): - arena(arena) {} +Pipeline_Builder::Pipeline_Builder(Arena* arena, Device* dev): + arena(arena), dev(dev) {} void Pipeline_Builder::begin_rp() { pass = (Render_Pass*)arena_alloc(arena, sizeof *pass); - pass->target = 0; - pass->clear = { 0, 0, 0, 0 }; + zero(pass, sizeof *pass); } void Pipeline_Builder::rp_target(Texture_Id id, Colour clear) { - pass->target = id; - pass->clear = clear; - pass->mode = Clear_Mode::clear; + int i = pass->colour_count++; + Texture& texture = dev->get_texture(id); + Render_Pass::Target t{ + .id = id, + .fmt = texture.fmt, + .mode = Clear_Mode::clear, + .clear = { .colour = clear } + }; + assert(i < max_colour_attachments); + pass->colours[i] = t; } void Pipeline_Builder::rp_target(Texture_Id id, Clear_Mode clear) { - pass->target = id; + int i = pass->colour_count++; + Texture& texture = dev->get_texture(id); + Render_Pass::Target t{ + .id = id, + .fmt = texture.fmt, + .mode = clear, + .clear = { .depth = 0.0f } + }; + assert(i < max_colour_attachments); assert(clear != Clear_Mode::clear); - pass->mode = clear; + pass->colours[i] = t; +} + +void Pipeline_Builder::rp_depth_target(Texture_Id id, float clear) { + Texture& texture = dev->get_texture(id); + Render_Pass::Target t{ + .id = id, + .fmt = texture.fmt, + .mode = Clear_Mode::clear, + .clear = { .depth = clear } + }; + pass->depth = t; +} + +void Pipeline_Builder::rp_depth_target(Texture_Id id, Clear_Mode mode) { + Texture& texture = dev->get_texture(id); + Render_Pass::Target t{ + .id = id, + .fmt = texture.fmt, + .mode = mode, + .clear = { .depth = 0.0f } + }; + assert(mode != Clear_Mode::clear); + pass->depth = t; } void Pipeline_Builder::validate_rp() { - assert(pass->target); + int i, c = pass->colour_count; + int w, h; + assert(c || pass->depth.id); + if (c) { + Texture& tex = dev->get_texture(pass->colours[0].id); + assert(pass->colours[0].id != 0); + assert(pass->colours[0].fmt == tex.fmt); + w = tex.w; + h = tex.h; + assert(w && h); + } + for (i = 1; i < c; i++) { + Texture& tex = dev->get_texture(pass->colours[i].id); + assert(pass->colours[i].id != 0); + assert(pass->colours[i].fmt == tex.fmt); + assert(tex.w == w); + assert(tex.h == h); + } + if (pass->depth.id) { + Texture& d = dev->get_texture(pass->depth.id); + assert(d.fmt == pass->depth.fmt); + assert( + d.fmt == texture_format_d16 || + d.fmt == texture_format_d24s8 || + d.fmt == texture_format_d32 + ); + assert(d.w == w); + assert(d.h == h); + } } Render_Pass& Pipeline_Builder::build_rp() { + int i, c = pass->colour_count; +#define h(n, v) \ + n = fnv1a64_2(n, (uint8_t*)&v, sizeof v) validate_rp(); + h(pass->layout_hash, pass->colour_count); + h(pass->layout_hash, pass->depth.fmt); + h(pass->layout_hash, pass->depth.mode); + h(pass->resource_hash, pass->depth.id); + for (i = 0; i < c; i++) { + Render_Pass::Target& ct = pass->colours[i]; + h(pass->layout_hash, ct.fmt); + h(pass->layout_hash, ct.mode); + h(pass->resource_hash, ct.id); + } return *pass; } -void Pipeline_Builder::begin(Device* dev) { +void Pipeline_Builder::begin() { pip = (Pipeline*)arena_alloc(arena, sizeof *pip); zero(pip, sizeof *pip); if (dev) { @@ -79,6 +157,12 @@ void Pipeline_Builder::scissor( pip->scissor[3] = h; } +void Pipeline_Builder::depth(bool test, bool write, Depth_Mode mode) { + pip->depth_test = test; + pip->depth_write = write; + pip->depth_mode = mode; +} + void Pipeline_Builder::shader(Shader_Id s) { pip->shader = s; } @@ -122,6 +206,17 @@ Pipeline& Pipeline_Builder::build() { h(pip->pipeline_hash, pip->vertex_format); h(pip->pipeline_hash, pip->shader); h(pip->pipeline_hash, pip->descriptor_count); + h(pip->pipeline_hash, pip->viewport[0]); + h(pip->pipeline_hash, pip->viewport[1]); + h(pip->pipeline_hash, pip->viewport[2]); + h(pip->pipeline_hash, pip->viewport[3]); + h(pip->pipeline_hash, pip->scissor[0]); + h(pip->pipeline_hash, pip->scissor[1]); + h(pip->pipeline_hash, pip->scissor[2]); + h(pip->pipeline_hash, pip->scissor[3]); + h(pip->pipeline_hash, pip->depth_test); + h(pip->pipeline_hash, pip->depth_write); + h(pip->pipeline_hash, pip->depth_mode); { int i, e = pip->descriptor_count; pip->descriptor_resource_hash = fnv1a64(0, 0); @@ -10,8 +10,8 @@ todo list - [x] 3D maths library - [x] model conversion - [x] render a model - - [ ] depth buffer - - [ ] seperate framebuffers renderpasses + - [x] depth buffer + - [x] seperate framebuffers and renderpasses - [ ] VRAM allocator - [ ] don't re-bind pipelines and descriptors if not necessary - [ ] material system @@ -99,6 +99,7 @@ static Texture_Id create_atlas(Device* d, Arena* a) { d->unmap_buffer(buf); tex = d->create_texture( texture_format_r8i, + Texture_Flags::sampleable | Texture_Flags::copy_dst, font_w, 10, buf @@ -267,12 +268,12 @@ void UI::render(Arena* s, Texture_Id target) { } } - Pipeline_Builder pb(s); + Pipeline_Builder pb(s, device); pb.begin_rp(); pb.rp_target(device->get_backbuffer(), Clear_Mode::restore); Render_Pass& pass = pb.build_rp(); - pb.begin(device); + pb.begin(); pb.shader(shader); pb.vertex_format(vertex_format); pb.cbuffer(shader_info.config_binding, config_buf); diff --git a/vid_enums.h b/vid_enums.h index fca4f06..d96f57c 100644 --- a/vid_enums.h +++ b/vid_enums.h @@ -12,9 +12,14 @@ x(rgb16f) \ x(rgb32f) \ x(rgba8i) \ + x(rgba8i_srgb) \ + x(bgra8i_srgb) \ x(rgba16f) \ x(rgba32f) \ - x(bc1) + x(bc1) \ + x(d16) \ + x(d24s8) \ + x(d32) typedef enum { #define x(n) texture_format_ ## n, @@ -6,6 +6,7 @@ #define max_buffers 1024 #define max_vertex_formats 64 #define max_rpos 64 +#define max_fbos 64 #define max_pipelines 64 #define max_descriptor_sets 64 #define max_shaders 32 @@ -203,11 +204,42 @@ static VkFormat get_vk_format(Texture_Format fmt) { 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_rgba8i_srgb: return VK_FORMAT_R8G8B8A8_SRGB; + case texture_format_bgra8i_srgb: return VK_FORMAT_B8G8R8A8_SRGB; 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; + case texture_format_d16: return VK_FORMAT_D16_UNORM; + case texture_format_d24s8: return VK_FORMAT_D24_UNORM_S8_UINT; + case texture_format_d32: return VK_FORMAT_D32_SFLOAT; + case texture_format_count: break; } + assert(0); + return VK_FORMAT_UNDEFINED; +} + +static VkImageUsageFlags get_texture_usage(int flags) { + VkImageUsageFlags f = 0; + if (flags & Texture_Flags::sampleable) + f |= VK_IMAGE_USAGE_SAMPLED_BIT; + if (flags & Texture_Flags::colour_target) + f |= VK_IMAGE_USAGE_COLOR_ATTACHMENT_BIT; + if (flags & Texture_Flags::depth_stencil_target) + f |= VK_IMAGE_USAGE_DEPTH_STENCIL_ATTACHMENT_BIT; + if (flags & Texture_Flags::copy_src) + f |= VK_IMAGE_USAGE_TRANSFER_SRC_BIT; + if (flags & Texture_Flags::copy_dst) + f |= VK_IMAGE_USAGE_TRANSFER_DST_BIT; + return f; +} + +static VkImageAspectFlags get_image_aspect(int flags) { + VkImageUsageFlags f = 0; + if (flags & Texture_Flags::depth_stencil_target) + f |= VK_IMAGE_ASPECT_DEPTH_BIT | VK_IMAGE_ASPECT_STENCIL_BIT; + else + f |= VK_IMAGE_ASPECT_COLOR_BIT; + return f; } VkImageLayout state_to_image_layout(Resource_State s) { @@ -446,6 +478,7 @@ struct Shader_Vk : public Shader, public Late_Terminated { }; struct Renderpass_Vk; +struct Framebuffer_Vk; struct Context_Vk : public Context { int state; Device_Vk* dev; @@ -463,8 +496,10 @@ struct Context_Vk : public Context { void release(); void destroy(); - Renderpass_Vk& begin_rp(const Render_Pass& rp); - void end_rp(Renderpass_Vk& rpo); + std::pair<Renderpass_Vk&, Framebuffer_Vk&> begin_rp( + const Render_Pass& rp + ); + void end_rp(Renderpass_Vk& rpo, Framebuffer_Vk& fbo); }; struct Texture_Vk : public Texture, public Late_Terminated { @@ -520,7 +555,14 @@ struct Rpo_Key { bool operator==(const Rpo_Key& other) const { return is_first == other.is_first && - rpo == other.rpo; + rpo.layout_eq(other.rpo); + } +}; + +struct Fbo_Key { + Render_Pass rpo; + bool operator==(const Fbo_Key& other) const { + return rpo.resources_eq(other.rpo); } }; @@ -546,8 +588,6 @@ struct Dso_Key { struct Renderpass_Vk { VkRenderPass rpo; - VkFramebuffer fbo; - VkClearValue clear; int age; void on_submit() { @@ -556,8 +596,23 @@ struct Renderpass_Vk { VkAttachmentLoadOp load_op_from_mode(Clear_Mode m); - void init_rp(Device_Vk* dev, const Rpo_Key& rp); - void init_fb(Device_Vk* dev, const Render_Pass& rp); + void init(Device_Vk* dev, const Rpo_Key& rp); + void destroy(Device_Vk* dev); +}; + +struct Framebuffer_Vk { + VkFramebuffer fbo; + int age; + + void on_submit() { + age = 0; + } + + void init( + Device_Vk* dev, + const Renderpass_Vk& rpo, + const Render_Pass& rp + ); void destroy(Device_Vk* dev); }; @@ -626,6 +681,8 @@ struct Pipeline_Vk { const Pipeline& desc ); + static VkCompareOp get_compare_op(Depth_Mode m); + void on_submit() { age = 0; } @@ -673,10 +730,18 @@ struct Sampler_Vk : public Late_Terminated { template<> struct Hash_Function<Rpo_Key> { size_t operator()(const Rpo_Key& k) const { - return (size_t)fnv1a64_2(fnv1a64( - (uint8_t*)&k.rpo, - sizeof k.rpo - ), (uint8_t*)&k.is_first, 1); + return (size_t)fnv1a64_2( + k.rpo.layout_hash, + (uint8_t*)&k.is_first, + 1 + ); + } +}; + +template<> +struct Hash_Function<Fbo_Key> { + size_t operator()(const Fbo_Key& k) const { + return k.rpo.resource_hash; } }; @@ -736,13 +801,6 @@ struct Hash_Function<Sampler_Id> { } }; -template<> -struct std::hash<Render_Pass> { - size_t operator()(const Render_Pass& rp) const { - return (size_t)fnv1a64((uint8_t*)&rp, sizeof rp); - } -}; - struct Shader_Loader : public Asset_Loader { Device_Vk* dev; void init(Device_Vk* d); @@ -818,6 +876,7 @@ struct Device_Vk : public Device { uint32_t sampler_count; Hash_Map<Rpo_Key, Renderpass_Vk, max_rpos> rpo_cache; + Hash_Map<Fbo_Key, Framebuffer_Vk, max_fbos> fbo_cache; Hash_Map<Pso_Key, Pipeline_Vk, max_pipelines> pso_cache; Hash_Map<Dso_Key, Descriptor_Set_Vk, max_descriptor_sets> dso_cache; @@ -825,6 +884,8 @@ struct Device_Vk : public Device { uint32_t terminator_index; bool first_rp; + Texture_Id depth; + Texture_Id alloc_texture(); Buffer_Id alloc_buffer(); Vertex_Format_Id alloc_vf(); @@ -846,6 +907,14 @@ struct Device_Vk : public Device { Renderpass_Vk& create_rpo(const Rpo_Key& rp); Renderpass_Vk& get_rpo(const Rpo_Key& rp); + Framebuffer_Vk& create_fbo( + const Renderpass_Vk& rpo, + const Fbo_Key& fb + ); + Framebuffer_Vk& get_fbo( + const Renderpass_Vk& rpo, + const Fbo_Key& fb + ); Pipeline_Vk& create_pso(const Pso_Key& pip); Pipeline_Vk& get_pso(const Pso_Key& pop); Descriptor_Set_Vk& create_dso( @@ -860,6 +929,7 @@ struct Device_Vk : public Device { void collect_garbage(); void queue_destroy(Late_Terminated* obj); void create_terminators(); + void create_depth(int w, int h); int find_memory_type( uint32_t filter, @@ -1175,6 +1245,7 @@ void Device_Vk::init_internal() { samplers.init(); sampler_count = 1; rpo_cache.init(); + fbo_cache.init(); pso_cache.init(); dso_cache.init(); shader_loader.init(this); @@ -1198,6 +1269,8 @@ void Device_Vk::init_internal() { contexts[i].state = context_state_avail; swapchain.init(*app, this); create_terminators(); + depth = 0; + create_depth(app->w, app->h); } void Device_Vk::create_terminators() { @@ -1216,14 +1289,29 @@ void Device_Vk::create_terminators() { } } +void Device_Vk::create_depth(int w, int h) { + if (depth) + destroy_texture(depth); + depth = create_texture( + texture_format_d24s8, + Texture_Flags::sampleable | Texture_Flags::depth_stencil_target, + w, + h, + 0 + ); +} + void Device_Vk::deinit_internal() { int i, image_count = swapchain.image_count; vkDeviceWaitIdle(dev); + destroy_texture(depth); swapchain.destroy(this); deinit_swap_cap(this, &swap_cap); app_destroy_vk_surface(app, inst, surf); for (auto i : rpo_cache) i.second.destroy(this); + for (auto i : fbo_cache) + i.second.destroy(this); for (auto i : pso_cache) i.second.destroy(this); for (auto i : dso_cache) @@ -1255,20 +1343,13 @@ void Device_Vk::on_resize_internal(int w, int h) { get_swap_cap(this, phys_dev, surf, &swap_cap); swapchain.recreate(*app, this); create_terminators(); + create_depth(w, h); } Renderpass_Vk& Device_Vk::create_rpo(const Rpo_Key& k) { - VkClearValue clear{}; - auto& rp = k.rpo; - clear.color.float32[0] = (float)rp.clear.r / 255.0f; - clear.color.float32[1] = (float)rp.clear.g / 255.0f; - clear.color.float32[2] = (float)rp.clear.b / 255.0f; - clear.color.float32[3] = (float)rp.clear.a / 255.0f; Renderpass_Vk rpo; - rpo.init_rp(this, k); - rpo.init_fb(this, rp); + rpo.init(this, k); rpo.age = 0; - rpo.clear = clear; return rpo_cache.set(k, rpo); } @@ -1279,6 +1360,27 @@ Renderpass_Vk& Device_Vk::get_rpo(const Rpo_Key& rp) { return *rpo; } +Framebuffer_Vk& Device_Vk::create_fbo( + const Renderpass_Vk& rpo, + const Fbo_Key& k +) { + auto& fb = k.rpo; + Framebuffer_Vk fbo; + fbo.init(this, rpo, fb); + fbo.age = 0; + return fbo_cache.set(k, fbo); +} + +Framebuffer_Vk& Device_Vk::get_fbo( + const Renderpass_Vk& rpo, + const Fbo_Key& fb +) { + Framebuffer_Vk* fbo = fbo_cache.get(fb); + if (!fbo) + return create_fbo(rpo, fb); + return *fbo; +} + Pipeline_Vk& Device_Vk::create_pso(const Pso_Key& pip) { Pipeline_Vk pso; pso.age = 0; @@ -1313,11 +1415,6 @@ Descriptor_Set_Vk& Device_Vk::get_dso( return *dso; } -void Renderpass_Vk::destroy(Device_Vk* dev) { - vkDestroyRenderPass(dev->dev, rpo, &dev->ac); - vkDestroyFramebuffer(dev->dev, fbo, &dev->ac); -} - void Device_Vk::collect_garbage() { int max_age = swapchain.image_count + 3; for (const auto& i: rpo_cache) { @@ -1328,6 +1425,14 @@ void Device_Vk::collect_garbage() { rpo_cache.remove(i.first); } } + for (const auto& i: fbo_cache) { + auto& fb = i.second; + fb.age++; + if (fb.age > max_age) { + fb.destroy(this); + fbo_cache.remove(i.first); + } + } for (const auto& i: pso_cache) { auto& pip = i.second; pip.age++; @@ -1377,39 +1482,63 @@ VkAttachmentLoadOp Renderpass_Vk::load_op_from_mode( return VK_ATTACHMENT_LOAD_OP_DONT_CARE; } -void Renderpass_Vk::init_rp( +void Renderpass_Vk::init( Device_Vk* dev, const Rpo_Key& rpk ) { VkRenderPassCreateInfo ri{}; - VkAttachmentDescription ad{}; - VkAttachmentReference ar{}; + VkAttachmentDescription ads[2]; + VkAttachmentReference car, dar; VkSubpassDescription sd{}; VkResult r; auto& rp = rpk.rpo; - - ad.format = dev->swapchain.format.format; - ad.samples = VK_SAMPLE_COUNT_1_BIT; - ad.loadOp = load_op_from_mode(rp.mode); - ad.storeOp = VK_ATTACHMENT_STORE_OP_STORE; - ad.stencilLoadOp = VK_ATTACHMENT_LOAD_OP_DONT_CARE; - ad.stencilStoreOp = VK_ATTACHMENT_STORE_OP_DONT_CARE; - if (rpk.is_first) - ad.initialLayout = VK_IMAGE_LAYOUT_PRESENT_SRC_KHR; - else - ad.initialLayout = VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL; - ad.finalLayout = VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL; - - ar.attachment = 0; - ar.layout = VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL; + bool has_depth = rp.depth.id; + int count = 0, i, c = rp.colour_count; + zero(ads, sizeof ads); + for (i = 0; i < c; i++) { + int index = count++; + auto& colour = rp.colours[i]; + auto& ad = ads[index]; + ad.format = get_vk_format(colour.fmt); + ad.samples = VK_SAMPLE_COUNT_1_BIT; + ad.loadOp = load_op_from_mode(colour.mode); + ad.storeOp = VK_ATTACHMENT_STORE_OP_STORE; + ad.stencilLoadOp = VK_ATTACHMENT_LOAD_OP_DONT_CARE; + ad.stencilStoreOp = VK_ATTACHMENT_STORE_OP_DONT_CARE; + if (rpk.is_first) + ad.initialLayout = VK_IMAGE_LAYOUT_PRESENT_SRC_KHR; + else + ad.initialLayout = VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL; + ad.finalLayout = VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL; + car.attachment = index; + car.layout = VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL; + } + if (has_depth) { + int i = count++; + auto& ad = ads[i]; + ad.format = get_vk_format(dev->get_texture(rp.depth.id).fmt); + ad.samples = VK_SAMPLE_COUNT_1_BIT; + ad.loadOp = load_op_from_mode(rp.depth.mode); + ad.storeOp = VK_ATTACHMENT_STORE_OP_STORE; + ad.stencilLoadOp = ad.loadOp; + ad.stencilStoreOp = VK_ATTACHMENT_STORE_OP_STORE; + if (rpk.is_first) + ad.initialLayout = VK_IMAGE_LAYOUT_UNDEFINED; + else + ad.initialLayout = VK_IMAGE_LAYOUT_UNDEFINED; + ad.finalLayout = VK_IMAGE_LAYOUT_DEPTH_STENCIL_ATTACHMENT_OPTIMAL; + dar.attachment = i; + dar.layout = VK_IMAGE_LAYOUT_DEPTH_STENCIL_ATTACHMENT_OPTIMAL; + } sd.pipelineBindPoint = VK_PIPELINE_BIND_POINT_GRAPHICS; sd.colorAttachmentCount = 1; - sd.pColorAttachments = &ar; + sd.pColorAttachments = &car; + sd.pDepthStencilAttachment = has_depth? &dar: 0; ri.sType = VK_STRUCTURE_TYPE_RENDER_PASS_CREATE_INFO; - ri.attachmentCount = 1; - ri.pAttachments = &ad; + ri.attachmentCount = count; + ri.pAttachments = ads; ri.subpassCount = 1; ri.pSubpasses = &sd; @@ -1420,21 +1549,42 @@ void Renderpass_Vk::init_rp( } } -void Renderpass_Vk::init_fb( +void Renderpass_Vk::destroy(Device_Vk* dev) { + vkDestroyRenderPass(dev->dev, rpo, &dev->ac); +} + +void Framebuffer_Vk::init( Device_Vk* dev, + const Renderpass_Vk& rpo, const Render_Pass& rp ) { - const Texture_Vk& texture = - *(const Texture_Vk*)&dev->get_texture(rp.target); + bool has_depth = rp.depth.id; + int i, count = 0, w, h; + VkImageView atts[2]; VkResult r; VkFramebufferCreateInfo fbi{}; + for (i = 0; i < rp.colour_count; i++) { + const auto& tar = rp.colours[i]; + const Texture_Vk& texture = + *(const Texture_Vk*)&dev->get_texture(tar.id); + atts[count++] = texture.view; + w = texture.w; + h = texture.h; + } + if (has_depth) { + const Texture_Vk& texture = + *(const Texture_Vk*)&dev->get_texture(rp.depth.id); + atts[count++] = texture.view; + w = texture.w; + h = texture.h; + } fbi.sType = VK_STRUCTURE_TYPE_FRAMEBUFFER_CREATE_INFO; - fbi.renderPass = rpo; - fbi.width = texture.w; - fbi.height = texture.h; + fbi.renderPass = rpo.rpo; + fbi.width = w; + fbi.height = h; fbi.layers = 1; - fbi.attachmentCount = 1; - fbi.pAttachments = &texture.view; + fbi.attachmentCount = count; + fbi.pAttachments = atts; r = vkCreateFramebuffer(dev->dev, &fbi, &dev->ac, &fbo); if (r != VK_SUCCESS) { print_err("Failed to create a framebuffer.\n"); @@ -1442,6 +1592,10 @@ void Renderpass_Vk::init_fb( } } +void Framebuffer_Vk::destroy(Device_Vk* dev) { + vkDestroyFramebuffer(dev->dev, fbo, &dev->ac); +} + static int get_image_count(const Swap_Cap& s) { const VkSurfaceCapabilitiesKHR& cap = s.cap; return cap.minImageCount + (cap.minImageCount < cap.maxImageCount); @@ -1479,7 +1633,7 @@ static VkImageView make_view( Device_Vk* dev, VkImage image, VkFormat fmt, - VkImageAspectFlagBits flags + VkImageAspectFlags flags ) { VkImageViewCreateInfo vi{}; VkResult r; @@ -1568,6 +1722,7 @@ Texture_Id Swapchain::create_image( tex.view = view; tex.w = w; tex.h = h; + tex.fmt = texture_format_bgra8i_srgb; tex.alias = true; return id; } @@ -1716,6 +1871,10 @@ Texture_Id Device::get_backbuffer() { return ((Device_Vk*)this)->backbuffer_id; } +Texture_Id Device::get_depth_target() { + return ((Device_Vk*)this)->depth; +} + Texture& Device::get_texture(Texture_Id id) { return ((Device_Vk*)this)->textures[id]; } @@ -1802,7 +1961,7 @@ void Context::submit( Descriptor_Set_Vk* dso = 0; if (p.descriptor_count) dso = &dev->get_dso(pso, *(Dso_Key*)&p); - auto& rpo = ctx->begin_rp(rp); + auto [rpo, fbo] = ctx->begin_rp(rp); Texture_Vk& target = *(Texture_Vk*)&dev->get_texture( dev->get_backbuffer() ); @@ -1855,7 +2014,7 @@ void Context::submit( draw.first_instance ); } - ctx->end_rp(rpo); + ctx->end_rp(rpo, fbo); pso.on_submit(); if (dso) dso->on_submit(); @@ -2003,33 +2162,56 @@ void Context::transition(Texture_Id id, Resource_State state) { ); } -Renderpass_Vk& Context_Vk::begin_rp(const Render_Pass& rp) { +std::pair<Renderpass_Vk&, Framebuffer_Vk&> Context_Vk::begin_rp( + const Render_Pass& rp +) { Renderpass_Vk& rpo = dev->get_rpo({ dev->first_rp, rp}); + Framebuffer_Vk& fbo = dev->get_fbo(rpo, { rp }); VkRenderPassBeginInfo rpbi{}; + VkClearValue clears[max_colour_attachments + 1]; + int i, c = rp.colour_count, clear_count = 0; + bool has_depth = rp.depth.id; + for (i = 0; i < c; i++) { + VkClearValue clear{}; + const auto& tar = rp.colours[i]; + const auto col = tar.clear.colour; + clear.color.float32[0] = (float)col.r / 255.0f; + clear.color.float32[1] = (float)col.g / 255.0f; + clear.color.float32[2] = (float)col.b / 255.0f; + clear.color.float32[3] = (float)col.a / 255.0f; + clears[clear_count++] = clear; + } + if (has_depth) { + VkClearValue dc{}; + dc.depthStencil.depth = rp.depth.clear.depth; + dc.depthStencil.stencil = 0; /* todo */ + clears[clear_count++] = dc; + } rpbi.sType = VK_STRUCTURE_TYPE_RENDER_PASS_BEGIN_INFO; rpbi.renderPass = rpo.rpo; - rpbi.framebuffer = rpo.fbo; + rpbi.framebuffer = fbo.fbo; rpbi.renderArea.extent = dev->swapchain.size; - rpbi.clearValueCount = rp.mode == Clear_Mode::clear? 1: 0; - rpbi.pClearValues = &rpo.clear; + rpbi.clearValueCount = clear_count; + rpbi.pClearValues = clears; vkCmdBeginRenderPass( cb, &rpbi, VK_SUBPASS_CONTENTS_INLINE ); dev->first_rp = false; - return rpo; + return { rpo, fbo }; } -void Context_Vk::end_rp(Renderpass_Vk& rpo) { +void Context_Vk::end_rp(Renderpass_Vk& rpo, Framebuffer_Vk& fbo) { vkCmdEndRenderPass(cb); rpo.on_submit(); + fbo.on_submit(); } void Context::submit(const Render_Pass& rp) { Context_Vk* ctx = (Context_Vk*)this; - auto& rpo = ctx->begin_rp(rp); - ctx->end_rp(rpo); + auto [rpo, fbo] = ctx->begin_rp(rp); + ctx->end_rp(rpo, fbo); } void Context_Vk::init_pool() { @@ -2305,9 +2487,9 @@ void Pipeline_Vk::init_depthstencil( (void)desc; zero(&ds, sizeof ds); ds.sType = VK_STRUCTURE_TYPE_PIPELINE_DEPTH_STENCIL_STATE_CREATE_INFO; - ds.depthTestEnable = VK_FALSE; - ds.depthWriteEnable = VK_FALSE; - ds.depthCompareOp = VK_COMPARE_OP_LESS; + ds.depthTestEnable = desc.depth_test; + ds.depthWriteEnable = desc.depth_write; + ds.depthCompareOp = get_compare_op(desc.depth_mode); ds.depthBoundsTestEnable = VK_FALSE; ds.stencilTestEnable = VK_FALSE; info.pDepthStencilState = &ds; @@ -2403,6 +2585,20 @@ void Pipeline_Vk::init_descriptors( } } +VkCompareOp Pipeline_Vk::get_compare_op(Depth_Mode m) { + switch (m) { + case Depth_Mode::less: return VK_COMPARE_OP_LESS; + case Depth_Mode::less_equal: return VK_COMPARE_OP_LESS_OR_EQUAL; + case Depth_Mode::equal: return VK_COMPARE_OP_EQUAL; + case Depth_Mode::greater: return VK_COMPARE_OP_GREATER; + case Depth_Mode::greater_equal: return VK_COMPARE_OP_GREATER_OR_EQUAL; + case Depth_Mode::always: return VK_COMPARE_OP_ALWAYS; + case Depth_Mode::never: return VK_COMPARE_OP_NEVER; + } + assert(0); + return VK_COMPARE_OP_LESS; +} + void Pipeline_Vk::init_layout( Device_Vk* dev, const Pipeline& desc @@ -2903,6 +3099,7 @@ Buffer& Device::get_buffer(Buffer_Id id) { Texture_Id Device::create_texture( Texture_Format fmt, + int flags, int w, int h, Buffer_Id init @@ -2913,6 +3110,7 @@ Texture_Id Device::create_texture( Texture_Id id = dev->alloc_texture(); Texture_Vk& tex = *(Texture_Vk*)&dev->get_texture(id); VkMemoryRequirements req; + VkImageAspectFlags aspect = get_image_aspect(flags); tex.state = Resource_State::undefined; ii.sType = VK_STRUCTURE_TYPE_IMAGE_CREATE_INFO; ii.imageType = VK_IMAGE_TYPE_2D; @@ -2924,7 +3122,7 @@ Texture_Id Device::create_texture( 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.usage = get_texture_usage(flags); ii.sharingMode = VK_SHARING_MODE_EXCLUSIVE; ii.samples = VK_SAMPLE_COUNT_1_BIT; r = vkCreateImage(dev->dev, &ii, &dev->ac, &tex.image); @@ -2957,12 +3155,13 @@ Texture_Id Device::create_texture( vkBindImageMemory(dev->dev, tex.image, tex.memory, 0); tex.w = w; tex.h = h; + tex.fmt = fmt; tex.alias = false; tex.view = make_view( dev, tex.image, ii.format, - VK_IMAGE_ASPECT_COLOR_BIT + aspect ); if (init) { Context& ctx = dev->acquire(); @@ -3170,7 +3369,13 @@ Asset* Texture_Loader::load(Arena* a, Arena* s, Pack_File* f) { pack_read(f, data, size); { Buffer_Id buf = upload(data, size); - Texture_Id tex = dev->create_texture(fmt, w, h, buf); + Texture_Id tex = dev->create_texture( + fmt, + Texture_Flags::sampleable | Texture_Flags::copy_dst, + w, + h, + buf + ); dev->destroy_buffer(buf); return &dev->get_texture(tex); } @@ -69,11 +69,23 @@ static_assert(sizeof(Constant_Buffer_Descriptor) <= descriptor_payload_size); #define pipeline_max_descriptors 16 +enum class Depth_Mode { + less, + less_equal, + equal, + greater, + greater_equal, + always, + never +}; + struct Pipeline { uint64_t pipeline_hash; uint64_t descriptor_resource_hash; int viewport[4]; int scissor[4]; + bool depth_test, depth_write; + Depth_Mode depth_mode; Vertex_Format_Id vertex_format; Shader_Id shader; Descriptor descriptors[pipeline_max_descriptors]; @@ -90,7 +102,10 @@ struct Pipeline { scissor[0] == other.scissor[0] && scissor[1] == other.scissor[1] && scissor[2] == other.scissor[2] && - scissor[3] == other.scissor[3]; + scissor[3] == other.scissor[3] && + depth_test == other.depth_test && + depth_write == other.depth_write && + depth_mode == other.depth_mode; } bool desc_layout_eq(const Pipeline& other) const { @@ -148,17 +163,48 @@ struct Colour { uint8_t r, g, b, a; }; +#define max_colour_attachments 8 + struct Render_Pass { - Texture_Id target; - Colour clear; - Clear_Mode mode; - - bool operator==(const Render_Pass& other) const { - int size = sizeof *this, i; - uint8_t* bba = (uint8_t*)this; - uint8_t* bbb = (uint8_t*)&other; - for (i = 0; i < size; i++, bba++, bbb++) - if (*bba != *bbb) return false; + struct Target { + Texture_Id id; + Texture_Format fmt; + Clear_Mode mode; + union { + Colour colour; + float depth; + } clear; + }; + Target colours[max_colour_attachments]; + int colour_count; + Target depth; + uint64_t layout_hash; + uint64_t resource_hash; + + bool layout_eq(const Render_Pass& other) const { + int i, c = colour_count; + if (layout_hash != other.layout_hash) return false; + if (c != other.colour_count) return false; + if (depth.fmt != other.depth.fmt) return false; + if (depth.mode != other.depth.mode) return false; + for (i = 0; i < c; i++) { + const Target& ac = colours[i]; + const Target& bc = other.colours[i]; + if (ac.fmt != bc.fmt) return false; + if (ac.mode != bc.mode) return false; + } + return true; + } + + bool resources_eq(const Render_Pass& other) const { + int i, c = colour_count; + if (resource_hash != other.resource_hash) return false; + if (!layout_eq(other)) return false; + for (i = 0; i < c; i++) { + const Target& ac = colours[i]; + const Target& bc = other.colours[i]; + if (ac.id != bc.id) return false; + } return true; } }; @@ -189,18 +235,22 @@ struct Pipeline_Builder { Arena* arena; Render_Pass* pass; Pipeline* pip; + Device* dev; - Pipeline_Builder(Arena* arena); + Pipeline_Builder(Arena* arena, Device* dev); void begin_rp(); void rp_target(Texture_Id id, Colour clear_colour); void rp_target(Texture_Id id, Clear_Mode mode); + void rp_depth_target(Texture_Id id, float clear_depth); + void rp_depth_target(Texture_Id id, Clear_Mode mode); Render_Pass& build_rp(); void validate_rp(); - void begin(Device* dev); + void begin(); void viewport(float x, float y, float w, float h); void scissor(float x, float y, float w, float h); + void depth(bool test, bool write, Depth_Mode mode); void shader(Shader_Id s); void vertex_format(Vertex_Format_Id vf); void texture(int binding, Texture_Id t, Sampler_Id s); @@ -220,6 +270,7 @@ enum Resource_State { struct Texture : public Asset { Texture_Id id; + Texture_Format fmt; int w, h; bool alias; }; @@ -237,6 +288,16 @@ namespace Buffer_Flags { }; }; +namespace Texture_Flags { + enum { + sampleable = 1 << 0, + colour_target = 1 << 1, + depth_stencil_target = 1 << 2, + copy_src = 1 << 3, + copy_dst = 1 << 4 + }; +}; + struct Buffer { Buffer_Id id; }; @@ -305,11 +366,13 @@ struct Device { Texture_Id create_texture( Texture_Format fmt, + int flags, int w, int h, Buffer_Id init ); Texture_Id get_backbuffer(); + Texture_Id get_depth_target(); Texture& get_texture(Texture_Id id); void destroy_texture(Texture_Id id); |