enum CMD_BUF_TYPE { GRAPHICS, COPY, ASYNC_COMPUTE, } class device_command_buffer { void start(); void stop(); VkCommandBuffer handle; queue owner; CMD_BUF_TYPE buf_type = CMD_BUF_TYPE::MAX; } class graphics_device_command_buffer : device_command_buffer { void image_barrier(handle tex, State new_state) { texture = rm::get_texture(tex); vkCmdBarrier(texture.img.device_image, texture.current_state, new_state, handle); texture.current_state = new_state; } } class device_swapchain { VkSurfaceKHR surface; VkFormat surface_format; VkExtent extent; VkSwapchain swapchain; } class device_buffer { VkBuffer handle; VmaAllocation allocation; VkDeviceAddr gpu_address; } class device_texture { VkImage handle; VmaAllocation allocation; array bindless_id; // TODO: Handle this in a higher level ? VkImageView whole_image_view; vector mips_view; const uint invalid_id = -1; } class global_descriptor_set { VkDescritptorSetLayout layout; VkDescriptorSet set; }; class device { device_image create_texture(TextureDesc desc) { auto tex {}; tex.handle = vmaCreateImage(&tex.alloc); } device_buffer create_buffer(BufferDesc desc); template void allocate_command_buffers(command_buffer_type ptr, uint32_t count, CMD_BUF_TYPE type) { vkAllocate(ptr, count, queue[type]); } submit(span cmd_buffers, semaphore) { vkSubmit(); } wait(fence); wait_idle(); private: create_surface(); create_swapchain(); device_swapchain sc; array sc_textures; array queues; global_descriptor_set global_set; // bindless // set 0 global, textures, samplers // set 1 materials buffer addr, camera, etc // set 2 per mesh vertex attributes buffers addr // set 2 per instance (transforms, etc) pipeline_layout compute; // set 0 & 1 pipeline_layout graphics; // set 0, 1, 2 } class renderer { renderer() { device_images = device::get_swapchain_images(); for (device_images) { sc_images.push(ressourcemanager::create_texture(desc, images[i])) } } void start_frame() { vframe_index++; device::wait(finish); img_index = device::get_image(sc) cur_image = images[img_index]; } void finish_frame() { device::submit(to_submit, wait_submit); device::present(wait_submit, finish); } command_buffer start_record() { auto buf = frames[current].command_buffers.pop(); buf.start(); device::submit(span(command_buffers[vframe_index][0...last_cmd_buf_index[vframe_index]]), wait_submit); device::present(); } void stop_record(command_buffer buf) { buf.stop(); auto buf = frames[current][buf_type][++last_cmd_buf_index[vframe_index]]; } private: vector sc_images; // Virtual frames stuff array wait_image; array wait_present; array finish; array command_pool; // One per queue type array, vframes_count> command_buffers; array last_cmd_buf_index = { 0 }; uint vframe_index; constexpr uint vframe_max_buf = 10; constexpr uint vframes_count = 3; } class ressourcemanager { handle create_texture(TextureDesc desc) { new_image = device::create_image(desc); return textures.push({ .desc = desc, .img = new_image, }) } handle create_texture(TextureDesc desc, device_image img) { return textures.push({ .desc = desc, .img = img, }) } handle create_pipeline(PipelineDesc desc) const Texture& get_texture(handle texture_handle); private: FreeList textures; } class PipelineDesc { // TODO: Where to store views ? span color_textures; Texture depth_texture; } class TextureDesc { Vector size; UsageFlag usages; Format format; } class BufferDesc { uint size; UsageFlag usages; } class Texture { const TextureDesc desc; const device_image img; Layout current_layout = initial_layout; } class Buffer { const BufferDesc desc; const device_buffer buffer; } class mesh { array attributes; buffer index; } class mesh_instance { handle mesh; mat4 transform; } class renderpass { // shadow example renderpass() { shadow_tex = rm::create_texture({ .size = { 1024, 1024 }, .usages = DEPTH_STENCIL_ATTACHMENT, .format = VK_FORMAT_D24, }); pso = rm::create_pipeline({ .color = null, .depth = scene_depth, // contains format }) } render() { handle meshes[] = scene.get_meshes(FILTER_NONE); buf = renderer::start_record(); buf.start_renderpass({ .color_attachments .depth_attachment = shadow_tex, }); buf.bind_pipeline(pso); foreach(mesh : meshes) { buf.draw_index(mesh); } buf.stop_renderpass(); } std::vector color_attachments; shadow_tex; pipeline pso; } renderer::add_render_pass() TODOS : 1. implement : * image loading * resizing * mesh drawing IDEAS : 1. Manage device resources with handles instead of having immutable ones in high level resources PROS: * Allows to manage (swap, create, delete, invalide) device resources easily CONS: * Can loose track of a resource easily RESOURCES : * SEED Halcyon presentation https://www.ea.com/seed/news/syysgraph-2018-modern-graphics-abstractions-real-time-ray-tracing * Cryengine master class https://www.youtube.com/watch?v=34S3onEr3r8 * Nicebyte tweet api https://twitter.com/nice_byte/status/1579187394880090112