From a49736099b52f8471febbef008daf35b7a4f8827 Mon Sep 17 00:00:00 2001 From: Ada Tufankjian Date: Tue, 27 Aug 2024 15:52:14 -0700 Subject: [PATCH] Multi-threaded Asset Loading + More (#6) * Moving around some ideas for a new texture system * Getting the first phase of async texture loading working * Setting up texture descriptor pool for texture system 2 * Making progress at getting the new texture system in and working. Replaced usage in the view system and it works. Next we replace the old texture system entirely. * Replacing the old texture system * Fixing log to actually print * Vulkan debugging breakpoint * Adding missing config.h.in changes * Lots of little texture load fixes * Fixing sync problems * memory mapped io for faster asset loading * Adding the concept of a thread local allocator * Fixing build for posix platforms * Texture loading no longer blocks the main thread * Fixing silly error * Another attempt to fix build * I swear I can fix this * Refactoring queues * Some deduplication * Sketching out the new material system and a couple other quality of life and bug fixes * More sketching async material loads * Loading materials that load textures * Making progress on figuring out how to get materials to chain load textures * Materials go through the new loading process * _Thread_local to thread_local * A bit of renaming * Fixing some material sizing issues * Progress. Just need to fix bistro * Fixing materials not loading * Some misc fixups * Fixing static windows build * allocator.h to tb_allocator.h * Renaming and trimming files * Sketching out the new mesh system * Some mesh systems * tb_engine_config * New mesh descriptor update system * Sketching mesh load system and fixing string dependency in material->texture loading * Fixing system not running * Sketching out some ideas for scene loading as well as getting async mesh loading to get data into driver owned memory * Handling submesh loading * Refactoring cmake presets to be a bit more flexible * Fixing presets * New loading strategy and descriptor write flow The new mesh system is invoked which is sadly very slow due to descriptor pool thrashing during load. The solution will be to only resize the descriptor pool when a scene has finished parsing all of its resources * Textures now load in phases to reduce descriptor thrash * Similar phasing strategy for material loading * Scene2 drives mesh loading * The new mesh system is plumbed for rendering but meshes aren't drawn properly yet * Fixing problem of how to identify meshes across multiple sources * Fixing failure to deduplicate material entities * Pinned tasks are now a way for systems to exfiltrate work outside of a readonly system * Removing more of the old mesh asset loading systems * Reorganizing a bit of the water systems * Meshes render again though with some problems * Some instrumentation and profiling fixes * Finally figured out why we'd get validation failures with tracy * Fixing meshes * Fixing obvious warning * Fixing a small issue in FINAL builds * Rough but functional improvement to gltf loading * Fixing build * Major progress on async scene loading. Still working on getting entities loading correctly * Fixed rendering but parents are screwed * Scene waits for entities to have components report readiness * Fixing parenting issues * Making sure entire scene gets parsed properly * Removing log and fixing crash with bistro * Improving scene loading. Can't load bistro yet but making progress * Progress on making sure loading completes. Smaller systems like the ocean test work but bistro isn't there yet * More loading UI and making sure not to overrun descriptor pool * Adding a progress bar for my own sake * Removing an unused variable and making sure that entities popped off the queue are loaded * Fixing some loading problems. GPU timeout remains * Sketching out descriptor buffer structure and API * Initializing descriptor buffer in the texture system * Making sure the descriptor buffer can be replaced inline * Cleaning up some of the buffer address access pattern * Migrating the render object system to descriptor buffers * Working on getting the descriptor buffer up to meeting the needs of the view system * Fixing build * More progress on descriptor buffers * Migrating mesh systems to descriptor buffers * Descriptor Buffer code path compiles + some refactoring * Validation layers stopped screaming but loading never finishes * Loads and runs but doesn't render yet * Fixing silly issue with buffer indices not being set properly * Fixing sky * Defining TbShader also sketching out what an update to the sky system would look like * An attempt to get the sky system to work with descriptor buffers that doesn't work but doesn't crash * Fixed the sky Descriptor Buffer not working * Small fix to make sure that the mesh and material systems don't try to write more descriptors than available * Some cleanup and improvements to reduce crashiness * Some extra cleanup * Working on a better descriptor access pattern * Updating material descriptor approach * Updating mesh descriptor system. Doesn't work lmao * Fixing the dyn desc pool not writing the correct resources. Still have a GPU crash * Making some of the descriptor API a bit more ergonomic to rule out crash sources * Seems less crashy when we remove some complexity * No more crashes * Time slicing mesh uploading * Fixing minor warning * Smoother loading by breaking up scene loading * Separating shader optimization flags from debug info flags * Moving the concept of a free list to its own module so I can reuse it * Reducing per-frame work that has to be done by the render object system --- .github/workflows/windows.yml | 2 +- CMakeLists.txt | 35 +- CMakePresets.json | 292 ++--- addons/water/include/ocean.hlsli | 8 +- addons/water/include/oceansystem.h | 13 - ...{oceancomponent.h => tb_ocean_component.h} | 4 +- addons/water/include/tb_ocean_system.h | 13 + ...{oceancomponent.c => tb_ocean_component.c} | 28 +- .../{oceansystem.c => tb_ocean_system.c} | 125 +- include/common.hlsli | 28 +- include/cube_view_lut.hlsli | 43 +- include/imgui.hlsli | 2 +- include/loadthread.h | 51 - include/logsystem.h | 32 - include/materialsystem.h | 59 - include/meshcomponent.h | 32 - include/noclipcontrollersystem.h | 5 - include/scene.h | 11 - include/shadowsystem.h | 16 - include/{allocator.h => tb_allocator.h} | 5 + include/{assets.h => tb_assets.h} | 2 +- include/{audiosystem.h => tb_audio_system.h} | 6 +- include/{bloom.h => tb_bloom.h} | 2 +- ...ameracomponent.h => tb_camera_component.h} | 2 +- .../{camerasystem.h => tb_camera_system.h} | 2 +- include/{tbcommon.h => tb_common.h} | 36 +- .../{coreuisystem.h => tb_coreui_system.h} | 6 +- include/tb_descriptor_buffer.h | 49 + include/tb_dyn_desc_pool.h | 46 + include/{dynarray.h => tb_dynarray.h} | 2 +- include/tb_free_list.h | 16 + include/{fxaa.h => tb_fxaa.h} | 4 +- include/{tbgltf.h => tb_gltf.h} | 12 + include/{hash.h => tb_hash.h} | 0 include/{tbimgui.h => tb_imgui.h} | 0 include/{imguisystem.h => tb_imgui_system.h} | 8 +- include/{inputsystem.h => tb_input_system.h} | 8 +- include/{tbktx.h => tb_ktx.h} | 0 ...{lightcomponent.h => tb_light_component.h} | 4 +- include/{lightsystem.h => tb_light_system.h} | 4 +- include/{tblog.h => tb_log.h} | 0 include/{luminance.h => tb_luminance.h} | 2 +- include/tb_material_system.h | 72 ++ include/tb_mesh_component.h | 10 + include/{meshsystem.h => tb_mesh_rnd_sys.h} | 52 +- include/tb_mesh_system.h | 57 + include/tb_mmap.h | 10 + ...oclipcomponent.h => tb_noclip_component.h} | 0 include/{physlayers.h => tb_phys_layers.h} | 0 .../{physicssystem.h => tb_physics_system.h} | 4 +- ...hysicssystem.hpp => tb_physics_system.hpp} | 6 +- include/{pi.h => tb_pi.h} | 0 include/{profiling.h => tb_profiling.h} | 22 + include/{tbqueue.h => tb_queue.h} | 26 +- include/{tbrand.h => tb_rand.h} | 0 .../{tbrendercommon.h => tb_render_common.h} | 21 +- ...jectsystem.h => tb_render_object_system.h} | 35 +- ...nesystem.h => tb_render_pipeline_system.h} | 28 +- .../{rendersystem.h => tb_render_system.h} | 39 +- ...rgetsystem.h => tb_render_target_system.h} | 8 +- .../{renderthread.h => tb_render_thread.h} | 17 +- ...dycomponent.h => tb_rigidbody_component.h} | 0 include/tb_scene.h | 10 + include/tb_scene_material.h | 4 + include/{tbsdl.h => tb_sdl.h} | 4 + include/{settings.h => tb_settings.h} | 2 +- .../{shadercommon.h => tb_shader_common.h} | 2 +- include/tb_shader_system.h | 16 +- include/{simd.h => tb_simd.h} | 0 .../{skycomponent.h => tb_sky_component.h} | 4 +- include/{skysystem.h => tb_sky_system.h} | 27 +- ...bsystempriority.h => tb_system_priority.h} | 0 include/tb_task_scheduler.h | 2 + include/tb_texture_system.h | 61 + ...ponents.h => tb_third_person_components.h} | 2 +- ...rmcomponent.h => tb_transform_component.h} | 6 +- include/{tbutil.h => tb_util.h} | 0 include/{viewsystem.h => tb_view_system.h} | 20 +- ...ingsystem.h => tb_visual_logging_system.h} | 15 +- include/{tbvk.h => tb_vk.h} | 0 include/{tbvkalloc.h => tb_vk_alloc.h} | 2 +- include/{vkdbg.h => tb_vk_dbg.h} | 6 +- include/{tbvma.h => tb_vma.h} | 0 include/{world.h => tb_world.h} | 32 +- include/texturesystem.h | 73 -- include/timeofdaysystem.h | 15 - readme.md | 2 +- samples/bistro/source/main.c | 2 +- .../ocean/assets/originals/components.blend | 4 +- samples/ocean/assets/scenes/components.glb | 4 +- samples/ocean/source/main.c | 2 +- samples/samplecore/include/samplecore.h | 2 +- samples/samplecore/source/samplecore.c | 23 +- samples/terrain/source/main.c | 2 +- source/cgltf.c | 8 - source/config.h.in | 11 + source/loadthread.c | 1 - source/lumavg.hlsl.cs | 65 +- source/lumhist.hlsl.cs | 41 +- source/materialsystem.c | 528 -------- source/meshcomponent.c | 189 --- source/renderobjectsystem.c | 225 ---- source/sky.hlsl | 9 +- source/skydome.c | 2 +- source/{allocator.c => tb_allocator.c} | 82 +- source/{assets.c => tb_assets.c} | 25 +- source/{audiosystem.c => tb_audio_system.c} | 8 +- source/{bloom.c => tb_bloom.c} | 8 +- ...ameracomponent.c => tb_camera_component.c} | 35 +- source/{camerasystem.c => tb_camera_system.c} | 18 +- source/tb_cgltf.c | 99 ++ source/{coreuisystem.c => tb_coreui_system.c} | 12 +- source/tb_descriptor_buffer.c | 225 ++++ source/tb_dyn_desc_pool.c | 141 +++ source/tb_free_list.c | 35 + source/{fxaa.c => tb_fxaa.c} | 23 +- source/{hash.c => tb_hash.c} | 2 +- source/{imguisystem.c => tb_imgui_system.c} | 37 +- source/{inputsystem.c => tb_input_system.c} | 8 +- ...{lightcomponent.c => tb_light_component.c} | 30 +- source/{lightsystem.c => tb_light_system.c} | 16 +- source/tb_loading_ui.c | 143 +++ source/{logsystem.c => tb_log_system.c} | 53 +- source/{luminance.c => tb_luminance.c} | 16 +- source/tb_material_system.c | 619 +++++++++ source/tb_mesh_component.c | 65 + source/{meshsystem.c => tb_mesh_rnd_sys.c} | 990 +++++---------- source/tb_mesh_system.c | 1113 +++++++++++++++++ source/tb_mmap.c | 112 ++ ...oclipcomponent.c => tb_noclip_component.c} | 27 +- ...system.c => tb_noclip_controller_system.c} | 29 +- ...hysicssystem.cpp => tb_physics_system.cpp} | 27 +- source/{profiling.cpp => tb_profiling.cpp} | 34 +- source/{tbrand.c => tb_rand.c} | 2 +- .../{tbrendercommon.c => tb_render_common.c} | 2 +- source/tb_render_object_system.c | 232 ++++ ...nesystem.c => tb_render_pipeline_system.c} | 51 +- source/{rendersystem.c => tb_render_system.c} | 199 ++- ...rgetsystem.c => tb_render_target_system.c} | 10 +- source/{renderthread.c => tb_render_thread.c} | 167 ++- ...mponent.cpp => tb_rigidbody_component.cpp} | 58 +- source/tb_scene.c | 620 +++++++++ source/tb_scene_material.c | 230 ++++ source/tb_sdl.c | 10 + source/{settings.c => tb_settings.c} | 12 +- .../{shader_system.c => tb_shader_system.c} | 47 +- source/{shadowsystem.c => tb_shadow_system.c} | 127 +- source/{simd.c => tb_simd.c} | 8 +- source/{skycomponent.c => tb_sky_component.c} | 29 +- source/{skysystem.c => tb_sky_system.c} | 339 +++-- ...l_audio_comp.c => tb_spatial_audio_comp.c} | 23 +- ...dio_system.c => tb_spatial_audio_system.c} | 12 +- .../{task_scheduler.c => tb_task_scheduler.c} | 22 +- source/tb_texture_system.c | 1100 ++++++++++++++++ ...ponents.c => tb_third_person_components.c} | 28 +- ...ystems.cpp => tb_third_person_systems.cpp} | 18 +- source/{thrower.c => tb_thrower.c} | 32 +- ...eofdaysystem.c => tb_time_of_day_system.c} | 47 +- ...rmcomponent.c => tb_transform_component.c} | 41 +- source/{tbutil.c => tb_util.c} | 2 +- source/{viewsystem.c => tb_view_system.c} | 302 ++++- ...ingsystem.c => tb_visual_logging_system.c} | 55 +- source/{tbvkalloc.c => tb_vk_alloc.c} | 6 +- source/{vkdbg.c => tb_vk_dbg.c} | 4 +- source/{vma.cpp => tb_vma.cpp} | 2 +- source/{world.c => tb_world.c} | 227 +--- source/texturesystem.c | 1007 --------------- source/upsample.hlsl.cs | 2 +- viewer/source/main.c | 24 +- viewer/source/viewersystem.c | 10 +- 170 files changed, 7595 insertions(+), 4389 deletions(-) delete mode 100644 addons/water/include/oceansystem.h rename addons/water/include/{oceancomponent.h => tb_ocean_component.h} (83%) create mode 100644 addons/water/include/tb_ocean_system.h rename addons/water/source/{oceancomponent.c => tb_ocean_component.c} (85%) rename addons/water/source/{oceansystem.c => tb_ocean_system.c} (93%) delete mode 100644 include/loadthread.h delete mode 100644 include/logsystem.h delete mode 100644 include/materialsystem.h delete mode 100644 include/meshcomponent.h delete mode 100644 include/noclipcontrollersystem.h delete mode 100644 include/scene.h delete mode 100644 include/shadowsystem.h rename include/{allocator.h => tb_allocator.h} (93%) rename include/{assets.h => tb_assets.h} (88%) rename include/{audiosystem.h => tb_audio_system.h} (92%) rename include/{bloom.h => tb_bloom.h} (98%) rename include/{cameracomponent.h => tb_camera_component.h} (95%) rename include/{camerasystem.h => tb_camera_system.h} (64%) rename include/{tbcommon.h => tb_common.h} (60%) rename include/{coreuisystem.h => tb_coreui_system.h} (88%) create mode 100644 include/tb_descriptor_buffer.h create mode 100644 include/tb_dyn_desc_pool.h rename include/{dynarray.h => tb_dynarray.h} (99%) create mode 100644 include/tb_free_list.h rename include/{fxaa.h => tb_fxaa.h} (91%) rename include/{tbgltf.h => tb_gltf.h} (68%) rename include/{hash.h => tb_hash.h} (100%) rename include/{tbimgui.h => tb_imgui.h} (100%) rename include/{imguisystem.h => tb_imgui_system.h} (91%) rename include/{inputsystem.h => tb_input_system.h} (95%) rename include/{tbktx.h => tb_ktx.h} (100%) rename include/{lightcomponent.h => tb_light_component.h} (85%) rename include/{lightsystem.h => tb_light_system.h} (75%) rename include/{tblog.h => tb_log.h} (100%) rename include/{luminance.h => tb_luminance.h} (98%) create mode 100644 include/tb_material_system.h create mode 100644 include/tb_mesh_component.h rename include/{meshsystem.h => tb_mesh_rnd_sys.h} (73%) create mode 100644 include/tb_mesh_system.h create mode 100644 include/tb_mmap.h rename include/{noclipcomponent.h => tb_noclip_component.h} (100%) rename include/{physlayers.h => tb_phys_layers.h} (100%) rename include/{physicssystem.h => tb_physics_system.h} (95%) rename include/{physicssystem.hpp => tb_physics_system.hpp} (88%) rename include/{pi.h => tb_pi.h} (100%) rename include/{profiling.h => tb_profiling.h} (73%) rename include/{tbqueue.h => tb_queue.h} (69%) rename include/{tbrand.h => tb_rand.h} (100%) rename include/{tbrendercommon.h => tb_render_common.h} (85%) rename include/{renderobjectsystem.h => tb_render_object_system.h} (62%) rename include/{renderpipelinesystem.h => tb_render_pipeline_system.h} (91%) rename include/{rendersystem.h => tb_render_system.h} (84%) rename include/{rendertargetsystem.h => tb_render_target_system.h} (96%) rename include/{renderthread.h => tb_render_thread.h} (91%) rename include/{rigidbodycomponent.h => tb_rigidbody_component.h} (100%) create mode 100644 include/tb_scene.h create mode 100644 include/tb_scene_material.h rename include/{tbsdl.h => tb_sdl.h} (85%) rename include/{settings.h => tb_settings.h} (97%) rename include/{shadercommon.h => tb_shader_common.h} (93%) rename include/{simd.h => tb_simd.h} (100%) rename include/{skycomponent.h => tb_sky_component.h} (79%) rename include/{skysystem.h => tb_sky_system.h} (76%) rename include/{tbsystempriority.h => tb_system_priority.h} (100%) create mode 100644 include/tb_texture_system.h rename include/{thirdpersoncomponents.h => tb_third_person_components.h} (96%) rename include/{transformcomponent.h => tb_transform_component.h} (89%) rename include/{tbutil.h => tb_util.h} (100%) rename include/{viewsystem.h => tb_view_system.h} (80%) rename include/{visualloggingsystem.h => tb_visual_logging_system.h} (87%) rename include/{tbvk.h => tb_vk.h} (100%) rename include/{tbvkalloc.h => tb_vk_alloc.h} (97%) rename include/{vkdbg.h => tb_vk_dbg.h} (97%) rename include/{tbvma.h => tb_vma.h} (100%) rename include/{world.h => tb_world.h} (71%) delete mode 100644 include/texturesystem.h delete mode 100644 include/timeofdaysystem.h delete mode 100644 source/cgltf.c delete mode 100644 source/loadthread.c delete mode 100644 source/materialsystem.c delete mode 100644 source/meshcomponent.c delete mode 100644 source/renderobjectsystem.c rename source/{allocator.c => tb_allocator.c} (75%) rename source/{assets.c => tb_assets.c} (79%) rename source/{audiosystem.c => tb_audio_system.c} (97%) rename source/{bloom.c => tb_bloom.c} (99%) rename source/{cameracomponent.c => tb_camera_component.c} (71%) rename source/{camerasystem.c => tb_camera_system.c} (90%) create mode 100644 source/tb_cgltf.c rename source/{coreuisystem.c => tb_coreui_system.c} (95%) create mode 100644 source/tb_descriptor_buffer.c create mode 100644 source/tb_dyn_desc_pool.c create mode 100644 source/tb_free_list.c rename source/{fxaa.c => tb_fxaa.c} (96%) rename source/{hash.c => tb_hash.c} (90%) rename source/{imguisystem.c => tb_imgui_system.c} (97%) rename source/{inputsystem.c => tb_input_system.c} (98%) rename source/{lightcomponent.c => tb_light_component.c} (54%) rename source/{lightsystem.c => tb_light_system.c} (93%) create mode 100644 source/tb_loading_ui.c rename source/{logsystem.c => tb_log_system.c} (88%) rename source/{luminance.c => tb_luminance.c} (96%) create mode 100644 source/tb_material_system.c create mode 100644 source/tb_mesh_component.c rename source/{meshsystem.c => tb_mesh_rnd_sys.c} (60%) create mode 100644 source/tb_mesh_system.c create mode 100644 source/tb_mmap.c rename source/{noclipcomponent.c => tb_noclip_component.c} (56%) rename source/{noclipcontrollersystem.c => tb_noclip_controller_system.c} (85%) rename source/{physicssystem.cpp => tb_physics_system.cpp} (97%) rename source/{profiling.cpp => tb_profiling.cpp} (63%) rename source/{tbrand.c => tb_rand.c} (99%) rename source/{tbrendercommon.c => tb_render_common.c} (94%) create mode 100644 source/tb_render_object_system.c rename source/{renderpipelinesystem.c => tb_render_pipeline_system.c} (99%) rename source/{rendersystem.c => tb_render_system.c} (87%) rename source/{rendertargetsystem.c => tb_render_target_system.c} (99%) rename source/{renderthread.c => tb_render_thread.c} (94%) rename source/{rigidbodycomponent.cpp => tb_rigidbody_component.cpp} (93%) create mode 100644 source/tb_scene.c create mode 100644 source/tb_scene_material.c create mode 100644 source/tb_sdl.c rename source/{settings.c => tb_settings.c} (96%) rename source/{shader_system.c => tb_shader_system.c} (76%) rename source/{shadowsystem.c => tb_shadow_system.c} (83%) rename source/{simd.c => tb_simd.c} (99%) rename source/{skycomponent.c => tb_sky_component.c} (59%) rename source/{skysystem.c => tb_sky_system.c} (84%) rename source/{spatial_audio_comp.c => tb_spatial_audio_comp.c} (70%) rename source/{spatial_audio_system.c => tb_spatial_audio_system.c} (95%) rename source/{task_scheduler.c => tb_task_scheduler.c} (92%) create mode 100644 source/tb_texture_system.c rename source/{thirdpersoncomponents.c => tb_third_person_components.c} (83%) rename source/{thirdpersonsystems.cpp => tb_third_person_systems.cpp} (95%) rename source/{thrower.c => tb_thrower.c} (74%) rename source/{timeofdaysystem.c => tb_time_of_day_system.c} (84%) rename source/{transformcomponent.c => tb_transform_component.c} (84%) rename source/{tbutil.c => tb_util.c} (90%) rename source/{viewsystem.c => tb_view_system.c} (58%) rename source/{visualloggingsystem.c => tb_visual_logging_system.c} (96%) rename source/{tbvkalloc.c => tb_vk_alloc.c} (96%) rename source/{vkdbg.c => tb_vk_dbg.c} (97%) rename source/{vma.cpp => tb_vma.cpp} (58%) rename source/{world.c => tb_world.c} (60%) delete mode 100644 source/texturesystem.c diff --git a/.github/workflows/windows.yml b/.github/workflows/windows.yml index 50b5fce1..2a0dc1c8 100644 --- a/.github/workflows/windows.yml +++ b/.github/workflows/windows.yml @@ -17,7 +17,7 @@ jobs: {preset: x64-windows-static-ninja-llvm}, ] - name: ${{matrix.toolsets.preset}} on Windows} + name: ${{matrix.toolsets.preset}} on Windows runs-on: [Windows, X64, self-hosted] steps: diff --git a/CMakeLists.txt b/CMakeLists.txt index 72633056..8dfe7b62 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -33,18 +33,28 @@ include(${CMAKE_CURRENT_LIST_DIR}/cmake/TargetArch.cmake) target_architecture(arch) # Platform detection +set(tb_windows 0) +set(tb_macos 0) +set(tb_linux 0) +set(tb_android 0) +set(tb_ios 0) if(WIN32) set(platform "windows") + set(tb_windows 1) elseif(APPLE) if(IOS) set(platform "ios") + set(tb_ios 1) else() set(platform "macos") + set(tb_macos 1) endif() elseif(ANDROID) set(platform "android") + set(tb_android 1) elseif(UNIX) set(platform "linux") + set(tb_linux 1) else() message(FATAL_ERROR "Unknown platform") endif() @@ -62,14 +72,20 @@ else() message(FATAL_ERROR "Unknown host") endif() +set(tb_x64 0) +set(tb_arm64 0) if(CMAKE_HOST_SYSTEM_PROCESSOR STREQUAL "AMD64") set(host_arch "x64") + set(tb_x64 1) elseif(CMAKE_HOST_SYSTEM_PROCESSOR STREQUAL "x86_64") set(host_arch "x64") + set(tb_x64 1) elseif(CMAKE_HOST_SYSTEM_PROCESSOR STREQUAL "arm64") set(host_arch "arm64") + set(tb_arm64 1) elseif(CMAKE_HOST_SYSTEM_PROCESSOR STREQUAL "aarch64") set(host_arch "arm64") + set(tb_arm64 1) else() message(FATAL_ERROR "Unknown host arch: ${CMAKE_HOST_SYSTEM_PROCESSOR}") endif() @@ -171,8 +187,8 @@ function(cook_shaders out_shader_sources out_shader_headers) add_custom_command( OUTPUT ${out_paths} COMMAND ${CMAKE_COMMAND} -E make_directory ${shader_out_path} - COMMAND ${DXC} -T vs_6_5 -E vert -Vn ${filename}_vert $<$:-O0> $<$:-Zi> -I ${shader_include_dir} -I ${engine_shader_include_dir} $<$:-Qembed_debug> -enable-16bit-types -fspv-target-env=vulkan1.3 -spirv ${shader} -Fh ${vert_out_path} - COMMAND ${DXC} -T ps_6_5 -E frag -Vn ${filename}_frag $<$:-O0> $<$:-Zi> -I ${shader_include_dir} -I ${engine_shader_include_dir} $<$:-Qembed_debug> -enable-16bit-types -fspv-target-env=vulkan1.3 -spirv ${shader} -Fh ${frag_out_path} + COMMAND ${DXC} -T vs_6_5 -E vert -Vn ${filename}_vert $<$:-O0> $<$>:-Zi> -I ${shader_include_dir} -I ${engine_shader_include_dir} $<$>:-Qembed_debug> -enable-16bit-types -fspv-target-env=vulkan1.3 -spirv ${shader} -Fh ${vert_out_path} + COMMAND ${DXC} -T ps_6_5 -E frag -Vn ${filename}_frag $<$:-O0> $<$>:-Zi> -I ${shader_include_dir} -I ${engine_shader_include_dir} $<$>:-Qembed_debug> -enable-16bit-types -fspv-target-env=vulkan1.3 -spirv ${shader} -Fh ${frag_out_path} MAIN_DEPENDENCY ${shader} DEPENDS ${shader_includes} ) @@ -193,8 +209,8 @@ function(cook_shaders out_shader_sources out_shader_headers) add_custom_command( OUTPUT ${out_paths} COMMAND ${CMAKE_COMMAND} -E make_directory ${shader_out_path} - COMMAND ${DXC} -T ms_6_5 -E mesh -Vn ${filename}_mesh $<$:-O0> -I ${shader_include_dir} -I ${engine_shader_include_dir} $<$:-Zi> $<$:-Qembed_debug> -fspv-target-env=vulkan1.3 -spirv ${shader} -Fh ${mesh_out_path} - COMMAND ${DXC} -T ps_6_5 -E frag -Vn ${filename}_frag $<$:-O0> -I ${shader_include_dir} -I ${engine_shader_include_dir} $<$:-Zi> $<$:-Qembed_debug> -fspv-target-env=vulkan1.3 -spirv ${shader} -Fh ${frag_out_path} + COMMAND ${DXC} -T ms_6_5 -E mesh -Vn ${filename}_mesh $<$:-O0> -I ${shader_include_dir} -I ${engine_shader_include_dir} $<$>:-Zi> $<$>:-Qembed_debug> -fspv-target-env=vulkan1.3 -spirv ${shader} -Fh ${mesh_out_path} + COMMAND ${DXC} -T ps_6_5 -E frag -Vn ${filename}_frag $<$:-O0> -I ${shader_include_dir} -I ${engine_shader_include_dir} $<$>:-Zi> $<$>:-Qembed_debug> -fspv-target-env=vulkan1.3 -spirv ${shader} -Fh ${frag_out_path} MAIN_DEPENDENCY ${shader} DEPENDS ${shader_includes} ) @@ -214,7 +230,7 @@ function(cook_shaders out_shader_sources out_shader_headers) add_custom_command( OUTPUT ${out_paths} COMMAND ${CMAKE_COMMAND} -E make_directory ${shader_out_path} - COMMAND ${DXC} -T cs_6_5 -E comp -Vn ${filename}_comp $<$:-O0> -I ${shader_include_dir} -I ${engine_shader_include_dir} $<$:-Zi> $<$:-Qembed_debug> -enable-16bit-types -fspv-target-env=vulkan1.3 -spirv ${shader} -Fh ${comp_out_path} + COMMAND ${DXC} -T cs_6_5 -E comp -Vn ${filename}_comp $<$:-O0> -I ${shader_include_dir} -I ${engine_shader_include_dir} $<$>:-Zi> $<$>:-Qembed_debug> -enable-16bit-types -fspv-target-env=vulkan1.3 -spirv ${shader} -Fh ${comp_out_path} MAIN_DEPENDENCY ${shader} DEPENDS ${shader_includes} ) @@ -257,7 +273,7 @@ function(cook_assets target_name out_assets_path) add_custom_command( OUTPUT ${packed_scene} COMMAND ${CMAKE_COMMAND} -E make_directory assets/${relpath} - COMMAND ${GLTFPACK} -cc -mi -kn -km -ke -tc -i ${scene} -o ${packed_scene} + COMMAND ${GLTFPACK} -cc -kn -km -ke -tc -i ${scene} -o ${packed_scene} MAIN_DEPENDENCY ${scene} WORKING_DIRECTORY ${CMAKE_CURRENT_BINARY_DIR}/$ ) @@ -357,9 +373,6 @@ add_library(toybox OBJECT ${lib_source}) # We provide a cross platform blocks runtime so we can use this target_compile_options(toybox PUBLIC -fblocks) -# We rely on gnu statement expressions -target_compile_options(toybox PUBLIC -Wno-gnu-statement-expression) - if(WIN32) # This really only matters for clang-cl # ${CMAKE_C_COMPILER_ID} being MSVC would fail the above check @@ -376,8 +389,6 @@ if(WIN32) # We use anonymous structs target_compile_options(toybox PUBLIC -Wno-gnu-anonymous-struct) target_compile_options(toybox PUBLIC -Wno-nested-anon-types) - # Flecs uses '$' in identifiers as part of a DSL so this gets in the way - target_compile_options(toybox PUBLIC -Wno-dollar-in-identifier-extension) elseif(ANDROID) if(CMAKE_C_COMPILER_ARCHITECTURE_ID MATCHES "arm") @@ -407,7 +418,7 @@ execute_process(COMMAND git log -1 --format=%h # Generate config header set(engine_config_include_dir ${CMAKE_CURRENT_BINARY_DIR}/config) -configure_file(source/config.h.in ${engine_config_include_dir}/tbengineconfig.h @ONLY) +configure_file(source/config.h.in ${engine_config_include_dir}/tb_engine_config.h @ONLY) target_include_directories(toybox PUBLIC "${CMAKE_CURRENT_LIST_DIR}/include") target_include_directories(toybox PUBLIC "source/" "${CGLTF_INCLUDE_DIRS}" "${engine_config_include_dir}") diff --git a/CMakePresets.json b/CMakePresets.json index 36089e57..5d96b61b 100644 --- a/CMakePresets.json +++ b/CMakePresets.json @@ -7,100 +7,83 @@ }, "configurePresets": [ { - "name": "x64-windows-ninja-llvm", - "displayName": "x64 Windows Ninja LLVM", + "name": "ninja", + "hidden": true, "generator": "Ninja Multi-Config", - "binaryDir": "${sourceDir}/build/x64-windows", "toolchainFile": "$env{VCPKG_ROOT}/scripts/buildsystems/vcpkg.cmake", - "cacheVariables": { - "VCPKG_TARGET_TRIPLET": "x64-win-llvm", - "CMAKE_C_COMPILER": "clang", - "CMAKE_CXX_COMPILER": "clang++", - "CMAKE_RC_COMPILER": "llvm-rc", - "CMAKE_EXPORT_COMPILE_COMMANDS": "ON", - "FINAL": "OFF", - "PROFILE_TRACY": "OFF" - }, "environment": { - "VCPKG_KEEP_ENV_VARS": "PATH", "VCPKG_OVERLAY_TRIPLETS": "${sourceDir}/triplets/" - }, - "condition": { - "type": "equals", - "lhs": "${hostSystemName}", - "rhs": "Windows" } }, { - "name": "x64-windows-static-ninja-llvm", - "displayName": "x64 Windows Static Ninja LLVM", - "generator": "Ninja Multi-Config", - "binaryDir": "${sourceDir}/build/x64-windows-static", - "toolchainFile": "$env{VCPKG_ROOT}/scripts/buildsystems/vcpkg.cmake", + "name": "llvm", + "hidden": true, "cacheVariables": { - "VCPKG_TARGET_TRIPLET": "x64-win-llvm-static", - "CMAKE_C_COMPILER": "clang", - "CMAKE_CXX_COMPILER": "clang++", - "CMAKE_RC_COMPILER": "llvm-rc", "CMAKE_EXPORT_COMPILE_COMMANDS": "ON" - }, - "environment": { - "VCPKG_KEEP_ENV_VARS": "PATH", - "VCPKG_OVERLAY_TRIPLETS": "${sourceDir}/triplets/" - }, - "condition": { - "type": "equals", - "lhs": "${hostSystemName}", - "rhs": "Windows" } }, { - "name": "arm64-android-ninja-llvm", - "displayName": "arm64 Android Ninja LLVM", - "generator": "Ninja Multi-Config", - "binaryDir": "${sourceDir}/build/arm64-android", - "toolchainFile": "$env{VCPKG_ROOT}/scripts/buildsystems/vcpkg.cmake", + "name": "arm64-android", + "hidden": true, "cacheVariables": { "VCPKG_TARGET_TRIPLET": "arm64-android", "VCPKG_CHAINLOAD_TOOLCHAIN_FILE": "$env{ANDROID_NDK_HOME}/build/cmake/android.toolchain.cmake", "ANDROID_PLATFORM": "android-31", - "ANDROID_ABI": "arm64-v8a", - "CMAKE_EXPORT_COMPILE_COMMANDS": "ON" + "ANDROID_ABI": "arm64-v8a" }, "environment": { "VCPKG_KEEP_ENV_VARS": "JAVA_HOME;ANDROID_HOME;ANDROID_NDK_HOME" } }, { - "name": "x64-linux-ninja-llvm", - "displayName": "x64 Linux Ninja LLVM", - "generator": "Ninja Multi-Config", - "binaryDir": "${sourceDir}/build/x64-linux-llvm", - "toolchainFile": "$env{VCPKG_ROOT}/scripts/buildsystems/vcpkg.cmake", + "name": "self-host", + "hidden": true, "cacheVariables": { - "VCPKG_TARGET_TRIPLET": "x64-linux", "CMAKE_C_COMPILER": "clang", - "CMAKE_CXX_COMPILER": "clang++", - "CMAKE_EXPORT_COMPILE_COMMANDS": "ON" + "CMAKE_CXX_COMPILER": "clang++" + } + }, + { + "name": "windows", + "hidden": true, + "cacheVariables": { + "CMAKE_RC_COMPILER": "llvm-rc" + }, + "environment": { + "VCPKG_KEEP_ENV_VARS": "PATH" }, "condition": { "type": "equals", "lhs": "${hostSystemName}", - "rhs": "Linux" + "rhs": "Windows" } }, { - "name": "arm64-linux-ninja-llvm", - "displayName": "arm64 Linux Ninja LLVM", - "generator": "Ninja Multi-Config", - "binaryDir": "${sourceDir}/build/arm64-linux-llvm", - "toolchainFile": "$env{VCPKG_ROOT}/scripts/buildsystems/vcpkg.cmake", + "name": "x64-windows", + "hidden": true, + "inherits": [ + "self-host", + "windows" + ], "cacheVariables": { - "VCPKG_TARGET_TRIPLET": "arm64-linux", - "CMAKE_C_COMPILER": "clang", - "CMAKE_CXX_COMPILER": "clang++", - "CMAKE_EXPORT_COMPILE_COMMANDS": "ON" - }, + "VCPKG_TARGET_TRIPLET": "x64-win-llvm" + } + }, + { + "name": "x64-windows-static", + "hidden": true, + "inherits": [ + "self-host", + "windows" + ], + "cacheVariables": { + "VCPKG_TARGET_TRIPLET": "x64-win-llvm-static" + } + }, + { + "name": "linux", + "hidden": true, + "inherits": "self-host", "condition": { "type": "equals", "lhs": "${hostSystemName}", @@ -108,37 +91,25 @@ } }, { - "name": "arm64-macos-ninja-llvm", - "displayName": "arm64 macOS Ninja LLVM", - "generator": "Ninja Multi-Config", - "binaryDir": "${sourceDir}/build/arm64-macos-llvm", - "toolchainFile": "$env{VCPKG_ROOT}/scripts/buildsystems/vcpkg.cmake", + "name": "x64-linux", + "hidden": true, + "inherits": "self-host", "cacheVariables": { - "VCPKG_TARGET_TRIPLET": "arm64-osx", - "CMAKE_OSX_ARCHITECTURES": "arm64", - "CMAKE_C_COMPILER": "clang", - "CMAKE_CXX_COMPILER": "clang++", - "CMAKE_EXPORT_COMPILE_COMMANDS": "ON" - }, - "condition": { - "type": "equals", - "lhs": "${hostSystemName}", - "rhs": "Darwin" + "VCPKG_TARGET_TRIPLET": "x64-linux" } }, { - "name": "arm64-macos-xcode-llvm", - "displayName": "arm64 macOS Xcode LLVM", - "generator": "Xcode", - "binaryDir": "${sourceDir}/build/arm64-macos-xcode-llvm", - "toolchainFile": "$env{VCPKG_ROOT}/scripts/buildsystems/vcpkg.cmake", + "name": "arm64-linux", + "hidden": true, + "inherits": "self-host", "cacheVariables": { - "VCPKG_TARGET_TRIPLET": "arm64-osx", - "CMAKE_OSX_ARCHITECTURES": "arm64", - "CMAKE_C_COMPILER": "clang", - "CMAKE_CXX_COMPILER": "clang++", - "CMAKE_EXPORT_COMPILE_COMMANDS": "ON" - }, + "VCPKG_TARGET_TRIPLET": "arm64-linux" + } + }, + { + "name": "macos", + "hidden": true, + "inherits": "self-host", "condition": { "type": "equals", "lhs": "${hostSystemName}", @@ -146,44 +117,93 @@ } }, { - "name": "arm64-ios-ninja-llvm", - "displayName": "arm64 iOS Ninja LLVM", - "generator": "Ninja Multi-Config", - "binaryDir": "${sourceDir}/build/arm64-ios-llvm", - "toolchainFile": "$env{VCPKG_ROOT}/scripts/buildsystems/vcpkg.cmake", + "name": "arm64-macos", + "hidden": true, + "inherits": "macos", "cacheVariables": { - "VCPKG_TARGET_TRIPLET": "arm64-ios", - "CMAKE_OSX_ARCHITECTURES": "arm64", - "CMAKE_SYSTEM_NAME": "iOS", - "CMAKE_C_COMPILER": "clang", - "CMAKE_CXX_COMPILER": "clang++", - "CMAKE_EXPORT_COMPILE_COMMANDS": "ON" - }, - "condition": { - "type": "equals", - "lhs": "${hostSystemName}", - "rhs": "Darwin" + "VCPKG_TARGET_TRIPLET": "arm64-osx", + "CMAKE_OSX_ARCHITECTURES": "arm64" } }, { - "name": "arm64-ios-xcode-llvm", - "displayName": "arm64 iOS Xcode LLVM", - "generator": "Xcode", - "binaryDir": "${sourceDir}/build/arm64-ios-xcode-llvm", - "toolchainFile": "$env{VCPKG_ROOT}/scripts/buildsystems/vcpkg.cmake", + "name": "arm64-ios", + "hidden": true, + "inherits": "macos", "cacheVariables": { "VCPKG_TARGET_TRIPLET": "arm64-ios", "CMAKE_OSX_ARCHITECTURES": "arm64", - "CMAKE_SYSTEM_NAME": "iOS", - "CMAKE_C_COMPILER": "clang", - "CMAKE_CXX_COMPILER": "clang++", - "CMAKE_EXPORT_COMPILE_COMMANDS": "ON" - }, - "condition": { - "type": "equals", - "lhs": "${hostSystemName}", - "rhs": "Darwin" + "CMAKE_SYSTEM_NAME": "iOS" } + }, + { + "name": "x64-windows-ninja-llvm", + "inherits": [ + "x64-windows", + "ninja", + "llvm" + ], + "displayName": "x64 Windows Ninja LLVM", + "binaryDir": "${sourceDir}/build/x64-windows" + }, + { + "name": "x64-windows-static-ninja-llvm", + "inherits": [ + "x64-windows-static", + "ninja", + "llvm" + ], + "displayName": "x64 Windows Static Ninja LLVM", + "binaryDir": "${sourceDir}/build/x64-windows-static" + }, + { + "name": "arm64-android-ninja-llvm", + "inherits": [ + "arm64-android", + "ninja", + "llvm" + ], + "displayName": "arm64 Android Ninja LLVM", + "binaryDir": "${sourceDir}/build/arm64-android" + }, + { + "name": "x64-linux-ninja-llvm", + "inherits": [ + "x64-linux", + "ninja", + "llvm" + ], + "displayName": "x64 Linux Ninja LLVM", + "binaryDir": "${sourceDir}/build/x64-linux-llvm" + }, + { + "name": "arm64-linux-ninja-llvm", + "inherits": [ + "arm64-linux", + "ninja", + "llvm" + ], + "displayName": "arm64 Linux Ninja LLVM", + "binaryDir": "${sourceDir}/build/arm64-linux-llvm" + }, + { + "name": "arm64-macos-ninja-llvm", + "inherits": [ + "arm64-macos", + "ninja", + "llvm" + ], + "displayName": "arm64 macOS Ninja LLVM", + "binaryDir": "${sourceDir}/build/arm64-macos-llvm" + }, + { + "name": "arm64-ios-ninja-llvm", + "inherits": [ + "arm64-ios", + "ninja", + "llvm" + ], + "displayName": "arm64 iOS Ninja LLVM", + "binaryDir": "${sourceDir}/build/arm64-ios-llvm" } ], "buildPresets": [ @@ -295,24 +315,6 @@ "configurePreset": "arm64-macos-ninja-llvm", "configuration": "Release" }, - { - "name": "debug-arm64-macos-xcode-llvm", - "displayName": "Debug", - "configurePreset": "arm64-macos-xcode-llvm", - "configuration": "Debug" - }, - { - "name": "relwithdebinfo-arm64-macos-xcode-llvm", - "displayName": "RelWithDebInfo", - "configurePreset": "arm64-macos-xcode-llvm", - "configuration": "RelWithDebInfo" - }, - { - "name": "release-arm64-macos-xcode-llvm", - "displayName": "Release", - "configurePreset": "arm64-macos-xcode-llvm", - "configuration": "Release" - }, { "name": "debug-arm64-ios-ninja-llvm", "displayName": "Debug", @@ -330,24 +332,6 @@ "displayName": "Release", "configurePreset": "arm64-ios-ninja-llvm", "configuration": "Release" - }, - { - "name": "debug-arm64-ios-xcode-llvm", - "displayName": "Debug", - "configurePreset": "arm64-ios-xcode-llvm", - "configuration": "Debug" - }, - { - "name": "relwithdebinfo-arm64-ios-xcode-llvm", - "displayName": "RelWithDebInfo", - "configurePreset": "arm64-ios-xcode-llvm", - "configuration": "RelWithDebInfo" - }, - { - "name": "release-arm64-ios-xcode-llvm", - "displayName": "Release", - "configurePreset": "arm64-ios-xcode-llvm", - "configuration": "Release" } ] } \ No newline at end of file diff --git a/addons/water/include/ocean.hlsli b/addons/water/include/ocean.hlsli index f202de65..e8faba14 100644 --- a/addons/water/include/ocean.hlsli +++ b/addons/water/include/ocean.hlsli @@ -1,7 +1,7 @@ #pragma once #include "common.hlsli" -#include "pi.h" +#include "tb_pi.h" #define TB_WAVE_MAX 8 @@ -11,13 +11,11 @@ typedef float4 TbOceanWave; // xy = dir, z = steep, w = wavelength typedef struct OceanData { float4 time_waves; // x = time, y = wave count TbOceanWave wave[TB_WAVE_MAX]; -} -OceanData; +} OceanData; typedef struct OceanPushConstants { float4x4 m; -} -OceanPushConstants; +} OceanPushConstants; // If not in a shader, make a quick static assert check #ifndef __HLSL_VERSION diff --git a/addons/water/include/oceansystem.h b/addons/water/include/oceansystem.h deleted file mode 100644 index 5fe6bef3..00000000 --- a/addons/water/include/oceansystem.h +++ /dev/null @@ -1,13 +0,0 @@ -#pragma once - -#include "allocator.h" -#include "rendersystem.h" -#include "rendertargetsystem.h" -#include "tbrendercommon.h" -#include "visualloggingsystem.h" - -#include - -#define TB_OCEAN_SYS_PRIO (TB_VLOG_SYS_PRIO + 1) - -ECS_STRUCT_EXTERN(TbOceanSystem, {}); diff --git a/addons/water/include/oceancomponent.h b/addons/water/include/tb_ocean_component.h similarity index 83% rename from addons/water/include/oceancomponent.h rename to addons/water/include/tb_ocean_component.h index 299e1698..64da0199 100644 --- a/addons/water/include/oceancomponent.h +++ b/addons/water/include/tb_ocean_component.h @@ -1,8 +1,8 @@ #pragma once -#include "simd.h" +#include "tb_simd.h" -#include "ocean.hlsli" // Must include simd.h before shader includes +#include "ocean.hlsli" // Must include tb_simd.h before shader includes #include diff --git a/addons/water/include/tb_ocean_system.h b/addons/water/include/tb_ocean_system.h new file mode 100644 index 00000000..0652ccfa --- /dev/null +++ b/addons/water/include/tb_ocean_system.h @@ -0,0 +1,13 @@ +#pragma once + +#include "tb_allocator.h" +#include "tb_render_common.h" +#include "tb_render_system.h" +#include "tb_render_target_system.h" +#include "tb_visual_logging_system.h" + +#include + +#define TB_OCEAN_SYS_PRIO (TB_VLOG_SYS_PRIO + 1) + +ECS_STRUCT_EXTERN(TbOceanSystem, {}); diff --git a/addons/water/source/oceancomponent.c b/addons/water/source/tb_ocean_component.c similarity index 85% rename from addons/water/source/oceancomponent.c rename to addons/water/source/tb_ocean_component.c index 4a10efb5..d520a8bc 100644 --- a/addons/water/source/oceancomponent.c +++ b/addons/water/source/tb_ocean_component.c @@ -1,9 +1,9 @@ -#include "oceancomponent.h" +#include "tb_ocean_component.h" -#include "oceansystem.h" -#include "tbcommon.h" -#include "transformcomponent.h" -#include "world.h" +#include "tb_common.h" +#include "tb_ocean_system.h" +#include "tb_transform_component.h" +#include "tb_world.h" #include #include @@ -100,7 +100,7 @@ TbOceanSample tb_sample_ocean(const TbOceanComponent *ocean, ecs_world_t *ecs, return sample; } -ecs_entity_t tb_register_ocean_comp(TbWorld *world) { +TbComponentRegisterResult tb_register_ocean_comp(TbWorld *world) { ecs_world_t *ecs = world->ecs; ECS_COMPONENT_DEFINE(ecs, TbOceanComponent); ECS_COMPONENT_DEFINE(ecs, TbOceanWave); @@ -126,16 +126,17 @@ ecs_entity_t tb_register_ocean_comp(TbWorld *world) { .type = ecs_vector(ecs, {.type = ecs_id(TbOceanWave)})}, }, }); - return ecs_id(TbOceanDescriptor); + return (TbComponentRegisterResult){ecs_id(TbOceanComponent), + ecs_id(TbOceanDescriptor)}; } -bool tb_load_ocean_comp(TbWorld *world, ecs_entity_t ent, - const char *source_path, const cgltf_node *node, - json_object *json) { +bool tb_load_ocean_comp(ecs_world_t *ecs, ecs_entity_t ent, + const char *source_path, const cgltf_data *data, + const cgltf_node *node, json_object *json) { (void)source_path; + (void)data; (void)node; (void)json; - tb_auto ecs = world->ecs; TbOceanComponent comp = create_ocean_component_internal(); ecs_set_ptr(ecs, ent, TbOceanComponent, &comp); return true; @@ -145,4 +146,9 @@ void tb_destroy_ocean_comp(TbWorld *world, ecs_entity_t ent) { ecs_remove(world->ecs, ent, TbOceanComponent); } +bool tb_ready_ocean_comp(ecs_world_t *ecs, ecs_entity_t ent) { + tb_auto comp = ecs_get(ecs, ent, TbOceanComponent); + return comp != NULL; +} + TB_REGISTER_COMP(tb, ocean) diff --git a/addons/water/source/oceansystem.c b/addons/water/source/tb_ocean_system.c similarity index 93% rename from addons/water/source/oceansystem.c rename to addons/water/source/tb_ocean_system.c index f8967eba..c2e0e623 100644 --- a/addons/water/source/oceansystem.c +++ b/addons/water/source/tb_ocean_system.c @@ -1,26 +1,27 @@ -#include "oceansystem.h" - -#include "assets.h" -#include "audiosystem.h" -#include "cameracomponent.h" -#include "cgltf.h" -#include "lightcomponent.h" -#include "meshsystem.h" +#include "tb_ocean_system.h" + #include "ocean.hlsli" -#include "oceancomponent.h" -#include "profiling.h" -#include "renderpipelinesystem.h" -#include "rendersystem.h" -#include "rendertargetsystem.h" +#include "tb_assets.h" +#include "tb_audio_system.h" +#include "tb_camera_component.h" +#include "tb_common.h" +#include "tb_gltf.h" +#include "tb_light_component.h" +#include "tb_mesh_rnd_sys.h" +#include "tb_mesh_system.h" +#include "tb_ocean_component.h" +#include "tb_profiling.h" +#include "tb_rand.h" +#include "tb_render_pipeline_system.h" +#include "tb_render_system.h" +#include "tb_render_target_system.h" #include "tb_shader_system.h" #include "tb_task_scheduler.h" -#include "tbcommon.h" -#include "tbrand.h" -#include "tbutil.h" -#include "transformcomponent.h" -#include "viewsystem.h" -#include "visualloggingsystem.h" -#include "world.h" +#include "tb_transform_component.h" +#include "tb_util.h" +#include "tb_view_system.h" +#include "tb_visual_logging_system.h" +#include "tb_world.h" #include @@ -65,7 +66,7 @@ typedef struct TbOceanSystem { TbSoundEffectId wave_sounds[TB_OCEAN_SFX_COUNT]; float wave_sound_timer; - TbMeshId ocean_patch_mesh; + TbMesh2 ocean_patch_mesh2; TbTransform ocean_transform; float tile_width; float tile_depth; @@ -73,7 +74,6 @@ typedef struct TbOceanSystem { uint32_t ocean_index_count; uint64_t ocean_pos_offset; uint64_t ocean_uv_offset; - VkBuffer ocean_geom_buffer; VkSampler sampler; VkSampler shadow_sampler; @@ -86,8 +86,8 @@ typedef struct TbOceanSystem { VkDescriptorSetLayout set_layout; VkPipelineLayout pipe_layout; - ecs_entity_t ocean_pass_shader; - ecs_entity_t ocean_prepass_shader; + TbShader ocean_pass_shader; + TbShader ocean_prepass_shader; } TbOceanSystem; ECS_COMPONENT_DECLARE(TbOceanSystem); @@ -189,6 +189,9 @@ VkPipeline create_ocean_prepass_shader(const TbOceanPipelineArgs *args) { VkGraphicsPipelineCreateInfo create_info = { .sType = VK_STRUCTURE_TYPE_GRAPHICS_PIPELINE_CREATE_INFO, +#if TB_USE_DESC_BUFFER == 1 + .flags = VK_PIPELINE_CREATE_DESCRIPTOR_BUFFER_BIT_EXT, +#endif .pNext = &(VkPipelineRenderingCreateInfo){ .sType = VK_STRUCTURE_TYPE_PIPELINE_RENDERING_CREATE_INFO, @@ -321,6 +324,9 @@ VkPipeline create_ocean_pass_shader(const TbOceanPipelineArgs *args) { VkGraphicsPipelineCreateInfo create_info = { .sType = VK_STRUCTURE_TYPE_GRAPHICS_PIPELINE_CREATE_INFO, +#if TB_USE_DESC_BUFFER == 1 + .flags = VK_PIPELINE_CREATE_DESCRIPTOR_BUFFER_BIT_EXT, +#endif .pNext = &(VkPipelineRenderingCreateInfo){ .sType = VK_STRUCTURE_TYPE_PIPELINE_RENDERING_CREATE_INFO, @@ -535,11 +541,11 @@ void init_ocean_system(ecs_world_t *ecs, TbOceanSystem *sys, sys->ocean_index_type == VK_INDEX_TYPE_UINT16 ? 2 : 4, 16); sys->ocean_pos_offset = index_size; - sys->ocean_patch_mesh = - tb_mesh_system_load_mesh(mesh_system, asset_path, &data->nodes[0]); + sys->ocean_patch_mesh2 = + tb_mesh_sys_load_gltf_mesh(ecs, data, asset_path, "ocean", 0); } - cgltf_free(data); + // cgltf_free(data); VkResult err = VK_SUCCESS; @@ -586,6 +592,9 @@ void init_ocean_system(ecs_world_t *ecs, TbOceanSystem *sys, { VkDescriptorSetLayoutCreateInfo create_info = { .sType = VK_STRUCTURE_TYPE_DESCRIPTOR_SET_LAYOUT_CREATE_INFO, +#if TB_USE_DESC_BUFFER == 1 + .flags = VK_DESCRIPTOR_SET_LAYOUT_CREATE_DESCRIPTOR_BUFFER_BIT_EXT, +#endif .bindingCount = 5, .pBindings = (VkDescriptorSetLayoutBinding[5]){ { @@ -634,7 +643,7 @@ void init_ocean_system(ecs_world_t *ecs, TbOceanSystem *sys, .pSetLayouts = (VkDescriptorSetLayout[2]){ sys->set_layout, - sys->view_sys->set_layout, + tb_view_sys_get_set_layout(ecs), }, .pushConstantRangeCount = 1, .pPushConstantRanges = @@ -715,10 +724,6 @@ void init_ocean_system(ecs_world_t *ecs, TbOceanSystem *sys, .draw_fn = ocean_pass_record, .pass_id = color_id, }); - - sys->ocean_geom_buffer = - tb_mesh_system_get_gpu_mesh(mesh_system, sys->ocean_patch_mesh); - TB_CHECK(sys->ocean_geom_buffer, "Failed to get gpu buffer for mesh"); } void destroy_ocean_system(TbOceanSystem *self) { @@ -727,7 +732,6 @@ void destroy_ocean_system(TbOceanSystem *self) { self->wave_sounds[i]); } // tb_audio_system_release_music_ref(self->audio_system, self->music); - tb_mesh_system_release_mesh_ref(self->mesh_system, self->ocean_patch_mesh); for (uint32_t i = 0; i < TB_MAX_FRAME_STATES; ++i) { tb_rnd_destroy_descriptor_pool(self->rnd_sys, @@ -771,13 +775,22 @@ void ocean_draw_tick(ecs_iter_t *it) { tb_auto sys = ecs_field(it, TbOceanSystem, 1); tb_auto cameras = ecs_field(it, TbCameraComponent, 2); - // If system pipelines aren't ready just bail + // If shaders aren't ready just bail if (!tb_is_shader_ready(ecs, sys->ocean_pass_shader) || !tb_is_shader_ready(ecs, sys->ocean_prepass_shader)) { TracyCZoneEnd(ctx); return; } + // If mesh isn't loaded just bail + if (!tb_is_mesh_ready(ecs, sys->ocean_patch_mesh2)) { + TracyCZoneEnd(ctx); + return; + } + + tb_auto rnd_sys = sys->rnd_sys; + tb_auto view_sys = sys->view_sys; + for (int32_t i = 0; i < it->count; ++i) { TbCameraComponent *camera = &cameras[i]; @@ -785,7 +798,12 @@ void ocean_draw_tick(ecs_iter_t *it) { const uint32_t height = camera->height; VkResult err = VK_SUCCESS; - TbRenderSystem *rnd_sys = sys->rnd_sys; + + tb_auto view_set = tb_view_system_get_descriptor(view_sys, camera->view_id); + // Skip camera if view set isn't ready + if (view_set == VK_NULL_HANDLE) { + continue; + } // We want to draw a number of ocean tiles to cover the entire ocean plane // Since only visible ocean tiles need to be drawn we can calculate the @@ -910,8 +928,9 @@ void ocean_draw_tick(ecs_iter_t *it) { for (uint32_t i = 0; i < ocean_count; ++i) { layouts[i] = sys->set_layout; } - err = tb_rnd_frame_desc_pool_tick(rnd_sys, &pool_info, layouts, NULL, - sys->ocean_pools, ocean_count); + err = tb_rnd_frame_desc_pool_tick(rnd_sys, "ocean", &pool_info, + layouts, NULL, sys->ocean_pools, + ocean_count, ocean_count); TB_VK_CHECK(err, "Failed to tick ocean's descriptor pool"); } @@ -1026,9 +1045,6 @@ void ocean_draw_tick(ecs_iter_t *it) { tb_auto prepass_draw_batches = tb_alloc_nm_tp(sys->tmp_alloc, batch_max, TbDrawBatch); - tb_auto view_set = - tb_view_system_get_descriptor(sys->view_sys, camera->view_id); - for (uint32_t ocean_idx = 0; ocean_idx < ocean_count; ++ocean_idx) { tb_auto ocean_set = tb_rnd_frame_desc_pool_get_set( sys->rnd_sys, sys->ocean_pools, ocean_idx); @@ -1055,7 +1071,8 @@ void ocean_draw_tick(ecs_iter_t *it) { .inst_buffer = tb_rnd_get_gpu_tmp_buffer(sys->rnd_sys), .inst_offset = tile_offset, .inst_count = visible_tile_count, - .geom_buffer = sys->ocean_geom_buffer, + .geom_buffer = + tb_mesh_sys_get_gpu_mesh(ecs, sys->ocean_patch_mesh2), .index_type = (VkIndexType)sys->ocean_index_type, .index_count = sys->ocean_index_count, .pos_offset = sys->ocean_pos_offset, @@ -1114,12 +1131,26 @@ void tb_register_ocean_sys(TbWorld *world) { }; ecs_singleton_set_ptr(ecs, TbOceanSystem, &sys); - ECS_SYSTEM(ecs, ocean_on_start, EcsOnStart, TbRenderSystem(TbRenderSystem), - TbRenderPipelineSystem(TbRenderPipelineSystem), - TbMeshSystem(TbMeshSystem), TbViewSystem(TbViewSystem), - TbRenderTargetSystem(TbRenderTargetSystem), - TbVisualLoggingSystem(TbVisualLoggingSystem), - TbAudioSystem(TbAudioSystem)); + // ocean_on_start must be no_readonly because it enqueues a mesh load request + ecs_system( + ecs, + {.entity = ecs_entity( + ecs, {.name = "ocean_on_start", .add = {ecs_dependson(EcsOnStart)}}), + .query.filter.terms = + { + {.id = ecs_id(TbRenderSystem), .src.id = ecs_id(TbRenderSystem)}, + {.id = ecs_id(TbRenderPipelineSystem), + .src.id = ecs_id(TbRenderPipelineSystem)}, + {.id = ecs_id(TbMeshSystem), .src.id = ecs_id(TbMeshSystem)}, + {.id = ecs_id(TbViewSystem), .src.id = ecs_id(TbViewSystem)}, + {.id = ecs_id(TbRenderTargetSystem), + .src.id = ecs_id(TbRenderTargetSystem)}, + {.id = ecs_id(TbVisualLoggingSystem), + .src.id = ecs_id(TbVisualLoggingSystem)}, + {.id = ecs_id(TbAudioSystem), .src.id = ecs_id(TbAudioSystem)}, + }, + .callback = ocean_on_start, + .no_readonly = true}); ECS_SYSTEM(ecs, ocean_audio_tick, EcsOnUpdate, TbOceanSystem(TbOceanSystem), TbOceanComponent); diff --git a/include/common.hlsli b/include/common.hlsli index 585df9ac..03c2c006 100644 --- a/include/common.hlsli +++ b/include/common.hlsli @@ -7,28 +7,25 @@ #define _Static_assert static_assert #endif -#include "pi.h" -#include "simd.h" +#include "tb_pi.h" +#include "tb_simd.h" #define PUSH_CONSTANT_BYTES 128 typedef struct TB_GPU_STRUCT TbSkyPushConstants { float4x4 vp; -} -TbSkyPushConstants; +} TbSkyPushConstants; typedef struct TB_GPU_STRUCT TbEnvFilterConstants { float roughness; uint32_t sample_count; -} -TbEnvFilterConstants; +} TbEnvFilterConstants; typedef struct TB_GPU_STRUCT TbPrimitivePushConstants { float3 position; float3 scale; float4 color; -} -TbPrimitivePushConstants; +} TbPrimitivePushConstants; // Constant per-view Camera Data typedef struct TB_GPU_STRUCT TbCommonViewData { @@ -39,8 +36,7 @@ typedef struct TB_GPU_STRUCT TbCommonViewData { float4x4 inv_proj; float3 view_pos; float4 proj_params; -} -TbCommonViewData; +} TbCommonViewData; // Constant per-view Light Data #define TB_CASCADE_COUNT 4 @@ -49,20 +45,18 @@ typedef struct TB_GPU_STRUCT TbCommonLightData { float3 light_dir; float4 cascade_splits; float4x4 cascade_vps[TB_CASCADE_COUNT]; -} -TbCommonLightData; +} TbCommonLightData; // Per-instance object data typedef struct TB_GPU_STRUCT TbCommonObjectData { float4x4 m; -} -TbCommonObjectData; +} TbCommonObjectData; // Macros for declaring access to common toybox descriptor sets // that represent global loaded resource tables #define TB_TEXTURE_SET(space) Texture2D gltf_textures[] : register(t0, space); #define TB_OBJECT_SET(space) \ - StructuredBuffer object_data[] : register(t0, space); + StructuredBuffer object_data : register(t0, space); #define TB_IDX_SET(space) RWBuffer idx_buffers[] : register(u0, space); #define TB_POS_SET(space) RWBuffer pos_buffers[] : register(u0, space); #define TB_NORM_SET(space) \ @@ -88,8 +82,8 @@ TbCommonObjectData; #ifdef __HLSL_VERSION TbCommonObjectData -tb_get_obj_data(int32_t obj, StructuredBuffer buffers[]) { - return buffers[NonUniformResourceIndex(obj)][0]; +tb_get_obj_data(int32_t obj, StructuredBuffer buffer) { + return buffer[NonUniformResourceIndex(obj)]; } Texture2D tb_get_texture(int32_t tex, Texture2D textures[]) { diff --git a/include/cube_view_lut.hlsli b/include/cube_view_lut.hlsli index e1f8a96c..00b6a942 100644 --- a/include/cube_view_lut.hlsli +++ b/include/cube_view_lut.hlsli @@ -1,6 +1,6 @@ #pragma once -#include "simd.h" +#include "tb_simd.h" // Per view matrix look up table // So that each view is pointing at the right face of the cubemap @@ -27,13 +27,14 @@ for (uint32_t dir_idx = 0; dir_idx < DIR_COUNT; ++dir_idx) { float4x4 proj = perspective(TB_PI_2, 1.0, 0.001, 1.0f); - float4x4 view = look_forward((float3){0}, directions[dir_idx], ups[dir_idx]); - matrices[dir_idx] = mulmf44(proj, view); - + float4x4 view = look_forward((float3){0}, directions[dir_idx], +ups[dir_idx]); matrices[dir_idx] = mulmf44(proj, view); + } // Examine matrices with a print or debugger; // e.g. print(transpose_mf44(matrices[dir_idx])) - // Note that the transpose is only for display purposes when writing into HLSL. + // Note that the transpose is only for display purposes when writing into +HLSL. // If you were to want to use these matrices at runtime you do not need // to transpose #undef DIR_COUNT @@ -44,33 +45,15 @@ */ static const column_major float4x4 view_proj_lut[6] = { // X+ - {1, 0, 0, 0, - 0, 1, 0, 0, - 0, 0, -1.001001, -0.00100100099, - 0, 0, -1, 0}, + {1, 0, 0, 0, 0, 1, 0, 0, 0, 0, -1.001001, -0.00100100099, 0, 0, -1, 0}, // X- - {-1, 0, 0, 0, - 0, 1, 0, 0, - 0, 0, 1.001001, -0.00100100099, - 0, 0, 1, 0}, + {-1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1.001001, -0.00100100099, 0, 0, 1, 0}, // Y+ - {0, 0, -1, 0, - 1, 0, 0, 0, - 0, 1.001001, 0, -0.00100100099, - 0, 1, 0, 0}, + {0, 0, -1, 0, 1, 0, 0, 0, 0, 1.001001, 0, -0.00100100099, 0, 1, 0, 0}, // Y- - {0, 0, -1, 0, - -1, 0, 0, 0, - 0, -1.001001, 0, -0.00100100099, - 0, -1, 0, 0}, - // Z+ - {0, 0, -1, 0, - 0, 1, 0, 0, - -1.001001, 0, 0, -0.00100100099, - -1, 0, 0, 0}, + {0, 0, -1, 0, -1, 0, 0, 0, 0, -1.001001, 0, -0.00100100099, 0, -1, 0, 0}, + // Z+ + {0, 0, -1, 0, 0, 1, 0, 0, -1.001001, 0, 0, -0.00100100099, -1, 0, 0, 0}, // Z- - {0, 0, 1, 0, - 0, 1, 0, 0, - 1.00010001, 0, 0, -0.00100100099, - 1, 0, 0, 0}, + {0, 0, 1, 0, 0, 1, 0, 0, 1.00010001, 0, 0, -0.00100100099, 1, 0, 0, 0}, }; diff --git a/include/imgui.hlsli b/include/imgui.hlsli index dc4cd896..f0b76da5 100644 --- a/include/imgui.hlsli +++ b/include/imgui.hlsli @@ -1,6 +1,6 @@ #pragma once -#include "simd.h" +#include "tb_simd.h" typedef struct TB_GPU_STRUCT TbImGuiPushConstants { float2 scale; diff --git a/include/loadthread.h b/include/loadthread.h deleted file mode 100644 index e6c23c6d..00000000 --- a/include/loadthread.h +++ /dev/null @@ -1,51 +0,0 @@ -#pragma once - -/* - Beginning of a sketch for an idea of how to implement async loading. - - World is just a database that we fill by loading a glb and converting nodes to - entities - So loading could look like: - Frame 0: Main thread gathers load requests - Frame 1: Loading thread is signaled by main thread to load glbs & begins load - Frame 2: Main thread continues; loading thread may occasionally update - completion until frame A - Frame A: Loading thread has created a new World table that can be appended to - the main world database. Main thread takes time to append loading thread's - payload onto the World and call any relevant callbacks for the newly created - objects - - Downside, one of the more expensive parts of loading a scene is making calls - to things like component's create and on_loaded function pointers. However - doing this work on the main thread means that it naturally syncs with work - that create or on_loaded might create for the render thread. Also it is - possible to update the world in chunks per frame, say only 5 entities are - added per frame to spread the load over time without sacrificing total frame - time. -*/ - -#include "allocator.h" - -typedef struct SDL_Thread SDL_Thread; -typedef struct SDL_Semaphore SDL_Semaphore; -typedef struct SDL_mutex SDL_mutex; - -typedef struct TbWorld TbWorld; - -typedef struct TbLoadThread { - SDL_Thread *thread; - - TbGeneralAllocator gp_alloc; - TbArenaAllocator loading_arena; -} TbLoadThread; - -// Copy the request list and tell the load thread to begin loading -void tb_signal_begin_load(TbLoadThread *thread, char const *const *scene_paths, - uint32_t scene_count); - -// Check to see if loading is complete -bool tb_load_complete(TbLoadThread *thread, float *percent); - -// Returns a pointer to a const World* that points to a chunk of loadthread -// owned memory that the main thread needs to copy and then decide how to append -void tb_on_world_loaded(TbLoadThread *thread, TbWorld const **world); diff --git a/include/logsystem.h b/include/logsystem.h deleted file mode 100644 index ec68d3be..00000000 --- a/include/logsystem.h +++ /dev/null @@ -1,32 +0,0 @@ -#pragma once - -/* - An optional logging system that can be enabled if you want to display - log messages through ImGui -*/ -#include "coreuisystem.h" -#include "dynarray.h" -#include "tblog.h" - -#include - -#define TB_LOG_SYS_PRIO (TB_COREUI_SYS_PRIO + 1) - -typedef struct TbWorld TbWorld; - -typedef struct TbLogMessage { - float time; - int32_t category; - SDL_LogPriority priority; - char *message; -} TbLogMessage; - -typedef struct TbLogSystem { - bool *ui; - bool enabled; - bool autoscroll; - void *orig_log_fn; - void *orig_userdata; - TB_DYN_ARR_OF(TbLogMessage) messages; -} TbLogSystem; -extern ECS_COMPONENT_DECLARE(TbLogSystem); diff --git a/include/materialsystem.h b/include/materialsystem.h deleted file mode 100644 index 405fd767..00000000 --- a/include/materialsystem.h +++ /dev/null @@ -1,59 +0,0 @@ -#pragma once - -#include "SDL3/SDL_stdinc.h" -#include "allocator.h" -#include "dynarray.h" -#include "rendersystem.h" -#include "tbcommon.h" -#include "tbrendercommon.h" -#include "texturesystem.h" - -#define TB_MAT_SYS_PRIO (TB_TEX_SYS_PRIO + 1) - -typedef struct cgltf_material cgltf_material; -typedef struct TbWorld TbWorld; -typedef struct VkSampler_T *VkSampler; -typedef struct VkDescriptorSetLayout_T *VkDescriptorSetLayout; -typedef struct TbRenderSystem TbRenderSystem; -typedef struct TbTextureSystem TbTextureSystem; -typedef struct TbMaterial TbMaterial; -typedef TbResourceId TbMaterialId; -typedef uint64_t TbTextureId; -typedef uint32_t TbMaterialPerm; - -static const TbMaterialId InvalidMaterialId = {SDL_MAX_UINT64, SDL_MAX_UINT32}; - -typedef struct TbMaterialSystem { - TbAllocator gp_alloc; - TbAllocator tmp_alloc; - - TbRenderSystem *rnd_sys; - TbTextureSystem *texture_system; - - VkSampler sampler; // Immutable sampler for material descriptor sets - VkSampler shadow_sampler; // Immutable sampler for sampling shadow maps - VkDescriptorSetLayout set_layout; - - const cgltf_material *default_material; - - // These two arrays are to be kept in sync - TB_DYN_ARR_OF(TbMaterial) materials; - - // Descriptor pool that owns descriptor arrays of material buffer data, color, - // normal and pbr textures - TbDescriptorPool mat_pool; -} TbMaterialSystem; -extern ECS_COMPONENT_DECLARE(TbMaterialSystem); - -VkDescriptorSetLayout tb_mat_system_get_set_layout(TbMaterialSystem *self); - -TbMaterialId tb_mat_system_load_material(TbMaterialSystem *self, - const char *path, - const cgltf_material *material); - -TbMaterialPerm tb_mat_system_get_perm(TbMaterialSystem *self, TbMaterialId mat); - -VkDescriptorSet tb_mat_system_get_set(TbMaterialSystem *self); - -void tb_mat_system_release_material_ref(TbMaterialSystem *self, - TbMaterialId mat); diff --git a/include/meshcomponent.h b/include/meshcomponent.h deleted file mode 100644 index 247c7b99..00000000 --- a/include/meshcomponent.h +++ /dev/null @@ -1,32 +0,0 @@ -#pragma once - -#include "dynarray.h" -#include "simd.h" -#include "tbrendercommon.h" - -#include - -typedef struct TbWorld TbWorld; - -#define TB_VERTEX_BINDING_MAX 4 - -typedef TbResourceId TbMeshId; -typedef TbResourceId TbMaterialId; -typedef uint64_t ecs_entity_t; - -typedef struct TbSubMesh { - uint32_t index_type; - uint32_t index_count; - uint64_t index_offset; - uint64_t vertex_offset; - uint32_t vertex_count; - uint32_t vertex_perm; - TbMaterialId material; -} TbSubMesh; - -typedef struct TbMeshComponent { - TbMeshId mesh_id; - TB_DYN_ARR_OF(TbSubMesh) submeshes; - TbAABB local_aabb; -} TbMeshComponent; -extern ECS_COMPONENT_DECLARE(TbMeshComponent); diff --git a/include/noclipcontrollersystem.h b/include/noclipcontrollersystem.h deleted file mode 100644 index bbd8ab43..00000000 --- a/include/noclipcontrollersystem.h +++ /dev/null @@ -1,5 +0,0 @@ -#pragma once - -#include "tbsystempriority.h" - -#define TB_NOCLIP_SYS_PRIO TB_SYSTEM_NORMAL diff --git a/include/scene.h b/include/scene.h deleted file mode 100644 index 7bbdc2b2..00000000 --- a/include/scene.h +++ /dev/null @@ -1,11 +0,0 @@ -#pragma once - -#include - -#include "dynarray.h" - -typedef uint64_t ecs_entity_t; - -typedef struct TbScene { - TB_DYN_ARR_OF(ecs_entity_t) entities; -} TbScene; diff --git a/include/shadowsystem.h b/include/shadowsystem.h deleted file mode 100644 index 34614501..00000000 --- a/include/shadowsystem.h +++ /dev/null @@ -1,16 +0,0 @@ -#pragma once - -#include "allocator.h" -#include "common.hlsli" -#include "rendersystem.h" -#include "tbsystempriority.h" - -#include - -#define TB_SHADOW_SYS_PRIO TB_SYSTEM_HIGH - -typedef struct TbWorld TbWorld; -typedef uint32_t TbDrawContextId; - -typedef struct TbShadowSystem TbShadowSystem; -extern ECS_COMPONENT_DECLARE(TbShadowSystem); diff --git a/include/allocator.h b/include/tb_allocator.h similarity index 93% rename from include/allocator.h rename to include/tb_allocator.h index 24da40a6..94e6e2ee 100644 --- a/include/allocator.h +++ b/include/tb_allocator.h @@ -45,6 +45,11 @@ typedef struct TbAllocator { extern TbAllocator tb_global_alloc; +// thread_local macro from threads.h is not as reliable as this +#pragma clang diagnostic push +#pragma clang diagnostic ignored "-Wc11-extensions" +extern _Thread_local TbAllocator tb_thread_alloc; + typedef struct TbGeneralAllocator { mi_heap_t *heap; TbAllocator alloc; diff --git a/include/assets.h b/include/tb_assets.h similarity index 88% rename from include/assets.h rename to include/tb_assets.h index b02c87b3..a42934f3 100644 --- a/include/assets.h +++ b/include/tb_assets.h @@ -1,6 +1,6 @@ #pragma once -#include "allocator.h" +#include "tb_allocator.h" typedef struct cgltf_data cgltf_data; diff --git a/include/audiosystem.h b/include/tb_audio_system.h similarity index 92% rename from include/audiosystem.h rename to include/tb_audio_system.h index 09bb6706..923e2fae 100644 --- a/include/audiosystem.h +++ b/include/tb_audio_system.h @@ -1,8 +1,8 @@ #pragma once -#include "allocator.h" -#include "dynarray.h" -#include "tbsystempriority.h" +#include "tb_allocator.h" +#include "tb_dynarray.h" +#include "tb_system_priority.h" #include diff --git a/include/bloom.h b/include/tb_bloom.h similarity index 98% rename from include/bloom.h rename to include/tb_bloom.h index dd2d4e4f..482082af 100644 --- a/include/bloom.h +++ b/include/tb_bloom.h @@ -6,7 +6,7 @@ #ifndef __HLSL_VERSION -#include "tbrendercommon.h" +#include "tb_render_common.h" typedef uint64_t ecs_entity_t; typedef struct ecs_world_t ecs_world_t; diff --git a/include/cameracomponent.h b/include/tb_camera_component.h similarity index 95% rename from include/cameracomponent.h rename to include/tb_camera_component.h index 1325600e..9ba5d50e 100644 --- a/include/cameracomponent.h +++ b/include/tb_camera_component.h @@ -2,7 +2,7 @@ // TODO: Physically based camera? -#include "simd.h" +#include "tb_simd.h" #include diff --git a/include/camerasystem.h b/include/tb_camera_system.h similarity index 64% rename from include/camerasystem.h rename to include/tb_camera_system.h index 02250c37..432e494d 100644 --- a/include/camerasystem.h +++ b/include/tb_camera_system.h @@ -1,5 +1,5 @@ #pragma once -#include "tbsystempriority.h" +#include "tb_system_priority.h" #define TB_CAMERA_SYS_PRIO TB_SYSTEM_NORMAL diff --git a/include/tbcommon.h b/include/tb_common.h similarity index 60% rename from include/tbcommon.h rename to include/tb_common.h index 8c432e4d..d0eee27c 100644 --- a/include/tbcommon.h +++ b/include/tb_common.h @@ -1,13 +1,28 @@ #pragma once -#include "tbsdl.h" +#include "tb_sdl.h" -#include "allocator.h" -#include "profiling.h" -#include "settings.h" -#include "tblog.h" -#include "shadercommon.h" -#include "simd.h" +#include "tb_allocator.h" +#include "tb_engine_config.h" +#include "tb_log.h" +#include "tb_profiling.h" +#include "tb_settings.h" +#include "tb_shader_common.h" +#include "tb_simd.h" + +// Manually definine preprocessor macros to reduce windows header includes +// https://aras-p.info/blog/2018/01/12/Minimizing-windows.h/ +#if TB_WINDOWS == 1 +#define WIN32_LEAN_AND_MEAN +#if TB_X64 == 1 +#define _AMD64_ +#elif TB_ARM64 = 1 +#define _ARM_ +#endif +#endif + +// Flecs uses '$' in identifiers as part of a DSL so this gets in the way +#pragma clang diagnostic ignored "-Wdollar-in-identifier-extension" // Leaning in to clang language extensions // __auto_type is really cool @@ -16,19 +31,16 @@ #define TB_CHECK(expr, message) \ if (!(expr)) { \ - TB_LOG_CRITICAL(SDL_LOG_CATEGORY_APPLICATION, "%s", (message)); \ + TB_LOG_CRITICAL(SDL_LOG_CATEGORY_APPLICATION, "%s", (message)); \ SDL_TriggerBreakpoint(); \ } #define TB_CHECK_RETURN(expr, message, ret) \ if (!(expr)) { \ - TB_LOG_CRITICAL(SDL_LOG_CATEGORY_APPLICATION, "%s", (message)); \ + TB_LOG_CRITICAL(SDL_LOG_CATEGORY_APPLICATION, "%s", (message)); \ SDL_TriggerBreakpoint(); \ return (ret); \ } -#define TB_COPY(dst, src, count, type) \ - SDL_memcpy((dst), (src), sizeof(type) * count) - #ifdef __ANDROID__ #define ASSET_PREFIX "" #else diff --git a/include/coreuisystem.h b/include/tb_coreui_system.h similarity index 88% rename from include/coreuisystem.h rename to include/tb_coreui_system.h index c7cdb7e3..25edabc9 100644 --- a/include/coreuisystem.h +++ b/include/tb_coreui_system.h @@ -1,8 +1,8 @@ #pragma once -#include "dynarray.h" -#include "imguisystem.h" -#include "tbcommon.h" +#include "tb_common.h" +#include "tb_dynarray.h" +#include "tb_imgui_system.h" #include #define TB_COREUI_SYS_PRIO (TB_IMGUI_SYS_PRIO + 1) diff --git a/include/tb_descriptor_buffer.h b/include/tb_descriptor_buffer.h new file mode 100644 index 00000000..c9c40bdc --- /dev/null +++ b/include/tb_descriptor_buffer.h @@ -0,0 +1,49 @@ +#pragma once + +#include "tb_render_common.h" +#include "tb_vk.h" + +typedef struct TbRenderSystem TbRenderSystem; + +typedef struct TbDescriptorBuffer { + VkDescriptorSetLayout layout; + VkDeviceSize layout_size; + uint32_t desc_count; + uint32_t desc_cap; + TB_DYN_ARR_OF(uint32_t) free_list; + TbBuffer buffer; + TbHostBuffer host; + uint8_t *data_ptr; +#ifndef FINAL + const char *name; +#endif +} TbDescriptorBuffer; + +typedef struct TbDescriptor { + VkDescriptorType type; + VkDescriptorDataEXT data; +} TbDescriptor; + +VkResult tb_create_descriptor_buffer(TbRenderSystem *rnd_sys, + VkDescriptorSetLayout layout, + const char *name, uint32_t capacity, + TbDescriptorBuffer *out_buf); + +void tb_destroy_descriptor_buffer(TbRenderSystem *rnd_sys, + TbDescriptorBuffer *buf); + +VkDescriptorBufferBindingInfoEXT +tb_desc_buff_get_binding(const TbDescriptorBuffer *desc_buf); + +// Returns the index of the descriptor in the buffer +uint32_t tb_write_desc_to_buffer(TbRenderSystem *rnd_sys, + TbDescriptorBuffer *desc_buf, uint32_t binding, + const TbDescriptor *desc); + +// For freeing an individual descriptor from the buffer +void tb_free_desc_from_buffer(TbDescriptorBuffer *desc_buf, uint32_t idx); + +// Resets the internal free-list and invalidates all previously written +// descriptors +void tb_reset_descriptor_buffer(TbRenderSystem *rnd_sys, + TbDescriptorBuffer *desc_buf); diff --git a/include/tb_dyn_desc_pool.h b/include/tb_dyn_desc_pool.h new file mode 100644 index 00000000..a919bfdf --- /dev/null +++ b/include/tb_dyn_desc_pool.h @@ -0,0 +1,46 @@ +#pragma once + +#include "tb_free_list.h" +#include "tb_render_common.h" +#include "tb_render_system.h" + +#define TB_DESC_POOL_CAP 4096 + +typedef union TbDynDesc { + VkDescriptorImageInfo image; + VkDescriptorBufferInfo buffer; + VkBufferView texel_buffer; +} TbDynDesc; + +typedef struct TbDynDescWrite { + VkDescriptorType type; + TbDynDesc desc; +} TbDynDescWrite; + +// A descriptor pool that contains a single set with a single type of resizable +// descriptor +typedef struct TbDynDescPool { + + uint32_t desc_cap; + uint32_t binding; + VkDescriptorSetLayout layout; + VkDescriptorType type; + VkDescriptorPool pools[TB_MAX_FRAME_STATES]; + VkDescriptorSet sets[TB_MAX_FRAME_STATES]; + TbFreeList free_list; + TB_QUEUE_OF(VkWriteDescriptorSet) write_queues[TB_MAX_FRAME_STATES]; +} TbDynDescPool; + +void tb_create_dyn_desc_pool(TbRenderSystem *rnd_sys, const char *name, + VkDescriptorSetLayout layout, + VkDescriptorType type, uint32_t desc_cap, + TbDynDescPool *pool, uint32_t binding); + +bool tb_write_dyn_desc_pool(TbDynDescPool *pool, uint32_t write_count, + const TbDynDescWrite *writes, uint32_t *out_idxs); + +// Call this once per frame after you've issues any relevant writes +void tb_tick_dyn_desc_pool(TbRenderSystem *rnd_sys, TbDynDescPool *pool); + +VkDescriptorSet tb_dyn_desc_pool_get_set(TbRenderSystem *rnd_sys, + const TbDynDescPool *pool); diff --git a/include/dynarray.h b/include/tb_dynarray.h similarity index 99% rename from include/dynarray.h rename to include/tb_dynarray.h index 5f770b56..560d817d 100644 --- a/include/dynarray.h +++ b/include/tb_dynarray.h @@ -5,7 +5,7 @@ #include -#include "allocator.h" +#include "tb_allocator.h" #define TB_DYN_ARR_OF(type) \ struct { \ diff --git a/include/tb_free_list.h b/include/tb_free_list.h new file mode 100644 index 00000000..a0bf31d3 --- /dev/null +++ b/include/tb_free_list.h @@ -0,0 +1,16 @@ +#pragma once + +#include "tb_dynarray.h" + +typedef TB_DYN_ARR_OF(uint32_t) TbFreeList; + +void tb_reset_free_list(TbAllocator alloc, TbFreeList *free_list, + uint32_t capacity); + +// Returns true if the index was properly retrieved +// False if the free list was exhausted +bool tb_pull_index(TbFreeList *free_list, uint32_t *out_idx); + +void tb_return_index(TbFreeList *free_list, uint32_t idx); + +void tb_destroy_free_list(TbFreeList *free_list); diff --git a/include/fxaa.h b/include/tb_fxaa.h similarity index 91% rename from include/fxaa.h rename to include/tb_fxaa.h index a5457a66..22a8566d 100644 --- a/include/fxaa.h +++ b/include/tb_fxaa.h @@ -1,8 +1,8 @@ #pragma once #include "fxaa.hlsli" -#include "renderpipelinesystem.h" -#include "tbrendercommon.h" +#include "tb_render_common.h" +#include "tb_render_pipeline_system.h" #include diff --git a/include/tbgltf.h b/include/tb_gltf.h similarity index 68% rename from include/tbgltf.h rename to include/tb_gltf.h index a9176d4a..b88df7e8 100644 --- a/include/tbgltf.h +++ b/include/tb_gltf.h @@ -13,3 +13,15 @@ #include "cgltf.h" #pragma clang diagnostic pop + +// Toybox specific helpers +#include "tb_allocator.h" + +#ifdef __cplusplus +extern "C" { +#endif +cgltf_result tb_decompress_buffer_view(TbAllocator alloc, + cgltf_buffer_view *view); +#ifdef __cplusplus +} +#endif diff --git a/include/hash.h b/include/tb_hash.h similarity index 100% rename from include/hash.h rename to include/tb_hash.h diff --git a/include/tbimgui.h b/include/tb_imgui.h similarity index 100% rename from include/tbimgui.h rename to include/tb_imgui.h diff --git a/include/imguisystem.h b/include/tb_imgui_system.h similarity index 91% rename from include/imguisystem.h rename to include/tb_imgui_system.h index 3b825d70..63e1e09b 100644 --- a/include/imguisystem.h +++ b/include/tb_imgui_system.h @@ -1,9 +1,9 @@ #pragma once -#include "renderpipelinesystem.h" -#include "tbcommon.h" -#include "tbrendercommon.h" -#include "world.h" +#include "tb_common.h" +#include "tb_render_common.h" +#include "tb_render_pipeline_system.h" +#include "tb_world.h" #include diff --git a/include/inputsystem.h b/include/tb_input_system.h similarity index 95% rename from include/inputsystem.h rename to include/tb_input_system.h index 89c49a14..86edb707 100644 --- a/include/inputsystem.h +++ b/include/tb_input_system.h @@ -1,9 +1,9 @@ #pragma once -#include "allocator.h" -#include "simd.h" -#include "tbsystempriority.h" -#include "world.h" +#include "tb_allocator.h" +#include "tb_simd.h" +#include "tb_system_priority.h" +#include "tb_world.h" #include #include diff --git a/include/tbktx.h b/include/tb_ktx.h similarity index 100% rename from include/tbktx.h rename to include/tb_ktx.h diff --git a/include/lightcomponent.h b/include/tb_light_component.h similarity index 85% rename from include/lightcomponent.h rename to include/tb_light_component.h index 5277dd7e..b7e593f7 100644 --- a/include/lightcomponent.h +++ b/include/tb_light_component.h @@ -1,8 +1,8 @@ #pragma once #include "common.hlsli" -#include "rendertargetsystem.h" -#include "simd.h" +#include "tb_render_target_system.h" +#include "tb_simd.h" #include diff --git a/include/lightsystem.h b/include/tb_light_system.h similarity index 75% rename from include/lightsystem.h rename to include/tb_light_system.h index 375ccbf4..bc14e812 100644 --- a/include/lightsystem.h +++ b/include/tb_light_system.h @@ -1,7 +1,7 @@ #pragma once -#include "lightcomponent.h" -#include "tbsystempriority.h" +#include "tb_light_component.h" +#include "tb_system_priority.h" #include diff --git a/include/tblog.h b/include/tb_log.h similarity index 100% rename from include/tblog.h rename to include/tb_log.h diff --git a/include/luminance.h b/include/tb_luminance.h similarity index 98% rename from include/luminance.h rename to include/tb_luminance.h index 5b08262d..39ee912a 100644 --- a/include/luminance.h +++ b/include/tb_luminance.h @@ -10,7 +10,7 @@ typedef struct TB_GPU_STRUCT TbLuminancePushConstants { _Static_assert(sizeof(TbLuminancePushConstants) <= PUSH_CONSTANT_BYTES, "Too Many Push Constants"); -#include "tbrendercommon.h" +#include "tb_render_common.h" typedef uint64_t ecs_entity_t; typedef struct ecs_world_t ecs_world_t; diff --git a/include/tb_material_system.h b/include/tb_material_system.h new file mode 100644 index 00000000..ee3da0ea --- /dev/null +++ b/include/tb_material_system.h @@ -0,0 +1,72 @@ +#pragma once + +#include "tb_render_system.h" + +#include + +// HACK: +2 because this needs to be after the texture system +#define TB_MAT_SYS_PRIO (TB_RND_SYS_PRIO + 2) + +typedef ecs_entity_t TbMaterial; // Entities can be handles to materials +typedef uint32_t TbMaterialPerm; +typedef struct cgltf_data cgltf_data; +typedef struct cgltf_material cgltf_material; + +// Material usage maps a material to the expected shader layout and usage +// Similar to unreal's Material Domain concept +typedef enum TbMaterialUsage { + TB_MAT_USAGE_UNKNOWN = 0, + TB_MAT_USAGE_SCENE, + TB_MAT_USAGE_EFFECTS, + TB_MAT_USAGE_POSTPROCESSING, + TB_MAT_USAGE_CUSTOM, +} TbMaterialUsage; +extern ECS_COMPONENT_DECLARE(TbMaterialUsage); + +typedef struct TbMaterialData { + TbBuffer gpu_buffer; + void *domain_data; +} TbMaterialData; +extern ECS_COMPONENT_DECLARE(TbMaterialData); + +typedef uint32_t TbMaterialComponent; +extern ECS_COMPONENT_DECLARE(TbMaterialComponent); + +// A function that parses a material asset and fills out a pointer to a block +// of memory that represents that material +typedef bool TbMatParseFn(const cgltf_data *gltf_data, const char *name, + const cgltf_material *material, void **out_mat_data); +typedef void TbMatOnLoadFn(ecs_world_t *ecs, void *mat_data); +typedef bool TbMatIsReadyFn(ecs_world_t *ecs, const TbMaterialData *data); +typedef void *TbMatGetDataFn(ecs_world_t *ecs, const TbMaterialData *data); +typedef size_t TbMatGetSizeFn(void); +typedef bool TbMatIsTransparent(const TbMaterialData *data); + +typedef struct TbMaterialDomain { + TbMatParseFn *parse_fn; + TbMatOnLoadFn *load_fn; + TbMatIsReadyFn *ready_fn; + TbMatGetDataFn *get_data_fn; + TbMatGetSizeFn *get_size_fn; + TbMatIsTransparent *is_trans_fn; +} TbMaterialDomain; + +bool tb_register_mat_usage(ecs_world_t *ecs, const char *domain_name, + TbMaterialUsage usage, TbMaterialDomain domain, + void *default_data, size_t size); + +VkDescriptorSetLayout tb_mat_sys_get_set_layout(ecs_world_t *ecs); + +VkDescriptorSet tb_mat_sys_get_set(ecs_world_t *ecs); + +VkDescriptorBufferBindingInfoEXT tb_mat_sys_get_table_addr(ecs_world_t *ecs); + +TbMaterial tb_mat_sys_load_gltf_mat(ecs_world_t *ecs, const cgltf_data *data, + const char *name, TbMaterialUsage usage); + +// Returns true if the material is ready to be used +bool tb_is_material_ready(ecs_world_t *ecs, TbMaterial mat_ent); + +bool tb_is_mat_transparent(ecs_world_t *ecs, TbMaterial mat_ent); + +TbMaterial tb_get_default_mat(ecs_world_t *ecs, TbMaterialUsage usage); diff --git a/include/tb_mesh_component.h b/include/tb_mesh_component.h new file mode 100644 index 00000000..6582e9d5 --- /dev/null +++ b/include/tb_mesh_component.h @@ -0,0 +1,10 @@ +#pragma once + +#include + +typedef ecs_entity_t TbMesh2; + +typedef struct TbMeshComponent { + TbMesh2 mesh2; +} TbMeshComponent; +extern ECS_COMPONENT_DECLARE(TbMeshComponent); diff --git a/include/meshsystem.h b/include/tb_mesh_rnd_sys.h similarity index 73% rename from include/meshsystem.h rename to include/tb_mesh_rnd_sys.h index 626a2d6e..0ffede96 100644 --- a/include/meshsystem.h +++ b/include/tb_mesh_rnd_sys.h @@ -1,19 +1,21 @@ #pragma once #include "SDL3/SDL_stdinc.h" -#include "allocator.h" -#include "dynarray.h" #include "gltf.hlsli" -#include "meshcomponent.h" -#include "rendersystem.h" -#include "rendertargetsystem.h" -#include "tbcommon.h" -#include "tbrendercommon.h" -#include "viewsystem.h" +#include "tb_allocator.h" +#include "tb_common.h" +#include "tb_dynarray.h" +#include "tb_mesh_component.h" +#include "tb_render_common.h" +#include "tb_render_system.h" +#include "tb_render_target_system.h" +#include "tb_view_system.h" #include +#ifndef TB_MESH_SYS_PRIO #define TB_MESH_SYS_PRIO (TB_RP_SYS_PRIO + 1) +#endif typedef struct TbRenderSystem TbRenderSystem; typedef struct TbMaterialSystem TbMaterialSystem; @@ -50,6 +52,18 @@ typedef struct TbIndirectDraw { } TbIndirectDraw; typedef struct TbPrimitiveBatch { +#if TB_USE_DESC_BUFFER == 1 + VkDescriptorBufferBindingInfoEXT view_addr; + VkDescriptorBufferBindingInfoEXT mat_addr; + VkDescriptorBufferBindingInfoEXT draw_addr; + VkDescriptorBufferBindingInfoEXT obj_addr; + VkDescriptorBufferBindingInfoEXT tex_addr; + VkDescriptorBufferBindingInfoEXT idx_addr; + VkDescriptorBufferBindingInfoEXT pos_addr; + VkDescriptorBufferBindingInfoEXT norm_addr; + VkDescriptorBufferBindingInfoEXT tan_addr; + VkDescriptorBufferBindingInfoEXT uv0_addr; +#else VkDescriptorSet view_set; VkDescriptorSet mat_set; VkDescriptorSet draw_set; @@ -60,6 +74,7 @@ typedef struct TbPrimitiveBatch { VkDescriptorSet norm_set; VkDescriptorSet tan_set; VkDescriptorSet uv0_set; +#endif } TbPrimitiveBatch; typedef struct TbMeshSystem { @@ -67,9 +82,7 @@ typedef struct TbMeshSystem { TbAllocator tmp_alloc; TbRenderSystem *rnd_sys; - TbMaterialSystem *material_system; TbViewSystem *view_sys; - TbRenderObjectSystem *render_object_system; TbRenderPipelineSystem *rp_sys; ecs_query_t *camera_query; @@ -80,14 +93,13 @@ typedef struct TbMeshSystem { TbDrawContextId opaque_draw_ctx2; TbDrawContextId transparent_draw_ctx2; - VkDescriptorSetLayout mesh_set_layout; VkDescriptorSetLayout draw_set_layout; VkPipelineLayout pipe_layout; VkPipelineLayout prepass_layout; - ecs_entity_t opaque_shader; - ecs_entity_t transparent_shader; - ecs_entity_t prepass_shader; + TbShader opaque_shader; + TbShader transparent_shader; + TbShader prepass_shader; // Re-used by shadows TbDrawBatch *opaque_batch; @@ -95,21 +107,15 @@ typedef struct TbMeshSystem { TB_DYN_ARR_OF(TbMesh) meshes; // For per draw data TbFrameDescriptorPoolList draw_pools; - // For per mesh bindless vertex buffers - TbDescriptorPool mesh_pool; - uint32_t mesh_desc_count; + + TbDescriptorBuffer opaque_draw_descs; + TbDescriptorBuffer trans_draw_descs; } TbMeshSystem; extern ECS_COMPONENT_DECLARE(TbMeshSystem); void tb_register_mesh_sys(TbWorld *world); void tb_unregister_mesh_sys(TbWorld *world); -TbMeshId tb_mesh_system_load_mesh(TbMeshSystem *self, const char *path, - const cgltf_node *node); -bool tb_mesh_system_take_mesh_ref(TbMeshSystem *self, TbMeshId id); -VkBuffer tb_mesh_system_get_gpu_mesh(TbMeshSystem *self, TbMeshId id); -void tb_mesh_system_release_mesh_ref(TbMeshSystem *self, TbMeshId id); - VkDescriptorSet tb_mesh_system_get_pos_set(TbMeshSystem *self); VkDescriptorSet tb_mesh_system_get_norm_set(TbMeshSystem *self); VkDescriptorSet tb_mesh_system_get_tan_set(TbMeshSystem *self); diff --git a/include/tb_mesh_system.h b/include/tb_mesh_system.h new file mode 100644 index 00000000..2c7a19ae --- /dev/null +++ b/include/tb_mesh_system.h @@ -0,0 +1,57 @@ +#pragma once + +#include "SDL3/SDL_stdinc.h" +#include "gltf.hlsli" +#include "tb_allocator.h" +#include "tb_common.h" +#include "tb_dynarray.h" +#include "tb_mesh_component.h" +#include "tb_render_common.h" +#include "tb_render_system.h" +#include "tb_render_target_system.h" +#include "tb_view_system.h" + +#include + +#ifndef TB_MESH_SYS_PRIO +#define TB_MESH_SYS_PRIO (TB_RP_SYS_PRIO + 1) +#endif + +typedef ecs_entity_t TbMesh2; +typedef struct ecs_query_t ecs_query_t; + +typedef uint32_t TbMeshIndex; +extern ECS_COMPONENT_DECLARE(TbMeshIndex); + +typedef ecs_entity_t TbSubMesh2; +typedef ecs_entity_t TbMaterial; +typedef struct TbSubMesh2Data { + uint32_t index_count; + uint64_t index_offset; + uint64_t vertex_offset; + uint32_t vertex_count; + uint32_t vertex_perm; + TbMaterial material; +} TbSubMesh2Data; +extern ECS_COMPONENT_DECLARE(TbSubMesh2Data); + +VkDescriptorSetLayout tb_mesh_sys_get_set_layout(ecs_world_t *ecs); + +VkDescriptorSet tb_mesh_sys_get_idx_set(ecs_world_t *ecs); +VkDescriptorSet tb_mesh_sys_get_pos_set(ecs_world_t *ecs); +VkDescriptorSet tb_mesh_sys_get_norm_set(ecs_world_t *ecs); +VkDescriptorSet tb_mesh_sys_get_tan_set(ecs_world_t *ecs); +VkDescriptorSet tb_mesh_sys_get_uv0_set(ecs_world_t *ecs); + +VkDescriptorBufferBindingInfoEXT tb_mesh_sys_get_idx_addr(ecs_world_t *ecs); +VkDescriptorBufferBindingInfoEXT tb_mesh_sys_get_pos_addr(ecs_world_t *ecs); +VkDescriptorBufferBindingInfoEXT tb_mesh_sys_get_norm_addr(ecs_world_t *ecs); +VkDescriptorBufferBindingInfoEXT tb_mesh_sys_get_tan_addr(ecs_world_t *ecs); +VkDescriptorBufferBindingInfoEXT tb_mesh_sys_get_uv0_addr(ecs_world_t *ecs); + +TbMesh2 tb_mesh_sys_load_gltf_mesh(ecs_world_t *ecs, cgltf_data *data, + const char *path, const char *name, + uint32_t index); +VkBuffer tb_mesh_sys_get_gpu_mesh(ecs_world_t *ecs, TbMesh2 mesh); + +bool tb_is_mesh_ready(ecs_world_t *ecs, TbMesh2 mesh_ent); diff --git a/include/tb_mmap.h b/include/tb_mmap.h new file mode 100644 index 00000000..77c6dd04 --- /dev/null +++ b/include/tb_mmap.h @@ -0,0 +1,10 @@ +#pragma once + +#include +#include +#include + +void *tb_mmap(void *start, size_t length, int32_t prot, int32_t flags, + void *file, size_t offset); + +void tb_munmap(void *addr, size_t length); diff --git a/include/noclipcomponent.h b/include/tb_noclip_component.h similarity index 100% rename from include/noclipcomponent.h rename to include/tb_noclip_component.h diff --git a/include/physlayers.h b/include/tb_phys_layers.h similarity index 100% rename from include/physlayers.h rename to include/tb_phys_layers.h diff --git a/include/physicssystem.h b/include/tb_physics_system.h similarity index 95% rename from include/physicssystem.h rename to include/tb_physics_system.h index 93e9169f..0fcdefd1 100644 --- a/include/physicssystem.h +++ b/include/tb_physics_system.h @@ -6,8 +6,8 @@ extern "C" { #endif -#include "simd.h" -#include "tbsystempriority.h" +#include "tb_simd.h" +#include "tb_system_priority.h" #define TB_PHYS_SYS_PRIO TB_SYSTEM_HIGH diff --git a/include/physicssystem.hpp b/include/tb_physics_system.hpp similarity index 88% rename from include/physicssystem.hpp rename to include/tb_physics_system.hpp index 7d1a9bfc..a19270fe 100644 --- a/include/physicssystem.hpp +++ b/include/tb_physics_system.hpp @@ -1,8 +1,8 @@ #pragma once -#include "allocator.h" -#include "physicssystem.h" -#include "rigidbodycomponent.h" +#include "tb_allocator.h" +#include "tb_physics_system.h" +#include "tb_rigidbody_component.h" namespace JPH { class PhysicsSystem; diff --git a/include/pi.h b/include/tb_pi.h similarity index 100% rename from include/pi.h rename to include/tb_pi.h diff --git a/include/profiling.h b/include/tb_profiling.h similarity index 73% rename from include/profiling.h rename to include/tb_profiling.h index 7488c46a..59b21e4e 100644 --- a/include/profiling.h +++ b/include/tb_profiling.h @@ -36,6 +36,10 @@ typedef struct TracyCGPUScope TracyCGPUScope; #ifdef __cplusplus extern "C" { #endif + +// A cleanup function used by TB_TRACY_SCOPE +void tb_tracy_zone_end(TracyCZoneCtx *ctx); + TracyCGPUContext *TracyCVkContext(VkPhysicalDevice gpu, VkDevice device, VkQueue queue, VkCommandBuffer buffer); TracyCGPUContext * @@ -43,6 +47,10 @@ TracyCVkContextExt(VkPhysicalDevice gpu, VkDevice device, VkQueue queue, VkCommandBuffer buffer, PFN_vkGetPhysicalDeviceCalibrateableTimeDomainsEXT ext1, PFN_vkGetCalibratedTimestampsEXT ext2); +TracyCGPUContext *TracyCVkContextHostCalib( + VkPhysicalDevice gpu, VkDevice device, PFN_vkResetQueryPoolEXT qpreset, + PFN_vkGetPhysicalDeviceCalibrateableTimeDomainsEXT gpdctd, + PFN_vkGetCalibratedTimestampsEXT gct); void TracyCVkContextDestroy(TracyCGPUContext *ctx); void TracyCVkContextName(TracyCGPUContext *ctx, const char *name, size_t len); @@ -62,6 +70,15 @@ void TracyCVkZoneEnd(TracyCGPUScope *scope); void TracyCVkCollect(TracyCGPUContext *ctx, VkCommandBuffer cmd_buf); +#define TB_TRACY_SCOPE(name) \ + static const struct ___tracy_source_location_data TracyConcat( \ + __tracy_source_location, TracyLine) = {name, __func__, TracyFile, \ + (uint32_t)TracyLine, 0}; \ + __attribute__((cleanup(tb_tracy_zone_end))) TracyCZoneCtx ctx##__COUNTER__ = \ + ___tracy_emit_zone_begin_callstack( \ + &TracyConcat(__tracy_source_location, TracyLine), TRACY_CALLSTACK, \ + true); + #ifdef __cplusplus } #endif @@ -75,6 +92,9 @@ typedef struct TracyCGPUScope TracyCGPUScope; (void)gpu, (void)device, (void)queue, (void)buffer, (void)ext1, (void)ext2; #define TracyCVkContext(...) +#define TracyCVkContextHostCalib(gpu, device, fn1, fn2, fn3) \ + 0; \ + (void)gpu, (void)device, (void)fn1, (void)fn2, (void)fn3 #define TracyCVkContextDestroy(...) #define TracyCVkContextName(ctx, name, len) (void)ctx, (void)name, (void)len #define TracyCVkNamedZone(ctx, var_name, cmd_buf, name, depth, active) \ @@ -84,6 +104,8 @@ typedef struct TracyCGPUScope TracyCGPUScope; #define TracyCVkZoneEnd(...) #define TracyCVkCollect(...) +#define TB_TRACY_SCOPE(...) + #endif #pragma clang diagnostic pop diff --git a/include/tbqueue.h b/include/tb_queue.h similarity index 69% rename from include/tbqueue.h rename to include/tb_queue.h index 5f9cc244..67343a36 100644 --- a/include/tbqueue.h +++ b/include/tb_queue.h @@ -1,8 +1,13 @@ #pragma once -#include "dynarray.h" +#include "tb_dynarray.h" #include +// Disabling this warning with -Wno-gnu-statement-expression +// doesn't seem to work in cmake's target_compile_options +#pragma clang diagnostic push +#pragma clang diagnostic ignored "-Wgnu-statement-expression" + #ifdef __cplusplus extern "C" { #endif @@ -33,25 +38,34 @@ extern "C" { SDL_UnlockMutex((queue).mutex); \ } +#define TB_QUEUE_PUSH_PTR(queue, element) \ + if (SDL_TryLockMutex((queue)->mutex) == 0) { \ + TB_DYN_ARR_APPEND((queue)->storage, element); \ + SDL_UnlockMutex((queue)->mutex); \ + } + #define TB_QUEUE_POP(queue, out) \ ({ \ - _Pragma("clang diagnostic push"); \ - _Pragma("clang diagnostic ignored \"-Wgnu-statement-expression\""); \ bool ret = false; \ if (SDL_TryLockMutex((queue).mutex) == 0) { \ if (TB_DYN_ARR_SIZE((queue).storage) > 0) { \ - (*out) = *TB_DYN_ARR_BACKPTR((queue).storage); \ + if (out) { \ + (*out) = *TB_DYN_ARR_BACKPTR((queue).storage); \ + } \ TB_DYN_ARR_POP((queue).storage); \ ret = true; \ } \ SDL_UnlockMutex((queue).mutex); \ } \ - _Pragma("clang diagnostic pop"); \ ret; \ }) #define TB_QUEUE_CLEAR(queue) \ - while (TB_QUEUE_POP((queue))) { \ + if (SDL_TryLockMutex((queue).mutex) == 0) { \ + while (TB_DYN_ARR_SIZE((queue).storage) > 0) { \ + TB_DYN_ARR_POP((queue).storage); \ + } \ + SDL_UnlockMutex((queue).mutex); \ } #ifdef __cplusplus diff --git a/include/tbrand.h b/include/tb_rand.h similarity index 100% rename from include/tbrand.h rename to include/tb_rand.h diff --git a/include/tbrendercommon.h b/include/tb_render_common.h similarity index 85% rename from include/tbrendercommon.h rename to include/tb_render_common.h index c378eae6..a3c8708e 100644 --- a/include/tbrendercommon.h +++ b/include/tb_render_common.h @@ -1,9 +1,10 @@ #pragma once -#include "dynarray.h" -#include "tbvk.h" -#include "tbvma.h" -#include "vkdbg.h" +#include "tb_dynarray.h" +#include "tb_queue.h" +#include "tb_vk.h" +#include "tb_vk_dbg.h" +#include "tb_vma.h" #define TB_MAX_FRAME_STATES 3 @@ -15,6 +16,9 @@ #define TB_MAX_RENDER_PASS_TRANS 16 #define TB_MAX_BARRIERS 16 +// TEMP: For migrating to descriptor buffers +#define TB_USE_DESC_BUFFER 0 + typedef struct TbDrawBatch { VkPipelineLayout layout; VkPipeline pipeline; @@ -42,8 +46,10 @@ typedef struct TbFullscreenBatch { typedef struct TbFrameDescriptorPool { uint32_t set_count; + uint32_t desc_count; VkDescriptorPool set_pool; VkDescriptorSet *sets; + TB_DYN_ARR_OF(uint32_t) free_list; } TbFrameDescriptorPool; typedef struct TbDescriptorPool { @@ -75,9 +81,9 @@ typedef struct TbBufferImageCopy { VkImageSubresourceRange range; } TbBufferImageCopy; -typedef TB_DYN_ARR_OF(VkWriteDescriptorSet) TbSetWriteQueue; -typedef TB_DYN_ARR_OF(TbBufferCopy) TbBufferCopyQueue; -typedef TB_DYN_ARR_OF(TbBufferImageCopy) TbBufferImageCopyQueue; +typedef TB_QUEUE_OF(VkWriteDescriptorSet) TbSetWriteQueue; +typedef TB_QUEUE_OF(TbBufferCopy) TbBufferCopyQueue; +typedef TB_QUEUE_OF(TbBufferImageCopy) TbBufferImageCopyQueue; typedef struct TbHostBuffer { VkBuffer buffer; @@ -90,6 +96,7 @@ typedef struct TbBuffer { VkBuffer buffer; VmaAllocation alloc; VmaAllocationInfo info; + VkDeviceAddress address; } TbBuffer; typedef struct TbImage { diff --git a/include/renderobjectsystem.h b/include/tb_render_object_system.h similarity index 62% rename from include/renderobjectsystem.h rename to include/tb_render_object_system.h index 55af12c6..ba6240fb 100644 --- a/include/renderobjectsystem.h +++ b/include/tb_render_object_system.h @@ -1,9 +1,12 @@ #pragma once -#include "allocator.h" -#include "dynarray.h" -#include "rendersystem.h" -#include "tbrendercommon.h" +#include "tb_allocator.h" +#include "tb_descriptor_buffer.h" +#include "tb_dyn_desc_pool.h" +#include "tb_dynarray.h" +#include "tb_free_list.h" +#include "tb_render_common.h" +#include "tb_render_system.h" #include @@ -30,13 +33,11 @@ typedef struct VkDescriptorSet_T *VkDescriptorSet; typedef struct ecs_query_t ecs_query_t; typedef struct TbRenderObject { - int32_t perm; int32_t index; } TbRenderObject; extern ECS_COMPONENT_DECLARE(TbRenderObject); typedef struct TbTransformsBuffer { - int32_t obj_count; TbBuffer gpu; TbHostBuffer host; } TbTransformsBuffer; @@ -46,13 +47,27 @@ typedef struct TbRenderObjectSystem { TbAllocator gp_alloc; TbAllocator tmp_alloc; + TbFreeList free_list; + VkDescriptorSetLayout set_layout; - TbFrameDescriptorPool pools[TB_MAX_FRAME_STATES]; + TbDynDescPool desc_pool; - TbTransformsBuffer trans_buffers[TB_MAX_FRAME_STATES]; + TbTransformsBuffer trans_buffer; - ecs_query_t *obj_query; + ecs_query_t *dirty_query; } TbRenderObjectSystem; extern ECS_COMPONENT_DECLARE(TbRenderObjectSystem); -VkDescriptorSet tb_render_object_sys_get_set(TbRenderObjectSystem *sys); +VkDescriptorSet tb_render_object_sys_get_set(ecs_world_t *ecs); + +VkDescriptorSetLayout tb_render_object_sys_get_set_layout(ecs_world_t *ecs); + +// Returns the address of the render object system's descriptor buffer +VkDescriptorBufferBindingInfoEXT +tb_render_object_sys_get_table_addr(ecs_world_t *ecs); + +// Called by rendering systems to mark meshes / etc. as render objects +// This is where the render object is assigned an index +void tb_mark_as_render_object(ecs_world_t *ecs, ecs_entity_t ent); + +void tb_render_object_mark_dirty(ecs_world_t *ecs, ecs_entity_t ent); diff --git a/include/renderpipelinesystem.h b/include/tb_render_pipeline_system.h similarity index 91% rename from include/renderpipelinesystem.h rename to include/tb_render_pipeline_system.h index 146aede1..6781098e 100644 --- a/include/renderpipelinesystem.h +++ b/include/tb_render_pipeline_system.h @@ -1,18 +1,20 @@ #pragma once -#include "allocator.h" -#include "bloom.h" -#include "dynarray.h" -#include "luminance.h" -#include "rendersystem.h" -#include "rendertargetsystem.h" -#include "skysystem.h" -#include "tbrendercommon.h" +#include "tb_allocator.h" +#include "tb_bloom.h" +#include "tb_dynarray.h" +#include "tb_luminance.h" +#include "tb_render_common.h" +#include "tb_render_system.h" +#include "tb_render_target_system.h" +#include "tb_sky_system.h" #include #include +typedef ecs_entity_t TbShader; + #define TB_RP_SYS_PRIO (TB_VIEW_SYS_PRIO + 1) #define TB_MAX_RENDER_PASS_ATTACHMENTS 4 @@ -105,11 +107,11 @@ typedef struct TbRenderPipelineSystem { VkPipelineLayout copy_pipe_layout; VkPipelineLayout comp_copy_pipe_layout; VkPipelineLayout tonemap_pipe_layout; - ecs_entity_t depth_copy_shader; - ecs_entity_t color_copy_shader; - ecs_entity_t brightness_shader; - ecs_entity_t comp_copy_shader; - ecs_entity_t tonemap_shader; + TbShader depth_copy_shader; + TbShader color_copy_shader; + TbShader brightness_shader; + TbShader comp_copy_shader; + TbShader tonemap_shader; TbFrameDescriptorPool descriptor_pools[TB_MAX_FRAME_STATES]; TbFrameDescriptorPool down_desc_pools[TB_MAX_FRAME_STATES]; diff --git a/include/rendersystem.h b/include/tb_render_system.h similarity index 84% rename from include/rendersystem.h rename to include/tb_render_system.h index 1f323e33..8bd48a2a 100644 --- a/include/rendersystem.h +++ b/include/tb_render_system.h @@ -1,10 +1,10 @@ #pragma once -#include "allocator.h" -#include "renderthread.h" -#include "tbsystempriority.h" -#include "tbvkalloc.h" -#include "tbvma.h" +#include "tb_allocator.h" +#include "tb_render_thread.h" +#include "tb_system_priority.h" +#include "tb_vk_alloc.h" +#include "tb_vma.h" #include @@ -16,6 +16,8 @@ typedef struct TbWorld TbWorld; +extern ECS_TAG_DECLARE(TbDescriptorReady); + typedef struct TbRenderSystemFrameState { TbHostBuffer tmp_host_buffer; TbSetWriteQueue set_write_queue; @@ -66,6 +68,10 @@ VkResult tb_rnd_sys_create_gpu_buffer_tmp(TbRenderSystem *self, const VkBufferCreateInfo *create_info, const char *name, TbBuffer *buffer, uint32_t alignment, void **ptr); +// Don't automatically enqueue an upload +VkResult tb_rnd_sys_create_gpu_buffer_noup( + TbRenderSystem *self, const VkBufferCreateInfo *create_info, + const char *name, TbBuffer *buffer, TbHostBuffer *host, void **ptr); // Create a GPU buffer and immediately copy the given data to it. // An upload will automatically be scheduled if necessary. // Caller must provide space for a host buffer though it will not be necessary @@ -91,11 +97,18 @@ VkResult tb_rnd_sys_create_gpu_image_tmp(TbRenderSystem *self, const void *data, VkBuffer tb_rnd_get_gpu_tmp_buffer(TbRenderSystem *self); +VkDeviceAddress tb_rnd_get_gpu_tmp_addr(TbRenderSystem *self); + // API for updating the contents of a buffer without resizing it VkResult tb_rnd_sys_update_gpu_buffer(TbRenderSystem *self, const TbBuffer *buffer, const TbHostBuffer *host, void **ptr); +// Updates the GPU buffer with the provided data via the tmp buffer +VkResult tb_rnd_sys_update_gpu_buffer_tmp(TbRenderSystem *self, + const TbBuffer *buffer, void *data, + size_t size, size_t alignment); + VkResult tb_rnd_create_sampler(TbRenderSystem *self, const VkSamplerCreateInfo *create_info, const char *name, VkSampler *sampler); @@ -132,6 +145,7 @@ tb_rnd_create_compute_pipelines(TbRenderSystem *self, uint32_t create_info_count, const VkComputePipelineCreateInfo *create_info, const char *name, VkPipeline *pipelines); + VkResult tb_rnd_create_graphics_pipelines( TbRenderSystem *self, uint32_t create_info_count, const VkGraphicsPipelineCreateInfo *create_info, const char *name, @@ -160,13 +174,20 @@ void tb_rnd_destroy_descriptor_pool(TbRenderSystem *self, void tb_rnd_update_descriptors(TbRenderSystem *self, uint32_t write_count, const VkWriteDescriptorSet *writes); -VkResult tb_rnd_frame_desc_pool_tick( - TbRenderSystem *self, const VkDescriptorPoolCreateInfo *pool_info, - const VkDescriptorSetLayout *layouts, void *alloc_next, - TbFrameDescriptorPool *pools, uint32_t set_count); +VkResult tb_rnd_alloc_descriptor_sets(TbRenderSystem *self, const char *name, + const VkDescriptorSetAllocateInfo *info, + VkDescriptorSet *sets); +VkResult +tb_rnd_frame_desc_pool_tick(TbRenderSystem *self, const char *name, + const VkDescriptorPoolCreateInfo *pool_info, + const VkDescriptorSetLayout *layouts, + void *alloc_next, TbFrameDescriptorPool *pools, + uint32_t set_count, uint32_t desc_count); VkDescriptorSet tb_rnd_frame_desc_pool_get_set(TbRenderSystem *self, TbFrameDescriptorPool *pools, uint32_t set_idx); +uint32_t tb_rnd_frame_desc_pool_get_desc_count(TbRenderSystem *self, + TbFrameDescriptorPool *pools); VkResult tb_rnd_resize_desc_pool(TbRenderSystem *self, const VkDescriptorPoolCreateInfo *pool_info, diff --git a/include/rendertargetsystem.h b/include/tb_render_target_system.h similarity index 96% rename from include/rendertargetsystem.h rename to include/tb_render_target_system.h index 496b4a1e..99f2bf3e 100644 --- a/include/rendertargetsystem.h +++ b/include/tb_render_target_system.h @@ -1,9 +1,9 @@ #pragma once -#include "allocator.h" -#include "dynarray.h" -#include "rendersystem.h" -#include "tbvk.h" +#include "tb_allocator.h" +#include "tb_dynarray.h" +#include "tb_render_system.h" +#include "tb_vk.h" #include #include diff --git a/include/renderthread.h b/include/tb_render_thread.h similarity index 91% rename from include/renderthread.h rename to include/tb_render_thread.h index 8f1c5058..51bc6d4b 100644 --- a/include/renderthread.h +++ b/include/tb_render_thread.h @@ -1,7 +1,7 @@ #pragma once -#include "allocator.h" -#include "tbrendercommon.h" +#include "tb_allocator.h" +#include "tb_render_common.h" #if !defined(FINAL) && !defined(__ANDROID__) #define VALIDATION @@ -97,14 +97,16 @@ typedef struct TbFrameState { VmaAllocation tmp_gpu_alloc; VkBuffer tmp_gpu_buffer; + VkDeviceAddress tmp_gpu_buf_addr; - // Memory expected to be actually allocated by the main thread - // The main thread will write to this and the render thread will read it - TbSetWriteQueue set_write_queue; - TbBufferCopyQueue buf_copy_queue; - TbBufferImageCopyQueue buf_img_copy_queue; + // References to queues owned by the main thread and pushed to by tasks + // The render thread only consumes these + TbSetWriteQueue *set_write_queue; + TbBufferCopyQueue *buf_copy_queue; + TbBufferImageCopyQueue *buf_img_copy_queue; TbArenaAllocator tmp_alloc; + TbAllocator gp_alloc; TB_DYN_ARR_OF(TbPassContext) pass_contexts; TB_DYN_ARR_OF(TbDrawContext) draw_contexts; @@ -145,6 +147,7 @@ typedef struct TbRenderThread { VkPhysicalDevice gpu; VkPhysicalDeviceProperties2 gpu_props; VkPhysicalDeviceDriverProperties driver_props; + VkPhysicalDeviceDescriptorBufferPropertiesEXT desc_buf_props; uint32_t queue_family_count; VkQueueFamilyProperties *queue_props; VkPhysicalDeviceFeatures gpu_features; diff --git a/include/rigidbodycomponent.h b/include/tb_rigidbody_component.h similarity index 100% rename from include/rigidbodycomponent.h rename to include/tb_rigidbody_component.h diff --git a/include/tb_scene.h b/include/tb_scene.h new file mode 100644 index 00000000..e8899b7b --- /dev/null +++ b/include/tb_scene.h @@ -0,0 +1,10 @@ +#pragma once + +#include + +typedef ecs_entity_t TbScene; +extern ECS_TAG_DECLARE(TbSceneRoot); + +TbScene tb_create_scene(ecs_world_t *ecs, const char *scene_path); + +bool tb_is_scene_ready(ecs_world_t *ecs, TbScene scene); diff --git a/include/tb_scene_material.h b/include/tb_scene_material.h new file mode 100644 index 00000000..17da88b6 --- /dev/null +++ b/include/tb_scene_material.h @@ -0,0 +1,4 @@ +#pragma once +#include "tb_material_system.h" + +void tb_register_scene_material_domain(ecs_world_t *ecs); diff --git a/include/tbsdl.h b/include/tb_sdl.h similarity index 85% rename from include/tbsdl.h rename to include/tb_sdl.h index 37c8ad02..87c9bff2 100644 --- a/include/tbsdl.h +++ b/include/tb_sdl.h @@ -16,4 +16,8 @@ #include #include +void *tb_rw_mmap(SDL_RWops *file, size_t size); + +void tb_rw_munmap(void *data, size_t size); + #pragma clang diagnostic pop diff --git a/include/settings.h b/include/tb_settings.h similarity index 97% rename from include/settings.h rename to include/tb_settings.h index 9203cfd8..786514d6 100644 --- a/include/settings.h +++ b/include/tb_settings.h @@ -1,6 +1,6 @@ #pragma once -#include "coreuisystem.h" +#include "tb_coreui_system.h" #include #include "fxaa.hlsli" diff --git a/include/shadercommon.h b/include/tb_shader_common.h similarity index 93% rename from include/shadercommon.h rename to include/tb_shader_common.h index 9296e79c..5005732e 100644 --- a/include/shadercommon.h +++ b/include/tb_shader_common.h @@ -1,6 +1,6 @@ #pragma once -#include "simd.h" +#include "tb_simd.h" #include "common.hlsli" #include "imgui.hlsli" diff --git a/include/tb_shader_system.h b/include/tb_shader_system.h index d6c72dc6..44fca69a 100644 --- a/include/tb_shader_system.h +++ b/include/tb_shader_system.h @@ -1,6 +1,6 @@ #pragma once -#include "rendersystem.h" +#include "tb_render_system.h" #include @@ -10,11 +10,13 @@ typedef struct VkPipeline_T *VkPipeline; typedef VkPipeline (*TbShaderCompileFn)(void *args); -ecs_entity_t tb_shader_load(ecs_world_t *ecs, TbShaderCompileFn create_fn, - void *args, size_t args_size); -void tb_shader_destroy(ecs_world_t *ecs, ecs_entity_t shader); +typedef ecs_entity_t TbShader; -bool tb_is_shader_ready(ecs_world_t *ecs, ecs_entity_t shader); -bool tb_wait_shader_ready(ecs_world_t *ecs, ecs_entity_t shader); +TbShader tb_shader_load(ecs_world_t *ecs, TbShaderCompileFn create_fn, + void *args, size_t args_size); +void tb_shader_destroy(ecs_world_t *ecs, TbShader shader); -VkPipeline tb_shader_get_pipeline(ecs_world_t *ecs, ecs_entity_t shader); +bool tb_is_shader_ready(ecs_world_t *ecs, TbShader shader); +bool tb_wait_shader_ready(ecs_world_t *ecs, TbShader shader); + +VkPipeline tb_shader_get_pipeline(ecs_world_t *ecs, TbShader shader); diff --git a/include/simd.h b/include/tb_simd.h similarity index 100% rename from include/simd.h rename to include/tb_simd.h diff --git a/include/skycomponent.h b/include/tb_sky_component.h similarity index 79% rename from include/skycomponent.h rename to include/tb_sky_component.h index ac509e8c..347ef8ae 100644 --- a/include/skycomponent.h +++ b/include/tb_sky_component.h @@ -1,6 +1,6 @@ #pragma once -#include "simd.h" +#include "tb_simd.h" #include @@ -8,7 +8,7 @@ typedef struct TbSkyDescriptor { float cirrus; float cumulus; } TbSkyDescriptor; -extern ECS_COMPONENT_DECLARE(TbSkyComponent); +extern ECS_COMPONENT_DECLARE(TbSkyDescriptor); typedef struct TbSkyComponent { float time; diff --git a/include/skysystem.h b/include/tb_sky_system.h similarity index 76% rename from include/skysystem.h rename to include/tb_sky_system.h index 2068f4f8..9adb6ac3 100644 --- a/include/skysystem.h +++ b/include/tb_sky_system.h @@ -2,11 +2,13 @@ #define PREFILTER_PASS_COUNT 10 -#include "tbrendercommon.h" -#include "viewsystem.h" +#include "tb_render_common.h" +#include "tb_view_system.h" #include +typedef ecs_entity_t TbShader; + #define TB_SKY_SYS_PRIO (TB_RP_SYS_PRIO + 1) typedef struct TbRenderSystem TbRenderSystem; @@ -20,12 +22,6 @@ typedef struct VkDescriptorSetLayout_T *VkDescriptorSetLayout; typedef struct TbWorld TbWorld; typedef struct ecs_query_t ecs_query_t; -typedef struct TbSkySystemFrameState { - uint32_t set_count; - VkDescriptorPool set_pool; - VkDescriptorSet *sets; -} TbSkySystemFrameState; - typedef struct TbSkySystem { TbRenderSystem *rnd_sys; TbRenderPipelineSystem *rp_sys; @@ -37,7 +33,8 @@ typedef struct TbSkySystem { ecs_query_t *camera_query; float time; - TbSkySystemFrameState frame_states[TB_MAX_FRAME_STATES]; + + TbFrameDescriptorPoolList pools; TbDrawContextId sky_draw_ctx; TbDrawContextId env_capture_ctxs[PREFILTER_PASS_COUNT]; @@ -46,16 +43,18 @@ typedef struct TbSkySystem { VkSampler irradiance_sampler; VkDescriptorSetLayout sky_set_layout; - VkPipelineLayout sky_pipe_layout; VkDescriptorSetLayout irr_set_layout; + VkPipelineLayout sky_pipe_layout; VkPipelineLayout irr_pipe_layout; VkPipelineLayout prefilter_pipe_layout; - ecs_entity_t sky_shader; - ecs_entity_t env_shader; - ecs_entity_t irradiance_shader; - ecs_entity_t prefilter_shader; + TbShader sky_shader; + TbShader env_shader; + TbShader irradiance_shader; + TbShader prefilter_shader; TbBuffer sky_geom_gpu_buffer; + TbDescriptorBuffer sky_desc_buffer; + TbDescriptorBuffer irr_desc_buffer; } TbSkySystem; extern ECS_COMPONENT_DECLARE(TbSkySystem); diff --git a/include/tbsystempriority.h b/include/tb_system_priority.h similarity index 100% rename from include/tbsystempriority.h rename to include/tb_system_priority.h diff --git a/include/tb_task_scheduler.h b/include/tb_task_scheduler.h index fa0b4806..646be9ac 100644 --- a/include/tb_task_scheduler.h +++ b/include/tb_task_scheduler.h @@ -23,6 +23,8 @@ extern ECS_COMPONENT_DECLARE(TbTaskScheduler); typedef struct TbAsyncTaskArgs TbAsyncTaskArgs; +void tb_run_pinned_tasks(ecs_world_t *ecs); + // Create a task that runs a given function on any available thread. // Args will be copied to a thread-safe heap // Task must be launched to begin execution diff --git a/include/tb_texture_system.h b/include/tb_texture_system.h new file mode 100644 index 00000000..5ec03a2c --- /dev/null +++ b/include/tb_texture_system.h @@ -0,0 +1,61 @@ +#pragma once + +#include "tb_render_system.h" + +#include + +#define TB_TEX_SYS_PRIO (TB_RND_SYS_PRIO + 1) + +typedef struct VkDescriptorSetLayout_T *VkDescriptorSetLayout; +typedef struct VkDescriptorSet_T *VkDescriptorSet; + +typedef ecs_entity_t TbTexture; // Entities can be handles to textures +typedef struct cgltf_data cgltf_data; + +typedef uint32_t TbTextureComponent; +extern ECS_COMPONENT_DECLARE(TbTextureComponent); + +typedef enum TbTextureUsage { + TB_TEX_USAGE_UNKNOWN = 0, + TB_TEX_USAGE_COLOR, + TB_TEX_USAGE_NORMAL, + TB_TEX_USAGE_METAL_ROUGH, + TB_TEX_USAGE_BRDF, +} TbTextureUsage; + +VkDescriptorSetLayout tb_tex_sys_get_set_layout(ecs_world_t *ecs); + +// Returns the descriptor set that can be used to access textures by index from +// a shader +VkDescriptorSet tb_tex_sys_get_set(ecs_world_t *ecs); + +// Returns the address of the texture system's descriptor buffer +VkDescriptorBufferBindingInfoEXT tb_tex_sys_get_table_addr(ecs_world_t *ecs); + +// Returns the image view of the given texture if it is ready +VkImageView tb_tex_sys_get_image_view2(ecs_world_t *ecs, TbTexture tex); + +// Begins an async texture load from a given set of pixels +// The given pixles are assumed to be kept live by the caller until +// the loading task finishes +TbTexture tb_tex_sys_load_raw_tex(ecs_world_t *ecs, const char *name, + const uint8_t *pixels, uint64_t size, + uint32_t width, uint32_t height, + TbTextureUsage usage); +// Begins an async texture load from a loaded glb file, the material, +// and the texture usage so the task can parse the gltf data and find the +// expected image +TbTexture tb_tex_sys_load_mat_tex(ecs_world_t *ecs, const cgltf_data *data, + const char *mat_name, TbTextureUsage usage); +// Begins an async texture load from a path to a given ktx file and the texture +// usage so the task can open the ktx file and find the expected image +TbTexture tb_tex_sys_load_ktx_tex(ecs_world_t *ecs, const char *path, + const char *name, TbTextureUsage usage); + +// Returns true if the texture is ready to be used +bool tb_is_texture_ready(ecs_world_t *ecs, TbTexture tex_ent); + +TbTexture tb_get_default_color_tex(ecs_world_t *ecs); +TbTexture tb_get_default_normal_tex(ecs_world_t *ecs); +TbTexture tb_get_default_metal_rough_tex(ecs_world_t *ecs); +TbTexture tb_get_brdf_tex(ecs_world_t *ecs); diff --git a/include/thirdpersoncomponents.h b/include/tb_third_person_components.h similarity index 96% rename from include/thirdpersoncomponents.h rename to include/tb_third_person_components.h index ceed2ada..62c5de6a 100644 --- a/include/thirdpersoncomponents.h +++ b/include/tb_third_person_components.h @@ -1,6 +1,6 @@ #pragma once -#include "simd.h" +#include "tb_simd.h" #include #include diff --git a/include/transformcomponent.h b/include/tb_transform_component.h similarity index 89% rename from include/transformcomponent.h rename to include/tb_transform_component.h index 531a0676..f2fade9c 100644 --- a/include/transformcomponent.h +++ b/include/tb_transform_component.h @@ -1,7 +1,7 @@ #pragma once -#include "dynarray.h" -#include "simd.h" +#include "tb_dynarray.h" +#include "tb_simd.h" #include @@ -9,8 +9,8 @@ extern "C" { #endif +extern ECS_TAG_DECLARE(TbTransformDirty); typedef struct TbTransformComponent { - bool dirty; float4x4 world_matrix; TbTransform transform; } TbTransformComponent; diff --git a/include/tbutil.h b/include/tb_util.h similarity index 100% rename from include/tbutil.h rename to include/tb_util.h diff --git a/include/viewsystem.h b/include/tb_view_system.h similarity index 80% rename from include/viewsystem.h rename to include/tb_view_system.h index 0af71742..a8de6f28 100644 --- a/include/viewsystem.h +++ b/include/tb_view_system.h @@ -1,11 +1,12 @@ #pragma once -#include "allocator.h" #include "common.hlsli" -#include "dynarray.h" -#include "renderpipelinesystem.h" -#include "tbrendercommon.h" -#include "texturesystem.h" +#include "tb_allocator.h" +#include "tb_descriptor_buffer.h" +#include "tb_dynarray.h" +#include "tb_render_common.h" +#include "tb_render_pipeline_system.h" +#include "tb_texture_system.h" #include @@ -35,12 +36,12 @@ typedef struct TbView { TbCommonViewData view_data; TbCommonLightData light_data; TbFrustum frustum; + TbDescriptorBuffer desc_buffer; } TbView; typedef struct TbViewSystem { TbRenderSystem *rnd_sys; TbRenderTargetSystem *rt_sys; - TbTextureSystem *texture_system; TbAllocator gp_alloc; TbAllocator tmp_alloc; @@ -49,6 +50,9 @@ typedef struct TbViewSystem { VkDescriptorSetLayout set_layout; TbViewSystemFrameState frame_states[TB_MAX_FRAME_STATES]; + VkDescriptorSetLayout set_layout2; + TbDescriptorBuffer desc_buffer; + TB_DYN_ARR_OF(TbView) views; } TbViewSystem; extern ECS_COMPONENT_DECLARE(TbViewSystem); @@ -66,3 +70,7 @@ VkDescriptorSet tb_view_system_get_descriptor(TbViewSystem *self, TbViewId view); VkDescriptorSet tb_view_sys_get_set(TbViewSystem *self); const TbView *tb_get_view(TbViewSystem *self, TbViewId view); + +VkDescriptorSetLayout tb_view_sys_get_set_layout(ecs_world_t *ecs); +VkDescriptorBufferBindingInfoEXT tb_view_sys_get_table_addr(ecs_world_t *ecs, + TbViewId view); diff --git a/include/visualloggingsystem.h b/include/tb_visual_logging_system.h similarity index 87% rename from include/visualloggingsystem.h rename to include/tb_visual_logging_system.h index cb8633fd..ffaa16ec 100644 --- a/include/visualloggingsystem.h +++ b/include/tb_visual_logging_system.h @@ -1,10 +1,11 @@ #pragma once -#include "allocator.h" -#include "dynarray.h" -#include "simd.h" -#include "tbrendercommon.h" -#include "tbsystempriority.h" +#include "tb_allocator.h" +#include "tb_dynarray.h" +#include "tb_mesh_system.h" +#include "tb_render_common.h" +#include "tb_simd.h" +#include "tb_system_priority.h" #include @@ -21,7 +22,6 @@ typedef struct TbMeshSystem TbMeshSystem; typedef struct VkPipelineLayout_T *VkPipelineLayout; typedef struct VkBuffer_T *VkBuffer; typedef uint32_t TbDrawContextId; -typedef TbResourceId TbMeshId; typedef struct TbWorld TbWorld; @@ -36,12 +36,11 @@ typedef struct TbVisualLoggingSystem { bool *ui; - TbMeshId sphere_mesh; + TbMesh2 sphere_mesh2; uint32_t sphere_index_type; uint32_t sphere_index_count; uint32_t sphere_pos_offset; float3 sphere_scale; - VkBuffer sphere_geom_buffer; VkPipelineLayout pipe_layout; ecs_entity_t shader; diff --git a/include/tbvk.h b/include/tb_vk.h similarity index 100% rename from include/tbvk.h rename to include/tb_vk.h diff --git a/include/tbvkalloc.h b/include/tb_vk_alloc.h similarity index 97% rename from include/tbvkalloc.h rename to include/tb_vk_alloc.h index 4ec86776..6733be00 100644 --- a/include/tbvkalloc.h +++ b/include/tb_vk_alloc.h @@ -1,6 +1,6 @@ #pragma once -#include "tbvk.h" +#include "tb_vk.h" typedef struct VmaAllocator_T *VmaAllocator; diff --git a/include/vkdbg.h b/include/tb_vk_dbg.h similarity index 97% rename from include/vkdbg.h rename to include/tb_vk_dbg.h index e1d6cfc0..9af8cd06 100644 --- a/include/vkdbg.h +++ b/include/tb_vk_dbg.h @@ -1,8 +1,8 @@ #pragma once -#include "simd.h" -#include "tblog.h" -#include "tbsdl.h" +#include "tb_log.h" +#include "tb_sdl.h" +#include "tb_simd.h" #ifndef FINAL typedef struct VkQueue_T *VkQueue; diff --git a/include/tbvma.h b/include/tb_vma.h similarity index 100% rename from include/tbvma.h rename to include/tb_vma.h diff --git a/include/world.h b/include/tb_world.h similarity index 71% rename from include/world.h rename to include/tb_world.h index ba8772fc..3a803104 100644 --- a/include/world.h +++ b/include/tb_world.h @@ -1,8 +1,8 @@ #pragma once -#include "allocator.h" -#include "dynarray.h" -#include "scene.h" +#include "tb_allocator.h" +#include "tb_dynarray.h" +#include "tb_scene.h" #include "blocks/Block.h" @@ -34,19 +34,29 @@ void tb_register_system(const char *name, int32_t priority, } typedef struct json_object json_object; +typedef struct cgltf_data cgltf_data; typedef struct cgltf_node cgltf_node; -typedef ecs_entity_t (*TbRegisterComponentFn)(TbWorld *); -typedef bool (*TbLoadComponentFn)(TbWorld *world, ecs_entity_t ent, +typedef struct TbComponentRegisterResult { + ecs_entity_t type_id; + ecs_entity_t desc_id; +} TbComponentRegisterResult; + +typedef TbComponentRegisterResult (*TbRegisterComponentFn)(TbWorld *); +typedef bool (*TbLoadComponentFn)(ecs_world_t *ecs, ecs_entity_t ent, const char *source_path, + const cgltf_data *data, const cgltf_node *node, json_object *json); +typedef bool (*TbReadyComponentFn)(ecs_world_t *ecs, ecs_entity_t ent); void tb_register_component(const char *name, TbRegisterComponentFn reg_fn, - TbLoadComponentFn load_fn); + TbLoadComponentFn load_fn, + TbReadyComponentFn ready_fn); #define TB_REGISTER_COMP(namespace, name) \ __attribute__(( \ __constructor__)) void __##namespace##_register_##name##_comp(void) { \ tb_register_component(#name, &namespace##_register_##name##_comp, \ - &namespace##_load_##name##_comp); \ + &namespace##_load_##name##_comp, \ + &namespace##_ready_##name##_comp); \ } typedef struct TbWorldDesc { @@ -65,7 +75,6 @@ typedef struct TbWorld { TbAllocator tmp_alloc; TbRenderThread *render_thread; SDL_Window *window; - TB_DYN_ARR_OF(TbScene) scenes; } TbWorld; typedef struct TbWorldRef { @@ -75,12 +84,15 @@ extern ECS_COMPONENT_DECLARE(TbWorldRef); bool tb_create_world(const TbWorldDesc *desc, TbWorld *world); bool tb_tick_world(TbWorld *world, float delta_seconds); -void tb_clear_world(TbWorld *world); void tb_destroy_world(TbWorld *world); -bool tb_load_scene(TbWorld *world, const char *scene_path); +TbScene tb_load_scene(TbWorld *world, const char *scene_path); void tb_unload_scene(TbWorld *world, TbScene *scene); +// HACK: Get component load function by name for scene2 +TbLoadComponentFn tb_get_component_load_fn(const char *name); +bool tb_enitity_components_ready(ecs_world_t *ecs, ecs_entity_t ent); + extern ECS_COMPONENT_DECLARE(float3); extern ECS_COMPONENT_DECLARE(float4); extern ECS_COMPONENT_DECLARE(float4x4); diff --git a/include/texturesystem.h b/include/texturesystem.h deleted file mode 100644 index 606cd0e9..00000000 --- a/include/texturesystem.h +++ /dev/null @@ -1,73 +0,0 @@ -#pragma once - -#include "SDL3/SDL_stdinc.h" -#include "allocator.h" -#include "dynarray.h" -#include "rendersystem.h" -#include "tbcommon.h" -#include "tbrendercommon.h" - -#include - -#define TB_TEX_SYS_PRIO (TB_RND_SYS_PRIO + 1) - -typedef struct TbWorld TbWorld; -typedef struct TbRenderSystem TbRenderSystem; -typedef struct TbTexture TbTexture; -typedef struct VkImageView_T *VkImageView; -typedef struct VkDescriptorSetLayout_T *VkDescriptorSetLayout; -typedef struct cgltf_texture cgltf_texture; -typedef uint64_t TbTextureId; - -static const TbTextureId TbInvalidTextureId = SDL_MAX_UINT64; - -typedef enum TbTextureUsage { - TB_TEX_USAGE_UNKNOWN = 0, - TB_TEX_USAGE_COLOR, - TB_TEX_USAGE_NORMAL, - TB_TEX_USAGE_METAL_ROUGH, - TB_TEX_USAGE_BRDF, -} TbTextureUsage; -extern ECS_COMPONENT_DECLARE(TbTextureUsage); - -extern ECS_TAG_DECLARE(TbTextureReady); - -typedef struct TbTextureSystem { - TbAllocator gp_alloc; - TbAllocator tmp_alloc; - - TbRenderSystem *rnd_sys; - - TB_DYN_ARR_OF(TbTexture) textures; - - VkDescriptorSetLayout set_layout; - TbDescriptorPool set_pool; - - TbTextureId default_color_tex; - TbTextureId default_normal_tex; - TbTextureId default_metal_rough_tex; - TbTextureId brdf_tex; -} TbTextureSystem; -extern ECS_COMPONENT_DECLARE(TbTextureSystem); - -VkDescriptorSet tb_tex_sys_get_set(TbTextureSystem *self); - -uint32_t tb_tex_system_get_index(TbTextureSystem *self, TbTextureId tex); -VkImageView tb_tex_system_get_image_view(TbTextureSystem *self, - TbTextureId tex); -TbTextureId tb_tex_system_create_texture(TbTextureSystem *self, - const char *path, const char *name, - TbTextureUsage usage, uint32_t width, - uint32_t height, const uint8_t *pixels, - uint64_t size); -TbTextureId tb_tex_system_load_texture(TbTextureSystem *self, const char *path, - const char *name, - const cgltf_texture *texture); -bool tb_tex_system_take_tex_ref(TbTextureSystem *self, TbTextureId id); -void tb_tex_system_release_texture_ref(TbTextureSystem *self, TbTextureId tex); - -ecs_entity_t tb_tex_sys_load_mat_tex(ecs_world_t *ecs, const char *path, - const char *mat_name, - TbTextureUsage usage); - -bool tb_is_tex_loaded(ecs_world_t *ecs, ecs_entity_t tex_ent); diff --git a/include/timeofdaysystem.h b/include/timeofdaysystem.h deleted file mode 100644 index 89bdbe38..00000000 --- a/include/timeofdaysystem.h +++ /dev/null @@ -1,15 +0,0 @@ -#pragma once - -#include "tbsystempriority.h" - -#include - -#define TB_TOD_SYS_PRIO TB_SYSTEM_NORMAL - -typedef struct TbWorld TbWorld; - -typedef struct TbTimeOfDayComponent { - float time; - float time_scale; -} TbTimeOfDayComponent; -extern ECS_COMPONENT_DECLARE(TbTimeOfDayComponent); diff --git a/readme.md b/readme.md index eadf6f5b..2414245a 100644 --- a/readme.md +++ b/readme.md @@ -78,7 +78,7 @@ So an example for configuring and building the `x64-windows` triplet with `ninja See the github actions page for build status and a quick overview of the supported and tested configurations ## Additional Notes -This project relies on some of clang's C language extensions because I was lazy and didn't want to write out SSE/NEON intrinsics for some basic math. See `src/simd.h` & `src/simd.c` for more details. This has balooned into a few other clang specific features being supported, such as Blocks. Only LLVM family compilers are expected to be able to compile Toybox for the foreseeable future. +This project relies on some of clang's C language extensions because I was lazy and didn't want to write out SSE/NEON intrinsics for some basic math. See `src/tb_simd.h` & `src/tb_simd.c` for more details. This has balooned into a few other clang specific features being supported, such as Blocks. Only LLVM family compilers are expected to be able to compile Toybox for the foreseeable future. For best results, use the latest version of vcpkg provided by your package manager or directly via Git. There are couple custom ports for SDL3 and SDL3_Mixer that I maintain. See `vcpkg-configuration.json` for how that's set up. diff --git a/samples/bistro/source/main.c b/samples/bistro/source/main.c index ceb56689..2ee32111 100644 --- a/samples/bistro/source/main.c +++ b/samples/bistro/source/main.c @@ -1,6 +1,6 @@ #include "samplecore.h" -#include "world.h" +#include "tb_world.h" void tb_sample_on_start(TbWorld *world) { tb_load_scene(world, "scenes/bistro.glb"); diff --git a/samples/ocean/assets/originals/components.blend b/samples/ocean/assets/originals/components.blend index f7529847..f37448b8 100644 --- a/samples/ocean/assets/originals/components.blend +++ b/samples/ocean/assets/originals/components.blend @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:d42423a8f500cb9fa2bf0b88559345941a751be859fda984b6f1c87ed112e4d0 -size 1078860 +oid sha256:29f213fb2ecb9ad3876a73d7a90c9f88a237edf1f7ee906ad93f1449674c608c +size 1082548 diff --git a/samples/ocean/assets/scenes/components.glb b/samples/ocean/assets/scenes/components.glb index 491e5b62..b581516f 100644 --- a/samples/ocean/assets/scenes/components.glb +++ b/samples/ocean/assets/scenes/components.glb @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:3dabeb9cf046693cc344a493490441616ed714b557e63efe8de833d97bef6e87 -size 423040 +oid sha256:d0c521eec1ccc231f3923857736afad5e07ca845a354cbda071a4add865c396d +size 424628 diff --git a/samples/ocean/source/main.c b/samples/ocean/source/main.c index 820f2ea5..39889210 100644 --- a/samples/ocean/source/main.c +++ b/samples/ocean/source/main.c @@ -1,6 +1,6 @@ #include "samplecore.h" -#include "world.h" +#include "tb_world.h" void tb_sample_on_start(TbWorld *world) { tb_load_scene(world, "scenes/components.glb"); diff --git a/samples/samplecore/include/samplecore.h b/samples/samplecore/include/samplecore.h index f5dc912a..17ddd7b8 100644 --- a/samples/samplecore/include/samplecore.h +++ b/samples/samplecore/include/samplecore.h @@ -1,6 +1,6 @@ #pragma once -#include "allocator.h" +#include "tb_allocator.h" typedef struct World World; typedef struct TbWorld TbWorld; diff --git a/samples/samplecore/source/samplecore.c b/samples/samplecore/source/samplecore.c index 33728b88..fcc24510 100644 --- a/samples/samplecore/source/samplecore.c +++ b/samples/samplecore/source/samplecore.c @@ -4,16 +4,16 @@ #include -#include "allocator.h" -#include "pi.h" -#include "profiling.h" -#include "settings.h" -#include "shadercommon.h" -#include "simd.h" -#include "tbcommon.h" -#include "tbengineconfig.h" -#include "tbsdl.h" -#include "world.h" +#include "tb_allocator.h" +#include "tb_common.h" +#include "tb_engine_config.h" +#include "tb_pi.h" +#include "tb_profiling.h" +#include "tb_sdl.h" +#include "tb_settings.h" +#include "tb_shader_common.h" +#include "tb_simd.h" +#include "tb_world.h" #include int32_t main(int32_t argc, char *argv[]) { @@ -111,9 +111,6 @@ int32_t main(int32_t argc, char *argv[]) { } return 0; - // This doesn't quite work yet - tb_clear_world(&world); - // This will also close the window that was provded tb_destroy_world(&world); diff --git a/samples/terrain/source/main.c b/samples/terrain/source/main.c index 493d2ef0..bf0c94fc 100644 --- a/samples/terrain/source/main.c +++ b/samples/terrain/source/main.c @@ -1,6 +1,6 @@ #include "samplecore.h" -#include "world.h" +#include "tb_world.h" void tb_sample_on_start(TbWorld *world) { tb_load_scene(world, "scenes/terrain.glb"); diff --git a/source/cgltf.c b/source/cgltf.c deleted file mode 100644 index dff7eda1..00000000 --- a/source/cgltf.c +++ /dev/null @@ -1,8 +0,0 @@ -#pragma clang diagnostic push -#pragma clang diagnostic ignored "-Wdeprecated-declarations" -#pragma clang diagnostic ignored "-Wextra-semi-stmt" - -#define CGLTF_IMPLEMENTATION -#include "tbgltf.h" - -#pragma clang diagnostic pop diff --git a/source/config.h.in b/source/config.h.in index d732cd35..cb407fdd 100644 --- a/source/config.h.in +++ b/source/config.h.in @@ -14,4 +14,15 @@ #define TB_PLATFORM "@platform@" #define TB_ARCH "@arch@" +// clang-format off +#define TB_WINDOWS @tb_windows@ +#define TB_MACOS @tb_macos@ +#define TB_LINUX @tb_linux@ +#define TB_ANDROID @tb_android@ +#define TB_IOS @tb_ios@ + +#define TB_X64 @tb_x64@ +#define TB_ARM64 @tb_arm64@ +// clang-format on + #define GIT_COMMIT_HASH "@GIT_COMMIT_HASH@" diff --git a/source/loadthread.c b/source/loadthread.c deleted file mode 100644 index 97a9da01..00000000 --- a/source/loadthread.c +++ /dev/null @@ -1 +0,0 @@ -#include "loadthread.h" diff --git a/source/lumavg.hlsl.cs b/source/lumavg.hlsl.cs index 9a8d0e8f..16da0d53 100644 --- a/source/lumavg.hlsl.cs +++ b/source/lumavg.hlsl.cs @@ -1,4 +1,4 @@ -#include "luminance.h" +#include "tb_luminance.h" #define GROUP_SIZE 256 #define THREADS_X 256 @@ -10,43 +10,46 @@ [[vk::push_constant]] ConstantBuffer consts : register(b2, space0); -groupshared uint histogram_shared[GROUP_SIZE]; +groupshared uint histogram_shared[GROUP_SIZE] ; [numthreads(THREADS_X, THREADS_Y, 1)] void comp(int group_idx: SV_GroupIndex) { const float min_log_lum = consts.params.x; - const float log_lum_range = consts.params.y; - const float time = consts.params.z; - const float num_pixels = consts.params.w; +const float log_lum_range = consts.params.y; +const float time = consts.params.z; +const float num_pixels = consts.params.w; - // Get the count from the hist buffer - uint count_this_bin = input[group_idx]; - histogram_shared[group_idx] = count_this_bin * group_idx; +// Get the count from the hist buffer +uint count_this_bin = input[group_idx]; +histogram_shared[group_idx] = count_this_bin * group_idx; - GroupMemoryBarrierWithGroupSync(); - - // Clear the input buffer now that we're done with it - input[group_idx] = 0; - - for (uint cutoff = (GROUP_SIZE >> 1); cutoff > 0; cutoff >>= 1) { - if (uint(group_idx) < cutoff) { - histogram_shared[group_idx] += histogram_shared[group_idx + cutoff]; - } - GroupMemoryBarrierWithGroupSync(); - } - - // only need to do this once - if (group_idx == 0) { - float weighted_log_avg = - (histogram_shared[0] / max(num_pixels - float(count_this_bin), 1.0)) - - 1.0; +GroupMemoryBarrierWithGroupSync(); - float weighted_avg_lum = - exp2(((weighted_log_avg / 254.0) * log_lum_range) + min_log_lum); +// Clear the input buffer now that we're done with it +input[group_idx] = 0; - float lum_last_frame = output[0].r; - float adapted_lum = - lum_last_frame + (weighted_avg_lum - lum_last_frame) * time; - output[0] = adapted_lum; +for (uint cutoff = (GROUP_SIZE >> 1); cutoff > 0; cutoff >>= 1) +{ + if (uint(group_idx) < cutoff) + { + histogram_shared[group_idx] += histogram_shared[group_idx + cutoff]; } + GroupMemoryBarrierWithGroupSync(); +} + +// only need to do this once +if (group_idx == 0) +{ + float weighted_log_avg = + (histogram_shared[0] / max(num_pixels - float(count_this_bin), 1.0)) - + 1.0; + + float weighted_avg_lum = + exp2(((weighted_log_avg / 254.0) * log_lum_range) + min_log_lum); + + float lum_last_frame = output[0].r; + float adapted_lum = + lum_last_frame + (weighted_avg_lum - lum_last_frame) * time; + output[0] = adapted_lum; +} } \ No newline at end of file diff --git a/source/lumhist.hlsl.cs b/source/lumhist.hlsl.cs index 0f9b6a41..3ce1ac93 100644 --- a/source/lumhist.hlsl.cs +++ b/source/lumhist.hlsl.cs @@ -1,4 +1,4 @@ -#include "luminance.h" +#include "tb_luminance.h" #define GROUP_SIZE 256 #define THREADS_X 16 @@ -15,12 +15,14 @@ [[vk::push_constant]] ConstantBuffer consts : register(b3, space0); -groupshared uint histogram_shared[GROUP_SIZE]; +groupshared uint histogram_shared[GROUP_SIZE] ; // Histogram bin lookup based on color and luminance range -uint color_to_hist(float3 color, float min_log_lum, float inv_log_lum_range) { +uint color_to_hist(float3 color, float min_log_lum, float inv_log_lum_range) +{ float lum = dot(color, RGB_TO_LUM); // convert RGB to Luminance - if (lum < EPSILON) { + if (lum < EPSILON) + { return 0; // avoid taking log of 0 } @@ -36,24 +38,25 @@ void comp(int group_idx: SV_GroupIndex, int3 dispatch_thread_id: SV_DispatchThreadID) { const float4 params = consts.params; - // Init and wait for all threads in the group to reach this point - histogram_shared[group_idx] = 0; - GroupMemoryBarrierWithGroupSync(); +// Init and wait for all threads in the group to reach this point +histogram_shared[group_idx] = 0; +GroupMemoryBarrierWithGroupSync(); - float2 dimensions = params.zw; +float2 dimensions = params.zw; - // Ignore any threads that map outside the image - if (dispatch_thread_id.x < dimensions.x && - dispatch_thread_id.y < dimensions.y) { - float2 uv = dispatch_thread_id.xy / dimensions; +// Ignore any threads that map outside the image +if (dispatch_thread_id.x < dimensions.x && + dispatch_thread_id.y < dimensions.y) +{ + float2 uv = dispatch_thread_id.xy / dimensions; - float3 color = input.SampleLevel(static_sampler, uv, 0).rgb; - uint idx = color_to_hist(color, params.x, params.y); - InterlockedAdd(histogram_shared[idx], 1); - } + float3 color = input.SampleLevel(static_sampler, uv, 0).rgb; + uint idx = color_to_hist(color, params.x, params.y); + InterlockedAdd(histogram_shared[idx], 1); +} - GroupMemoryBarrierWithGroupSync(); +GroupMemoryBarrierWithGroupSync(); - // Contention here should be low - InterlockedAdd(output[group_idx], histogram_shared[group_idx]); +// Contention here should be low +InterlockedAdd(output[group_idx], histogram_shared[group_idx]); } \ No newline at end of file diff --git a/source/materialsystem.c b/source/materialsystem.c deleted file mode 100644 index c88030a3..00000000 --- a/source/materialsystem.c +++ /dev/null @@ -1,528 +0,0 @@ -#include "materialsystem.h" - -#include "cgltf.h" -#include "common.hlsli" -#include "gltf.hlsli" -#include "hash.h" -#include "profiling.h" -#include "rendersystem.h" -#include "texturesystem.h" -#include "world.h" - -#include - -ECS_COMPONENT_DECLARE(TbMaterialSystem); - -typedef struct TbMaterial { - TbMaterialId id; - uint32_t ref_count; - TbMaterialPerm permutation; - TbBuffer gpu_buffer; - TbTextureId color_map; - TbTextureId normal_map; - TbTextureId metal_rough_map; -} TbMaterial; - -void tb_register_material_sys(TbWorld *world); -void tb_unregister_material_sys(TbWorld *world); - -TB_REGISTER_SYS(tb, material, TB_MAT_SYS_PRIO) - -uint32_t find_mat_by_hash(TbMaterialSystem *self, uint64_t id) { - TB_DYN_ARR_FOREACH(self->materials, i) { - if (TB_DYN_ARR_AT(self->materials, i).id.id == id) { - return i; - break; - } - } - return SDL_MAX_UINT32; -} - -TbMaterialSystem create_material_system(TbAllocator gp_alloc, - TbAllocator tmp_alloc, - TbRenderSystem *rnd_sys, - TbTextureSystem *tex_sys) { - TbMaterialSystem sys = { - .gp_alloc = gp_alloc, - .tmp_alloc = tmp_alloc, - .rnd_sys = rnd_sys, - .texture_system = tex_sys, - }; - - TB_DYN_ARR_RESET(sys.materials, gp_alloc, 1); - - // Create immutable sampler for materials - { - VkSamplerCreateInfo create_info = { - .sType = VK_STRUCTURE_TYPE_SAMPLER_CREATE_INFO, - .magFilter = VK_FILTER_LINEAR, - .minFilter = VK_FILTER_LINEAR, - .mipmapMode = VK_SAMPLER_MIPMAP_MODE_LINEAR, - .addressModeU = VK_SAMPLER_ADDRESS_MODE_REPEAT, - .addressModeV = VK_SAMPLER_ADDRESS_MODE_REPEAT, - .addressModeW = VK_SAMPLER_ADDRESS_MODE_REPEAT, - .anisotropyEnable = VK_TRUE, - .maxAnisotropy = 16.0f, // 16x anisotropy is cheap - .maxLod = 14.0f, // Hack; known number of mips for 8k textures - .borderColor = VK_BORDER_COLOR_FLOAT_OPAQUE_BLACK, - }; - tb_rnd_create_sampler(rnd_sys, &create_info, "Material Sampler", - &sys.sampler); - } - - // Create immutable sampler for sampling shadows - { - VkSamplerCreateInfo create_info = { - .sType = VK_STRUCTURE_TYPE_SAMPLER_CREATE_INFO, - .magFilter = VK_FILTER_NEAREST, - .minFilter = VK_FILTER_NEAREST, - .mipmapMode = VK_SAMPLER_MIPMAP_MODE_NEAREST, - .addressModeU = VK_SAMPLER_ADDRESS_MODE_CLAMP_TO_BORDER, - .addressModeV = VK_SAMPLER_ADDRESS_MODE_CLAMP_TO_BORDER, - .addressModeW = VK_SAMPLER_ADDRESS_MODE_CLAMP_TO_BORDER, - .anisotropyEnable = VK_FALSE, - .compareEnable = VK_TRUE, - .compareOp = VK_COMPARE_OP_LESS_OR_EQUAL, - .maxAnisotropy = 1.0f, - .maxLod = 1.0f, - .borderColor = VK_BORDER_COLOR_FLOAT_OPAQUE_WHITE, - }; - tb_rnd_create_sampler(rnd_sys, &create_info, "Material Shadow Sampler", - &sys.shadow_sampler); - } - - // Create descriptor set layout for materials - { - const VkDescriptorBindingFlags flags = - VK_DESCRIPTOR_BINDING_VARIABLE_DESCRIPTOR_COUNT_BIT | - VK_DESCRIPTOR_BINDING_PARTIALLY_BOUND_BIT; - const uint32_t binding_count = 3; - VkDescriptorSetLayoutCreateInfo create_info = { - .sType = VK_STRUCTURE_TYPE_DESCRIPTOR_SET_LAYOUT_CREATE_INFO, - .pNext = - &(VkDescriptorSetLayoutBindingFlagsCreateInfo){ - .sType = - VK_STRUCTURE_TYPE_DESCRIPTOR_SET_LAYOUT_BINDING_FLAGS_CREATE_INFO, - .bindingCount = binding_count, - .pBindingFlags = - (VkDescriptorBindingFlags[binding_count]){0, 0, flags}, - }, - .bindingCount = binding_count, - .pBindings = - (VkDescriptorSetLayoutBinding[binding_count]){ - {0, VK_DESCRIPTOR_TYPE_SAMPLER, 1, VK_SHADER_STAGE_FRAGMENT_BIT, - &sys.sampler}, - {1, VK_DESCRIPTOR_TYPE_SAMPLER, 1, VK_SHADER_STAGE_FRAGMENT_BIT, - &sys.shadow_sampler}, - {2, VK_DESCRIPTOR_TYPE_STORAGE_BUFFER, - 2048, // HACK: Some high upper limit - VK_SHADER_STAGE_FRAGMENT_BIT | VK_SHADER_STAGE_VERTEX_BIT, - NULL}, - }, - }; - tb_rnd_create_set_layout(rnd_sys, &create_info, "Material Set Layout", - &sys.set_layout); - } - - // Create a default material - // It uses the metallic roughness flow - // it does not supply textures, those will be provided by default since - // it is marked to use pbr metal rough but provides no texture views - cgltf_material *default_mat = tb_alloc_tp(gp_alloc, cgltf_material); - *default_mat = (cgltf_material){ - .name = "default", - .has_pbr_metallic_roughness = true, - .pbr_metallic_roughness = - { - .base_color_factor = {1, 1, 1, 1}, - .metallic_factor = 0.5f, - .roughness_factor = 0.5f, - }, - }; - sys.default_material = default_mat; - - return sys; -} - -void destroy_material_system(TbMaterialSystem *self) { - TbRenderSystem *rnd_sys = self->rnd_sys; - - tb_free(self->gp_alloc, (void *)self->default_material); - - tb_rnd_destroy_set_layout(rnd_sys, self->set_layout); - tb_rnd_destroy_sampler(rnd_sys, self->sampler); - tb_rnd_destroy_sampler(rnd_sys, self->shadow_sampler); - - TB_DYN_ARR_FOREACH(self->materials, i) { - if (TB_DYN_ARR_AT(self->materials, i).ref_count != 0) { - TB_CHECK(false, "Leaking materials"); - } - } - - TB_DYN_ARR_DESTROY(self->materials); - - *self = (TbMaterialSystem){0}; -} - -void tick_material_system(ecs_iter_t *it) { - ecs_world_t *ecs = it->world; - - tb_auto mat_sys = ecs_singleton_get_mut(ecs, TbMaterialSystem); - tb_auto rnd_sys = ecs_singleton_get_mut(ecs, TbRenderSystem); - - // If the number of materials has grown to the point where we have run out of - // space in the descriptor pool we must reallocate. That means destroying the - // old pool and creating a new pool for all the new writes - const uint64_t incoming_mat_count = TB_DYN_ARR_SIZE(mat_sys->materials); - if (incoming_mat_count > mat_sys->mat_pool.capacity) { - mat_sys->mat_pool.capacity = incoming_mat_count + 128; - const uint64_t desc_count = mat_sys->mat_pool.capacity; - - // Re-create pool and allocate the one set that everything will be bound to - { - VkDescriptorPoolCreateInfo create_info = { - .sType = VK_STRUCTURE_TYPE_DESCRIPTOR_POOL_CREATE_INFO, - .maxSets = mat_sys->mat_pool.capacity, - .poolSizeCount = 1, - .pPoolSizes = - (VkDescriptorPoolSize[1]){ - { - .type = VK_DESCRIPTOR_TYPE_STORAGE_BUFFER, - .descriptorCount = desc_count * 4, - }, - }, - }; - VkDescriptorSetVariableDescriptorCountAllocateInfo alloc_info = { - .sType = - VK_STRUCTURE_TYPE_DESCRIPTOR_SET_VARIABLE_DESCRIPTOR_COUNT_ALLOCATE_INFO, - .descriptorSetCount = 1, - .pDescriptorCounts = (uint32_t[1]){incoming_mat_count}, - }; - tb_rnd_resize_desc_pool(rnd_sys, &create_info, &mat_sys->set_layout, - &alloc_info, &mat_sys->mat_pool, 1); - } - } else { - // No work to do :) - return; - } - - // Just write all material buffer info to the descriptor set - tb_auto write = (VkWriteDescriptorSet){0}; - { - tb_auto mat_count = TB_DYN_ARR_SIZE(mat_sys->materials); - tb_auto buffer_info = - tb_alloc_nm_tp(mat_sys->tmp_alloc, mat_count, VkDescriptorBufferInfo); - - TB_DYN_ARR_FOREACH(mat_sys->materials, i) { - tb_auto material = &TB_DYN_ARR_AT(mat_sys->materials, i); - - buffer_info[i] = (VkDescriptorBufferInfo){ - .range = VK_WHOLE_SIZE, - .buffer = material->gpu_buffer.buffer, - }; - } - - write = (VkWriteDescriptorSet){ - .sType = VK_STRUCTURE_TYPE_WRITE_DESCRIPTOR_SET, - .descriptorCount = mat_count, - .descriptorType = VK_DESCRIPTOR_TYPE_STORAGE_BUFFER, - .dstSet = tb_mat_system_get_set(mat_sys), - .dstBinding = 2, - .pBufferInfo = buffer_info, - }; - } - - tb_rnd_update_descriptors(rnd_sys, 1, &write); -} - -void tb_register_material_sys(TbWorld *world) { - TracyCZoneN(ctx, "Register Material Sys", true); - ecs_world_t *ecs = world->ecs; - - ECS_COMPONENT_DEFINE(ecs, TbMaterialSystem); - - tb_auto rnd_sys = ecs_singleton_get_mut(ecs, TbRenderSystem); - tb_auto tex_sys = ecs_singleton_get_mut(ecs, TbTextureSystem); - - TbMaterialSystem sys = create_material_system( - world->gp_alloc, world->tmp_alloc, rnd_sys, tex_sys); - - ECS_SYSTEM(ecs, tick_material_system, EcsPreStore, - TbMaterialSystem(TbMaterialSystem)); - - // Sets a singleton based on the value at a pointer - ecs_set_ptr(ecs, ecs_id(TbMaterialSystem), TbMaterialSystem, &sys); - TracyCZoneEnd(ctx); -} - -void tb_unregister_material_sys(TbWorld *world) { - ecs_world_t *ecs = world->ecs; - - TbMaterialSystem *sys = ecs_singleton_get_mut(ecs, TbMaterialSystem); - destroy_material_system(sys); - ecs_singleton_remove(ecs, TbMaterialSystem); -} - -VkDescriptorSetLayout tb_mat_system_get_set_layout(TbMaterialSystem *self) { - return self->set_layout; -} - -TbMaterialId tb_mat_system_load_material(TbMaterialSystem *self, - const char *path, - const cgltf_material *mat) { - TracyCZoneN(ctx, "Load Material", true); - VkResult err = VK_SUCCESS; - - tb_auto tex_sys = self->texture_system; - - // Hash the materials's path and gltf name to get the id - uint64_t id = tb_hash(0, (const uint8_t *)path, SDL_strlen(path)); - id = tb_hash(id, (const uint8_t *)mat->name, SDL_strlen(mat->name)); - - uint32_t index = find_mat_by_hash(self, id); - - // Material was not found, load it now - if (index == SDL_MAX_UINT32) { - index = TB_DYN_ARR_SIZE(self->materials); - TB_DYN_ARR_APPEND(self->materials, (TbMaterial){0}); - tb_auto material = &TB_DYN_ARR_AT(self->materials, index); - - // Load material - { - // Find a suitable texture transform from the material - cgltf_texture_transform tex_trans = { - .scale = {1, 1}, - }; - if (mat) { - // Expecting that all textures in the material share the same texture - // transform - if (mat->has_pbr_metallic_roughness) { - tex_trans = mat->pbr_metallic_roughness.base_color_texture.transform; - } else if (mat->has_pbr_specular_glossiness) { - tex_trans = mat->pbr_specular_glossiness.diffuse_texture.transform; - } else if (mat->normal_texture.texture) { - tex_trans = mat->normal_texture.transform; - } - } - - // Determine feature permutations and load textures - { - TbTextureId metal_rough_id = tex_sys->default_metal_rough_tex; - TbTextureId color_id = tex_sys->default_color_tex; - TbTextureId normal_id = tex_sys->default_normal_tex; - - // GLTFpack strips image names so we have to synthesize something - char image_name[100] = {0}; - - TbMaterialPerm feat_perm = 0; - if (mat->has_pbr_metallic_roughness) { - feat_perm |= GLTF_PERM_PBR_METALLIC_ROUGHNESS; - - if (mat->pbr_metallic_roughness.metallic_roughness_texture.texture != - NULL) { - feat_perm |= GLTF_PERM_PBR_METAL_ROUGH_TEX; - SDL_snprintf(image_name, 100, "%s_metal", mat->name); - metal_rough_id = tb_tex_system_load_texture( - tex_sys, path, image_name, - mat->pbr_metallic_roughness.metallic_roughness_texture.texture); - } else { - tb_tex_system_take_tex_ref(tex_sys, metal_rough_id); - } - if (mat->pbr_metallic_roughness.base_color_texture.texture != NULL) { - feat_perm |= GLTF_PERM_BASE_COLOR_MAP; - SDL_snprintf(image_name, 100, "%s_color", mat->name); - color_id = tb_tex_system_load_texture( - tex_sys, path, image_name, - mat->pbr_metallic_roughness.base_color_texture.texture); - } else { - tb_tex_system_take_tex_ref(tex_sys, color_id); - } - - material->metal_rough_map = metal_rough_id; - material->color_map = color_id; - } - if (mat->has_pbr_specular_glossiness) { - feat_perm |= GLTF_PERM_PBR_SPECULAR_GLOSSINESS; - - if (mat->pbr_specular_glossiness.diffuse_texture.texture != NULL) { - feat_perm |= GLTF_PERM_BASE_COLOR_MAP; - SDL_snprintf(image_name, 100, "%s_color", mat->name); - color_id = tb_tex_system_load_texture( - tex_sys, path, image_name, - mat->pbr_metallic_roughness.base_color_texture.texture); - } else { - tb_tex_system_take_tex_ref(tex_sys, color_id); - } - material->color_map = color_id; - } - if (mat->has_clearcoat) { - feat_perm |= GLTF_PERM_CLEARCOAT; - } - if (mat->has_transmission) { - feat_perm |= GLTF_PERM_TRANSMISSION; - } - if (mat->has_volume) { - feat_perm |= GLTF_PERM_VOLUME; - } - if (mat->has_ior) { - feat_perm |= GLTF_PERM_IOR; - } - if (mat->has_specular) { - feat_perm |= GLTF_PERM_SPECULAR; - } - if (mat->has_sheen) { - feat_perm |= GLTF_PERM_SHEEN; - } - if (mat->unlit) { - feat_perm |= GLTF_PERM_UNLIT; - } - if (mat->alpha_mode == cgltf_alpha_mode_mask) { - feat_perm |= GLTF_PERM_ALPHA_CLIP; - } - if (mat->alpha_mode == cgltf_alpha_mode_blend) { - feat_perm |= GLTF_PERM_ALPHA_BLEND; - } - if (mat->double_sided) { - feat_perm |= GLTF_PERM_DOUBLE_SIDED; - } - if (mat->normal_texture.texture != NULL) { - feat_perm |= GLTF_PERM_NORMAL_MAP; - SDL_snprintf(image_name, 100, "%s_normal", mat->name); - normal_id = tb_tex_system_load_texture(tex_sys, path, image_name, - mat->normal_texture.texture); - } else { - tb_tex_system_take_tex_ref(tex_sys, normal_id); - } - material->normal_map = normal_id; - - material->permutation = feat_perm; - } - - // Gather material buffer data and copy to the GPU - { - TbGLTFMaterialData data = { - .tex_transform = - { - .offset = - (float2){ - tex_trans.offset[0], - tex_trans.offset[1], - }, - .scale = - (float2){ - tex_trans.scale[0], - tex_trans.scale[1], - }, - }, - .pbr_metallic_roughness = - { - .base_color_factor = - tb_atof4(mat->pbr_metallic_roughness.base_color_factor), - .metal_rough_factors = - {mat->pbr_metallic_roughness.metallic_factor, - mat->pbr_metallic_roughness.roughness_factor}, - }, - .pbr_specular_glossiness.diffuse_factor = - tb_atof4(mat->pbr_specular_glossiness.diffuse_factor), - .specular = tb_f3tof4( - tb_atof3(mat->pbr_specular_glossiness.specular_factor), - mat->pbr_specular_glossiness.glossiness_factor), - .emissives = tb_f3tof4(tb_atof3(mat->emissive_factor), 1.0f), - .perm = material->permutation, - .color_idx = tb_tex_system_get_index(tex_sys, material->color_map), - .normal_idx = - tb_tex_system_get_index(tex_sys, material->normal_map), - .pbr_idx = - tb_tex_system_get_index(tex_sys, material->metal_rough_map), - }; - - if (mat->has_emissive_strength) { - data.emissives[3] = mat->emissive_strength.emissive_strength; - } - if (mat->alpha_mode == cgltf_alpha_mode_mask) { - data.sheen_alpha[3] = mat->alpha_cutoff; - } - - // Send data to GPU - { - VkBufferCreateInfo create_info = { - .sType = VK_STRUCTURE_TYPE_BUFFER_CREATE_INFO, - .size = sizeof(TbGLTFMaterialData), - .usage = VK_BUFFER_USAGE_STORAGE_BUFFER_BIT | - VK_BUFFER_USAGE_TRANSFER_DST_BIT | - VK_BUFFER_USAGE_TRANSFER_SRC_BIT, - }; - // HACK: Known alignment for uniform buffers - err = tb_rnd_sys_create_gpu_buffer2_tmp(self->rnd_sys, &create_info, - &data, mat->name, - &material->gpu_buffer, 0x40); - TB_VK_CHECK_RET( - err, - "Failed to allocate material uniform data in tmp host buffer", - InvalidMaterialId); - } - } - } - - material->id = (TbMaterialId){id, index}; - material->ref_count = 0; - } else { - TbMaterial *material = &TB_DYN_ARR_AT(self->materials, index); - // If the material was already loaded, go through the textures and grab - // a reference so it's known that the texture is in use - tb_tex_system_take_tex_ref(tex_sys, material->color_map); - tb_tex_system_take_tex_ref(tex_sys, material->normal_map); - tb_tex_system_take_tex_ref(tex_sys, material->metal_rough_map); - } - - TB_DYN_ARR_AT(self->materials, index).ref_count++; - - TracyCZoneEnd(ctx); - return (TbMaterialId){id, index}; -} - -TbMaterialPerm tb_mat_system_get_perm(TbMaterialSystem *self, - TbMaterialId mat) { - const uint32_t index = mat.idx; - if (index == SDL_MAX_UINT32) { - TB_CHECK_RETURN(false, "Failed to find material to get permutation", 0); - } - return TB_DYN_ARR_AT(self->materials, index).permutation; -} - -VkDescriptorSet tb_mat_system_get_set(TbMaterialSystem *self) { - return self->mat_pool.sets[0]; -} - -void tb_mat_system_release_material_ref(TbMaterialSystem *self, - TbMaterialId mat) { - const uint32_t index = mat.idx; - if (index == SDL_MAX_UINT32) { - TB_CHECK(false, "Failed to find material to release"); - return; - } - TbMaterial *material = &TB_DYN_ARR_AT(self->materials, index); - - if (material->ref_count == 0) { - TB_CHECK(false, "Tried to release reference to material with 0 ref count"); - return; - } - - material->ref_count--; - - // Materials should always release their texture references since materials - // don't own their textures - tb_auto tex_sys = self->texture_system; - tb_tex_system_release_texture_ref(tex_sys, material->color_map); - tb_tex_system_release_texture_ref(tex_sys, material->normal_map); - tb_tex_system_release_texture_ref(tex_sys, material->metal_rough_map); - - if (material->ref_count == 0) { - // Free the mesh at this index - VmaAllocator vma_alloc = self->rnd_sys->vma_alloc; - - TbBuffer *gpu_buf = &material->gpu_buffer; - vmaDestroyBuffer(vma_alloc, gpu_buf->buffer, gpu_buf->alloc); - *gpu_buf = (TbBuffer){0}; - } -} diff --git a/source/meshcomponent.c b/source/meshcomponent.c deleted file mode 100644 index 66dff0ba..00000000 --- a/source/meshcomponent.c +++ /dev/null @@ -1,189 +0,0 @@ -#include "meshcomponent.h" - -#include "simd.h" - -#include "common.hlsli" -#include "json.h" -#include "materialsystem.h" -#include "meshsystem.h" -#include "profiling.h" -#include "renderobjectsystem.h" -#include "tbgltf.h" -#include "tbutil.h" -#include "world.h" - -ECS_COMPONENT_DECLARE(TbMeshComponent); - -bool create_mesh_component_internal(TbMeshComponent *self, TbMeshId id, - TbAllocator gp_alloc, - const char *source_path, - const cgltf_node *node, - TbMaterialSystem *mat_system) { - const uint32_t submesh_count = node->mesh->primitives_count; - - *self = (TbMeshComponent){ - .mesh_id = id, - }; - TB_DYN_ARR_RESET(self->submeshes, gp_alloc, submesh_count); - TB_DYN_ARR_RESIZE(self->submeshes, submesh_count); - - // Retrieve info about how to draw this mesh - uint64_t offset = 0; - { - TracyCZoneN(prim_ctx, "load primitives", true); - for (uint32_t prim_idx = 0; prim_idx < submesh_count; ++prim_idx) { - const cgltf_primitive *prim = &node->mesh->primitives[prim_idx]; - const cgltf_accessor *indices = prim->indices; - - tb_auto submesh = &TB_DYN_ARR_AT(self->submeshes, prim_idx); - - VkIndexType index_type = VK_INDEX_TYPE_UINT16; - if (indices->stride > 2) { - index_type = VK_INDEX_TYPE_UINT32; - } - - submesh->index_type = index_type; - submesh->index_count = indices->count; - submesh->index_offset = offset; - submesh->vertex_count = prim->attributes[0].data->count; - - // If no material is provided we use a default - const cgltf_material *material = prim->material; - if (material == NULL) { - material = mat_system->default_material; - } - - // Load materials - submesh->material = - tb_mat_system_load_material(mat_system, source_path, material); - TB_CHECK_RETURN(submesh->material.id, "Failed to load material", false); - - // calculate the aligned size - size_t index_size = - tb_calc_aligned_size(indices->count, indices->stride, 16); - // calculate number of indices that represent that aligned size - offset += (index_size / indices->stride); - } - - // While we determine the vertex offset we'll also calculate the local space - // TbAABB for this mesh across all primitives - self->local_aabb = tb_aabb_init(); - - // Determine the vertex offset for each primitive - uint32_t vertex_offset = 0; - for (uint32_t prim_idx = 0; prim_idx < submesh_count; ++prim_idx) { - const cgltf_primitive *prim = &node->mesh->primitives[prim_idx]; - tb_auto submesh = &TB_DYN_ARR_AT(self->submeshes, prim_idx); - - submesh->vertex_offset = vertex_offset; - vertex_offset += prim->attributes[0].data->count; - - // Determine input permutation and attribute count - uint64_t vertex_attributes = 0; - for (cgltf_size attr_idx = 0; attr_idx < prim->attributes_count; - ++attr_idx) { - cgltf_attribute_type type = prim->attributes[attr_idx].type; - int32_t index = prim->attributes[attr_idx].index; - if ((type == cgltf_attribute_type_position || - type == cgltf_attribute_type_normal || - type == cgltf_attribute_type_tangent || - type == cgltf_attribute_type_texcoord) && - index == 0) { - if (type == cgltf_attribute_type_position) { - vertex_attributes |= TB_INPUT_PERM_POSITION; - } else if (type == cgltf_attribute_type_normal) { - vertex_attributes |= TB_INPUT_PERM_NORMAL; - } else if (type == cgltf_attribute_type_tangent) { - vertex_attributes |= TB_INPUT_PERM_TANGENT; - } else if (type == cgltf_attribute_type_texcoord) { - vertex_attributes |= TB_INPUT_PERM_TEXCOORD0; - } - } - } - submesh->vertex_perm = vertex_attributes; - - // Read AABB from gltf - { - const cgltf_attribute *pos_attr = NULL; - // Find position attribute - for (size_t i = 0; i < prim->attributes_count; ++i) { - tb_auto attr = &prim->attributes[i]; - if (attr->type == cgltf_attribute_type_position) { - pos_attr = attr; - break; - } - } - - TB_CHECK(pos_attr, "Expected a position attribute"); - TB_CHECK(pos_attr->type == cgltf_attribute_type_position, - "Unexpected vertex attribute type"); - - float *min = pos_attr->data->min; - float *max = pos_attr->data->max; - - tb_aabb_add_point(&self->local_aabb, (float3){min[0], min[1], min[2]}); - tb_aabb_add_point(&self->local_aabb, (float3){max[0], max[1], max[2]}); - } - } - - TracyCZoneEnd(prim_ctx); - } - return true; -} - -void destroy_mesh_component_internal(TbMeshComponent *self, - TbMeshSystem *mesh_system, - TbMaterialSystem *mat_system) { - TB_DYN_ARR_FOREACH(self->submeshes, i) { - tb_mat_system_release_material_ref( - mat_system, TB_DYN_ARR_AT(self->submeshes, i).material); - } - TB_DYN_ARR_DESTROY(self->submeshes); - tb_mesh_system_release_mesh_ref(mesh_system, self->mesh_id); - - *self = (TbMeshComponent){0}; -} - -bool tb_load_mesh_comp(TbWorld *world, ecs_entity_t ent, - const char *source_path, const cgltf_node *node, - json_object *json) { - TracyCZoneN(ctx, "Load Mesh Component", true); - (void)json; - tb_auto ecs = world->ecs; - tb_auto mesh_sys = ecs_singleton_get_mut(ecs, TbMeshSystem); - tb_auto mat_sys = ecs_singleton_get_mut(ecs, TbMaterialSystem); - - // Load mesh - tb_auto id = tb_mesh_system_load_mesh(mesh_sys, source_path, node); - - TbMeshComponent comp = {0}; - bool ret = create_mesh_component_internal(&comp, id, world->gp_alloc, - source_path, node, mat_sys); - TB_CHECK(ret, "Failed to create mesh component"); - ecs_set_ptr(ecs, ent, TbMeshComponent, &comp); - - // Mark this entity as a render object - ecs_set(ecs, ent, TbRenderObject, {0}); - - TracyCZoneEnd(ctx); - return true; -} - -ecs_entity_t tb_register_mesh_comp(TbWorld *world) { - tb_auto ecs = world->ecs; - ECS_COMPONENT_DEFINE(ecs, TbMeshComponent); - - // Metadata for mesh component - ecs_struct(ecs, - { - .entity = ecs_id(TbMeshComponent), - .members = - { - {.name = "submesh_count", .type = ecs_id(ecs_u32_t)}, - }, - }); - - return 0; -} - -TB_REGISTER_COMP(tb, mesh) diff --git a/source/renderobjectsystem.c b/source/renderobjectsystem.c deleted file mode 100644 index e25edf37..00000000 --- a/source/renderobjectsystem.c +++ /dev/null @@ -1,225 +0,0 @@ -#include "renderobjectsystem.h" - -#include "common.hlsli" -#include "meshcomponent.h" -#include "profiling.h" -#include "rendersystem.h" -#include "tbcommon.h" -#include "transformcomponent.h" -#include "world.h" - -#include "blocks/Block.h" - -void tb_register_render_object_sys(TbWorld *world); -void tb_unregister_render_object_sys(TbWorld *world); - -TB_REGISTER_SYS(tb, render_object, TB_RND_OBJ_SYS_PRIO) - -ECS_COMPONENT_DECLARE(TbRenderObject); -ECS_COMPONENT_DECLARE(TbRenderObjectSystem); - -TbRenderObjectSystem create_render_object_system(TbAllocator gp_alloc, - TbAllocator tmp_alloc, - TbRenderSystem *rnd_sys) { - tb_auto sys = (TbRenderObjectSystem){ - .rnd_sys = rnd_sys, - .tmp_alloc = tmp_alloc, - .gp_alloc = gp_alloc, - }; - - // Create render object descriptor set layout - const VkDescriptorBindingFlags flags = - VK_DESCRIPTOR_BINDING_VARIABLE_DESCRIPTOR_COUNT_BIT | - VK_DESCRIPTOR_BINDING_PARTIALLY_BOUND_BIT; - tb_rnd_create_set_layout( - rnd_sys, - &(VkDescriptorSetLayoutCreateInfo){ - .sType = VK_STRUCTURE_TYPE_DESCRIPTOR_SET_LAYOUT_CREATE_INFO, - .pNext = - &(VkDescriptorSetLayoutBindingFlagsCreateInfo){ - .sType = - VK_STRUCTURE_TYPE_DESCRIPTOR_SET_LAYOUT_BINDING_FLAGS_CREATE_INFO, - .bindingCount = 1, - .pBindingFlags = (VkDescriptorBindingFlags[1]){flags}, - }, - .bindingCount = 1, - .pBindings = - &(VkDescriptorSetLayoutBinding){ - .binding = 0, - .descriptorCount = 2048, // HACK: Some high upper limit - .descriptorType = VK_DESCRIPTOR_TYPE_STORAGE_BUFFER, - .stageFlags = - VK_SHADER_STAGE_VERTEX_BIT | VK_SHADER_STAGE_FRAGMENT_BIT, - }, - }, - "Object Descriptor Set Layout", &sys.set_layout); - - return sys; -} - -void destroy_render_object_system(TbRenderObjectSystem *self) { - tb_rnd_destroy_set_layout(self->rnd_sys, self->set_layout); - - *self = (TbRenderObjectSystem){0}; -} - -void tick_render_object_system(ecs_iter_t *it) { - TracyCZoneNC(ctx, "Render Object System", TracyCategoryColorCore, true); - ecs_world_t *ecs = it->world; - - tb_auto rnd_sys = ecs_singleton_get_mut(ecs, TbRenderSystem); - tb_auto ro_sys = ecs_singleton_get_mut(ecs, TbRenderObjectSystem); - - TbTransformsBuffer *trans_buffer = &ro_sys->trans_buffers[rnd_sys->frame_idx]; - - int32_t prev_count = trans_buffer->obj_count; - int32_t obj_count = 0; - // Find object count by running query - ecs_iter_t obj_it = ecs_query_iter(ecs, ro_sys->obj_query); - while (ecs_query_next(&obj_it)) { - obj_count += obj_it.count; - } - trans_buffer->obj_count = obj_count; - - if (obj_count == 0) { - TracyCZoneEnd(ctx); - return; - } - - obj_it = ecs_query_iter(ecs, ro_sys->obj_query); // reset query - - TbCommonObjectData *obj_ptr = NULL; - if (obj_count > prev_count) { - // We need to resize the GPU buffer - tb_rnd_free_gpu_buffer(ro_sys->rnd_sys, &trans_buffer->gpu); - tb_auto create_info = (VkBufferCreateInfo){ - .sType = VK_STRUCTURE_TYPE_BUFFER_CREATE_INFO, - .size = sizeof(TbCommonObjectData) * obj_count, - .usage = VK_BUFFER_USAGE_TRANSFER_SRC_BIT | - VK_BUFFER_USAGE_TRANSFER_DST_BIT | - VK_BUFFER_USAGE_STORAGE_BUFFER_BIT, - }; - tb_rnd_sys_create_gpu_buffer(rnd_sys, &create_info, "TbTransform Buffer", - &trans_buffer->gpu, &trans_buffer->host, - (void **)&obj_ptr); - } else { - tb_rnd_sys_update_gpu_buffer(rnd_sys, &trans_buffer->gpu, - &trans_buffer->host, (void **)&obj_ptr); - } - - { - tb_auto obj_idx = 0ul; - while (ecs_query_next(&obj_it)) { - tb_auto rnd_objs = ecs_field(&obj_it, TbRenderObject, 2); - for (tb_auto i = 0; i < obj_it.count; ++i) { - tb_auto entity = obj_it.entities[i]; - // TODO: We want to only have to do this when transforms are dirty - // but we need to triple buffer the transform buffers to avoid - // stomping the transform and when a transform is dirty *all* transform - // buffers need that new data. - // Maybe we need some kind of queueing procedure? - obj_ptr[obj_idx].m = tb_transform_get_world_matrix(ecs, entity); - rnd_objs[i].index = obj_idx; - obj_idx++; - } - } - } - - // We can optimize this later but for now just always update this descriptor - // set every frame - if (trans_buffer->obj_count > 0) { - VkDescriptorPoolCreateInfo pool_info = { - .sType = VK_STRUCTURE_TYPE_DESCRIPTOR_POOL_CREATE_INFO, - .flags = VK_DESCRIPTOR_POOL_CREATE_UPDATE_AFTER_BIND_BIT, - .poolSizeCount = 1, - .pPoolSizes = (VkDescriptorPoolSize[1]){{ - .descriptorCount = trans_buffer->obj_count, - .type = VK_DESCRIPTOR_TYPE_STORAGE_BUFFER, - }}, - .maxSets = 1, - }; - VkDescriptorSetVariableDescriptorCountAllocateInfo alloc_info = { - .sType = - VK_STRUCTURE_TYPE_DESCRIPTOR_SET_VARIABLE_DESCRIPTOR_COUNT_ALLOCATE_INFO, - .descriptorSetCount = 1, - .pDescriptorCounts = (uint32_t[1]){trans_buffer->obj_count}, - }; - tb_rnd_frame_desc_pool_tick(rnd_sys, &pool_info, &ro_sys->set_layout, - &alloc_info, ro_sys->pools, 1); - - // Write all transform data to one descriptor - tb_auto buffer_info = tb_alloc_nm_tp( - rnd_sys->tmp_alloc, trans_buffer->obj_count, VkDescriptorBufferInfo); - - for (int32_t i = 0; i < trans_buffer->obj_count; ++i) { - buffer_info[i] = (VkDescriptorBufferInfo){ - .offset = sizeof(TbCommonObjectData) * i, - .range = sizeof(TbCommonObjectData), - .buffer = trans_buffer->gpu.buffer, - }; - } - - tb_auto write = (VkWriteDescriptorSet){ - .sType = VK_STRUCTURE_TYPE_WRITE_DESCRIPTOR_SET, - .descriptorCount = trans_buffer->obj_count, - .descriptorType = VK_DESCRIPTOR_TYPE_STORAGE_BUFFER, - .dstSet = tb_render_object_sys_get_set(ro_sys), - .pBufferInfo = buffer_info, - }; - tb_rnd_update_descriptors(rnd_sys, 1, &write); - } - TracyCZoneEnd(ctx); -} - -VkDescriptorSet tb_render_object_sys_get_set(TbRenderObjectSystem *sys) { - return tb_rnd_frame_desc_pool_get_set(sys->rnd_sys, sys->pools, 0); -} - -void tb_register_render_object_sys(TbWorld *world) { - TracyCZoneN(ctx, "Register Render Object Sys", true); - ecs_world_t *ecs = world->ecs; - - ECS_COMPONENT_DEFINE(ecs, TbRenderObject); - ECS_COMPONENT_DEFINE(ecs, TbRenderObjectSystem); - - // Metadata for TbRenderObject - ecs_struct(ecs, { - .entity = ecs_id(TbRenderObject), - .members = - { - {.name = "perm", .type = ecs_id(ecs_u32_t)}, - {.name = "index", .type = ecs_id(ecs_u32_t)}, - }, - }); - - tb_auto rnd_sys = ecs_singleton_get_mut(ecs, TbRenderSystem); - tb_auto sys = - create_render_object_system(world->gp_alloc, world->tmp_alloc, rnd_sys); - sys.obj_query = - ecs_query(ecs, { - .filter.terms = - { - { - .id = ecs_id(TbTransformComponent), - }, - { - .id = ecs_id(TbRenderObject), - }, - }, - }); - // Sets a singleton based on the value at a pointer - ecs_set_ptr(ecs, ecs_id(TbRenderObjectSystem), TbRenderObjectSystem, &sys); - - // Register a tick function - ECS_SYSTEM(ecs, tick_render_object_system, EcsPreStore, - TbRenderObjectSystem(TbRenderObjectSystem)); - TracyCZoneEnd(ctx); -} - -void tb_unregister_render_object_sys(TbWorld *world) { - ecs_world_t *ecs = world->ecs; - tb_auto sys = ecs_singleton_get_mut(ecs, TbRenderObjectSystem); - ecs_query_fini(sys->obj_query); - destroy_render_object_system(sys); - ecs_singleton_remove(ecs, TbRenderObjectSystem); -} diff --git a/source/sky.hlsl b/source/sky.hlsl index 87e261ed..048528f8 100644 --- a/source/sky.hlsl +++ b/source/sky.hlsl @@ -2,12 +2,13 @@ #include "common.hlsli" #include "sky_common.hlsli" -#include "simd.h" +#include "tb_simd.h" -ConstantBuffer sky_data : register(b0, space0); // Fragment Stage Only +ConstantBuffer sky_data + : register(b0, space0); // Fragment Stage Only -[[vk::push_constant]] ConstantBuffer consts - : register(b1, space0); +[[vk::push_constant]] +ConstantBuffer consts : register(b1, space0); struct VertexIn { float3 local_pos : SV_POSITION; diff --git a/source/skydome.c b/source/skydome.c index 950300ee..501fa63e 100644 --- a/source/skydome.c +++ b/source/skydome.c @@ -1,6 +1,6 @@ #include "skydome.h" -#include "simd.h" +#include "tb_simd.h" #include static const uint16_t skydome_indices[] = { diff --git a/source/allocator.c b/source/tb_allocator.c similarity index 75% rename from source/allocator.c rename to source/tb_allocator.c index ab4f333d..2088842d 100644 --- a/source/allocator.c +++ b/source/tb_allocator.c @@ -1,4 +1,4 @@ -#include "allocator.h" +#include "tb_allocator.h" #include #include @@ -6,13 +6,13 @@ #include #include -#include "profiling.h" +#include "tb_profiling.h" static void *global_alloc(void *user_data, size_t size) { (void)user_data; TracyCZone(ctx, true); TracyCZoneColor(ctx, TracyCategoryColorMemory); - void *ptr = mi_recalloc(NULL, 1, size); + void *ptr = mi_calloc(1, size); TracyCAllocN(ptr, size, "Global Alloc"); TracyCZoneEnd(ctx); return ptr; @@ -69,6 +69,82 @@ TbAllocator tb_global_alloc = { .free = global_free, }; +_Thread_local mi_heap_t *thread_heap = NULL; + +static void *thread_alloc(void *user_data, size_t size) { + (void)user_data; + // TracyCZone(ctx, true); + // TracyCZoneColor(ctx, TracyCategoryColorMemory); + if (thread_heap == NULL) { + thread_heap = mi_heap_new(); + } + void *ptr = mi_heap_malloc(thread_heap, size); + // TracyCAllocN(ptr, size, "Thread Alloc"); + // TracyCZoneEnd(ctx); + return ptr; +} + +static void *thread_alloc_aligned(void *user_data, size_t size, + size_t alignment) { + (void)user_data; + // TracyCZone(ctx, true); + // TracyCZoneColor(ctx, TracyCategoryColorMemory); + if (thread_heap == NULL) { + thread_heap = mi_heap_new(); + } + void *ptr = mi_heap_calloc_aligned(thread_heap, 1, size, alignment); + // TracyCAllocN(ptr, size, "Thread Alloc"); + // TracyCZoneEnd(ctx); + return ptr; +} + +static void *thread_realloc(void *user_data, void *original, size_t size) { + (void)user_data; + // TracyCZone(ctx, true); + // TracyCZoneColor(ctx, TracyCategoryColorMemory); + // TracyCFreeN(original, "Thread Alloc"); + if (thread_heap == NULL) { + thread_heap = mi_heap_new(); + } + void *ptr = mi_heap_recalloc(thread_heap, original, 1, size); + // TracyCAllocN(ptr, size, "Thread Alloc"); + // TracyCZoneEnd(ctx); + return ptr; +} + +static void *thread_realloc_aligned(void *user_data, void *original, + size_t size, size_t alignment) { + (void)user_data; + // TracyCZone(ctx, true); + // TracyCZoneColor(ctx, TracyCategoryColorMemory); + // TracyCFreeN(original, "Thread Alloc"); + if (thread_heap == NULL) { + thread_heap = mi_heap_new(); + } + void *ptr = + mi_heap_recalloc_aligned(thread_heap, original, 1, size, alignment); + // TracyCAllocN(ptr, size, "Thread Alloc"); + // TracyCZoneEnd(ctx); + return ptr; +} + +static void thread_free(void *user_data, void *ptr) { + (void)user_data; + // TracyCZone(ctx, true); + // TracyCZoneColor(ctx, TracyCategoryColorMemory); + // TracyCFreeN(ptr, "Thread Alloc"); + mi_free(ptr); + // TracyCZoneEnd(ctx); +} + +_Thread_local TbAllocator tb_thread_alloc = { + .alloc = thread_alloc, + .alloc_aligned = thread_alloc_aligned, + .realloc = thread_realloc, + .realloc_aligned = thread_realloc_aligned, + .free = thread_free, +}; + static void *arena_alloc(void *user_data, size_t size) { TracyCZone(ctx, true); TracyCZoneColor(ctx, TracyCategoryColorMemory); diff --git a/source/assets.c b/source/tb_assets.c similarity index 79% rename from source/assets.c rename to source/tb_assets.c index 5d62119a..e734e7a1 100644 --- a/source/assets.c +++ b/source/tb_assets.c @@ -1,24 +1,20 @@ -#include "assets.h" +#include "tb_assets.h" #include "cgltf.h" -#include "tbcommon.h" +#include "tb_common.h" +#include "tb_mmap.h" static cgltf_result sdl_read_glb(const struct cgltf_memory_options *memory_options, const struct cgltf_file_options *file_options, const char *path, cgltf_size *size, void **data) { - SDL_RWops *file = (SDL_RWops *)file_options->user_data; - cgltf_size file_size = (cgltf_size)SDL_RWsize(file); + (void)memory_options; (void)path; + tb_auto file = (SDL_RWops *)file_options->user_data; + tb_auto file_size = (cgltf_size)SDL_RWsize(file); - void *mem = memory_options->alloc_func(memory_options->user_data, file_size); - TB_CHECK_RETURN(mem, "clgtf out of memory.", cgltf_result_out_of_memory); - - size_t err = SDL_RWread(file, mem, file_size); - TB_CHECK_RETURN(err != 0, "clgtf io error.", cgltf_result_io_error); - + *data = tb_rw_mmap(file, file_size); *size = file_size; - *data = mem; return cgltf_result_success; } @@ -26,9 +22,11 @@ sdl_read_glb(const struct cgltf_memory_options *memory_options, static void sdl_release_glb(const struct cgltf_memory_options *memory_options, const struct cgltf_file_options *file_options, void *data) { - SDL_RWops *file = (SDL_RWops *)file_options->user_data; + (void)memory_options; - memory_options->free_func(memory_options->user_data, data); + tb_auto file = (SDL_RWops *)file_options->user_data; + tb_auto file_size = (cgltf_size)SDL_RWsize(file); + tb_rw_munmap(data, file_size); bool ok = SDL_RWclose(file) == 0; TB_CHECK(ok, "Failed to close glb file."); @@ -75,5 +73,6 @@ cgltf_data *tb_read_glb(TbAllocator gp_alloc, const char *path) { res = cgltf_validate(data); TB_CHECK_RETURN(res == cgltf_result_success, "Failed to validate glb.", NULL); #endif + return data; } diff --git a/source/audiosystem.c b/source/tb_audio_system.c similarity index 97% rename from source/audiosystem.c rename to source/tb_audio_system.c index a6b4595a..0c48c7c9 100644 --- a/source/audiosystem.c +++ b/source/tb_audio_system.c @@ -1,8 +1,8 @@ -#include "audiosystem.h" +#include "tb_audio_system.h" -#include "profiling.h" -#include "tbcommon.h" -#include "world.h" +#include "tb_common.h" +#include "tb_profiling.h" +#include "tb_world.h" #include diff --git a/source/bloom.c b/source/tb_bloom.c similarity index 99% rename from source/bloom.c rename to source/tb_bloom.c index 7b77571c..a43d0f46 100644 --- a/source/bloom.c +++ b/source/tb_bloom.c @@ -1,9 +1,9 @@ -#include "bloom.h" +#include "tb_bloom.h" -#include "profiling.h" -#include "renderpipelinesystem.h" +#include "tb_common.h" +#include "tb_profiling.h" +#include "tb_render_pipeline_system.h" #include "tb_shader_system.h" -#include "tbcommon.h" #pragma clang diagnostic push #pragma clang diagnostic ignored "-Wmissing-variable-declarations" diff --git a/source/cameracomponent.c b/source/tb_camera_component.c similarity index 71% rename from source/cameracomponent.c rename to source/tb_camera_component.c index 8e304757..4498cd72 100644 --- a/source/cameracomponent.c +++ b/source/tb_camera_component.c @@ -1,24 +1,22 @@ -#include "cameracomponent.h" +#include "tb_camera_component.h" -#include "camerasystem.h" #include "common.hlsli" -#include "rendersystem.h" -#include "renderthread.h" -#include "tbcommon.h" -#include "tbgltf.h" -#include "viewsystem.h" -#include "world.h" +#include "tb_camera_system.h" +#include "tb_common.h" +#include "tb_gltf.h" +#include "tb_render_system.h" +#include "tb_render_thread.h" +#include "tb_view_system.h" +#include "tb_world.h" ECS_COMPONENT_DECLARE(TbCameraComponent); -bool tb_load_camera_comp(TbWorld *world, ecs_entity_t ent, - const char *source_path, const cgltf_node *node, - json_object *json) { +bool tb_load_camera_comp(ecs_world_t *ecs, ecs_entity_t ent, + const char *source_path, const cgltf_data *data, + const cgltf_node *node, json_object *json) { (void)source_path; + (void)data; (void)json; - - tb_auto ecs = world->ecs; - tb_auto view_sys = ecs_singleton_get_mut(ecs, TbViewSystem); if (node->camera->type == cgltf_camera_type_perspective) { @@ -43,7 +41,7 @@ bool tb_load_camera_comp(TbWorld *world, ecs_entity_t ent, return true; } -ecs_entity_t tb_register_camera_comp(TbWorld *world) { +TbComponentRegisterResult tb_register_camera_comp(TbWorld *world) { ecs_world_t *ecs = world->ecs; ECS_COMPONENT_DEFINE(ecs, TbCameraComponent); @@ -64,7 +62,12 @@ ecs_entity_t tb_register_camera_comp(TbWorld *world) { }); // Returning 0 means we need no custom descriptor for editor UI - return 0; + return (TbComponentRegisterResult){ecs_id(TbCameraComponent), 0}; +} + +bool tb_ready_camera_comp(ecs_world_t *ecs, ecs_entity_t ent) { + tb_auto comp = ecs_get(ecs, ent, TbCameraComponent); + return comp != NULL; } TB_REGISTER_COMP(tb, camera) diff --git a/source/camerasystem.c b/source/tb_camera_system.c similarity index 90% rename from source/camerasystem.c rename to source/tb_camera_system.c index 4b88fe48..32f5de1f 100644 --- a/source/camerasystem.c +++ b/source/tb_camera_system.c @@ -1,14 +1,14 @@ -#include "camerasystem.h" +#include "tb_camera_system.h" -#include "cameracomponent.h" #include "common.hlsli" -#include "profiling.h" -#include "rendersystem.h" -#include "rendertargetsystem.h" -#include "tbcommon.h" -#include "transformcomponent.h" -#include "viewsystem.h" -#include "world.h" +#include "tb_camera_component.h" +#include "tb_common.h" +#include "tb_profiling.h" +#include "tb_render_system.h" +#include "tb_render_target_system.h" +#include "tb_transform_component.h" +#include "tb_view_system.h" +#include "tb_world.h" #include diff --git a/source/tb_cgltf.c b/source/tb_cgltf.c new file mode 100644 index 00000000..65831993 --- /dev/null +++ b/source/tb_cgltf.c @@ -0,0 +1,99 @@ +#pragma clang diagnostic push +#pragma clang diagnostic ignored "-Wdeprecated-declarations" +#pragma clang diagnostic ignored "-Wextra-semi-stmt" + +#define CGLTF_IMPLEMENTATION +#include "tb_gltf.h" + +#pragma clang diagnostic pop + +#include "tb_common.h" +#include "tb_profiling.h" +#include "tb_sdl.h" + +#include + +// Based on an example from this cgltf commit message: +// https://github.com/jkuhlmann/cgltf/commit/bd8bd2c9cc08ff9b75a9aa9f99091f7144665c60 +cgltf_result tb_decompress_buffer_view(TbAllocator alloc, + cgltf_buffer_view *view) { + TracyCZoneN(ctx, "Decompress Buffer", true); + if (view->data != NULL) { + // Already decoded + TracyCZoneEnd(ctx); + return cgltf_result_success; + } + + // Uncompressed buffer? No problem + if (!view->has_meshopt_compression) { + uint8_t *data = (uint8_t *)view->buffer->data; + data += view->offset; + + uint8_t *result = tb_alloc(alloc, view->size); + SDL_memcpy(result, data, view->size); // NOLINT + view->data = result; + TB_LOG_INFO(SDL_LOG_CATEGORY_SYSTEM, "%s", "Using Uncompressed Buffer"); + TracyCZoneEnd(ctx); + return cgltf_result_success; + } + + const cgltf_meshopt_compression *mc = &view->meshopt_compression; + uint8_t *data = (uint8_t *)mc->buffer->data; + data += mc->offset; + TB_CHECK_RETURN(data, "Invalid data", cgltf_result_invalid_gltf); + + uint8_t *result = tb_alloc(alloc, mc->count * mc->stride); + TB_CHECK_RETURN(result, "Failed to allocate space for decoded buffer view", + cgltf_result_out_of_memory); + + TracyCZoneN(decode_ctx, "Decoding", true); + int32_t res = -1; + switch (mc->mode) { + default: + case cgltf_meshopt_compression_mode_invalid: + break; + + case cgltf_meshopt_compression_mode_attributes: + res = meshopt_decodeVertexBuffer(result, mc->count, mc->stride, data, + mc->size); + break; + + case cgltf_meshopt_compression_mode_triangles: + res = meshopt_decodeIndexBuffer(result, mc->count, mc->stride, data, + mc->size); + break; + + case cgltf_meshopt_compression_mode_indices: + res = meshopt_decodeIndexSequence(result, mc->count, mc->stride, data, + mc->size); + break; + } + TB_CHECK_RETURN(res == 0, "Failed to decode buffer view", + cgltf_result_io_error); + TracyCZoneEnd(decode_ctx); + + TracyCZoneN(filter_ctx, "Filtering", true); + switch (mc->filter) { + default: + case cgltf_meshopt_compression_filter_none: + break; + + case cgltf_meshopt_compression_filter_octahedral: + meshopt_decodeFilterOct(result, mc->count, mc->stride); + break; + + case cgltf_meshopt_compression_filter_quaternion: + meshopt_decodeFilterQuat(result, mc->count, mc->stride); + break; + + case cgltf_meshopt_compression_filter_exponential: + meshopt_decodeFilterExp(result, mc->count, mc->stride); + break; + } + TracyCZoneEnd(filter_ctx); + + view->data = result; + + TracyCZoneEnd(ctx); + return cgltf_result_success; +} diff --git a/source/coreuisystem.c b/source/tb_coreui_system.c similarity index 95% rename from source/coreuisystem.c rename to source/tb_coreui_system.c index 204c7256..28b21a59 100644 --- a/source/coreuisystem.c +++ b/source/tb_coreui_system.c @@ -1,10 +1,10 @@ -#include "coreuisystem.h" +#include "tb_coreui_system.h" -#include "imguisystem.h" -#include "profiling.h" -#include "tbcommon.h" -#include "tbengineconfig.h" -#include "tbimgui.h" +#include "tb_common.h" +#include "tb_engine_config.h" +#include "tb_imgui.h" +#include "tb_imgui_system.h" +#include "tb_profiling.h" ECS_COMPONENT_DECLARE(TbCoreUISystem); diff --git a/source/tb_descriptor_buffer.c b/source/tb_descriptor_buffer.c new file mode 100644 index 00000000..212a24cd --- /dev/null +++ b/source/tb_descriptor_buffer.c @@ -0,0 +1,225 @@ +#include "tb_descriptor_buffer.h" + +#include "tb_common.h" +#include "tb_render_system.h" +#include "tb_util.h" + +#define TB_DESC_BUF_PAGE_SIZE 64 + +VkResult tb_resize_desc_buffer(TbRenderSystem *rnd_sys, uint32_t capacity, + TbDescriptorBuffer *out_buf) { + VkResult err = VK_SUCCESS; + + // The old buffer which we will copy from and schedule for clean-up later + TbDescriptorBuffer prev_buf = *out_buf; + + const uint32_t cap = capacity > 0 ? capacity : 1; + out_buf->desc_cap = cap; + + const VkDeviceSize buffer_size = capacity * out_buf->layout_size; + { + VkBufferCreateInfo create_info = { + .sType = VK_STRUCTURE_TYPE_BUFFER_CREATE_INFO, + .size = buffer_size, + .usage = VK_BUFFER_USAGE_RESOURCE_DESCRIPTOR_BUFFER_BIT_EXT | + VK_BUFFER_USAGE_SHADER_DEVICE_ADDRESS_BIT | + VK_BUFFER_USAGE_TRANSFER_SRC_BIT | + VK_BUFFER_USAGE_TRANSFER_DST_BIT, + }; + const char *buf_name = ""; +#ifndef FINAL + buf_name = out_buf->name; +#endif + err = tb_rnd_sys_create_gpu_buffer(rnd_sys, &create_info, buf_name, + &out_buf->buffer, &out_buf->host, + (void **)&out_buf->data_ptr); + TB_VK_CHECK(err, "Failed to create descriptor buffer"); + + // Did the previous generation buffer have contents that we should copy? + if (prev_buf.desc_count > 0) { + const VkDeviceSize prev_buf_size = + prev_buf.desc_cap * out_buf->layout_size; + // NOTE: This may be extremely expensive. Driver owned memory usually + // isn't supposed to be read from but maybe doing this all in one op will + // be okay. Maybe a vkCmdCopyBuffer instead? + SDL_memcpy(out_buf->data_ptr, prev_buf.data_ptr, prev_buf_size); + } + } + + // Free list can be initialized and all possible indices should be placed into + // the list + TB_DYN_ARR_RESET(out_buf->free_list, rnd_sys->gp_alloc, cap); + // Reverse iter so the last idx we append is 0 + // Iter from the previous buf's cap so that we only reports newly allocated + // indices as free. Existing free list is still correct + for (int32_t i = (int32_t)cap - 1; i >= (int32_t)prev_buf.desc_cap; --i) { + TB_DYN_ARR_APPEND(out_buf->free_list, i); + } + + return err; +} + +VkResult tb_create_descriptor_buffer(TbRenderSystem *rnd_sys, + VkDescriptorSetLayout layout, + const char *name, uint32_t capacity, + TbDescriptorBuffer *out_buf) { + const tb_auto alignment = + rnd_sys->render_thread->desc_buf_props.descriptorBufferOffsetAlignment; + + VkDevice device = rnd_sys->render_thread->device; + + VkDeviceSize layout_size = 0; + vkGetDescriptorSetLayoutSizeEXT(device, layout, &layout_size); + layout_size = tb_calc_aligned_size(1, layout_size, alignment); + + *out_buf = (TbDescriptorBuffer){ + .layout = layout, + .layout_size = layout_size, + }; + +#ifndef FINAL + const uint32_t name_len = SDL_strlen(name) + 1; + char *buf_name = tb_alloc_nm_tp(rnd_sys->gp_alloc, name_len, char); + SDL_strlcpy(buf_name, name, name_len); + out_buf->name = buf_name; +#endif + + VkResult err = tb_resize_desc_buffer(rnd_sys, capacity, out_buf); + TB_VK_CHECK(err, "Error occurred during resize"); + return err; +} + +void tb_destroy_descriptor_buffer(TbRenderSystem *rnd_sys, + TbDescriptorBuffer *buf) { + (void)rnd_sys; + (void)buf; + TB_CHECK(false, "Unimplemented"); +} + +VkDescriptorBufferBindingInfoEXT +tb_desc_buff_get_binding(const TbDescriptorBuffer *desc_buf) { + return (VkDescriptorBufferBindingInfoEXT){ + .sType = VK_STRUCTURE_TYPE_DESCRIPTOR_BUFFER_BINDING_INFO_EXT, + .address = desc_buf->buffer.address, + .usage = VK_BUFFER_USAGE_RESOURCE_DESCRIPTOR_BUFFER_BIT_EXT | + VK_BUFFER_USAGE_SHADER_DEVICE_ADDRESS_BIT | + VK_BUFFER_USAGE_TRANSFER_SRC_BIT | + VK_BUFFER_USAGE_TRANSFER_DST_BIT, + }; +} + +VkDeviceSize tb_lookup_desc_size( + VkDescriptorType type, + const VkPhysicalDeviceDescriptorBufferPropertiesEXT *props) { + switch (type) { + case VK_DESCRIPTOR_TYPE_SAMPLER: { + return props->samplerDescriptorSize; + } + case VK_DESCRIPTOR_TYPE_SAMPLED_IMAGE: { + return props->sampledImageDescriptorSize; + } + case VK_DESCRIPTOR_TYPE_STORAGE_IMAGE: { + return props->storageImageDescriptorSize; + } + case VK_DESCRIPTOR_TYPE_UNIFORM_TEXEL_BUFFER: { + return props->uniformTexelBufferDescriptorSize; + } + case VK_DESCRIPTOR_TYPE_STORAGE_TEXEL_BUFFER: { + return props->storageTexelBufferDescriptorSize; + } + case VK_DESCRIPTOR_TYPE_UNIFORM_BUFFER: { + return props->uniformBufferDescriptorSize; + } + case VK_DESCRIPTOR_TYPE_STORAGE_BUFFER: { + return props->storageBufferDescriptorSize; + } + default: { + TB_CHECK(false, "Failed to determine descriptor size"); + return 0; + } + } +} + +uint32_t tb_write_desc_to_buffer(TbRenderSystem *rnd_sys, + TbDescriptorBuffer *desc_buf, uint32_t binding, + const TbDescriptor *desc) { + // See if we need to resize the buffer + if (desc_buf->desc_count + 1 >= desc_buf->desc_cap) { + const uint32_t new_cap = desc_buf->desc_cap + TB_DESC_BUF_PAGE_SIZE; + VkResult err = tb_resize_desc_buffer(rnd_sys, new_cap, desc_buf); + TB_VK_CHECK(err, "Error occurred during resize"); + } + + // Take first free index + TB_CHECK(TB_DYN_ARR_SIZE(desc_buf->free_list) > 0, "Free list exhausted"); + uint32_t idx = *TB_DYN_ARR_BACKPTR(desc_buf->free_list); + TB_DYN_ARR_POP(desc_buf->free_list); + + VkDevice device = rnd_sys->render_thread->device; + + // Last minute look up binding offset + VkDeviceSize binding_offset = 0; + vkGetDescriptorSetLayoutBindingOffsetEXT(device, desc_buf->layout, binding, + &binding_offset); + VkDescriptorGetInfoEXT desc_info = { + .sType = VK_STRUCTURE_TYPE_DESCRIPTOR_GET_INFO_EXT, + .type = desc->type, + .data = desc->data, + }; + tb_auto desc_buf_props = &rnd_sys->render_thread->desc_buf_props; + tb_auto desc_size = tb_lookup_desc_size(desc->type, desc_buf_props); + + // Write to the buffer by having vkGetDescriptorExt output to the buf's ptr + VkDeviceSize offset = (idx * desc_size) + binding_offset; + uint8_t *ptr = desc_buf->data_ptr + offset; + vkGetDescriptorEXT(device, &desc_info, desc_size, ptr); + + // Host will be null if the buffer was mappable + if (desc_buf->host.buffer != VK_NULL_HANDLE) { + // NOTE: This could cause a lot of time spent processing buffer uploads + // Upload just the region of the buffer that changed + TbBufferCopy copy = { + .dst = desc_buf->buffer.buffer, + .src = desc_buf->host.buffer, + .region = {.srcOffset = offset, .dstOffset = offset, .size = desc_size}, + }; + tb_rnd_upload_buffers(rnd_sys, ©, 1); + } + + // Increase buffer count + desc_buf->desc_count++; + return idx; +} + +void tb_free_desc_from_buffer(TbDescriptorBuffer *desc_buf, uint32_t idx) { + TB_CHECK(desc_buf->desc_count > 0, "No descriptors exist to free"); + TB_CHECK(TB_DYN_ARR_SIZE(desc_buf->free_list) < desc_buf->desc_cap, + "No space for free index"); + // If the idx is already free, do nothing + TB_DYN_ARR_FOREACH(desc_buf->free_list, i) { + tb_auto free_idx = TB_DYN_ARR_AT(desc_buf->free_list, i); + if (free_idx == idx) { + return; + } + } + // We could be nice and set the data at ptr to 0 but that is unnecessary + TB_DYN_ARR_APPEND(desc_buf->free_list, idx); + --desc_buf->desc_count; +} + +// Resets the internal free-list and invalidates all previously written +// descriptors +void tb_reset_descriptor_buffer(TbRenderSystem *rnd_sys, + TbDescriptorBuffer *desc_buf) { + tb_auto cap = desc_buf->desc_cap; + // Free list can be initialized and all possible indices should be placed into + // the list + TB_DYN_ARR_RESET(desc_buf->free_list, rnd_sys->gp_alloc, cap); + // Reverse iter so the last idx we append is 0 + // Iter from the previous buf's cap so that we only reports newly allocated + // indices as free. Existing free list is still correct + for (int32_t i = (int32_t)cap - 1; i >= 0; --i) { + TB_DYN_ARR_APPEND(desc_buf->free_list, i); + } + desc_buf->desc_count = 0; +} diff --git a/source/tb_dyn_desc_pool.c b/source/tb_dyn_desc_pool.c new file mode 100644 index 00000000..526bbadd --- /dev/null +++ b/source/tb_dyn_desc_pool.c @@ -0,0 +1,141 @@ +#include "tb_dyn_desc_pool.h" +#include "tb_common.h" +#include "tb_util.h" + +void tb_create_dyn_desc_pool(TbRenderSystem *rnd_sys, const char *name, + VkDescriptorSetLayout layout, + VkDescriptorType type, uint32_t desc_cap, + TbDynDescPool *pool, uint32_t binding) { + TB_TRACY_SCOPE("Create Dynamic Descriptor Pool"); + *pool = (TbDynDescPool){ + .layout = layout, + .binding = binding, + .type = type, + .desc_cap = desc_cap, + }; + tb_reset_free_list(rnd_sys->gp_alloc, &pool->free_list, pool->desc_cap); + for (uint32_t frame_idx = 0; frame_idx < TB_MAX_FRAME_STATES; ++frame_idx) { + VkDescriptorPoolCreateInfo create_info = { + .sType = VK_STRUCTURE_TYPE_DESCRIPTOR_POOL_CREATE_INFO, + .flags = VK_DESCRIPTOR_POOL_CREATE_UPDATE_AFTER_BIND_BIT, + .maxSets = 1, + .poolSizeCount = 1, + .pPoolSizes = + (VkDescriptorPoolSize[1]){ + { + .type = pool->type, + .descriptorCount = pool->desc_cap, + }, + }, + }; + tb_rnd_create_descriptor_pool(rnd_sys, &create_info, name, + &pool->pools[frame_idx]); + VkDescriptorSetVariableDescriptorCountAllocateInfo count_info = { + .sType = + VK_STRUCTURE_TYPE_DESCRIPTOR_SET_VARIABLE_DESCRIPTOR_COUNT_ALLOCATE_INFO, + .descriptorSetCount = 1, + .pDescriptorCounts = (uint32_t[1]){pool->desc_cap}, + }; + VkDescriptorSetAllocateInfo alloc_info = { + .sType = VK_STRUCTURE_TYPE_DESCRIPTOR_SET_ALLOCATE_INFO, + .pNext = &count_info, + .descriptorSetCount = 1, + .descriptorPool = pool->pools[frame_idx], + .pSetLayouts = &pool->layout, + }; + tb_rnd_alloc_descriptor_sets(rnd_sys, name, &alloc_info, + &pool->sets[frame_idx]); + + TB_QUEUE_RESET(pool->write_queues[frame_idx], rnd_sys->gp_alloc, desc_cap); + } +} + +bool tb_write_dyn_desc_pool(TbDynDescPool *pool, uint32_t write_count, + const TbDynDescWrite *writes, uint32_t *out_idxs) { + TB_TRACY_SCOPE("Write Dynamic Descriptor Pool"); + if (write_count == 0) { + return true; + } + TB_CHECK_RETURN(TB_DYN_ARR_SIZE(pool->free_list) >= write_count, + "Not enough space for writes", false); + + for (uint32_t i = 0; i < write_count; ++i) { + uint32_t free_idx = 0; + bool idx_ok = tb_pull_index(&pool->free_list, &free_idx); + TB_CHECK(idx_ok, "Failed to retrieve index from free list"); + + tb_auto desc_write = &writes[i]; + TB_CHECK(pool->type == desc_write->type, "Invalid write type"); + + // Need to enqueue a write per frame + for (uint32_t frame_idx = 0; frame_idx < TB_MAX_FRAME_STATES; ++frame_idx) { + tb_auto write_queue = &pool->write_queues[frame_idx]; + + VkWriteDescriptorSet write = { + .sType = VK_STRUCTURE_TYPE_WRITE_DESCRIPTOR_SET, + .descriptorCount = 1, + .descriptorType = pool->type, + .dstBinding = pool->binding, + .dstSet = pool->sets[frame_idx], + .dstArrayElement = free_idx, + }; + + if (pool->type == VK_DESCRIPTOR_TYPE_SAMPLED_IMAGE || + pool->type == VK_DESCRIPTOR_TYPE_STORAGE_IMAGE || + pool->type == VK_DESCRIPTOR_TYPE_COMBINED_IMAGE_SAMPLER) { + write.pImageInfo = &desc_write->desc.image; + if (write.pImageInfo->imageView == VK_NULL_HANDLE) { + continue; + } + } else if (pool->type == VK_DESCRIPTOR_TYPE_UNIFORM_TEXEL_BUFFER || + pool->type == VK_DESCRIPTOR_TYPE_STORAGE_TEXEL_BUFFER) { + write.pTexelBufferView = &desc_write->desc.texel_buffer; + if (write.pTexelBufferView == VK_NULL_HANDLE) { + continue; + } + } else if (pool->type == VK_DESCRIPTOR_TYPE_UNIFORM_BUFFER || + pool->type == VK_DESCRIPTOR_TYPE_STORAGE_BUFFER) { + write.pBufferInfo = &desc_write->desc.buffer; + if (write.pBufferInfo->buffer == VK_NULL_HANDLE) { + continue; + } + } else { + TB_CHECK(false, "Unexpected descriptor type"); + } + + TB_QUEUE_PUSH_PTR(write_queue, write); + } + if (out_idxs) { + out_idxs[i] = free_idx; + } + } + return true; +} + +void tb_tick_dyn_desc_pool(TbRenderSystem *rnd_sys, TbDynDescPool *pool) { + TB_TRACY_SCOPE("Tick Dynamic Descriptor Pool"); + const uint32_t frame_idx = rnd_sys->frame_idx; + tb_auto write_queue = &pool->write_queues[frame_idx]; + + // Render Thread allocator to make sure writes live + tb_auto rnd_tmp_alloc = + rnd_sys->render_thread->frame_states[rnd_sys->frame_idx].tmp_alloc.alloc; + + // Dequeue to a local collection + TB_DYN_ARR_OF(VkWriteDescriptorSet) writes = {0}; + TB_DYN_ARR_RESET(writes, rnd_tmp_alloc, pool->desc_cap); + VkWriteDescriptorSet write = {0}; + while (TB_QUEUE_POP(*write_queue, &write)) { + TB_DYN_ARR_APPEND(writes, write); + } + + // Issue any writes that were gathered + if (!TB_DYN_ARR_EMPTY(writes)) { + tb_rnd_update_descriptors(rnd_sys, TB_DYN_ARR_SIZE(writes), writes.data); + } +} + +VkDescriptorSet tb_dyn_desc_pool_get_set(TbRenderSystem *rnd_sys, + const TbDynDescPool *pool) { + return pool->sets[rnd_sys->frame_idx]; +} diff --git a/source/tb_free_list.c b/source/tb_free_list.c new file mode 100644 index 00000000..fa78a774 --- /dev/null +++ b/source/tb_free_list.c @@ -0,0 +1,35 @@ +#include "tb_free_list.h" +#include "tb_common.h" + +void tb_reset_free_list(TbAllocator alloc, TbFreeList *free_list, + uint32_t capacity) { + TB_DYN_ARR_RESET(*free_list, alloc, capacity); + TB_DYN_ARR_RESERVE(*free_list, capacity); + // Reverse iter so the last idx we append is 0 which will make sense as the + // first index to pop + for (int32_t i = (int32_t)capacity - 1; i >= 0; --i) { + TB_DYN_ARR_APPEND(*free_list, i); + } +} + +bool tb_pull_index(TbFreeList *free_list, uint32_t *out_idx) { + TB_CHECK(out_idx, "Invalid output pointer"); + if (TB_DYN_ARR_SIZE(*free_list) <= 0) { + TB_CHECK(false, "Free list exhausted"); + return false; + } + + *out_idx = *TB_DYN_ARR_BACKPTR(*free_list); + TB_DYN_ARR_POP(*free_list); + return true; +} + +void tb_return_index(TbFreeList *free_list, uint32_t idx) { + TB_CHECK(TB_DYN_ARR_SIZE(*free_list) < free_list->capacity, + "No space to return index to"); + TB_DYN_ARR_APPEND(*free_list, idx); +} + +void tb_destroy_free_list(TbFreeList *free_list) { + TB_DYN_ARR_DESTROY(*free_list); +} diff --git a/source/fxaa.c b/source/tb_fxaa.c similarity index 96% rename from source/fxaa.c rename to source/tb_fxaa.c index 7ab8da05..a50f0693 100644 --- a/source/fxaa.c +++ b/source/tb_fxaa.c @@ -1,13 +1,13 @@ -#include "fxaa.h" - -#include "profiling.h" -#include "renderpipelinesystem.h" -#include "rendersystem.h" -#include "rendertargetsystem.h" +#include "tb_fxaa.h" + +#include "tb_common.h" +#include "tb_imgui.h" +#include "tb_profiling.h" +#include "tb_render_pipeline_system.h" +#include "tb_render_system.h" +#include "tb_render_target_system.h" #include "tb_shader_system.h" -#include "tbcommon.h" -#include "tbimgui.h" -#include "world.h" +#include "tb_world.h" ECS_COMPONENT_DECLARE(TbFXAASystem); @@ -84,8 +84,9 @@ void tick_fxaa_draw(ecs_iter_t *it) { }, }, }; - tb_rnd_frame_desc_pool_tick(rnd_sys, &create_info, &self->set_layout, NULL, - self->pools.pools, 1); + tb_rnd_frame_desc_pool_tick(rnd_sys, "fxaa", &create_info, + &self->set_layout, NULL, self->pools.pools, 1, + 1); VkDescriptorSet set = tb_rnd_frame_desc_pool_get_set(rnd_sys, self->pools.pools, 0); diff --git a/source/hash.c b/source/tb_hash.c similarity index 90% rename from source/hash.c rename to source/tb_hash.c index a7627877..e138db7d 100644 --- a/source/hash.c +++ b/source/tb_hash.c @@ -1,4 +1,4 @@ -#include "hash.h" +#include "tb_hash.h" uint64_t tb_hash(uint64_t hash, const uint8_t *data, uint64_t len) { int32_t c; diff --git a/source/imguisystem.c b/source/tb_imgui_system.c similarity index 97% rename from source/imguisystem.c rename to source/tb_imgui_system.c index 48d47016..422776d7 100644 --- a/source/imguisystem.c +++ b/source/tb_imgui_system.c @@ -1,4 +1,4 @@ -#include "imguisystem.h" +#include "tb_imgui_system.h" // Ignore some warnings for the generated headers #pragma clang diagnostic push @@ -7,18 +7,18 @@ #include "imgui_vert.h" #pragma clang diagnostic pop -#include "inputsystem.h" -#include "profiling.h" -#include "renderpipelinesystem.h" -#include "rendersystem.h" -#include "rendertargetsystem.h" -#include "shadercommon.h" +#include "tb_common.h" +#include "tb_imgui.h" +#include "tb_input_system.h" +#include "tb_profiling.h" +#include "tb_render_pipeline_system.h" +#include "tb_render_system.h" +#include "tb_render_target_system.h" +#include "tb_shader_common.h" #include "tb_shader_system.h" #include "tb_task_scheduler.h" -#include "tbcommon.h" -#include "tbimgui.h" -#include "tbvma.h" -#include "vkdbg.h" +#include "tb_vk_dbg.h" +#include "tb_vma.h" ECS_COMPONENT_DECLARE(TbImGuiSystem); @@ -615,8 +615,8 @@ void imgui_descriptor_sys(ecs_iter_t *it) { layouts[i] = ig_sys->set_layout; } - tb_rnd_frame_desc_pool_tick(rnd_sys, &create_info, layouts, NULL, - ig_sys->desc_pools, set_count); + tb_rnd_frame_desc_pool_tick(rnd_sys, "imgui", &create_info, layouts, NULL, + ig_sys->desc_pools, set_count, set_count); } tb_auto tmp_alloc = ig_sys->tmp_alloc; @@ -670,8 +670,8 @@ void imgui_draw_sys(ecs_iter_t *it) { // we *require* the imgui shader be ready by this point // so wait for it if necessary { - static bool ready = false; - if (!ready && !tb_is_shader_ready(it->world, ig_sys->shader)) { + bool ready = tb_is_shader_ready(it->world, ig_sys->shader); + if (!ready) { // we *require* the imgui shader be ready by this point // so wait for it if necessary // If we get a false back that means we couldn't verify @@ -680,6 +680,13 @@ void imgui_draw_sys(ecs_iter_t *it) { } if (!ready) { + // If we're bailing we need to advance a frame for imgui's sake + for (uint32_t imgui_idx = 0; imgui_idx < ctx_count; ++imgui_idx) { + tb_auto ui_ctx = &ig_sys->contexts[imgui_idx]; + igSetCurrentContext(ui_ctx->context); + igRender(); + } + TracyCZoneEnd(ctx); return; } diff --git a/source/inputsystem.c b/source/tb_input_system.c similarity index 98% rename from source/inputsystem.c rename to source/tb_input_system.c index d31a1b67..985f8304 100644 --- a/source/inputsystem.c +++ b/source/tb_input_system.c @@ -1,8 +1,8 @@ -#include "inputsystem.h" +#include "tb_input_system.h" -#include "profiling.h" -#include "tbcommon.h" -#include "tbsdl.h" +#include "tb_common.h" +#include "tb_profiling.h" +#include "tb_sdl.h" ECS_COMPONENT_DECLARE(TbInputSystem); diff --git a/source/lightcomponent.c b/source/tb_light_component.c similarity index 54% rename from source/lightcomponent.c rename to source/tb_light_component.c index 62bb0dee..9eadd6cf 100644 --- a/source/lightcomponent.c +++ b/source/tb_light_component.c @@ -1,23 +1,22 @@ -#include "lightcomponent.h" +#include "tb_light_component.h" -#include "lightsystem.h" -#include "tbcommon.h" -#include "tbgltf.h" -#include "viewsystem.h" -#include "world.h" +#include "tb_common.h" +#include "tb_gltf.h" +#include "tb_light_system.h" +#include "tb_view_system.h" +#include "tb_world.h" #include ECS_COMPONENT_DECLARE(TbDirectionalLightComponent); -bool tb_load_light_comp(TbWorld *world, ecs_entity_t ent, - const char *source_path, const cgltf_node *node, - json_object *json) { +bool tb_load_light_comp(ecs_world_t *ecs, ecs_entity_t ent, + const char *source_path, const cgltf_data *data, + const cgltf_node *node, json_object *json) { (void)source_path; + (void)data; (void)json; - ecs_world_t *ecs = world->ecs; - tb_auto view_sys = ecs_singleton_get_mut(ecs, TbViewSystem); tb_auto light = node->light; @@ -35,11 +34,16 @@ bool tb_load_light_comp(TbWorld *world, ecs_entity_t ent, return true; } -ecs_entity_t tb_register_light_comp(TbWorld *world) { +TbComponentRegisterResult tb_register_light_comp(TbWorld *world) { ecs_world_t *ecs = world->ecs; ECS_COMPONENT_DEFINE(ecs, TbDirectionalLightComponent); // Returning 0 means we need no custom descriptor for editor UI - return 0; + return (TbComponentRegisterResult){ecs_id(TbDirectionalLightComponent), 0}; +} + +bool tb_ready_light_comp(ecs_world_t *ecs, ecs_entity_t ent) { + tb_auto comp = ecs_get(ecs, ent, TbDirectionalLightComponent); + return comp != NULL; } TB_REGISTER_COMP(tb, light) diff --git a/source/lightsystem.c b/source/tb_light_system.c similarity index 93% rename from source/lightsystem.c rename to source/tb_light_system.c index 17e748ed..95f47034 100644 --- a/source/lightsystem.c +++ b/source/tb_light_system.c @@ -1,11 +1,11 @@ -#include "lightsystem.h" - -#include "cameracomponent.h" -#include "profiling.h" -#include "tbgltf.h" -#include "transformcomponent.h" -#include "viewsystem.h" -#include "world.h" +#include "tb_light_system.h" + +#include "tb_camera_component.h" +#include "tb_gltf.h" +#include "tb_profiling.h" +#include "tb_transform_component.h" +#include "tb_view_system.h" +#include "tb_world.h" ECS_COMPONENT_DECLARE(TbLightSystem); diff --git a/source/tb_loading_ui.c b/source/tb_loading_ui.c new file mode 100644 index 00000000..dd8f57f0 --- /dev/null +++ b/source/tb_loading_ui.c @@ -0,0 +1,143 @@ +#include "tb_common.h" +#include "tb_imgui.h" +#include "tb_material_system.h" +#include "tb_mesh_component.h" +#include "tb_mesh_system.h" +#include "tb_system_priority.h" +#include "tb_world.h" + +typedef struct TbLoadUICtx { + bool visible; +} TbLoadUICtx; +ECS_COMPONENT_DECLARE(TbLoadUICtx); + +// From tb_scene.c +typedef uint32_t TbSceneEntityCount; +typedef uint32_t TbSceneEntParseCounter; +typedef uint32_t TbSceneEntReadyCounter; +extern ECS_COMPONENT_DECLARE(TbSceneEntityCount); +extern ECS_COMPONENT_DECLARE(TbSceneEntParseCounter); +extern ECS_COMPONENT_DECLARE(TbSceneEntReadyCounter); + +void tb_load_ui_tick(ecs_iter_t *it) { + tb_auto ecs = it->world; + + if (igBegin("Loading", NULL, 0)) { + + uint64_t total_counter = 0; + uint64_t counter = 0; + + // For each scene root + for (int32_t i = 0; i < it->count; ++i) { + TbScene scene = it->entities[i]; + tb_auto scene_name = ecs_get_name(ecs, scene); + tb_auto loaded_state = + tb_is_scene_ready(ecs, scene) ? "Ready" : "Loading"; + igText("Scene %s - : %s", scene_name, loaded_state); + + if (ecs_has(ecs, scene, TbSceneEntityCount) && + ecs_has(ecs, scene, TbSceneEntParseCounter) && + ecs_has(ecs, scene, TbSceneEntReadyCounter)) { + tb_auto ent_count = *ecs_get(ecs, scene, TbSceneEntityCount); + tb_auto ents_to_parse = *ecs_get(ecs, scene, TbSceneEntParseCounter); + tb_auto ents_ready = *ecs_get(ecs, scene, TbSceneEntReadyCounter); + if (ents_to_parse > 0) { + igText("%d/%d to parse", ents_to_parse, ent_count); + } + igText("%d/%d ready", ents_ready, ent_count); + + total_counter += ent_count; + counter += ents_ready; + } + } + + // Check Mesh State + { + uint64_t mesh_count = 0; + uint64_t ready_mesh_count = 0; + tb_auto mesh_filter = + ecs_filter(ecs, { + .terms = {{.id = ecs_id(TbMeshComponent)}}, + }); + tb_auto mesh_it = ecs_filter_iter(ecs, mesh_filter); + while (ecs_iter_next(&mesh_it)) { + mesh_count += mesh_it.count; + tb_auto mesh_comps = ecs_field(&mesh_it, TbMeshComponent, 1); + for (int32_t i = 0; i < mesh_it.count; ++i) { + if (tb_is_mesh_ready(ecs, mesh_comps[i].mesh2)) { + ready_mesh_count++; + } + } + } + total_counter += mesh_count; + counter += ready_mesh_count; + igText("Meshes %d/%d", ready_mesh_count, mesh_count); + ecs_filter_fini(mesh_filter); + } + + // Check Material State + { + uint64_t mat_count = 0; + uint64_t ready_mat_count = 0; + tb_auto mat_filter = + ecs_filter(ecs, { + .terms = {{.id = ecs_id(TbMaterialComponent)}}, + }); + tb_auto mat_it = ecs_filter_iter(ecs, mat_filter); + while (ecs_iter_next(&mat_it)) { + mat_count += mat_it.count; + for (int32_t i = 0; i < mat_it.count; ++i) { + if (tb_is_material_ready(ecs, mat_it.entities[i])) { + ready_mat_count++; + } + } + } + total_counter += mat_count; + counter += ready_mat_count; + igText("Materials %d/%d", ready_mat_count, mat_count); + ecs_filter_fini(mat_filter); + } + + // Check Texture State + { + uint64_t tex_count = 0; + uint64_t ready_tex_count = 0; + tb_auto tex_filter = + ecs_filter(ecs, { + .terms = {{.id = ecs_id(TbTextureComponent)}}, + }); + tb_auto tex_it = ecs_filter_iter(ecs, tex_filter); + while (ecs_iter_next(&tex_it)) { + tex_count += tex_it.count; + for (int32_t i = 0; i < tex_it.count; ++i) { + if (tb_is_texture_ready(ecs, tex_it.entities[i])) { + ready_tex_count++; + } + } + } + total_counter += tex_count; + counter += ready_tex_count; + igText("Textures %d/%d", ready_tex_count, tex_count); + ecs_filter_fini(tex_filter); + } + + if (total_counter > 0) { + igProgressBar((float)counter / (float)total_counter, (ImVec2){0}, NULL); + } + + igEnd(); + } +} + +void tb_register_load_ui_sys(TbWorld *world) { + tb_auto ecs = world->ecs; + ECS_COMPONENT_DEFINE(ecs, TbLoadUICtx); + + ECS_SYSTEM(ecs, tb_load_ui_tick, EcsOnUpdate, TbSceneRoot); + + ecs_singleton_set(ecs, TbLoadUICtx, {true}); +} + +void tb_unregister_load_ui_sys(TbWorld *world) { (void)world; } + +TB_REGISTER_SYS(tb, load_ui, TB_SYSTEM_NORMAL) diff --git a/source/logsystem.c b/source/tb_log_system.c similarity index 88% rename from source/logsystem.c rename to source/tb_log_system.c index adc8a51b..88f7b168 100644 --- a/source/logsystem.c +++ b/source/tb_log_system.c @@ -1,11 +1,29 @@ -#include "logsystem.h" - -#include "coreuisystem.h" -#include "dynarray.h" -#include "profiling.h" -#include "tbimgui.h" -#include "world.h" - +#include "tb_coreui_system.h" +#include "tb_dynarray.h" +#include "tb_engine_config.h" +#include "tb_imgui.h" +#include "tb_profiling.h" +#include "tb_world.h" + +#if TB_WINDOWS == 1 +#include "debugapi.h" +#endif + +#define TB_LOG_SYS_PRIO (TB_COREUI_SYS_PRIO + 1) + +typedef struct TbLogMessage { + float time; + int32_t category; + SDL_LogPriority priority; + char *message; +} TbLogMessage; + +typedef struct TbLogSystem { + bool *ui; + bool enabled; + bool autoscroll; + TB_DYN_ARR_OF(TbLogMessage) messages; +} TbLogSystem; ECS_COMPONENT_DECLARE(TbLogSystem); static float tb_log_time = 0.0f; @@ -30,12 +48,13 @@ void tb_log_hook(void *userdata, int32_t category, SDL_LogPriority priority, TB_DYN_ARR_APPEND(sys->messages, tb_msg); } - // Fall in to the original SDL log impl so we also get info written to the - // console - if (sys->orig_log_fn) { - tb_auto fn = (SDL_LogOutputFunction)sys->orig_log_fn; - fn(sys->orig_userdata, category, priority, message); - } +#if TB_WINDOWS == 1 + OutputDebugString(message); + OutputDebugString("\n"); +#else + printf("%s\n", message); + fflush(stdout); +#endif } void log_ui_tick(ecs_iter_t *it) { @@ -145,9 +164,6 @@ void tb_register_log_sys(TbWorld *world) { .autoscroll = true, }; - SDL_LogGetOutputFunction((SDL_LogOutputFunction *)sys->orig_log_fn, - &sys->orig_userdata); - TB_DYN_ARR_RESET(sys->messages, tb_global_alloc, 1024); ECS_SYSTEM(ecs, log_ui_tick, EcsPostUpdate, TbLogSystem(TbLogSystem)); @@ -160,9 +176,6 @@ void tb_unregister_log_sys(TbWorld *world) { tb_auto ecs = world->ecs; tb_auto sys = ecs_singleton_get_mut(ecs, TbLogSystem); - SDL_LogSetOutputFunction((SDL_LogOutputFunction)sys->orig_log_fn, - sys->orig_userdata); - TB_DYN_ARR_FOREACH(sys->messages, i) { tb_auto message = TB_DYN_ARR_AT(sys->messages, i); tb_free(tb_global_alloc, message.message); diff --git a/source/luminance.c b/source/tb_luminance.c similarity index 96% rename from source/luminance.c rename to source/tb_luminance.c index f2d7b9e5..d6896a23 100644 --- a/source/luminance.c +++ b/source/tb_luminance.c @@ -1,10 +1,10 @@ -#include "luminance.h" +#include "tb_luminance.h" -#include "profiling.h" -#include "renderpipelinesystem.h" -#include "rendersystem.h" +#include "tb_common.h" +#include "tb_profiling.h" +#include "tb_render_pipeline_system.h" +#include "tb_render_system.h" #include "tb_shader_system.h" -#include "tbcommon.h" #pragma clang diagnostic push #pragma clang diagnostic ignored "-Wmissing-variable-declarations" @@ -162,7 +162,8 @@ void tb_create_lum_hist_work(ecs_world_t *ecs, TbRenderSystem *rnd_sys, VkBufferCreateInfo create_info = { .sType = VK_STRUCTURE_TYPE_BUFFER_CREATE_INFO, .size = sizeof(uint32_t) * 256, - .usage = VK_BUFFER_USAGE_STORAGE_BUFFER_BIT, + .usage = VK_BUFFER_USAGE_STORAGE_BUFFER_BIT | + VK_BUFFER_USAGE_SHADER_DEVICE_ADDRESS_BIT, }; tb_rnd_sys_alloc_gpu_buffer(rnd_sys, &create_info, "Luminance Histogram", &work->lum_histogram); @@ -303,7 +304,8 @@ void tb_create_lum_avg_work(ecs_world_t *ecs, TbRenderSystem *rnd_sys, VkBufferCreateInfo create_info = { .sType = VK_STRUCTURE_TYPE_BUFFER_CREATE_INFO, .size = sizeof(float), - .usage = VK_BUFFER_USAGE_STORAGE_BUFFER_BIT, + .usage = VK_BUFFER_USAGE_STORAGE_BUFFER_BIT | + VK_BUFFER_USAGE_SHADER_DEVICE_ADDRESS_BIT, }; tb_rnd_sys_alloc_gpu_buffer(rnd_sys, &create_info, "Luminance Average", &work->lum_avg); diff --git a/source/tb_material_system.c b/source/tb_material_system.c new file mode 100644 index 00000000..aefe2cc9 --- /dev/null +++ b/source/tb_material_system.c @@ -0,0 +1,619 @@ +#include "tb_material_system.h" + +#include "tb_assets.h" +#include "tb_common.h" +#include "tb_dyn_desc_pool.h" +#include "tb_gltf.h" +#include "tb_queue.h" +#include "tb_scene_material.h" +#include "tb_task_scheduler.h" +#include "tb_world.h" + +static const int32_t TbMaxParallelMaterialLoads = 128; +static SDL_AtomicInt tb_parallel_mat_load_count = {0}; +static const int32_t TbMaxMaterialUploadsPerFrame = 8; + +// Components + +ECS_COMPONENT_DECLARE(TbMaterialUsage); + +ECS_COMPONENT_DECLARE(TbMaterialData); + +typedef struct TbMaterialDomainHandler { + TbMaterialUsage usage; + TbMaterialDomain domain; + size_t type_size; + TbMaterial default_mat; +} TbMaterialDomainHandler; +ECS_COMPONENT_DECLARE(TbMaterialDomainHandler); + +typedef struct TbMaterialCtx { + VkSampler sampler; // Immutable sampler for material descriptor sets + VkSampler shadow_sampler; // Immutable sampler for sampling shadow maps + VkDescriptorSetLayout set_layout; + uint32_t owned_mat_count; + TbDynDescPool desc_pool; + + TbDescriptorBuffer desc_buffer; + + TB_DYN_ARR_OF(TbMaterialDomainHandler) usage_map; +} TbMaterialCtx; +ECS_COMPONENT_DECLARE(TbMaterialCtx); + +ECS_COMPONENT_DECLARE(TbMaterialComponent); + +// Describes the creation of a material that lives in a GLB file +typedef struct TbMaterialGLTFLoadRequest { + const cgltf_data *data; + const char *name; +} TbMaterialGLTFLoadRequest; +ECS_COMPONENT_DECLARE(TbMaterialGLTFLoadRequest); + +ECS_TAG_DECLARE(TbMaterialLoaded); +ECS_TAG_DECLARE(TbMaterialUploaded); + +// Internals + +typedef struct TbMaterialLoadedArgs { + ecs_world_t *ecs; + TbMaterial mat; + TbMaterialDomain domain; + TbMaterialData comp; +} TbMaterialLoadedArgs; + +void tb_material_loaded(const void *args) { + TB_TRACY_SCOPE("Material Loaded Task"); + tb_auto loaded_args = (const TbMaterialLoadedArgs *)args; + tb_auto ecs = loaded_args->ecs; + tb_auto mat = loaded_args->mat; + if (mat == 0) { + TB_CHECK(false, "Material load failed. Do we need to retry?"); + } + + loaded_args->domain.load_fn(ecs, loaded_args->comp.domain_data); + + SDL_AtomicDecRef(&tb_parallel_mat_load_count); + ecs_add(ecs, mat, TbMaterialLoaded); + ecs_set_ptr(ecs, mat, TbMaterialData, &loaded_args->comp); +} + +typedef struct TbLoadCommonMaterialArgs { + ecs_world_t *ecs; + TbMaterial mat; + TbTaskScheduler enki; + TbPinnedTask loaded_task; + TbMaterialDomain domain; +} TbLoadCommonMaterialArgs; + +typedef struct TbLoadGLTFMaterialArgs { + TbLoadCommonMaterialArgs common; + TbMaterialGLTFLoadRequest gltf; +} TbLoadGLTFMaterialArgs; + +TbMaterialData tb_parse_gltf_mat(const cgltf_data *gltf_data, const char *name, + TbMatParseFn parse_fn, + const cgltf_material *material) { + TB_TRACY_SCOPE("Parse GLTF Mat"); + TbMaterialData mat_data = {0}; + // Load material based on usage + void *data = NULL; + if (!parse_fn(gltf_data, name, material, &data)) { + tb_free(tb_global_alloc, data); + return mat_data; + } + mat_data.domain_data = data; + return mat_data; +} + +void tb_load_gltf_material_task(const void *args) { + TB_TRACY_SCOPE("Load GLTF Material Task"); + tb_auto load_args = (const TbLoadGLTFMaterialArgs *)args; + TbMaterial mat = load_args->common.mat; + tb_auto domain = load_args->common.domain; + + tb_auto data = load_args->gltf.data; + tb_auto name = load_args->gltf.name; + + // Find material by name + struct cgltf_material *material = NULL; + for (cgltf_size i = 0; i < data->materials_count; ++i) { + tb_auto m = &data->materials[i]; + if (SDL_strcmp(name, m->name) == 0) { + material = m; + break; + } + } + if (material == NULL) { + TB_CHECK(false, "Failed to find material by name"); + mat = 0; // Invalid ent means task failed + } + + // Parse material based on usage + TbMaterialData mat_data = {0}; + if (mat != 0) { + mat_data = tb_parse_gltf_mat(data, name, domain.parse_fn, material); + } + + tb_free(tb_global_alloc, (void *)name); + + // Launch pinned task to handle loading signals on main thread + TbMaterialLoadedArgs loaded_args = { + .ecs = load_args->common.ecs, + .mat = mat, + .comp = mat_data, + .domain = domain, + }; + tb_launch_pinned_task_args(load_args->common.enki, + load_args->common.loaded_task, &loaded_args, + sizeof(TbMaterialLoadedArgs)); +} + +TbMaterialDomainHandler tb_find_material_domain(const TbMaterialCtx *ctx, + TbMaterialUsage usage) { + TB_TRACY_SCOPE("Find Mat Domain"); + TB_DYN_ARR_FOREACH(ctx->usage_map, i) { + tb_auto handler = &TB_DYN_ARR_AT(ctx->usage_map, i); + if (handler->usage == usage) { + return *handler; + } + } + TB_CHECK(false, "Failed to find material domain from usage"); + return (TbMaterialDomainHandler){0}; +} + +// Systems + +void tb_queue_gltf_mat_loads(ecs_iter_t *it) { + TB_TRACY_SCOPE("Queue GLTF Mat Loads"); + + tb_auto ecs = it->world; + + tb_auto enki = *ecs_field(it, TbTaskScheduler, 1); + tb_auto mat_ctx = ecs_field(it, TbMaterialCtx, 2); + tb_auto reqs = ecs_field(it, TbMaterialGLTFLoadRequest, 3); + tb_auto usages = ecs_field(it, TbMaterialUsage, 4); + + // TODO: Time slice the time spent creating tasks + // Iterate texture load tasks + for (int32_t i = 0; i < it->count; ++i) { + if (SDL_AtomicGet(&tb_parallel_mat_load_count) > + TbMaxParallelMaterialLoads) { + break; + } + TbMaterial ent = it->entities[i]; + tb_auto req = reqs[i]; + tb_auto usage = usages[i]; + + TbMaterialDomainHandler handler = tb_find_material_domain(mat_ctx, usage); + if (handler.type_size == 0 || handler.usage == TB_MAT_USAGE_UNKNOWN) { + TB_CHECK(false, "Unexpected material usage"); + } + + // This pinned task will be launched by the loading task + TbPinnedTask loaded_task = + tb_create_pinned_task(enki, tb_material_loaded, NULL, 0); + + TbLoadGLTFMaterialArgs args = { + .common = + { + .ecs = ecs, + .mat = ent, + .enki = enki, + .loaded_task = loaded_task, + .domain = handler.domain, + }, + .gltf = req, + }; + TbTask load_task = tb_async_task(enki, tb_load_gltf_material_task, &args, + sizeof(TbLoadGLTFMaterialArgs)); + // Apply task component to texture entity + ecs_set(ecs, ent, TbTask, {load_task}); + + SDL_AtomicIncRef(&tb_parallel_mat_load_count); + mat_ctx->owned_mat_count++; + + // Remove load request as it has now been enqueued to the task system + ecs_remove(ecs, ent, TbMaterialGLTFLoadRequest); + } +} + +void tb_upload_gltf_mats(ecs_iter_t *it) { + TB_TRACY_SCOPE("Material Uploads"); + tb_auto rnd_sys = ecs_field(it, TbRenderSystem, 1); + tb_auto mat_ctx = ecs_field(it, TbMaterialCtx, 2); + + tb_auto materials = ecs_field(it, TbMaterialData, 3); + tb_auto usages = ecs_field(it, TbMaterialUsage, 4); + for (int32_t i = 0; i < it->count; ++i) { + if (i >= TbMaxMaterialUploadsPerFrame) { + break; + } + TbMaterial ent = it->entities[i]; + tb_auto material = &materials[i]; + tb_auto usage = usages[i]; + + const char *name = ecs_get_name(it->world, ent); + + // Determine if the material's dependencies are also met + tb_auto handler = tb_find_material_domain(mat_ctx, usage); + tb_auto domain = handler.domain; + + // Material must be skipped if its dependencies aren't ready + if (!domain.ready_fn(it->world, material)) { + continue; + } + + void *domain_data = domain.get_data_fn(it->world, material); + size_t domain_size = domain.get_size_fn(); + + VkBufferCreateInfo create_info = { + .sType = VK_STRUCTURE_TYPE_BUFFER_CREATE_INFO, + .size = domain_size, + .usage = VK_BUFFER_USAGE_STORAGE_BUFFER_BIT | + VK_BUFFER_USAGE_TRANSFER_DST_BIT | + VK_BUFFER_USAGE_TRANSFER_SRC_BIT | + VK_BUFFER_USAGE_SHADER_DEVICE_ADDRESS_BIT, + }; + // HACK: Known alignment for uniform buffers + tb_rnd_sys_create_gpu_buffer2_tmp(rnd_sys, &create_info, domain_data, name, + &material->gpu_buffer, 0x40); + + ecs_remove(it->world, ent, TbMaterialLoaded); + ecs_add(it->world, ent, TbMaterialUploaded); + ecs_remove(it->world, ent, TbDescriptorReady); + } +} + +void tb_finalize_materials(ecs_iter_t *it) { + TB_TRACY_SCOPE("Finalize Materials"); + + tb_auto mat_ctx = ecs_field(it, TbMaterialCtx, 1); + tb_auto rnd_sys = ecs_field(it, TbRenderSystem, 2); + tb_auto materials = ecs_field(it, TbMaterialData, 3); + + if (mat_ctx->owned_mat_count == 0 || it->count == 0) { + return; + } + + // Render Thread allocator to make sure writes live + tb_auto rnd_tmp_alloc = + rnd_sys->render_thread->frame_states[rnd_sys->frame_idx].tmp_alloc.alloc; + + // Collect a write for every new material + TB_DYN_ARR_OF(TbDynDescWrite) writes = {0}; + TB_DYN_ARR_RESET(writes, rnd_tmp_alloc, it->count); + for (int32_t i = 0; i < it->count; ++i) { + tb_auto material = &materials[i]; + TB_CHECK(material->gpu_buffer.info.size, "Material GPU Buffer is size 0"); + TbDynDescWrite write = { + .type = VK_DESCRIPTOR_TYPE_STORAGE_BUFFER, + .desc.buffer = + (VkDescriptorBufferInfo){ + .buffer = material->gpu_buffer.buffer, + .range = material->gpu_buffer.info.size, + }, + }; + TB_DYN_ARR_APPEND(writes, write); + } + + const tb_auto write_count = TB_DYN_ARR_SIZE(writes); + if (write_count > 0) { + // Allocate space for indices + uint32_t *mat_indices = + tb_alloc_nm_tp(rnd_sys->tmp_alloc, TB_DYN_ARR_SIZE(writes), uint32_t); + tb_write_dyn_desc_pool(&mat_ctx->desc_pool, write_count, writes.data, + mat_indices); + + TB_DYN_ARR_FOREACH(writes, i) { + // Material is now ready to be referenced elsewhere + ecs_set(it->world, it->entities[i], TbMaterialComponent, + {mat_indices[i]}); + ecs_add(it->world, it->entities[i], TbDescriptorReady); + } + } +} + +void tb_update_material_pool(ecs_iter_t *it) { + TB_TRACY_SCOPE("Update Material Pool"); + tb_auto mat_ctx = ecs_field(it, TbMaterialCtx, 1); + tb_auto rnd_sys = ecs_field(it, TbRenderSystem, 2); + // Tick the pool in case any new materials require us to expand the pool + tb_tick_dyn_desc_pool(rnd_sys, &mat_ctx->desc_pool); +} + +// Toybox Glue + +void tb_register_material_sys(TbWorld *world) { + TB_TRACY_SCOPE("Register Material System"); + tb_auto ecs = world->ecs; + ECS_COMPONENT_DEFINE(ecs, TbMaterialCtx); + ECS_COMPONENT_DEFINE(ecs, TbMaterialGLTFLoadRequest); + ECS_COMPONENT_DEFINE(ecs, TbMaterialComponent); + ECS_COMPONENT_DEFINE(ecs, TbMaterialData); + ECS_COMPONENT_DEFINE(ecs, TbMaterialDomainHandler); + ECS_COMPONENT_DEFINE(ecs, TbMaterialUsage); + ECS_TAG_DEFINE(ecs, TbMaterialLoaded); + ECS_TAG_DEFINE(ecs, TbMaterialUploaded); + + tb_auto rnd_sys = ecs_singleton_get_mut(ecs, TbRenderSystem); + + ECS_SYSTEM(ecs, tb_queue_gltf_mat_loads, EcsPostLoad, + TbTaskScheduler(TbTaskScheduler), TbMaterialCtx(TbMaterialCtx), + [in] TbMaterialGLTFLoadRequest, [in] TbMaterialUsage); + + ECS_SYSTEM(ecs, tb_upload_gltf_mats, EcsPreUpdate, + TbRenderSystem(TbRenderSystem), TbMaterialCtx(TbMaterialCtx), + [in] TbMaterialData, [in] TbMaterialUsage, [in] TbMaterialLoaded, + !TbMaterialUploaded); + + ECS_SYSTEM(ecs, tb_finalize_materials, + EcsPostUpdate, [in] TbMaterialCtx(TbMaterialCtx), + [in] TbRenderSystem(TbRenderSystem), [in] TbMaterialData, + [in] TbMaterialUploaded, !TbDescriptorReady); + ECS_SYSTEM(ecs, tb_update_material_pool, + EcsPreStore, [in] TbMaterialCtx(TbMaterialCtx), + [in] TbRenderSystem(TbRenderSystem)); + + TbMaterialCtx ctx = {0}; + + SDL_AtomicSet(&tb_parallel_mat_load_count, 0); + + // Create immutable sampler for materials + { + VkSamplerCreateInfo create_info = { + .sType = VK_STRUCTURE_TYPE_SAMPLER_CREATE_INFO, + .magFilter = VK_FILTER_LINEAR, + .minFilter = VK_FILTER_LINEAR, + .mipmapMode = VK_SAMPLER_MIPMAP_MODE_LINEAR, + .addressModeU = VK_SAMPLER_ADDRESS_MODE_REPEAT, + .addressModeV = VK_SAMPLER_ADDRESS_MODE_REPEAT, + .addressModeW = VK_SAMPLER_ADDRESS_MODE_REPEAT, + .anisotropyEnable = VK_TRUE, + .maxAnisotropy = 16.0f, // 16x anisotropy is cheap + .maxLod = 14.0f, // HACK: known number of mips for 8k textures + .borderColor = VK_BORDER_COLOR_FLOAT_OPAQUE_BLACK, + }; + tb_rnd_create_sampler(rnd_sys, &create_info, "Material Sampler", + &ctx.sampler); + } + + // Create immutable sampler for sampling shadows + { + VkSamplerCreateInfo create_info = { + .sType = VK_STRUCTURE_TYPE_SAMPLER_CREATE_INFO, + .magFilter = VK_FILTER_NEAREST, + .minFilter = VK_FILTER_NEAREST, + .mipmapMode = VK_SAMPLER_MIPMAP_MODE_NEAREST, + .addressModeU = VK_SAMPLER_ADDRESS_MODE_CLAMP_TO_BORDER, + .addressModeV = VK_SAMPLER_ADDRESS_MODE_CLAMP_TO_BORDER, + .addressModeW = VK_SAMPLER_ADDRESS_MODE_CLAMP_TO_BORDER, + .anisotropyEnable = VK_FALSE, + .compareEnable = VK_TRUE, + .compareOp = VK_COMPARE_OP_LESS_OR_EQUAL, + .maxAnisotropy = 1.0f, + .maxLod = 1.0f, + .borderColor = VK_BORDER_COLOR_FLOAT_OPAQUE_WHITE, + }; + tb_rnd_create_sampler(rnd_sys, &create_info, "Material Shadow Sampler", + &ctx.shadow_sampler); + } + + // Create descriptor set layout for materials + { + const VkDescriptorBindingFlags flags = + VK_DESCRIPTOR_BINDING_VARIABLE_DESCRIPTOR_COUNT_BIT | + VK_DESCRIPTOR_BINDING_PARTIALLY_BOUND_BIT; + const uint32_t binding_count = 3; + VkDescriptorSetLayoutCreateInfo create_info = { + .sType = VK_STRUCTURE_TYPE_DESCRIPTOR_SET_LAYOUT_CREATE_INFO, +#if TB_USE_DESC_BUFFER == 1 + .flags = VK_DESCRIPTOR_SET_LAYOUT_CREATE_DESCRIPTOR_BUFFER_BIT_EXT, +#endif + .pNext = + &(VkDescriptorSetLayoutBindingFlagsCreateInfo){ + .sType = + VK_STRUCTURE_TYPE_DESCRIPTOR_SET_LAYOUT_BINDING_FLAGS_CREATE_INFO, + .bindingCount = binding_count, + .pBindingFlags = + (VkDescriptorBindingFlags[binding_count]){0, 0, flags}, + }, + .bindingCount = binding_count, + .pBindings = + (VkDescriptorSetLayoutBinding[binding_count]){ + {0, VK_DESCRIPTOR_TYPE_SAMPLER, 1, VK_SHADER_STAGE_FRAGMENT_BIT, + &ctx.sampler}, + {1, VK_DESCRIPTOR_TYPE_SAMPLER, 1, VK_SHADER_STAGE_FRAGMENT_BIT, + &ctx.shadow_sampler}, + {2, VK_DESCRIPTOR_TYPE_STORAGE_BUFFER, TB_DESC_POOL_CAP, + VK_SHADER_STAGE_FRAGMENT_BIT | VK_SHADER_STAGE_VERTEX_BIT, + NULL}, + }, + }; + tb_rnd_create_set_layout(rnd_sys, &create_info, "Material Set Layout", + &ctx.set_layout); + } + + TB_DYN_ARR_RESET(ctx.usage_map, tb_global_alloc, 4); + + tb_create_dyn_desc_pool(rnd_sys, "Material Descriptors", ctx.set_layout, + VK_DESCRIPTOR_TYPE_STORAGE_BUFFER, TB_DESC_POOL_CAP, + &ctx.desc_pool, 2); + + // Must set ctx before we try to load any materials + ecs_singleton_set_ptr(ecs, TbMaterialCtx, &ctx); + + // Register default material usage handlers + tb_register_scene_material_domain(ecs); +} + +void tb_unregister_material_sys(TbWorld *world) { + tb_auto ecs = world->ecs; + tb_auto rnd_sys = ecs_singleton_get_mut(ecs, TbRenderSystem); + tb_auto ctx = ecs_singleton_get_mut(ecs, TbMaterialCtx); + + tb_rnd_destroy_set_layout(rnd_sys, ctx->set_layout); + +#if TB_DESC_BUFFER == 1 + tb_destroy_descriptor_buffer(rnd_sys, &ctx->desc_buffer); +#endif + + // TODO: Release all default references + + // TODO: Check for leaks + + // TODO: Clean up descriptor pool + + ecs_singleton_remove(ecs, TbMaterialCtx); +} + +TB_REGISTER_SYS(tb, material, TB_MAT_SYS_PRIO) + +// Public API + +bool tb_register_mat_usage(ecs_world_t *ecs, const char *domain_name, + TbMaterialUsage usage, TbMaterialDomain domain, + void *default_data, size_t size) { + tb_auto ctx = ecs_singleton_get_mut(ecs, TbMaterialCtx); + tb_auto rnd_sys = ecs_singleton_get_mut(ecs, TbRenderSystem); + + // Copy data onto the global allocator so it can be safely freed + // from a thread + uint8_t *data_copy = tb_alloc_nm_tp(tb_global_alloc, size, uint8_t); + SDL_memcpy(data_copy, default_data, size); + + TbMaterial default_mat = ecs_new_entity(ecs, 0); + ecs_set(ecs, default_mat, TbMaterialUsage, {usage}); + ecs_add(ecs, default_mat, TbMaterialLoaded); + + TbMaterialData mat_data = { + .domain_data = data_copy, + }; + + const size_t data_size = domain.get_size_fn(); + + const uint32_t name_max = 100; + char name[name_max] = {0}; + SDL_snprintf(name, name_max, "%s_default", domain_name); + + VkBufferCreateInfo create_info = { + .sType = VK_STRUCTURE_TYPE_BUFFER_CREATE_INFO, + .size = data_size, + .usage = VK_BUFFER_USAGE_STORAGE_BUFFER_BIT | + VK_BUFFER_USAGE_TRANSFER_DST_BIT | + VK_BUFFER_USAGE_TRANSFER_SRC_BIT | + VK_BUFFER_USAGE_SHADER_DEVICE_ADDRESS_BIT, + }; + // HACK: Known alignment for uniform buffers + tb_rnd_sys_create_gpu_buffer2_tmp(rnd_sys, &create_info, data_copy, name, + &mat_data.gpu_buffer, 0x40); + + ecs_set_ptr(ecs, default_mat, TbMaterialData, &mat_data); + + TbMaterialDomainHandler handler = { + .usage = usage, + .domain = domain, + .type_size = size, + .default_mat = default_mat, + }; + TB_DYN_ARR_APPEND(ctx->usage_map, handler); + + ctx->owned_mat_count++; + return true; +} + +VkDescriptorSetLayout tb_mat_sys_get_set_layout(ecs_world_t *ecs) { + tb_auto ctx = ecs_singleton_get_mut(ecs, TbMaterialCtx); + return ctx->set_layout; +} + +VkDescriptorSet tb_mat_sys_get_set(ecs_world_t *ecs) { + tb_auto ctx = ecs_singleton_get_mut(ecs, TbMaterialCtx); + tb_auto rnd_sys = ecs_singleton_get_mut(ecs, TbRenderSystem); + return tb_dyn_desc_pool_get_set(rnd_sys, &ctx->desc_pool); +} + +VkDescriptorBufferBindingInfoEXT tb_mat_sys_get_table_addr(ecs_world_t *ecs) { + tb_auto ctx = ecs_singleton_get_mut(ecs, TbMaterialCtx); + return tb_desc_buff_get_binding(&ctx->desc_buffer); +} + +TbMaterial tb_mat_sys_load_gltf_mat(ecs_world_t *ecs, const cgltf_data *data, + const char *name, TbMaterialUsage usage) { + /* + If we are in a deferred ecs state (in the middle of the execution of a + system) we would not be able to determine if a material entity already +exists or not. So the calling system *must* be no_readonly and we will have to + manually check if we need to stop suspending commands. + Otherwise a system that attempts to add the same material twice will not be + looking at the correct version of the ECS state when trying to determine if +an entity for a material already exists + */ + bool deferred = false; + if (ecs_is_deferred(ecs)) { + deferred = ecs_defer_end(ecs); + } + // If an entity already exists with this name it is either loading or loaded + TbMaterial mat_ent = ecs_lookup_child(ecs, ecs_id(TbMaterialCtx), name); + if (mat_ent != 0) { + if (deferred) { + ecs_defer_begin(ecs); + } + return mat_ent; + } + + if (data == NULL) { + TB_CHECK(false, "Expected data"); + return 0; + } + + // Create a material entity + mat_ent = ecs_new_entity(ecs, 0); + ecs_set_name(ecs, mat_ent, name); + + // It is a child of the texture system context singleton + ecs_add_pair(ecs, mat_ent, EcsChildOf, ecs_id(TbMaterialCtx)); + + // Need to copy strings for task safety + // Task is responsible for freeing this string + const size_t name_len = SDL_strnlen(name, 256) + 1; + char *name_cpy = tb_alloc_nm_tp(tb_global_alloc, name_len, char); + SDL_strlcpy(name_cpy, name, name_len); + + // Append a texture load request onto the entity to schedule loading + ecs_set(ecs, mat_ent, TbMaterialGLTFLoadRequest, {data, name_cpy}); + ecs_set(ecs, mat_ent, TbMaterialUsage, {usage}); + ecs_remove(ecs, mat_ent, TbDescriptorReady); + + if (deferred) { + ecs_defer_begin(ecs); + } + + return mat_ent; +} + +bool tb_is_material_ready(ecs_world_t *ecs, TbMaterial mat_ent) { + return ecs_has(ecs, mat_ent, TbMaterialUploaded) && + ecs_has(ecs, mat_ent, TbMaterialComponent) && + ecs_has(ecs, mat_ent, TbDescriptorReady); +} + +bool tb_is_mat_transparent(ecs_world_t *ecs, TbMaterial mat_ent) { + tb_auto ctx = ecs_singleton_get(ecs, TbMaterialCtx); + tb_auto data = ecs_get(ecs, mat_ent, TbMaterialData); + tb_auto usage = *ecs_get(ecs, mat_ent, TbMaterialUsage); + + tb_auto handler = tb_find_material_domain(ctx, usage); + return handler.domain.is_trans_fn(data); +} + +TbMaterial tb_get_default_mat(ecs_world_t *ecs, TbMaterialUsage usage) { + tb_auto ctx = ecs_singleton_get(ecs, TbMaterialCtx); + TB_DYN_ARR_FOREACH(ctx->usage_map, i) { + tb_auto handler = &TB_DYN_ARR_AT(ctx->usage_map, i); + if (handler->usage == usage) { + return handler->default_mat; + } + } + TB_CHECK(false, "Failed to get default material"); + return 0; +} diff --git a/source/tb_mesh_component.c b/source/tb_mesh_component.c new file mode 100644 index 00000000..4f3c7ac1 --- /dev/null +++ b/source/tb_mesh_component.c @@ -0,0 +1,65 @@ +#include "tb_mesh_component.h" + +#include "tb_simd.h" + +#include "common.hlsli" +#include "json.h" +#include "tb_assets.h" +#include "tb_gltf.h" +#include "tb_material_system.h" +#include "tb_mesh_rnd_sys.h" +#include "tb_mesh_system.h" +#include "tb_profiling.h" +#include "tb_render_object_system.h" +#include "tb_util.h" +#include "tb_world.h" + +ECS_COMPONENT_DECLARE(TbMeshComponent); + +bool tb_load_mesh_comp(ecs_world_t *ecs, ecs_entity_t ent, + const char *source_path, const cgltf_data *data, + const cgltf_node *node, json_object *json) { + (void)json; + TB_TRACY_SCOPE("Load Mesh Component"); + + // Find mesh index by indexing. This is dirty but it works + uint32_t mesh_idx = SDL_MAX_UINT32; + for (cgltf_size i = 0; i < data->meshes_count; ++i) { + if (&data->meshes[i] == node->mesh) { + mesh_idx = i; + break; + } + } + TB_CHECK(mesh_idx != SDL_MAX_UINT32, "Failed to find mesh"); + + // We don't reserve here because we're expecting + // all meshes to already be loading + const uint32_t max_name_len = 256; + char name[max_name_len] = {0}; + SDL_snprintf(name, max_name_len, "mesh_%d", mesh_idx); + TbMeshComponent comp = { + .mesh2 = tb_mesh_sys_load_gltf_mesh(ecs, (cgltf_data *)data, source_path, + name, mesh_idx), + }; + ecs_set_ptr(ecs, ent, TbMeshComponent, &comp); + tb_mark_as_render_object(ecs, ent); + + return true; +} + +TbComponentRegisterResult tb_register_mesh_comp(TbWorld *world) { + tb_auto ecs = world->ecs; + ECS_COMPONENT_DEFINE(ecs, TbMeshComponent); + return (TbComponentRegisterResult){ecs_id(TbMeshComponent), 0}; +} + +bool tb_ready_mesh_comp(ecs_world_t *ecs, ecs_entity_t ent) { + // Component is ready when mesh is ready + tb_auto comp = ecs_get(ecs, ent, TbMeshComponent); + if (!comp) { + return false; + } + return tb_is_mesh_ready(ecs, comp->mesh2); +} + +TB_REGISTER_COMP(tb, mesh) diff --git a/source/meshsystem.c b/source/tb_mesh_rnd_sys.c similarity index 60% rename from source/meshsystem.c rename to source/tb_mesh_rnd_sys.c index 9cce6171..d62857a7 100644 --- a/source/meshsystem.c +++ b/source/tb_mesh_rnd_sys.c @@ -1,28 +1,29 @@ -#include "meshsystem.h" +#include "tb_mesh_rnd_sys.h" -#include "cameracomponent.h" #include "cgltf.h" #include "common.hlsli" #include "gltf.hlsli" -#include "hash.h" -#include "lightcomponent.h" -#include "materialsystem.h" -#include "meshcomponent.h" -#include "profiling.h" -#include "renderobjectsystem.h" -#include "renderpipelinesystem.h" -#include "rendersystem.h" -#include "rendertargetsystem.h" +#include "tb_camera_component.h" +#include "tb_gltf.h" +#include "tb_hash.h" +#include "tb_light_component.h" +#include "tb_material_system.h" +#include "tb_mesh_component.h" +#include "tb_mesh_system.h" +#include "tb_profiling.h" +#include "tb_render_object_system.h" +#include "tb_render_pipeline_system.h" +#include "tb_render_system.h" +#include "tb_render_target_system.h" #include "tb_shader_system.h" -#include "tbutil.h" -#include "texturesystem.h" -#include "transformcomponent.h" -#include "viewsystem.h" -#include "vkdbg.h" -#include "world.h" +#include "tb_texture_system.h" +#include "tb_transform_component.h" +#include "tb_util.h" +#include "tb_view_system.h" +#include "tb_vk_dbg.h" +#include "tb_world.h" #include -#include // Ignore some warnings for the generated headers #pragma clang diagnostic push @@ -85,6 +86,9 @@ VkPipeline create_prepass_pipeline(void *args) { VkGraphicsPipelineCreateInfo create_info = { .sType = VK_STRUCTURE_TYPE_GRAPHICS_PIPELINE_CREATE_INFO, +#if TB_USE_DESC_BUFFER == 1 + .flags = VK_PIPELINE_CREATE_DESCRIPTOR_BUFFER_BIT_EXT, +#endif .pNext = &(VkPipelineRenderingCreateInfo){ .sType = VK_STRUCTURE_TYPE_PIPELINE_RENDERING_CREATE_INFO, @@ -216,6 +220,9 @@ VkPipeline create_opaque_mesh_pipeline(void *args) { VkGraphicsPipelineCreateInfo create_info = { .sType = VK_STRUCTURE_TYPE_GRAPHICS_PIPELINE_CREATE_INFO, +#if TB_USE_DESC_BUFFER == 1 + .flags = VK_PIPELINE_CREATE_DESCRIPTOR_BUFFER_BIT_EXT, +#endif .pNext = &(VkPipelineRenderingCreateInfo){ .sType = VK_STRUCTURE_TYPE_PIPELINE_RENDERING_CREATE_INFO, @@ -349,6 +356,9 @@ VkPipeline create_transparent_mesh_pipeline(void *args) { VkGraphicsPipelineCreateInfo create_info = { .sType = VK_STRUCTURE_TYPE_GRAPHICS_PIPELINE_CREATE_INFO, +#if TB_USE_DESC_BUFFER == 1 + .flags = VK_PIPELINE_CREATE_DESCRIPTOR_BUFFER_BIT_EXT, +#endif .pNext = &(VkPipelineRenderingCreateInfo){ .sType = VK_STRUCTURE_TYPE_PIPELINE_RENDERING_CREATE_INFO, @@ -479,11 +489,29 @@ void prepass_record(TracyCGPUContext *gpu_ctx, VkCommandBuffer buffer, vkCmdSetScissor(buffer, 0, 1, &batch->scissor); const uint32_t set_count = 6; - VkDescriptorSet sets[set_count] = { - prim_batch->view_set, prim_batch->draw_set, prim_batch->obj_set, - prim_batch->idx_set, prim_batch->pos_set, prim_batch->norm_set}; - vkCmdBindDescriptorSets(buffer, VK_PIPELINE_BIND_POINT_GRAPHICS, layout, 0, - set_count, sets, 0, NULL); + +#if TB_USE_DESC_BUFFER == 1 + { + const VkDescriptorBufferBindingInfoEXT buffer_bindings[set_count] = { + prim_batch->view_addr, prim_batch->draw_addr, prim_batch->obj_addr, + prim_batch->idx_addr, prim_batch->pos_addr, prim_batch->norm_addr, + }; + vkCmdBindDescriptorBuffersEXT(buffer, set_count, buffer_bindings); + uint32_t buf_indices[set_count] = {0, 1, 2, 3, 4, 5}; + VkDeviceSize buf_offsets[set_count] = {0}; + vkCmdSetDescriptorBufferOffsetsEXT( + buffer, VK_PIPELINE_BIND_POINT_GRAPHICS, layout, 0, set_count, + buf_indices, buf_offsets); + } +#else + { + VkDescriptorSet sets[set_count] = { + prim_batch->view_set, prim_batch->draw_set, prim_batch->obj_set, + prim_batch->idx_set, prim_batch->pos_set, prim_batch->norm_set}; + vkCmdBindDescriptorSets(buffer, VK_PIPELINE_BIND_POINT_GRAPHICS, layout, + 0, set_count, sets, 0, NULL); + } +#endif for (uint32_t draw_idx = 0; draw_idx < batch->draw_count; ++draw_idx) { tb_auto draw = &((const TbIndirectDraw *)batch->draws)[draw_idx]; @@ -524,14 +552,35 @@ void mesh_record_common(TracyCGPUContext *gpu_ctx, VkCommandBuffer buffer, vkCmdSetScissor(buffer, 0, 1, &batch->scissor); const uint32_t set_count = 10; - const VkDescriptorSet sets[set_count] = { - prim_batch->view_set, prim_batch->mat_set, prim_batch->draw_set, - prim_batch->obj_set, prim_batch->tex_set, prim_batch->idx_set, - prim_batch->pos_set, prim_batch->norm_set, prim_batch->tan_set, - prim_batch->uv0_set, - }; - vkCmdBindDescriptorSets(buffer, VK_PIPELINE_BIND_POINT_GRAPHICS, layout, 0, - set_count, sets, 0, NULL); + +#if TB_USE_DESC_BUFFER == 1 + { + const VkDescriptorBufferBindingInfoEXT buffer_bindings[set_count] = { + prim_batch->view_addr, prim_batch->mat_addr, prim_batch->draw_addr, + prim_batch->obj_addr, prim_batch->tex_addr, prim_batch->idx_addr, + prim_batch->pos_addr, prim_batch->norm_addr, prim_batch->tan_addr, + prim_batch->uv0_addr, + }; + vkCmdBindDescriptorBuffersEXT(buffer, set_count, buffer_bindings); + uint32_t buf_indices[set_count] = {0, 1, 2, 3, 4, 5, 6, 7, 8, 9}; + VkDeviceSize buf_offsets[set_count] = {0}; + vkCmdSetDescriptorBufferOffsetsEXT( + buffer, VK_PIPELINE_BIND_POINT_GRAPHICS, layout, 0, set_count, + buf_indices, buf_offsets); + } +#else + { + const VkDescriptorSet sets[set_count] = { + prim_batch->view_set, prim_batch->mat_set, prim_batch->draw_set, + prim_batch->obj_set, prim_batch->tex_set, prim_batch->idx_set, + prim_batch->pos_set, prim_batch->norm_set, prim_batch->tan_set, + prim_batch->uv0_set, + }; + vkCmdBindDescriptorSets(buffer, VK_PIPELINE_BIND_POINT_GRAPHICS, layout, + 0, set_count, sets, 0, NULL); + } +#endif + for (uint32_t draw_idx = 0; draw_idx < batch->draw_count; ++draw_idx) { TracyCZoneNC(draw_ctx, "Record Indirect Draw", TracyCategoryColorRendering, true); @@ -571,18 +620,16 @@ void transparent_pass_record(TracyCGPUContext *gpu_ctx, VkCommandBuffer buffer, TracyCZoneEnd(ctx); } -TbMeshSystem create_mesh_system_internal( - ecs_world_t *ecs, TbAllocator gp_alloc, TbAllocator tmp_alloc, - TbRenderSystem *rnd_sys, TbMaterialSystem *mat_sys, - TbTextureSystem *tex_sys, TbViewSystem *view_sys, - TbRenderObjectSystem *ro_sys, TbRenderPipelineSystem *rp_sys) { +TbMeshSystem create_mesh_system_internal(ecs_world_t *ecs, TbAllocator gp_alloc, + TbAllocator tmp_alloc, + TbRenderSystem *rnd_sys, + TbViewSystem *view_sys, + TbRenderPipelineSystem *rp_sys) { TbMeshSystem sys = { .gp_alloc = gp_alloc, .tmp_alloc = tmp_alloc, .rnd_sys = rnd_sys, - .material_system = mat_sys, .view_sys = view_sys, - .render_object_system = ro_sys, .rp_sys = rp_sys, }; TB_DYN_ARR_RESET(sys.meshes, gp_alloc, 8); @@ -590,45 +637,20 @@ TbMeshSystem create_mesh_system_internal( TbRenderPassId opaque_pass_id = rp_sys->opaque_color_pass; TbRenderPassId transparent_pass_id = rp_sys->transparent_color_pass; + tb_auto mesh_set_layout = tb_mesh_sys_get_set_layout(ecs); + // Setup mesh system for rendering { VkResult err = VK_SUCCESS; - // Create mesh descriptor set layout - { - const VkDescriptorBindingFlags flags = - VK_DESCRIPTOR_BINDING_VARIABLE_DESCRIPTOR_COUNT_BIT | - VK_DESCRIPTOR_BINDING_PARTIALLY_BOUND_BIT; - VkDescriptorSetLayoutCreateInfo create_info = { - .sType = VK_STRUCTURE_TYPE_DESCRIPTOR_SET_LAYOUT_CREATE_INFO, - .pNext = - &(VkDescriptorSetLayoutBindingFlagsCreateInfo){ - .sType = - VK_STRUCTURE_TYPE_DESCRIPTOR_SET_LAYOUT_BINDING_FLAGS_CREATE_INFO, - .bindingCount = 1, - .pBindingFlags = (VkDescriptorBindingFlags[1]){flags}, - }, - .bindingCount = 1, - .pBindings = - (VkDescriptorSetLayoutBinding[1]){ - { - .binding = 0, - .descriptorCount = 4096, - .descriptorType = VK_DESCRIPTOR_TYPE_STORAGE_TEXEL_BUFFER, - .stageFlags = VK_SHADER_STAGE_VERTEX_BIT, - }, - }, - }; - err = tb_rnd_create_set_layout(rnd_sys, &create_info, "Mesh Attr Layout", - &sys.mesh_set_layout); - TB_VK_CHECK(err, "Failed to create mesh attr set layout"); - } - // Create instance descriptor set layout { VkDescriptorSetLayoutCreateInfo create_info = { .sType = VK_STRUCTURE_TYPE_DESCRIPTOR_SET_LAYOUT_CREATE_INFO, .bindingCount = 1, +#if TB_USE_DESC_BUFFER == 1 + .flags = VK_DESCRIPTOR_SET_LAYOUT_CREATE_DESCRIPTOR_BUFFER_BIT_EXT, +#endif .pBindings = (VkDescriptorSetLayoutBinding[1]){ { @@ -647,17 +669,18 @@ TbMeshSystem create_mesh_system_internal( // Create prepass pipeline layout { + VkPipelineLayoutCreateInfo create_info = { .sType = VK_STRUCTURE_TYPE_PIPELINE_LAYOUT_CREATE_INFO, .setLayoutCount = 6, .pSetLayouts = (VkDescriptorSetLayout[6]){ - view_sys->set_layout, + tb_view_sys_get_set_layout(ecs), sys.draw_set_layout, - ro_sys->set_layout, - sys.mesh_set_layout, - sys.mesh_set_layout, - sys.mesh_set_layout, + tb_render_object_sys_get_set_layout(ecs), + mesh_set_layout, + mesh_set_layout, + mesh_set_layout, }, }; err = tb_rnd_create_pipeline_layout(rnd_sys, &create_info, @@ -687,18 +710,18 @@ TbMeshSystem create_mesh_system_internal( .setLayoutCount = layout_count, .pSetLayouts = (VkDescriptorSetLayout[layout_count]){ - view_sys->set_layout, - mat_sys->set_layout, + tb_view_sys_get_set_layout(ecs), + tb_mat_sys_get_set_layout(ecs), sys.draw_set_layout, - ro_sys->set_layout, - tex_sys->set_layout, - sys.mesh_set_layout, - sys.mesh_set_layout, - sys.mesh_set_layout, - sys.mesh_set_layout, - sys.mesh_set_layout, - sys.mesh_set_layout, - sys.mesh_set_layout, + tb_render_object_sys_get_set_layout(ecs), + tb_tex_sys_get_set_layout(ecs), + mesh_set_layout, + mesh_set_layout, + mesh_set_layout, + mesh_set_layout, + mesh_set_layout, + mesh_set_layout, + mesh_set_layout, }, }; tb_rnd_create_pipeline_layout(rnd_sys, &create_info, @@ -748,6 +771,17 @@ TbMeshSystem create_mesh_system_internal( sizeof(TbMeshShaderArgs)); } } + +#if TB_USE_DESC_BUFFER == 1 + // Create descriptor buffers for opaque and transparent draws + tb_create_descriptor_buffer(rnd_sys, sys.draw_set_layout, + "Opaque Draw Desc Buffer", 1, + &sys.opaque_draw_descs); + tb_create_descriptor_buffer(rnd_sys, sys.draw_set_layout, + "Transparent Draw Desc Buffer", 1, + &sys.trans_draw_descs); +#endif + // Register drawing with the pipelines sys.prepass_draw_ctx2 = tb_render_pipeline_register_draw_context( rp_sys, &(TbDrawContextDescriptor){ @@ -779,6 +813,9 @@ void destroy_mesh_system(ecs_world_t *ecs, TbMeshSystem *self) { tb_rnd_destroy_pipe_layout(rnd_sys, self->pipe_layout); tb_rnd_destroy_pipe_layout(rnd_sys, self->prepass_layout); + tb_destroy_descriptor_buffer(rnd_sys, &self->opaque_draw_descs); + tb_destroy_descriptor_buffer(rnd_sys, &self->trans_draw_descs); + TB_DYN_ARR_FOREACH(self->meshes, i) { if (TB_DYN_ARR_AT(self->meshes, i).ref_count != 0) { TB_CHECK(false, "Leaking meshes"); @@ -790,513 +827,6 @@ void destroy_mesh_system(ecs_world_t *ecs, TbMeshSystem *self) { *self = (TbMeshSystem){0}; } -uint32_t find_mesh_by_id(TbMeshSystem *self, uint64_t id) { - TB_DYN_ARR_FOREACH(self->meshes, i) { - if (TB_DYN_ARR_AT(self->meshes, i).id.id == id) { - return i; - break; - } - } - return SDL_MAX_UINT32; -} - -// Based on an example from this cgltf commit message: -// https://github.com/jkuhlmann/cgltf/commit/bd8bd2c9cc08ff9b75a9aa9f99091f7144665c60 -cgltf_result decompress_buffer_view(TbAllocator alloc, - cgltf_buffer_view *view) { - if (view->data != NULL) { - // Already decoded - return cgltf_result_success; - } - - // Uncompressed buffer? No problem - if (!view->has_meshopt_compression) { - uint8_t *data = (uint8_t *)view->buffer->data; - data += view->offset; - - uint8_t *result = tb_alloc(alloc, view->size); - SDL_memcpy(result, data, view->size); // NOLINT - view->data = result; - return cgltf_result_success; - } - - const cgltf_meshopt_compression *mc = &view->meshopt_compression; - uint8_t *data = (uint8_t *)mc->buffer->data; - data += mc->offset; - TB_CHECK_RETURN(data, "Invalid data", cgltf_result_invalid_gltf); - - uint8_t *result = tb_alloc(alloc, mc->count * mc->stride); - TB_CHECK_RETURN(result, "Failed to allocate space for decoded buffer view", - cgltf_result_out_of_memory); - - int32_t res = -1; - switch (mc->mode) { - default: - case cgltf_meshopt_compression_mode_invalid: - break; - - case cgltf_meshopt_compression_mode_attributes: - res = meshopt_decodeVertexBuffer(result, mc->count, mc->stride, data, - mc->size); - break; - - case cgltf_meshopt_compression_mode_triangles: - res = meshopt_decodeIndexBuffer(result, mc->count, mc->stride, data, - mc->size); - break; - - case cgltf_meshopt_compression_mode_indices: - res = meshopt_decodeIndexSequence(result, mc->count, mc->stride, data, - mc->size); - break; - } - TB_CHECK_RETURN(res == 0, "Failed to decode buffer view", - cgltf_result_io_error); - - switch (mc->filter) { - default: - case cgltf_meshopt_compression_filter_none: - break; - - case cgltf_meshopt_compression_filter_octahedral: - meshopt_decodeFilterOct(result, mc->count, mc->stride); - break; - - case cgltf_meshopt_compression_filter_quaternion: - meshopt_decodeFilterQuat(result, mc->count, mc->stride); - break; - - case cgltf_meshopt_compression_filter_exponential: - meshopt_decodeFilterExp(result, mc->count, mc->stride); - break; - } - - view->data = result; - - return cgltf_result_success; -} - -TbMeshId tb_mesh_system_load_mesh(TbMeshSystem *self, const char *path, - const cgltf_node *node) { - // Hash the mesh's path and the cgltf_mesh structure to get - // an id We'd prefer to use a name but gltfpack is currently - // stripping mesh names - const cgltf_mesh *mesh = node->mesh; - TB_CHECK_RETURN(mesh, "Given node has no mesh", TbInvalidMeshId); - - uint64_t id = tb_hash(0, (const uint8_t *)path, SDL_strlen(path)); - id = tb_hash(id, (const uint8_t *)mesh, sizeof(cgltf_mesh)); - - uint32_t mesh_idx = find_mesh_by_id(self, id); - if (mesh_idx != SDL_MAX_UINT32) { - // Mesh was found, just return that - TB_DYN_ARR_AT(self->meshes, mesh_idx).ref_count++; - return (TbMeshId){id, mesh_idx}; - } - - // Mesh was not found, load it now - mesh_idx = TB_DYN_ARR_SIZE(self->meshes); - { - TbMesh m = {.id = {.id = id, .idx = mesh_idx}}; - TB_DYN_ARR_APPEND(self->meshes, m); - } - TbMesh *tb_mesh = &TB_DYN_ARR_AT(self->meshes, mesh_idx); - - // Determine how big this mesh is - uint64_t index_size = 0; - uint64_t geom_size = 0; - uint64_t attr_size_per_type[cgltf_attribute_type_max_enum] = {0}; - { - // Determine mesh index type - { - tb_auto stride = mesh->primitives[0].indices->stride; - if (stride == sizeof(uint16_t)) { - tb_mesh->idx_type = VK_INDEX_TYPE_UINT16; - } else if (stride == sizeof(uint32_t)) { - tb_mesh->idx_type = VK_INDEX_TYPE_UINT32; - } else { - TB_CHECK(false, "Unexpected index stride"); - } - } - - uint64_t vertex_size = 0; - uint32_t vertex_count = 0; - for (cgltf_size prim_idx = 0; prim_idx < mesh->primitives_count; - ++prim_idx) { - cgltf_primitive *prim = &mesh->primitives[prim_idx]; - cgltf_accessor *indices = prim->indices; - cgltf_size idx_size = - tb_calc_aligned_size(indices->count, indices->stride, 16); - - index_size += idx_size; - vertex_count = prim->attributes[0].data->count; - - for (cgltf_size attr_idx = 0; attr_idx < prim->attributes_count; - ++attr_idx) { - // Only care about certain attributes at the moment - cgltf_attribute_type type = prim->attributes[attr_idx].type; - int32_t idx = prim->attributes[attr_idx].index; - if ((type == cgltf_attribute_type_position || - type == cgltf_attribute_type_normal || - type == cgltf_attribute_type_tangent || - type == cgltf_attribute_type_texcoord) && - idx == 0) { - cgltf_accessor *attr = prim->attributes[attr_idx].data; - uint64_t attr_size = vertex_count * attr->stride; - attr_size_per_type[type] += attr_size; - } - } - - for (uint32_t i = 0; i < cgltf_attribute_type_max_enum; ++i) { - tb_auto attr_size = attr_size_per_type[i]; - if (attr_size > 0) { - attr_size_per_type[i] = tb_calc_aligned_size(1, attr_size, 16); - vertex_size += attr_size_per_type[i]; - } - } - } - - geom_size = index_size + vertex_size; - } - - uint64_t attr_offset_per_type[cgltf_attribute_type_max_enum] = {0}; - { - uint64_t offset = index_size; - for (uint32_t i = 0; i < cgltf_attribute_type_max_enum; ++i) { - tb_auto attr_size = attr_size_per_type[i]; - if (attr_size > 0) { - attr_offset_per_type[i] = offset; - offset += attr_size; - } - } - } - - VkResult err = VK_SUCCESS; - - // Create space for the mesh on the GPU - void *ptr = NULL; - { - VkBufferCreateInfo create_info = { - .sType = VK_STRUCTURE_TYPE_BUFFER_CREATE_INFO, - .size = geom_size, - .usage = VK_BUFFER_USAGE_TRANSFER_SRC_BIT | - VK_BUFFER_USAGE_TRANSFER_DST_BIT | - VK_BUFFER_USAGE_INDEX_BUFFER_BIT | - VK_BUFFER_USAGE_VERTEX_BUFFER_BIT | - VK_BUFFER_USAGE_STORAGE_TEXEL_BUFFER_BIT | - VK_BUFFER_USAGE_STORAGE_BUFFER_BIT, - }; - err = tb_rnd_sys_create_gpu_buffer(self->rnd_sys, &create_info, mesh->name, - &tb_mesh->gpu_buffer, - &tb_mesh->host_buffer, &ptr); - TB_VK_CHECK_RET(err, "Failed to create mesh buffer", TbInvalidMeshId); - } - - // Read the cgltf mesh into the driver owned memory - { - uint64_t idx_offset = 0; - uint64_t vertex_count = 0; - cgltf_size attr_count = 0; - for (cgltf_size prim_idx = 0; prim_idx < mesh->primitives_count; - ++prim_idx) { - cgltf_primitive *prim = &mesh->primitives[prim_idx]; - - { - cgltf_accessor *indices = prim->indices; - cgltf_buffer_view *view = indices->buffer_view; - cgltf_size src_size = indices->count * indices->stride; - cgltf_size padded_size = - tb_calc_aligned_size(indices->count, indices->stride, 16); - - // Decode the buffer - cgltf_result res = decompress_buffer_view(self->gp_alloc, view); - TB_CHECK(res == cgltf_result_success, "Failed to decode buffer view"); - - void *src = ((uint8_t *)view->data) + indices->offset; - void *dst = ((uint8_t *)(ptr)) + idx_offset; - SDL_memcpy(dst, src, src_size); // NOLINT - idx_offset += padded_size; - } - - // Determine the order of attributes - cgltf_size attr_order[6] = {0}; - { - const cgltf_attribute_type req_order[6] = { - cgltf_attribute_type_position, cgltf_attribute_type_normal, - cgltf_attribute_type_tangent, cgltf_attribute_type_texcoord, - cgltf_attribute_type_joints, cgltf_attribute_type_weights, - }; - cgltf_size attr_target_idx = 0; - for (uint32_t i = 0; i < 6; ++i) { - bool found = false; - for (cgltf_size attr_idx = 0; attr_idx < prim->attributes_count; - ++attr_idx) { - cgltf_attribute *attr = &prim->attributes[attr_idx]; - if (attr->type == req_order[i]) { - attr_order[attr_target_idx] = attr_idx; - attr_target_idx++; - if (prim_idx == 0) { - attr_count++; - } - found = true; - } - if (found) { - break; - } - } - } - } - - for (cgltf_size attr_idx = 0; attr_idx < attr_count; ++attr_idx) { - cgltf_attribute *attr = &prim->attributes[attr_order[attr_idx]]; - cgltf_accessor *accessor = attr->data; - cgltf_buffer_view *view = accessor->buffer_view; - - uint64_t mesh_vert_offset = vertex_count * attr->data->stride; - uint64_t vtx_offset = - attr_offset_per_type[attr->type] + mesh_vert_offset; - - size_t src_size = accessor->stride * accessor->count; - - // Decode the buffer - cgltf_result res = decompress_buffer_view(self->gp_alloc, view); - TB_CHECK(res == cgltf_result_success, "Failed to decode buffer view"); - - void *src = ((uint8_t *)view->data) + accessor->offset; - void *dst = ((uint8_t *)(ptr)) + vtx_offset; - SDL_memcpy(dst, src, src_size); // NOLINT - } - - vertex_count += prim->attributes[0].data->count; - } - - // Construct one write per primitive - { - static const VkFormat - attr_formats_per_type[cgltf_attribute_type_max_enum] = { - VK_FORMAT_UNDEFINED, VK_FORMAT_R16G16B16A16_SINT, - VK_FORMAT_R8G8B8A8_SNORM, VK_FORMAT_R8G8B8A8_SNORM, - VK_FORMAT_R16G16_SINT, VK_FORMAT_R8G8B8A8_UNORM, - VK_FORMAT_R16G16B16A16_SINT, VK_FORMAT_R8G8B8A8_SINT, - }; - static const int32_t attr_idx_per_type[cgltf_attribute_type_max_enum] = { - -1, 0, 1, 2, 3, -1, 5, 6, -1, - }; - - // Create one buffer view for indices - { - VkFormat idx_format = VK_FORMAT_R16_UINT; - if (tb_mesh->idx_type == VK_INDEX_TYPE_UINT32) { - idx_format = VK_FORMAT_R32_UINT; - } - VkBufferViewCreateInfo create_info = { - .sType = VK_STRUCTURE_TYPE_BUFFER_VIEW_CREATE_INFO, - .buffer = tb_mesh->gpu_buffer.buffer, - .offset = 0, - .range = index_size, - .format = idx_format, - }; - tb_rnd_create_buffer_view(self->rnd_sys, &create_info, - "Mesh Index View", &tb_mesh->index_view); - } - - for (size_t attr_idx = 0; attr_idx < attr_count; ++attr_idx) { - cgltf_attribute *attr = &mesh->primitives[0].attributes[attr_idx]; - - // Create a buffer view per attribute - VkBufferViewCreateInfo create_info = { - .sType = VK_STRUCTURE_TYPE_BUFFER_VIEW_CREATE_INFO, - .buffer = tb_mesh->gpu_buffer.buffer, - .offset = attr_offset_per_type[attr->type], - .range = VK_WHOLE_SIZE, - .format = attr_formats_per_type[attr->type], - }; - tb_rnd_create_buffer_view( - self->rnd_sys, &create_info, "Mesh Attribute View", - &tb_mesh->attr_views[attr_idx_per_type[attr->type]]); - } - } - - // Make sure to flush the gpu alloc if necessary - tb_flush_alloc(self->rnd_sys, tb_mesh->gpu_buffer.alloc); - } - - TB_DYN_ARR_AT(self->meshes, mesh_idx).ref_count++; - return (TbMeshId){id, mesh_idx}; -} - -bool tb_mesh_system_take_mesh_ref(TbMeshSystem *self, TbMeshId id) { - uint32_t index = id.idx; - TB_CHECK_RETURN(index != SDL_MAX_UINT32, "Failed to find mesh", false); - TB_DYN_ARR_AT(self->meshes, index).ref_count++; - return true; -} - -VkBuffer tb_mesh_system_get_gpu_mesh(TbMeshSystem *self, TbMeshId id) { - uint32_t index = id.idx; - TB_CHECK_RETURN(index != SDL_MAX_UINT32, "Failed to find mesh", - VK_NULL_HANDLE); - - VkBuffer buffer = TB_DYN_ARR_AT(self->meshes, index).gpu_buffer.buffer; - TB_CHECK_RETURN(buffer, "Failed to retrieve buffer", VK_NULL_HANDLE); - - return buffer; -} - -void tb_mesh_system_release_mesh_ref(TbMeshSystem *self, TbMeshId id) { - uint32_t index = id.idx; - - if (index == SDL_MAX_UINT32) { - TB_CHECK(false, "Failed to find mesh"); - return; - } - - TbMesh *mesh = &TB_DYN_ARR_AT(self->meshes, index); - - if (mesh->ref_count == 0) { - TB_CHECK(false, "Tried to release reference to mesh with " - "0 ref count"); - return; - } - - mesh->ref_count--; - - if (mesh->ref_count == 0) { - // Free the mesh at this index - VmaAllocator vma_alloc = self->rnd_sys->vma_alloc; - - TbHostBuffer *host_buf = &mesh->host_buffer; - TbBuffer *gpu_buf = &mesh->gpu_buffer; - - vmaUnmapMemory(vma_alloc, host_buf->alloc); - - vmaDestroyBuffer(vma_alloc, host_buf->buffer, host_buf->alloc); - vmaDestroyBuffer(vma_alloc, gpu_buf->buffer, gpu_buf->alloc); - - *host_buf = (TbHostBuffer){0}; - *gpu_buf = (TbBuffer){0}; - } -} - -VkDescriptorSet tb_mesh_system_get_idx_set(TbMeshSystem *self) { - return self->mesh_pool.sets[0]; -} -VkDescriptorSet tb_mesh_system_get_pos_set(TbMeshSystem *self) { - return self->mesh_pool.sets[1]; -} -VkDescriptorSet tb_mesh_system_get_norm_set(TbMeshSystem *self) { - return self->mesh_pool.sets[2]; -} -VkDescriptorSet tb_mesh_system_get_tan_set(TbMeshSystem *self) { - return self->mesh_pool.sets[3]; -} -VkDescriptorSet tb_mesh_system_get_uv0_set(TbMeshSystem *self) { - return self->mesh_pool.sets[4]; -} - -void mesh_descriptor_update(ecs_iter_t *it) { - ecs_world_t *ecs = it->world; - - tb_auto mesh_sys = ecs_singleton_get_mut(ecs, TbMeshSystem); - tb_auto rnd_sys = ecs_singleton_get_mut(ecs, TbRenderSystem); - - // If the number of meshes has grown to the point where we have run out of - // space in the descriptor pool we must reallocate. That means destroying the - // old pool and creating a new pool for all the new writes - const uint32_t view_count = TB_INPUT_PERM_COUNT + 1; // +1 for index buffer - const uint64_t incoming_mesh_count = TB_DYN_ARR_SIZE(mesh_sys->meshes); - const uint64_t incoming_cap = incoming_mesh_count * view_count; - if (incoming_cap > mesh_sys->mesh_pool.capacity) { - mesh_sys->mesh_pool.capacity = incoming_cap; - const uint64_t desc_count = mesh_sys->mesh_pool.capacity; - - // Re-create pool and allocate the one set that everything will be bound to - { - VkDescriptorPoolCreateInfo create_info = { - .sType = VK_STRUCTURE_TYPE_DESCRIPTOR_POOL_CREATE_INFO, - .maxSets = view_count, - .poolSizeCount = 1, - .pPoolSizes = - (VkDescriptorPoolSize[1]){ - { - .type = VK_DESCRIPTOR_TYPE_STORAGE_TEXEL_BUFFER, - .descriptorCount = desc_count * view_count, - }, - }, - }; - - VkDescriptorSetVariableDescriptorCountAllocateInfo alloc_info = { - .sType = - VK_STRUCTURE_TYPE_DESCRIPTOR_SET_VARIABLE_DESCRIPTOR_COUNT_ALLOCATE_INFO, - .descriptorSetCount = view_count, - .pDescriptorCounts = - (uint32_t[view_count]){incoming_mesh_count, incoming_mesh_count, - incoming_mesh_count, incoming_mesh_count, - incoming_mesh_count, incoming_mesh_count, - incoming_mesh_count}, - }; - VkDescriptorSetLayout layouts[view_count] = { - mesh_sys->mesh_set_layout, mesh_sys->mesh_set_layout, - mesh_sys->mesh_set_layout, mesh_sys->mesh_set_layout, - mesh_sys->mesh_set_layout, mesh_sys->mesh_set_layout, - mesh_sys->mesh_set_layout}; - tb_rnd_resize_desc_pool(rnd_sys, &create_info, layouts, &alloc_info, - &mesh_sys->mesh_pool, view_count); - } - } - if (incoming_mesh_count <= mesh_sys->mesh_desc_count) { - // No work to do :) - return; - } - mesh_sys->mesh_desc_count = incoming_mesh_count; - - // Write the index view descriptor - { - tb_auto mesh_count = TB_DYN_ARR_SIZE(mesh_sys->meshes); - tb_auto index_views = - tb_alloc_nm_tp(mesh_sys->tmp_alloc, mesh_count, VkBufferView); - - TB_DYN_ARR_FOREACH(mesh_sys->meshes, i) { - tb_auto mesh = &TB_DYN_ARR_AT(mesh_sys->meshes, i); - index_views[i] = mesh->index_view; - } - - tb_auto write = (VkWriteDescriptorSet){ - .sType = VK_STRUCTURE_TYPE_WRITE_DESCRIPTOR_SET, - .descriptorCount = mesh_count, - .descriptorType = VK_DESCRIPTOR_TYPE_STORAGE_TEXEL_BUFFER, - .dstSet = mesh_sys->mesh_pool.sets[0], - .dstBinding = 0, - .pTexelBufferView = index_views, - }; - - tb_rnd_update_descriptors(rnd_sys, 1, &write); - } - - // Just write all mesh vertex info to the descriptor set - VkWriteDescriptorSet writes[TB_INPUT_PERM_COUNT] = {0}; - for (uint32_t attr_idx = 0; attr_idx < TB_INPUT_PERM_COUNT; ++attr_idx) { - tb_auto mesh_count = TB_DYN_ARR_SIZE(mesh_sys->meshes); - tb_auto buffer_views = - tb_alloc_nm_tp(mesh_sys->tmp_alloc, mesh_count, VkBufferView); - - TB_DYN_ARR_FOREACH(mesh_sys->meshes, i) { - tb_auto mesh = &TB_DYN_ARR_AT(mesh_sys->meshes, i); - buffer_views[i] = mesh->attr_views[attr_idx]; - } - - writes[attr_idx] = (VkWriteDescriptorSet){ - .sType = VK_STRUCTURE_TYPE_WRITE_DESCRIPTOR_SET, - .descriptorCount = mesh_count, - .descriptorType = VK_DESCRIPTOR_TYPE_STORAGE_TEXEL_BUFFER, - .dstSet = mesh_sys->mesh_pool.sets[attr_idx + 1], - .dstBinding = 0, - .pTexelBufferView = buffer_views, - }; - } - tb_rnd_update_descriptors(rnd_sys, TB_INPUT_PERM_COUNT, writes); -} - void mesh_draw_tick(ecs_iter_t *it) { TracyCZoneNC(ctx, "Mesh Draw Tick", TracyCategoryColorRendering, true); ecs_world_t *ecs = it->world; @@ -1304,9 +834,6 @@ void mesh_draw_tick(ecs_iter_t *it) { ECS_COMPONENT_DEFINE(ecs, TbMeshSystem); tb_auto mesh_sys = ecs_field(it, TbMeshSystem, 1); - tb_auto ro_sys = ecs_singleton_get_mut(ecs, TbRenderObjectSystem); - tb_auto tex_sys = ecs_singleton_get_mut(ecs, TbTextureSystem); - tb_auto mat_sys = ecs_singleton_get_mut(ecs, TbMaterialSystem); tb_auto rnd_sys = ecs_singleton_get_mut(ecs, TbRenderSystem); tb_auto rp_sys = ecs_singleton_get_mut(ecs, TbRenderPipelineSystem); tb_auto view_sys = ecs_singleton_get_mut(ecs, TbViewSystem); @@ -1324,51 +851,103 @@ void mesh_draw_tick(ecs_iter_t *it) { while (ecs_query_next(&camera_it)) { tb_auto cameras = ecs_field(&camera_it, TbCameraComponent, 1); for (int32_t cam_idx = 0; cam_idx < camera_it.count; ++cam_idx) { + TracyCZoneN(cam_ctx, "Camera", 1); tb_auto camera = &cameras[cam_idx]; - tb_auto view_set = - tb_view_system_get_descriptor(view_sys, camera->view_id); + tb_auto view_id = camera->view_id; +#if TB_USE_DESC_BUFFER == 1 + tb_auto view_addr = tb_view_sys_get_table_addr(ecs, view_id); + // Skip camera if view set isn't ready + if (view_addr.address == VK_NULL_HANDLE) { + TracyCZoneEnd(cam_ctx); + continue; + } +#else + tb_auto view_set = tb_view_system_get_descriptor(view_sys, view_id); + // Skip camera if view set isn't ready + if (view_set == VK_NULL_HANDLE) { + TracyCZoneEnd(cam_ctx); + continue; + } +#endif const float width = camera->width; const float height = camera->height; // Run query to determine how many meshes so we can pre-allocate space for // batches + TracyCZoneN(count_ctx, "Count Meshes", true); tb_auto mesh_it = ecs_query_iter(ecs, mesh_sys->mesh_query); uint32_t opaque_draw_count = 0; uint32_t trans_draw_count = 0; while (ecs_query_next(&mesh_it)) { tb_auto meshes = ecs_field(&mesh_it, TbMeshComponent, 1); for (tb_auto mesh_idx = 0; mesh_idx < mesh_it.count; ++mesh_idx) { - tb_auto mesh = &meshes[mesh_idx]; + TbMesh2 mesh = meshes[mesh_idx].mesh2; + + if (!tb_is_mesh_ready(it->world, mesh)) { + continue; + } + + tb_auto submesh_itr = ecs_children(it->world, mesh); - for (uint32_t submesh_idx = 0; - submesh_idx < TB_DYN_ARR_SIZE(mesh->submeshes); ++submesh_idx) { - tb_auto sm = &TB_DYN_ARR_AT(mesh->submeshes, submesh_idx); - tb_auto perm = tb_mat_system_get_perm(mat_sys, sm->material); + uint32_t submesh_count = 0; + while (ecs_children_next(&submesh_itr)) { + submesh_count += submesh_itr.count; + } + + submesh_itr = ecs_children(it->world, mesh); + while (ecs_children_next(&submesh_itr)) { + for (int32_t sm_i = 0; sm_i < submesh_itr.count; ++sm_i) { + TbSubMesh2 sm_ent = submesh_itr.entities[sm_i]; + if (!ecs_has(it->world, sm_ent, TbSubMesh2Data)) { + TB_CHECK(false, + "Submesh entity unexpectedly lacked submesh data"); + continue; + } + tb_auto sm = ecs_get(it->world, sm_ent, TbSubMesh2Data); - if (perm & GLTF_PERM_ALPHA_CLIP || perm & GLTF_PERM_ALPHA_BLEND) { - trans_draw_count += TB_DYN_ARR_SIZE(mesh->submeshes); - } else { - opaque_draw_count += TB_DYN_ARR_SIZE(mesh->submeshes); + // Material must be loaded and ready + if (!tb_is_material_ready(ecs, sm->material)) { + continue; + } + + if (tb_is_mat_transparent(ecs, sm->material)) { + trans_draw_count += submesh_count; + } else { + opaque_draw_count += submesh_count; + } } } } } mesh_it = ecs_query_iter(ecs, mesh_sys->mesh_query); + TracyCZoneEnd(count_ctx); const uint32_t max_draw_count = opaque_draw_count + trans_draw_count; if (max_draw_count == 0) { + TracyCZoneEnd(cam_ctx); continue; } - tb_auto obj_set = tb_render_object_sys_get_set(ro_sys); - tb_auto tex_set = tb_tex_sys_get_set(tex_sys); - tb_auto mat_set = tb_mat_system_get_set(mat_sys); - tb_auto idx_set = tb_mesh_system_get_idx_set(mesh_sys); - tb_auto pos_set = tb_mesh_system_get_pos_set(mesh_sys); - tb_auto norm_set = tb_mesh_system_get_norm_set(mesh_sys); - tb_auto tan_set = tb_mesh_system_get_tan_set(mesh_sys); - tb_auto uv0_set = tb_mesh_system_get_uv0_set(mesh_sys); +#if TB_USE_DESC_BUFFER == 1 + tb_auto obj_addr = tb_render_object_sys_get_table_addr(ecs); + tb_auto tex_addr = tb_tex_sys_get_table_addr(ecs); + tb_auto mat_addr = tb_mat_sys_get_table_addr(ecs); + tb_auto idx_addr = tb_mesh_sys_get_idx_addr(ecs); + tb_auto pos_addr = tb_mesh_sys_get_pos_addr(ecs); + tb_auto norm_addr = tb_mesh_sys_get_norm_addr(ecs); + tb_auto tan_addr = tb_mesh_sys_get_tan_addr(ecs); + tb_auto uv0_addr = tb_mesh_sys_get_uv0_addr(ecs); +#else + tb_auto obj_set = tb_render_object_sys_get_set(ecs); + tb_auto tex_set = tb_tex_sys_get_set(ecs); + tb_auto mat_set = tb_mat_sys_get_set(ecs); + tb_auto idx_set = tb_mesh_sys_get_idx_set(ecs); + tb_auto pos_set = tb_mesh_sys_get_pos_set(ecs); + tb_auto norm_set = tb_mesh_sys_get_norm_set(ecs); + tb_auto tan_set = tb_mesh_sys_get_tan_set(ecs); + tb_auto uv0_set = tb_mesh_sys_get_uv0_set(ecs); +#endif // Allocate indirect draw buffers VkDrawIndirectCommand *opaque_draw_cmds = NULL; @@ -1405,6 +984,11 @@ void mesh_draw_tick(ecs_iter_t *it) { &trans_data_offset, (void **)&trans_draw_data); +#if TB_USE_DESC_BUFFER == 1 + // Reset descriptor buffers + tb_reset_descriptor_buffer(rnd_sys, &mesh_sys->opaque_draw_descs); + tb_reset_descriptor_buffer(rnd_sys, &mesh_sys->trans_draw_descs); +#else // Allocate per-draw descriptor sets const uint32_t set_count = 2; { @@ -1424,65 +1008,104 @@ void mesh_draw_tick(ecs_iter_t *it) { mesh_sys->draw_set_layout, mesh_sys->draw_set_layout, }; - tb_rnd_frame_desc_pool_tick(rnd_sys, &create_info, layouts, NULL, - mesh_sys->draw_pools.pools, set_count); + tb_rnd_frame_desc_pool_tick( + rnd_sys, "mesh_draw_instances", &create_info, layouts, NULL, + mesh_sys->draw_pools.pools, set_count, set_count); } +#endif TracyCZoneN(ctx2, "Iterate Meshes", true); while (ecs_query_next(&mesh_it)) { tb_auto meshes = ecs_field(&mesh_it, TbMeshComponent, 1); + tb_auto render_objects = ecs_field(&mesh_it, TbRenderObject, 2); for (int32_t mesh_idx = 0; mesh_idx < mesh_it.count; ++mesh_idx) { - tb_auto mesh = &meshes[mesh_idx]; - tb_auto entity = mesh_it.entities[mesh_idx]; - tb_auto ro = ecs_get_mut(ecs, entity, TbRenderObject); - - for (uint32_t submesh_idx = 0; - submesh_idx < TB_DYN_ARR_SIZE(mesh->submeshes); ++submesh_idx) { - tb_auto sm = &TB_DYN_ARR_AT(mesh->submeshes, submesh_idx); - tb_auto perm = tb_mat_system_get_perm(mat_sys, sm->material); - - // Deduce whether to write to opaque or transparent data - tb_auto draw_cmds = opaque_draw_cmds; - tb_auto draw_count = &opaque_cmd_count; - tb_auto draw_data = opaque_draw_data; - if (perm & GLTF_PERM_ALPHA_CLIP || perm & GLTF_PERM_ALPHA_BLEND) { - draw_cmds = trans_draw_cmds; - draw_count = &trans_cmd_count; - draw_data = trans_draw_data; - } + tb_auto mesh = meshes[mesh_idx].mesh2; + tb_auto ro = render_objects[mesh_idx]; - // Write a command and a piece of draw data into the buffers - tb_auto draw_idx = *draw_count; - draw_cmds[draw_idx] = (VkDrawIndirectCommand){ - .vertexCount = sm->index_count, - .instanceCount = 1, - }; - draw_data[draw_idx] = (TbGLTFDrawData){ - .perm = sm->vertex_perm, - .obj_idx = ro->index, - .mesh_idx = mesh->mesh_id.idx, - .mat_idx = sm->material.idx, - .index_offset = sm->index_offset, - .vertex_offset = sm->vertex_offset, - }; - (*draw_count) += 1; + if (!tb_is_mesh_ready(it->world, mesh)) { + continue; + } + + tb_auto mesh_desc_idx = *ecs_get(it->world, mesh, TbMeshIndex); + + tb_auto submesh_itr = ecs_children(it->world, mesh); + while (ecs_children_next(&submesh_itr)) { + for (int32_t sm_i = 0; sm_i < submesh_itr.count; ++sm_i) { + TbSubMesh2 sm_ent = submesh_itr.entities[sm_i]; + if (!ecs_has(it->world, sm_ent, TbSubMesh2Data)) { + TB_CHECK(false, + "Submesh entity unexpectedly lacked submesh data"); + continue; + } + tb_auto sm = ecs_get(it->world, sm_ent, TbSubMesh2Data); + // Material must be loaded and ready + if (!tb_is_material_ready(ecs, sm->material)) { + continue; + } + + // Deduce whether to write to opaque or transparent data + tb_auto draw_cmds = opaque_draw_cmds; + tb_auto draw_count = &opaque_cmd_count; + tb_auto draw_data = opaque_draw_data; + if (tb_is_mat_transparent(ecs, sm->material)) { + draw_cmds = trans_draw_cmds; + draw_count = &trans_cmd_count; + draw_data = trans_draw_data; + } + + // Write a command and a piece of draw data into the buffers + tb_auto draw_idx = *draw_count; + draw_cmds[draw_idx] = (VkDrawIndirectCommand){ + .vertexCount = sm->index_count, + .instanceCount = 1, + }; + draw_data[draw_idx] = (TbGLTFDrawData){ + .perm = sm->vertex_perm, + .obj_idx = ro.index, + .mesh_idx = mesh_desc_idx, + .mat_idx = *ecs_get(ecs, sm->material, TbMaterialComponent), + .index_offset = sm->index_offset, + .vertex_offset = sm->vertex_offset, + }; + (*draw_count) += 1; + } } } } TracyCZoneEnd(ctx2); +#if TB_USE_DESC_BUFFER == 1 + VkDescriptorBufferBindingInfoEXT opaque_draw_addr = + tb_desc_buff_get_binding(&mesh_sys->opaque_draw_descs); + VkDescriptorBufferBindingInfoEXT trans_draw_addr = + tb_desc_buff_get_binding(&mesh_sys->trans_draw_descs); +#else VkDescriptorSet opaque_draw_set = tb_rnd_frame_desc_pool_get_set( rnd_sys, mesh_sys->draw_pools.pools, 0); VkDescriptorSet trans_draw_set = tb_rnd_frame_desc_pool_get_set( rnd_sys, mesh_sys->draw_pools.pools, 1); +#endif // Opaque batch is a bit special since we need to share with the shadow // system + TB_CHECK(mesh_sys->opaque_batch == NULL, "Opaque batch was not consumed"); mesh_sys->opaque_batch = tb_alloc_tp(mesh_sys->tmp_alloc, TbDrawBatch); tb_auto opaque_prim_batch = tb_alloc_tp(mesh_sys->tmp_alloc, TbPrimitiveBatch); *opaque_prim_batch = (TbPrimitiveBatch){ +#if TB_USE_DESC_BUFFER == 1 + .view_addr = view_addr, + .mat_addr = mat_addr, + .draw_addr = opaque_draw_addr, + .obj_addr = obj_addr, + .tex_addr = tex_addr, + .idx_addr = idx_addr, + .pos_addr = pos_addr, + .norm_addr = norm_addr, + .tan_addr = tan_addr, + .uv0_addr = uv0_addr, +#else .view_set = view_set, .mat_set = mat_set, .draw_set = opaque_draw_set, @@ -1493,6 +1116,7 @@ void mesh_draw_tick(ecs_iter_t *it) { .norm_set = norm_set, .tan_set = tan_set, .uv0_set = uv0_set, +#endif }; // Define batches @@ -1524,6 +1148,18 @@ void mesh_draw_tick(ecs_iter_t *it) { .scissor = {{0, 0}, {width, height}}, .user_batch = &(TbPrimitiveBatch){ +#if TB_USE_DESC_BUFFER == 1 + .view_addr = view_addr, + .mat_addr = mat_addr, + .draw_addr = trans_draw_addr, + .obj_addr = obj_addr, + .tex_addr = tex_addr, + .idx_addr = idx_addr, + .pos_addr = pos_addr, + .norm_addr = norm_addr, + .tan_addr = tan_addr, + .uv0_addr = uv0_addr, +#else .view_set = view_set, .mat_set = mat_set, .draw_set = trans_draw_set, @@ -1534,6 +1170,7 @@ void mesh_draw_tick(ecs_iter_t *it) { .norm_set = norm_set, .tan_set = tan_set, .uv0_set = uv0_set, +#endif }, .draw_count = 1, .draw_size = sizeof(TbIndirectDraw), @@ -1555,6 +1192,37 @@ void mesh_draw_tick(ecs_iter_t *it) { prepass_batch.layout = mesh_sys->prepass_layout; } +#if TB_USE_DESC_BUFFER == 1 + { + VkDeviceAddress tmp_buf_addr = tb_rnd_get_gpu_tmp_addr(rnd_sys); + if (opaque_data_size > 0) { + TbDescriptor desc = { + .type = VK_DESCRIPTOR_TYPE_STORAGE_BUFFER, + .data.pStorageBuffer = + &(VkDescriptorAddressInfoEXT){ + .sType = VK_STRUCTURE_TYPE_DESCRIPTOR_ADDRESS_INFO_EXT, + .address = tmp_buf_addr + opaque_data_offset, + .range = opaque_data_size, + }, + }; + tb_write_desc_to_buffer(rnd_sys, &mesh_sys->opaque_draw_descs, 0, + &desc); + } + if (trans_data_size > 0) { + TbDescriptor desc = { + .type = VK_DESCRIPTOR_TYPE_STORAGE_BUFFER, + .data.pStorageBuffer = + &(VkDescriptorAddressInfoEXT){ + .sType = VK_STRUCTURE_TYPE_DESCRIPTOR_ADDRESS_INFO_EXT, + .address = tmp_buf_addr + trans_data_offset, + .range = trans_data_size, + }, + }; + tb_write_desc_to_buffer(rnd_sys, &mesh_sys->trans_draw_descs, 0, + &desc); + } + } +#else // Write draw data buffer to descriptor sets { if (opaque_data_size > 0) { @@ -1589,6 +1257,7 @@ void mesh_draw_tick(ecs_iter_t *it) { tb_rnd_update_descriptors(rnd_sys, 1, &write); } } +#endif { TracyCZoneN(ctx2, "Submit Batches", true); @@ -1608,6 +1277,8 @@ void mesh_draw_tick(ecs_iter_t *it) { } TracyCZoneEnd(ctx2); } + + TracyCZoneEnd(cam_ctx); } } @@ -1620,27 +1291,18 @@ void tb_register_mesh_sys(TbWorld *world) { ECS_COMPONENT_DEFINE(ecs, TbMeshSystem); tb_auto rnd_sys = ecs_singleton_get_mut(ecs, TbRenderSystem); - tb_auto mat_sys = ecs_singleton_get_mut(ecs, TbMaterialSystem); - tb_auto tex_sys = ecs_singleton_get_mut(ecs, TbTextureSystem); tb_auto view_sys = ecs_singleton_get_mut(ecs, TbViewSystem); - tb_auto ro_sys = ecs_singleton_get_mut(ecs, TbRenderObjectSystem); tb_auto rp_sys = ecs_singleton_get_mut(ecs, TbRenderPipelineSystem); - tb_auto sys = create_mesh_system_internal(ecs, world->gp_alloc, - world->tmp_alloc, rnd_sys, mat_sys, - tex_sys, view_sys, ro_sys, rp_sys); + tb_auto sys = create_mesh_system_internal( + ecs, world->gp_alloc, world->tmp_alloc, rnd_sys, view_sys, rp_sys); sys.camera_query = ecs_query(ecs, {.filter.terms = { {.id = ecs_id(TbCameraComponent)}, }}); - sys.mesh_query = ecs_query(ecs, { - .filter.terms = - { - { - .id = ecs_id(TbMeshComponent), - .inout = EcsInOut, - }, - }, - }); + sys.mesh_query = ecs_query(ecs, {.filter.terms = { + {.id = ecs_id(TbMeshComponent)}, + {.id = ecs_id(TbRenderObject)}, + }}); sys.dir_light_query = ecs_query(ecs, {.filter.terms = { {.id = ecs_id(TbDirectionalLightComponent)}, @@ -1649,8 +1311,6 @@ void tb_register_mesh_sys(TbWorld *world) { // Sets a singleton by ptr ecs_set_ptr(ecs, ecs_id(TbMeshSystem), TbMeshSystem, &sys); - ECS_SYSTEM(ecs, mesh_descriptor_update, EcsPreStore, - TbMeshSystem(TbMeshSystem)); ECS_SYSTEM(ecs, mesh_draw_tick, EcsOnStore, TbMeshSystem(TbMeshSystem)); TracyCZoneEnd(ctx); diff --git a/source/tb_mesh_system.c b/source/tb_mesh_system.c new file mode 100644 index 00000000..315ebcf9 --- /dev/null +++ b/source/tb_mesh_system.c @@ -0,0 +1,1113 @@ +#include "tb_mesh_system.h" + +#include "tb_assets.h" +#include "tb_dyn_desc_pool.h" +#include "tb_gltf.h" +#include "tb_log.h" +#include "tb_material_system.h" +#include "tb_task_scheduler.h" +#include "tb_util.h" + +// Internals + +#define TB_MAX_READY_CHECKS_PER_FRAME 16 + +#define TB_MAX_MESH_QUEUE_PER_FRAME 4 +#define TB_MAX_SUBMESH_QUEUE_PER_FRAME 4 + +// Mesh system probably shouldn't own this +ECS_COMPONENT_DECLARE(TbAABB); + +static const int32_t TbMaxParallelMeshLoads = 128; + +typedef SDL_AtomicInt TbMeshQueueCounter; +ECS_COMPONENT_DECLARE(TbMeshQueueCounter); +typedef SDL_AtomicInt TbSubMeshQueueCounter; +ECS_COMPONENT_DECLARE(TbSubMeshQueueCounter); +typedef uint32_t TbPerFrameMeshQueueCounter; +ECS_COMPONENT_DECLARE(TbPerFrameMeshQueueCounter); +typedef uint32_t TbPerFrameSubmeshQueueCounter; +ECS_COMPONENT_DECLARE(TbPerFrameSubmeshQueueCounter); + +ECS_COMPONENT_DECLARE(TbSubMesh2Data); + +typedef struct TbMeshCtx { + uint32_t owned_mesh_count; + VkDescriptorSetLayout set_layout; + TbDynDescPool idx_desc_pool; + TbDynDescPool pos_desc_pool; + TbDynDescPool norm_desc_pool; + TbDynDescPool tan_desc_pool; + TbDynDescPool uv0_desc_pool; + + ecs_query_t *mesh_load_query; + ecs_query_t *submesh_load_query; + + TbDescriptorBuffer idx_desc_buf; + TbDescriptorBuffer pos_desc_buf; + TbDescriptorBuffer norm_desc_buf; + TbDescriptorBuffer tan_desc_buf; + TbDescriptorBuffer uv0_desc_buf; +} TbMeshCtx; +ECS_COMPONENT_DECLARE(TbMeshCtx); + +typedef struct TbMeshData { + VkIndexType idx_type; + TbHostBuffer host_buffer; + TbBuffer gpu_buffer; +#if TB_USE_DESC_BUFFER == 1 + VkDescriptorAddressInfoEXT index_addr; + VkDescriptorAddressInfoEXT attribute_addr[TB_INPUT_PERM_COUNT]; +#else + VkBufferView index_view; + VkBufferView attr_views[TB_INPUT_PERM_COUNT]; +#endif +} TbMeshData; +ECS_COMPONENT_DECLARE(TbMeshData); + +ECS_COMPONENT_DECLARE(TbMeshIndex); + +// Describes the creation of a mesh that lives in a GLB file +typedef struct TbMeshGLTFLoadRequest { + cgltf_data *data; + uint32_t index; +} TbMeshGLTFLoadRequest; +ECS_COMPONENT_DECLARE(TbMeshGLTFLoadRequest); + +typedef struct TbSubMeshGLTFLoadRequest { + const cgltf_data *data; + const cgltf_mesh *gltf_mesh; +} TbSubMeshGLTFLoadRequest; +ECS_COMPONENT_DECLARE(TbSubMeshGLTFLoadRequest); + +ECS_TAG_DECLARE(TbMeshLoaded); +ECS_TAG_DECLARE(TbMeshParsed); +ECS_TAG_DECLARE(TbMeshReady); +ECS_TAG_DECLARE(TbSubMeshParsed); +ECS_TAG_DECLARE(TbSubMeshReady); + +typedef struct TbMeshLoadedArgs { + ecs_world_t *ecs; + TbMesh2 mesh; + TbMeshData comp; + const cgltf_data *data; + const cgltf_mesh *gltf_mesh; + TbMeshQueueCounter *counter; +} TbMeshLoadedArgs; + +void tb_mesh_loaded(const void *args) { + TB_TRACY_SCOPE("Mesh Loaded"); + tb_auto loaded_args = (const TbMeshLoadedArgs *)args; + tb_auto ecs = loaded_args->ecs; + tb_auto mesh = loaded_args->mesh; + tb_auto data = loaded_args->data; + tb_auto gltf_mesh = loaded_args->gltf_mesh; + tb_auto counter = loaded_args->counter; + if (mesh == 0) { + TB_CHECK(false, "Mesh load failed. Do we need to retry?"); + } + + TbSubMeshGLTFLoadRequest submesh_req = { + .data = data, + .gltf_mesh = gltf_mesh, + }; + + ecs_add(ecs, mesh, TbMeshLoaded); + ecs_add(ecs, mesh, TbMeshParsed); + ecs_set_ptr(ecs, mesh, TbMeshData, &loaded_args->comp); + ecs_set_ptr(ecs, mesh, TbSubMeshGLTFLoadRequest, &submesh_req); + + SDL_AtomicDecRef(counter); +} + +typedef struct TbLoadCommonMeshArgs { + ecs_world_t *ecs; + TbRenderSystem *rnd_sys; + TbMesh2 mesh; + TbTaskScheduler enki; + TbPinnedTask loaded_task; +} TbLoadCommonMeshArgs; + +typedef struct TbLoadGLTFMeshArgs { + TbLoadCommonMeshArgs common; + TbMeshGLTFLoadRequest gltf; + TbMeshQueueCounter *counter; +} TbLoadGLTFMeshArgs; + +TbMeshData tb_load_gltf_mesh(TbRenderSystem *rnd_sys, + const cgltf_mesh *gltf_mesh) { + TB_TRACY_SCOPE("Load GLTF Mesh"); + TbMeshData data = {0}; + + // Determine how big this mesh is + uint64_t index_size = 0; + uint64_t geom_size = 0; + uint64_t attr_size_per_type[cgltf_attribute_type_max_enum] = {0}; + { + // Determine mesh index type + { + tb_auto stride = gltf_mesh->primitives[0].indices->stride; + if (stride == sizeof(uint16_t)) { + data.idx_type = VK_INDEX_TYPE_UINT16; + } else if (stride == sizeof(uint32_t)) { + data.idx_type = VK_INDEX_TYPE_UINT32; + } else { + TB_CHECK(false, "Unexpected index stride"); + } + } + + uint64_t vertex_size = 0; + uint32_t vertex_count = 0; + for (cgltf_size prim_idx = 0; prim_idx < gltf_mesh->primitives_count; + ++prim_idx) { + cgltf_primitive *prim = &gltf_mesh->primitives[prim_idx]; + cgltf_accessor *indices = prim->indices; + cgltf_size idx_size = + tb_calc_aligned_size(indices->count, indices->stride, 16); + + index_size += idx_size; + vertex_count = prim->attributes[0].data->count; + + for (cgltf_size attr_idx = 0; attr_idx < prim->attributes_count; + ++attr_idx) { + // Only care about certain attributes at the moment + cgltf_attribute_type type = prim->attributes[attr_idx].type; + int32_t idx = prim->attributes[attr_idx].index; + if ((type == cgltf_attribute_type_position || + type == cgltf_attribute_type_normal || + type == cgltf_attribute_type_tangent || + type == cgltf_attribute_type_texcoord) && + idx == 0) { + cgltf_accessor *attr = prim->attributes[attr_idx].data; + uint64_t attr_size = vertex_count * attr->stride; + attr_size_per_type[type] += attr_size; + } + } + + for (uint32_t i = 0; i < cgltf_attribute_type_max_enum; ++i) { + tb_auto attr_size = attr_size_per_type[i]; + if (attr_size > 0) { + attr_size_per_type[i] = tb_calc_aligned_size(1, attr_size, 16); + vertex_size += attr_size_per_type[i]; + } + } + } + + geom_size = index_size + vertex_size; + } + + uint64_t attr_offset_per_type[cgltf_attribute_type_max_enum] = {0}; + { + uint64_t offset = index_size; + for (uint32_t i = 0; i < cgltf_attribute_type_max_enum; ++i) { + tb_auto attr_size = attr_size_per_type[i]; + if (attr_size > 0) { + attr_offset_per_type[i] = offset; + offset += attr_size; + } + } + } + + // Create space for the mesh on the GPU + void *ptr = NULL; + TbBufferCopy buf_copy = {0}; + { + + VkBufferCreateInfo create_info = { + .sType = VK_STRUCTURE_TYPE_BUFFER_CREATE_INFO, + .size = geom_size, + .usage = VK_BUFFER_USAGE_TRANSFER_SRC_BIT | + VK_BUFFER_USAGE_TRANSFER_DST_BIT | + VK_BUFFER_USAGE_INDEX_BUFFER_BIT | + VK_BUFFER_USAGE_VERTEX_BUFFER_BIT | + VK_BUFFER_USAGE_STORAGE_TEXEL_BUFFER_BIT | + VK_BUFFER_USAGE_STORAGE_BUFFER_BIT | + VK_BUFFER_USAGE_SHADER_DEVICE_ADDRESS_BIT, + }; + char mesh_name[512] = {0}; + + tb_rnd_sys_create_gpu_buffer_noup(rnd_sys, &create_info, mesh_name, + &data.gpu_buffer, &data.host_buffer, + &ptr); + + buf_copy = (TbBufferCopy){ + .dst = data.gpu_buffer.buffer, + .src = data.host_buffer.buffer, + .region = + { + .size = create_info.size, + }, + }; + } + + // Read the cgltf mesh into the driver owned memory + { + uint64_t idx_offset = 0; + uint64_t vertex_count = 0; + cgltf_size attr_count = 0; + for (cgltf_size prim_idx = 0; prim_idx < gltf_mesh->primitives_count; + ++prim_idx) { + tb_auto prim = &gltf_mesh->primitives[prim_idx]; + + { + tb_auto indices = prim->indices; + tb_auto view = indices->buffer_view; + cgltf_size src_size = indices->count * indices->stride; + cgltf_size padded_size = + tb_calc_aligned_size(indices->count, indices->stride, 16); + + // Decode the buffer + cgltf_result res = tb_decompress_buffer_view(tb_thread_alloc, view); + TB_CHECK(res == cgltf_result_success, "Failed to decode buffer view"); + + void *src = ((uint8_t *)view->data) + indices->offset; + void *dst = ((uint8_t *)(ptr)) + idx_offset; + SDL_memcpy(dst, src, src_size); // NOLINT + idx_offset += padded_size; + } + + // Determine the order of attributes + cgltf_size attr_order[6] = {0}; + { + const cgltf_attribute_type req_order[6] = { + cgltf_attribute_type_position, cgltf_attribute_type_normal, + cgltf_attribute_type_tangent, cgltf_attribute_type_texcoord, + cgltf_attribute_type_joints, cgltf_attribute_type_weights, + }; + cgltf_size attr_target_idx = 0; + for (uint32_t i = 0; i < 6; ++i) { + bool found = false; + for (cgltf_size attr_idx = 0; attr_idx < prim->attributes_count; + ++attr_idx) { + cgltf_attribute *attr = &prim->attributes[attr_idx]; + if (attr->type == req_order[i]) { + attr_order[attr_target_idx] = attr_idx; + attr_target_idx++; + if (prim_idx == 0) { + attr_count++; + } + found = true; + } + if (found) { + break; + } + } + } + } + + for (cgltf_size attr_idx = 0; attr_idx < attr_count; ++attr_idx) { + cgltf_attribute *attr = &prim->attributes[attr_order[attr_idx]]; + cgltf_accessor *accessor = attr->data; + cgltf_buffer_view *view = accessor->buffer_view; + + uint64_t mesh_vert_offset = vertex_count * attr->data->stride; + uint64_t vtx_offset = + attr_offset_per_type[attr->type] + mesh_vert_offset; + + size_t src_size = accessor->stride * accessor->count; + + // Decode the buffer + cgltf_result res = tb_decompress_buffer_view(tb_thread_alloc, view); + TB_CHECK(res == cgltf_result_success, "Failed to decode buffer view"); + + void *src = ((uint8_t *)view->data) + accessor->offset; + void *dst = ((uint8_t *)(ptr)) + vtx_offset; + SDL_memcpy(dst, src, src_size); // NOLINT + } + + vertex_count += prim->attributes[0].data->count; + } + + // Construct one write per primitive + { + static const VkFormat + attr_formats_per_type[cgltf_attribute_type_max_enum] = { + VK_FORMAT_UNDEFINED, VK_FORMAT_R16G16B16A16_SINT, + VK_FORMAT_R8G8B8A8_SNORM, VK_FORMAT_R8G8B8A8_SNORM, + VK_FORMAT_R16G16_SINT, VK_FORMAT_R8G8B8A8_UNORM, + VK_FORMAT_R16G16B16A16_SINT, VK_FORMAT_R8G8B8A8_SINT, + }; + static const int32_t attr_idx_per_type[cgltf_attribute_type_max_enum] = { + -1, 0, 1, 2, 3, -1, 5, 6, -1, + }; + + // Create one buffer view for indices + { + VkFormat idx_format = VK_FORMAT_R16_UINT; + if (data.idx_type == VK_INDEX_TYPE_UINT32) { + idx_format = VK_FORMAT_R32_UINT; + } + TB_CHECK(index_size, "Unexpected index size of 0"); + +#if TB_USE_DESC_BUFFER == 1 + data.index_addr = (VkDescriptorAddressInfoEXT){ + .sType = VK_STRUCTURE_TYPE_DESCRIPTOR_ADDRESS_INFO_EXT, + .address = data.gpu_buffer.address, + .range = index_size, + .format = idx_format, + }; +#else + VkBufferViewCreateInfo create_info = { + .sType = VK_STRUCTURE_TYPE_BUFFER_VIEW_CREATE_INFO, + .buffer = data.gpu_buffer.buffer, + .offset = 0, + .range = index_size, + .format = idx_format, + }; + tb_rnd_create_buffer_view(rnd_sys, &create_info, "Mesh Index View", + &data.index_view); +#endif + } + +#if TB_USE_DESC_BUFFER == 1 + // Set a default buffer for each primitive + for (size_t attr_idx = 0; attr_idx < TB_INPUT_PERM_COUNT; ++attr_idx) { + data.attribute_addr[attr_idx] = (VkDescriptorAddressInfoEXT){ + .sType = VK_STRUCTURE_TYPE_DESCRIPTOR_ADDRESS_INFO_EXT, + .range = VK_WHOLE_SIZE, + }; + } +#endif + + for (size_t attr_idx = 0; attr_idx < attr_count; ++attr_idx) { + cgltf_attribute *attr = &gltf_mesh->primitives[0].attributes[attr_idx]; + +#if TB_USE_DESC_BUFFER == 1 + VkDeviceAddress address = + data.gpu_buffer.address + attr_offset_per_type[attr->type]; + data.attribute_addr[attr_idx_per_type[attr->type]] = + (VkDescriptorAddressInfoEXT){ + .sType = VK_STRUCTURE_TYPE_DESCRIPTOR_ADDRESS_INFO_EXT, + .address = address, + .range = attr_size_per_type[attr->type], + .format = attr_formats_per_type[attr->type], + }; +#else + // Create a buffer view per attribute + VkBufferViewCreateInfo create_info = { + .sType = VK_STRUCTURE_TYPE_BUFFER_VIEW_CREATE_INFO, + .buffer = data.gpu_buffer.buffer, + .offset = attr_offset_per_type[attr->type], + .range = VK_WHOLE_SIZE, + .format = attr_formats_per_type[attr->type], + }; + tb_rnd_create_buffer_view( + rnd_sys, &create_info, "Mesh Attribute View", + &data.attr_views[attr_idx_per_type[attr->type]]); +#endif + } + } + + // Make sure to flush the gpu alloc if necessary + tb_flush_alloc(rnd_sys, data.gpu_buffer.alloc); + + // Only enqueue this buffer upload request after the allocation is flushed + if (buf_copy.src != NULL) { + tb_rnd_upload_buffers(rnd_sys, &buf_copy, 1); + } + } + return data; +} + +void tb_load_gltf_mesh_task(const void *args) { + TB_TRACY_SCOPE("Load GLTF Mesh Task"); + tb_auto load_args = (const TbLoadGLTFMeshArgs *)args; + tb_auto rnd_sys = load_args->common.rnd_sys; + tb_auto mesh = load_args->common.mesh; + tb_auto data = load_args->gltf.data; + tb_auto index = load_args->gltf.index; + tb_auto counter = load_args->counter; + + cgltf_mesh *gltf_mesh = &data->meshes[index]; + + // Queue upload of mesh data to the GPU + TbMeshData mesh_data = {0}; + if (mesh != 0) { + mesh_data = tb_load_gltf_mesh(rnd_sys, gltf_mesh); + } + + // Launch pinned task to handle loading signals on main thread + TbMeshLoadedArgs loaded_args = { + .ecs = load_args->common.ecs, + .mesh = mesh, + .comp = mesh_data, + .data = data, + .gltf_mesh = gltf_mesh, + .counter = counter, + }; + tb_launch_pinned_task_args(load_args->common.enki, + load_args->common.loaded_task, &loaded_args, + sizeof(TbMeshLoadedArgs)); +} + +// Systems + +void tb_reset_queue_counters(ecs_iter_t *it) { + TB_TRACY_SCOPE("Reset Queue Counters"); + tb_auto mesh_counter = ecs_field(it, TbPerFrameMeshQueueCounter, 1); + tb_auto submesh_counter = ecs_field(it, TbPerFrameSubmeshQueueCounter, 2); + *mesh_counter = 0; + *submesh_counter = 0; +} + +void tb_queue_gltf_mesh_loads(ecs_iter_t *it) { + TB_TRACY_SCOPE("Queue GLTF Mesh Loads"); + + tb_auto ecs = it->world; + + tb_auto ctx = ecs_field(it, TbMeshCtx, 1); + tb_auto enki = *ecs_field(it, TbTaskScheduler, 2); + tb_auto counter = ecs_field(it, TbMeshQueueCounter, 3); + tb_auto queue_counter = ecs_field(it, TbPerFrameMeshQueueCounter, 4); + + bool saturated = false; + + tb_auto mesh_it = ecs_query_iter(ecs, ctx->mesh_load_query); + while (ecs_query_next(&mesh_it)) { + if (saturated) { + break; + } + tb_auto reqs = ecs_field(&mesh_it, TbMeshGLTFLoadRequest, 1); + for (int32_t i = 0; i < mesh_it.count; ++i) { + if (*queue_counter >= TB_MAX_MESH_QUEUE_PER_FRAME) { + saturated = true; + break; + } + (*queue_counter)++; + + if (SDL_AtomicGet(counter) > TbMaxParallelMeshLoads) { + saturated = true; + break; + } + TbMesh2 ent = mesh_it.entities[i]; + tb_auto req = reqs[i]; + + // This pinned task will be launched by the loading task + TbPinnedTask loaded_task = + tb_create_pinned_task(enki, tb_mesh_loaded, NULL, 0); + + TbLoadGLTFMeshArgs args = { + .common = + { + .ecs = ecs, + .rnd_sys = ecs_singleton_get_mut(ecs, TbRenderSystem), + .mesh = ent, + .enki = enki, + .loaded_task = loaded_task, + }, + .gltf = req, + .counter = counter, + }; + TbTask load_task = tb_async_task(enki, tb_load_gltf_mesh_task, &args, + sizeof(TbLoadGLTFMeshArgs)); + // Apply task component to mesh entity + ecs_set(ecs, ent, TbTask, {load_task}); + SDL_AtomicIncRef(counter); + ctx->owned_mesh_count++; + + // Remove load request as it has now been enqueued to the task system + ecs_remove(ecs, ent, TbMeshGLTFLoadRequest); + } + } +} + +typedef struct TbSubMeshLoadArgs { + ecs_world_t *ecs; + TbSubMeshQueueCounter *counter; + TbMesh2 mesh; + TbSubMeshGLTFLoadRequest req; +} TbSubMeshLoadArgs; + +void tb_load_submeshes_task(const void *args) { + TB_TRACY_SCOPE("Load Submeshes Task"); + tb_auto load_args = (const TbSubMeshLoadArgs *)args; + tb_auto ecs = load_args->ecs; + tb_auto counter = load_args->counter; + tb_auto mesh = load_args->mesh; + tb_auto gltf_mesh = load_args->req.gltf_mesh; + tb_auto data = load_args->req.data; + + // As we go through submeshes we also want to construct an AABB for this + // mesh + TbAABB mesh_aabb = tb_aabb_init(); + + // Meshes are uploaded so now we just need to setup submeshes + uint32_t index_offset = 0; + uint32_t vertex_offset = 0; + for (cgltf_size i = 0; i < gltf_mesh->primitives_count; ++i) { + tb_auto prim = &gltf_mesh->primitives[i]; + + // Create an entity for this submesh which is a child of the mesh entity + TbSubMesh2 submesh = ecs_new_entity(ecs, 0); + ecs_add_pair(ecs, submesh, EcsChildOf, mesh); + + TbSubMesh2Data submesh_data = { + .vertex_offset = vertex_offset, + .vertex_count = prim->attributes[0].data->count, + }; + vertex_offset += submesh_data.vertex_count; + + // If no material is provided we use a default + const cgltf_material *material = prim->material; + if (material == NULL) { + submesh_data.material = tb_get_default_mat(ecs, TB_MAT_USAGE_SCENE); + } else { + submesh_data.material = tb_mat_sys_load_gltf_mat( + ecs, data, material->name, TB_MAT_USAGE_SCENE); + } + + // Determine index size and count + { + const cgltf_accessor *indices = prim->indices; + + submesh_data.index_count = indices->count; + submesh_data.index_offset = index_offset; + + // calculate the aligned size + size_t index_size = + tb_calc_aligned_size(indices->count, indices->stride, 16); + // calculate number of indices that represent that aligned size + index_offset += (index_size / indices->stride); + } + + // Determine input permutation and attribute count + { + uint64_t vertex_attributes = 0; + for (cgltf_size attr_idx = 0; attr_idx < prim->attributes_count; + ++attr_idx) { + cgltf_attribute_type type = prim->attributes[attr_idx].type; + int32_t index = prim->attributes[attr_idx].index; + if ((type == cgltf_attribute_type_position || + type == cgltf_attribute_type_normal || + type == cgltf_attribute_type_tangent || + type == cgltf_attribute_type_texcoord) && + index == 0) { + if (type == cgltf_attribute_type_position) { + vertex_attributes |= TB_INPUT_PERM_POSITION; + } else if (type == cgltf_attribute_type_normal) { + vertex_attributes |= TB_INPUT_PERM_NORMAL; + } else if (type == cgltf_attribute_type_tangent) { + vertex_attributes |= TB_INPUT_PERM_TANGENT; + } else if (type == cgltf_attribute_type_texcoord) { + vertex_attributes |= TB_INPUT_PERM_TEXCOORD0; + } + } + } + submesh_data.vertex_perm = vertex_attributes; + } + + // Read AABB from gltf + TbAABB submesh_aabb = tb_aabb_init(); + { + const cgltf_attribute *pos_attr = NULL; + // Find position attribute + for (size_t i = 0; i < prim->attributes_count; ++i) { + tb_auto attr = &prim->attributes[i]; + if (attr->type == cgltf_attribute_type_position) { + pos_attr = attr; + break; + } + } + + TB_CHECK(pos_attr, "Expected a position attribute"); + TB_CHECK(pos_attr->type == cgltf_attribute_type_position, + "Unexpected vertex attribute type"); + + float *min = pos_attr->data->min; + float *max = pos_attr->data->max; + + tb_aabb_add_point(&submesh_aabb, tb_f3(min[0], min[1], min[2])); + tb_aabb_add_point(&submesh_aabb, tb_f3(max[0], max[1], max[2])); + } + ecs_set_ptr(ecs, submesh, TbAABB, &submesh_aabb); + ecs_set_ptr(ecs, submesh, TbSubMesh2Data, &submesh_data); + ecs_add(ecs, submesh, TbSubMeshParsed); + + tb_aabb_add_point(&mesh_aabb, submesh_aabb.min); + tb_aabb_add_point(&mesh_aabb, submesh_aabb.max); + } + ecs_set_ptr(ecs, mesh, TbAABB, &mesh_aabb); + ecs_remove(ecs, mesh, TbSubMeshGLTFLoadRequest); + + // Mesh submeshes are loaded + SDL_AtomicDecRef(counter); +} + +void tb_queue_gltf_submesh_loads(ecs_iter_t *it) { + TB_TRACY_SCOPE("Queue GLTF Submesh Loads"); + tb_auto ctx = ecs_field(it, TbMeshCtx, 1); + tb_auto enki = *ecs_field(it, TbTaskScheduler, 2); + tb_auto counter = ecs_field(it, TbSubMeshQueueCounter, 3); + tb_auto queue_counter = ecs_field(it, TbPerFrameSubmeshQueueCounter, 4); + + bool saturated = false; + + tb_auto submesh_it = ecs_query_iter(it->world, ctx->submesh_load_query); + while (ecs_query_next(&submesh_it)) { + if (saturated) { + break; + } + tb_auto reqs = ecs_field(&submesh_it, TbSubMeshGLTFLoadRequest, 1); + for (int32_t i = 0; i < submesh_it.count; ++i) { + if (*queue_counter >= TB_MAX_SUBMESH_QUEUE_PER_FRAME) { + saturated = true; + break; + } + (*queue_counter)++; + if (SDL_AtomicGet(counter) > TbMaxParallelMeshLoads) { + saturated = true; + break; + } + + // Enqueue task so that the submesh loads outside of a system where the + // ecs will be in a writable state + TbSubMeshLoadArgs args = { + .ecs = it->world, + .mesh = submesh_it.entities[i], + .req = reqs[i], + .counter = counter, + }; + tb_auto task = tb_create_pinned_task(enki, tb_load_submeshes_task, &args, + sizeof(TbSubMeshLoadArgs)); + tb_launch_pinned_task(enki, task); + + SDL_AtomicIncRef(counter); + } + } +} + +void tb_write_mesh_attr_desc(ecs_world_t *ecs, TbMeshCtx *ctx, + uint32_t mesh_count, const ecs_entity_t *entities, + const TbMeshData *data, TbAllocator tmp_alloc, + TbAllocator rnd_tmp_alloc, uint32_t attr_idx) { + TB_DYN_ARR_OF(TbDynDescWrite) writes = {0}; + TB_DYN_ARR_RESET(writes, rnd_tmp_alloc, mesh_count); + + for (uint32_t i = 0; i < mesh_count; ++i) { + tb_auto mesh = &data[i]; + VkBufferView attr_view = mesh->index_view; + if (attr_idx > 0) { + attr_view = mesh->attr_views[attr_idx - 1]; + } + TbDynDescWrite write = { + .type = VK_DESCRIPTOR_TYPE_STORAGE_TEXEL_BUFFER, + .desc.texel_buffer = attr_view, + }; + TB_DYN_ARR_APPEND(writes, write); + } + + const uint32_t write_count = TB_DYN_ARR_SIZE(writes); + if (write_count > 0) { + tb_auto indices = tb_alloc_nm_tp(tmp_alloc, write_count, uint32_t); + TbDynDescPool *pool = NULL; + switch (attr_idx) { + case 0: + pool = &ctx->idx_desc_pool; + break; + case 1: + pool = &ctx->pos_desc_pool; + break; + case 2: + pool = &ctx->norm_desc_pool; + break; + case 3: + pool = &ctx->tan_desc_pool; + break; + case 4: + pool = &ctx->uv0_desc_pool; + break; + default: + pool = NULL; + } + TB_CHECK(pool, "Failed to find pool for attribute"); + tb_write_dyn_desc_pool(pool, write_count, writes.data, indices); + + // A bit hack but we always expect attr idx 0 (indices) + // so this is when we can use the reported indices to mark + // mesh components + // Ideally we'd make sure the various attr pools are in sync but for now we + // just pray + if (attr_idx == 0) { + TB_DYN_ARR_FOREACH(writes, i) { + ecs_set(ecs, entities[i], TbMeshIndex, {indices[i]}); + ecs_add(ecs, entities[i], TbDescriptorReady); + } + } + } +} + +void tb_finalize_meshes(ecs_iter_t *it) { + TB_TRACY_SCOPE("Finalize Meshes"); + + tb_auto ctx = ecs_field(it, TbMeshCtx, 1); + tb_auto rnd_sys = ecs_field(it, TbRenderSystem, 2); + tb_auto meshes = ecs_field(it, TbMeshData, 3); + + if (ctx->owned_mesh_count == 0 || it->count == 0) { + return; + } + + // Render Thread allocator to make sure writes live + tb_auto rnd_tmp_alloc = + rnd_sys->render_thread->frame_states[rnd_sys->frame_idx].tmp_alloc.alloc; + + // HACK: known fixed number of mesh attributes + for (uint32_t i = 0; i < 5; ++i) { + tb_write_mesh_attr_desc(it->world, ctx, it->count, it->entities, meshes, + rnd_sys->tmp_alloc, rnd_tmp_alloc, i); + } +} + +void tb_update_mesh_pool(ecs_iter_t *it) { + TB_TRACY_SCOPE("Update Mesh Pool"); + + tb_auto ctx = ecs_field(it, TbMeshCtx, 1); + tb_auto rnd_sys = ecs_field(it, TbRenderSystem, 2); + + tb_tick_dyn_desc_pool(rnd_sys, &ctx->idx_desc_pool); + tb_tick_dyn_desc_pool(rnd_sys, &ctx->pos_desc_pool); + tb_tick_dyn_desc_pool(rnd_sys, &ctx->norm_desc_pool); + tb_tick_dyn_desc_pool(rnd_sys, &ctx->tan_desc_pool); + tb_tick_dyn_desc_pool(rnd_sys, &ctx->uv0_desc_pool); +} + +void tb_check_submesh_readiness(ecs_iter_t *it) { + TB_TRACY_SCOPE("Check Submesh Readiness"); + uint32_t count = 0; + tb_auto submesh_data = ecs_field(it, TbSubMesh2Data, 1); + for (int32_t i = 0; i < it->count; ++i) { + TbSubMesh2 submesh = it->entities[i]; + tb_auto data = &submesh_data[i]; + // Submeshes are ready when dependant materials are ready + if (tb_is_material_ready(it->world, data->material)) { + ecs_remove(it->world, submesh, TbSubMeshParsed); + ecs_add(it->world, submesh, TbSubMeshReady); + } + count++; + if (count == TB_MAX_READY_CHECKS_PER_FRAME) { + return; + } + } +} + +void tb_check_mesh_readiness(ecs_iter_t *it) { + TB_TRACY_SCOPE("Check Mesh Readiness"); + uint32_t count = 0; + for (int32_t i = 0; i < it->count; ++i) { + TbMesh2 mesh = it->entities[i]; + + // Check that all children are ready + tb_auto child_iter = ecs_children(it->world, mesh); + bool children_ready = false; + while (ecs_children_next(&child_iter)) { + if (children_ready == false) { + children_ready = child_iter.count > 0; + } + for (int32_t child_i = 0; child_i < child_iter.count; ++child_i) { + ecs_entity_t child_ent = child_iter.entities[child_i]; + // If the child is a submesh + if (ecs_has(it->world, child_ent, TbSubMesh2Data)) { + // Ensure that it is ready + if (!ecs_has(it->world, child_ent, TbSubMeshReady)) { + children_ready = false; + break; + } + } + } + } + + // If all submeshes are ready, we are ready + if (children_ready) { + ecs_remove(it->world, mesh, TbMeshParsed); + ecs_add(it->world, mesh, TbMeshReady); + } + + count++; + if (count == TB_MAX_READY_CHECKS_PER_FRAME) { + return; + } + } +} + +// Toybox Glue + +void tb_register_mesh2_sys(TbWorld *world) { + tb_auto ecs = world->ecs; + ECS_COMPONENT_DEFINE(ecs, TbMeshCtx); + ECS_COMPONENT_DEFINE(ecs, TbMeshData); + ECS_COMPONENT_DEFINE(ecs, TbSubMesh2Data); + ECS_COMPONENT_DEFINE(ecs, TbSubMeshQueueCounter); + ECS_COMPONENT_DEFINE(ecs, TbMeshQueueCounter); + ECS_COMPONENT_DEFINE(ecs, TbPerFrameMeshQueueCounter); + ECS_COMPONENT_DEFINE(ecs, TbPerFrameSubmeshQueueCounter); + ECS_COMPONENT_DEFINE(ecs, TbMeshIndex); + ECS_COMPONENT_DEFINE(ecs, TbAABB); + ECS_COMPONENT_DEFINE(ecs, TbMeshGLTFLoadRequest); + ECS_COMPONENT_DEFINE(ecs, TbSubMeshGLTFLoadRequest); + ECS_TAG_DEFINE(ecs, TbMeshLoaded); + ECS_TAG_DEFINE(ecs, TbMeshParsed); + ECS_TAG_DEFINE(ecs, TbMeshReady); + ECS_TAG_DEFINE(ecs, TbSubMeshParsed); + ECS_TAG_DEFINE(ecs, TbSubMeshReady); + + ECS_SYSTEM( + ecs, tb_reset_queue_counters, + EcsOnLoad, [out] TbPerFrameMeshQueueCounter(TbPerFrameMeshQueueCounter), + [out] TbPerFrameSubmeshQueueCounter(TbPerFrameSubmeshQueueCounter)); + + ECS_SYSTEM(ecs, tb_queue_gltf_mesh_loads, + EcsPostLoad, [in] TbMeshCtx(TbMeshCtx), + [inout] TbTaskScheduler(TbTaskScheduler), + [inout] TbMeshQueueCounter(TbMeshQueueCounter), + [inout] TbPerFrameMeshQueueCounter(TbPerFrameMeshQueueCounter)); + ECS_SYSTEM( + ecs, tb_queue_gltf_submesh_loads, EcsPostLoad, [in] TbMeshCtx(TbMeshCtx), + [inout] TbTaskScheduler(TbTaskScheduler), + [inout] TbSubMeshQueueCounter(TbSubMeshQueueCounter), + [inout] TbPerFrameSubmeshQueueCounter(TbPerFrameSubmeshQueueCounter)); + + // System that ticks as we ensure mesh descriptors are written + ECS_SYSTEM( + ecs, tb_finalize_meshes, EcsPostUpdate, [in] TbMeshCtx(TbMeshCtx), + [in] TbRenderSystem(TbRenderSystem), [in] TbMeshData, [in] TbMeshLoaded, + !TbDescriptorReady); + // When all meshes are loaded we start making them available to shaders + ECS_SYSTEM(ecs, tb_update_mesh_pool, EcsPreStore, [in] TbMeshCtx(TbMeshCtx), + [in] TbRenderSystem(TbRenderSystem)); + + ECS_SYSTEM(ecs, tb_check_submesh_readiness, + EcsPreStore, [in] TbSubMesh2Data, [in] TbSubMeshParsed); + ECS_SYSTEM( + ecs, tb_check_mesh_readiness, + EcsPreStore, [in] TbMeshData, [in] TbMeshParsed, [in] TbDescriptorReady); + + tb_auto rnd_sys = ecs_singleton_get_mut(ecs, TbRenderSystem); + + TbMeshCtx ctx = { + .mesh_load_query = + ecs_query(ecs, {.filter.terms = + { + {.id = ecs_id(TbMeshGLTFLoadRequest)}, + }}), + .submesh_load_query = + ecs_query(ecs, {.filter.terms = + { + {.id = ecs_id(TbSubMeshGLTFLoadRequest)}, + }}), + }; + + // Create mesh descriptor set layout + { + const VkDescriptorBindingFlags flags = + VK_DESCRIPTOR_BINDING_VARIABLE_DESCRIPTOR_COUNT_BIT | + VK_DESCRIPTOR_BINDING_PARTIALLY_BOUND_BIT; + VkDescriptorSetLayoutCreateInfo create_info = { + .sType = VK_STRUCTURE_TYPE_DESCRIPTOR_SET_LAYOUT_CREATE_INFO, +#if TB_USE_DESC_BUFFER == 1 + .flags = VK_DESCRIPTOR_SET_LAYOUT_CREATE_DESCRIPTOR_BUFFER_BIT_EXT, +#endif + .pNext = + &(VkDescriptorSetLayoutBindingFlagsCreateInfo){ + .sType = + VK_STRUCTURE_TYPE_DESCRIPTOR_SET_LAYOUT_BINDING_FLAGS_CREATE_INFO, + .bindingCount = 1, + .pBindingFlags = (VkDescriptorBindingFlags[1]){flags}, + }, + .bindingCount = 1, + .pBindings = + (VkDescriptorSetLayoutBinding[1]){ + { + .binding = 0, + .descriptorCount = TB_DESC_POOL_CAP, + .descriptorType = VK_DESCRIPTOR_TYPE_STORAGE_TEXEL_BUFFER, + .stageFlags = VK_SHADER_STAGE_VERTEX_BIT, + }, + }, + }; + tb_rnd_create_set_layout(rnd_sys, &create_info, "Mesh Attr Layout", + &ctx.set_layout); + } + +#if TB_USE_DESC_BUFFER == 1 + tb_create_descriptor_buffer(rnd_sys, ctx.set_layout, "Mesh Idx Descriptors", + 4, &ctx.idx_desc_buf); + tb_create_descriptor_buffer(rnd_sys, ctx.set_layout, "Mesh Pos Descriptors", + 4, &ctx.pos_desc_buf); + tb_create_descriptor_buffer(rnd_sys, ctx.set_layout, "Mesh Norm Descriptors", + 4, &ctx.norm_desc_buf); + tb_create_descriptor_buffer(rnd_sys, ctx.set_layout, "Mesh Tan Descriptors", + 4, &ctx.tan_desc_buf); + tb_create_descriptor_buffer(rnd_sys, ctx.set_layout, "Mesh UV0 Descriptors", + 4, &ctx.uv0_desc_buf); +#else + static const uint32_t desc_cap = TB_DESC_POOL_CAP; + tb_create_dyn_desc_pool(rnd_sys, "Mesh Index Descriptors", ctx.set_layout, + VK_DESCRIPTOR_TYPE_STORAGE_TEXEL_BUFFER, desc_cap, + &ctx.idx_desc_pool, 0); + tb_create_dyn_desc_pool(rnd_sys, "Mesh Position Descriptors", ctx.set_layout, + VK_DESCRIPTOR_TYPE_STORAGE_TEXEL_BUFFER, desc_cap, + &ctx.pos_desc_pool, 0); + tb_create_dyn_desc_pool(rnd_sys, "Mesh Normal Descriptors", ctx.set_layout, + VK_DESCRIPTOR_TYPE_STORAGE_TEXEL_BUFFER, desc_cap, + &ctx.norm_desc_pool, 0); + tb_create_dyn_desc_pool(rnd_sys, "Mesh Tangent Descriptors", ctx.set_layout, + VK_DESCRIPTOR_TYPE_STORAGE_TEXEL_BUFFER, desc_cap, + &ctx.tan_desc_pool, 0); + tb_create_dyn_desc_pool(rnd_sys, "Mesh Texcoord0 Descriptors", ctx.set_layout, + VK_DESCRIPTOR_TYPE_STORAGE_TEXEL_BUFFER, desc_cap, + &ctx.uv0_desc_pool, 0); +#endif + + ecs_singleton_set_ptr(ecs, TbMeshCtx, &ctx); + + { + TbMeshQueueCounter queue_count = {0}; + SDL_AtomicSet(&queue_count, 0); + ecs_singleton_set_ptr(ecs, TbMeshQueueCounter, &queue_count); + } + { + TbSubMeshQueueCounter queue_count = {0}; + SDL_AtomicSet(&queue_count, 0); + ecs_singleton_set_ptr(ecs, TbSubMeshQueueCounter, &queue_count); + } + + ecs_singleton_set(ecs, TbPerFrameMeshQueueCounter, {0}); + ecs_singleton_set(ecs, TbPerFrameSubmeshQueueCounter, {0}); +} + +void tb_unregister_mesh2_sys(TbWorld *world) { + tb_auto ecs = world->ecs; + tb_auto rnd_sys = ecs_singleton_get_mut(ecs, TbRenderSystem); + tb_auto ctx = ecs_singleton_get_mut(ecs, TbMeshCtx); + + ecs_query_fini(ctx->mesh_load_query); + ecs_query_fini(ctx->submesh_load_query); + + tb_rnd_destroy_set_layout(rnd_sys, ctx->set_layout); + + tb_destroy_descriptor_buffer(rnd_sys, &ctx->idx_desc_buf); + tb_destroy_descriptor_buffer(rnd_sys, &ctx->pos_desc_buf); + tb_destroy_descriptor_buffer(rnd_sys, &ctx->norm_desc_buf); + tb_destroy_descriptor_buffer(rnd_sys, &ctx->tan_desc_buf); + tb_destroy_descriptor_buffer(rnd_sys, &ctx->uv0_desc_buf); + + // TODO: Release all default references + + // TODO: Check for leaks + + // TODO: Clean up descriptor pool + + ecs_singleton_remove(ecs, TbMeshCtx); +} + +TB_REGISTER_SYS(tb, mesh2, TB_MESH_SYS_PRIO) + +// Public API + +VkDescriptorSetLayout tb_mesh_sys_get_set_layout(ecs_world_t *ecs) { + tb_auto ctx = ecs_singleton_get_mut(ecs, TbMeshCtx); + return ctx->set_layout; +} + +VkDescriptorSet tb_mesh_sys_get_idx_set(ecs_world_t *ecs) { + tb_auto ctx = ecs_singleton_get_mut(ecs, TbMeshCtx); + tb_auto rnd_sys = ecs_singleton_get_mut(ecs, TbRenderSystem); + return tb_dyn_desc_pool_get_set(rnd_sys, &ctx->idx_desc_pool); +} +VkDescriptorSet tb_mesh_sys_get_pos_set(ecs_world_t *ecs) { + tb_auto ctx = ecs_singleton_get_mut(ecs, TbMeshCtx); + tb_auto rnd_sys = ecs_singleton_get_mut(ecs, TbRenderSystem); + return tb_dyn_desc_pool_get_set(rnd_sys, &ctx->pos_desc_pool); +} +VkDescriptorSet tb_mesh_sys_get_norm_set(ecs_world_t *ecs) { + tb_auto ctx = ecs_singleton_get_mut(ecs, TbMeshCtx); + tb_auto rnd_sys = ecs_singleton_get_mut(ecs, TbRenderSystem); + return tb_dyn_desc_pool_get_set(rnd_sys, &ctx->norm_desc_pool); +} +VkDescriptorSet tb_mesh_sys_get_tan_set(ecs_world_t *ecs) { + tb_auto ctx = ecs_singleton_get_mut(ecs, TbMeshCtx); + tb_auto rnd_sys = ecs_singleton_get_mut(ecs, TbRenderSystem); + return tb_dyn_desc_pool_get_set(rnd_sys, &ctx->tan_desc_pool); +} +VkDescriptorSet tb_mesh_sys_get_uv0_set(ecs_world_t *ecs) { + tb_auto ctx = ecs_singleton_get_mut(ecs, TbMeshCtx); + tb_auto rnd_sys = ecs_singleton_get_mut(ecs, TbRenderSystem); + return tb_dyn_desc_pool_get_set(rnd_sys, &ctx->uv0_desc_pool); +} + +VkDescriptorBufferBindingInfoEXT tb_mesh_sys_get_idx_addr(ecs_world_t *ecs) { + tb_auto ctx = ecs_singleton_get_mut(ecs, TbMeshCtx); + return tb_desc_buff_get_binding(&ctx->idx_desc_buf); +} + +VkDescriptorBufferBindingInfoEXT tb_mesh_sys_get_pos_addr(ecs_world_t *ecs) { + tb_auto ctx = ecs_singleton_get_mut(ecs, TbMeshCtx); + return tb_desc_buff_get_binding(&ctx->pos_desc_buf); +} + +VkDescriptorBufferBindingInfoEXT tb_mesh_sys_get_norm_addr(ecs_world_t *ecs) { + tb_auto ctx = ecs_singleton_get_mut(ecs, TbMeshCtx); + return tb_desc_buff_get_binding(&ctx->norm_desc_buf); +} + +VkDescriptorBufferBindingInfoEXT tb_mesh_sys_get_tan_addr(ecs_world_t *ecs) { + tb_auto ctx = ecs_singleton_get_mut(ecs, TbMeshCtx); + return tb_desc_buff_get_binding(&ctx->tan_desc_buf); +} + +VkDescriptorBufferBindingInfoEXT tb_mesh_sys_get_uv0_addr(ecs_world_t *ecs) { + tb_auto ctx = ecs_singleton_get_mut(ecs, TbMeshCtx); + return tb_desc_buff_get_binding(&ctx->uv0_desc_buf); +} + +TbMesh2 tb_mesh_sys_load_gltf_mesh(ecs_world_t *ecs, cgltf_data *data, + const char *path, const char *name, + uint32_t index) { + TB_TRACY_SCOPE("Load GLTF Mesh"); + bool deferred = false; + if (ecs_is_deferred(ecs)) { + deferred = ecs_defer_end(ecs); + } + + const uint32_t max_name_len = 512; + char mesh_name[max_name_len] = {0}; + SDL_snprintf(mesh_name, max_name_len, "%s_%s", path, name); + + // If an entity already exists with this name it is either loading or loaded + TbMesh2 mesh_ent = ecs_lookup_child(ecs, ecs_id(TbMeshCtx), mesh_name); + if (mesh_ent != 0) { + if (deferred) { + ecs_defer_begin(ecs); + } + return mesh_ent; + } + + TB_CHECK_RETURN(data, "Expected data", 0); + + // Create a mesh entity + mesh_ent = ecs_new_entity(ecs, 0); + ecs_set_name(ecs, mesh_ent, mesh_name); + + // It is a child of the mesh system context singleton + ecs_add_pair(ecs, mesh_ent, EcsChildOf, ecs_id(TbMeshCtx)); + + // Append a mesh load request onto the entity to schedule loading + ecs_set(ecs, mesh_ent, TbMeshGLTFLoadRequest, {data, index}); + ecs_remove(ecs, mesh_ent, TbDescriptorReady); + + if (deferred) { + ecs_defer_begin(ecs); + } + + return mesh_ent; +} + +VkBuffer tb_mesh_sys_get_gpu_mesh(ecs_world_t *ecs, TbMesh2 mesh) { + TB_CHECK(ecs_has(ecs, mesh, TbMeshData), "Entity must have mesh data"); + return ecs_get(ecs, mesh, TbMeshData)->gpu_buffer.buffer; +} + +bool tb_is_mesh_ready(ecs_world_t *ecs, TbMesh2 mesh_ent) { + return ecs_has(ecs, mesh_ent, TbMeshReady) && + ecs_has(ecs, mesh_ent, TbDescriptorReady); +} diff --git a/source/tb_mmap.c b/source/tb_mmap.c new file mode 100644 index 00000000..cf6b6291 --- /dev/null +++ b/source/tb_mmap.c @@ -0,0 +1,112 @@ +#include "tb_mmap.h" + +#include "tb_common.h" + +// Adapted from https://github.com/m-labs/uclibc-lm32/ (public domain) + +#if TB_WINDOWS == 1 + +#include +#include +#include + +#define PROT_READ 0x1 +#define PROT_WRITE 0x2 +/* This flag is only available in WinXP+ */ +#ifdef FILE_MAP_EXECUTE +#define PROT_EXEC 0x4 +#else +#define PROT_EXEC 0x0 +#define FILE_MAP_EXECUTE 0 +#endif + +#define MAP_SHARED 0x01 +#define MAP_PRIVATE 0x02 +#define MAP_ANONYMOUS 0x20 +#define MAP_ANON MAP_ANONYMOUS +#define MAP_FAILED ((void *)-1) + +#ifdef __USE_FILE_OFFSET64 +#define DWORD_HI(x) (x >> 32) +#define DWORD_LO(x) ((x) & 0xffffffff) +#else +#define DWORD_HI(x) (0) +#define DWORD_LO(x) (x) +#endif + +void *tb_mmap(void *start, size_t length, int32_t prot, int32_t flags, + void *file, size_t offset) { + (void)start; + if (prot & ~(PROT_READ | PROT_WRITE | PROT_EXEC)) + return MAP_FAILED; + if ((intptr_t)file == (intptr_t)-1) { + if (!(flags & MAP_ANON) || offset) { + return MAP_FAILED; + } + } else if (flags & MAP_ANON) { + return MAP_FAILED; + } + + DWORD flProtect = PAGE_READONLY; + if (prot & PROT_WRITE) { + if (prot & PROT_EXEC) { + flProtect = PAGE_EXECUTE_READWRITE; + } else { + flProtect = PAGE_READWRITE; + } + } else if (prot & PROT_EXEC) { + if (prot & PROT_READ) { + flProtect = PAGE_EXECUTE_READ; + } else if (prot & PROT_EXEC) { + flProtect = PAGE_EXECUTE; + } + } + + size_t end = length + offset; + HANDLE h = CreateFileMapping(file, NULL, flProtect, DWORD_HI(end), + DWORD_LO(end), NULL); + if (h == NULL) { + return MAP_FAILED; + } + + DWORD dwDesiredAccess = 0; + if (prot & PROT_WRITE) { + dwDesiredAccess = FILE_MAP_WRITE; + } else { + dwDesiredAccess = FILE_MAP_READ; + } + if (prot & PROT_EXEC) { + dwDesiredAccess |= FILE_MAP_EXECUTE; + } + if (flags & MAP_PRIVATE) { + dwDesiredAccess |= FILE_MAP_COPY; + } + void *ret = MapViewOfFile(h, dwDesiredAccess, DWORD_HI(offset), + DWORD_LO(offset), length); + if (ret == NULL) { + CloseHandle(h); + ret = MAP_FAILED; + } + return ret; +} + +void tb_munmap(void *addr, size_t length) { + (void)length; + UnmapViewOfFile(addr); + /* ruh-ro, we leaked handle from CreateFileMapping() ... */ +} + +#undef DWORD_HI +#undef DWORD_LO + +#else +#include + +void *tb_mmap(void *start, size_t length, int32_t prot, int32_t flags, + void *file, size_t offset) { + return mmap(start, length, prot, flags, (intptr_t)file, offset); +} + +void tb_munmap(void *addr, size_t length) { munmap(addr, length); } + +#endif diff --git a/source/noclipcomponent.c b/source/tb_noclip_component.c similarity index 56% rename from source/noclipcomponent.c rename to source/tb_noclip_component.c index e9349614..6a4e93cd 100644 --- a/source/noclipcomponent.c +++ b/source/tb_noclip_component.c @@ -1,19 +1,18 @@ -#include "noclipcomponent.h" +#include "tb_noclip_component.h" -#include "json.h" -#include "world.h" +#include "tb_common.h" +#include "tb_world.h" -#include +#include ECS_COMPONENT_DECLARE(TbNoClipComponent); -bool tb_load_noclip_comp(TbWorld *world, ecs_entity_t ent, - const char *source_path, const cgltf_node *node, - json_object *json) { +bool tb_load_noclip_comp(ecs_world_t *ecs, ecs_entity_t ent, + const char *source_path, const cgltf_data *data, + const cgltf_node *node, json_object *json) { (void)source_path; + (void)data; (void)node; - ecs_world_t *ecs = world->ecs; - TbNoClipComponent comp = {0}; json_object_object_foreach(json, key, value) { if (SDL_strcmp(key, "move_speed") == 0) { @@ -26,7 +25,7 @@ bool tb_load_noclip_comp(TbWorld *world, ecs_entity_t ent, return true; } -ecs_entity_t tb_register_noclip_comp(TbWorld *world) { +TbComponentRegisterResult tb_register_noclip_comp(TbWorld *world) { ecs_world_t *ecs = world->ecs; ECS_COMPONENT_DEFINE(ecs, TbNoClipComponent); @@ -35,7 +34,13 @@ ecs_entity_t tb_register_noclip_comp(TbWorld *world) { {.name = "move_speed", .type = ecs_id(ecs_f32_t)}, {.name = "look_speed", .type = ecs_id(ecs_f32_t)}, }}); - return ecs_id(TbNoClipComponent); + return (TbComponentRegisterResult){ecs_id(TbNoClipComponent), + ecs_id(TbNoClipComponent)}; +} + +bool tb_ready_noclip_comp(ecs_world_t *ecs, ecs_entity_t ent) { + tb_auto comp = ecs_get(ecs, ent, TbNoClipComponent); + return comp != NULL; } TB_REGISTER_COMP(tb, noclip) diff --git a/source/noclipcontrollersystem.c b/source/tb_noclip_controller_system.c similarity index 85% rename from source/noclipcontrollersystem.c rename to source/tb_noclip_controller_system.c index 51b94612..1cd26fa7 100644 --- a/source/noclipcontrollersystem.c +++ b/source/tb_noclip_controller_system.c @@ -1,13 +1,11 @@ -#include "noclipcontrollersystem.h" - -#include "inputsystem.h" #include "json.h" -#include "noclipcomponent.h" -#include "profiling.h" -#include "tbcommon.h" -#include "tbsdl.h" -#include "transformcomponent.h" -#include "world.h" +#include "tb_common.h" +#include "tb_input_system.h" +#include "tb_noclip_component.h" +#include "tb_profiling.h" +#include "tb_sdl.h" +#include "tb_transform_component.h" +#include "tb_world.h" #include @@ -21,8 +19,9 @@ void noclip_update_tick(ecs_iter_t *it) { TbNoClipComponent *noclips = ecs_field(it, TbNoClipComponent, 2); for (int32_t i = 0; i < it->count; ++i) { - TbTransformComponent *transform = &transforms[i]; - TbNoClipComponent *noclip = &noclips[i]; + tb_auto transform = &transforms[i]; + tb_auto noclip = &noclips[i]; + tb_auto entity = it->entities[i]; float2 look_axis = {0}; float2 move_axis = {0}; @@ -80,9 +79,9 @@ void noclip_update_tick(ecs_iter_t *it) { { float delta_look_speed = noclip->look_speed * it->delta_time; - TbQuaternion av0 = + tb_auto av0 = tb_angle_axis_to_quat(tb_f3tof4(up, look_axis.x * delta_look_speed)); - TbQuaternion av1 = tb_angle_axis_to_quat( + tb_auto av1 = tb_angle_axis_to_quat( tb_f3tof4(right, look_axis.y * delta_look_speed)); angular_velocity = tb_mulq(av0, av1); @@ -90,7 +89,7 @@ void noclip_update_tick(ecs_iter_t *it) { tb_translate(&transform->transform, velocity); tb_rotate(&transform->transform, angular_velocity); - transform->dirty = true; + tb_transform_mark_dirty(it->world, entity); } TracyCZoneEnd(ctx); } @@ -106,4 +105,4 @@ void tb_register_noclip_sys(TbWorld *world) { void tb_unregister_noclip_sys(TbWorld *world) { (void)world; } -TB_REGISTER_SYS(tb, noclip, TB_NOCLIP_SYS_PRIO) +TB_REGISTER_SYS(tb, noclip, TB_SYSTEM_NORMAL) diff --git a/source/physicssystem.cpp b/source/tb_physics_system.cpp similarity index 97% rename from source/physicssystem.cpp rename to source/tb_physics_system.cpp index 39fad8f0..940f3a4a 100644 --- a/source/physicssystem.cpp +++ b/source/tb_physics_system.cpp @@ -1,15 +1,15 @@ -#include "physicssystem.h" - -#include "dynarray.h" -#include "physicssystem.hpp" -#include "profiling.h" -#include "rigidbodycomponent.h" +#include "tb_physics_system.h" + +#include "tb_common.h" +#include "tb_dynarray.h" +#include "tb_log.h" +#include "tb_physics_system.hpp" +#include "tb_profiling.h" +#include "tb_queue.h" +#include "tb_rigidbody_component.h" #include "tb_task_scheduler.h" -#include "tbcommon.h" -#include "tblog.h" -#include "tbqueue.h" -#include "transformcomponent.h" -#include "world.h" +#include "tb_transform_component.h" +#include "tb_world.h" #include #include @@ -33,7 +33,7 @@ #include #include -#include "physlayers.h" +#include "tb_phys_layers.h" #include @@ -95,8 +95,6 @@ class TbJobSystem final : public JPH::JobSystemWithBarrier { JPH::JobSystem::Job *job = nullptr; -#pragma clang diagnostic push -#pragma clang diagnostic ignored "-Wgnu-statement-expression" while (TB_QUEUE_POP(*job_queue, &job)) { ZoneScopedC(TracyCategoryColorPhysics); #if (defined(JPH_EXTERNAL_PROFILE) || defined(JPH_PROFILE_ENABLED)) && \ @@ -106,7 +104,6 @@ class TbJobSystem final : public JPH::JobSystemWithBarrier { #endif job->Execute(); } -#pragma clang diagnostic pop } explicit TbJobSystem(TbTaskScheduler enki, TbAllocator std_alloc, diff --git a/source/profiling.cpp b/source/tb_profiling.cpp similarity index 63% rename from source/profiling.cpp rename to source/tb_profiling.cpp index c533f93e..e20cddca 100644 --- a/source/profiling.cpp +++ b/source/tb_profiling.cpp @@ -1,4 +1,4 @@ -#include "profiling.h" +#include "tb_profiling.h" #ifdef TRACY_VK_C_ENABLE @@ -8,16 +8,18 @@ #include #include -#include "tbvk.h" +#include "tb_vk.h" #include "tracy/TracyVulkan.hpp" extern "C" { +void tb_tracy_zone_end(TracyCZoneCtx *ctx) { TracyCZoneEnd(*ctx); } + TracyCGPUContext *TracyCVkContext(VkPhysicalDevice gpu, VkDevice device, VkQueue queue, VkCommandBuffer buffer) { - auto ctx = (tracy::VkCtx *)tracy::tracy_malloc(sizeof(tracy::VkCtx)); - new (ctx) tracy::VkCtx(gpu, device, queue, buffer, nullptr, nullptr); + auto ctx = + tracy::CreateVkContext(gpu, device, queue, buffer, nullptr, nullptr); return (TracyCGPUContext *)ctx; } @@ -26,15 +28,20 @@ TracyCVkContextExt(VkPhysicalDevice gpu, VkDevice device, VkQueue queue, VkCommandBuffer buffer, PFN_vkGetPhysicalDeviceCalibrateableTimeDomainsEXT ext1, PFN_vkGetCalibratedTimestampsEXT ext2) { - auto ctx = (tracy::VkCtx *)tracy::tracy_malloc(sizeof(tracy::VkCtx)); - new (ctx) tracy::VkCtx(gpu, device, queue, buffer, ext1, ext2); + auto ctx = tracy::CreateVkContext(gpu, device, queue, buffer, ext1, ext2); + return (TracyCGPUContext *)ctx; +} + +TracyCGPUContext *TracyCVkContextHostCalib( + VkPhysicalDevice gpu, VkDevice device, PFN_vkResetQueryPoolEXT qpreset, + PFN_vkGetPhysicalDeviceCalibrateableTimeDomainsEXT gpdctd, + PFN_vkGetCalibratedTimestampsEXT gct) { + auto ctx = tracy::CreateVkContext(gpu, device, qpreset, gpdctd, gct); return (TracyCGPUContext *)ctx; } void TracyCVkContextDestroy(TracyCGPUContext *ctx) { - auto *c = (tracy::VkCtx *)ctx; - c->~VkCtx(); - tracy::tracy_free((void *)c); + tracy::DestroyVkContext((tracy::VkCtx *)ctx); } void TracyCVkContextName(TracyCGPUContext *ctx, const char *name, size_t len) { @@ -48,10 +55,11 @@ _TracyCVkNamedZone(TracyCGPUContext *ctx, bool active) { auto scope = (tracy::VkCtxScope *)tracy::tracy_malloc(sizeof(tracy::VkCtxScope)); - new (scope) tracy::VkCtxScope( - (tracy::VkCtx *)ctx, source_loc->line, source_loc->file, - strlen(source_loc->file), source_loc->function, - strlen(source_loc->function), name, strlen(name), cmd_buf, depth, active); + new (scope) + tracy::VkCtxScope((tracy::VkCtx *)ctx, source_loc->line, source_loc->file, + strlen(source_loc->file), source_loc->function, + strlen(source_loc->function), name, strlen(name) + 1, + cmd_buf, depth, active); return (TracyCGPUScope *)scope; } void TracyCVkZoneEnd(TracyCGPUScope *scope) { diff --git a/source/tbrand.c b/source/tb_rand.c similarity index 99% rename from source/tbrand.c rename to source/tb_rand.c index 6cbee232..175b1dfc 100644 --- a/source/tbrand.c +++ b/source/tb_rand.c @@ -1,4 +1,4 @@ -#include "tbrand.h" +#include "tb_rand.h" /* Written in 2016-2018 by David Blackman and Sebastiano Vigna (vigna@acm.org) diff --git a/source/tbrendercommon.c b/source/tb_render_common.c similarity index 94% rename from source/tbrendercommon.c rename to source/tb_render_common.c index e60bb541..4941a307 100644 --- a/source/tbrendercommon.c +++ b/source/tb_render_common.c @@ -1,4 +1,4 @@ -#include "tbrendercommon.h" +#include "tb_render_common.h" void tb_record_fullscreen(VkCommandBuffer buffer, const TbDrawBatch *batch, const TbFullscreenBatch *fs_batch) { diff --git a/source/tb_render_object_system.c b/source/tb_render_object_system.c new file mode 100644 index 00000000..2c46e706 --- /dev/null +++ b/source/tb_render_object_system.c @@ -0,0 +1,232 @@ +#include "tb_render_object_system.h" + +#include "common.hlsli" +#include "tb_common.h" +#include "tb_mesh_component.h" +#include "tb_profiling.h" +#include "tb_render_system.h" +#include "tb_transform_component.h" +#include "tb_world.h" + +#include "blocks/Block.h" + +// Configuration +static const uint32_t TbMaxRenderObjects = 1 << 12; // 4096 + +void tb_register_render_object_sys(TbWorld *world); +void tb_unregister_render_object_sys(TbWorld *world); + +TB_REGISTER_SYS(tb, render_object, TB_RND_OBJ_SYS_PRIO) + +ECS_COMPONENT_DECLARE(TbRenderObject); +ECS_COMPONENT_DECLARE(TbRenderObjectSystem); +ECS_TAG_DECLARE(TbRenderObjectDirty); + +TbRenderObjectSystem create_render_object_system(TbAllocator gp_alloc, + TbAllocator tmp_alloc, + TbRenderSystem *rnd_sys) { + tb_auto sys = (TbRenderObjectSystem){ + .rnd_sys = rnd_sys, + .tmp_alloc = tmp_alloc, + .gp_alloc = gp_alloc, + }; + + { + const VkDescriptorBindingFlags flags = + VK_DESCRIPTOR_BINDING_VARIABLE_DESCRIPTOR_COUNT_BIT | + VK_DESCRIPTOR_BINDING_PARTIALLY_BOUND_BIT; + tb_rnd_create_set_layout( + rnd_sys, + &(VkDescriptorSetLayoutCreateInfo){ + .sType = VK_STRUCTURE_TYPE_DESCRIPTOR_SET_LAYOUT_CREATE_INFO, + .pNext = + &(VkDescriptorSetLayoutBindingFlagsCreateInfo){ + .sType = + VK_STRUCTURE_TYPE_DESCRIPTOR_SET_LAYOUT_BINDING_FLAGS_CREATE_INFO, + .bindingCount = 1, + .pBindingFlags = (VkDescriptorBindingFlags[1]){flags}, + }, + .bindingCount = 1, + .pBindings = + &(VkDescriptorSetLayoutBinding){ + .binding = 0, + .descriptorCount = 1, + .descriptorType = VK_DESCRIPTOR_TYPE_STORAGE_BUFFER, + .stageFlags = VK_SHADER_STAGE_VERTEX_BIT | + VK_SHADER_STAGE_FRAGMENT_BIT, + }, + }, + "Object Descriptor Set Layout", &sys.set_layout); + } + + // TODO: Must initialize render object GPU buffer + + // Pool just needs to point to one buffer + tb_create_dyn_desc_pool(rnd_sys, "Render Object Descriptor Pool", + sys.set_layout, VK_DESCRIPTOR_TYPE_STORAGE_BUFFER, 1, + &sys.desc_pool, 0); + + tb_reset_free_list(gp_alloc, &sys.free_list, TbMaxRenderObjects); + + return sys; +} + +VkDescriptorSet tb_render_object_sys_get_set(ecs_world_t *ecs) { + tb_auto rnd_sys = ecs_singleton_get_mut(ecs, TbRenderSystem); + tb_auto ctx = ecs_singleton_get_mut(ecs, TbRenderObjectSystem); + return tb_dyn_desc_pool_get_set(rnd_sys, &ctx->desc_pool); +} + +VkDescriptorSetLayout tb_render_object_sys_get_set_layout(ecs_world_t *ecs) { + tb_auto ctx = ecs_singleton_get_mut(ecs, TbRenderObjectSystem); + return ctx->set_layout; +} + +// Returns the binding info of the render object system's descriptor buffer +VkDescriptorBufferBindingInfoEXT +tb_render_object_sys_get_table_addr(ecs_world_t *ecs) { + (void)ecs; + // TODO: Remove + return (VkDescriptorBufferBindingInfoEXT){0}; +} + +void tb_mark_as_render_object(ecs_world_t *ecs, ecs_entity_t ent) { + tb_auto ctx = ecs_singleton_get_mut(ecs, TbRenderObjectSystem); + uint32_t idx = 0; + bool ok = tb_pull_index(&ctx->free_list, &idx); + TB_CHECK(ok, "Failed to retrieve index from free list"); + ecs_set(ecs, ent, TbRenderObject, + { + .index = idx, + }); + tb_render_object_mark_dirty(ecs, ent); +} + +void tb_render_object_mark_dirty(ecs_world_t *ecs, ecs_entity_t ent) { + if (ecs_has(ecs, ent, TbRenderObject)) { + ecs_add(ecs, ent, TbRenderObjectDirty); + } +} + +void tb_update_ro_pool(ecs_iter_t *it) { + TB_TRACY_SCOPE("Update Render Object Pool"); + tb_auto ctx = ecs_field(it, TbRenderObjectSystem, 1); + tb_auto rnd_sys = ecs_field(it, TbRenderSystem, 2); + + tb_tick_dyn_desc_pool(rnd_sys, &ctx->desc_pool); +} + +void tb_upload_transforms(ecs_iter_t *it) { + TB_TRACY_SCOPE("Upload Render Object Buffer"); + tb_auto ecs = it->world; + + tb_auto ctx = ecs_field(it, TbRenderObjectSystem, 1); + tb_auto rnd_sys = ecs_field(it, TbRenderSystem, 2); + + uint32_t dirty_count = 0; + ecs_iter_t dirty_it = ecs_query_iter(ecs, ctx->dirty_query); + while (ecs_query_next(&dirty_it)) { + dirty_count += dirty_it.count; + } + if (dirty_count == 0) { + return; + } + // Reset query + dirty_it = ecs_query_iter(ecs, ctx->dirty_query); + + // If we have any dirty objects just mark the whole buffer for upload + // We may want to do this in chunks instead + TbCommonObjectData *write_ptr = NULL; + tb_rnd_sys_update_gpu_buffer(rnd_sys, &ctx->trans_buffer.gpu, + &ctx->trans_buffer.host, (void **)&write_ptr); + while (ecs_query_next(&dirty_it)) { + tb_auto render_objects = ecs_field(&dirty_it, TbRenderObject, 1); + for (int32_t i = 0; i < dirty_it.count; ++i) { + tb_auto dst_idx = render_objects[i].index; + write_ptr[dst_idx].m = + tb_transform_get_world_matrix(dirty_it.world, dirty_it.entities[i]); + ecs_remove(dirty_it.world, dirty_it.entities[i], TbRenderObjectDirty); + } + } +} + +void tb_register_render_object_sys(TbWorld *world) { + TB_TRACY_SCOPE("Register Render Object System"); + ecs_world_t *ecs = world->ecs; + + ECS_COMPONENT_DEFINE(ecs, TbRenderObject); + ECS_COMPONENT_DEFINE(ecs, TbRenderObjectSystem); + ECS_TAG_DEFINE(ecs, TbRenderObjectDirty); + + // Metadata for TbRenderObject + ecs_struct(ecs, { + .entity = ecs_id(TbRenderObject), + .members = + { + {.name = "index", .type = ecs_id(ecs_u32_t)}, + }, + }); + + tb_auto rnd_sys = ecs_singleton_get_mut(ecs, TbRenderSystem); + tb_auto sys = + create_render_object_system(world->gp_alloc, world->tmp_alloc, rnd_sys); + + sys.dirty_query = ecs_query( + ecs, {.filter.terms = { + {.id = ecs_id(TbRenderObject), .inout = EcsIn}, + {.id = ecs_id(TbTransformComponent), .inout = EcsInOutNone}, + {.id = ecs_id(TbRenderObjectDirty), .inout = EcsInOutNone}, + }}); + + VkBufferCreateInfo create_info = { + .sType = VK_STRUCTURE_TYPE_BUFFER_CREATE_INFO, + .size = sizeof(TbCommonObjectData) * TbMaxRenderObjects, + .usage = VK_BUFFER_USAGE_TRANSFER_SRC_BIT | + VK_BUFFER_USAGE_TRANSFER_DST_BIT | + VK_BUFFER_USAGE_STORAGE_BUFFER_BIT | + VK_BUFFER_USAGE_SHADER_DEVICE_ADDRESS_BIT, + }; + void *write_ptr = NULL; + tb_rnd_sys_create_gpu_buffer(rnd_sys, &create_info, "TbTransform Buffer", + &sys.trans_buffer.gpu, &sys.trans_buffer.host, + (void **)&write_ptr); + (void)write_ptr; // Unused + + // Only need to write this once + { + // HACK: This leaks but we need it to survive + tb_auto write = tb_alloc_tp(rnd_sys->gp_alloc, TbDynDescWrite); + *write = (TbDynDescWrite){ + .type = VK_DESCRIPTOR_TYPE_STORAGE_BUFFER, + .desc.buffer = + (VkDescriptorBufferInfo){ + .offset = 0, + .buffer = sys.trans_buffer.gpu.buffer, + .range = sys.trans_buffer.gpu.info.size, + }, + }; + tb_write_dyn_desc_pool(&sys.desc_pool, 1, write, 0); + } + + // Sets a singleton based on the value at a pointer + ecs_set_ptr(ecs, ecs_id(TbRenderObjectSystem), TbRenderObjectSystem, &sys); + + ECS_SYSTEM(ecs, tb_update_ro_pool, + EcsPreStore, [in] TbRenderObjectSystem($), [in] TbRenderSystem($)); + ECS_SYSTEM(ecs, tb_upload_transforms, + EcsOnStore, [in] TbRenderObjectSystem($), [in] TbRenderSystem($)); +} + +void tb_unregister_render_object_sys(TbWorld *world) { + ecs_world_t *ecs = world->ecs; + tb_auto ctx = ecs_singleton_get_mut(ecs, TbRenderObjectSystem); + tb_auto rnd_sys = ecs_singleton_get_mut(ecs, TbRenderSystem); + + tb_rnd_destroy_set_layout(rnd_sys, ctx->set_layout); + tb_destroy_free_list(&ctx->free_list); + + tb_rnd_free_gpu_buffer(rnd_sys, &ctx->trans_buffer.gpu); + ctx->trans_buffer = (TbTransformsBuffer){0}; + + ecs_singleton_remove(ecs, TbRenderObjectSystem); +} diff --git a/source/renderpipelinesystem.c b/source/tb_render_pipeline_system.c similarity index 99% rename from source/renderpipelinesystem.c rename to source/tb_render_pipeline_system.c index 1787b699..12f99e6a 100644 --- a/source/renderpipelinesystem.c +++ b/source/tb_render_pipeline_system.c @@ -1,13 +1,14 @@ -#include "renderpipelinesystem.h" +#include "tb_render_pipeline_system.h" -#include "profiling.h" -#include "rendersystem.h" -#include "rendertargetsystem.h" -#include "shadercommon.h" +#include "tb_common.h" +#include "tb_profiling.h" +#include "tb_render_system.h" +#include "tb_render_target_system.h" +#include "tb_shader_common.h" #include "tb_shader_system.h" -#include "tbcommon.h" -#include "viewsystem.h" -#include "world.h" +#include "tb_texture_system.h" +#include "tb_view_system.h" +#include "tb_world.h" #include @@ -2602,34 +2603,35 @@ void destroy_render_pipeline_system(ecs_world_t *ecs, void tick_core_desc_pool(TbRenderPipelineSystem *self) { VkResult err = VK_SUCCESS; -#define SET_COUNT 5 + const uint32_t set_count = 5; VkDescriptorPoolCreateInfo pool_info = { .sType = VK_STRUCTURE_TYPE_DESCRIPTOR_POOL_CREATE_INFO, - .maxSets = SET_COUNT * 4, + .maxSets = set_count * 4, .poolSizeCount = 3, .pPoolSizes = (VkDescriptorPoolSize[3]){ { - .descriptorCount = SET_COUNT * 4, + .descriptorCount = set_count * 4, .type = VK_DESCRIPTOR_TYPE_SAMPLED_IMAGE, }, { - .descriptorCount = SET_COUNT * 4, + .descriptorCount = set_count * 4, .type = VK_DESCRIPTOR_TYPE_STORAGE_IMAGE, }, { - .descriptorCount = SET_COUNT * 4, + .descriptorCount = set_count * 4, .type = VK_DESCRIPTOR_TYPE_STORAGE_BUFFER, }, }, }; - VkDescriptorSetLayout layouts[SET_COUNT] = { + VkDescriptorSetLayout layouts[set_count] = { self->copy_set_layout, self->copy_set_layout, self->lum_hist_work.set_layout, self->lum_avg_work.set_layout, self->tonemap_set_layout, }; - err = tb_rnd_frame_desc_pool_tick(self->rnd_sys, &pool_info, layouts, NULL, - self->descriptor_pools, SET_COUNT); + err = tb_rnd_frame_desc_pool_tick( + self->rnd_sys, "render_pipeline", &pool_info, layouts, NULL, + self->descriptor_pools, set_count, set_count); TB_VK_CHECK(err, "Failed to tick descriptor pool"); #undef SET_COUNT @@ -2802,8 +2804,9 @@ void tick_downsample_desc_pool(TbRenderPipelineSystem *self) { layouts[i] = self->downsample_work.set_layout; } - err = tb_rnd_frame_desc_pool_tick(self->rnd_sys, &pool_info, layouts, NULL, - self->down_desc_pools, BLUR_BATCH_COUNT); + err = tb_rnd_frame_desc_pool_tick(self->rnd_sys, "downsample", &pool_info, + layouts, NULL, self->down_desc_pools, + BLUR_BATCH_COUNT, BLUR_BATCH_COUNT); TB_VK_CHECK(err, "Failed to tick descriptor pool"); #define WRITE_COUNT BLUR_BATCH_COUNT * 2 @@ -2894,8 +2897,9 @@ void tick_upsample_desc_pool(TbRenderPipelineSystem *self) { layouts[i] = self->upsample_work.set_layout; } - err = tb_rnd_frame_desc_pool_tick(self->rnd_sys, &pool_info, layouts, NULL, - self->up_desc_pools, BLUR_BATCH_COUNT); + err = tb_rnd_frame_desc_pool_tick(self->rnd_sys, "upsample", &pool_info, + layouts, NULL, self->up_desc_pools, + BLUR_BATCH_COUNT, BLUR_BATCH_COUNT); TB_VK_CHECK(err, "Failed to tick descriptor pool"); #define WRITE_COUNT BLUR_BATCH_COUNT * 2 @@ -2961,6 +2965,8 @@ void tick_render_pipeline_sys(ecs_iter_t *it) { tb_auto self = ecs_field(it, TbRenderPipelineSystem, 1); + tb_auto brdf_tex = tb_get_brdf_tex(it->world); + // A few passes will be driven from here because an external system // has no need to directly drive these passes @@ -3073,7 +3079,8 @@ void tick_render_pipeline_sys(ecs_iter_t *it) { } } // Brightness pass - if (tb_is_shader_ready(it->world, self->brightness_shader)) { + if (tb_is_shader_ready(it->world, self->brightness_shader) && + tb_is_texture_ready(it->world, brdf_tex)) { TbFullscreenBatch fs_batch = { .set = color_set, }; @@ -3174,7 +3181,7 @@ void rp_check_swapchain_resize(ecs_iter_t *it) { } // Clear out any in flight descriptor updates since this resize will // invalidate them - TB_DYN_ARR_CLEAR(frame_state->set_write_queue); + TB_QUEUE_CLEAR(*frame_state->set_write_queue); } tb_rnd_on_swapchain_resize(rp_sys); diff --git a/source/rendersystem.c b/source/tb_render_system.c similarity index 87% rename from source/rendersystem.c rename to source/tb_render_system.c index 96154910..483854c9 100644 --- a/source/rendersystem.c +++ b/source/tb_render_system.c @@ -1,15 +1,17 @@ -#include "rendersystem.h" +#include "tb_render_system.h" #include "mimalloc.h" -#include "profiling.h" -#include "renderthread.h" -#include "tbcommon.h" -#include "world.h" +#include "tb_common.h" +#include "tb_profiling.h" +#include "tb_render_thread.h" +#include "tb_world.h" #include ECS_COMPONENT_DECLARE(TbRenderSystem); +ECS_TAG_DECLARE(TbDescriptorReady); + void tb_register_render_sys(TbWorld *world); void tb_unregister_render_sys(TbWorld *world); @@ -103,6 +105,7 @@ TbRenderSystem create_render_system(TbAllocator gp_alloc, TbAllocator tmp_alloc, NULL, }; VmaAllocatorCreateInfo create_info = { + .flags = VMA_ALLOCATOR_CREATE_BUFFER_DEVICE_ADDRESS_BIT, .physicalDevice = thread->gpu, .device = thread->device, .pVulkanFunctions = &volk_functions, @@ -119,9 +122,10 @@ TbRenderSystem create_render_system(TbAllocator gp_alloc, TbAllocator tmp_alloc, for (uint32_t state_idx = 0; state_idx < TB_MAX_FRAME_STATES; ++state_idx) { TbRenderSystemFrameState *state = &sys.frame_states[state_idx]; - TB_DYN_ARR_RESET(state->set_write_queue, sys.gp_alloc, 1); - TB_DYN_ARR_RESET(state->buf_copy_queue, sys.gp_alloc, 1); - TB_DYN_ARR_RESET(state->buf_img_copy_queue, sys.gp_alloc, 1); + // Using global alloc because queues may be pushed to from task threads + TB_QUEUE_RESET(state->set_write_queue, tb_global_alloc, 1); + TB_QUEUE_RESET(state->buf_copy_queue, tb_global_alloc, 1); + TB_QUEUE_RESET(state->buf_img_copy_queue, tb_global_alloc, 1); // Allocate tmp host buffer { @@ -213,9 +217,9 @@ void destroy_render_system(TbRenderSystem *self) { vmaUnmapMemory(vma_alloc, state->tmp_host_buffer.alloc); vmaDestroyBuffer(vma_alloc, state->tmp_host_buffer.buffer, state->tmp_host_buffer.alloc); - TB_DYN_ARR_DESTROY(state->set_write_queue); - TB_DYN_ARR_DESTROY(state->buf_copy_queue); - TB_DYN_ARR_DESTROY(state->buf_img_copy_queue); + TB_QUEUE_DESTROY(state->set_write_queue); + TB_QUEUE_DESTROY(state->buf_copy_queue); + TB_QUEUE_DESTROY(state->buf_img_copy_queue); } // Clean up main thread owned memory that the render thread held the primary @@ -301,13 +305,10 @@ void render_frame_end(ecs_iter_t *it) { // Send and Reset buffer upload pool { - // Assign to the thread - thread_state->set_write_queue = state->set_write_queue; - thread_state->buf_copy_queue = state->buf_copy_queue; - thread_state->buf_img_copy_queue = state->buf_img_copy_queue; - TB_DYN_ARR_CLEAR(state->set_write_queue); - TB_DYN_ARR_CLEAR(state->buf_copy_queue); - TB_DYN_ARR_CLEAR(state->buf_img_copy_queue); + // Queue is thread safe so render thread just has a reference + thread_state->set_write_queue = &state->set_write_queue; + thread_state->buf_copy_queue = &state->buf_copy_queue; + thread_state->buf_img_copy_queue = &state->buf_img_copy_queue; } // Reset temp pool, the contents will still be intact for the render thread @@ -331,6 +332,7 @@ void tb_register_render_sys(TbWorld *world) { TracyCZoneN(ctx, "Register Render Sys", true); ecs_world_t *ecs = world->ecs; ECS_COMPONENT_DEFINE(ecs, TbRenderSystem); + ECS_TAG_DEFINE(ecs, TbDescriptorReady); TbRenderSystem sys = create_render_system(world->gp_alloc, world->tmp_alloc, world->render_thread); // Sets a singleton based on the value at a pointer @@ -340,6 +342,7 @@ void tb_register_render_sys(TbWorld *world) { TbRenderSystem(TbRenderSystem)); ECS_SYSTEM(ecs, render_frame_end, EcsPostFrame, TbRenderSystem(TbRenderSystem)); + TracyCZoneEnd(ctx); } @@ -426,8 +429,15 @@ VkResult tb_rnd_sys_alloc_gpu_buffer(TbRenderSystem *self, &buffer->buffer, &buffer->alloc, &buffer->info); TB_VK_CHECK_RET(err, "Failed to allocate gpu buffer", err); - SET_VK_NAME(self->render_thread->device, buffer->buffer, - VK_OBJECT_TYPE_BUFFER, name); + tb_auto device = self->render_thread->device; + + VkBufferDeviceAddressInfo addr_info = { + .sType = VK_STRUCTURE_TYPE_BUFFER_DEVICE_ADDRESS_INFO, + .buffer = buffer->buffer, + }; + buffer->address = vkGetBufferDeviceAddress(device, &addr_info); + + SET_VK_NAME(device, buffer->buffer, VK_OBJECT_TYPE_BUFFER, name); return err; } @@ -515,13 +525,6 @@ VkResult tb_rnd_sys_create_gpu_buffer_tmp(TbRenderSystem *self, // Create the GPU side buffer VkResult err = tb_rnd_sys_alloc_gpu_buffer(self, create_info, name, buffer); - // If this buffer is host visible, just get the pointer to write to - if (try_map(self->vma_alloc, buffer->alloc, ptr)) { - return err; - } - - // Otherwise we have to create write to the tmp buffer and schedule an - // upload TbHostBuffer host = {0}; err = alloc_tmp_buffer(self, create_info->size, alignment, &host); TB_VK_CHECK_RET(err, "Failed to alloc host buffer", err); @@ -541,6 +544,30 @@ VkResult tb_rnd_sys_create_gpu_buffer_tmp(TbRenderSystem *self, return err; } +VkResult tb_rnd_sys_create_gpu_buffer_noup( + TbRenderSystem *self, const VkBufferCreateInfo *create_info, + const char *name, TbBuffer *buffer, TbHostBuffer *host, void **ptr) { + // Create the GPU side buffer + VkResult err = tb_rnd_sys_alloc_gpu_buffer(self, create_info, name, buffer); + + // If this buffer is host visible, just get the pointer to write to + if (try_map(self->vma_alloc, buffer->alloc, ptr)) { + return err; + } + + // Otherwise we have to create a host buffer + + // It's the caller's responsibility to handle the host buffer too + // even if it's not going to be used on non-uma systems + err = alloc_host_buffer(self, create_info, name, host); + TB_VK_CHECK_RET(err, "Failed to alloc host buffer", err); + + // Assuming the caller will schedule an upload manually + + *ptr = host->info.pMappedData; + return err; +} + VkResult tb_rnd_sys_create_gpu_buffer2(TbRenderSystem *self, const VkBufferCreateInfo *create_info, const void *data, const char *name, @@ -668,6 +695,10 @@ VkBuffer tb_rnd_get_gpu_tmp_buffer(TbRenderSystem *self) { return self->render_thread->frame_states[self->frame_idx].tmp_gpu_buffer; } +VkDeviceAddress tb_rnd_get_gpu_tmp_addr(TbRenderSystem *self) { + return self->render_thread->frame_states[self->frame_idx].tmp_gpu_buf_addr; +} + VkResult tb_rnd_sys_update_gpu_buffer(TbRenderSystem *self, const TbBuffer *buffer, const TbHostBuffer *host, void **ptr) { @@ -694,6 +725,36 @@ VkResult tb_rnd_sys_update_gpu_buffer(TbRenderSystem *self, return err; } +VkResult tb_rnd_sys_update_gpu_buffer_tmp(TbRenderSystem *self, + const TbBuffer *buffer, void *data, + size_t size, size_t alignment) { + VkResult err = VK_SUCCESS; + if (buffer->info.size == 0) { + return err; + } + + TbHostBuffer host = {0}; + err = alloc_tmp_buffer(self, buffer->info.size, alignment, &host); + TB_VK_CHECK_RET(err, "Failed to alloc host buffer", err); + + // Schedule another upload + TbBufferCopy upload = { + .src = host.buffer, + .dst = buffer->buffer, + .region = + { + .srcOffset = host.offset, + .size = buffer->info.size, + }, + }; + tb_rnd_upload_buffers(self, &upload, 1); + + void *ptr = host.info.pMappedData; + SDL_memcpy(ptr, data, size); + tb_flush_alloc(self, buffer->alloc); + return err; +} + VkResult tb_rnd_create_sampler(TbRenderSystem *self, const VkSamplerCreateInfo *create_info, const char *name, VkSampler *sampler) { @@ -810,11 +871,8 @@ VkResult tb_rnd_create_graphics_pipelines( void tb_rnd_upload_buffers(TbRenderSystem *self, TbBufferCopy *uploads, uint32_t upload_count) { TbRenderSystemFrameState *state = &self->frame_states[self->frame_idx]; - uint32_t head = TB_DYN_ARR_SIZE(state->buf_copy_queue); - TB_DYN_ARR_RESIZE(state->buf_copy_queue, head + upload_count); - // Append uploads to queue for (uint32_t i = 0; i < upload_count; ++i) { - TB_DYN_ARR_AT(state->buf_copy_queue, head + i) = uploads[i]; + TB_QUEUE_PUSH(state->buf_copy_queue, uploads[i]) } } @@ -822,11 +880,8 @@ void tb_rnd_upload_buffer_to_image(TbRenderSystem *self, TbBufferImageCopy *uploads, uint32_t upload_count) { TbRenderSystemFrameState *state = &self->frame_states[self->frame_idx]; - uint32_t head = TB_DYN_ARR_SIZE(state->buf_img_copy_queue); - TB_DYN_ARR_RESIZE(state->buf_img_copy_queue, head + upload_count); - // Append uploads to queue for (uint32_t i = 0; i < upload_count; ++i) { - TB_DYN_ARR_AT(state->buf_img_copy_queue, head + i) = uploads[i]; + TB_QUEUE_PUSH(state->buf_img_copy_queue, uploads[i]) } } @@ -879,9 +934,7 @@ void tb_rnd_destroy_descriptor_pool(TbRenderSystem *self, void tb_rnd_update_descriptors(TbRenderSystem *self, uint32_t write_count, const VkWriteDescriptorSet *writes) { // Queue these writes to be processed by the render thread - TbRenderSystemFrameState *state = &self->frame_states[self->frame_idx]; - uint32_t head = TB_DYN_ARR_SIZE(state->set_write_queue); - TB_DYN_ARR_RESIZE(state->set_write_queue, head + write_count); + tb_auto state = &self->frame_states[self->frame_idx]; // We know the frame state isn't in use so it's safe to grab its temp // allocator and make sure that descriptor info is properly copied @@ -890,49 +943,75 @@ void tb_rnd_update_descriptors(TbRenderSystem *self, uint32_t write_count, // Append uploads to queue for (uint32_t i = 0; i < write_count; ++i) { - tb_auto write = &TB_DYN_ARR_AT(state->set_write_queue, head + i); - *write = writes[i]; + VkWriteDescriptorSet write = writes[i]; - const tb_auto desc_count = write->descriptorCount; - if (write->pBufferInfo != NULL) { + const tb_auto desc_count = write.descriptorCount; + if (write.pBufferInfo != NULL) { tb_auto info = tb_alloc_nm_tp(rt_state_tmp_alloc, desc_count, VkDescriptorBufferInfo); - SDL_memcpy(info, write->pBufferInfo, + SDL_memcpy(info, write.pBufferInfo, sizeof(VkDescriptorBufferInfo) * desc_count); - write->pBufferInfo = info; + write.pBufferInfo = info; } - if (write->pImageInfo != NULL) { + if (write.pImageInfo != NULL) { tb_auto info = tb_alloc_nm_tp(rt_state_tmp_alloc, desc_count, VkDescriptorImageInfo); - SDL_memcpy(info, write->pImageInfo, + SDL_memcpy(info, write.pImageInfo, sizeof(VkDescriptorImageInfo) * desc_count); - write->pImageInfo = info; + write.pImageInfo = info; } - if (write->pTexelBufferView != NULL) { + if (write.pTexelBufferView != NULL) { tb_auto info = tb_alloc_nm_tp(rt_state_tmp_alloc, desc_count, VkBufferView); - SDL_memcpy(info, write->pTexelBufferView, + SDL_memcpy(info, write.pTexelBufferView, sizeof(VkBufferView) * desc_count); - write->pTexelBufferView = info; + write.pTexelBufferView = info; } + + TB_QUEUE_PUSH(state->set_write_queue, write); } } -VkResult tb_rnd_frame_desc_pool_tick( - TbRenderSystem *self, const VkDescriptorPoolCreateInfo *pool_info, - const VkDescriptorSetLayout *layouts, void *alloc_next, - TbFrameDescriptorPool *pools, uint32_t set_count) { +VkResult tb_rnd_alloc_descriptor_sets(TbRenderSystem *self, const char *name, + const VkDescriptorSetAllocateInfo *info, + VkDescriptorSet *sets) { + VkResult err = + vkAllocateDescriptorSets(self->render_thread->device, info, sets); + TB_VK_CHECK(err, "Failed to allocate descriptor sets"); + + const uint32_t MAX_SET_NAME_LEN = 256; + char set_name[MAX_SET_NAME_LEN] = {0}; + for (uint32_t i = 0; i < info->descriptorSetCount; ++i) { + SDL_snprintf(set_name, MAX_SET_NAME_LEN, "%s_set%d", name, i); + SET_VK_NAME(self->render_thread->device, sets[i], + VK_OBJECT_TYPE_DESCRIPTOR_SET, set_name); + } + + return err; +} + +VkResult +tb_rnd_frame_desc_pool_tick(TbRenderSystem *self, const char *name, + const VkDescriptorPoolCreateInfo *pool_info, + const VkDescriptorSetLayout *layouts, + void *alloc_next, TbFrameDescriptorPool *pools, + uint32_t set_count, uint32_t desc_count) { VkResult err = VK_SUCCESS; TbFrameDescriptorPool *pool = &pools[self->frame_idx]; + // If descriptor count hasn't changed don't do anything + if (pool->desc_count == desc_count) { + return err; + } + pool->desc_count = desc_count; + // Resize the pool if (pool->set_count < set_count) { if (pool->set_pool) { tb_rnd_destroy_descriptor_pool(self, pool->set_pool); } - err = - tb_rnd_create_descriptor_pool(self, pool_info, "Pool", &pool->set_pool); + err = tb_rnd_create_descriptor_pool(self, pool_info, name, &pool->set_pool); TB_VK_CHECK(err, "Failed to create pool"); pool->set_count = set_count; pool->sets = tb_realloc_nm_tp(self->gp_alloc, pool->sets, pool->set_count, @@ -949,8 +1028,7 @@ VkResult tb_rnd_frame_desc_pool_tick( .descriptorPool = pool->set_pool, .pSetLayouts = layouts, }; - err = vkAllocateDescriptorSets(self->render_thread->device, &alloc_info, - pool->sets); + err = tb_rnd_alloc_descriptor_sets(self, name, &alloc_info, pool->sets); TB_VK_CHECK(err, "Failed to re-allocate descriptor sets"); return err; @@ -962,6 +1040,11 @@ VkDescriptorSet tb_rnd_frame_desc_pool_get_set(TbRenderSystem *self, return pools[self->frame_idx].sets[set_idx]; } +uint32_t tb_rnd_frame_desc_pool_get_desc_count(TbRenderSystem *self, + TbFrameDescriptorPool *pools) { + return pools[self->frame_idx].desc_count; +} + VkResult tb_rnd_resize_desc_pool(TbRenderSystem *self, const VkDescriptorPoolCreateInfo *pool_info, const VkDescriptorSetLayout *layouts, diff --git a/source/rendertargetsystem.c b/source/tb_render_target_system.c similarity index 99% rename from source/rendertargetsystem.c rename to source/tb_render_target_system.c index 13e566c6..53038575 100644 --- a/source/rendertargetsystem.c +++ b/source/tb_render_target_system.c @@ -1,10 +1,10 @@ -#include "rendertargetsystem.h" +#include "tb_render_target_system.h" -#include "bloom.h" #include "common.hlsli" -#include "rendersystem.h" -#include "tbcommon.h" -#include "world.h" +#include "tb_bloom.h" +#include "tb_common.h" +#include "tb_render_system.h" +#include "tb_world.h" #include diff --git a/source/renderthread.c b/source/tb_render_thread.c similarity index 94% rename from source/renderthread.c rename to source/tb_render_thread.c index 5a9f262a..de18a8de 100644 --- a/source/renderthread.c +++ b/source/tb_render_thread.c @@ -1,19 +1,19 @@ -#include "renderthread.h" +#include "tb_render_thread.h" #include "mimalloc.h" #include -#include "allocator.h" -#include "profiling.h" +#include "tb_allocator.h" +#include "tb_profiling.h" -#include "tbcommon.h" -#include "tbengineconfig.h" -#include "tblog.h" -#include "tbsdl.h" -#include "tbvk.h" -#include "tbvkalloc.h" -#include "tbvma.h" -#include "vkdbg.h" +#include "tb_common.h" +#include "tb_engine_config.h" +#include "tb_log.h" +#include "tb_sdl.h" +#include "tb_vk.h" +#include "tb_vk_alloc.h" +#include "tb_vk_dbg.h" +#include "tb_vma.h" // we know that rendersystem.c defines this extern bool try_map(VmaAllocator vma, VmaAllocation alloc, void **ptr); @@ -142,7 +142,6 @@ vk_debug_callback(VkDebugUtilsMessageSeverityFlagBitsEXT messageSeverity, (void)pUserData; (void)pCallbackData; - // Can't use TB_LOG_* here because the internal allocator is not thread safe if (messageSeverity & VK_DEBUG_UTILS_MESSAGE_SEVERITY_VERBOSE_BIT_EXT) { TB_LOG_VERBOSE(SDL_LOG_CATEGORY_RENDER, "%s", pCallbackData->pMessage); } else if (messageSeverity & VK_DEBUG_UTILS_MESSAGE_SEVERITY_INFO_BIT_EXT) { @@ -158,7 +157,7 @@ vk_debug_callback(VkDebugUtilsMessageSeverityFlagBitsEXT messageSeverity, // Helper for breaking when encountering a non-info message if (messageSeverity > VK_DEBUG_UTILS_MESSAGE_SEVERITY_INFO_BIT_EXT) { - // SDL_TriggerBreakpoint(); + SDL_TriggerBreakpoint(); } return false; @@ -306,11 +305,11 @@ VkResult create_semaphore(VkDevice device, } bool init_frame_states(VkPhysicalDevice gpu, VkDevice device, - const TbSwapchain *swapchain, VkQueue graphics_queue, + const TbSwapchain *swapchain, uint32_t graphics_queue_family_index, VmaAllocator vma_alloc, const VkAllocationCallbacks *vk_alloc, - TbFrameState *states) { + TbAllocator gp_alloc, TbFrameState *states) { TracyCZoneN(ctx, "Initialize Frame States", true); TB_CHECK_RETURN(states, "Invalid states", false); VkResult err = VK_SUCCESS; @@ -337,6 +336,7 @@ bool init_frame_states(VkPhysicalDevice gpu, VkDevice device, for (uint32_t i = 0; i < TB_MAX_FRAME_STATES; ++i) { TbFrameState *state = &states[i]; + state->gp_alloc = gp_alloc; tb_create_arena_alloc("Render Thread Frame State Tmp Alloc", &state->tmp_alloc, 128 * 1024 * 1024); @@ -407,8 +407,8 @@ bool init_frame_states(VkPhysicalDevice gpu, VkDevice device, } { - TracyCGPUContext *gpu_ctx = TracyCVkContextExt( - gpu, device, graphics_queue, state->base_command_buffers[0], + TracyCGPUContext *gpu_ctx = TracyCVkContextHostCalib( + gpu, device, vkResetQueryPool, vkGetPhysicalDeviceCalibrateableTimeDomainsEXT, vkGetCalibratedTimestampsEXT); const char *name = "Frame State GPU Context"; @@ -426,7 +426,8 @@ bool init_frame_states(VkPhysicalDevice gpu, VkDevice device, VK_BUFFER_USAGE_STORAGE_BUFFER_BIT | VK_BUFFER_USAGE_INDEX_BUFFER_BIT | VK_BUFFER_USAGE_VERTEX_BUFFER_BIT | - VK_BUFFER_USAGE_INDIRECT_BUFFER_BIT, + VK_BUFFER_USAGE_INDIRECT_BUFFER_BIT | + VK_BUFFER_USAGE_SHADER_DEVICE_ADDRESS_BIT, }; VmaAllocationCreateInfo alloc_create_info = { .usage = VMA_MEMORY_USAGE_AUTO, @@ -438,6 +439,13 @@ bool init_frame_states(VkPhysicalDevice gpu, VkDevice device, err = vmaCreateBuffer(vma_alloc, &create_info, &alloc_create_info, &state->tmp_gpu_buffer, &state->tmp_gpu_alloc, &alloc_info); + + VkBufferDeviceAddressInfo addr_info = { + .sType = VK_STRUCTURE_TYPE_BUFFER_DEVICE_ADDRESS_INFO, + .buffer = state->tmp_gpu_buffer, + }; + state->tmp_gpu_buf_addr = vkGetBufferDeviceAddress(device, &addr_info); + TB_VK_CHECK_RET(err, "Failed to create vma temp gpu buffer", false); SET_VK_NAME(device, state->tmp_gpu_buffer, VK_OBJECT_TYPE_BUFFER, "Vulkan Tmp GPU Buffer"); @@ -494,6 +502,7 @@ void destroy_frame_states(VkDevice device, VmaAllocator vma_alloc, bool init_gpu(VkInstance instance, TbAllocator gp_alloc, TbAllocator tmp_alloc, VkPhysicalDevice *gpu, VkPhysicalDeviceProperties2 *gpu_props, VkPhysicalDeviceDriverProperties *driver_props, + VkPhysicalDeviceDescriptorBufferPropertiesEXT *desc_buf_props, uint32_t *queue_family_count, VkQueueFamilyProperties **queue_props, VkPhysicalDeviceFeatures *gpu_features, @@ -550,11 +559,18 @@ bool init_gpu(VkInstance instance, TbAllocator gp_alloc, TbAllocator tmp_alloc, *gpu = gpus[gpu_idx]; // Check physical device properties + *desc_buf_props = (VkPhysicalDeviceDescriptorBufferPropertiesEXT){ + .sType = + VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_DESCRIPTOR_BUFFER_PROPERTIES_EXT, + }; *driver_props = (VkPhysicalDeviceDriverProperties){ - .sType = VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_DRIVER_PROPERTIES}; + .sType = VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_DRIVER_PROPERTIES, + .pNext = desc_buf_props, + }; *gpu_props = (VkPhysicalDeviceProperties2){ .sType = VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_PROPERTIES_2, - .pNext = driver_props}; + .pNext = driver_props, + }; vkGetPhysicalDeviceProperties2(*gpu, gpu_props); vkGetPhysicalDeviceQueueFamilyProperties(*gpu, queue_family_count, NULL); @@ -732,9 +748,16 @@ bool init_device(VkPhysicalDevice gpu, uint32_t graphics_queue_family_index, required_device_ext((const char **)&device_ext_names, &device_ext_count, props, prop_count, VK_KHR_SPIRV_1_4_EXTENSION_NAME); - // Mesh Shader support - // required_device_ext((const char **)&device_ext_names, &device_ext_count, - // props, prop_count, VK_EXT_MESH_SHADER_EXTENSION_NAME); +// Mesh Shader support +// required_device_ext((const char **)&device_ext_names, &device_ext_count, +// props, prop_count, VK_EXT_MESH_SHADER_EXTENSION_NAME); + +// We want to use descriptor buffers +#if TB_USE_DESC_BUFFER == 1 + required_device_ext((const char **)&device_ext_names, &device_ext_count, + props, prop_count, + VK_EXT_DESCRIPTOR_BUFFER_EXTENSION_NAME); +#endif #ifdef TRACY_ENABLE // Enable calibrated timestamps if we can when profiling with tracy @@ -746,8 +769,19 @@ bool init_device(VkPhysicalDevice gpu, uint32_t graphics_queue_family_index, #endif } +#if TB_USE_DESC_BUFFER == 1 + VkPhysicalDeviceDescriptorBufferFeaturesEXT vk_desc_buf_features = { + .sType = VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_DESCRIPTOR_BUFFER_FEATURES_EXT, + .pNext = NULL, + .descriptorBuffer = VK_TRUE, + }; +#endif + VkPhysicalDeviceRobustness2FeaturesEXT vk_rob2_features = { .sType = VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_ROBUSTNESS_2_FEATURES_EXT, +#if TB_USE_DESC_BUFFER == 1 + .pNext = &vk_desc_buf_features, +#endif .nullDescriptor = VK_TRUE, }; @@ -762,6 +796,8 @@ bool init_device(VkPhysicalDevice gpu, uint32_t graphics_queue_family_index, VkPhysicalDeviceVulkan12Features vk_12_features = { .sType = VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_VULKAN_1_2_FEATURES, .pNext = &vk_13_features, + .bufferDeviceAddress = VK_TRUE, + .hostQueryReset = VK_TRUE, .shaderFloat16 = VK_TRUE, .descriptorIndexing = VK_TRUE, .descriptorBindingVariableDescriptorCount = VK_TRUE, @@ -895,6 +931,7 @@ bool init_vma(VkInstance instance, VkPhysicalDevice gpu, VkDevice device, NULL, }; VmaAllocatorCreateInfo create_info = { + .flags = VMA_ALLOCATOR_CREATE_BUFFER_DEVICE_ADDRESS_BIT, .physicalDevice = gpu, .device = device, .pVulkanFunctions = &volk_functions, @@ -1108,8 +1145,9 @@ bool init_render_thread(TbRenderThread *thread) { TB_CHECK_RETURN(init_gpu(thread->instance, gp_alloc, tmp_alloc, &thread->gpu, &thread->gpu_props, &thread->driver_props, - &thread->queue_family_count, &thread->queue_props, - &thread->gpu_features, &thread->gpu_mem_props), + &thread->desc_buf_props, &thread->queue_family_count, + &thread->queue_props, &thread->gpu_features, + &thread->gpu_mem_props), "Failed to select gpu", false) TB_CHECK_RETURN(SDL_Vulkan_CreateSurface(thread->window, thread->instance, @@ -1143,12 +1181,11 @@ bool init_render_thread(TbRenderThread *thread) { &thread->swapchain), "Failed to init swapchain", false); - TB_CHECK_RETURN(init_frame_states(thread->gpu, thread->device, - &thread->swapchain, thread->graphics_queue, - thread->graphics_queue_family_index, - thread->vma_alloc, vk_alloc, - thread->frame_states), - "Failed to init frame states", false); + TB_CHECK_RETURN( + init_frame_states(thread->gpu, thread->device, &thread->swapchain, + thread->graphics_queue_family_index, thread->vma_alloc, + vk_alloc, gp_alloc, thread->frame_states), + "Failed to init frame states", false); TracyCZoneEnd(ctx); return true; @@ -1239,7 +1276,16 @@ void tick_render_thread(TbRenderThread *thread, TbFrameState *state) { { TracyCZoneN(fence_ctx, "Wait for GPU", true); TracyCZoneColor(fence_ctx, TracyCategoryColorWait); - vkWaitForFences(thread->device, 1, &fence, VK_TRUE, SDL_MAX_UINT64); + VkResult res = VK_TIMEOUT; + while (res != VK_SUCCESS) { + if (res == VK_TIMEOUT) { + res = + vkWaitForFences(thread->device, 1, &fence, VK_TRUE, SDL_MAX_UINT64); + } else { + TB_CHECK(false, "Error waiting for fence"); + } + } + TracyCZoneEnd(fence_ctx); vkResetFences(device, 1, &fence); @@ -1288,9 +1334,23 @@ void tick_render_thread(TbRenderThread *thread, TbFrameState *state) { // Write descriptor set updates at the top of the frame here { - uint32_t write_count = TB_DYN_ARR_SIZE(state->set_write_queue); - VkWriteDescriptorSet *writes = state->set_write_queue.data; - vkUpdateDescriptorSets(device, write_count, writes, 0, NULL); + TracyCZoneN(desc_ctx, "Update Descriptors", true); + TB_DYN_ARR_OF(VkWriteDescriptorSet) writes = {0}; + // We HAVE to use the gp alloc here. The size of the set_write_queue is + // unknown so we will have to resize the array + TB_DYN_ARR_RESET(writes, state->gp_alloc, 16); + + VkWriteDescriptorSet write = {0}; + while (TB_QUEUE_POP(*state->set_write_queue, &write)) { + TB_DYN_ARR_APPEND(writes, write); + } + uint32_t write_count = TB_DYN_ARR_SIZE(writes); + + vkUpdateDescriptorSets(device, write_count, writes.data, 0, NULL); + + // Must clean up array + TB_DYN_ARR_DESTROY(writes); + TracyCZoneEnd(desc_ctx); } // Draw @@ -1316,32 +1376,29 @@ void tick_render_thread(TbRenderThread *thread, TbFrameState *state) { TracyCVkNamedZone(gpu_ctx, upload_scope, start_buffer, "Upload", 1, true); // Upload all buffer requests - if (TB_DYN_ARR_SIZE(state->buf_copy_queue) > 0) { - TB_DYN_ARR_FOREACH(state->buf_copy_queue, i) { - const TbBufferCopy *up = &TB_DYN_ARR_AT(state->buf_copy_queue, i); - vkCmdCopyBuffer(start_buffer, up->src, up->dst, 1, &up->region); + { + TbBufferCopy up = {0}; + while (TB_QUEUE_POP(*state->buf_copy_queue, &up)) { + vkCmdCopyBuffer(start_buffer, up.src, up.dst, 1, &up.region); } - TB_DYN_ARR_CLEAR(state->buf_copy_queue); } // Upload all buffer to image requests - if (TB_DYN_ARR_SIZE(state->buf_img_copy_queue) > 0) { - TB_DYN_ARR_FOREACH(state->buf_img_copy_queue, i) { - const TbBufferImageCopy *up = - &TB_DYN_ARR_AT(state->buf_img_copy_queue, i); - + { + TbBufferImageCopy up = {0}; + while (TB_QUEUE_POP(*state->buf_img_copy_queue, &up)) { // Issue an upload command only if the src buffer exists // If it doesn't, assume that we want to only do a transition but // no copy - if (up->src != VK_NULL_HANDLE) { + if (up.src != VK_NULL_HANDLE) { VkImageMemoryBarrier barrier = { .sType = VK_STRUCTURE_TYPE_IMAGE_MEMORY_BARRIER, - .subresourceRange = up->range, + .subresourceRange = up.range, .srcAccessMask = 0, .dstAccessMask = VK_ACCESS_TRANSFER_WRITE_BIT, .oldLayout = VK_IMAGE_LAYOUT_UNDEFINED, .newLayout = VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL, - .image = up->dst, + .image = up.dst, }; vkCmdPipelineBarrier(start_buffer, VK_PIPELINE_STAGE_TOP_OF_PIPE_BIT, @@ -1349,29 +1406,27 @@ void tick_render_thread(TbRenderThread *thread, TbFrameState *state) { 0, NULL, 1, &barrier); // Perform the copy - vkCmdCopyBufferToImage(start_buffer, up->src, up->dst, + vkCmdCopyBufferToImage(start_buffer, up.src, up.dst, VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL, 1, - &up->region); + &up.region); } // Transition to readable layout { VkImageMemoryBarrier barrier = { .sType = VK_STRUCTURE_TYPE_IMAGE_MEMORY_BARRIER, - .subresourceRange = up->range, + .subresourceRange = up.range, .srcAccessMask = 0, .dstAccessMask = VK_ACCESS_SHADER_READ_BIT, .oldLayout = VK_IMAGE_LAYOUT_UNDEFINED, .newLayout = VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL, - .image = up->dst, + .image = up.dst, }; vkCmdPipelineBarrier(start_buffer, VK_PIPELINE_STAGE_TRANSFER_BIT, VK_PIPELINE_STAGE_FRAGMENT_SHADER_BIT, 0, 0, NULL, 0, NULL, 1, &barrier); } } - - TB_DYN_ARR_CLEAR(state->buf_img_copy_queue); } TracyCVkZoneEnd(upload_scope); @@ -1442,8 +1497,8 @@ void tick_render_thread(TbRenderThread *thread, TbFrameState *state) { queue_begin_label(graphics_queue, "Upload", (float4){1.0f, 0.1f, 0.1f, 1.0f}); err = vkQueueSubmit(graphics_queue, 1, &submit_info, VK_NULL_HANDLE); - queue_end_label(graphics_queue); TB_VK_CHECK(err, "Failed to submit upload work"); + queue_end_label(graphics_queue); } TracyCZoneEnd(submit_ctx); @@ -1629,8 +1684,8 @@ void tick_render_thread(TbRenderThread *thread, TbFrameState *state) { queue_begin_label(graphics_queue, "Finalize", (float4){1.0f, 0.1f, 0.1f, 1.0f}); err = vkQueueSubmit(graphics_queue, 1, &submit_info, state->fence); - queue_end_label(graphics_queue); TB_VK_CHECK(err, "Failed to submit ending work"); + queue_end_label(graphics_queue); } TracyCZoneEnd(submit_ctx); diff --git a/source/rigidbodycomponent.cpp b/source/tb_rigidbody_component.cpp similarity index 93% rename from source/rigidbodycomponent.cpp rename to source/tb_rigidbody_component.cpp index 6ecc6fe9..fc5e1b31 100644 --- a/source/rigidbodycomponent.cpp +++ b/source/tb_rigidbody_component.cpp @@ -1,12 +1,13 @@ -#include "rigidbodycomponent.h" +#include "tb_rigidbody_component.h" -#include "meshsystem.h" -#include "physicssystem.hpp" -#include "simd.h" -#include "tbcommon.h" -#include "tbgltf.h" -#include "transformcomponent.h" -#include "world.h" +#include "tb_common.h" +#include "tb_gltf.h" +#include "tb_mesh_rnd_sys.h" +#include "tb_mesh_system.h" +#include "tb_physics_system.hpp" +#include "tb_simd.h" +#include "tb_transform_component.h" +#include "tb_world.h" #include #include @@ -21,15 +22,11 @@ #include #include -#include "physlayers.h" +#include "tb_phys_layers.h" #include #include -// From meshsystem.c -extern "C" cgltf_result decompress_buffer_view(TbAllocator alloc, - cgltf_buffer_view *view); - ECS_COMPONENT_DECLARE(TbPhysLayer); ECS_COMPONENT_DECLARE(TbPhysMotionType); ECS_COMPONENT_DECLARE(TbShapeType); @@ -82,7 +79,7 @@ JPH::ShapeRefC create_mesh_shape(TbAllocator gp_alloc, const cgltf_node *node, auto *indices = primitive.indices; auto *view = indices->buffer_view; - auto res = decompress_buffer_view(gp_alloc, view); + auto res = tb_decompress_buffer_view(gp_alloc, view); TB_CHECK(res == cgltf_result_success, "Failed to decode buffer view"); auto stride = indices->stride; @@ -121,7 +118,7 @@ JPH::ShapeRefC create_mesh_shape(TbAllocator gp_alloc, const cgltf_node *node, auto &positions = primitive.attributes[pos_idx].data; auto *view = positions->buffer_view; - auto res = decompress_buffer_view(gp_alloc, view); + auto res = tb_decompress_buffer_view(gp_alloc, view); TB_CHECK(res == cgltf_result_success, "Failed to decode buffer view"); auto vert_count = positions->count; @@ -169,7 +166,7 @@ JPH::ShapeRefC create_convex_hull_shape(TbAllocator gp_alloc, auto *indices = primitive.indices; auto *view = indices->buffer_view; - auto res = decompress_buffer_view(gp_alloc, view); + auto res = tb_decompress_buffer_view(gp_alloc, view); TB_CHECK(res == cgltf_result_success, "Failed to decode buffer view"); auto stride = indices->stride; @@ -208,7 +205,7 @@ JPH::ShapeRefC create_convex_hull_shape(TbAllocator gp_alloc, auto &positions = primitive.attributes[pos_idx].data; auto *view = positions->buffer_view; - auto res = decompress_buffer_view(gp_alloc, view); + auto res = tb_decompress_buffer_view(gp_alloc, view); TB_CHECK(res == cgltf_result_success, "Failed to decode buffer view"); auto vert_count = positions->count; @@ -275,7 +272,7 @@ void tb_on_rigidbody_removed(flecs::entity ent, TbRigidbodyComponent &rb) { bodies.RemoveBody(body); } -ecs_entity_t tb_register_rigidbody_comp(TbWorld *world) { +TbComponentRegisterResult tb_register_rigidbody_comp(TbWorld *world) { flecs::world ecs(world->ecs); // Is there a better way to avoid having to use this macro in C++? @@ -352,14 +349,17 @@ ecs_entity_t tb_register_rigidbody_comp(TbWorld *world) { }}); #pragma clang diagnostic pop - return ecs_id(TbRigidbodyDescriptor); + return TbComponentRegisterResult{ecs_id(TbRigidbodyComponent), + ecs_id(TbRigidbodyDescriptor)}; } -bool tb_load_rigidbody_comp(TbWorld *world, ecs_entity_t ent, - const char *source_path, const cgltf_node *node, - json_object *object) { +bool tb_load_rigidbody_comp(ecs_world_t *_ecs, ecs_entity_t ent, + const char *source_path, const cgltf_data *data, + const cgltf_node *node, json_object *json) { + ZoneScopedN("Load Rigidbody Component"); (void)source_path; - flecs::world ecs(world->ecs); + (void)data; + flecs::world ecs(_ecs); bool sensor = false; JPH::EShapeSubType shape_type = (JPH::EShapeSubType)-1; @@ -370,12 +370,13 @@ bool tb_load_rigidbody_comp(TbWorld *world, ecs_entity_t ent, float radius = 0; JPH::EAllowedDOFs allowed_dofs = JPH::EAllowedDOFs::All; - auto shape_trans = tb_transform_from_node(node); - const float3 shape_scale = shape_trans.scale; + // const auto shape_trans = tb_transform_from_node(node); + // const float3 shape_scale = shape_trans.scale; + const float3 shape_scale = tb_f3(1, 1, 1); const float max_scale = SDL_max(shape_scale.x, SDL_max(shape_scale.y, shape_scale.z)); - json_object_object_foreach(object, key, value) { + json_object_object_foreach(json, key, value) { // Parse Shape type if (SDL_strcmp(key, "shape_type") == 0) { const char *shape_type_str = json_object_get_string(value); @@ -526,5 +527,10 @@ bool tb_load_rigidbody_comp(TbWorld *world, ecs_entity_t ent, return true; } +bool tb_ready_rigidbody_comp(ecs_world_t *ecs, ecs_entity_t ent) { + auto comp = ecs_get(ecs, ent, TbRigidbodyComponent); + return comp != nullptr; +} + TB_REGISTER_COMP(tb, rigidbody); } \ No newline at end of file diff --git a/source/tb_scene.c b/source/tb_scene.c new file mode 100644 index 00000000..21e26cd8 --- /dev/null +++ b/source/tb_scene.c @@ -0,0 +1,620 @@ +#include "tb_scene.h" + +#include "tb_allocator.h" +#include "tb_assets.h" +#include "tb_common.h" +#include "tb_gltf.h" +#include "tb_material_system.h" +#include "tb_mesh_system.h" +#include "tb_profiling.h" +#include "tb_render_object_system.h" +#include "tb_task_scheduler.h" +#include "tb_texture_system.h" +#include "tb_transform_component.h" +#include "tb_world.h" + +#include +#include + +static const uint32_t TbMaxPerFrameParentResolutions = 16; +static const uint32_t TbMaxPerFrameComponentReadyChecks = 64; +static const uint32_t TbMaxPerFrameEntityReadyChecks = 64; + +static uint32_t tb_parent_resolution_count = 0; +static uint32_t tb_component_ready_check_count = 0; +static uint32_t tb_entity_ready_check_count = 0; + +typedef struct TbSceneCtx { + ecs_query_t *parent_resolve_query; + ecs_query_t *comp_ready_query; + ecs_query_t *ent_ready_query; +} TbSceneCtx; +ECS_COMPONENT_DECLARE(TbSceneCtx); + +typedef struct TbEntityLoadRequest { + ecs_world_t *ecs; + const char *source_path; + const cgltf_data *data; + const cgltf_node *node; + json_object *json; +} TbEntityLoadRequest; +typedef TB_QUEUE_OF(TbEntityLoadRequest) TbEntityTaskQueue; + +ECS_COMPONENT_DECLARE(TbEntityTaskQueue); + +typedef const struct cgltf_node *TbNode; +ECS_COMPONENT_DECLARE(TbNode); +ECS_TAG_DECLARE(TbParentRequest); + +typedef uint32_t TbSceneEntityCount; +ECS_COMPONENT_DECLARE(TbSceneEntityCount); +typedef uint32_t TbSceneEntParseCounter; +ECS_COMPONENT_DECLARE(TbSceneEntParseCounter); +typedef uint32_t TbSceneEntReadyCounter; +ECS_COMPONENT_DECLARE(TbSceneEntReadyCounter); + +typedef TbScene TbSceneRef; +ECS_COMPONENT_DECLARE(TbSceneRef); + +ECS_TAG_DECLARE(TbSceneRoot); +ECS_TAG_DECLARE(TbSceneParsing); +ECS_TAG_DECLARE(TbSceneParsed); +ECS_TAG_DECLARE(TbSceneLoading); +ECS_TAG_DECLARE(TbSceneLoaded); +ECS_TAG_DECLARE(TbSceneReady); +ECS_TAG_DECLARE(TbComponentsReady); +ECS_TAG_DECLARE(TbEntityReady); + +typedef struct TbSceneParsedArgs { + ecs_world_t *ecs; + TbScene scene; + uint32_t local_parent; + const char *path; + TbEntityTaskQueue *queue; +} TbSceneParsedArgs; + +void tb_scene_parsed(const void *args) { + TracyCZoneN(ctx, "Scene Parsed", true); + tb_auto load_args = (const TbSceneParsedArgs *)args; + tb_auto scene = load_args->scene; + tb_auto ecs = load_args->ecs; + tb_auto queue = load_args->queue; + tb_auto used_node_count = TB_DYN_ARR_SIZE(queue->storage); + + ecs_remove(ecs, scene, TbSceneParsing); + ecs_add(ecs, scene, TbSceneParsed); + + ecs_set_ptr(ecs, scene, TbEntityTaskQueue, queue); + ecs_set(ecs, scene, TbSceneEntityCount, {used_node_count}); + ecs_set(ecs, scene, TbSceneEntParseCounter, {used_node_count}); // Counts down + ecs_set(ecs, scene, TbSceneEntReadyCounter, {0}); // Counts up + + TracyCZoneEnd(ctx); +} + +typedef struct TbParseSceneArgs { + ecs_world_t *ecs; + TbTaskScheduler enki; + TbScene scene; + TbEntityTaskQueue *queue; + const char *scene_path; + TbPinnedTask parsed_task; +} TbParseSceneArgs; + +void tb_enqueue_entity_parse_req(ecs_world_t *ecs, const char *path, + TbEntityTaskQueue *queue, json_tokener *tok, + const cgltf_data *data, + const cgltf_node *node) { + TB_TRACY_SCOPE("Enqueue Entity Parse Req"); + json_object *json = NULL; + { + cgltf_size extra_size = 0; + char *extra_json = NULL; + if (node->extras.end_offset != 0 && node->extras.start_offset != 0) { + extra_size = (node->extras.end_offset - node->extras.start_offset) + 1; + extra_json = tb_alloc_nm_tp(tb_thread_alloc, extra_size, char); + if (cgltf_copy_extras_json(data, &node->extras, extra_json, + &extra_size) != cgltf_result_success) { + extra_size = 0; + extra_json = NULL; + } + } + + if (extra_json) { + json = json_tokener_parse_ex(tok, extra_json, (int32_t)extra_size); + } + } + + // Enqueue entity load request + TbEntityLoadRequest req = { + .ecs = ecs, + .source_path = path, + .data = data, + .node = node, + .json = json, + }; + TB_QUEUE_PUSH_PTR(queue, req); + + for (cgltf_size i = 0; i < node->children_count; ++i) { + tb_enqueue_entity_parse_req(ecs, path, queue, tok, data, node->children[i]); + } +} + +void tb_parse_scene_task(const void *args) { + TB_TRACY_SCOPE("Parse GLTF Scene Task"); + tb_auto load_args = (const TbParseSceneArgs *)args; + + tb_auto ecs = load_args->ecs; + tb_auto enki = load_args->enki; + tb_auto scene = load_args->scene; + tb_auto path = load_args->scene_path; + tb_auto parsed_task = load_args->parsed_task; + tb_auto queue = load_args->queue; + + // Read gltf onto the global allocator so it can be safely freed from any + // thread later + tb_auto data = tb_read_glb(tb_global_alloc, path); + + json_tokener *tok = json_tokener_new(); // TODO: clean this up alongside data + + // Create an entity for each node + for (cgltf_size i = 0; i < data->scenes[0].nodes_count; ++i) { + tb_auto node = data->scenes[0].nodes[i]; + tb_enqueue_entity_parse_req(ecs, path, queue, tok, data, node); + } + + TbSceneParsedArgs parsed_args = { + .ecs = ecs, + .scene = scene, + .path = path, + .queue = queue, + }; + tb_launch_pinned_task_args(enki, parsed_task, &parsed_args, + sizeof(TbSceneParsedArgs)); +} + +TbScene tb_create_scene(ecs_world_t *ecs, const char *scene_path) { + TB_TRACY_SCOPE("Create Scene"); + // If an entity already exists with this name it is either loading or loaded + TbScene scene = ecs_lookup(ecs, scene_path); + if (scene != 0) { + return scene; + } + + tb_auto enki = *ecs_singleton_get(ecs, TbTaskScheduler); + + scene = ecs_new_entity(ecs, 0); + ecs_set_name(ecs, scene, scene_path); + + // Need to copy strings for task safety + // Tasks are responsible for freeing these names + const size_t path_len = SDL_strnlen(scene_path, 256) + 1; + char *path_cpy = tb_alloc_nm_tp(tb_global_alloc, path_len, char); + SDL_strlcpy(path_cpy, scene_path, path_len); + + // This pinned task will be launched by the loading task + TbPinnedTask parsed_task = + tb_create_pinned_task(enki, tb_scene_parsed, NULL, 0); + + tb_auto entity_queue = ecs_get_mut(ecs, scene, TbEntityTaskQueue); + *entity_queue = (TbEntityTaskQueue){0}; + TB_QUEUE_RESET(*entity_queue, tb_global_alloc, 8192); + + // Launch a task to open the scene, parse it, and load relevant children + TbParseSceneArgs args = { + .ecs = ecs, + .enki = enki, + .scene = scene, + .scene_path = path_cpy, + .parsed_task = parsed_task, + .queue = entity_queue, + }; + TbTask load_task = + tb_async_task(enki, tb_parse_scene_task, &args, sizeof(TbParseSceneArgs)); + + ecs_set(ecs, scene, TbTask, {load_task}); + ecs_add(ecs, scene, TbSceneParsing); + ecs_add(ecs, scene, TbSceneRoot); + + return scene; +} + +ecs_entity_t tb_load_entity(ecs_world_t *ecs, const char *source_path, + const cgltf_data *data, const cgltf_node *node, + json_object *json) { + TB_TRACY_SCOPE("Load Entity"); + // See if the entity is enabled or a prefab + bool enabled = true; + bool prefab = false; + if (json) { + tb_auto enabled_obj = json_object_object_get(json, "enabled"); + if (enabled_obj) { + enabled = (bool)json_object_get_boolean(enabled_obj); + } + tb_auto prefab_obj = json_object_object_get(json, "prefab"); + if (prefab_obj) { + prefab = (bool)json_object_get_boolean(prefab_obj); + } + } + + // Determine what to call this entity + const char *name = node->name; + if (name && ecs_lookup(ecs, name) != TbInvalidEntityId) { + name = NULL; + } + + // Create an entity + ecs_entity_t ent = 0; + if (prefab) { + ent = ecs_new_prefab(ecs, 0); + } else { + ent = ecs_new_entity(ecs, 0); + } + + if (name) { + ecs_set_name(ecs, ent, name); + } + + // We don't know our parent yet until all entities have been loaded + // Associate the node with the entity to be able to resolve parents + ecs_set(ecs, ent, TbNode, {node}); + if (node->parent) { + ecs_add(ecs, ent, TbParentRequest); + } + + // Some default components need to be tested for + { + tb_auto load_fn = tb_get_component_load_fn("transform"); + if (load_fn) { + load_fn(ecs, ent, source_path, data, node, NULL); + } + if (node->mesh) { + load_fn = tb_get_component_load_fn("mesh"); + if (load_fn) { + load_fn(ecs, ent, source_path, data, node, NULL); + } + } + if (node->camera) { + load_fn = tb_get_component_load_fn("camera"); + if (load_fn) { + load_fn(ecs, ent, source_path, data, node, NULL); + } + } + if (node->light) { + load_fn = tb_get_component_load_fn("light"); + if (load_fn) { + load_fn(ecs, ent, source_path, data, node, NULL); + } + } + } + + // Add custom components + if (json) { + json_object_object_foreach(json, component_name, component_obj) { + TB_TRACY_SCOPE("Component"); + tb_auto load_fn = tb_get_component_load_fn(component_name); + if (load_fn) { + load_fn(ecs, ent, source_path, data, node, component_obj); + } + } + } + + ecs_enable(ecs, ent, enabled); + return ent; +} + +void tb_load_entities(ecs_iter_t *it) { + TB_TRACY_SCOPE("Load Entities"); + // TODO: Really this should be timesliced since entities may take + // a while to load their components + static const uint32_t MAX_ENTITIES_DEQUEUE_PER_FRAME = 16; + + tb_auto entity_queues = ecs_field(it, TbEntityTaskQueue, 1); + tb_auto counters = ecs_field(it, TbSceneEntParseCounter, 2); + uint32_t dequeue_counter = 0; + bool exit = false; + + TbEntityLoadRequest load_req = {0}; + for (int32_t i = 0; i < it->count; ++i) { + tb_auto entity_queue = &entity_queues[i]; + tb_auto scene_counter = &counters[i]; + if (*scene_counter == 0) { + continue; + } + + TbScene scene = it->entities[i]; + while (TB_QUEUE_POP(*entity_queue, &load_req)) { + if (dequeue_counter >= MAX_ENTITIES_DEQUEUE_PER_FRAME || + *scene_counter == 0) { + dequeue_counter = 0; + exit = true; + // USED TO HAVE A BREAK HERE BUT THAT IS NOT CORRECT + // IF WE POP AN ENTITY FROM THE QUEUE WE MUST PROCESS IT + } + + tb_auto ecs = load_req.ecs; + tb_auto source_path = load_req.source_path; + tb_auto data = load_req.data; + tb_auto node = load_req.node; + tb_auto json = load_req.json; + + tb_auto ent = tb_load_entity(ecs, source_path, data, node, json); + + // Entities need a refernce to their parent scene since they may not + // be directly parented + ecs_set(ecs, ent, TbSceneRef, {scene}); + + (*scene_counter)--; + dequeue_counter++; + + if (exit) { + break; + } + } + + if (*scene_counter == 0) { + ecs_remove(it->world, scene, TbSceneParsed); + ecs_add(it->world, scene, TbSceneLoading); + } + + if (exit) { + break; + } + } +} + +void tb_resolve_parents(ecs_iter_t *it) { + TB_TRACY_SCOPE("Resolve Parents"); + tb_auto ecs = it->world; + + tb_auto query = ecs_field(it, TbSceneCtx, 1)->parent_resolve_query; + tb_auto parent_it = ecs_query_iter(ecs, query); + while (ecs_query_next(&parent_it)) { + if (tb_parent_resolution_count >= TbMaxPerFrameParentResolutions) { + break; + } + tb_auto nodes = ecs_field(&parent_it, TbNode, 2); + tb_auto scene_refs = ecs_field(&parent_it, TbSceneRef, 3); + for (int32_t i = 0; i < parent_it.count; ++i) { + if (tb_parent_resolution_count >= TbMaxPerFrameParentResolutions) { + break; + } + tb_parent_resolution_count++; + tb_auto entity = parent_it.entities[i]; + tb_auto node = nodes[i]; + tb_auto scene = scene_refs[i]; + + tb_auto filter = ecs_filter(ecs, {.terms = { + {.id = ecs_id(TbNode)}, + {.id = ecs_id(TbSceneRef)}, + }}); + tb_auto filter_it = ecs_filter_iter(ecs, filter); + while (ecs_filter_next(&filter_it)) { + tb_auto parent_nodes = ecs_field(&filter_it, TbNode, 1); + tb_auto parent_scenes = ecs_field(&filter_it, TbSceneRef, 2); + for (int32_t node_idx = 0; node_idx < filter_it.count; ++node_idx) { + if (parent_scenes[node_idx] != scene) { + continue; + } + tb_auto parent_ent = filter_it.entities[node_idx]; + tb_auto parent_node = parent_nodes[node_idx]; + if (node->parent == parent_node) { + ecs_add_pair(ecs, entity, EcsChildOf, parent_ent); + ecs_remove(ecs, entity, TbParentRequest); + } + } + } + + ecs_filter_fini(filter); + } + } + tb_parent_resolution_count = 0; +} + +void tb_ready_check_components(ecs_iter_t *it) { + TB_TRACY_SCOPE("Ready Check Components"); + tb_auto ecs = it->world; + + tb_auto query = ecs_field(it, TbSceneCtx, 1)->comp_ready_query; + tb_auto comp_it = ecs_query_iter(ecs, query); + while (ecs_query_next(&comp_it)) { + if (tb_component_ready_check_count >= TbMaxPerFrameComponentReadyChecks) { + break; + } + for (int32_t i = 0; i < comp_it.count; ++i) { + if (tb_component_ready_check_count >= TbMaxPerFrameComponentReadyChecks) { + break; + } + tb_component_ready_check_count++; + tb_auto ent = comp_it.entities[i]; + if (tb_enitity_components_ready(ecs, ent)) { + ecs_add(ecs, ent, TbComponentsReady); + } + } + } + tb_component_ready_check_count = 0; +} + +void tb_ready_check_entities(ecs_iter_t *it) { + TB_TRACY_SCOPE("Ready Check Entities"); + tb_auto ecs = it->world; + + tb_auto query = ecs_field(it, TbSceneCtx, 1)->ent_ready_query; + + tb_auto ent_it = ecs_query_iter(ecs, query); + while (ecs_query_next(&ent_it)) { + if (tb_entity_ready_check_count >= TbMaxPerFrameEntityReadyChecks) { + break; + }; + tb_auto ent_totals = ecs_field(&ent_it, TbSceneEntityCount, 1); + tb_auto counters = ecs_field(&ent_it, TbSceneEntReadyCounter, 2); + for (int32_t scene_idx = 0; scene_idx < ent_it.count; ++scene_idx) { + if (tb_entity_ready_check_count >= TbMaxPerFrameEntityReadyChecks) { + break; + }; + tb_auto scene = ent_it.entities[scene_idx]; + tb_auto counter = &counters[scene_idx]; + tb_auto total = ent_totals[scene_idx]; + + bool complete = false; + tb_auto filter = ecs_filter(ecs, {.terms = { + {.id = ecs_id(TbNode)}, + }}); + tb_auto filter_it = ecs_filter_iter(ecs, filter); + while (ecs_filter_next(&filter_it)) { + if (complete) { + break; + } + if (tb_entity_ready_check_count >= TbMaxPerFrameEntityReadyChecks) { + break; + }; + for (int32_t ent_idx = 0; ent_idx < filter_it.count; ++ent_idx) { + if (tb_entity_ready_check_count >= TbMaxPerFrameEntityReadyChecks) { + break; + }; + tb_entity_ready_check_count++; + + tb_auto entity = filter_it.entities[ent_idx]; + + // Entity already ready + if (ecs_has(ecs, entity, TbEntityReady)) { + continue; + } + + // Not relevant to this scene + if (scene != *ecs_get(ecs, entity, TbSceneRef)) { + continue; + } + // Parent is still being resolved + if (ecs_has(ecs, entity, TbParentRequest)) { + continue; + } + // Components are still pending + if (!ecs_has(ecs, entity, TbComponentsReady)) { + continue; + } + + // Entity is ready! Remove unnecessary components and mark as ready + ecs_remove(ecs, entity, TbNode); + ecs_add(ecs, entity, TbEntityReady); + (*counter)++; + if (*counter == total) { + complete = true; + break; + } + } + } + ecs_filter_fini(filter); + + if (complete) { + ecs_remove(ecs, scene, TbSceneLoading); + ecs_add(ecs, scene, TbSceneLoaded); + } + } + } + + tb_entity_ready_check_count = 0; +} + +void tb_ready_check_scenes(ecs_iter_t *it) { + TB_TRACY_SCOPE("Ready Check Scenes"); + tb_auto ecs = it->world; + for (int32_t scene_idx = 0; scene_idx < it->count; ++scene_idx) { + TbScene scene = it->entities[scene_idx]; + + // We're here because all entities are loaded + // Mark everything as ready! + tb_auto filter = ecs_filter(ecs, {.terms = { + {.id = ecs_id(TbSceneRef)}, + }}); + tb_auto filter_it = ecs_filter_iter(ecs, filter); + while (ecs_filter_next(&filter_it)) { + tb_auto scene_refs = ecs_field(&filter_it, TbSceneRef, 1); + for (int32_t ent_idx = 0; ent_idx < filter_it.count; ++ent_idx) { + tb_auto entity = filter_it.entities[ent_idx]; + tb_auto scene_ref = scene_refs[ent_idx]; + if (scene != scene_ref) { + continue; + } + + if (ecs_has(ecs, entity, TbTransformComponent)) { + tb_transform_mark_dirty(ecs, entity); + } + + tb_render_object_mark_dirty(ecs, entity); + } + } + + ecs_remove(ecs, scene, TbSceneLoaded); + ecs_add(ecs, scene, TbSceneReady); + } +} + +void tb_register_scene_sys(TbWorld *world) { + tb_auto ecs = world->ecs; + ECS_COMPONENT_DEFINE(ecs, TbSceneCtx); + ECS_COMPONENT_DEFINE(ecs, TbEntityTaskQueue); + ECS_COMPONENT_DEFINE(ecs, TbSceneEntityCount); + ECS_COMPONENT_DEFINE(ecs, TbSceneEntParseCounter); + ECS_COMPONENT_DEFINE(ecs, TbSceneEntReadyCounter); + ECS_COMPONENT_DEFINE(ecs, TbNode); + ECS_COMPONENT_DEFINE(ecs, TbSceneRef); + ECS_TAG_DEFINE(ecs, TbParentRequest); + ECS_TAG_DEFINE(ecs, TbSceneRoot); + ECS_TAG_DEFINE(ecs, TbSceneParsing); + ECS_TAG_DEFINE(ecs, TbSceneParsed); + ECS_TAG_DEFINE(ecs, TbSceneLoading); + ECS_TAG_DEFINE(ecs, TbSceneLoaded); + ECS_TAG_DEFINE(ecs, TbSceneReady); + ECS_TAG_DEFINE(ecs, TbComponentsReady); + ECS_TAG_DEFINE(ecs, TbEntityReady); + + TbSceneCtx ctx = { + .parent_resolve_query = ecs_query( + ecs, {.filter.terms = + { + {.id = ecs_id(TbParentRequest), .inout = EcsIn}, + {.id = ecs_id(TbNode), .inout = EcsIn}, + {.id = ecs_id(TbSceneRef), .inout = EcsIn}, + }}), + .comp_ready_query = + ecs_query(ecs, {.filter.terms = + { + {.id = ecs_id(TbNode), .inout = EcsIn}, + { + .id = ecs_id(TbComponentsReady), + .oper = EcsNot, + .inout = EcsIn, + }, + }}), + .ent_ready_query = ecs_query( + ecs, {.filter.terms = { + {.id = ecs_id(TbSceneEntityCount), .inout = EcsIn}, + {.id = ecs_id(TbSceneEntReadyCounter), .inout = EcsOut}, + {.id = ecs_id(TbSceneLoading), .inout = EcsIn}, + }})}; + ecs_singleton_set_ptr(ecs, TbSceneCtx, &ctx); + + // This is a no-readonly system because we are adding entities + ecs_system(ecs, + {.entity = ecs_entity(ecs, {.name = "tb_load_entities", + .add = {ecs_dependson(EcsOnLoad)}}), + .query.filter.terms = {{.id = ecs_id(TbEntityTaskQueue)}, + {.id = ecs_id(TbSceneEntParseCounter)}, + {.id = ecs_id(TbSceneParsed)}}, + .callback = tb_load_entities, + .no_readonly = true}); + + ECS_SYSTEM(ecs, tb_resolve_parents, EcsPostLoad, [in] TbSceneCtx); + ECS_SYSTEM(ecs, tb_ready_check_components, EcsPostLoad, [in] TbSceneCtx); + ECS_SYSTEM(ecs, tb_ready_check_entities, EcsPostLoad, [in] TbSceneCtx); + ECS_SYSTEM(ecs, tb_ready_check_scenes, EcsPostLoad, TbSceneLoaded); +} + +void tb_unregister_scene_sys(TbWorld *world) { (void)world; } + +TB_REGISTER_SYS(tb, scene, TB_SYSTEM_NORMAL) + +bool tb_is_scene_ready(ecs_world_t *ecs, TbScene scene) { + return ecs_has(ecs, scene, TbSceneReady); +} diff --git a/source/tb_scene_material.c b/source/tb_scene_material.c new file mode 100644 index 00000000..b13d63f7 --- /dev/null +++ b/source/tb_scene_material.c @@ -0,0 +1,230 @@ +#include "tb_scene_material.h" + +#include "gltf.hlsli" +#include "tb_common.h" +#include "tb_gltf.h" +#include "tb_texture_system.h" + +typedef struct TbSceneMaterial { + const cgltf_data *gltf_data; + const char *name; + TbGLTFMaterialData data; + TbTexture color_map; + TbTexture normal_map; + TbTexture metal_rough_map; +} TbSceneMaterial; + +// Run on a thread +bool tb_parse_scene_mat(const cgltf_data *gltf_data, const char *name, + const cgltf_material *material, void **out_mat_data) { + *out_mat_data = tb_alloc(tb_global_alloc, sizeof(TbSceneMaterial)); + tb_auto scene_mat = (TbSceneMaterial *)*out_mat_data; + + const size_t name_len = SDL_strnlen(name, 256) + 1; + char *name_cpy = tb_alloc_nm_tp(tb_global_alloc, name_len, char); + SDL_strlcpy(name_cpy, name, name_len); + + scene_mat->gltf_data = gltf_data; + scene_mat->name = name_cpy; + + // Find a suitable texture transform from the material + cgltf_texture_transform tex_trans = { + .scale = {1, 1}, + }; + if (material) { + // Expecting that all textures in the material share the same texture + // transform + if (material->has_pbr_metallic_roughness) { + tex_trans = material->pbr_metallic_roughness.base_color_texture.transform; + } else if (material->has_pbr_specular_glossiness) { + tex_trans = material->pbr_specular_glossiness.diffuse_texture.transform; + } else if (material->normal_texture.texture) { + tex_trans = material->normal_texture.transform; + } + } + + TbMaterialPerm feat_perm = 0; + if (material->has_pbr_metallic_roughness) { + feat_perm |= GLTF_PERM_PBR_METALLIC_ROUGHNESS; + if (material->pbr_metallic_roughness.metallic_roughness_texture.texture != + NULL) { + feat_perm |= GLTF_PERM_PBR_METAL_ROUGH_TEX; + } + if (material->pbr_metallic_roughness.base_color_texture.texture != NULL) { + feat_perm |= GLTF_PERM_BASE_COLOR_MAP; + } + } + if (material->has_pbr_specular_glossiness) { + feat_perm |= GLTF_PERM_PBR_SPECULAR_GLOSSINESS; + + if (material->pbr_specular_glossiness.diffuse_texture.texture != NULL && + material->pbr_specular_glossiness.specular_glossiness_texture.texture != + NULL) { + // feat_perm |= GLTF_PERM_PBR_SPECULAR_GLOSSINESS_TEX; + } + if (material->pbr_specular_glossiness.diffuse_texture.texture != NULL) { + feat_perm |= GLTF_PERM_BASE_COLOR_MAP; + } + } + if (material->has_clearcoat) { + feat_perm |= GLTF_PERM_CLEARCOAT; + } + if (material->has_transmission) { + feat_perm |= GLTF_PERM_TRANSMISSION; + } + if (material->has_volume) { + feat_perm |= GLTF_PERM_VOLUME; + } + if (material->has_ior) { + feat_perm |= GLTF_PERM_IOR; + } + if (material->has_specular) { + feat_perm |= GLTF_PERM_SPECULAR; + } + if (material->has_sheen) { + feat_perm |= GLTF_PERM_SHEEN; + } + if (material->unlit) { + feat_perm |= GLTF_PERM_UNLIT; + } + if (material->alpha_mode == cgltf_alpha_mode_mask) { + feat_perm |= GLTF_PERM_ALPHA_CLIP; + } + if (material->alpha_mode == cgltf_alpha_mode_blend) { + feat_perm |= GLTF_PERM_ALPHA_BLEND; + } + if (material->double_sided) { + feat_perm |= GLTF_PERM_DOUBLE_SIDED; + } + if (material->normal_texture.texture != NULL) { + feat_perm |= GLTF_PERM_NORMAL_MAP; + } + + scene_mat->data = (TbGLTFMaterialData){ + .tex_transform = + { + .offset = + (float2){ + tex_trans.offset[0], + tex_trans.offset[1], + }, + .scale = + (float2){ + tex_trans.scale[0], + tex_trans.scale[1], + }, + }, + .pbr_metallic_roughness = + { + .base_color_factor = + tb_atof4(material->pbr_metallic_roughness.base_color_factor), + .metal_rough_factors = + {material->pbr_metallic_roughness.metallic_factor, + material->pbr_metallic_roughness.roughness_factor}, + }, + .pbr_specular_glossiness.diffuse_factor = + tb_atof4(material->pbr_specular_glossiness.diffuse_factor), + .specular = + tb_f3tof4(tb_atof3(material->pbr_specular_glossiness.specular_factor), + material->pbr_specular_glossiness.glossiness_factor), + .emissives = tb_f3tof4(tb_atof3(material->emissive_factor), 1.0f), + .perm = feat_perm, + }; + + if (material->has_emissive_strength) { + scene_mat->data.emissives[3] = + material->emissive_strength.emissive_strength; + } + if (material->alpha_mode == cgltf_alpha_mode_mask) { + scene_mat->data.sheen_alpha[3] = material->alpha_cutoff; + } + + return true; +} + +// Run in a task on the main thread +void tb_load_scene_mat(ecs_world_t *ecs, void *mat_data) { + tb_auto scene_mat = (TbSceneMaterial *)mat_data; + if (scene_mat == NULL) { + return; + } + + const cgltf_data *gltf_data = scene_mat->gltf_data; + const char *name = scene_mat->name; + + tb_auto color = tb_get_default_color_tex(ecs); + if ((scene_mat->data.perm & GLTF_PERM_BASE_COLOR_MAP) > 0) { + color = tb_tex_sys_load_mat_tex(ecs, gltf_data, name, TB_TEX_USAGE_COLOR); + } + scene_mat->color_map = color; + + tb_auto normal = tb_get_default_normal_tex(ecs); + if ((scene_mat->data.perm & GLTF_PERM_NORMAL_MAP) > 0) { + normal = tb_tex_sys_load_mat_tex(ecs, gltf_data, name, TB_TEX_USAGE_NORMAL); + } + scene_mat->normal_map = normal; + + tb_auto metal_rough = tb_get_default_metal_rough_tex(ecs); + if ((scene_mat->data.perm & GLTF_PERM_PBR_METAL_ROUGH_TEX) > 0) { + metal_rough = + tb_tex_sys_load_mat_tex(ecs, gltf_data, name, TB_TEX_USAGE_METAL_ROUGH); + } + scene_mat->metal_rough_map = metal_rough; + + tb_free(tb_global_alloc, (void *)name); +} + +bool tb_is_scene_mat_ready(ecs_world_t *ecs, const TbMaterialData *data) { + tb_auto scene_mat = (TbSceneMaterial *)data->domain_data; + if (scene_mat == NULL) { + return false; + } + + return tb_is_texture_ready(ecs, scene_mat->color_map) && + tb_is_texture_ready(ecs, scene_mat->normal_map) && + tb_is_texture_ready(ecs, scene_mat->metal_rough_map); +} + +void *tb_get_scene_mat_data(ecs_world_t *ecs, const TbMaterialData *data) { + tb_auto scene_mat = (TbSceneMaterial *)data->domain_data; + scene_mat->data.color_idx = + *ecs_get(ecs, scene_mat->color_map, TbTextureComponent); + scene_mat->data.normal_idx = + *ecs_get(ecs, scene_mat->normal_map, TbTextureComponent); + scene_mat->data.pbr_idx = + *ecs_get(ecs, scene_mat->metal_rough_map, TbTextureComponent); + return (void *)&scene_mat->data; +} + +size_t tb_get_scene_mat_size(void) { return sizeof(TbGLTFMaterialData); } + +bool tb_is_scene_mat_trans(const TbMaterialData *data) { + tb_auto scene_mat = (TbSceneMaterial *)data->domain_data; + tb_auto perm = scene_mat->data.perm; + return perm & GLTF_PERM_ALPHA_CLIP || perm & GLTF_PERM_ALPHA_BLEND; +} + +void tb_register_scene_material_domain(ecs_world_t *ecs) { + TbSceneMaterial default_scene_mat = { + .data = + { + .perm = GLTF_PERM_BASE_COLOR_MAP | GLTF_PERM_NORMAL_MAP | + GLTF_PERM_PBR_METAL_ROUGH_TEX, + }, + .color_map = tb_get_default_color_tex(ecs), + .metal_rough_map = tb_get_default_metal_rough_tex(ecs), + .normal_map = tb_get_default_normal_tex(ecs), + }; + + TbMaterialDomain domain = { + .parse_fn = tb_parse_scene_mat, + .load_fn = tb_load_scene_mat, + .ready_fn = tb_is_scene_mat_ready, + .get_data_fn = tb_get_scene_mat_data, + .get_size_fn = tb_get_scene_mat_size, + .is_trans_fn = tb_is_scene_mat_trans, + }; + + tb_register_mat_usage(ecs, "scene", TB_MAT_USAGE_SCENE, domain, + &default_scene_mat, sizeof(TbSceneMaterial)); +} diff --git a/source/tb_sdl.c b/source/tb_sdl.c new file mode 100644 index 00000000..f8c7863e --- /dev/null +++ b/source/tb_sdl.c @@ -0,0 +1,10 @@ +#include "tb_sdl.h" + +#include "tb_common.h" +#include "tb_mmap.h" + +void *tb_rw_mmap(SDL_RWops *file, size_t size) { + return tb_mmap(0, size, 0, 0, file->hidden.stdio.fp, 0); +} + +void tb_rw_munmap(void *data, size_t size) { tb_munmap(data, size); } diff --git a/source/settings.c b/source/tb_settings.c similarity index 96% rename from source/settings.c rename to source/tb_settings.c index b4b96e70..d6cce18d 100644 --- a/source/settings.c +++ b/source/tb_settings.c @@ -1,10 +1,10 @@ -#include "settings.h" +#include "tb_settings.h" -#include "fxaa.h" -#include "profiling.h" -#include "tbcommon.h" -#include "tbimgui.h" -#include "world.h" +#include "tb_common.h" +#include "tb_fxaa.h" +#include "tb_imgui.h" +#include "tb_profiling.h" +#include "tb_world.h" #include diff --git a/source/shader_system.c b/source/tb_shader_system.c similarity index 76% rename from source/shader_system.c rename to source/tb_shader_system.c index ddb619ae..192c3c46 100644 --- a/source/shader_system.c +++ b/source/tb_shader_system.c @@ -1,16 +1,12 @@ #include "tb_shader_system.h" -#include "profiling.h" +#include "tb_common.h" +#include "tb_profiling.h" #include "tb_task_scheduler.h" -#include "tbcommon.h" -#include "world.h" +#include "tb_world.h" -#include - -typedef struct TbShader { - VkPipeline pipeline; -} TbShader; -ECS_COMPONENT_DECLARE(TbShader); +typedef VkPipeline TbPipeline; +ECS_COMPONENT_DECLARE(TbPipeline); ECS_TAG_DECLARE(TbShaderCompiled); typedef struct TbShaderCompleteArgs { @@ -26,7 +22,7 @@ void tb_shader_complete_task(const void *args) { tb_auto ecs = complete_args->ecs; tb_auto ent = complete_args->ent; - ecs_set(ecs, ent, TbShader, {complete_args->pipeline}); + ecs_set(ecs, ent, TbPipeline, {complete_args->pipeline}); ecs_add_id(ecs, ent, TbShaderCompiled); TracyCZoneEnd(ctx); @@ -55,21 +51,21 @@ void tb_shader_compile_task(const void *args) { sizeof(TbShaderCompleteArgs)); // We're only responsible for the compile args - mi_free(task_args->compile_args); + tb_free(tb_global_alloc, task_args->compile_args); TracyCZoneEnd(ctx); } -ecs_entity_t tb_shader_load(ecs_world_t *ecs, TbShaderCompileFn compile_fn, - void *args, size_t args_size) { +TbShader tb_shader_load(ecs_world_t *ecs, TbShaderCompileFn compile_fn, + void *args, size_t args_size) { TracyCZoneN(ctx, "Create Shader Load task", true); tb_auto enki = *ecs_singleton_get(ecs, TbTaskScheduler); - ecs_entity_t ent = ecs_new_entity(ecs, 0); - ecs_set(ecs, ent, TbShader, {0}); + TbShader ent = ecs_new_entity(ecs, 0); + ecs_set(ecs, ent, TbPipeline, {0}); // Need to make a copy of the args into a thread-safe pool - tb_auto compile_args = mi_malloc(args_size); + tb_auto compile_args = tb_alloc(tb_global_alloc, args_size); SDL_memcpy(compile_args, args, args_size); // Create main thread task @@ -92,8 +88,8 @@ ecs_entity_t tb_shader_load(ecs_world_t *ecs, TbShaderCompileFn compile_fn, return ent; } -void tb_shader_destroy(ecs_world_t *ecs, ecs_entity_t shader) { - if (!ecs_has(ecs, shader, TbShader) || !tb_is_shader_ready(ecs, shader)) { +void tb_shader_destroy(ecs_world_t *ecs, TbShader shader) { + if (!ecs_has(ecs, shader, TbPipeline) || !tb_is_shader_ready(ecs, shader)) { return; } tb_auto rnd_sys = ecs_singleton_get(ecs, TbRenderSystem); @@ -103,12 +99,12 @@ void tb_shader_destroy(ecs_world_t *ecs, ecs_entity_t shader) { ecs_delete(ecs, shader); } -bool tb_is_shader_ready(ecs_world_t *ecs, ecs_entity_t shader) { +bool tb_is_shader_ready(ecs_world_t *ecs, TbShader shader) { return ecs_has_id(ecs, shader, TbShaderCompiled) && tb_shader_get_pipeline(ecs, shader) != VK_NULL_HANDLE; } -bool tb_wait_shader_ready(ecs_world_t *ecs, ecs_entity_t shader) { +bool tb_wait_shader_ready(ecs_world_t *ecs, TbShader shader) { if (!tb_is_shader_ready(ecs, shader)) { // we *require* the imgui shader be ready by this point // so wait for it if necessary @@ -122,18 +118,17 @@ bool tb_wait_shader_ready(ecs_world_t *ecs, ecs_entity_t shader) { return false; } -VkPipeline tb_shader_get_pipeline(ecs_world_t *ecs, ecs_entity_t ent) { - tb_auto shader = ecs_get(ecs, ent, TbShader); - if (!shader) { +VkPipeline tb_shader_get_pipeline(ecs_world_t *ecs, TbShader ent) { + tb_auto pipe = ecs_get(ecs, ent, TbPipeline); + if (pipe == NULL) { return VK_NULL_HANDLE; } - - return shader->pipeline; + return *pipe; } void tb_register_shader_sys(TbWorld *world) { TracyCZoneN(ctx, "Register Shader Sys", true); - ECS_COMPONENT_DEFINE(world->ecs, TbShader); + ECS_COMPONENT_DEFINE(world->ecs, TbPipeline); ECS_TAG_DEFINE(world->ecs, TbShaderCompiled); TracyCZoneEnd(ctx); } diff --git a/source/shadowsystem.c b/source/tb_shadow_system.c similarity index 83% rename from source/shadowsystem.c rename to source/tb_shadow_system.c index f0ee7caf..adafc8c5 100644 --- a/source/shadowsystem.c +++ b/source/tb_shadow_system.c @@ -1,19 +1,19 @@ -#include "shadowsystem.h" - -#include "cameracomponent.h" -#include "lightcomponent.h" -#include "materialsystem.h" -#include "meshcomponent.h" -#include "meshsystem.h" -#include "profiling.h" -#include "renderobjectsystem.h" -#include "renderpipelinesystem.h" -#include "rendertargetsystem.h" -#include "tbcommon.h" -#include "transformcomponent.h" -#include "viewsystem.h" -#include "visualloggingsystem.h" -#include "world.h" +#include "tb_camera_component.h" +#include "tb_common.h" +#include "tb_light_component.h" +#include "tb_mesh_component.h" +#include "tb_mesh_rnd_sys.h" +#include "tb_mesh_system.h" +#include "tb_profiling.h" +#include "tb_render_common.h" +#include "tb_render_object_system.h" +#include "tb_render_pipeline_system.h" +#include "tb_render_target_system.h" +#include "tb_shader_system.h" +#include "tb_transform_component.h" +#include "tb_view_system.h" +#include "tb_visual_logging_system.h" +#include "tb_world.h" #include @@ -42,7 +42,7 @@ ECS_COMPONENT_DECLARE(TbShadowSystem); void tb_register_shadow_sys(TbWorld *world); void tb_unregister_shadow_sys(TbWorld *world); -TB_REGISTER_SYS(tb, shadow, TB_SHADOW_SYS_PRIO) +TB_REGISTER_SYS(tb, shadow, TB_SYSTEM_HIGH) VkResult create_shadow_pipeline(TbRenderSystem *rnd_sys, VkFormat depth_format, VkPipelineLayout pipe_layout, @@ -69,6 +69,9 @@ VkResult create_shadow_pipeline(TbRenderSystem *rnd_sys, VkFormat depth_format, VkGraphicsPipelineCreateInfo create_info = { .sType = VK_STRUCTURE_TYPE_GRAPHICS_PIPELINE_CREATE_INFO, +#if TB_USE_DESC_BUFFER == 1 + .flags = VK_PIPELINE_CREATE_DESCRIPTOR_BUFFER_BIT_EXT, +#endif .pNext = &(VkPipelineRenderingCreateInfo){ .sType = VK_STRUCTURE_TYPE_PIPELINE_RENDERING_CREATE_INFO, @@ -179,11 +182,29 @@ void shadow_pass_record(TracyCGPUContext *gpu_ctx, VkCommandBuffer buffer, vkCmdSetScissor(buffer, 0, 1, &batch->scissor); const uint32_t set_count = 5; - VkDescriptorSet sets[set_count] = { - prim_batch->view_set, prim_batch->draw_set, prim_batch->obj_set, - prim_batch->idx_set, prim_batch->pos_set}; - vkCmdBindDescriptorSets(buffer, VK_PIPELINE_BIND_POINT_GRAPHICS, layout, 0, - set_count, sets, 0, NULL); +#if TB_USE_DESC_BUFFER == 1 + { + const VkDescriptorBufferBindingInfoEXT buffer_bindings[set_count] = { + prim_batch->view_addr, prim_batch->draw_addr, prim_batch->obj_addr, + prim_batch->idx_addr, prim_batch->pos_addr, + }; + vkCmdBindDescriptorBuffersEXT(buffer, set_count, buffer_bindings); + uint32_t buf_indices[set_count] = {0, 1, 2, 3, 4}; + VkDeviceSize buf_offsets[set_count] = {0}; + vkCmdSetDescriptorBufferOffsetsEXT( + buffer, VK_PIPELINE_BIND_POINT_GRAPHICS, layout, 0, set_count, + buf_indices, buf_offsets); + } +#else + { + VkDescriptorSet sets[set_count] = { + prim_batch->view_set, prim_batch->draw_set, prim_batch->obj_set, + prim_batch->idx_set, prim_batch->pos_set}; + vkCmdBindDescriptorSets(buffer, VK_PIPELINE_BIND_POINT_GRAPHICS, layout, + 0, set_count, sets, 0, NULL); + } +#endif + for (uint32_t draw_idx = 0; draw_idx < batch->draw_count; ++draw_idx) { TracyCZoneNC(draw_ctx, "Record Indirect Draw", TracyCategoryColorRendering, true); @@ -348,7 +369,7 @@ void shadow_update_tick(ecs_iter_t *it) { } void shadow_draw_tick(ecs_iter_t *it) { - TracyCZoneNC(ctx, "Shadow System Draw", TracyCategoryColorCore, true); + TB_TRACY_SCOPE("Shadow System Draw"); tb_auto ecs = it->world; tb_auto rp_sys = ecs_singleton_get_mut(ecs, TbRenderPipelineSystem); @@ -356,16 +377,17 @@ void shadow_draw_tick(ecs_iter_t *it) { tb_auto mesh_sys = ecs_singleton_get_mut(ecs, TbMeshSystem); tb_auto view_sys = ecs_singleton_get_mut(ecs, TbViewSystem); + // If any shaders aren't ready just bail + if (!tb_is_shader_ready(ecs, mesh_sys->opaque_shader) || + !tb_is_shader_ready(ecs, mesh_sys->transparent_shader) || + !tb_is_shader_ready(ecs, mesh_sys->prepass_shader)) { + return; + } + // The shadow batch is just the opaque batch but with a different pipeline if (!mesh_sys->opaque_batch) { - TracyCZoneEnd(ctx); return; } - TbDrawBatch shadow_batch = *mesh_sys->opaque_batch; - tb_auto shadow_prim_batch = *(TbPrimitiveBatch *)shadow_batch.user_batch; - shadow_batch.pipeline = shadow_sys->pipeline; - shadow_batch.layout = shadow_sys->pipe_layout; - shadow_batch.user_batch = &shadow_prim_batch; // For each shadow casting light we want to record shadow draws ecs_iter_t light_it = ecs_query_iter(ecs, shadow_sys->dir_light_query); @@ -373,20 +395,43 @@ void shadow_draw_tick(ecs_iter_t *it) { TbDirectionalLightComponent *lights = ecs_field(&light_it, TbDirectionalLightComponent, 1); for (int32_t light_idx = 0; light_idx < light_it.count; ++light_idx) { + TB_TRACY_SCOPE("Submit Batches"); const TbDirectionalLightComponent *light = &lights[light_idx]; - // Submit batch for each shadow cascade - TracyCZoneN(ctx2, "Submit Batches", true); for (uint32_t cascade_idx = 0; cascade_idx < TB_CASCADE_COUNT; ++cascade_idx) { + tb_auto view_id = light->cascade_views[cascade_idx]; - tb_auto view_set = tb_view_system_get_descriptor( - view_sys, light->cascade_views[cascade_idx]); +#if TB_USE_DESC_BUFFER == 1 + tb_auto view_addr = tb_view_sys_get_table_addr(ecs, view_id); + // Skip camera if view set isn't ready + if (view_addr.address == VK_NULL_HANDLE) { + continue; + } +#else + tb_auto view_set = tb_view_system_get_descriptor(view_sys, view_id); + // Skip camera if view set isn't ready + if (view_set == VK_NULL_HANDLE) { + continue; + } +#endif + + // Must perform the above check before we try to access the opaque batch + TbDrawBatch shadow_batch = *mesh_sys->opaque_batch; + tb_auto shadow_prim_batch = + *(TbPrimitiveBatch *)shadow_batch.user_batch; + shadow_batch.pipeline = shadow_sys->pipeline; + shadow_batch.layout = shadow_sys->pipe_layout; + shadow_batch.user_batch = &shadow_prim_batch; tb_auto batch = &shadow_batch; tb_auto prim_batch = (TbPrimitiveBatch *)batch->user_batch; +#if TB_USE_DESC_BUFFER == 1 + prim_batch->view_addr = view_addr; +#else prim_batch->view_set = view_set; +#endif const float dim = TB_SHADOW_MAP_DIM; batch->viewport = (VkViewport){0, 0, dim, dim, 0, 1}; batch->scissor = (VkRect2D){{0, 0}, {dim, dim}}; @@ -394,11 +439,11 @@ void shadow_draw_tick(ecs_iter_t *it) { tb_render_pipeline_issue_draw_batch( rp_sys, shadow_sys->draw_ctxs[cascade_idx], 1, batch); } - TracyCZoneEnd(ctx2); } } - TracyCZoneEnd(ctx); + // The opaque batch has been consumed and invalidated + mesh_sys->opaque_batch = NULL; } void tb_register_shadow_sys(TbWorld *world) { @@ -409,9 +454,9 @@ void tb_register_shadow_sys(TbWorld *world) { tb_auto rnd_sys = ecs_singleton_get_mut(ecs, TbRenderSystem); tb_auto rp_sys = ecs_singleton_get_mut(ecs, TbRenderPipelineSystem); - tb_auto ro_sys = ecs_singleton_get_mut(ecs, TbRenderObjectSystem); tb_auto mesh_sys = ecs_singleton_get_mut(ecs, TbMeshSystem); tb_auto view_sys = ecs_singleton_get_mut(ecs, TbViewSystem); + (void)view_sys; TbShadowSystem sys = { .gp_alloc = world->gp_alloc, @@ -437,16 +482,18 @@ void tb_register_shadow_sys(TbWorld *world) { VkResult err = VK_SUCCESS; // Create pipeline layout { + tb_auto mesh_set_layout = tb_mesh_sys_get_set_layout(ecs); + VkPipelineLayoutCreateInfo create_info = { .sType = VK_STRUCTURE_TYPE_PIPELINE_LAYOUT_CREATE_INFO, .setLayoutCount = 5, .pSetLayouts = (VkDescriptorSetLayout[5]){ - view_sys->set_layout, + tb_view_sys_get_set_layout(ecs), mesh_sys->draw_set_layout, - ro_sys->set_layout, - mesh_sys->mesh_set_layout, - mesh_sys->mesh_set_layout, + tb_render_object_sys_get_set_layout(ecs), + mesh_set_layout, + mesh_set_layout, }, }; err = tb_rnd_create_pipeline_layout( diff --git a/source/simd.c b/source/tb_simd.c similarity index 99% rename from source/simd.c rename to source/tb_simd.c index 1940fd76..b2a92590 100644 --- a/source/simd.c +++ b/source/tb_simd.c @@ -1,4 +1,4 @@ -#include "simd.h" +#include "tb_simd.h" #include @@ -8,9 +8,9 @@ #include -#include "pi.h" -#include "profiling.h" -#include "tbgltf.h" +#include "tb_gltf.h" +#include "tb_pi.h" +#include "tb_profiling.h" #pragma clang diagnostic push #pragma clang diagnostic ignored "-Wmissing-braces" diff --git a/source/skycomponent.c b/source/tb_sky_component.c similarity index 59% rename from source/skycomponent.c rename to source/tb_sky_component.c index c831693b..af294346 100644 --- a/source/skycomponent.c +++ b/source/tb_sky_component.c @@ -1,21 +1,24 @@ -#include "skycomponent.h" +#include "tb_sky_component.h" -#include "lightcomponent.h" -#include "skysystem.h" -#include "world.h" +#include "tb_common.h" +#include "tb_light_component.h" +#include "tb_sky_system.h" +#include "tb_world.h" #include ECS_COMPONENT_DECLARE(TbSkyDescriptor); ECS_COMPONENT_DECLARE(TbSkyComponent); -bool tb_load_sky_comp(TbWorld *world, ecs_entity_t ent, const char *source_path, - const cgltf_node *node, json_object *object) { +bool tb_load_sky_comp(ecs_world_t *ecs, ecs_entity_t ent, + const char *source_path, const cgltf_data *data, + const cgltf_node *node, json_object *json) { (void)source_path; + (void)data; (void)node; TbSkyComponent sky = {0}; - json_object_object_foreach(object, key, value) { + json_object_object_foreach(json, key, value) { if (SDL_strcmp(key, "cirrus") == 0) { sky.cirrus = (float)json_object_get_double(value); } else if (SDL_strcmp(key, "cumulus") == 0) { @@ -23,12 +26,12 @@ bool tb_load_sky_comp(TbWorld *world, ecs_entity_t ent, const char *source_path, } } - ecs_set_ptr(world->ecs, ent, TbSkyComponent, &sky); + ecs_set_ptr(ecs, ent, TbSkyComponent, &sky); return true; } -ecs_entity_t tb_register_sky_comp(TbWorld *world) { +TbComponentRegisterResult tb_register_sky_comp(TbWorld *world) { ecs_world_t *ecs = world->ecs; ECS_COMPONENT_DEFINE(ecs, TbSkyDescriptor); ECS_COMPONENT_DEFINE(ecs, TbSkyComponent); @@ -45,7 +48,13 @@ ecs_entity_t tb_register_sky_comp(TbWorld *world) { {.name = "cumulus", .type = ecs_id(ecs_f32_t)}, }}); - return ecs_id(TbSkyDescriptor); + return (TbComponentRegisterResult){ecs_id(TbSkyComponent), + ecs_id(TbSkyDescriptor)}; +} + +bool tb_ready_sky_comp(ecs_world_t *ecs, ecs_entity_t ent) { + tb_auto sky = ecs_get(ecs, ent, TbSkyComponent); + return sky != NULL; } TB_REGISTER_COMP(tb, sky) diff --git a/source/skysystem.c b/source/tb_sky_system.c similarity index 84% rename from source/skysystem.c rename to source/tb_sky_system.c index b45b7ed4..463a22b0 100644 --- a/source/skysystem.c +++ b/source/tb_sky_system.c @@ -1,4 +1,4 @@ -#include "skysystem.h" +#include "tb_sky_system.h" // Ignore some warnings for the generated headers #pragma clang diagnostic push @@ -13,22 +13,22 @@ #include "sky_vert.h" #pragma clang diagnostic pop -#include "cameracomponent.h" #include "common.hlsli" #include "json.h" -#include "lightcomponent.h" -#include "profiling.h" -#include "renderpipelinesystem.h" -#include "rendersystem.h" -#include "rendertargetsystem.h" #include "sky.hlsli" -#include "skycomponent.h" #include "skydome.h" +#include "tb_camera_component.h" +#include "tb_common.h" +#include "tb_light_component.h" +#include "tb_profiling.h" +#include "tb_render_pipeline_system.h" +#include "tb_render_system.h" +#include "tb_render_target_system.h" #include "tb_shader_system.h" -#include "tbcommon.h" -#include "transformcomponent.h" -#include "viewsystem.h" -#include "world.h" +#include "tb_sky_component.h" +#include "tb_transform_component.h" +#include "tb_view_system.h" +#include "tb_world.h" #include #include @@ -36,12 +36,21 @@ #define FILTERED_ENV_DIM 512 #define FILTERED_ENV_MIPS ((uint32_t)(SDL_floorf(log2f(FILTERED_ENV_DIM))) + 1u) +typedef VkDescriptorBufferBindingInfoEXT TbSkyDescInfo; +ECS_COMPONENT_DECLARE(TbSkyDescInfo); + ECS_COMPONENT_DECLARE(TbSkySystem); +ECS_TAG_DECLARE(TbSkyRenderDirty); + typedef struct SkyDrawBatch { VkPushConstantRange const_range; TbSkyPushConstants consts; +#if TB_USE_DESC_BUFFER == 1 + VkDescriptorBufferBindingInfoEXT desc_binding; +#else VkDescriptorSet sky_set; +#endif VkBuffer geom_buffer; uint32_t index_count; @@ -49,7 +58,11 @@ typedef struct SkyDrawBatch { } SkyDrawBatch; typedef struct IrradianceBatch { +#if TB_USE_DESC_BUFFER == 1 + VkDescriptorBufferBindingInfoEXT desc_binding; +#else VkDescriptorSet set; +#endif VkBuffer geom_buffer; uint32_t index_count; @@ -58,7 +71,11 @@ typedef struct IrradianceBatch { typedef struct PrefilterBatch { TbEnvFilterConstants consts; +#if TB_USE_DESC_BUFFER == 1 + VkDescriptorBufferBindingInfoEXT desc_binding; +#else VkDescriptorSet set; +#endif VkBuffer geom_buffer; uint32_t index_count; @@ -110,6 +127,9 @@ VkPipeline create_sky_pipeline(void *args) { VkGraphicsPipelineCreateInfo create_info = { .sType = VK_STRUCTURE_TYPE_GRAPHICS_PIPELINE_CREATE_INFO, +#if TB_USE_DESC_BUFFER == 1 + .flags = VK_PIPELINE_CREATE_DESCRIPTOR_BUFFER_BIT_EXT, +#endif .pNext = &(VkPipelineRenderingCreateInfo){ .sType = VK_STRUCTURE_TYPE_PIPELINE_RENDERING_CREATE_INFO, @@ -241,6 +261,9 @@ VkPipeline create_env_capture_pipeline(void *args) { VkGraphicsPipelineCreateInfo create_info = { .sType = VK_STRUCTURE_TYPE_GRAPHICS_PIPELINE_CREATE_INFO, +#if TB_USE_DESC_BUFFER == 1 + .flags = VK_PIPELINE_CREATE_DESCRIPTOR_BUFFER_BIT_EXT, +#endif .pNext = &(VkPipelineRenderingCreateInfo){ .sType = VK_STRUCTURE_TYPE_PIPELINE_RENDERING_CREATE_INFO, @@ -375,6 +398,9 @@ VkPipeline create_irradiance_pipeline(void *args) { VkGraphicsPipelineCreateInfo create_info = { .sType = VK_STRUCTURE_TYPE_GRAPHICS_PIPELINE_CREATE_INFO, +#if TB_USE_DESC_BUFFER == 1 + .flags = VK_PIPELINE_CREATE_DESCRIPTOR_BUFFER_BIT_EXT, +#endif .pNext = &(VkPipelineRenderingCreateInfo){ .sType = VK_STRUCTURE_TYPE_PIPELINE_RENDERING_CREATE_INFO, @@ -509,6 +535,9 @@ VkPipeline create_prefilter_pipeline(void *args) { VkGraphicsPipelineCreateInfo create_info = { .sType = VK_STRUCTURE_TYPE_GRAPHICS_PIPELINE_CREATE_INFO, +#if TB_USE_DESC_BUFFER == 1 + .flags = VK_PIPELINE_CREATE_DESCRIPTOR_BUFFER_BIT_EXT, +#endif .pNext = &(VkPipelineRenderingCreateInfo){ .sType = VK_STRUCTURE_TYPE_PIPELINE_RENDERING_CREATE_INFO, @@ -632,8 +661,17 @@ void record_sky_common(VkCommandBuffer buffer, uint32_t batch_count, vkCmdPushConstants(buffer, batch->layout, range.stageFlags, range.offset, range.size, consts); +#if TB_USE_DESC_BUFFER == 1 + vkCmdBindDescriptorBuffersEXT(buffer, 1, &sky_batch->desc_binding); + uint32_t buf_indices[1] = {0}; + VkDeviceSize buf_offsets[1] = {0}; + vkCmdSetDescriptorBufferOffsetsEXT(buffer, VK_PIPELINE_BIND_POINT_GRAPHICS, + batch->layout, 0, 1, buf_indices, + buf_offsets); +#else vkCmdBindDescriptorSets(buffer, VK_PIPELINE_BIND_POINT_GRAPHICS, batch->layout, 0, 1, &sky_batch->sky_set, 0, NULL); +#endif vkCmdBindIndexBuffer(buffer, sky_batch->geom_buffer, 0, VK_INDEX_TYPE_UINT16); @@ -656,10 +694,18 @@ void record_sky(TracyCGPUContext *gpu_ctx, VkCommandBuffer buffer, TracyCZoneEnd(ctx); } +/* + What's with the commented out TracyCVkNamedZone calls? + Passes that use multi-view can occasionally step out of phase with the + Tracy query ringbuffer and some invalid number of bits will be requested + before the query count can wrap around back to 0 +*/ + void record_env_capture(TracyCGPUContext *gpu_ctx, VkCommandBuffer buffer, uint32_t batch_count, const TbDrawBatch *batches) { + (void)gpu_ctx; TracyCZoneNC(ctx, "Env Capture Record", TracyCategoryColorRendering, true); - TracyCVkNamedZone(gpu_ctx, frame_scope, buffer, "Env Capture", 3, true); + // TracyCVkNamedZone(gpu_ctx, frame_scope, buffer, "Env Capture", 3, true); cmd_begin_label(buffer, "Env Capture", (float4){0.4f, 0.0f, 0.8f, 1.0f}); record_sky_common(buffer, batch_count, batches); @@ -667,14 +713,15 @@ void record_env_capture(TracyCGPUContext *gpu_ctx, VkCommandBuffer buffer, // TODO: After capturing the environment map we need to mip it down cmd_end_label(buffer); - TracyCVkZoneEnd(frame_scope); + // TracyCVkZoneEnd(frame_scope); TracyCZoneEnd(ctx); } void record_irradiance(TracyCGPUContext *gpu_ctx, VkCommandBuffer buffer, uint32_t batch_count, const TbDrawBatch *batches) { + (void)gpu_ctx; TracyCZoneNC(ctx, "Irradiance Record", TracyCategoryColorRendering, true); - TracyCVkNamedZone(gpu_ctx, frame_scope, buffer, "Irradiance", 3, true); + // TracyCVkNamedZone(gpu_ctx, frame_scope, buffer, "Irradiance", 3, true); cmd_begin_label(buffer, "Irradiance", (float4){0.4f, 0.0f, 0.8f, 1.0f}); for (uint32_t i = 0; i < batch_count; ++i) { @@ -686,8 +733,17 @@ void record_irradiance(TracyCGPUContext *gpu_ctx, VkCommandBuffer buffer, vkCmdSetViewport(buffer, 0, 1, &batch->viewport); vkCmdSetScissor(buffer, 0, 1, &batch->scissor); +#if TB_USE_DESC_BUFFER == 1 + vkCmdBindDescriptorBuffersEXT(buffer, 1, &irr_batch->desc_binding); + uint32_t buf_indices[1] = {0}; + VkDeviceSize buf_offsets[1] = {0}; + vkCmdSetDescriptorBufferOffsetsEXT(buffer, VK_PIPELINE_BIND_POINT_GRAPHICS, + batch->layout, 0, 1, buf_indices, + buf_offsets); +#else vkCmdBindDescriptorSets(buffer, VK_PIPELINE_BIND_POINT_GRAPHICS, batch->layout, 0, 1, &irr_batch->set, 0, NULL); +#endif vkCmdBindIndexBuffer(buffer, irr_batch->geom_buffer, 0, VK_INDEX_TYPE_UINT16); @@ -697,14 +753,15 @@ void record_irradiance(TracyCGPUContext *gpu_ctx, VkCommandBuffer buffer, } cmd_end_label(buffer); - TracyCVkZoneEnd(frame_scope); + // TracyCVkZoneEnd(frame_scope); TracyCZoneEnd(ctx); } void record_env_filter(TracyCGPUContext *gpu_ctx, VkCommandBuffer buffer, uint32_t batch_count, const TbDrawBatch *batches) { + (void)gpu_ctx; TracyCZoneNC(ctx, "Env Filter Record", TracyCategoryColorRendering, true); - TracyCVkNamedZone(gpu_ctx, frame_scope, buffer, "Env Filter", 3, true); + // TracyCVkNamedZone(gpu_ctx, frame_scope, buffer, "Env Filter", 3, true); cmd_begin_label(buffer, "Env Filter", (float4){0.4f, 0.0f, 0.8f, 1.0f}); for (uint32_t i = 0; i < batch_count; ++i) { @@ -712,8 +769,17 @@ void record_env_filter(TracyCGPUContext *gpu_ctx, VkCommandBuffer buffer, const PrefilterBatch *pre_batch = (const PrefilterBatch *)batch->user_batch; vkCmdBindPipeline(buffer, VK_PIPELINE_BIND_POINT_GRAPHICS, batch->pipeline); +#if TB_USE_DESC_BUFFER == 1 + vkCmdBindDescriptorBuffersEXT(buffer, 1, &pre_batch->desc_binding); + uint32_t buf_indices[1] = {0}; + VkDeviceSize buf_offsets[1] = {0}; + vkCmdSetDescriptorBufferOffsetsEXT(buffer, VK_PIPELINE_BIND_POINT_GRAPHICS, + batch->layout, 0, 1, buf_indices, + buf_offsets); +#else vkCmdBindDescriptorSets(buffer, VK_PIPELINE_BIND_POINT_GRAPHICS, batch->layout, 0, 1, &pre_batch->set, 0, NULL); +#endif vkCmdBindIndexBuffer(buffer, pre_batch->geom_buffer, 0, VK_INDEX_TYPE_UINT16); @@ -730,7 +796,7 @@ void record_env_filter(TracyCGPUContext *gpu_ctx, VkCommandBuffer buffer, } cmd_end_label(buffer); - TracyCVkZoneEnd(frame_scope); + // TracyCVkZoneEnd(frame_scope); TracyCZoneEnd(ctx); } @@ -748,8 +814,6 @@ TbSkySystem create_sky_system(ecs_world_t *ecs, TbAllocator gp_alloc, .gp_alloc = gp_alloc, }; - VkResult err = VK_SUCCESS; - // Get passes TbRenderPassId sky_pass_id = rp_sys->sky_pass; TbRenderPassId irr_pass_id = rp_sys->irradiance_pass; @@ -768,30 +832,38 @@ TbSkySystem create_sky_system(ecs_world_t *ecs, TbAllocator gp_alloc, .maxLod = FILTERED_ENV_MIPS, .borderColor = VK_BORDER_COLOR_FLOAT_OPAQUE_BLACK, }; - err = tb_rnd_create_sampler(rnd_sys, &create_info, "Irradiance Sampler", - &sys.irradiance_sampler); - TB_VK_CHECK(err, "Failed to create irradiance sampler"); + tb_rnd_create_sampler(rnd_sys, &create_info, "Irradiance Sampler", + &sys.irradiance_sampler); } + VkFlags layout_flags = + VK_DESCRIPTOR_SET_LAYOUT_CREATE_UPDATE_AFTER_BIND_POOL_BIT; +#if TB_USE_DESC_BUFFER == 1 + layout_flags = VK_DESCRIPTOR_SET_LAYOUT_CREATE_DESCRIPTOR_BUFFER_BIT_EXT; +#endif + // Create Descriptor Set Layouts { VkDescriptorSetLayoutCreateInfo create_info = { .sType = VK_STRUCTURE_TYPE_DESCRIPTOR_SET_LAYOUT_CREATE_INFO, - .flags = VK_DESCRIPTOR_SET_LAYOUT_CREATE_UPDATE_AFTER_BIND_POOL_BIT, + .flags = layout_flags, .bindingCount = 1, .pBindings = &(VkDescriptorSetLayoutBinding){ 0, VK_DESCRIPTOR_TYPE_UNIFORM_BUFFER, 1, VK_SHADER_STAGE_FRAGMENT_BIT, NULL}, }; - err = tb_rnd_create_set_layout(rnd_sys, &create_info, "Sky Set Layout", - &sys.sky_set_layout); - TB_VK_CHECK(err, "Failed to create sky descriptor set layout"); + tb_rnd_create_set_layout(rnd_sys, &create_info, "Sky Set Layout", + &sys.sky_set_layout); +#if TB_USE_DESC_BUFFER == 1 + tb_create_descriptor_buffer(rnd_sys, sys.sky_set_layout, "Sky Desc Buffer", + 1, &sys.sky_desc_buffer); +#endif } { VkDescriptorSetLayoutCreateInfo create_info = { .sType = VK_STRUCTURE_TYPE_DESCRIPTOR_SET_LAYOUT_CREATE_INFO, - .flags = VK_DESCRIPTOR_SET_LAYOUT_CREATE_UPDATE_AFTER_BIND_POOL_BIT, + .flags = layout_flags, .bindingCount = 2, .pBindings = (VkDescriptorSetLayoutBinding[2]){ @@ -801,9 +873,13 @@ TbSkySystem create_sky_system(ecs_world_t *ecs, TbAllocator gp_alloc, &sys.irradiance_sampler}, }, }; - err = tb_rnd_create_set_layout( - rnd_sys, &create_info, "Irradiance Set Layout", &sys.irr_set_layout); - TB_VK_CHECK(err, "Failed to irradiance sky descriptor set layout"); + tb_rnd_create_set_layout(rnd_sys, &create_info, "Irradiance Set Layout", + &sys.irr_set_layout); +#if TB_USE_DESC_BUFFER == 1 + tb_create_descriptor_buffer(rnd_sys, sys.irr_set_layout, + "Irradiance Desc Buffer", 1, + &sys.irr_desc_buffer); +#endif } // Create Pipeline Layouts @@ -820,9 +896,8 @@ TbSkySystem create_sky_system(ecs_world_t *ecs, TbAllocator gp_alloc, sizeof(TbSkyPushConstants), }, }; - err = tb_rnd_create_pipeline_layout( - rnd_sys, &create_info, "Sky Pipeline Layout", &sys.sky_pipe_layout); - TB_VK_CHECK(err, "Failed to create sky pipeline layout"); + tb_rnd_create_pipeline_layout(rnd_sys, &create_info, "Sky Pipeline Layout", + &sys.sky_pipe_layout); } { VkPipelineLayoutCreateInfo create_info = { @@ -830,10 +905,9 @@ TbSkySystem create_sky_system(ecs_world_t *ecs, TbAllocator gp_alloc, .setLayoutCount = 1, .pSetLayouts = &sys.irr_set_layout, }; - err = tb_rnd_create_pipeline_layout(rnd_sys, &create_info, - "Irradiance Pipeline Layout", - &sys.irr_pipe_layout); - TB_VK_CHECK(err, "Failed to create irradiance pipeline layout"); + tb_rnd_create_pipeline_layout(rnd_sys, &create_info, + "Irradiance Pipeline Layout", + &sys.irr_pipe_layout); } { VkPipelineLayoutCreateInfo create_info = { @@ -847,10 +921,9 @@ TbSkySystem create_sky_system(ecs_world_t *ecs, TbAllocator gp_alloc, .stageFlags = VK_SHADER_STAGE_FRAGMENT_BIT, }, }; - err = tb_rnd_create_pipeline_layout(rnd_sys, &create_info, - "Prefilter Pipeline Layout", - &sys.prefilter_pipe_layout); - TB_VK_CHECK(err, "Failed to create prefilter pipeline layout"); + tb_rnd_create_pipeline_layout(rnd_sys, &create_info, + "Prefilter Pipeline Layout", + &sys.prefilter_pipe_layout); } // Look up target color and depth formats for pipeline creation @@ -984,13 +1057,13 @@ TbSkySystem create_sky_system(ecs_world_t *ecs, TbAllocator gp_alloc, .usage = VK_BUFFER_USAGE_TRANSFER_SRC_BIT | VK_BUFFER_USAGE_TRANSFER_DST_BIT | VK_BUFFER_USAGE_INDEX_BUFFER_BIT | - VK_BUFFER_USAGE_VERTEX_BUFFER_BIT, + VK_BUFFER_USAGE_VERTEX_BUFFER_BIT | + VK_BUFFER_USAGE_SHADER_DEVICE_ADDRESS_BIT, .size = skydome_size, }; - err = tb_rnd_sys_create_gpu_buffer_tmp( - rnd_sys, &create_info, "SkyDome Geom Buffer", - &sys.sky_geom_gpu_buffer, 16, &ptr); - TB_VK_CHECK(err, "Failed to create skydome geom buffer"); + tb_rnd_sys_create_gpu_buffer_tmp(rnd_sys, &create_info, + "SkyDome Geom Buffer", + &sys.sky_geom_gpu_buffer, 16, &ptr); } copy_skydome(ptr); } @@ -1002,10 +1075,6 @@ TbSkySystem create_sky_system(ecs_world_t *ecs, TbAllocator gp_alloc, void destroy_sky_system(ecs_world_t *ecs, TbSkySystem *self) { TbRenderSystem *rnd_sys = self->rnd_sys; - for (uint32_t i = 0; i < TB_MAX_FRAME_STATES; ++i) { - tb_rnd_destroy_descriptor_pool(rnd_sys, self->frame_states[i].set_pool); - } - vmaDestroyBuffer(rnd_sys->vma_alloc, self->sky_geom_gpu_buffer.buffer, self->sky_geom_gpu_buffer.alloc); @@ -1023,7 +1092,7 @@ void destroy_sky_system(ecs_world_t *ecs, TbSkySystem *self) { *self = (TbSkySystem){0}; } -void sky_draw_tick(ecs_iter_t *it) { +void tb_sky_draw_tick(ecs_iter_t *it) { TracyCZoneNC(ctx, "Sky Draw", TracyCategoryColorCore, true); ecs_world_t *ecs = it->world; @@ -1037,12 +1106,6 @@ void sky_draw_tick(ecs_iter_t *it) { tb_auto rp_sys = ecs_singleton_get_mut(ecs, TbRenderPipelineSystem); ecs_singleton_modified(ecs, TbRenderPipelineSystem); - // TODO: Make this less hacky - const uint32_t width = rnd_sys->render_thread->swapchain.width; - const uint32_t height = rnd_sys->render_thread->swapchain.height; - - tb_auto state = &sky_sys->frame_states[rnd_sys->frame_idx]; - // Early out if any shaders aren't compiled yet if (!tb_is_shader_ready(ecs, sky_sys->sky_shader) || !tb_is_shader_ready(ecs, sky_sys->env_shader) || @@ -1052,6 +1115,14 @@ void sky_draw_tick(ecs_iter_t *it) { return; } +#if TB_USE_DESC_BUFFER == 1 + // Reset descriptor buffers + { + tb_reset_descriptor_buffer(rnd_sys, &sky_sys->sky_desc_buffer); + tb_reset_descriptor_buffer(rnd_sys, &sky_sys->irr_desc_buffer); + } +#endif + // Write descriptor sets for each sky tb_auto skys = ecs_field(it, TbSkyComponent, 1); tb_auto trans = ecs_field(it, TbTransformComponent, 2); @@ -1062,64 +1133,36 @@ void sky_draw_tick(ecs_iter_t *it) { float3 sun_dir = -tb_transform_get_forward(&transform->transform); VkResult err = VK_SUCCESS; - VkBuffer tmp_gpu_buffer = tb_rnd_get_gpu_tmp_buffer(rnd_sys); +#if TB_USE_DESC_BUFFER == 0 const uint32_t write_count = 2; // +1 for irradiance pass - - // Allocate all the descriptor sets for this frame { - // Resize the pool - if (state->set_count < write_count) { - if (state->set_pool) { - tb_rnd_destroy_descriptor_pool(rnd_sys, state->set_pool); - } - - VkDescriptorPoolCreateInfo create_info = { - .sType = VK_STRUCTURE_TYPE_DESCRIPTOR_POOL_CREATE_INFO, - .maxSets = write_count, - .poolSizeCount = 2, - .pPoolSizes = - (VkDescriptorPoolSize[2]){ - { - .descriptorCount = write_count, - .type = VK_DESCRIPTOR_TYPE_UNIFORM_BUFFER, - }, - { - .descriptorCount = write_count, - .type = VK_DESCRIPTOR_TYPE_SAMPLED_IMAGE, - }, - }, - .flags = VK_DESCRIPTOR_POOL_CREATE_UPDATE_AFTER_BIND_BIT, - }; - err = tb_rnd_create_descriptor_pool( - rnd_sys, &create_info, "Sky System Frame State Descriptor Pool", - &state->set_pool); - TB_VK_CHECK(err, - "Failed to create sky system frame state descriptor pool"); - - state->set_count = write_count; - state->sets = tb_realloc_nm_tp(sky_sys->gp_alloc, state->sets, - state->set_count, VkDescriptorSet); - } else { - vkResetDescriptorPool(rnd_sys->render_thread->device, state->set_pool, - 0); - state->set_count = write_count; - } + VkDescriptorPoolCreateInfo create_info = { + .sType = VK_STRUCTURE_TYPE_DESCRIPTOR_POOL_CREATE_INFO, + .maxSets = write_count, + .poolSizeCount = 2, + .pPoolSizes = + (VkDescriptorPoolSize[2]){ + { + .descriptorCount = write_count, + .type = VK_DESCRIPTOR_TYPE_UNIFORM_BUFFER, + }, + { + .descriptorCount = write_count, + .type = VK_DESCRIPTOR_TYPE_SAMPLED_IMAGE, + }, + }, + .flags = VK_DESCRIPTOR_POOL_CREATE_UPDATE_AFTER_BIND_BIT, + }; tb_auto layouts = tb_alloc_nm_tp(sky_sys->tmp_alloc, write_count, VkDescriptorSetLayout); layouts[0] = sky_sys->sky_set_layout; layouts[1] = sky_sys->irr_set_layout; - VkDescriptorSetAllocateInfo alloc_info = { - .sType = VK_STRUCTURE_TYPE_DESCRIPTOR_SET_ALLOCATE_INFO, - .descriptorSetCount = state->set_count, - .descriptorPool = state->set_pool, - .pSetLayouts = layouts, - }; - err = vkAllocateDescriptorSets(rnd_sys->render_thread->device, - &alloc_info, state->sets); - TB_VK_CHECK(err, "Failed to re-allocate sky descriptor sets"); + tb_rnd_frame_desc_pool_tick(rnd_sys, "sky", &create_info, layouts, NULL, + sky_sys->pools.pools, write_count, + write_count); } // Just upload and write all views for now, they tend to be important anyway @@ -1127,7 +1170,7 @@ void sky_draw_tick(ecs_iter_t *it) { tb_alloc_nm_tp(sky_sys->tmp_alloc, write_count, VkWriteDescriptorSet); tb_auto buffer_info = tb_alloc_tp(sky_sys->tmp_alloc, VkDescriptorBufferInfo); - +#endif TbSkyData data = { .time = (float)world->time, .cirrus = sky->cirrus, @@ -1141,8 +1184,34 @@ void sky_draw_tick(ecs_iter_t *it) { &offset); TB_VK_CHECK(err, "Failed to make tmp host buffer allocation for sky"); + tb_auto env_map_view = tb_render_target_get_view( + sky_sys->rt_sys, rnd_sys->frame_idx, sky_sys->rt_sys->env_cube); + +#if TB_USE_DESC_BUFFER == 1 + tb_auto tmp_gpu_buf_addr = tb_rnd_get_gpu_tmp_addr(rnd_sys); + TbDescriptor sky_desc = { + .type = VK_DESCRIPTOR_TYPE_UNIFORM_BUFFER, + .data.pUniformBuffer = + &(VkDescriptorAddressInfoEXT){ + .sType = VK_STRUCTURE_TYPE_DESCRIPTOR_ADDRESS_INFO_EXT, + .address = tmp_gpu_buf_addr + offset, + .range = sizeof(TbSkyData), + }, + }; + tb_write_desc_to_buffer(rnd_sys, &sky_sys->sky_desc_buffer, 0, &sky_desc); + + TbDescriptor irr_desc = { + .type = VK_DESCRIPTOR_TYPE_SAMPLED_IMAGE, + .data.pSampledImage = + &(VkDescriptorImageInfo){ + .imageLayout = VK_IMAGE_LAYOUT_READ_ONLY_OPTIMAL, + .imageView = env_map_view, + }, + }; + tb_write_desc_to_buffer(rnd_sys, &sky_sys->irr_desc_buffer, 0, &irr_desc); +#else *buffer_info = (VkDescriptorBufferInfo){ - .buffer = tmp_gpu_buffer, + .buffer = tb_rnd_get_gpu_tmp_buffer(rnd_sys), .offset = offset, .range = sizeof(TbSkyData), }; @@ -1150,7 +1219,8 @@ void sky_draw_tick(ecs_iter_t *it) { // Construct a write descriptor writes[0] = (VkWriteDescriptorSet){ .sType = VK_STRUCTURE_TYPE_WRITE_DESCRIPTOR_SET, - .dstSet = state->sets[0], + .dstSet = + tb_rnd_frame_desc_pool_get_set(rnd_sys, sky_sys->pools.pools, 0), .dstBinding = 0, .dstArrayElement = 0, .descriptorCount = 1, @@ -1159,11 +1229,10 @@ void sky_draw_tick(ecs_iter_t *it) { }; // Last write is for the irradiance pass - tb_auto env_map_view = tb_render_target_get_view( - sky_sys->rt_sys, rnd_sys->frame_idx, sky_sys->rt_sys->env_cube); writes[1] = (VkWriteDescriptorSet){ .sType = VK_STRUCTURE_TYPE_WRITE_DESCRIPTOR_SET, - .dstSet = state->sets[1], + .dstSet = + tb_rnd_frame_desc_pool_get_set(rnd_sys, sky_sys->pools.pools, 1), .dstBinding = 0, .dstArrayElement = 0, .descriptorCount = 1, @@ -1175,6 +1244,7 @@ void sky_draw_tick(ecs_iter_t *it) { }, }; tb_rnd_update_descriptors(rnd_sys, write_count, writes); +#endif } ecs_iter_t cam_it = ecs_query_iter(ecs, sky_sys->camera_query); @@ -1185,6 +1255,23 @@ void sky_draw_tick(ecs_iter_t *it) { tb_auto camera = &cameras[cam_idx]; tb_auto transform = &transforms[cam_idx]; +#if TB_USE_DESC_BUFFER == 1 + tb_auto view_addr = tb_view_sys_get_table_addr(ecs, camera->view_id); + if (view_addr.address == 0) { + continue; + } +#else + tb_auto view_set = + tb_view_system_get_descriptor(sky_sys->view_sys, camera->view_id); + // Skip camera if view set isn't ready + if (view_set == VK_NULL_HANDLE) { + continue; + } +#endif + + const uint32_t width = camera->width; + const uint32_t height = camera->height; + // Need to manually calculate this here float4x4 vp = {.col0 = {0}}; { @@ -1207,7 +1294,12 @@ void sky_draw_tick(ecs_iter_t *it) { { .vp = vp, }, - .sky_set = state->sets[0], +#if TB_USE_DESC_BUFFER == 1 + .desc_binding = tb_desc_buff_get_binding(&sky_sys->sky_desc_buffer), +#else + .sky_set = tb_rnd_frame_desc_pool_get_set(rnd_sys, + sky_sys->pools.pools, 0), +#endif .geom_buffer = sky_sys->sky_geom_gpu_buffer.buffer, .index_count = get_skydome_index_count(), .vertex_offset = get_skydome_vert_offset(), @@ -1252,7 +1344,13 @@ void sky_draw_tick(ecs_iter_t *it) { .user_batch = irradiance_batch, }; *irradiance_batch = (IrradianceBatch){ - .set = state->sets[state->set_count - 1], +#if TB_USE_DESC_BUFFER == 1 + .desc_binding = + tb_desc_buff_get_binding(&sky_sys->irr_desc_buffer), +#else + .set = tb_rnd_frame_desc_pool_get_set(rnd_sys, + sky_sys->pools.pools, 1), +#endif .geom_buffer = sky_sys->sky_geom_gpu_buffer.buffer, .index_count = get_skydome_index_count(), .vertex_offset = get_skydome_vert_offset(), @@ -1278,7 +1376,13 @@ void sky_draw_tick(ecs_iter_t *it) { }; prefilter_batches[i] = (PrefilterBatch){ - .set = state->sets[state->set_count - 1], +#if TB_USE_DESC_BUFFER == 1 + .desc_binding = + tb_desc_buff_get_binding(&sky_sys->irr_desc_buffer), +#else + .set = tb_rnd_frame_desc_pool_get_set(rnd_sys, + sky_sys->pools.pools, 1), +#endif .geom_buffer = sky_sys->sky_geom_gpu_buffer.buffer, .index_count = get_skydome_index_count(), .vertex_offset = get_skydome_vert_offset(), @@ -1312,6 +1416,7 @@ void tb_register_sky_sys(TbWorld *world) { ecs_world_t *ecs = world->ecs; ECS_COMPONENT_DEFINE(ecs, TbSkySystem); + ECS_TAG_DEFINE(ecs, TbSkyRenderDirty); tb_auto rnd_sys = ecs_singleton_get_mut(ecs, TbRenderSystem); tb_auto rp_sys = ecs_singleton_get_mut(ecs, TbRenderPipelineSystem); @@ -1328,7 +1433,7 @@ void tb_register_sky_sys(TbWorld *world) { // Sets a singleton by ptr ecs_set_ptr(ecs, ecs_id(TbSkySystem), TbSkySystem, &sys); - ECS_SYSTEM(ecs, sky_draw_tick, + ECS_SYSTEM(ecs, tb_sky_draw_tick, EcsOnStore, [inout] TbSkyComponent, [in] TbTransformComponent); TracyCZoneEnd(ctx); diff --git a/source/spatial_audio_comp.c b/source/tb_spatial_audio_comp.c similarity index 70% rename from source/spatial_audio_comp.c rename to source/tb_spatial_audio_comp.c index 4656acba..8ee8067d 100644 --- a/source/spatial_audio_comp.c +++ b/source/tb_spatial_audio_comp.c @@ -1,7 +1,7 @@ #include "tb_spatial_audio_comp.h" -#include "tbcommon.h" -#include "world.h" +#include "tb_common.h" +#include "tb_world.h" #include #include @@ -19,16 +19,20 @@ typedef struct TbSpatialAudioSource { ECS_COMPONENT_DECLARE(TbSpatialAudioSourceDesc); ECS_COMPONENT_DECLARE(TbSpatialAudioSource); -bool tb_load_spatial_audio_source_comp(TbWorld *world, ecs_entity_t ent, +bool tb_load_spatial_audio_source_comp(ecs_world_t *ecs, ecs_entity_t ent, const char *source_path, + const cgltf_data *data, const cgltf_node *node, json_object *json) { (void)source_path; + (void)data; (void)node; char *file_path = NULL; TbSpatialAudioSource comp = {0}; + tb_auto world = ecs_singleton_get(ecs, TbWorldRef)->world; + json_object_object_foreach(json, key, value) { // Determine file path if (SDL_strcmp(key, "file_path") == 0) { @@ -42,11 +46,12 @@ bool tb_load_spatial_audio_source_comp(TbWorld *world, ecs_entity_t ent, // TODO: Start task to load the audio file - ecs_set_ptr(world->ecs, ent, TbSpatialAudioSource, &comp); + ecs_set_ptr(ecs, ent, TbSpatialAudioSource, &comp); return true; } -ecs_entity_t tb_register_spatial_audio_source_comp(TbWorld *world) { +TbComponentRegisterResult +tb_register_spatial_audio_source_comp(TbWorld *world) { tb_auto ecs = world->ecs; ECS_COMPONENT_DEFINE(ecs, TbSpatialAudioSource); ECS_COMPONENT_DEFINE(ecs, TbSpatialAudioSourceDesc); @@ -56,7 +61,13 @@ ecs_entity_t tb_register_spatial_audio_source_comp(TbWorld *world) { {.name = "file_path", .type = ecs_id(ecs_string_t)}, }}); - return ecs_id(TbSpatialAudioSourceDesc); + return (TbComponentRegisterResult){ecs_id(TbSpatialAudioSource), + ecs_id(TbSpatialAudioSourceDesc)}; +} + +bool tb_ready_spatial_audio_source_comp(ecs_world_t *ecs, ecs_entity_t ent) { + tb_auto comp = ecs_get(ecs, ent, TbSpatialAudioSource); + return comp != NULL; } TB_REGISTER_COMP(tb, spatial_audio_source) diff --git a/source/spatial_audio_system.c b/source/tb_spatial_audio_system.c similarity index 95% rename from source/spatial_audio_system.c rename to source/tb_spatial_audio_system.c index b033fe73..2397ee5b 100644 --- a/source/spatial_audio_system.c +++ b/source/tb_spatial_audio_system.c @@ -1,12 +1,12 @@ #include -#include "audiosystem.h" -#include "profiling.h" -#include "tbcommon.h" -#include "tblog.h" -#include "tbsystempriority.h" -#include "world.h" +#include "tb_audio_system.h" +#include "tb_common.h" +#include "tb_log.h" +#include "tb_profiling.h" +#include "tb_system_priority.h" +#include "tb_world.h" typedef struct TbSpatialAudioSystem { IPLContext ctx; diff --git a/source/task_scheduler.c b/source/tb_task_scheduler.c similarity index 92% rename from source/task_scheduler.c rename to source/tb_task_scheduler.c index e4c52453..33d19a59 100644 --- a/source/task_scheduler.c +++ b/source/tb_task_scheduler.c @@ -1,20 +1,20 @@ #include "tb_task_scheduler.h" -#include "tbcommon.h" -#include "tbsystempriority.h" -#include "world.h" +#include "tb_common.h" +#include "tb_system_priority.h" +#include "tb_world.h" #include void *tb_ts_alloc(size_t size) { - void *ptr = mi_malloc(size); - TracyCAllocN(ptr, size, "Task Alloc"); + void *ptr = tb_alloc(tb_global_alloc, size); + // TracyCAllocN(ptr, size, "Task Alloc"); return ptr; } void tb_ts_free(void *ptr) { - mi_free(ptr); - TracyCFreeN(ptr, "Task Alloc"); + tb_free(tb_global_alloc, ptr); + // TracyCFreeN(ptr, "Task Alloc"); } #define tb_ts_alloc_tp(T) (T *)tb_ts_alloc(sizeof(T)) @@ -171,9 +171,9 @@ void tb_launch_pinned_task_args(enkiTaskScheduler *enki, enkiPinnedTask *task, enkiAddPinnedTaskArgs(enki, task, task_args); } -void tb_run_pinned_tasks_sys(ecs_iter_t *it) { - TracyCZoneNC(ctx, "Run Pinned Tasks Sys", TracyCategoryColorCore, true); - tb_auto enki = *ecs_field(it, TbTaskScheduler, 1); +void tb_run_pinned_tasks(ecs_world_t *ecs) { + TracyCZoneNC(ctx, "Run Pinned Tasks", TracyCategoryColorCore, true); + tb_auto enki = *ecs_singleton_get(ecs, TbTaskScheduler); enkiRunPinnedTasks(enki); TracyCZoneEnd(ctx); } @@ -205,8 +205,6 @@ void tb_register_task_scheduler_sys(TbWorld *world) { ecs_singleton_set(world->ecs, TbTaskScheduler, {enki}); - ECS_SYSTEM(world->ecs, tb_run_pinned_tasks_sys, - EcsPostLoad, [inout] TbTaskScheduler(TbTaskScheduler)); TracyCZoneEnd(ctx); } diff --git a/source/tb_texture_system.c b/source/tb_texture_system.c new file mode 100644 index 00000000..08c7cb45 --- /dev/null +++ b/source/tb_texture_system.c @@ -0,0 +1,1100 @@ +#include "tb_texture_system.h" + +#include "tb_assets.h" +#include "tb_common.h" +#include "tb_descriptor_buffer.h" +#include "tb_dyn_desc_pool.h" +#include "tb_gltf.h" +#include "tb_ktx.h" +#include "tb_queue.h" +#include "tb_task_scheduler.h" +#include "tb_world.h" + +static const int32_t TbMaxParallelTextureLoads = 128; +static SDL_AtomicInt tb_parallel_tex_load_count = {0}; + +ECS_COMPONENT_DECLARE(TbTextureUsage); + +typedef struct TbTextureCtx { + VkDescriptorSetLayout set_layout; + uint32_t owned_tex_count; + TbDynDescPool desc_pool; + + TbDescriptorBuffer desc_buffer; + + TbTexture default_color_tex; + TbTexture default_normal_tex; + TbTexture default_metal_rough_tex; + TbTexture brdf_tex; +} TbTextureCtx; +ECS_COMPONENT_DECLARE(TbTextureCtx); + +typedef struct TbTextureImage { + uint32_t ref_count; + TbHostBuffer host_buffer; + TbImage gpu_image; + VkImageView image_view; +} TbTextureImage; +ECS_COMPONENT_DECLARE(TbTextureImage); + +ECS_COMPONENT_DECLARE(TbTextureComponent); + +// Describes the creation of a texture that lives in a GLB file +typedef struct TbTextureGLTFLoadRequest { + const cgltf_data *data; + const char *mat_name; +} TbTextureGLTFLoadRequest; +ECS_COMPONENT_DECLARE(TbTextureGLTFLoadRequest); + +typedef struct TbTextureKTXLoadRequest { + const char *path; + const char *name; +} TbTextureKTXLoadRequest; +ECS_COMPONENT_DECLARE(TbTextureKTXLoadRequest); + +typedef struct TbTextureRawLoadRequest { + const char *name; + const uint8_t *pixels; + uint64_t size; + uint32_t width; + uint32_t height; +} TbTextureRawLoadRequest; +ECS_COMPONENT_DECLARE(TbTextureRawLoadRequest); + +ECS_TAG_DECLARE(TbTextureLoaded); + +// Internals + +typedef struct KTX2IterData { + VkBuffer buffer; + VkImage image; + TbBufferImageCopy *uploads; + uint64_t offset; +} KTX2IterData; + +ktx_error_code_e iterate_ktx2_levels(int32_t mip_level, int32_t face, + int32_t width, int32_t height, + int32_t depth, uint64_t face_lod_size, + void *pixels, void *userdata) { + (void)pixels; + KTX2IterData *user_data = (KTX2IterData *)userdata; + + user_data->uploads[mip_level] = (TbBufferImageCopy){ + .src = user_data->buffer, + .dst = user_data->image, + .region = + { + .bufferOffset = user_data->offset, + .imageSubresource = + { + .aspectMask = VK_IMAGE_ASPECT_COLOR_BIT, + .layerCount = 1, + .baseArrayLayer = face, + .mipLevel = mip_level, + }, + .imageExtent = + { + .width = width, + .height = height, + .depth = depth, + }, + }, + .range = + { + .aspectMask = VK_IMAGE_ASPECT_COLOR_BIT, + .baseArrayLayer = face, + .baseMipLevel = mip_level, + .layerCount = 1, + .levelCount = 1, + }, + }; + + user_data->offset += face_lod_size; + return KTX_SUCCESS; +} + +VkImageType get_ktx2_image_type(const ktxTexture2 *t) { + return (VkImageType)(t->numDimensions - 1); +} + +VkImageViewType get_ktx2_image_view_type(const ktxTexture2 *t) { + const VkImageType img_type = get_ktx2_image_type(t); + + const bool cube = t->isCubemap; + const bool array = t->isArray; + + if (img_type == VK_IMAGE_TYPE_1D) { + if (array) { + return VK_IMAGE_VIEW_TYPE_1D_ARRAY; + } else { + return VK_IMAGE_VIEW_TYPE_1D; + } + } else if (img_type == VK_IMAGE_TYPE_2D) { + if (array) { + return VK_IMAGE_VIEW_TYPE_2D_ARRAY; + } else { + return VK_IMAGE_VIEW_TYPE_2D; + } + + } else if (img_type == VK_IMAGE_TYPE_3D) { + // No such thing as a 3D array + return VK_IMAGE_VIEW_TYPE_3D; + } else if (cube) { + if (array) { + return VK_IMAGE_VIEW_TYPE_CUBE_ARRAY; + } + return VK_IMAGE_VIEW_TYPE_CUBE; + } + + TB_CHECK(false, "Invalid"); + return VK_IMAGE_VIEW_TYPE_MAX_ENUM; +} + +TbTextureImage tb_load_ktx_image(TbRenderSystem *rnd_sys, const char *name, + ktxTexture2 *ktx) { + TB_TRACY_SCOPE("Load KTX Texture"); + bool needs_transcoding = ktxTexture2_NeedsTranscoding(ktx); + if (needs_transcoding) { + TB_TRACY_SCOPE("KTX Basis Transcode"); + // TODO: pre-calculate the best format for the platform + ktx_error_code_e err = ktxTexture2_TranscodeBasis(ktx, KTX_TTF_BC7_RGBA, 0); + TB_CHECK(err == KTX_SUCCESS, "Failed to transcode basis texture"); + } + + size_t host_buffer_size = ktx->dataSize; + uint32_t width = ktx->baseWidth; + uint32_t height = ktx->baseHeight; + uint32_t depth = ktx->baseDepth; + uint32_t layers = ktx->numLayers; + uint32_t mip_levels = ktx->numLevels; + VkFormat format = (VkFormat)ktx->vkFormat; + + TB_CHECK(ktx->generateMipmaps == false, + "Not expecting to have to generate mips"); + + TbTextureImage texture = {0}; + + // Allocate gpu image + { + VkImageCreateInfo create_info = { + .sType = VK_STRUCTURE_TYPE_IMAGE_CREATE_INFO, + .arrayLayers = layers, + .extent = + (VkExtent3D){ + .width = width, + .height = height, + .depth = depth, + }, + .format = format, + .imageType = get_ktx2_image_type(ktx), + .mipLevels = mip_levels, + .samples = VK_SAMPLE_COUNT_1_BIT, + .usage = VK_IMAGE_USAGE_TRANSFER_DST_BIT | VK_IMAGE_USAGE_SAMPLED_BIT, + }; + tb_rnd_sys_create_gpu_image(rnd_sys, ktx->pData, host_buffer_size, + &create_info, name, &texture.gpu_image, + &texture.host_buffer); + } + + // Create image view + { + VkImageViewCreateInfo create_info = { + .sType = VK_STRUCTURE_TYPE_IMAGE_VIEW_CREATE_INFO, + .image = texture.gpu_image.image, + .viewType = get_ktx2_image_view_type(ktx), + .format = format, + .components = + { + VK_COMPONENT_SWIZZLE_R, + VK_COMPONENT_SWIZZLE_G, + VK_COMPONENT_SWIZZLE_B, + VK_COMPONENT_SWIZZLE_A, + }, + .subresourceRange = + { + VK_IMAGE_ASPECT_COLOR_BIT, + 0, + mip_levels, + 0, + layers, + }, + }; + char view_name[100] = {0}; + SDL_snprintf(view_name, 100, "%s Image TbView", name); // NOLINT + tb_rnd_create_image_view(rnd_sys, &create_info, view_name, + &texture.image_view); + } + + // Issue uploads + { + TbBufferImageCopy *uploads = + tb_alloc_nm_tp(rnd_sys->tmp_alloc, mip_levels, TbBufferImageCopy); + + KTX2IterData iter_data = { + .buffer = texture.host_buffer.buffer, + .image = texture.gpu_image.image, + .offset = texture.host_buffer.offset, + .uploads = uploads, + }; + +#pragma clang diagnostic push +#pragma clang diagnostic ignored "-Wincompatible-pointer-types" + // Iterate over texture levels to fill out upload requests + ktxTexture_IterateLevels(ktx, iterate_ktx2_levels, &iter_data); +#pragma clang diagnostic pop + + // Will handle transitioning the image's layout to shader read only + tb_rnd_upload_buffer_to_image(rnd_sys, uploads, mip_levels); + } + + return texture; +} + +TbTextureImage tb_load_gltf_texture(TbRenderSystem *rnd_sys, const char *name, + const cgltf_texture *texture) { + TB_TRACY_SCOPE("Load GLTF Texture"); + TbTextureImage tex = {0}; + + if (texture->has_basisu) { + const cgltf_image *image = texture->basisu_image; + + const cgltf_buffer_view *image_view = image->buffer_view; + TB_CHECK(image->buffer_view->buffer->uri == NULL, + "Not setup to load data from uri"); + const cgltf_buffer *image_data = image_view->buffer; + + // Points to some jpg/png whatever image format data + uint8_t *raw_data = (uint8_t *)(image_data->data) + image_view->offset; + const int32_t raw_size = (int32_t)image_view->size; + + // Parse the ktx texture + ktxTexture2 *ktx = NULL; + { + ktxTextureCreateFlags flags = KTX_TEXTURE_CREATE_LOAD_IMAGE_DATA_BIT; + + ktx_error_code_e err = + ktxTexture2_CreateFromMemory(raw_data, raw_size, flags, &ktx); + TB_CHECK(err == KTX_SUCCESS, "Failed to create KTX texture from memory"); + } + tex = tb_load_ktx_image(rnd_sys, name, ktx); + } else { + TB_CHECK(false, "Uncompressed texture loading not implemented"); + } + + return tex; +} + +TbTextureImage tb_load_raw_image(TbRenderSystem *rnd_sys, const char *name, + const uint8_t *pixels, uint64_t size, + uint32_t width, uint32_t height, + TbTextureUsage usage) { + TB_TRACY_SCOPE("Load Raw Texture"); + + TbTextureImage texture = {0}; + + // Determine some parameters + const uint32_t depth = 1; + const uint32_t layers = 1; + const uint32_t mip_levels = 1; + VkFormat format = VK_FORMAT_R8G8B8A8_UNORM; + if (usage == TB_TEX_USAGE_COLOR) { + format = VK_FORMAT_R8G8B8A8_SRGB; + } else if (usage == TB_TEX_USAGE_BRDF) { + format = VK_FORMAT_R32G32_SFLOAT; + } else if (usage == TB_TEX_USAGE_UNKNOWN) { + TB_CHECK(false, "Unexpected texture usage"); + } + + // Allocate gpu image + { + VkImageCreateInfo create_info = { + .sType = VK_STRUCTURE_TYPE_IMAGE_CREATE_INFO, + .arrayLayers = layers, + .extent = + (VkExtent3D){ + .width = width, + .height = height, + .depth = depth, + }, + .format = format, + .imageType = VK_IMAGE_TYPE_2D, + .mipLevels = mip_levels, + .samples = VK_SAMPLE_COUNT_1_BIT, + .usage = VK_IMAGE_USAGE_TRANSFER_DST_BIT | VK_IMAGE_USAGE_SAMPLED_BIT, + }; + tb_rnd_sys_create_gpu_image(rnd_sys, pixels, size, &create_info, name, + &texture.gpu_image, &texture.host_buffer); + } + + // Create image view + { + VkImageViewCreateInfo create_info = { + .sType = VK_STRUCTURE_TYPE_IMAGE_VIEW_CREATE_INFO, + .image = texture.gpu_image.image, + .viewType = VK_IMAGE_VIEW_TYPE_2D, + .format = format, + .components = + { + VK_COMPONENT_SWIZZLE_R, + VK_COMPONENT_SWIZZLE_G, + VK_COMPONENT_SWIZZLE_B, + VK_COMPONENT_SWIZZLE_A, + }, + .subresourceRange = + { + VK_IMAGE_ASPECT_COLOR_BIT, + 0, + mip_levels, + 0, + layers, + }, + }; + char view_name[100] = {0}; + SDL_snprintf(view_name, 100, "%s Image TbView", name); // NOLINT + tb_rnd_create_image_view(rnd_sys, &create_info, view_name, + &texture.image_view); + } + + // Issue uploads + { + TbBufferImageCopy *uploads = + tb_alloc_nm_tp(rnd_sys->tmp_alloc, mip_levels, TbBufferImageCopy); + + TB_CHECK(mip_levels == 1, "Only expecting one mip level"); + uploads[0] = (TbBufferImageCopy){ + .src = texture.host_buffer.buffer, + .dst = texture.gpu_image.image, + .region = + { + .bufferOffset = texture.host_buffer.offset, + .imageSubresource = + { + .aspectMask = VK_IMAGE_ASPECT_COLOR_BIT, + .layerCount = 1, + }, + .imageExtent = + { + .width = width, + .height = height, + .depth = 1, + }, + }, + .range = + { + .aspectMask = VK_IMAGE_ASPECT_COLOR_BIT, + .baseArrayLayer = 0, + .baseMipLevel = 0, + .layerCount = 1, + .levelCount = 1, + }, + }; + + // Will handle transitioning the image's layout to shader read only + tb_rnd_upload_buffer_to_image(rnd_sys, uploads, mip_levels); + } + + return texture; +} + +typedef struct TbTextureLoadedArgs { + ecs_world_t *ecs; + TbTexture tex; + TbTextureImage comp; +} TbTextureLoadedArgs; + +void tb_texture_loaded(const void *args) { + tb_auto loaded_args = (const TbTextureLoadedArgs *)args; + tb_auto ecs = loaded_args->ecs; + tb_auto tex = loaded_args->tex; + if (tex != 0) { + SDL_AtomicDecRef(&tb_parallel_tex_load_count); + ecs_add(ecs, tex, TbTextureLoaded); + ecs_set_ptr(ecs, tex, TbTextureImage, &loaded_args->comp); + } else { + TB_CHECK(false, "Texture load failed. Do we need to retry?"); + } +} + +typedef struct TbLoadCommonTextureArgs { + ecs_world_t *ecs; + TbTexture tex; + TbTaskScheduler enki; + TbRenderSystem *rnd_sys; + TbPinnedTask loaded_task; + TbTextureUsage usage; +} TbLoadCommonTextureArgs; + +typedef struct TbLoadGLTFTexture2Args { + TbLoadCommonTextureArgs common; + TbTextureGLTFLoadRequest gltf; +} TbLoadGLTFTexture2Args; + +void tb_load_gltf_texture_task(const void *args) { + TB_TRACY_SCOPE("Load GLTF Texture Task"); + tb_auto load_args = (const TbLoadGLTFTexture2Args *)args; + TbTexture tex = load_args->common.tex; + tb_auto rnd_sys = load_args->common.rnd_sys; + tb_auto usage = load_args->common.usage; + + tb_auto data = load_args->gltf.data; + tb_auto mat_name = load_args->gltf.mat_name; + + // Find material by name + struct cgltf_material *mat = NULL; + for (cgltf_size i = 0; i < data->materials_count; ++i) { + tb_auto material = &data->materials[i]; + if (SDL_strcmp(mat_name, material->name) == 0) { + mat = material; + break; + } + } + if (mat == NULL) { + TB_CHECK(false, "Failed to find material by name"); + tex = 0; // Invalid ent means task failed + } + + char image_name[100] = {0}; + struct cgltf_texture *texture = NULL; + // Find image by usage + switch (usage) { + case TB_TEX_USAGE_COLOR: { + if (mat->has_pbr_metallic_roughness) { + texture = mat->pbr_metallic_roughness.base_color_texture.texture; + } else if (mat->has_pbr_specular_glossiness) { + texture = mat->pbr_specular_glossiness.diffuse_texture.texture; + } else { + TB_CHECK(false, "Expected material to have a color texture somewhere"); + } + SDL_snprintf(image_name, 100, "%s_color", mat->name); + break; + } + case TB_TEX_USAGE_METAL_ROUGH: + if (mat->has_pbr_metallic_roughness) { + texture = mat->pbr_metallic_roughness.metallic_roughness_texture.texture; + } else { + TB_CHECK(false, "Expected material to have metallic roughness model"); + } + SDL_snprintf(image_name, 100, "%s_metal", mat->name); + break; + case TB_TEX_USAGE_NORMAL: + texture = mat->normal_texture.texture; + SDL_snprintf(image_name, 100, "%s_normal", mat->name); + break; + default: + texture = NULL; + break; + } + if (texture == NULL) { + TB_CHECK(false, "Failed to find texture by usage"); + tex = 0; // Invalid ent means task failed + } + + TbTextureImage tex_comp = {0}; + if (tex != 0) { + tex_comp = tb_load_gltf_texture(rnd_sys, image_name, texture); + } + + // Strings were copies that can be freed now + tb_free(tb_global_alloc, (void *)mat_name); + + // Launch pinned task to handle loading signals on main thread + TbTextureLoadedArgs loaded_args = { + .ecs = load_args->common.ecs, + .tex = tex, + .comp = tex_comp, + }; + tb_launch_pinned_task_args(load_args->common.enki, + load_args->common.loaded_task, &loaded_args, + sizeof(TbTextureLoadedArgs)); +} + +typedef struct TbLoadKTXTexture2Args { + TbLoadCommonTextureArgs common; + TbTextureKTXLoadRequest ktx; +} TbLoadKTXTexture2Args; + +void tb_load_ktx_texture_task(const void *args) { + TB_TRACY_SCOPE("Load KTX Texture Task"); + tb_auto load_args = (const TbLoadKTXTexture2Args *)args; + TbTexture tex = load_args->common.tex; + tb_auto rnd_sys = load_args->common.rnd_sys; + + tb_auto path = load_args->ktx.path; + tb_auto name = load_args->ktx.name; + + ktxTexture2 *ktx = NULL; + + ktxTextureCreateFlags flags = KTX_TEXTURE_CREATE_LOAD_IMAGE_DATA_BIT; + + // We need to open this file with SDL_RWops because on a platform like + // android where the asset lives in package storage, this is the best way + // to actually open the file you're looking for + SDL_RWops *tex_file = SDL_RWFromFile(path, "rb"); + size_t tex_size = SDL_RWsize(tex_file); + + uint8_t *tex_data = tb_alloc(tb_thread_alloc, tex_size); + SDL_RWread(tex_file, (void *)tex_data, tex_size); + SDL_RWclose(tex_file); + + ktx_error_code_e err = + ktxTexture2_CreateFromMemory(tex_data, tex_size, flags, &ktx); + TB_CHECK(err == KTX_SUCCESS, "Failed to create KTX texture from memory"); + + TbTextureImage tex_comp = {0}; + if (ktx != NULL) { + tex_comp = tb_load_ktx_image(rnd_sys, name, ktx); + } + + tb_free(tb_thread_alloc, tex_data); + + // Launch pinned task to handle loading signals on main thread + TbTextureLoadedArgs loaded_args = { + .ecs = load_args->common.ecs, + .tex = tex, + .comp = tex_comp, + }; + tb_launch_pinned_task_args(load_args->common.enki, + load_args->common.loaded_task, &loaded_args, + sizeof(TbTextureLoadedArgs)); +} + +typedef struct TbLoadRawTextureArgs { + TbLoadCommonTextureArgs common; + TbTextureRawLoadRequest raw; +} TbLoadRawTextureArgs; + +void tb_load_raw_texture_task(const void *args) { + TB_TRACY_SCOPE("Load Raw Texture Task"); + tb_auto load_args = (const TbLoadRawTextureArgs *)args; + TbTexture tex = load_args->common.tex; + tb_auto rnd_sys = load_args->common.rnd_sys; + tb_auto usage = load_args->common.usage; + + tb_auto name = load_args->raw.name; + tb_auto pixels = load_args->raw.pixels; + tb_auto size = load_args->raw.size; + tb_auto width = load_args->raw.width; + tb_auto height = load_args->raw.height; + + TbTextureImage tex_comp = + tb_load_raw_image(rnd_sys, name, pixels, size, width, height, usage); + + // Launch pinned task to handle loading signals on main thread + TbTextureLoadedArgs loaded_args = { + .ecs = load_args->common.ecs, + .tex = tex, + .comp = tex_comp, + }; + tb_launch_pinned_task_args(load_args->common.enki, + load_args->common.loaded_task, &loaded_args, + sizeof(TbTextureLoadedArgs)); +} + +// Systems + +void tb_queue_gltf_tex_loads(ecs_iter_t *it) { + TB_TRACY_SCOPE("Queue GLTF Tex Loads"); + + tb_auto ecs = it->world; + + tb_auto enki = *ecs_field(it, TbTaskScheduler, 1); + tb_auto rnd_sys = ecs_field(it, TbRenderSystem, 2); + tb_auto reqs = ecs_field(it, TbTextureGLTFLoadRequest, 3); + tb_auto usages = ecs_field(it, TbTextureUsage, 4); + + tb_auto tex_ctx = ecs_singleton_get_mut(it->world, TbTextureCtx); + + // TODO: Time slice the time spent creating tasks + // Iterate texture load tasks + for (int32_t i = 0; i < it->count; ++i) { + if (SDL_AtomicGet(&tb_parallel_tex_load_count) >= + TbMaxParallelTextureLoads) { + break; + } + TbTexture ent = it->entities[i]; + tb_auto req = reqs[i]; + tb_auto usage = usages[i]; + + // This pinned task will be launched by the loading task + TbPinnedTask loaded_task = + tb_create_pinned_task(enki, tb_texture_loaded, NULL, 0); + + TbLoadGLTFTexture2Args args = { + .common = + { + .ecs = ecs, + .tex = ent, + .enki = enki, + .rnd_sys = rnd_sys, + .loaded_task = loaded_task, + .usage = usage, + }, + .gltf = req, + }; + TbTask load_task = tb_async_task(enki, tb_load_gltf_texture_task, &args, + sizeof(TbLoadGLTFTexture2Args)); + // Apply task component to texture entity + ecs_set(ecs, ent, TbTask, {load_task}); + + SDL_AtomicIncRef(&tb_parallel_tex_load_count); + tex_ctx->owned_tex_count++; + + // Remove load request as it has now been enqueued to the task system + ecs_remove(ecs, ent, TbTextureGLTFLoadRequest); + } +} + +void tb_queue_ktx_tex_loads(ecs_iter_t *it) { + TB_TRACY_SCOPE("Queue KTX Tex Loads"); + + tb_auto ecs = it->world; + + tb_auto enki = *ecs_field(it, TbTaskScheduler, 1); + tb_auto rnd_sys = ecs_field(it, TbRenderSystem, 2); + tb_auto reqs = ecs_field(it, TbTextureKTXLoadRequest, 3); + tb_auto usages = ecs_field(it, TbTextureUsage, 4); + + tb_auto tex_ctx = ecs_singleton_get_mut(it->world, TbTextureCtx); + + // TODO: Time slice the time spent creating tasks + // Iterate texture load tasks + for (int32_t i = 0; i < it->count; ++i) { + if (SDL_AtomicGet(&tb_parallel_tex_load_count) > + TbMaxParallelTextureLoads) { + break; + } + TbTexture ent = it->entities[i]; + tb_auto req = reqs[i]; + tb_auto usage = usages[i]; + + // This pinned task will be launched by the loading task + TbPinnedTask loaded_task = + tb_create_pinned_task(enki, tb_texture_loaded, NULL, 0); + + TbLoadKTXTexture2Args args = { + .common = + { + .ecs = ecs, + .tex = ent, + .enki = enki, + .rnd_sys = rnd_sys, + .loaded_task = loaded_task, + .usage = usage, + }, + .ktx = req, + }; + TbTask load_task = tb_async_task(enki, tb_load_ktx_texture_task, &args, + sizeof(TbLoadKTXTexture2Args)); + // Apply task component to texture entity + ecs_set(ecs, ent, TbTask, {load_task}); + + SDL_AtomicIncRef(&tb_parallel_tex_load_count); + tex_ctx->owned_tex_count++; + + // Remove load request as it has now been enqueued to the task system + ecs_remove(ecs, ent, TbTextureKTXLoadRequest); + } +} + +void tb_queue_raw_tex_loads(ecs_iter_t *it) { + TB_TRACY_SCOPE("Queue Raw Tex Loads"); + + tb_auto ecs = it->world; + + tb_auto enki = *ecs_field(it, TbTaskScheduler, 1); + tb_auto rnd_sys = ecs_field(it, TbRenderSystem, 2); + tb_auto reqs = ecs_field(it, TbTextureRawLoadRequest, 3); + tb_auto usages = ecs_field(it, TbTextureUsage, 4); + + tb_auto tex_ctx = ecs_singleton_get_mut(it->world, TbTextureCtx); + + // TODO: Time slice the time spent creating tasks + // Iterate texture load tasks + for (int32_t i = 0; i < it->count; ++i) { + if (SDL_AtomicGet(&tb_parallel_tex_load_count) > + TbMaxParallelTextureLoads) { + break; + } + + TbTexture ent = it->entities[i]; + tb_auto req = reqs[i]; + tb_auto usage = usages[i]; + + // This pinned task will be launched by the loading task + TbPinnedTask loaded_task = + tb_create_pinned_task(enki, tb_texture_loaded, NULL, 0); + + TbLoadRawTextureArgs args = { + .common = + { + .ecs = ecs, + .tex = ent, + .enki = enki, + .rnd_sys = rnd_sys, + .loaded_task = loaded_task, + .usage = usage, + }, + .raw = req, + }; + TbTask load_task = tb_async_task(enki, tb_load_raw_texture_task, &args, + sizeof(TbLoadRawTextureArgs)); + // Apply task component to texture entity + ecs_set(ecs, ent, TbTask, {load_task}); + + SDL_AtomicIncRef(&tb_parallel_tex_load_count); + tex_ctx->owned_tex_count++; + + // Remove load request as it has now been enqueued to the task system + ecs_remove(ecs, ent, TbTextureRawLoadRequest); + } +} + +void tb_finalize_textures(ecs_iter_t *it) { + TB_TRACY_SCOPE("Finalize Textures"); + + tb_auto tex_ctx = ecs_field(it, TbTextureCtx, 1); + tb_auto rnd_sys = ecs_field(it, TbRenderSystem, 2); + tb_auto textures = ecs_field(it, TbTextureImage, 3); + + if (tex_ctx->owned_tex_count == 0 || it->count == 0) { + return; + } + + // Render Thread allocator to make sure writes live + tb_auto rnd_tmp_alloc = + rnd_sys->render_thread->frame_states[rnd_sys->frame_idx].tmp_alloc.alloc; + + // Collect a write for every new texture + TB_DYN_ARR_OF(TbDynDescWrite) writes = {0}; + TB_DYN_ARR_RESET(writes, rnd_tmp_alloc, it->count); + for (int32_t i = 0; i < it->count; ++i) { + tb_auto texture = &textures[i]; + TbDynDescWrite write = { + .type = VK_DESCRIPTOR_TYPE_SAMPLED_IMAGE, + .desc.image = + (VkDescriptorImageInfo){ + .imageLayout = VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL, + .imageView = texture->image_view, + }, + }; + TB_DYN_ARR_APPEND(writes, write); + } + + // Allocate space for indices + const tb_auto write_count = TB_DYN_ARR_SIZE(writes); + if (write_count > 0) { + uint32_t *tex_indices = + tb_alloc_nm_tp(rnd_sys->tmp_alloc, TB_DYN_ARR_SIZE(writes), uint32_t); + tb_write_dyn_desc_pool(&tex_ctx->desc_pool, write_count, writes.data, + tex_indices); + + TB_DYN_ARR_FOREACH(writes, i) { + // Texture is now ready to be referenced elsewhere + ecs_set(it->world, it->entities[i], TbTextureComponent, {tex_indices[i]}); + ecs_add(it->world, it->entities[i], TbDescriptorReady); + } + } +} + +void tb_update_texture_pool(ecs_iter_t *it) { + TB_TRACY_SCOPE("Update Texture Pool"); + + tb_auto tex_ctx = ecs_field(it, TbTextureCtx, 1); + tb_auto rnd_sys = ecs_field(it, TbRenderSystem, 2); + + // Tick the pool in case any new textures require us to expand the pool + tb_tick_dyn_desc_pool(rnd_sys, &tex_ctx->desc_pool); +} + +// Toybox Glue + +void tb_register_texture_sys(TbWorld *world) { + tb_auto ecs = world->ecs; + ECS_COMPONENT_DEFINE(ecs, TbTextureCtx); + ECS_COMPONENT_DEFINE(ecs, TbTextureGLTFLoadRequest); + ECS_COMPONENT_DEFINE(ecs, TbTextureKTXLoadRequest); + ECS_COMPONENT_DEFINE(ecs, TbTextureRawLoadRequest); + ECS_COMPONENT_DEFINE(ecs, TbTextureImage); + ECS_COMPONENT_DEFINE(ecs, TbTextureComponent); + ECS_COMPONENT_DEFINE(ecs, TbTextureUsage); + + ECS_TAG_DEFINE(ecs, TbTextureLoaded); + + ECS_SYSTEM(ecs, tb_queue_gltf_tex_loads, EcsPostLoad, + TbTaskScheduler(TbTaskScheduler), TbRenderSystem(TbRenderSystem), + [in] TbTextureGLTFLoadRequest, [in] TbTextureUsage); + ECS_SYSTEM(ecs, tb_queue_ktx_tex_loads, EcsPostLoad, + TbTaskScheduler(TbTaskScheduler), TbRenderSystem(TbRenderSystem), + [in] TbTextureKTXLoadRequest, [in] TbTextureUsage); + ECS_SYSTEM(ecs, tb_queue_raw_tex_loads, EcsPostLoad, + TbTaskScheduler(TbTaskScheduler), TbRenderSystem(TbRenderSystem), + [in] TbTextureRawLoadRequest, [in] TbTextureUsage); + + ECS_SYSTEM(ecs, tb_finalize_textures, + EcsPostUpdate, [in] TbTextureCtx(TbTextureCtx), + [in] TbRenderSystem(TbRenderSystem), [in] TbTextureImage, + [in] TbTextureLoaded, !TbDescriptorReady); + ECS_SYSTEM(ecs, tb_update_texture_pool, + EcsPreStore, [in] TbTextureCtx(TbTextureCtx), + [in] TbRenderSystem(TbRenderSystem)); + + TbTextureCtx ctx = {0}; + + SDL_AtomicSet(&tb_parallel_tex_load_count, 0); + + tb_auto rnd_sys = ecs_singleton_get_mut(ecs, TbRenderSystem); + + // Create descriptor set layout + { + const VkDescriptorBindingFlags flags = + VK_DESCRIPTOR_BINDING_VARIABLE_DESCRIPTOR_COUNT_BIT | + VK_DESCRIPTOR_BINDING_PARTIALLY_BOUND_BIT | + VK_DESCRIPTOR_BINDING_UPDATE_AFTER_BIND_BIT | + VK_DESCRIPTOR_BINDING_UPDATE_UNUSED_WHILE_PENDING_BIT; + const uint32_t binding_count = 1; + VkDescriptorSetLayoutCreateInfo create_info = { + .sType = VK_STRUCTURE_TYPE_DESCRIPTOR_SET_LAYOUT_CREATE_INFO, +#if TB_USE_DESC_BUFFER == 1 + .flags = VK_DESCRIPTOR_SET_LAYOUT_CREATE_DESCRIPTOR_BUFFER_BIT_EXT, +#else + .flags = VK_DESCRIPTOR_SET_LAYOUT_CREATE_UPDATE_AFTER_BIND_POOL_BIT, +#endif + .pNext = + &(VkDescriptorSetLayoutBindingFlagsCreateInfo){ + .sType = + VK_STRUCTURE_TYPE_DESCRIPTOR_SET_LAYOUT_BINDING_FLAGS_CREATE_INFO, + .bindingCount = binding_count, + .pBindingFlags = + (VkDescriptorBindingFlags[binding_count]){flags}, + }, + .bindingCount = binding_count, + .pBindings = + (VkDescriptorSetLayoutBinding[binding_count]){ + { + .binding = 0, + .descriptorCount = TB_DESC_POOL_CAP, + .descriptorType = VK_DESCRIPTOR_TYPE_SAMPLED_IMAGE, + .stageFlags = VK_SHADER_STAGE_FRAGMENT_BIT, + }, + }, + }; + tb_rnd_create_set_layout(rnd_sys, &create_info, "Texture Table Layout", + &ctx.set_layout); + } + + tb_create_dyn_desc_pool(rnd_sys, "Texture Descriptors", ctx.set_layout, + VK_DESCRIPTOR_TYPE_SAMPLED_IMAGE, TB_DESC_POOL_CAP, + &ctx.desc_pool, 0); + + // Must set ctx before we try to load any textures + ecs_singleton_set_ptr(ecs, TbTextureCtx, &ctx); + + // Load some default textures + { + const uint8_t pixels[] = { + 255, 255, 255, 255, 255, 255, 255, 255, + 255, 255, 255, 255, 255, 255, 255, 255, + }; + ctx.default_color_tex = + tb_tex_sys_load_raw_tex(ecs, "Default Color Texture", pixels, + sizeof(pixels), 2, 2, TB_TEX_USAGE_COLOR); + } + { + const uint8_t pixels[] = { + 0x7E, 0x7E, 0xFF, 255, 0x7E, 0x7E, 0xFF, 255, + 0x7E, 0x7E, 0xFF, 255, 0x7E, 0x7E, 0xFF, 255, + }; + ctx.default_normal_tex = + tb_tex_sys_load_raw_tex(ecs, "Default Normal Texture", pixels, + sizeof(pixels), 2, 2, TB_TEX_USAGE_NORMAL); + } + { + const uint8_t pixels[] = { + 255, 255, 255, 255, 255, 255, 255, 255, + 255, 255, 255, 255, 255, 255, 255, 255, + }; + ctx.default_metal_rough_tex = + tb_tex_sys_load_raw_tex(ecs, "Default Metal Rough Texture", pixels, + sizeof(pixels), 2, 2, TB_TEX_USAGE_METAL_ROUGH); + } + // Load BRDF LUT texture + { + const char *path = ASSET_PREFIX "textures/brdf.ktx2"; + ctx.brdf_tex = + tb_tex_sys_load_ktx_tex(ecs, path, "BRDF LUT", TB_TEX_USAGE_BRDF); + } + + ecs_singleton_set_ptr(ecs, TbTextureCtx, &ctx); +} + +void tb_unregister_texture_sys(TbWorld *world) { + tb_auto ecs = world->ecs; + tb_auto rnd_sys = ecs_singleton_get_mut(ecs, TbRenderSystem); + tb_auto ctx = ecs_singleton_get_mut(ecs, TbTextureCtx); + + tb_destroy_descriptor_buffer(rnd_sys, &ctx->desc_buffer); + + tb_rnd_destroy_set_layout(rnd_sys, ctx->set_layout); + + // TODO: Release all default texture references + + // TODO: Check for leaks + + // TODO: Clean up descriptor pool + + ecs_singleton_remove(ecs, TbTextureCtx); +} + +TB_REGISTER_SYS(tb, texture, TB_TEX_SYS_PRIO) + +// Public API + +VkDescriptorSetLayout tb_tex_sys_get_set_layout(ecs_world_t *ecs) { + tb_auto ctx = ecs_singleton_get_mut(ecs, TbTextureCtx); + return ctx->set_layout; +} + +VkDescriptorSet tb_tex_sys_get_set(ecs_world_t *ecs) { + tb_auto ctx = ecs_singleton_get_mut(ecs, TbTextureCtx); + tb_auto rnd_sys = ecs_singleton_get_mut(ecs, TbRenderSystem); + return tb_dyn_desc_pool_get_set(rnd_sys, &ctx->desc_pool); +} + +// Returns the binding info of the texture system's descriptor buffer +VkDescriptorBufferBindingInfoEXT tb_tex_sys_get_table_addr(ecs_world_t *ecs) { + tb_auto ctx = ecs_singleton_get_mut(ecs, TbTextureCtx); + return tb_desc_buff_get_binding(&ctx->desc_buffer); +} + +VkImageView tb_tex_sys_get_image_view2(ecs_world_t *ecs, TbTexture tex) { + TB_CHECK_RETURN(tb_is_texture_ready(ecs, tex), + "Tried to get image view of a texture that wasn't ready", + VK_NULL_HANDLE); + tb_auto image = ecs_get(ecs, tex, TbTextureImage); + return image->image_view; +} + +TbTexture tb_tex_sys_load_raw_tex(ecs_world_t *ecs, const char *name, + const uint8_t *pixels, uint64_t size, + uint32_t width, uint32_t height, + TbTextureUsage usage) { + // If an entity already exists with this name it is either loading or loaded + TbTexture tex_ent = ecs_lookup_child(ecs, ecs_id(TbTextureCtx), name); + if (tex_ent != 0) { + return tex_ent; + } + + // Create a texture entity + tex_ent = ecs_new_entity(ecs, 0); + ecs_set_name(ecs, tex_ent, name); + + // It is a child of the texture system context singleton + ecs_add_pair(ecs, tex_ent, EcsChildOf, ecs_id(TbTextureCtx)); + + // Append a texture load request onto the entity to schedule loading + ecs_set(ecs, tex_ent, TbTextureRawLoadRequest, + {name, pixels, size, width, height}); + ecs_set(ecs, tex_ent, TbTextureUsage, {usage}); + ecs_remove(ecs, tex_ent, TbDescriptorReady); + + return tex_ent; +} + +TbTexture tb_tex_sys_load_mat_tex(ecs_world_t *ecs, const cgltf_data *data, + const char *mat_name, TbTextureUsage usage) { + const uint32_t image_name_max = 100; + char image_name[image_name_max] = {0}; + // GLTFpack strips image names so we have to synthesize something + { + switch (usage) { + case TB_TEX_USAGE_BRDF: + default: + TB_CHECK(false, + "Material textures should have Color, Metal or Normal usage"); + break; + case TB_TEX_USAGE_COLOR: + SDL_snprintf(image_name, image_name_max, "%s_color", mat_name); + break; + case TB_TEX_USAGE_METAL_ROUGH: + SDL_snprintf(image_name, image_name_max, "%s_metal", mat_name); + break; + case TB_TEX_USAGE_NORMAL: + SDL_snprintf(image_name, image_name_max, "%s_normal", mat_name); + break; + } + } + + // If an entity already exists with this name it is either loading or loaded + TbTexture tex_ent = ecs_lookup_child(ecs, ecs_id(TbTextureCtx), image_name); + if (tex_ent != 0) { + return tex_ent; + } + + TB_CHECK_RETURN(data, "Expected Data", 0); + + // Create a texture entity + tex_ent = ecs_new_entity(ecs, 0); + ecs_set_name(ecs, tex_ent, image_name); + + // Need to copy strings for task safety + // Task is responsible for freeing this name + const size_t mat_name_len = SDL_strnlen(mat_name, 256) + 1; + char *mat_name_cpy = tb_alloc_nm_tp(tb_global_alloc, mat_name_len, char); + SDL_strlcpy(mat_name_cpy, mat_name, mat_name_len); + + // It is a child of the texture system context singleton + ecs_add_pair(ecs, tex_ent, EcsChildOf, ecs_id(TbTextureCtx)); + + // Append a texture load request onto the entity to schedule loading + ecs_set(ecs, tex_ent, TbTextureGLTFLoadRequest, {data, mat_name_cpy}); + ecs_set(ecs, tex_ent, TbTextureUsage, {usage}); + ecs_remove(ecs, tex_ent, TbDescriptorReady); + + return tex_ent; +} + +TbTexture tb_tex_sys_load_ktx_tex(ecs_world_t *ecs, const char *path, + const char *name, TbTextureUsage usage) { + // If an entity already exists with this name it is either loading or loaded + TbTexture tex_ent = ecs_lookup(ecs, name); + if (tex_ent != 0) { + return tex_ent; + } + + // Create a texture entity + tex_ent = ecs_new_entity(ecs, 0); + ecs_set_name(ecs, tex_ent, name); + + // It is a child of the texture system context singleton + ecs_add_pair(ecs, tex_ent, EcsChildOf, ecs_id(TbTextureCtx)); + + // Append a texture load request onto the entity to schedule loading + ecs_set(ecs, tex_ent, TbTextureKTXLoadRequest, {path, name}); + ecs_set(ecs, tex_ent, TbTextureUsage, {usage}); + ecs_remove(ecs, tex_ent, TbDescriptorReady); + + return tex_ent; +} + +bool tb_is_texture_ready(ecs_world_t *ecs, TbTexture tex) { + return ecs_has(ecs, tex, TbTextureLoaded) && + ecs_has(ecs, tex, TbTextureComponent) && + ecs_has(ecs, tex, TbDescriptorReady); +} + +TbTexture tb_get_default_color_tex(ecs_world_t *ecs) { + tb_auto ctx = ecs_singleton_get(ecs, TbTextureCtx); + return ctx->default_color_tex; +} +TbTexture tb_get_default_normal_tex(ecs_world_t *ecs) { + tb_auto ctx = ecs_singleton_get(ecs, TbTextureCtx); + return ctx->default_normal_tex; +} +TbTexture tb_get_default_metal_rough_tex(ecs_world_t *ecs) { + tb_auto ctx = ecs_singleton_get(ecs, TbTextureCtx); + return ctx->default_metal_rough_tex; +} +TbTexture tb_get_brdf_tex(ecs_world_t *ecs) { + tb_auto ctx = ecs_singleton_get(ecs, TbTextureCtx); + return ctx->brdf_tex; +} diff --git a/source/thirdpersoncomponents.c b/source/tb_third_person_components.c similarity index 83% rename from source/thirdpersoncomponents.c rename to source/tb_third_person_components.c index 1e0b6f83..9d70a86c 100644 --- a/source/thirdpersoncomponents.c +++ b/source/tb_third_person_components.c @@ -1,10 +1,10 @@ -#include "thirdpersoncomponents.h" +#include "tb_third_person_components.h" -#include "cameracomponent.h" -#include "rigidbodycomponent.h" -#include "tbcommon.h" -#include "transformcomponent.h" -#include "world.h" +#include "tb_camera_component.h" +#include "tb_common.h" +#include "tb_rigidbody_component.h" +#include "tb_transform_component.h" +#include "tb_world.h" #include #include @@ -57,13 +57,14 @@ void third_person_move_on_set(ecs_iter_t *it) { } } -bool tb_load_third_person_move_comp(TbWorld *world, ecs_entity_t ent, +bool tb_load_third_person_move_comp(ecs_world_t *ecs, ecs_entity_t ent, const char *source_path, + const cgltf_data *data, const cgltf_node *node, json_object *json) { (void)source_path; + (void)data; (void)node; - ecs_world_t *ecs = world->ecs; TbThirdPersonMovementComponent comp = {0}; json_object_object_foreach(json, key, value) { if (SDL_strcmp(key, "speed") == 0) { @@ -90,7 +91,7 @@ bool tb_load_third_person_move_comp(TbWorld *world, ecs_entity_t ent, return true; } -ecs_entity_t tb_register_third_person_move_comp(TbWorld *world) { +TbComponentRegisterResult tb_register_third_person_move_comp(TbWorld *world) { ecs_world_t *ecs = world->ecs; ECS_COMPONENT_DEFINE(ecs, TbThirdPersonMovementComponentDesc); ECS_COMPONENT_DEFINE(ecs, TbThirdPersonMovementComponent); @@ -110,7 +111,14 @@ ecs_entity_t tb_register_third_person_move_comp(TbWorld *world) { {.name = "fixed_rotation", .type = ecs_id(ecs_bool_t)}, }}); - return ecs_id(TbThirdPersonMovementComponentDesc); + return (TbComponentRegisterResult){ + ecs_id(TbThirdPersonMovementComponent), + ecs_id(TbThirdPersonMovementComponentDesc)}; +} + +bool tb_ready_third_person_move_comp(ecs_world_t *ecs, ecs_entity_t ent) { + tb_auto comp = ecs_get(ecs, ent, TbThirdPersonMovementComponent); + return comp != NULL; } TB_REGISTER_COMP(tb, third_person_move) diff --git a/source/thirdpersonsystems.cpp b/source/tb_third_person_systems.cpp similarity index 95% rename from source/thirdpersonsystems.cpp rename to source/tb_third_person_systems.cpp index a91f3408..6a31c67c 100644 --- a/source/thirdpersonsystems.cpp +++ b/source/tb_third_person_systems.cpp @@ -1,11 +1,11 @@ -#include "cameracomponent.h" -#include "inputsystem.h" -#include "physicssystem.hpp" -#include "profiling.h" -#include "rigidbodycomponent.h" -#include "thirdpersoncomponents.h" -#include "transformcomponent.h" -#include "world.h" +#include "tb_camera_component.h" +#include "tb_input_system.h" +#include "tb_physics_system.hpp" +#include "tb_profiling.h" +#include "tb_rigidbody_component.h" +#include "tb_third_person_components.h" +#include "tb_transform_component.h" +#include "tb_world.h" #include #include @@ -79,7 +79,7 @@ void update_tp_movement(flecs::world &ecs, float delta_time, camera_pos += target_pos; camera_trans = tb_look_forward_transform(camera_pos, -body_to_cam, TB_UP); - camera_trans_comp.dirty = true; + tb_transform_mark_dirty(ecs, ecs.entity(move.camera)); } // Handle movement of body diff --git a/source/thrower.c b/source/tb_thrower.c similarity index 74% rename from source/thrower.c rename to source/tb_thrower.c index af5818bd..5a1af4bf 100644 --- a/source/thrower.c +++ b/source/tb_thrower.c @@ -1,10 +1,10 @@ -#include "inputsystem.h" -#include "physicssystem.h" -#include "rigidbodycomponent.h" -#include "tbcommon.h" -#include "tbsystempriority.h" -#include "transformcomponent.h" -#include "world.h" +#include "tb_common.h" +#include "tb_input_system.h" +#include "tb_physics_system.h" +#include "tb_rigidbody_component.h" +#include "tb_system_priority.h" +#include "tb_transform_component.h" +#include "tb_world.h" #include @@ -23,18 +23,19 @@ ECS_COMPONENT_DECLARE(TbThrowDir); ECS_COMPONENT_DECLARE(TbThrowForce); ECS_COMPONENT_DECLARE(TbThrower); -bool tb_load_thrower_comp(TbWorld *world, ecs_entity_t ent, - const char *source_path, const cgltf_node *node, - json_object *json) { +bool tb_load_thrower_comp(ecs_world_t *ecs, ecs_entity_t ent, + const char *source_path, const cgltf_data *data, + const cgltf_node *node, json_object *json) { (void)source_path; + (void)data; (void)node; (void)json; // Descriptor just marks that a thrower component should be attached - ecs_set(world->ecs, ent, TbThrower, {0}); + ecs_set(ecs, ent, TbThrower, {0}); return true; } -ecs_entity_t tb_register_thrower_comp(TbWorld *world) { +TbComponentRegisterResult tb_register_thrower_comp(TbWorld *world) { tb_auto ecs = world->ecs; ECS_COMPONENT_DEFINE(ecs, TbThrowerDesc); @@ -48,7 +49,12 @@ ecs_entity_t tb_register_thrower_comp(TbWorld *world) { }}); - return ecs_id(TbThrowerDesc); + return (TbComponentRegisterResult){ecs_id(TbThrower), ecs_id(TbThrowerDesc)}; +} + +bool tb_ready_thrower_comp(ecs_world_t *ecs, ecs_entity_t ent) { + tb_auto comp = ecs_get(ecs, ent, TbThrower); + return comp != NULL; } TB_REGISTER_COMP(tb, thrower) diff --git a/source/timeofdaysystem.c b/source/tb_time_of_day_system.c similarity index 84% rename from source/timeofdaysystem.c rename to source/tb_time_of_day_system.c index 835bb3b3..82d65cec 100644 --- a/source/timeofdaysystem.c +++ b/source/tb_time_of_day_system.c @@ -1,20 +1,22 @@ -#include "timeofdaysystem.h" - #include "common.hlsli" -#include "coreuisystem.h" -#include "lightcomponent.h" -#include "profiling.h" -#include "skycomponent.h" -#include "tbcommon.h" -#include "tbimgui.h" -#include "transformcomponent.h" -#include "viewsystem.h" -#include "world.h" +#include "tb_common.h" +#include "tb_coreui_system.h" +#include "tb_imgui.h" +#include "tb_light_component.h" +#include "tb_profiling.h" +#include "tb_sky_component.h" +#include "tb_transform_component.h" +#include "tb_view_system.h" +#include "tb_world.h" #include #include #include +typedef struct TbTimeOfDayComponent { + float time; + float time_scale; +} TbTimeOfDayComponent; ECS_COMPONENT_DECLARE(TbTimeOfDayComponent); typedef struct TbTimeOfDayDescriptor { @@ -186,9 +188,9 @@ void tb_unregister_time_of_day_sys(TbWorld *world) { #endif } -TB_REGISTER_SYS(tb, time_of_day, TB_TOD_SYS_PRIO) +TB_REGISTER_SYS(tb, time_of_day, TB_SYSTEM_NORMAL) -ecs_entity_t tb_register_time_of_day_comp(TbWorld *world) { +TbComponentRegisterResult tb_register_time_of_day_comp(TbWorld *world) { ecs_world_t *ecs = world->ecs; ECS_COMPONENT_DEFINE(ecs, TbTimeOfDayComponent); ECS_COMPONENT_DEFINE(ecs, TbTimeOfDayDescriptor); @@ -199,17 +201,19 @@ ecs_entity_t tb_register_time_of_day_comp(TbWorld *world) { {.name = "time_scale", .type = ecs_id(ecs_f32_t)}, }}); - return ecs_id(TbTimeOfDayDescriptor); + return (TbComponentRegisterResult){ecs_id(TbTimeOfDayComponent), + ecs_id(TbTimeOfDayDescriptor)}; } -bool tb_load_time_of_day_comp(TbWorld *world, ecs_entity_t ent, - const char *source_path, const cgltf_node *node, - json_object *object) { +bool tb_load_time_of_day_comp(ecs_world_t *ecs, ecs_entity_t ent, + const char *source_path, const cgltf_data *data, + const cgltf_node *node, json_object *json) { (void)source_path; + (void)data; (void)node; TbTimeOfDayComponent comp = {0}; - json_object_object_foreach(object, key, value) { + json_object_object_foreach(json, key, value) { if (SDL_strcmp(key, "start_time") == 0) { comp.time = (float)json_object_get_double(value); } @@ -217,7 +221,7 @@ bool tb_load_time_of_day_comp(TbWorld *world, ecs_entity_t ent, comp.time_scale = (float)json_object_get_double(value); } } - ecs_set_ptr(world->ecs, ent, TbTimeOfDayComponent, &comp); + ecs_set_ptr(ecs, ent, TbTimeOfDayComponent, &comp); return true; } @@ -226,4 +230,9 @@ void tb_destroy_time_of_day_comp(TbWorld *world, ecs_entity_t ent) { ecs_remove(world->ecs, ent, TbTimeOfDayComponent); } +bool tb_ready_time_of_day_comp(ecs_world_t *ecs, ecs_entity_t ent) { + tb_auto comp = ecs_get(ecs, ent, TbTimeOfDayComponent); + return comp != NULL; +} + TB_REGISTER_COMP(tb, time_of_day) diff --git a/source/transformcomponent.c b/source/tb_transform_component.c similarity index 84% rename from source/transformcomponent.c rename to source/tb_transform_component.c index dd54ec49..f0f9f4eb 100644 --- a/source/transformcomponent.c +++ b/source/tb_transform_component.c @@ -1,18 +1,20 @@ -#include "transformcomponent.h" +#include "tb_transform_component.h" -#include "profiling.h" -#include "tbcommon.h" -#include "world.h" +#include "tb_common.h" +#include "tb_profiling.h" +#include "tb_render_object_system.h" // HACK: Evil for a component to include a system header like this +#include "tb_world.h" #include +ECS_TAG_DECLARE(TbTransformDirty); ECS_COMPONENT_DECLARE(TbTransformComponent); float4x4 tb_transform_get_world_matrix(ecs_world_t *ecs, ecs_entity_t entity) { TracyCZoneNC(ctx, "TbTransform Get World Matrix", TracyCategoryColorCore, true); tb_auto comp = ecs_get_mut(ecs, entity, TbTransformComponent); - if (comp->dirty) { + if (ecs_has(ecs, entity, TbTransformDirty)) { comp->world_matrix = tb_transform_to_matrix(&comp->transform); // If we have a parent, look up its world transform and combine it with this tb_auto parent = ecs_get_parent(ecs, entity); @@ -26,7 +28,7 @@ float4x4 tb_transform_get_world_matrix(ecs_world_t *ecs, ecs_entity_t entity) { comp->world_matrix = tb_mulf44f44(parent_mat, comp->world_matrix); parent = ecs_get_parent(ecs, parent); } - comp->dirty = false; + ecs_remove(ecs, entity, TbTransformDirty); ecs_modified(ecs, entity, TbTransformComponent); } @@ -58,14 +60,16 @@ TbTransform tb_transform_get_world_trans(ecs_world_t *ecs, void tb_transform_mark_dirty(ecs_world_t *ecs, ecs_entity_t entity) { TracyCZoneNC(ctx, "TbTransform Set Dirty", TracyCategoryColorCore, true); - tb_auto comp = ecs_get_mut(ecs, entity, TbTransformComponent); - comp->dirty = true; + ecs_add(ecs, entity, TbTransformDirty); tb_auto child_it = ecs_children(ecs, entity); while (ecs_children_next(&child_it)) { for (int i = 0; i < child_it.count; i++) { tb_transform_mark_dirty(ecs, child_it.entities[i]); } } + // HACK: If the transform was updated, any relevant render object data also + // needs to be updated + tb_render_object_mark_dirty(ecs, entity); ecs_modified(ecs, entity, TbTransformComponent); TracyCZoneEnd(ctx); } @@ -109,26 +113,28 @@ void tb_transform_set_world(ecs_world_t *ecs, ecs_entity_t entity, tb_transform_mark_dirty(ecs, entity); } -bool tb_load_transform_comp(TbWorld *world, ecs_entity_t ent, - const char *source_path, const cgltf_node *node, - json_object *json) { +bool tb_load_transform_comp(ecs_world_t *ecs, ecs_entity_t ent, + const char *source_path, const cgltf_data *data, + const cgltf_node *node, json_object *json) { (void)source_path; + (void)data; (void)json; TbTransformComponent comp = { - .dirty = true, .transform = tb_transform_from_node(node), }; - ecs_set_ptr(world->ecs, ent, TbTransformComponent, &comp); + ecs_set_ptr(ecs, ent, TbTransformComponent, &comp); + ecs_add(ecs, ent, TbTransformDirty); return true; } -ecs_entity_t tb_register_transform_comp(TbWorld *world) { +TbComponentRegisterResult tb_register_transform_comp(TbWorld *world) { ecs_world_t *ecs = world->ecs; ECS_COMPONENT_DEFINE(ecs, float3); ECS_COMPONENT_DEFINE(ecs, float4); ECS_COMPONENT_DEFINE(ecs, float4x4); ECS_COMPONENT_DEFINE(ecs, TbTransform); ECS_COMPONENT_DEFINE(ecs, TbTransformComponent); + ECS_TAG_DEFINE(ecs, TbTransformDirty); ecs_struct(ecs, {.entity = ecs_id(float3), .members = { @@ -168,7 +174,12 @@ ecs_entity_t tb_register_transform_comp(TbWorld *world) { }); // Return 0 because we don't actually want to mark an id for UI schema export - return 0; + return (TbComponentRegisterResult){ecs_id(TbTransformComponent), 0}; +} + +bool tb_ready_transform_comp(ecs_world_t *ecs, ecs_entity_t ent) { + tb_auto comp = ecs_get(ecs, ent, TbTransformComponent); + return comp != NULL; } TB_REGISTER_COMP(tb, transform) diff --git a/source/tbutil.c b/source/tb_util.c similarity index 90% rename from source/tbutil.c rename to source/tb_util.c index 6ca94c59..f2a11222 100644 --- a/source/tbutil.c +++ b/source/tb_util.c @@ -1,4 +1,4 @@ -#include "tbutil.h" +#include "tb_util.h" uint64_t tb_calc_aligned_size(uint32_t count, uint32_t stride, uint32_t alignment) { diff --git a/source/viewsystem.c b/source/tb_view_system.c similarity index 58% rename from source/viewsystem.c rename to source/tb_view_system.c index 836a9011..79df17ae 100644 --- a/source/viewsystem.c +++ b/source/tb_view_system.c @@ -1,14 +1,14 @@ -#include "viewsystem.h" +#include "tb_view_system.h" -#include "cameracomponent.h" #include "common.hlsli" -#include "profiling.h" -#include "rendersystem.h" -#include "rendertargetsystem.h" -#include "tbcommon.h" -#include "texturesystem.h" -#include "transformcomponent.h" -#include "world.h" +#include "tb_camera_component.h" +#include "tb_common.h" +#include "tb_profiling.h" +#include "tb_render_system.h" +#include "tb_render_target_system.h" +#include "tb_texture_system.h" +#include "tb_transform_component.h" +#include "tb_world.h" #include @@ -21,20 +21,16 @@ TB_REGISTER_SYS(tb, view, TB_VIEW_SYS_PRIO) TbViewSystem create_view_system(TbAllocator gp_alloc, TbAllocator tmp_alloc, TbRenderSystem *rnd_sys, - TbRenderTargetSystem *rt_sys, - TbTextureSystem *tex_sys) { + TbRenderTargetSystem *rt_sys) { TbViewSystem sys = { .rnd_sys = rnd_sys, .rt_sys = rt_sys, - .texture_system = tex_sys, .tmp_alloc = tmp_alloc, .gp_alloc = gp_alloc, }; TB_DYN_ARR_RESET(sys.views, sys.gp_alloc, 1); - VkResult err = VK_SUCCESS; - // Create a filtered env sampler { VkSamplerCreateInfo create_info = { @@ -48,9 +44,8 @@ TbViewSystem create_view_system(TbAllocator gp_alloc, TbAllocator tmp_alloc, .maxLod = 9.0f, // TODO: Fix hack .borderColor = VK_BORDER_COLOR_FLOAT_OPAQUE_WHITE, }; - err = tb_rnd_create_sampler(rnd_sys, &create_info, "Filtered Env Sampler", - &sys.filtered_env_sampler); - TB_VK_CHECK(err, "Failed to create filtered env sampler"); + tb_rnd_create_sampler(rnd_sys, &create_info, "Filtered Env Sampler", + &sys.filtered_env_sampler); } // Create a BRDF sampler @@ -66,9 +61,8 @@ TbViewSystem create_view_system(TbAllocator gp_alloc, TbAllocator tmp_alloc, .maxLod = 1.0f, .borderColor = VK_BORDER_COLOR_FLOAT_OPAQUE_WHITE, }; - err = tb_rnd_create_sampler(rnd_sys, &create_info, "BRDF Sampler", - &sys.brdf_sampler); - TB_VK_CHECK(err, "Failed to create brdf sampler"); + tb_rnd_create_sampler(rnd_sys, &create_info, "BRDF Sampler", + &sys.brdf_sampler); } // Create view descriptor set layout @@ -138,23 +132,101 @@ TbViewSystem create_view_system(TbAllocator gp_alloc, TbAllocator tmp_alloc, }, }, }; - err = tb_rnd_create_set_layout( - rnd_sys, &create_info, "TbView Descriptor Set Layout", &sys.set_layout); - TB_VK_CHECK(err, "Failed to create view descriptor set"); + tb_rnd_create_set_layout(rnd_sys, &create_info, + "TbView Descriptor Set Layout", &sys.set_layout); } + +#if TB_USE_DESC_BUFFER == 1 + { + VkDescriptorSetLayoutCreateInfo create_info = { + .sType = VK_STRUCTURE_TYPE_DESCRIPTOR_SET_LAYOUT_CREATE_INFO, + .flags = VK_DESCRIPTOR_SET_LAYOUT_CREATE_DESCRIPTOR_BUFFER_BIT_EXT, + .bindingCount = 9, + .pBindings = + (VkDescriptorSetLayoutBinding[9]){ + { + .binding = 0, + .descriptorCount = 1, + .descriptorType = VK_DESCRIPTOR_TYPE_UNIFORM_BUFFER, + .stageFlags = VK_SHADER_STAGE_VERTEX_BIT | + VK_SHADER_STAGE_FRAGMENT_BIT, + }, + { + .binding = 1, + .descriptorCount = 1, + .descriptorType = VK_DESCRIPTOR_TYPE_SAMPLED_IMAGE, + .stageFlags = VK_SHADER_STAGE_FRAGMENT_BIT, + }, + { + .binding = 2, + .descriptorCount = 1, + .descriptorType = VK_DESCRIPTOR_TYPE_SAMPLED_IMAGE, + .stageFlags = VK_SHADER_STAGE_FRAGMENT_BIT, + }, + { + .binding = 3, + .descriptorCount = 1, + .descriptorType = VK_DESCRIPTOR_TYPE_SAMPLED_IMAGE, + .stageFlags = VK_SHADER_STAGE_FRAGMENT_BIT, + }, + { + .binding = 4, + .descriptorCount = 1, + .descriptorType = VK_DESCRIPTOR_TYPE_UNIFORM_BUFFER, + .stageFlags = VK_SHADER_STAGE_VERTEX_BIT | + VK_SHADER_STAGE_FRAGMENT_BIT, + }, + { + .binding = 5, + .descriptorCount = 1, + .descriptorType = VK_DESCRIPTOR_TYPE_SAMPLED_IMAGE, + .stageFlags = VK_SHADER_STAGE_FRAGMENT_BIT, + }, + { + .binding = 6, + .descriptorCount = 1, + .descriptorType = VK_DESCRIPTOR_TYPE_SAMPLED_IMAGE, + .stageFlags = VK_SHADER_STAGE_FRAGMENT_BIT, + }, + { + .binding = 7, + .descriptorCount = 1, + .descriptorType = VK_DESCRIPTOR_TYPE_SAMPLER, + .stageFlags = VK_SHADER_STAGE_FRAGMENT_BIT, + .pImmutableSamplers = &sys.filtered_env_sampler, + }, + { + .binding = 8, + .descriptorCount = 1, + .descriptorType = VK_DESCRIPTOR_TYPE_SAMPLER, + .stageFlags = VK_SHADER_STAGE_FRAGMENT_BIT, + .pImmutableSamplers = &sys.brdf_sampler, + }, + }, + }; + tb_rnd_create_set_layout(rnd_sys, &create_info, + "View Descriptor Set Layout", &sys.set_layout2); + tb_create_descriptor_buffer(rnd_sys, sys.set_layout2, "View Descriptors", 4, + &sys.desc_buffer); + } +#endif + return sys; } -void destroy_view_system(TbViewSystem *self) { +void destroy_view_system(TbViewSystem *self, TbRenderSystem *rnd_sys) { TB_DYN_ARR_DESTROY(self->views); - tb_rnd_destroy_sampler(self->rnd_sys, self->brdf_sampler); - tb_rnd_destroy_sampler(self->rnd_sys, self->filtered_env_sampler); - tb_rnd_destroy_set_layout(self->rnd_sys, self->set_layout); + tb_rnd_destroy_sampler(rnd_sys, self->brdf_sampler); + tb_rnd_destroy_sampler(rnd_sys, self->filtered_env_sampler); + tb_rnd_destroy_set_layout(rnd_sys, self->set_layout); + + tb_rnd_destroy_set_layout(rnd_sys, self->set_layout2); + tb_destroy_descriptor_buffer(rnd_sys, &self->desc_buffer); for (uint32_t i = 0; i < TB_MAX_FRAME_STATES; ++i) { TbViewSystemFrameState *state = &self->frame_states[i]; - tb_rnd_destroy_descriptor_pool(self->rnd_sys, state->set_pool); + tb_rnd_destroy_descriptor_pool(rnd_sys, state->set_pool); } *self = (TbViewSystem){0}; @@ -163,7 +235,14 @@ void destroy_view_system(TbViewSystem *self) { void view_update_tick(ecs_iter_t *it) { TracyCZoneNC(ctx, "TbView System Tick", TracyCategoryColorRendering, true); - TbViewSystem *sys = ecs_field(it, TbViewSystem, 1); + // The view system requires that the texture system's BRDF texture be ready + tb_auto brdf_tex = tb_get_brdf_tex(it->world); + if (!tb_is_texture_ready(it->world, brdf_tex)) { + TracyCZoneEnd(ctx); + return; + } + + tb_auto sys = ecs_field(it, TbViewSystem, 1); const uint32_t view_count = TB_DYN_ARR_SIZE(sys->views); @@ -179,6 +258,108 @@ void view_update_tick(ecs_iter_t *it) { VkBuffer tmp_gpu_buffer = tb_rnd_get_gpu_tmp_buffer(rnd_sys); +#if TB_USE_DESC_BUFFER == 1 + { + // Reset the descriptor buffer so we can just re-use it from the start + // Similar to restarting a descriptor pool + tb_reset_descriptor_buffer(rnd_sys, &sys->desc_buffer); + + const uint32_t frame_idx = rnd_sys->frame_idx; + + TB_DYN_ARR_FOREACH(sys->views, view_idx) { + const TbView *view = &TB_DYN_ARR_AT(sys->views, view_idx); + const TbCommonViewData *view_data = &view->view_data; + const TbCommonLightData *light_data = &view->light_data; + + tb_auto tmp_addr = tb_rnd_get_gpu_tmp_addr(rnd_sys); + + // Write view data into the tmp buffer we know will wind up on the GPU + uint64_t view_offset = 0; + tb_rnd_sys_copy_to_tmp_buffer(rnd_sys, sizeof(TbCommonViewData), 0x40, + view_data, &view_offset); + uint64_t light_offset = 0; + tb_rnd_sys_copy_to_tmp_buffer(rnd_sys, sizeof(TbCommonLightData), 0x40, + light_data, &light_offset); + + TB_DYN_ARR_OF(TbDescriptor) descriptors = {0}; + TB_DYN_ARR_RESET(descriptors, rnd_sys->tmp_alloc, 16); + + // Binding 0: Common View Data + TB_DYN_ARR_APPEND( + descriptors, + ((TbDescriptor){ + .type = VK_DESCRIPTOR_TYPE_UNIFORM_BUFFER, + .data = { + .pUniformBuffer = &(VkDescriptorAddressInfoEXT){ + .sType = VK_STRUCTURE_TYPE_DESCRIPTOR_ADDRESS_INFO_EXT, + .address = tmp_addr + view_offset, + .range = sizeof(TbCommonViewData), + }}})); + // Binding 1: Irradiance Map + TB_DYN_ARR_APPEND( + descriptors, + ((TbDescriptor){ + .type = VK_DESCRIPTOR_TYPE_SAMPLED_IMAGE, + .data = { + .pSampledImage = &(VkDescriptorImageInfo){ + .imageLayout = VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL, + .imageView = tb_render_target_get_view( + rt_sys, frame_idx, rt_sys->irradiance_map), + }}})); + // Binding 2: Environment Map + TB_DYN_ARR_APPEND( + descriptors, + ((TbDescriptor){ + .type = VK_DESCRIPTOR_TYPE_SAMPLED_IMAGE, + .data = { + .pSampledImage = &(VkDescriptorImageInfo){ + .imageLayout = VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL, + .imageView = tb_render_target_get_view( + rt_sys, frame_idx, rt_sys->prefiltered_cube), + }}})); + // Binding 3: BRDF + TB_DYN_ARR_APPEND( + descriptors, + ((TbDescriptor){ + .type = VK_DESCRIPTOR_TYPE_SAMPLED_IMAGE, + .data = { + .pSampledImage = &(VkDescriptorImageInfo){ + .imageLayout = VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL, + .imageView = + tb_tex_sys_get_image_view2(it->world, brdf_tex), + }}})); + // Binding 4: Cascaded Shadow Map + TB_DYN_ARR_APPEND( + descriptors, + ((TbDescriptor){ + .type = VK_DESCRIPTOR_TYPE_SAMPLED_IMAGE, + .data = { + .pSampledImage = &(VkDescriptorImageInfo){ + .imageLayout = VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL, + .imageView = tb_render_target_get_view( + rt_sys, frame_idx, rt_sys->shadow_map), + }}})); + // Binding 5: Lighting Data + TB_DYN_ARR_APPEND( + descriptors, + ((TbDescriptor){ + .type = VK_DESCRIPTOR_TYPE_UNIFORM_BUFFER, + .data = { + .pUniformBuffer = &(VkDescriptorAddressInfoEXT){ + .sType = VK_STRUCTURE_TYPE_DESCRIPTOR_ADDRESS_INFO_EXT, + .address = tmp_addr + light_offset, + .range = sizeof(TbCommonLightData), + }}})); + + // Write all descriptors to buffer + TB_DYN_ARR_FOREACH(descriptors, i) { + tb_auto descriptor = TB_DYN_ARR_AT(descriptors, i); + tb_write_desc_to_buffer(rnd_sys, &sys->desc_buffer, i, &descriptor); + } + } + } + +#else TbViewSystemFrameState *state = &sys->frame_states[rnd_sys->frame_idx]; // Allocate all the descriptor sets for this frame { @@ -240,11 +421,11 @@ void view_update_tick(ecs_iter_t *it) { const uint32_t img_count = 4; const uint32_t write_count = buf_count + img_count; - VkWriteDescriptorSet *writes = tb_alloc_nm_tp( + tb_auto writes = tb_alloc_nm_tp( sys->tmp_alloc, (uint64_t)view_count * write_count, VkWriteDescriptorSet); - VkDescriptorBufferInfo *buffer_info = tb_alloc_nm_tp( + tb_auto buffer_info = tb_alloc_nm_tp( sys->tmp_alloc, (uint64_t)view_count * buf_count, VkDescriptorBufferInfo); - VkDescriptorImageInfo *image_info = tb_alloc_nm_tp( + tb_auto image_info = tb_alloc_nm_tp( sys->tmp_alloc, (uint64_t)view_count * img_count, VkDescriptorImageInfo); TB_DYN_ARR_FOREACH(sys->views, view_idx) { const TbView *view = &TB_DYN_ARR_AT(sys->views, view_idx); @@ -291,8 +472,7 @@ void view_update_tick(ecs_iter_t *it) { }; image_info[image_idx + 2] = (VkDescriptorImageInfo){ .imageLayout = VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL, - .imageView = tb_tex_system_get_image_view( - sys->texture_system, sys->texture_system->brdf_tex)}; + .imageView = tb_tex_sys_get_image_view2(it->world, brdf_tex)}; image_info[image_idx + 3] = (VkDescriptorImageInfo){ .imageLayout = VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL, @@ -358,6 +538,7 @@ void view_update_tick(ecs_iter_t *it) { }; } tb_rnd_update_descriptors(rnd_sys, view_count * write_count, writes); +#endif TracyCZoneEnd(ctx); } @@ -368,13 +549,11 @@ void tb_register_view_sys(TbWorld *world) { ECS_COMPONENT_DEFINE(ecs, TbViewSystem); - TbRenderSystem *rnd_sys = ecs_singleton_get_mut(ecs, TbRenderSystem); - TbRenderTargetSystem *rt_sys = - ecs_singleton_get_mut(ecs, TbRenderTargetSystem); - TbTextureSystem *tex_sys = ecs_singleton_get_mut(ecs, TbTextureSystem); + tb_auto rnd_sys = ecs_singleton_get_mut(ecs, TbRenderSystem); + tb_auto rt_sys = ecs_singleton_get_mut(ecs, TbRenderTargetSystem); - TbViewSystem sys = create_view_system(world->gp_alloc, world->tmp_alloc, - rnd_sys, rt_sys, tex_sys); + tb_auto sys = + create_view_system(world->gp_alloc, world->tmp_alloc, rnd_sys, rt_sys); // Sets a singleton based on the value at a pointer ecs_set_ptr(ecs, ecs_id(TbViewSystem), TbViewSystem, &sys); @@ -384,9 +563,9 @@ void tb_register_view_sys(TbWorld *world) { void tb_unregister_view_sys(TbWorld *world) { ecs_world_t *ecs = world->ecs; - - TbViewSystem *sys = ecs_singleton_get_mut(ecs, TbViewSystem); - destroy_view_system(sys); + tb_auto sys = ecs_singleton_get_mut(ecs, TbViewSystem); + tb_auto rnd_sys = ecs_singleton_get_mut(ecs, TbRenderSystem); + destroy_view_system(sys, rnd_sys); ecs_singleton_remove(ecs, TbViewSystem); } @@ -450,7 +629,12 @@ void tb_view_system_set_view_frustum(TbViewSystem *self, TbViewId view, VkDescriptorSet tb_view_system_get_descriptor(TbViewSystem *self, TbViewId view) { if (view >= TB_DYN_ARR_SIZE(self->views)) { - TB_CHECK(false, "TbView Id out of range"); + return VK_NULL_HANDLE; + } + + // Can happen if view system's descriptors weren't ready yet + if (self->frame_states[self->rnd_sys->frame_idx].sets == NULL) { + return VK_NULL_HANDLE; } return self->frame_states[self->rnd_sys->frame_idx].sets[view]; @@ -462,3 +646,33 @@ const TbView *tb_get_view(TbViewSystem *self, TbViewId view) { } return &TB_DYN_ARR_AT(self->views, view); } + +VkDescriptorSetLayout tb_view_sys_get_set_layout(ecs_world_t *ecs) { + tb_auto ctx = ecs_singleton_get_mut(ecs, TbViewSystem); +#if TB_USE_DESC_BUFFER == 1 + return ctx->set_layout2; +#else + return ctx->set_layout; +#endif +} + +VkDescriptorBufferBindingInfoEXT tb_view_sys_get_table_addr(ecs_world_t *ecs, + TbViewId view) { + tb_auto ctx = ecs_singleton_get_mut(ecs, TbViewSystem); + tb_auto addr = ctx->desc_buffer.buffer.address; + tb_auto set_size = ctx->desc_buffer.layout_size; + // An address of 0 indicates an error + if (addr > 0 && set_size > 0) { + addr = addr + ((uint64_t)view * set_size); + } + VkDescriptorBufferBindingInfoEXT binding_info = { + .sType = VK_STRUCTURE_TYPE_DESCRIPTOR_BUFFER_BINDING_INFO_EXT, + .address = addr, + // HACK: Hardcoded same usage from tb_descriptor_buffer.c + .usage = VK_BUFFER_USAGE_RESOURCE_DESCRIPTOR_BUFFER_BIT_EXT | + VK_BUFFER_USAGE_SHADER_DEVICE_ADDRESS_BIT | + VK_BUFFER_USAGE_TRANSFER_SRC_BIT | + VK_BUFFER_USAGE_TRANSFER_DST_BIT, + }; + return binding_info; +} diff --git a/source/visualloggingsystem.c b/source/tb_visual_logging_system.c similarity index 96% rename from source/visualloggingsystem.c rename to source/tb_visual_logging_system.c index 9d0404df..25f5118b 100644 --- a/source/visualloggingsystem.c +++ b/source/tb_visual_logging_system.c @@ -1,20 +1,21 @@ -#include "visualloggingsystem.h" - -#include "assets.h" -#include "cameracomponent.h" -#include "coreuisystem.h" -#include "meshsystem.h" -#include "profiling.h" -#include "renderobjectsystem.h" -#include "renderpipelinesystem.h" -#include "rendersystem.h" +#include "tb_visual_logging_system.h" + +#include "tb_assets.h" +#include "tb_camera_component.h" +#include "tb_common.h" +#include "tb_coreui_system.h" +#include "tb_gltf.h" +#include "tb_imgui.h" +#include "tb_mesh_rnd_sys.h" +#include "tb_mesh_system.h" +#include "tb_profiling.h" +#include "tb_render_object_system.h" +#include "tb_render_pipeline_system.h" +#include "tb_render_system.h" #include "tb_shader_system.h" -#include "tbcommon.h" -#include "tbgltf.h" -#include "tbimgui.h" -#include "transformcomponent.h" -#include "viewsystem.h" -#include "world.h" +#include "tb_transform_component.h" +#include "tb_view_system.h" +#include "tb_world.h" #include @@ -322,16 +323,13 @@ TbVisualLoggingSystem create_visual_logging_system( sys.sphere_pos_offset = index_size + idx_padding; const cgltf_node *node = &data->nodes[0]; - sys.sphere_mesh = tb_mesh_system_load_mesh(mesh_system, asset_path, node); + sys.sphere_mesh2 = + tb_mesh_sys_load_gltf_mesh(ecs, data, asset_path, "sphere", 0); sys.sphere_scale = (float3){node->scale[0], node->scale[1], node->scale[2]}; } - sys.sphere_geom_buffer = - tb_mesh_system_get_gpu_mesh(mesh_system, sys.sphere_mesh); - TB_CHECK(sys.sphere_geom_buffer, "Failed to get gpu buffer for mesh"); - - cgltf_free(data); + // cgltf_free(data); } { @@ -348,7 +346,7 @@ TbVisualLoggingSystem create_visual_logging_system( .setLayoutCount = 1, .pSetLayouts = (VkDescriptorSetLayout[1]){ - view_sys->set_layout, + tb_view_sys_get_set_layout(ecs), }, }; err = tb_rnd_create_pipeline_layout( @@ -400,8 +398,6 @@ void destroy_visual_logging_system(ecs_world_t *ecs, TbVisualLoggingSystem *self) { (void)ecs; #ifndef FINAL - tb_mesh_system_release_mesh_ref(self->mesh_system, self->sphere_mesh); - tb_rnd_destroy_pipe_layout(self->rnd_sys, self->pipe_layout); tb_shader_destroy(ecs, self->shader); @@ -424,6 +420,12 @@ void vlog_draw_tick(ecs_iter_t *it) { return; } + // Requires meshes to be loaded + if (!tb_is_mesh_ready(it->world, sys->sphere_mesh2)) { + TracyCZoneEnd(ctx); + return; + } + // Render primitives from selected frame if (sys->logging && sys->frames.capacity > 0) { const TbVLogFrame *frame = &TB_DYN_ARR_AT(sys->frames, sys->log_frame_idx); @@ -446,7 +448,8 @@ void vlog_draw_tick(ecs_iter_t *it) { *loc_batch = (VLogDrawBatch){ .index_count = sys->sphere_index_count, .pos_offset = sys->sphere_pos_offset, - .shape_geom_buffer = sys->sphere_geom_buffer, + .shape_geom_buffer = + tb_mesh_sys_get_gpu_mesh(it->world, sys->sphere_mesh2), .shape_scale = sys->sphere_scale, .type = TB_VLOG_SHAPE_LOCATION, .view_set = diff --git a/source/tbvkalloc.c b/source/tb_vk_alloc.c similarity index 96% rename from source/tbvkalloc.c rename to source/tb_vk_alloc.c index 7c4c8bc1..9b49e968 100644 --- a/source/tbvkalloc.c +++ b/source/tb_vk_alloc.c @@ -1,8 +1,8 @@ -#include "tbvkalloc.h" +#include "tb_vk_alloc.h" #include "mimalloc.h" -#include "profiling.h" -#include "tbvma.h" +#include "tb_profiling.h" +#include "tb_vma.h" void *tb_vk_alloc_fn(void *pUserData, size_t size, size_t alignment, VkSystemAllocationScope scope) { diff --git a/source/vkdbg.c b/source/tb_vk_dbg.c similarity index 97% rename from source/vkdbg.c rename to source/tb_vk_dbg.c index 8438c089..35e436b1 100644 --- a/source/vkdbg.c +++ b/source/tb_vk_dbg.c @@ -1,6 +1,6 @@ -#include "vkdbg.h" +#include "tb_vk_dbg.h" -#include "tbvk.h" +#include "tb_vk.h" #ifndef FINAL diff --git a/source/vma.cpp b/source/tb_vma.cpp similarity index 58% rename from source/vma.cpp rename to source/tb_vma.cpp index 7499d484..9ca298bb 100644 --- a/source/vma.cpp +++ b/source/tb_vma.cpp @@ -1,3 +1,3 @@ #define VMA_IMPLEMENTATION -#include "tbvma.h" +#include "tb_vma.h" diff --git a/source/world.c b/source/tb_world.c similarity index 60% rename from source/world.c rename to source/tb_world.c index e0ee77dc..8a3cb4e0 100644 --- a/source/world.c +++ b/source/tb_world.c @@ -1,16 +1,18 @@ -#include "world.h" - -#include "allocator.h" -#include "assets.h" - -#include "profiling.h" -#include "simd.h" -#include "tbcommon.h" -#include "tbgltf.h" - -#include "transformcomponent.h" - -#include "inputsystem.h" +#include "tb_world.h" + +#include "tb_allocator.h" +#include "tb_assets.h" +#include "tb_common.h" +#include "tb_gltf.h" +#include "tb_input_system.h" +#include "tb_material_system.h" +#include "tb_mesh_system.h" +#include "tb_profiling.h" +#include "tb_scene.h" +#include "tb_simd.h" +#include "tb_task_scheduler.h" +#include "tb_texture_system.h" +#include "tb_transform_component.h" #include #include @@ -33,6 +35,8 @@ typedef struct TbComponentEntry { char *name; TbRegisterComponentFn reg_fn; TbLoadComponentFn load_fn; + TbReadyComponentFn ready_fn; + ecs_entity_t id; ecs_entity_t desc_id; } TbComponentEntry; @@ -115,7 +119,8 @@ void tb_register_system(const char *name, int32_t priority, static TbComponentRegistry s_comp_reg = {0}; void tb_register_component(const char *name, TbRegisterComponentFn reg_fn, - TbLoadComponentFn load_fn) { + TbLoadComponentFn load_fn, + TbReadyComponentFn ready_fn) { TracyCZoneN(ctx, "Register Component", true); int32_t index = s_comp_reg.count; int32_t next_count = ++s_comp_reg.count; @@ -126,6 +131,7 @@ void tb_register_component(const char *name, TbRegisterComponentFn reg_fn, tb_auto entry = &s_comp_reg.entries[index]; entry->reg_fn = reg_fn; entry->load_fn = load_fn; + entry->ready_fn = ready_fn; entry->name = mi_malloc(name_len); SDL_memset(entry->name, 0, name_len); SDL_strlcpy(entry->name, name, name_len); @@ -205,7 +211,9 @@ bool tb_create_world(const TbWorldDesc *desc, TbWorld *world) { for (int32_t i = 0; i < s_comp_reg.count; ++i) { tb_auto fn = s_comp_reg.entries[i].reg_fn; if (fn) { - s_comp_reg.entries[i].desc_id = fn(world); + tb_auto result = fn(world); + s_comp_reg.entries[i].id = result.type_id; + s_comp_reg.entries[i].desc_id = result.desc_id; } } @@ -228,7 +236,6 @@ bool tb_create_world(const TbWorldDesc *desc, TbWorld *world) { tb_wait_thread_initialized(render_thread); world->render_thread = render_thread; - TB_DYN_ARR_RESET(world->scenes, gp_alloc, 1); // Create all registered systems after sorting by priority { @@ -262,6 +269,9 @@ bool tb_tick_world(TbWorld *world, float delta_seconds) { world->time += (double)delta_seconds; + // Run pinned tasks first + tb_run_pinned_tasks(ecs); + // Tick with flecs if (!ecs_progress(ecs, delta_seconds)) { return false; @@ -281,20 +291,10 @@ bool tb_tick_world(TbWorld *world, float delta_seconds) { return true; } -void tb_clear_world(TbWorld *world) { - TB_DYN_ARR_FOREACH(world->scenes, i) { - TbScene *scene = &TB_DYN_ARR_AT(world->scenes, i); - tb_unload_scene(world, scene); - } - TB_DYN_ARR_CLEAR(world->scenes); -} - void tb_destroy_world(TbWorld *world) { // Clean up singletons ecs_world_t *ecs = world->ecs; - tb_clear_world(world); - // Stop the render thread before we start destroying render objects tb_stop_render_thread(world->render_thread); @@ -313,165 +313,38 @@ void tb_destroy_world(TbWorld *world) { ecs_fini(ecs); } -void load_entity(TbWorld *world, TbScene *scene, json_tokener *tok, - const cgltf_data *data, const char *root_scene_path, - ecs_entity_t parent, const cgltf_node *node) { - TracyCZoneN(ctx, "Load Entity", true); - ecs_world_t *ecs = world->ecs; - ECS_TAG(ecs, EcsDisabled); - ECS_TAG(ecs, EcsPrefab); - - TbAllocator tmp_alloc = world->tmp_alloc; - - // Get extras - json_object *json = NULL; - { - cgltf_size extra_size = 0; - char *extra_json = NULL; - if (node->extras.end_offset != 0 && node->extras.start_offset != 0) { - extra_size = (node->extras.end_offset - node->extras.start_offset) + 1; - extra_json = tb_alloc_nm_tp(tmp_alloc, extra_size, char); - if (cgltf_copy_extras_json(data, &node->extras, extra_json, - &extra_size) != cgltf_result_success) { - extra_size = 0; - extra_json = NULL; - } - } - - if (extra_json) { - json = json_tokener_parse_ex(tok, extra_json, (int32_t)extra_size); - } - } - - // See if the entity is enabled or a prefab - bool enabled = true; - bool prefab = false; - if (json) { - tb_auto enabled_obj = json_object_object_get(json, "enabled"); - if (enabled_obj) { - enabled = (bool)json_object_get_boolean(enabled_obj); - } - tb_auto prefab_obj = json_object_object_get(json, "prefab"); - if (prefab_obj) { - prefab = (bool)json_object_get_boolean(prefab_obj); - } - } - // We inherit some properties from a parent - if (parent != TbInvalidEntityId) { - if (ecs_has(ecs, parent, EcsDisabled)) { - enabled = false; - } - if (ecs_has(ecs, parent, EcsPrefab)) { - prefab = true; - } - } - - const char *name = node->name; - if (name && ecs_lookup(ecs, node->name) != TbInvalidEntityId) { - name = NULL; // Name already exists - } - - // Create an entity - ecs_entity_t ent = 0; - if (prefab) { - ent = ecs_new_prefab(ecs, 0); - } else { - ent = ecs_new_entity(ecs, 0); - } - - if (name) { - ecs_set_name(ecs, ent, name); - } - - // Express heirarchy via flecs - if (parent != TbInvalidEntityId) { - ecs_add_pair(ecs, ent, EcsChildOf, parent); - } +TbScene tb_load_scene(TbWorld *world, const char *scene_path) { + TB_TRACY_SCOPE("Load Scene"); + // Get qualified path to scene asset + char *asset_path = tb_resolve_asset_path(world->tmp_alloc, scene_path); + // Create an entity that represents the scene and trigger an async load + return tb_create_scene(world->ecs, asset_path); +} - // See if there are core components that need construction first +TbLoadComponentFn tb_get_component_load_fn(const char *name) { for (int32_t i = 0; i < s_comp_reg.count; ++i) { - const char *name = s_comp_reg.entries[i].name; + const char *comp_name = s_comp_reg.entries[i].name; tb_auto load_fn = s_comp_reg.entries[i].load_fn; - if (load_fn) { - if (SDL_strcmp(name, "transform") == 0) { - load_fn(world, ent, root_scene_path, node, NULL); - } else if (SDL_strcmp(name, "mesh") == 0 && node->mesh) { - load_fn(world, ent, root_scene_path, node, NULL); - } else if (SDL_strcmp(name, "camera") == 0 && node->camera) { - load_fn(world, ent, root_scene_path, node, NULL); - } else if (SDL_strcmp(name, "light") == 0 && node->light) { - load_fn(world, ent, root_scene_path, node, NULL); - } + if (SDL_strcmp(comp_name, name) == 0) { + return load_fn; } } + return NULL; +} - if (node->children_count > 0) { - tb_auto trans_comp = ecs_get_mut(ecs, ent, TbTransformComponent); - // Make sure this entity actually has a transform - if (trans_comp) { - // Load all children - for (uint32_t i = 0; i < node->children_count; ++i) { - tb_auto child = node->children[i]; - load_entity(world, scene, tok, data, root_scene_path, ent, child); - } +bool tb_enitity_components_ready(ecs_world_t *ecs, ecs_entity_t ent) { + bool ready = true; + for (int32_t i = 0; i < s_comp_reg.count; ++i) { + tb_auto comp_entry = &s_comp_reg.entries[i]; + // If the entity lacks this component, don't test it + if (!ecs_has_id(ecs, ent, comp_entry->id)) { + continue; } - } - - // Add custom components - if (json) { - json_object_object_foreach(json, component_name, component_obj) { - for (int32_t i = 0; i < s_comp_reg.count; ++i) { - const char *name = s_comp_reg.entries[i].name; - tb_auto load_fn = s_comp_reg.entries[i].load_fn; - if (SDL_strcmp(component_name, name) == 0) { - load_fn(world, ent, root_scene_path, node, component_obj); - } - } + ready = comp_entry->ready_fn(ecs, ent); + if (ready == false) { + break; } } - ecs_enable(ecs, ent, enabled); - - TB_DYN_ARR_APPEND(scene->entities, ent); - - TracyCZoneEnd(ctx); -} - -bool tb_load_scene(TbWorld *world, const char *scene_path) { - TracyCZoneN(ctx, "Load Scene", true); - // Get qualified path to scene asset - char *asset_path = tb_resolve_asset_path(world->tmp_alloc, scene_path); - - // Load glb off disk - cgltf_data *data = tb_read_glb(world->gp_alloc, asset_path); - TB_CHECK_RETURN(data, "Failed to load glb", false); - - json_tokener *tok = json_tokener_new(); - - TbScene scene = {0}; - TB_DYN_ARR_RESET(scene.entities, world->gp_alloc, data->scene->nodes_count); - - // Create an entity for each node - for (cgltf_size i = 0; i < data->scene->nodes_count; ++i) { - tb_auto node = data->scene->nodes[i]; - load_entity(world, &scene, tok, data, scene_path, TbInvalidEntityId, node); - } - - TB_DYN_ARR_APPEND(world->scenes, scene); - - json_tokener_free(tok); - - // Clean up gltf file now that it's parsed - cgltf_free(data); - - TracyCZoneEnd(ctx); - return true; -} - -void tb_unload_scene(TbWorld *world, TbScene *scene) { - ecs_world_t *ecs = world->ecs; - // Remove all entities in the scene from the world - TB_DYN_ARR_FOREACH(scene->entities, i) { - ecs_delete(ecs, TB_DYN_ARR_AT(scene->entities, i)); - } + return ready; } diff --git a/source/texturesystem.c b/source/texturesystem.c deleted file mode 100644 index d2d8ec88..00000000 --- a/source/texturesystem.c +++ /dev/null @@ -1,1007 +0,0 @@ -#include "texturesystem.h" - -#include "cgltf.h" -#include "common.hlsli" -#include "hash.h" -#include "profiling.h" -#include "rendersystem.h" -#include "tbktx.h" -#include "world.h" - -#include - -ECS_COMPONENT_DECLARE(TbTextureSystem); - -typedef struct TbTexture { - TbTextureId id; - uint32_t ref_count; - TbHostBuffer host_buffer; - TbImage gpu_image; - VkImageView image_view; -} TBTexture; - -void tb_register_texture_sys(TbWorld *world); -void tb_unregister_texture_sys(TbWorld *world); - -TB_REGISTER_SYS(tb, texture, TB_TEX_SYS_PRIO) - -uint32_t find_tex_by_id(TbTextureSystem *self, TbTextureId id) { - TB_DYN_ARR_FOREACH(self->textures, i) { - if (TB_DYN_ARR_AT(self->textures, i).id == id) { - return i; - } - } - return SDL_MAX_UINT32; -} - -typedef struct KTX2IterData { - VkBuffer buffer; - VkImage image; - TbBufferImageCopy *uploads; - uint64_t offset; -} KTX2IterData; - -static ktx_error_code_e iterate_ktx2_levels(int32_t mip_level, int32_t face, - int32_t width, int32_t height, - int32_t depth, - uint64_t face_lod_size, - void *pixels, void *userdata) { - (void)pixels; - KTX2IterData *user_data = (KTX2IterData *)userdata; - - user_data->uploads[mip_level] = (TbBufferImageCopy){ - .src = user_data->buffer, - .dst = user_data->image, - .region = - { - .bufferOffset = user_data->offset, - .imageSubresource = - { - .aspectMask = VK_IMAGE_ASPECT_COLOR_BIT, - .layerCount = 1, - .baseArrayLayer = face, - .mipLevel = mip_level, - }, - .imageExtent = - { - .width = width, - .height = height, - .depth = depth, - }, - }, - .range = - { - .aspectMask = VK_IMAGE_ASPECT_COLOR_BIT, - .baseArrayLayer = face, - .baseMipLevel = mip_level, - .layerCount = 1, - .levelCount = 1, - }, - }; - - user_data->offset += face_lod_size; - return KTX_SUCCESS; -} - -TbTextureId calc_tex_id(const char *path, const char *name) { - TbTextureId id = tb_hash(0, (const uint8_t *)path, SDL_strlen(path)); - id = tb_hash(id, (const uint8_t *)name, SDL_strlen(name)); - return id; -} - -TbTexture *alloc_tex(TbTextureSystem *self, TbTextureId id) { - uint32_t index = TB_DYN_ARR_SIZE(self->textures); - TbTexture tex = {.id = id, .ref_count = 1}; - TB_DYN_ARR_APPEND(self->textures, tex); - return &TB_DYN_ARR_AT(self->textures, index); -} - -static VkImageType get_ktx2_image_type(const ktxTexture2 *t) { - return (VkImageType)(t->numDimensions - 1); -} - -static VkImageViewType get_ktx2_image_view_type(const ktxTexture2 *t) { - const VkImageType img_type = get_ktx2_image_type(t); - - const bool cube = t->isCubemap; - const bool array = t->isArray; - - if (img_type == VK_IMAGE_TYPE_1D) { - if (array) { - return VK_IMAGE_VIEW_TYPE_1D_ARRAY; - } else { - return VK_IMAGE_VIEW_TYPE_1D; - } - } else if (img_type == VK_IMAGE_TYPE_2D) { - if (array) { - return VK_IMAGE_VIEW_TYPE_2D_ARRAY; - } else { - return VK_IMAGE_VIEW_TYPE_2D; - } - - } else if (img_type == VK_IMAGE_TYPE_3D) { - // No such thing as a 3D array - return VK_IMAGE_VIEW_TYPE_3D; - } else if (cube) { - if (array) { - return VK_IMAGE_VIEW_TYPE_CUBE_ARRAY; - } - return VK_IMAGE_VIEW_TYPE_CUBE; - } - - TB_CHECK(false, "Invalid"); - return VK_IMAGE_VIEW_TYPE_MAX_ENUM; -} - -TbTextureId tb_tex_system_create_texture_ktx2(TbTextureSystem *self, - const char *path, - const char *name, - ktxTexture2 *ktx) { - TbTextureId id = calc_tex_id(path, name); - uint32_t index = find_tex_by_id(self, id); - - // Index was found, we can just inc the ref count and early out - if (index != SDL_MAX_UINT32) { - tb_tex_system_take_tex_ref(self, id); - return id; - } - - // If texture wasn't found, load it now - VkResult err = VK_SUCCESS; - TbRenderSystem *rnd_sys = self->rnd_sys; - - TbTexture *texture = alloc_tex(self, id); - - // Load texture - { - size_t host_buffer_size = ktx->dataSize; - uint32_t width = ktx->baseWidth; - uint32_t height = ktx->baseHeight; - uint32_t depth = ktx->baseDepth; - uint32_t layers = ktx->numLayers; - uint32_t mip_levels = ktx->numLevels; - VkFormat format = (VkFormat)ktx->vkFormat; - - TB_CHECK_RETURN(ktx->generateMipmaps == false, - "Not expecting to have to generate mips", - TbInvalidTextureId); - - // Allocate gpu image - { - VkImageCreateInfo create_info = { - .sType = VK_STRUCTURE_TYPE_IMAGE_CREATE_INFO, - .arrayLayers = layers, - .extent = - (VkExtent3D){ - .width = width, - .height = height, - .depth = depth, - }, - .format = format, - .imageType = get_ktx2_image_type(ktx), - .mipLevels = mip_levels, - .samples = VK_SAMPLE_COUNT_1_BIT, - .usage = VK_IMAGE_USAGE_TRANSFER_DST_BIT | VK_IMAGE_USAGE_SAMPLED_BIT, - }; - err = tb_rnd_sys_create_gpu_image(rnd_sys, ktx->pData, host_buffer_size, - &create_info, name, &texture->gpu_image, - &texture->host_buffer); - TB_VK_CHECK_RET(err, "Failed to allocate gpu image for texture", - TbInvalidTextureId); - } - - // Create image view - { - VkImageViewCreateInfo create_info = { - .sType = VK_STRUCTURE_TYPE_IMAGE_VIEW_CREATE_INFO, - .image = texture->gpu_image.image, - .viewType = get_ktx2_image_view_type(ktx), - .format = format, - .components = - { - VK_COMPONENT_SWIZZLE_R, - VK_COMPONENT_SWIZZLE_G, - VK_COMPONENT_SWIZZLE_B, - VK_COMPONENT_SWIZZLE_A, - }, - .subresourceRange = - { - VK_IMAGE_ASPECT_COLOR_BIT, - 0, - mip_levels, - 0, - layers, - }, - }; - char view_name[100] = {0}; - SDL_snprintf(view_name, 100, "%s Image TbView", name); // NOLINT - err = tb_rnd_create_image_view(rnd_sys, &create_info, view_name, - &texture->image_view); - TB_VK_CHECK_RET(err, "Failed to allocate image view for texture", - TbInvalidTextureId); - } - - // Issue uploads - { - TbBufferImageCopy *uploads = - tb_alloc_nm_tp(rnd_sys->tmp_alloc, mip_levels, TbBufferImageCopy); - - KTX2IterData iter_data = { - .buffer = texture->host_buffer.buffer, - .image = texture->gpu_image.image, - .offset = texture->host_buffer.offset, - .uploads = uploads, - }; - -#pragma clang diagnostic push -#pragma clang diagnostic ignored "-Wincompatible-pointer-types" - // Iterate over texture levels to fill out upload requests - ktxTexture_IterateLevels(ktx, iterate_ktx2_levels, &iter_data); -#pragma clang diagnostic pop - - // Will handle transitioning the image's layout to shader read only - tb_rnd_upload_buffer_to_image(rnd_sys, uploads, mip_levels); - } - } - - return id; -} - -TbTextureSystem create_texture_system(TbAllocator gp_alloc, - TbAllocator tmp_alloc, - TbRenderSystem *rnd_sys) { - TbTextureSystem sys = { - .tmp_alloc = tmp_alloc, - .gp_alloc = gp_alloc, - .rnd_sys = rnd_sys, - .default_color_tex = TbInvalidTextureId, - .default_normal_tex = TbInvalidTextureId, - .default_metal_rough_tex = TbInvalidTextureId, - }; - - // Create descriptor set layout - { - const VkDescriptorBindingFlags flags = - VK_DESCRIPTOR_BINDING_VARIABLE_DESCRIPTOR_COUNT_BIT | - VK_DESCRIPTOR_BINDING_PARTIALLY_BOUND_BIT | - VK_DESCRIPTOR_BINDING_UPDATE_AFTER_BIND_BIT | - VK_DESCRIPTOR_BINDING_UPDATE_UNUSED_WHILE_PENDING_BIT; - const uint32_t binding_count = 1; - VkDescriptorSetLayoutCreateInfo create_info = { - .sType = VK_STRUCTURE_TYPE_DESCRIPTOR_SET_LAYOUT_CREATE_INFO, - .flags = VK_DESCRIPTOR_SET_LAYOUT_CREATE_UPDATE_AFTER_BIND_POOL_BIT, - .pNext = - &(VkDescriptorSetLayoutBindingFlagsCreateInfo){ - .sType = - VK_STRUCTURE_TYPE_DESCRIPTOR_SET_LAYOUT_BINDING_FLAGS_CREATE_INFO, - .bindingCount = binding_count, - .pBindingFlags = - (VkDescriptorBindingFlags[binding_count]){flags}, - }, - .bindingCount = binding_count, - .pBindings = - (VkDescriptorSetLayoutBinding[binding_count]){ - { - .binding = 0, - .descriptorCount = 2048, // HACK: High upper bound - .descriptorType = VK_DESCRIPTOR_TYPE_SAMPLED_IMAGE, - .stageFlags = VK_SHADER_STAGE_FRAGMENT_BIT, - }, - }, - }; - tb_rnd_create_set_layout(rnd_sys, &create_info, "Texture Table Layout", - &sys.set_layout); - } - - // Init textures dyn array - TB_DYN_ARR_RESET(sys.textures, sys.gp_alloc, 4); - - // All white 2x2 RGBA image - { - const uint8_t pixels[] = { - 255, 255, 255, 255, 255, 255, 255, 255, - 255, 255, 255, 255, 255, 255, 255, 255, - }; - sys.default_color_tex = tb_tex_system_create_texture( - &sys, "", "Default Color Texture", TB_TEX_USAGE_COLOR, 2, 2, pixels, - sizeof(pixels)); - } - // 2x2 blank normal image - { - const uint8_t pixels[] = { - 0x7E, 0x7E, 0xFF, 255, 0x7E, 0x7E, 0xFF, 255, - 0x7E, 0x7E, 0xFF, 255, 0x7E, 0x7E, 0xFF, 255, - }; - sys.default_normal_tex = tb_tex_system_create_texture( - &sys, "", "Default Normal Texture", TB_TEX_USAGE_NORMAL, 2, 2, pixels, - sizeof(pixels)); - } - // 2x2 blank metal rough image - { - const uint8_t pixels[] = { - 255, 255, 255, 255, 255, 255, 255, 255, - 255, 255, 255, 255, 255, 255, 255, 255, - }; - sys.default_metal_rough_tex = tb_tex_system_create_texture( - &sys, "", "Default Metal Rough Texture", TB_TEX_USAGE_METAL_ROUGH, 2, 2, - pixels, sizeof(pixels)); - } - // Load BRDF LUT texture - { - const char *path = ASSET_PREFIX "textures/brdf.ktx2"; - - ktxTexture2 *ktx = NULL; - { - ktxTextureCreateFlags flags = KTX_TEXTURE_CREATE_LOAD_IMAGE_DATA_BIT; - - // We need to open this file with SDL_RWops because on a platform like - // android where the asset lives in package storage, this is the best way - // to actually open the file you're looking for - SDL_RWops *tex_file = SDL_RWFromFile(path, "rb"); - size_t tex_size = SDL_RWsize(tex_file); - // We never free this since the BRDF LUT texture is a once time - // importance - // We *could* construct a KTX stream that directly reads the file - // without reading to memory first but that's a lot of work for a single - // texture - uint8_t *tex_data = tb_alloc(sys.gp_alloc, tex_size); - SDL_RWread(tex_file, (void *)tex_data, tex_size); - SDL_RWclose(tex_file); - - ktx_error_code_e err = - ktxTexture2_CreateFromMemory(tex_data, tex_size, flags, &ktx); - TB_CHECK(err == KTX_SUCCESS, "Failed to create KTX texture from memory"); - - bool needs_transcoding = ktxTexture2_NeedsTranscoding(ktx); - if (needs_transcoding) { - // TODO: pre-calculate the best format for the platform - err = ktxTexture2_TranscodeBasis(ktx, KTX_TTF_BC7_RGBA, 0); - TB_CHECK(err == KTX_SUCCESS, "Failed transcode basis texture"); - } - } - - // Create texture from ktx2 texture - sys.brdf_tex = - tb_tex_system_create_texture_ktx2(&sys, path, "BRDF LUT", ktx); - } - - return sys; -} - -void destroy_texture_system(TbTextureSystem *self) { - tb_tex_system_release_texture_ref(self, self->default_metal_rough_tex); - tb_tex_system_release_texture_ref(self, self->default_normal_tex); - tb_tex_system_release_texture_ref(self, self->default_color_tex); - tb_tex_system_release_texture_ref(self, self->brdf_tex); - - TB_DYN_ARR_FOREACH(self->textures, i) { - if (TB_DYN_ARR_AT(self->textures, i).ref_count != 0) { - TB_CHECK(false, "Leaking textures"); - } - } - TB_DYN_ARR_DESTROY(self->textures); - - *self = (TbTextureSystem){ - .default_color_tex = TbInvalidTextureId, - .default_normal_tex = TbInvalidTextureId, - .default_metal_rough_tex = TbInvalidTextureId, - }; -} - -void tick_texture_system(ecs_iter_t *it) { - ecs_world_t *ecs = it->world; - - tb_auto tex_sys = ecs_singleton_get_mut(ecs, TbTextureSystem); - tb_auto rnd_sys = ecs_singleton_get_mut(ecs, TbRenderSystem); - - const uint64_t incoming_tex_count = TB_DYN_ARR_SIZE(tex_sys->textures); - if (incoming_tex_count != tex_sys->set_pool.capacity) { - tex_sys->set_pool.capacity = incoming_tex_count; - const uint64_t desc_count = tex_sys->set_pool.capacity; - - // Re-create pool and allocate the one set that everything will be bound to - { - VkDescriptorPoolCreateInfo create_info = { - .sType = VK_STRUCTURE_TYPE_DESCRIPTOR_POOL_CREATE_INFO, - .flags = VK_DESCRIPTOR_POOL_CREATE_UPDATE_AFTER_BIND_BIT, - .maxSets = tex_sys->set_pool.capacity, - .poolSizeCount = 1, - .pPoolSizes = - (VkDescriptorPoolSize[1]){ - { - .type = VK_DESCRIPTOR_TYPE_SAMPLED_IMAGE, - .descriptorCount = desc_count * 4, - }, - }, - }; - VkDescriptorSetVariableDescriptorCountAllocateInfo alloc_info = { - .sType = - VK_STRUCTURE_TYPE_DESCRIPTOR_SET_VARIABLE_DESCRIPTOR_COUNT_ALLOCATE_INFO, - .descriptorSetCount = 1, - .pDescriptorCounts = (uint32_t[1]){incoming_tex_count}, - }; - tb_rnd_resize_desc_pool(rnd_sys, &create_info, &tex_sys->set_layout, - &alloc_info, &tex_sys->set_pool, 1); - } - } - - // Write all textures into the descriptor set table - TB_DYN_ARR_OF(VkWriteDescriptorSet) writes = {0}; - { - tb_auto tex_count = TB_DYN_ARR_SIZE(tex_sys->textures); - TB_DYN_ARR_RESET(writes, tex_sys->tmp_alloc, tex_count); - tb_auto image_info = - tb_alloc_nm_tp(tex_sys->tmp_alloc, tex_count, VkDescriptorImageInfo); - - TB_DYN_ARR_FOREACH(tex_sys->textures, i) { - tb_auto texture = &TB_DYN_ARR_AT(tex_sys->textures, i); - - image_info[i] = (VkDescriptorImageInfo){ - .imageLayout = VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL, - .imageView = texture->image_view, - }; - - tb_auto write = (VkWriteDescriptorSet){ - .sType = VK_STRUCTURE_TYPE_WRITE_DESCRIPTOR_SET, - .descriptorCount = 1, - .descriptorType = VK_DESCRIPTOR_TYPE_SAMPLED_IMAGE, - .dstSet = tb_tex_sys_get_set(tex_sys), - .dstArrayElement = i, - .pImageInfo = &image_info[i], - }; - TB_DYN_ARR_APPEND(writes, write); - } - } - tb_rnd_update_descriptors(rnd_sys, TB_DYN_ARR_SIZE(writes), writes.data); -} - -void tb_register_texture_sys(TbWorld *world) { - TracyCZoneN(ctx, "Register Texture Sys", true); - ecs_world_t *ecs = world->ecs; - - ECS_COMPONENT_DEFINE(ecs, TbTextureSystem); - - TbRenderSystem *rnd_sys = ecs_singleton_get_mut(ecs, TbRenderSystem); - ecs_singleton_modified(ecs, TbRenderSystem); - - TbTextureSystem sys = - create_texture_system(world->gp_alloc, world->tmp_alloc, rnd_sys); - - ECS_SYSTEM(ecs, tick_texture_system, EcsPreStore, - TbTextureSystem(TbTextureSystem)); - - // Sets a singleton based on the value at a pointer - ecs_set_ptr(ecs, ecs_id(TbTextureSystem), TbTextureSystem, &sys); - TracyCZoneEnd(ctx); -} - -void tb_unregister_texture_sys(TbWorld *world) { - ecs_world_t *ecs = world->ecs; - - TbTextureSystem *sys = ecs_singleton_get_mut(ecs, TbTextureSystem); - destroy_texture_system(sys); - ecs_singleton_remove(ecs, TbTextureSystem); -} - -VkDescriptorSet tb_tex_sys_get_set(TbTextureSystem *self) { - return self->set_pool.sets[0]; -} - -uint32_t tb_tex_system_get_index(TbTextureSystem *self, TbTextureId tex) { - const uint32_t index = find_tex_by_id(self, tex); - TB_CHECK_RETURN(index != SDL_MAX_UINT32, - "Failed to find texture by id when retrieving image index", - SDL_MAX_UINT32); - return index; -} - -VkImageView tb_tex_system_get_image_view(TbTextureSystem *self, - TbTextureId tex) { - const uint32_t index = find_tex_by_id(self, tex); - TB_CHECK_RETURN(index != SDL_MAX_UINT32, - "Failed to find texture by id when retrieving image view", - VK_NULL_HANDLE); - - return TB_DYN_ARR_AT(self->textures, index).image_view; -} - -TbTextureId tb_tex_system_create_texture(TbTextureSystem *self, - const char *path, const char *name, - TbTextureUsage usage, uint32_t width, - uint32_t height, const uint8_t *pixels, - uint64_t size) { - TbTextureId id = calc_tex_id(path, name); - uint32_t index = find_tex_by_id(self, id); - - // Index was found, we can just inc the ref count and early out - if (index != SDL_MAX_UINT32) { - tb_tex_system_take_tex_ref(self, id); - return id; - } - - // If texture wasn't found, load it now - VkResult err = VK_SUCCESS; - TbRenderSystem *rnd_sys = self->rnd_sys; - - TbTexture *texture = alloc_tex(self, id); - - // Load texture - { - // Determine format based on usage - VkFormat format = VK_FORMAT_R8G8B8A8_UNORM; - if (usage == TB_TEX_USAGE_COLOR) { - format = VK_FORMAT_R8G8B8A8_SRGB; - } else if (usage == TB_TEX_USAGE_BRDF) { - format = VK_FORMAT_R32G32_SFLOAT; - } - - // Allocate gpu image - { - // TODO: Think about mip maps - VkImageCreateInfo create_info = { - .sType = VK_STRUCTURE_TYPE_IMAGE_CREATE_INFO, - .arrayLayers = 1, - .extent = - (VkExtent3D){ - .width = width, - .height = height, - .depth = 1, - }, - .format = format, - .imageType = VK_IMAGE_TYPE_2D, - .mipLevels = 1, - .samples = VK_SAMPLE_COUNT_1_BIT, - .usage = VK_IMAGE_USAGE_TRANSFER_DST_BIT | VK_IMAGE_USAGE_SAMPLED_BIT, - }; - err = tb_rnd_sys_create_gpu_image(rnd_sys, pixels, size, &create_info, - name, &texture->gpu_image, - &texture->host_buffer); - TB_VK_CHECK_RET(err, "Failed to allocate gpu image for texture", - TbInvalidTextureId); - } - - // Create image view - { - VkImageViewCreateInfo create_info = { - .sType = VK_STRUCTURE_TYPE_IMAGE_VIEW_CREATE_INFO, - .image = texture->gpu_image.image, - .viewType = VK_IMAGE_VIEW_TYPE_2D, - .format = format, - .components = - { - VK_COMPONENT_SWIZZLE_R, - VK_COMPONENT_SWIZZLE_G, - VK_COMPONENT_SWIZZLE_B, - VK_COMPONENT_SWIZZLE_A, - }, - .subresourceRange = - { - VK_IMAGE_ASPECT_COLOR_BIT, - 0, - 1, - 0, - 1, - }, - }; - char view_name[100] = {0}; - SDL_snprintf(view_name, 100, "%s Image TbView", name); // NOLINT - err = tb_rnd_create_image_view(rnd_sys, &create_info, view_name, - &texture->image_view); - TB_VK_CHECK_RET(err, "Failed to allocate image view for texture", - TbInvalidTextureId); - } - - // Issue upload - { - TbBufferImageCopy upload = { - .src = texture->host_buffer.buffer, - .dst = texture->gpu_image.image, - .region = - { - .bufferOffset = texture->host_buffer.offset, - .imageSubresource = - { - .aspectMask = VK_IMAGE_ASPECT_COLOR_BIT, - .layerCount = 1, - }, - .imageExtent = - { - .width = width, - .height = height, - .depth = 1, - }, - }, - .range = - { - .aspectMask = VK_IMAGE_ASPECT_COLOR_BIT, - .baseArrayLayer = 0, - .baseMipLevel = 0, - .layerCount = 1, - .levelCount = 1, - }, - }; - // Will handle transitioning the image's layout to shader read only - tb_rnd_upload_buffer_to_image(rnd_sys, &upload, 1); - } - } - - return id; -} - -TbTextureId tb_tex_system_alloc_texture(TbTextureSystem *self, const char *name, - const VkImageCreateInfo *create_info) { - TbTextureId id = calc_tex_id("", name); - uint32_t index = find_tex_by_id(self, id); - - // Index was found, we can just inc the ref count and early out - if (index != SDL_MAX_UINT32) { - tb_tex_system_take_tex_ref(self, id); - return id; - } - - VkResult err = VK_SUCCESS; - TbRenderSystem *rnd_sys = self->rnd_sys; - - TbTexture *texture = alloc_tex(self, id); - - { - const VkFormat format = create_info->format; - // Allocate image - { - err = tb_rnd_sys_alloc_gpu_image(rnd_sys, create_info, 0, name, - &texture->gpu_image); - TB_VK_CHECK_RET(err, "Failed to allocate gpu image for texture", - TbInvalidTextureId); - } - - // Create image view - { - VkImageViewCreateInfo create_info = { - .sType = VK_STRUCTURE_TYPE_IMAGE_VIEW_CREATE_INFO, - .image = texture->gpu_image.image, - .viewType = VK_IMAGE_VIEW_TYPE_2D, - .format = format, - .components = - { - VK_COMPONENT_SWIZZLE_R, - VK_COMPONENT_SWIZZLE_G, - VK_COMPONENT_SWIZZLE_B, - VK_COMPONENT_SWIZZLE_A, - }, - .subresourceRange = - { - VK_IMAGE_ASPECT_COLOR_BIT, - 0, - 1, - 0, - 1, - }, - }; - char view_name[100] = {0}; - SDL_snprintf(view_name, 100, "%s Image TbView", name); // NOLINT - err = tb_rnd_create_image_view(rnd_sys, &create_info, view_name, - &texture->image_view); - TB_VK_CHECK_RET(err, "Failed to allocate image view for texture", - TbInvalidTextureId); - } - } - - return id; -} - -TbTextureId tb_tex_system_import_texture(TbTextureSystem *self, - const char *name, const TbImage *image, - VkFormat format) { - TbTextureId id = calc_tex_id("", name); - uint32_t index = find_tex_by_id(self, id); - - // Index was found, we can just inc the ref count and early out - if (index != SDL_MAX_UINT32) { - tb_tex_system_take_tex_ref(self, id); - return id; - } - - VkResult err = VK_SUCCESS; - TbRenderSystem *rnd_sys = self->rnd_sys; - - TbTexture *texture = alloc_tex(self, id); - - // Create image view - { - VkImageViewCreateInfo create_info = { - .sType = VK_STRUCTURE_TYPE_IMAGE_VIEW_CREATE_INFO, - .image = texture->gpu_image.image, - .viewType = VK_IMAGE_VIEW_TYPE_2D, - .format = format, - .components = - { - VK_COMPONENT_SWIZZLE_R, - VK_COMPONENT_SWIZZLE_G, - VK_COMPONENT_SWIZZLE_B, - VK_COMPONENT_SWIZZLE_A, - }, - .subresourceRange = - { - VK_IMAGE_ASPECT_COLOR_BIT, - 0, - 1, - 0, - 1, - }, - }; - char view_name[100] = {0}; - SDL_snprintf(view_name, 100, "%s Image TbView", name); // NOLINT - err = tb_rnd_create_image_view(rnd_sys, &create_info, view_name, - &texture->image_view); - TB_VK_CHECK_RET(err, "Failed to allocate image view for texture", - TbInvalidTextureId); - } - - texture->gpu_image = *image; - - return id; -} - -TbTextureId tb_tex_system_load_texture(TbTextureSystem *self, const char *path, - const char *name, - const cgltf_texture *texture) { - TracyCZoneN(ctx, "Load Texture", true); - // Must use basisu image - TB_CHECK_RETURN(texture->has_basisu, "Expecting basisu image", - TbInvalidTextureId); - - const cgltf_image *image = texture->basisu_image; - const cgltf_buffer_view *image_view = image->buffer_view; - const cgltf_buffer *image_data = image_view->buffer; - - // Points to some jpg/png whatever image format data - uint8_t *raw_data = (uint8_t *)(image_data->data) + image_view->offset; - const int32_t raw_size = (int32_t)image_view->size; - - TB_CHECK_RETURN(image->buffer_view->buffer->uri == NULL, - "Not setup to load data from uri", TbInvalidTextureId); - - // Load the ktx texture - ktxTexture2 *ktx = NULL; - { - ktxTextureCreateFlags flags = KTX_TEXTURE_CREATE_LOAD_IMAGE_DATA_BIT; - - ktx_error_code_e err = - ktxTexture2_CreateFromMemory(raw_data, raw_size, flags, &ktx); - TB_CHECK_RETURN(err == KTX_SUCCESS, - "Failed to create KTX texture from memory", - TbInvalidTextureId); - - bool needs_transcoding = ktxTexture2_NeedsTranscoding(ktx); - if (needs_transcoding) { - // TODO: pre-calculate the best format for the platform - err = ktxTexture2_TranscodeBasis(ktx, KTX_TTF_BC7_RGBA, 0); - TB_CHECK_RETURN(err == KTX_SUCCESS, "Failed transcode basis texture", - TbInvalidTextureId); - } - } - - // Create texture from ktx2 texture - TbTextureId tex = tb_tex_system_create_texture_ktx2(self, path, name, ktx); - TracyCZoneEnd(ctx); - return tex; -} - -bool tb_tex_system_take_tex_ref(TbTextureSystem *self, TbTextureId id) { - uint32_t index = find_tex_by_id(self, id); - TB_CHECK_RETURN(index != SDL_MAX_UINT32, "Failed to find texture", false); - - TB_DYN_ARR_AT(self->textures, index).ref_count++; - return true; -} - -void tb_tex_system_release_texture_ref(TbTextureSystem *self, TbTextureId tex) { - const uint32_t index = find_tex_by_id(self, tex); - if (index == SDL_MAX_UINT32) { - TB_CHECK(false, "Failed to find texture to release"); - return; - } - - TbTexture *texture = &TB_DYN_ARR_AT(self->textures, index); - - if (texture->ref_count == 0) { - TB_CHECK(false, "Tried to release reference to texture with 0 ref count"); - return; - } - texture->ref_count--; - - if (texture->ref_count == 0) { - // Free the mesh at this index - VmaAllocator vma_alloc = self->rnd_sys->vma_alloc; - - TbHostBuffer *host_buf = &texture->host_buffer; - TbImage *gpu_img = &texture->gpu_image; - VkImageView *view = &texture->image_view; - - vmaUnmapMemory(vma_alloc, host_buf->alloc); - - vmaDestroyBuffer(vma_alloc, host_buf->buffer, host_buf->alloc); - vmaDestroyImage(vma_alloc, gpu_img->image, gpu_img->alloc); - vkDestroyImageView(self->rnd_sys->render_thread->device, *view, - &self->rnd_sys->vk_host_alloc_cb); - - *host_buf = (TbHostBuffer){0}; - *gpu_img = (TbImage){0}; - *view = VK_NULL_HANDLE; - } -} - -typedef struct TbTextureSysCtx { - ecs_entity_t ctx_ent; - VkDescriptorSetLayout set_layout; - TbFrameDescriptorPoolList frame_pools; -} TbTextureSysCtx; -ECS_COMPONENT_DECLARE(TbTextureSysCtx); - -// Describes the creation of a texture resource of a particular size from raw -// RGBA8 bytes -typedef struct TbTextureCreateRequest { - TbTextureUsage usage; - uint32_t width; - uint32_t height; - const uint8_t *pixels; - uint64_t size; -} TbTextureCreateRequest; -ECS_COMPONENT_DECLARE(TbTextureCreateRequest); - -// Describes the creation of a texture from a cgltf texture pointer -typedef struct TbTextureGLTFLoadRequest { - const char *path; - const char *mat_name; // TODO: Should be an entity id - TbTextureUsage usage; -} TbTextureGLTFLoadRequest; -ECS_COMPONENT_DECLARE(TbTextureGLTFLoadRequest); - -ECS_TAG_DECLARE(TbTextureLoaded); -ECS_TAG_DECLARE(TbTextureReady); -ECS_TAG_DECLARE(TbNeedTexDescUpdate); - -ecs_entity_t tb_tex_sys_load_mat_tex(ecs_world_t *ecs, const char *path, - const char *mat_name, - TbTextureUsage usage) { - tb_auto sys_ctx = ecs_singleton_get(ecs, TbTextureSysCtx); - - // Create a texture entity - ecs_entity_t tex_ent = ecs_new_entity(ecs, 0); - - // GLTFpack strips image names so we have to synthesize something - { - const uint32_t image_name_max = 100; - char image_name[image_name_max] = {0}; - - switch (usage) { - case TB_TEX_USAGE_BRDF: - default: - TB_CHECK(false, - "Material textures should have Color, Metal or Normal usage"); - break; - case TB_TEX_USAGE_COLOR: - SDL_snprintf(image_name, image_name_max, "%s_color", mat_name); - break; - case TB_TEX_USAGE_METAL_ROUGH: - SDL_snprintf(image_name, image_name_max, "%s_metal", mat_name); - break; - case TB_TEX_USAGE_NORMAL: - SDL_snprintf(image_name, image_name_max, "%s_normal", mat_name); - break; - } - - ecs_set_name(ecs, tex_ent, image_name); - } - - // It is a child of the texture system context singleton - ecs_entity_t ctx_ent = sys_ctx->ctx_ent; - ecs_add_pair(ecs, tex_ent, EcsChildOf, ctx_ent); - - // Append a texture load request onto the entity to schedule loading - // Or do we create a task pinned on the loading thread? - ecs_set(ecs, tex_ent, TbTextureGLTFLoadRequest, {path, mat_name, usage}); - - return tex_ent; -} - -void tb_load_mat_textures_sys(ecs_iter_t *it) { - tb_auto tex_ctx = ecs_field(it, TbTextureSysCtx, 1); - tb_auto load_tasks = ecs_field(it, TbTextureGLTFLoadRequest, 2); - - // Iterate texture load tasks - for (int32_t i = 0; i < it->count; ++i) { - tb_auto load_task = load_tasks[i]; - (void)load_task; - tb_auto tex_ent = it->entities[i]; - - // If a texture load task is completed we take the resulting texture data - // and put it in an ecs component - if (true /*load_task_completed*/) { - // We can now mark the texture entity as loaded - ecs_remove(it->world, tex_ent, TbTextureGLTFLoadRequest); - ecs_add(it->world, tex_ent, TbTextureLoaded); - ecs_add(it->world, tex_ctx->ctx_ent, TbNeedTexDescUpdate); - } - } -} - -void tb_tex_sys_update_descriptors(ecs_iter_t *it) { - TracyCZoneNC(ctx, "Texture Descriptor Update System", - TracyCategoryColorRendering, true); - tb_auto world = ecs_field(it, TbWorldRef, 1)->world; - tb_auto rnd_sys = ecs_field(it, TbRenderSystem, 2); - tb_auto tex_ctx = ecs_field(it, TbTextureSysCtx, 3); - - tb_auto textures = ecs_field(it, TbTexture, 4); - - const uint64_t tex_count = it->count; - - // Update the descriptor pool - { - VkDescriptorPoolCreateInfo create_info = { - .sType = VK_STRUCTURE_TYPE_DESCRIPTOR_POOL_CREATE_INFO, - .flags = VK_DESCRIPTOR_POOL_CREATE_UPDATE_AFTER_BIND_BIT, - .maxSets = 4, - .poolSizeCount = 1, - .pPoolSizes = - (VkDescriptorPoolSize[1]){ - { - .type = VK_DESCRIPTOR_TYPE_SAMPLED_IMAGE, - .descriptorCount = tex_count * 4, - }, - }, - }; - tb_auto layouts = - tb_alloc_nm_tp(world->tmp_alloc, tex_count, VkDescriptorSetLayout); - for (uint64_t i = 0; i < tex_count; ++i) { - layouts[i] = tex_ctx->set_layout; - } - tb_rnd_frame_desc_pool_tick(rnd_sys, &create_info, layouts, NULL, - tex_ctx->frame_pools.pools, tex_count); - } - - // Write descriptor updates - { - // Write all textures into the descriptor set table - TB_DYN_ARR_OF(VkWriteDescriptorSet) writes = {0}; - TB_DYN_ARR_RESET(writes, world->tmp_alloc, tex_count); - tb_auto image_info = - tb_alloc_nm_tp(world->tmp_alloc, tex_count, VkDescriptorImageInfo); - - for (uint64_t i = 0; i < tex_count; ++i) { - tb_auto texture = &textures[i]; - - image_info[i] = (VkDescriptorImageInfo){ - .imageLayout = VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL, - .imageView = texture->image_view, - }; - - tb_auto write = (VkWriteDescriptorSet){ - .sType = VK_STRUCTURE_TYPE_WRITE_DESCRIPTOR_SET, - .descriptorCount = 1, - .descriptorType = VK_DESCRIPTOR_TYPE_SAMPLED_IMAGE, - .dstSet = tb_rnd_frame_desc_pool_get_set( - rnd_sys, tex_ctx->frame_pools.pools, 0), - .dstArrayElement = i, - .pImageInfo = &image_info[i], - }; - TB_DYN_ARR_APPEND(writes, write); - - // Mark the texture as ready to be used now that its in the texture - // descriptor pool - ecs_add(it->world, it->entities[i], TbTextureReady); - } - - tb_rnd_update_descriptors(rnd_sys, TB_DYN_ARR_SIZE(writes), writes.data); - } - - // All done, remove the tag that signaled this system to run - ecs_remove(it->world, tex_ctx->ctx_ent, TbNeedTexDescUpdate); - - TracyCZoneEnd(ctx); -} - -bool tb_is_tex_loaded(ecs_world_t *ecs, ecs_entity_t tex_ent) { - return ecs_has(ecs, tex_ent, TbTextureLoaded); -} diff --git a/source/upsample.hlsl.cs b/source/upsample.hlsl.cs index 22b85da1..85c223a8 100644 --- a/source/upsample.hlsl.cs +++ b/source/upsample.hlsl.cs @@ -1,4 +1,4 @@ -#include "bloom.h" +#include "tb_bloom.h" Texture2D input : register(t0, space0); RWTexture2D output : register(u1, space0); diff --git a/viewer/source/main.c b/viewer/source/main.c index deadf177..a50bdf5e 100644 --- a/viewer/source/main.c +++ b/viewer/source/main.c @@ -1,15 +1,15 @@ #include -#include "profiling.h" -#include "settings.h" -#include "shadercommon.h" -#include "simd.h" -#include "tbcommon.h" -#include "tbengineconfig.h" -#include "tbsdl.h" -#include "tbvk.h" -#include "tbvma.h" -#include "world.h" +#include "tb_common.h" +#include "tb_engine_config.h" +#include "tb_profiling.h" +#include "tb_sdl.h" +#include "tb_settings.h" +#include "tb_shader_common.h" +#include "tb_simd.h" +#include "tb_vk.h" +#include "tb_vma.h" +#include "tb_world.h" #include "viewersystem.h" @@ -101,7 +101,7 @@ int32_t main(int32_t argc, char *argv[]) { if (viewer->unload_scene_signal) { // TODO: Properly wait for the render thread to be finished otherwise // we'll destroy resources in flight - tb_unload_scene(&world, &world.scenes.data[0]); + // tb_unload_scene(&world, &world.scenes.data[0]); viewer->unload_scene_signal = false; } if (viewer->load_scene_signal) { @@ -133,8 +133,6 @@ int32_t main(int32_t argc, char *argv[]) { } return 0; - tb_clear_world(&world); - tb_destroy_world(&world); SDL_Quit(); diff --git a/viewer/source/viewersystem.c b/viewer/source/viewersystem.c index 72a8acae..8f9df9db 100644 --- a/viewer/source/viewersystem.c +++ b/viewer/source/viewersystem.c @@ -1,13 +1,13 @@ #include "viewersystem.h" -#include "coreuisystem.h" -#include "profiling.h" -#include "tbcommon.h" -#include "tbimgui.h" +#include "tb_common.h" +#include "tb_coreui_system.h" +#include "tb_imgui.h" +#include "tb_profiling.h" #ifdef TB_COOKED #include "tb_viewer_assetmanifest.h" #endif -#include "world.h" +#include "tb_world.h" #include