From edc84213fadc9bbd30030a57600550bde562b24e Mon Sep 17 00:00:00 2001 From: quou Date: Fri, 27 Dec 2024 11:33:28 +1100 Subject: allow renderpasses to specify a clear mode and pipelines to specify their viewport and scissor --- c2.cpp | 2 +- pipeline.cpp | 45 +++++++++++++++- video.cpp | 168 +++++++++++++++++++++++++++++++++++++++++------------------ video.hpp | 33 +++++++++++- 4 files changed, 194 insertions(+), 54 deletions(-) diff --git a/c2.cpp b/c2.cpp index 052ebf0..3ea05b9 100644 --- a/c2.cpp +++ b/c2.cpp @@ -127,7 +127,7 @@ int main() { pb.rp_target(dev->get_backbuffer(), { r, 0x00, 0xff, 0xff }); Render_Pass& pass = pb.build_rp(); - pb.begin(); + pb.begin(dev); pb.shader(shader->id); pb.vertex_format(shader->vf); pb.texture( diff --git a/pipeline.cpp b/pipeline.cpp index 89f606d..b0a88eb 100644 --- a/pipeline.cpp +++ b/pipeline.cpp @@ -20,6 +20,13 @@ void Pipeline_Builder::begin_rp() { void Pipeline_Builder::rp_target(Texture_Id id, Colour clear) { pass->target = id; pass->clear = clear; + pass->mode = Clear_Mode::clear; +} + +void Pipeline_Builder::rp_target(Texture_Id id, Clear_Mode clear) { + pass->target = id; + assert(clear != Clear_Mode::clear); + pass->mode = clear; } void Pipeline_Builder::validate_rp() { @@ -31,9 +38,45 @@ Render_Pass& Pipeline_Builder::build_rp() { return *pass; } -void Pipeline_Builder::begin() { +void Pipeline_Builder::begin(Device* dev) { pip = (Pipeline*)arena_alloc(arena, sizeof *pip); memset(pip, 0, sizeof *pip); + if (dev) { + Texture_Id backbuffer_id = dev->get_backbuffer(); + Texture& backbuffer = dev->get_texture(backbuffer_id); + pip->scissor[0] = 0; + pip->scissor[1] = 0; + pip->scissor[2] = backbuffer.w; + pip->scissor[3] = backbuffer.h; + pip->viewport[0] = 0; + pip->viewport[1] = 0; + pip->viewport[2] = backbuffer.w; + pip->viewport[3] = backbuffer.h; + } +} + +void Pipeline_Builder::viewport( + float x, + float y, + float w, + float h +) { + pip->viewport[0] = x; + pip->viewport[1] = y; + pip->viewport[2] = w; + pip->viewport[3] = h; +} + +void Pipeline_Builder::scissor( + float x, + float y, + float w, + float h +) { + pip->scissor[0] = x; + pip->scissor[1] = y; + pip->scissor[2] = w; + pip->scissor[3] = h; } void Pipeline_Builder::shader(Shader_Id s) { diff --git a/video.cpp b/video.cpp index 7c88b54..6dc0ef7 100644 --- a/video.cpp +++ b/video.cpp @@ -200,12 +200,15 @@ static VkFormat get_vk_format(Texture_Format fmt) { 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; + 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; + case render_target: return VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL; + case presentable: return VK_IMAGE_LAYOUT_PRESENT_SRC_KHR; } + assert(0); + return VK_IMAGE_LAYOUT_UNDEFINED; } static void* vk_alloc( @@ -501,28 +504,25 @@ struct Buffer_Vk : public Buffer, public Late_Terminated { } }; -struct Renderpass_Vk { - VkRenderPass rpo; - VkFramebuffer fbo; - VkClearValue clear; - int age; +struct Rpo_Key { + bool is_first; + Render_Pass rpo; - void on_submit() { - age = 0; + bool operator==(const Rpo_Key& other) const { + return + is_first == other.is_first && + rpo == other.rpo; } - - void init_rp(Device_Vk* dev, const Render_Pass& rp); - void init_fb(Device_Vk* dev, const Render_Pass& rp); - void destroy(Device_Vk* dev); }; struct Pso_Key { Pipeline pso; - Render_Pass rpo; + Rpo_Key rpo; bool operator==(const Pso_Key& other) const { return rpo == other.rpo && + pso.pipeline_eq(other.pso) && pso.desc_layout_eq(other.pso); } }; @@ -535,6 +535,23 @@ struct Dso_Key { } }; +struct Renderpass_Vk { + VkRenderPass rpo; + VkFramebuffer fbo; + VkClearValue clear; + int age; + + void on_submit() { + age = 0; + } + + 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 destroy(Device_Vk* dev); +}; + struct Pipeline_Vk { VkPipeline pip; VkPipelineLayout lay; @@ -566,7 +583,7 @@ struct Pipeline_Vk { Arena& scope, Device_Vk* dev, VkGraphicsPipelineCreateInfo& info, - const Render_Pass& desc + const Pipeline& desc ); void init_rasterisation( Arena& scope, @@ -646,10 +663,12 @@ struct Sampler_Vk : public Late_Terminated { }; template<> -struct Hash_Function -{ - size_t operator()(const Render_Pass& rp) const { - return (size_t)fnv1a64((uint8_t*)&rp, sizeof rp); +struct Hash_Function { + 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); } }; @@ -794,12 +813,13 @@ struct Device_Vk : public Device { uint32_t shader_count; uint32_t sampler_count; - Hash_Map rpo_cache; + Hash_Map rpo_cache; Hash_Map pso_cache; Hash_Map dso_cache; Terminator* terminators; uint32_t terminator_index; + bool first_rp; Texture_Id alloc_texture(); Buffer_Id alloc_buffer(); @@ -820,8 +840,8 @@ struct Device_Vk : public Device { void create_surf(); void on_resize_internal(int w, int h); - Renderpass_Vk& create_rpo(const Render_Pass& rp); - Renderpass_Vk& get_rpo(const Render_Pass& rp); + Renderpass_Vk& create_rpo(const Rpo_Key& rp); + Renderpass_Vk& get_rpo(const Rpo_Key& rp); Pipeline_Vk& create_pso(const Pso_Key& pip); Pipeline_Vk& get_pso(const Pso_Key& pop); Descriptor_Set_Vk& create_dso( @@ -1168,12 +1188,12 @@ void Device_Vk::init_internal() { create_dev(&swap_cap); 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; + swapchain.init(*app, this); + create_terminators(); } void Device_Vk::create_terminators() { @@ -1233,21 +1253,22 @@ void Device_Vk::on_resize_internal(int w, int h) { create_terminators(); } -Renderpass_Vk& Device_Vk::create_rpo(const Render_Pass& rp) { +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, rp); + rpo.init_rp(this, k); rpo.init_fb(this, rp); rpo.age = 0; rpo.clear = clear; - return rpo_cache.set(rp, rpo); + return rpo_cache.set(k, rpo); } -Renderpass_Vk& Device_Vk::get_rpo(const Render_Pass& rp) { +Renderpass_Vk& Device_Vk::get_rpo(const Rpo_Key& rp) { Renderpass_Vk* rpo = rpo_cache.get(rp); if (!rpo) return create_rpo(rp); @@ -1340,25 +1361,40 @@ int Device_Vk::find_memory_type( return -1; } +VkAttachmentLoadOp Renderpass_Vk::load_op_from_mode( + Clear_Mode m +) { + switch (m) { + case Clear_Mode::discard: return VK_ATTACHMENT_LOAD_OP_DONT_CARE; + case Clear_Mode::clear: return VK_ATTACHMENT_LOAD_OP_CLEAR; + case Clear_Mode::restore: return VK_ATTACHMENT_LOAD_OP_LOAD; + } + assert(0); + return VK_ATTACHMENT_LOAD_OP_DONT_CARE; +} + void Renderpass_Vk::init_rp( Device_Vk* dev, - const Render_Pass& rp + const Rpo_Key& rpk ) { VkRenderPassCreateInfo ri{}; VkAttachmentDescription ad{}; VkAttachmentReference ar{}; VkSubpassDescription sd{}; VkResult r; - (void)rp; + auto& rp = rpk.rpo; ad.format = dev->swapchain.format.format; ad.samples = VK_SAMPLE_COUNT_1_BIT; - ad.loadOp = VK_ATTACHMENT_LOAD_OP_CLEAR; + 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; - ad.initialLayout = VK_IMAGE_LAYOUT_UNDEFINED; - ad.finalLayout = VK_IMAGE_LAYOUT_PRESENT_SRC_KHR; + 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; @@ -1527,6 +1563,7 @@ void Swapchain::get_images(Device_Vk* dev) { int i; VkImage* images; vkGetSwapchainImagesKHR(dev->dev, swapchain, &count, 0); + Context& ctx = dev->acquire(); image_count = count; images = (VkImage*)heap_alloc( dev->heap, @@ -1550,7 +1587,11 @@ void Swapchain::get_images(Device_Vk* dev) { size.width, size.height ); + /* needs to be presentable since the first renderpass + * will expect it to be presentable from "last" frame */ + ctx.transition(textures[i], Resource_State::presentable); } + dev->submit(ctx); heap_free(dev->heap, images); } @@ -1604,6 +1645,7 @@ void Device::begin_frame() { &dev->backbuffer_index ); dev->backbuffer_id = dev->swapchain.textures[dev->backbuffer_index]; + dev->first_rp = true; } void Device::submit(Context& ctx_) { @@ -1631,6 +1673,10 @@ void Device::present() { VkSubmitInfo si{}; VkPipelineStageFlags stage = VK_PIPELINE_STAGE_COLOR_ATTACHMENT_OUTPUT_BIT; + ctx->transition( + dev->get_backbuffer(), + Resource_State::presentable + ); si.sType = VK_STRUCTURE_TYPE_SUBMIT_INFO; si.waitSemaphoreCount = 1; si.pWaitSemaphores = &ctx->semaphore; @@ -1735,10 +1781,15 @@ void Context::submit( Context_Vk* ctx = (Context_Vk*)this; Device_Vk* dev = ctx->dev; Vertex_Buffer_Binding* binding; - Pso_Key pso_key = { p, rp }; + Rpo_Key rpo_key = { dev->first_rp, rp }; + Pso_Key pso_key = { p, rpo_key }; Pipeline_Vk& pso = dev->get_pso(pso_key); Descriptor_Set_Vk& dso = dev->get_dso(pso, *(Dso_Key*)&p); auto& rpo = ctx->begin_rp(rp); + Texture_Vk& target = *(Texture_Vk*)&dev->get_texture( + dev->get_backbuffer() + ); + target.state = Resource_State::render_target; vkCmdBindPipeline( ctx->cb, VK_PIPELINE_BIND_POINT_GRAPHICS, @@ -1835,6 +1886,7 @@ void Context::transition(Texture_Id id, Resource_State state) { VkImageLayout src_layout = state_to_image_layout(tex.state); VkImageLayout dst_layout = state_to_image_layout(state); VkPipelineStageFlags src_stage, dst_stage; + tex.state = state; b.sType = VK_STRUCTURE_TYPE_IMAGE_MEMORY_BARRIER; b.oldLayout = src_layout; b.newLayout = dst_layout; @@ -1878,6 +1930,22 @@ void Context::transition(Texture_Id id, Resource_State state) { b.dstAccessMask = VK_ACCESS_SHADER_READ_BIT; src_stage = VK_PIPELINE_STAGE_FRAGMENT_SHADER_BIT; dst_stage = VK_PIPELINE_STAGE_FRAGMENT_SHADER_BIT; + } else if ( + src_layout == VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL && + dst_layout == VK_IMAGE_LAYOUT_PRESENT_SRC_KHR + ) { + b.srcAccessMask = VK_ACCESS_COLOR_ATTACHMENT_WRITE_BIT; + b.dstAccessMask = 0; + src_stage = VK_PIPELINE_STAGE_COLOR_ATTACHMENT_OUTPUT_BIT; + dst_stage = VK_PIPELINE_STAGE_BOTTOM_OF_PIPE_BIT; + } else if ( + src_layout == VK_IMAGE_LAYOUT_UNDEFINED && + dst_layout == VK_IMAGE_LAYOUT_PRESENT_SRC_KHR + ) { + b.srcAccessMask = 0; + b.dstAccessMask = 0; + src_stage = VK_PIPELINE_STAGE_TOP_OF_PIPE_BIT; + dst_stage = VK_PIPELINE_STAGE_BOTTOM_OF_PIPE_BIT; } else { print_err("Bad resource transition.\n"); pbreak(389); @@ -1897,19 +1965,20 @@ void Context::transition(Texture_Id id, Resource_State state) { } Renderpass_Vk& Context_Vk::begin_rp(const Render_Pass& rp) { - Renderpass_Vk& rpo = dev->get_rpo(rp); + Renderpass_Vk& rpo = dev->get_rpo({ dev->first_rp, rp}); VkRenderPassBeginInfo rpbi{}; rpbi.sType = VK_STRUCTURE_TYPE_RENDER_PASS_BEGIN_INFO; rpbi.renderPass = rpo.rpo; rpbi.framebuffer = rpo.fbo; rpbi.renderArea.extent = dev->swapchain.size; - rpbi.clearValueCount = 1; + rpbi.clearValueCount = rp.mode == Clear_Mode::clear? 1: 0; rpbi.pClearValues = &rpo.clear; vkCmdBeginRenderPass( cb, &rpbi, VK_SUBPASS_CONTENTS_INLINE ); + dev->first_rp = false; return rpo; } @@ -2102,9 +2171,8 @@ void Pipeline_Vk::init_viewport( Arena& scope, Device_Vk* dev, VkGraphicsPipelineCreateInfo& info, - const Render_Pass& desc + const Pipeline& desc ) { - Texture& texture = dev->get_texture(desc.target); VkPipelineViewportStateCreateInfo& vi = *(VkPipelineViewportStateCreateInfo*)arena_alloc( &scope, @@ -2121,14 +2189,14 @@ void Pipeline_Vk::init_viewport( memset(&vi, 0, sizeof vi); memset(&scissor, 0, sizeof scissor); memset(&viewport, 0, sizeof viewport); - scissor.offset.x = 0; - scissor.offset.y = 0; - scissor.extent.width = texture.w; - scissor.extent.height = texture.h; - viewport.x = 0; - viewport.y = 0; - viewport.width = texture.w; - viewport.height = texture.h; + scissor.offset.x = desc.scissor[0]; + scissor.offset.y = desc.scissor[1]; + scissor.extent.width = desc.scissor[2]; + scissor.extent.height = desc.scissor[3]; + viewport.x = desc.viewport[0]; + viewport.y = desc.viewport[1]; + viewport.width = desc.viewport[2]; + viewport.height = desc.viewport[3]; viewport.minDepth = 0.0f; viewport.maxDepth = 1.0f; vi.sType = VK_STRUCTURE_TYPE_PIPELINE_VIEWPORT_STATE_CREATE_INFO; @@ -2335,7 +2403,7 @@ void Pipeline_Vk::init(Device_Vk* dev, const Pso_Key& key) { init_stages(scope, dev, info, desc); init_vertex_input(scope, dev, info, desc); init_input_assembly(scope, dev, info, desc); - init_viewport(scope, dev, info, key.rpo); + init_viewport(scope, dev, info, desc); init_rasterisation(scope, dev, info, desc); init_msaa(scope, dev, info, desc); init_depthstencil(scope, dev, info, desc); diff --git a/video.hpp b/video.hpp index 7639b50..0363a9d 100644 --- a/video.hpp +++ b/video.hpp @@ -72,11 +72,27 @@ static_assert(sizeof(Constant_Buffer_Descriptor) <= descriptor_payload_size); struct Pipeline { uint64_t pipeline_hash; uint64_t descriptor_resource_hash; + int viewport[4]; + int scissor[4]; Vertex_Format_Id vertex_format; Shader_Id shader; Descriptor descriptors[pipeline_max_descriptors]; int descriptor_count; + bool pipeline_eq(const Pipeline& other) const { + return + shader == other.shader && + vertex_format == other.vertex_format && + viewport[0] == other.viewport[0] && + viewport[1] == other.viewport[1] && + viewport[2] == other.viewport[2] && + viewport[3] == other.viewport[3] && + scissor[0] == other.scissor[0] && + scissor[1] == other.scissor[1] && + scissor[2] == other.scissor[2] && + scissor[3] == other.scissor[3]; + } + bool desc_layout_eq(const Pipeline& other) const { int i, c = descriptor_count; if (other.pipeline_hash != pipeline_hash) @@ -122,6 +138,12 @@ struct Pipeline { } }; +enum class Clear_Mode { + discard, + clear, + restore +}; + struct Colour { uint8_t r, g, b, a; }; @@ -129,6 +151,7 @@ struct Colour { struct Render_Pass { Texture_Id target; Colour clear; + Clear_Mode mode; bool operator==(const Render_Pass& other) const { int size = sizeof *this, i; @@ -154,6 +177,7 @@ struct Draw { int first_instance; }; +struct Device; struct Pipeline_Builder { Arena* arena; Render_Pass* pass; @@ -163,10 +187,13 @@ struct Pipeline_Builder { void begin_rp(); void rp_target(Texture_Id id, Colour clear_colour); + void rp_target(Texture_Id id, Clear_Mode mode); Render_Pass& build_rp(); void validate_rp(); - void begin(); + void begin(Device* dev); + void viewport(float x, float y, float w, float h); + void scissor(float x, float y, float w, float h); void shader(Shader_Id s); void vertex_format(Vertex_Format_Id vf); void texture(int binding, Texture_Id t, Sampler_Id s); @@ -179,7 +206,9 @@ enum Resource_State { undefined, copy_dst, copy_src, - shader_read + shader_read, + render_target, + presentable }; struct Texture : public Asset { -- cgit v1.2.3-54-g00ecf