diff --git a/.github/workflows/windows.yml b/.github/workflows/windows.yml index 0f888dc9..4ad6b7bb 100644 --- a/.github/workflows/windows.yml +++ b/.github/workflows/windows.yml @@ -22,14 +22,9 @@ jobs: uses: actions/checkout@v4 - name: Install Dependencies on Windows - run: choco install nasm openssl make + run: choco install nasm make shell: bash - - name: List OpenSSL - run: | - dir C:\ - dir "C:\Program Files\" - - name: Download NSIS shell: pwsh run: | diff --git a/Tests.cmake b/Tests.cmake index c3daafe5..0a3c2826 100644 --- a/Tests.cmake +++ b/Tests.cmake @@ -42,5 +42,7 @@ file(COPY images/Suzuho-Ueda.bmp DESTINATION ${CMAKE_BINARY_DIR}/images) file(COPY images/hi.bmp DESTINATION ${CMAKE_BINARY_DIR}/images) file(COPY models/SaiyanOne.glb DESTINATION ${CMAKE_BINARY_DIR}/models) file(COPY models/GoldenSportsCar.glb DESTINATION ${CMAKE_BINARY_DIR}/models) +file(COPY models/RedSportsCar.glb DESTINATION ${CMAKE_BINARY_DIR}/models) +file(COPY models/metal_plane.glb DESTINATION ${CMAKE_BINARY_DIR}/models) file(COPY hdri/autumn_field_puresky_4k.hdr DESTINATION ${CMAKE_BINARY_DIR}/hdri) file(COPY hdri/zwartkops_straight_afternoon_4k.hdr DESTINATION ${CMAKE_BINARY_DIR}/hdri) \ No newline at end of file diff --git a/include/dz/BufferGroup.hpp b/include/dz/BufferGroup.hpp index 443fed06..dce1751b 100644 --- a/include/dz/BufferGroup.hpp +++ b/include/dz/BufferGroup.hpp @@ -46,7 +46,7 @@ namespace dz * @param data_pointer Optional pointer to image data. * @return Pointer to the created Image. */ - Image* buffer_group_define_image_2D(BufferGroup* buffer_group, const std::string& buffer_name, uint32_t image_width, uint32_t image_height, void* data_pointer = 0); + Image* buffer_group_define_image_2D(BufferGroup* buffer_group, const std::string& buffer_name, uint32_t image_width, uint32_t image_height, const std::vector>& datas = {}); /** * @brief Defines a 3D image in the buffer group. @@ -59,7 +59,7 @@ namespace dz * @param data_pointer Optional pointer to image data. * @return Pointer to the created Image. */ - Image* buffer_group_define_image_3D(BufferGroup* buffer_group, const std::string& buffer_name, uint32_t image_width, uint32_t image_height, uint32_t image_depth, void* data_pointer = 0); + Image* buffer_group_define_image_3D(BufferGroup* buffer_group, const std::string& buffer_name, uint32_t image_width, uint32_t image_height, uint32_t image_depth, const std::vector>& datas = {}); /** * @brief Sets the number of elements in a named buffer. diff --git a/include/dz/ECS.hpp b/include/dz/ECS.hpp index 3d7e3911..3b8417f6 100644 --- a/include/dz/ECS.hpp +++ b/include/dz/ECS.hpp @@ -42,11 +42,14 @@ namespace dz { inline static std::string MetalnessRoughnessAtlas_Str = "MetalnessRoughnessAtlas"; inline static std::string ShininessAtlas_Str = "ShininessAtlas"; inline static std::string HDRIAtlas_Str = "HDRIAtlas"; + inline static std::string IrradianceAtlas_Str = "IrradianceAtlas"; + inline static std::string RadianceAtlas_Str = "RadianceAtlas"; inline static std::string VertexPositions_Str = "VertexPositions"; inline static std::string VertexUV2s_Str = "VertexUV2s"; inline static std::string VertexNormals_Str = "VertexNormals"; inline static std::string VertexTangents_Str = "VertexTangents"; inline static std::string VertexBitangents_Str = "VertexBitangents"; + inline static std::string brdfLUT_Str = "brdfLUT"; template struct ECS : Restorable { @@ -68,7 +71,7 @@ namespace dz { int constructed_count = 0; }; - struct ProviderReflectableGroup : ReflectableGroup { + struct ProviderReflectableGroup : ::ReflectableGroup { float priority; std::string name; std::string struct_name; @@ -119,11 +122,14 @@ namespace dz { MetalnessRoughnessAtlas_Str, ShininessAtlas_Str, HDRIAtlas_Str, + IrradianceAtlas_Str, + RadianceAtlas_Str, VertexPositions_Str, VertexUV2s_Str, VertexNormals_Str, VertexTangents_Str, - VertexBitangents_Str + VertexBitangents_Str, + brdfLUT_Str }; // ! std::map registered_component_map; // ! bool components_registered = false; // ! @@ -137,6 +143,7 @@ namespace dz { using MeshProviderT = typename FirstMatchingOrDefault::type; using HDRIProviderT = typename FirstMatchingOrDefault::type; using SkyBoxProviderT = typename FirstMatchingOrDefault::type; + using LightProviderT = typename FirstMatchingOrDefault::type; DrawListManager skybox_mg; // ! DrawListManager draw_mg; // ! @@ -162,6 +169,8 @@ namespace dz { ImagePack metalness_roughness_atlas_pack; ImagePack shininess_atlas_pack; ImagePack hdri_atlas_pack; + ImagePack irradiance_atlas_pack; + ImagePack radiance_atlas_pack; auto GenerateSkyBoxDrawFunction() { return [&](auto buffer_group, auto& skybox) -> DrawTuple { @@ -313,6 +322,11 @@ namespace dz { void Initialize() { RegisterProviders(); + + hdri_atlas_pack.SetAtlasFormat(VK_FORMAT_R32G32B32A32_SFLOAT); + irradiance_atlas_pack.SetAtlasFormat(VK_FORMAT_R16G16B16A16_SFLOAT); + radiance_atlas_pack.SetAtlasFormat(VK_FORMAT_R16G16B16A16_SFLOAT); + buffer_group = CreateBufferGroup(); assert(buffer_group); @@ -384,6 +398,8 @@ namespace dz { UseAtlas(shininess_atlas_pack, ShininessAtlas_Str); UpdateHDRIAtlas(); UseAtlas(hdri_atlas_pack, HDRIAtlas_Str); + UseAtlas(irradiance_atlas_pack, IrradianceAtlas_Str); + UseAtlas(radiance_atlas_pack, RadianceAtlas_Str); if (!buffer_initialized) { buffer_group_initialize(buffer_group); buffer_initialized = true; @@ -428,6 +444,8 @@ namespace dz { void UpdateHDRIAtlas() { hdri_atlas_pack.check(); + irradiance_atlas_pack.check(); + radiance_atlas_pack.check(); for (auto& hdri_group_sh_ptr : hdri_group_vector) { auto generic_group_ptr = hdri_group_sh_ptr.get(); auto hdri_group_ptr = dynamic_cast(generic_group_ptr); @@ -435,6 +453,8 @@ namespace dz { auto& hdri = GetHDRI(hdri_group.id); // UpdatePackedRect(hdri_group.hdri_image, hdri_atlas_pack, hdri.hdri_atlas_pack); + UpdatePackedRect(hdri_group.irradiance_image, irradiance_atlas_pack, hdri.irradiance_atlas_pack); + UpdatePackedRect(hdri_group.radiance_image, radiance_atlas_pack, hdri.radiance_atlas_pack); continue; } } @@ -541,7 +561,7 @@ namespace dz { assert(cam_ptr); cam_ptr->InitFramebuffer(raster_shaders, width, height); cam_ptr->update_draw_list_fn = [&]() { - draw_mg.MarkDirty(); + MarkDirty(); }; } } @@ -599,6 +619,7 @@ namespace dz { template void RegisterProvider() { std::lock_guard lock(e_mutex); + TProvider::RunStaticInitialize(); auto priority = TProvider::GetPriority(); auto& name = TProvider::GetProviderName(); auto& struct_name = TProvider::GetStructName(); @@ -802,20 +823,22 @@ namespace dz { auto& parent_group = GetGenericGroupByID(parent_id); if constexpr (std::is_same_v) SetWhoParent(GetEntity(id), &parent_group); - if constexpr (std::is_same_v) + else if constexpr (std::is_same_v) SetWhoParent(GetCamera(id), &parent_group); - if constexpr (std::is_same_v) + else if constexpr (std::is_same_v) SetWhoParent(GetScene(id), &parent_group); - if constexpr (std::is_same_v) + else if constexpr (std::is_same_v) SetWhoParent(GetSubMesh(id), &parent_group); - if constexpr (std::is_same_v) + else if constexpr (std::is_same_v) + SetWhoParent(GetLight(id), &parent_group); + else if constexpr (std::is_same_v) SetWhoParent(GetSkyBox(id), &parent_group); } if constexpr (requires { data.Initialize(*this, group); }) - data.Initialize(*this, group); + data.Initialize(*this, group, args...); else if constexpr (requires { data.Initialize(); }) - data.Initialize(); + data.Initialize(args...); group.UpdateChildren(); @@ -829,27 +852,34 @@ namespace dz { who.parent_cid = new_parent_ptr ? new_parent_ptr->cid : 0; } - template + template int AddProvider( int parent_id, const TData& data, std::vector>& reflectable_group_vector, int& out_index, - const std::string& name + const std::string& name, + const Args&... args ) { size_t id = 0; - (AddProviderSingle(parent_id, id, data, reflectable_group_vector, out_index, name), ...); + (AddProviderSingle(parent_id, id, data, reflectable_group_vector, out_index, name, args...), ...); if (!id) throw std::runtime_error("Unable to find Provider supporting TData"); return id; } - template - int AddEntity(int parent_id, const TEntity& entity_data, const std::vector& mesh_indexes, const std::string& name) { + template + int AddEntity( + int parent_id, + const TEntity& entity_data, + const std::vector& mesh_indexes, + const std::string& name, + const Args&... args + ) { auto parent_group_ptr = FindParentGroupPtr(parent_id); int out_index = -1; auto entity_id = AddProvider(parent_id, entity_data, - parent_group_ptr ? parent_group_ptr->GetChildren() : reflectable_group_root_vector, out_index, name); + parent_group_ptr ? parent_group_ptr->GetChildren() : reflectable_group_root_vector, out_index, name, args...); auto& entity_group = GetGroupByID(entity_id); for (auto& mesh_index : mesh_indexes) { auto& mesh_group = GetGroupByIndex(mesh_index); @@ -864,31 +894,31 @@ namespace dz { return entity_id; } - template - int AddEntity(const TEntity& entity_data, const std::vector& mesh_indexes, const std::string& name) { - return AddEntity(-1, entity_data, mesh_indexes, name); + template + int AddEntity(const TEntity& entity_data, const std::vector& mesh_indexes, const std::string& name, const Args&... args) { + return AddEntity(-1, entity_data, mesh_indexes, name, args...); } - template - int AddScene(int parent_id, const TScene& scene_data, const std::string& name) { + template + int AddScene(int parent_id, const TScene& scene_data, const std::string& name, const Args&... args) { auto parent_group_ptr = FindParentGroupPtr(parent_id); int out_index = -1; return AddProvider(parent_id, scene_data, - parent_group_ptr ? parent_group_ptr->GetChildren() : reflectable_group_root_vector, out_index, name); + parent_group_ptr ? parent_group_ptr->GetChildren() : reflectable_group_root_vector, out_index, name, args...); } - template - int AddScene(const TScene& scene_data, const std::string& name) { - return AddScene(-1, scene_data, name); + template + int AddScene(const TScene& scene_data, const std::string& name, const Args&... args) { + return AddScene(-1, scene_data, name, args...); } SceneProviderT& GetScene(size_t scene_id) { return GetProviderData(scene_id); } - template - int AddMaterial(const TMaterial& material_data, int& out_index, const std::string& name) { - return AddProvider(-1, material_data, material_group_vector, out_index, name); + template + int AddMaterial(const TMaterial& material_data, int& out_index, const std::string& name, const Args&... args) { + return AddProvider(-1, material_data, material_group_vector, out_index, name, args...); } MaterialProviderT& GetMaterial(size_t material_id) { @@ -940,26 +970,35 @@ namespace dz { UpdateAtlases(); } - template - int AddHDRI(const THDRI& hdri_data, int& out_index, const std::string& name) { - return AddProvider(-1, hdri_data, hdri_group_vector, out_index, name); + template + int AddHDRI(const THDRI& hdri_data, int& out_index, const std::string& name, const Args&... args) { + return AddProvider(-1, hdri_data, hdri_group_vector, out_index, name, args...); } HDRIProviderT& GetHDRI(size_t hdri_id) { return GetProviderData(hdri_id); } - void SetHDRIImage(size_t hdri_id, Image* image_ptr) { + template + void SetHDRIImage(size_t hdri_id, Image* image_ptr, const Args&... args) { auto& hdri_group = GetGroupByID(hdri_id); + auto& hdri = GetHDRI(hdri_group.id); + hdri_group.hdri_image = image_ptr; hdri_group.hdri_frame_image_ds = image_create_descriptor_set(image_ptr).second; hdri_atlas_pack.addImage(image_ptr); + if constexpr (requires { hdri.Initialize(*this, hdri_group); }) + hdri.Initialize(*this, hdri_group, args...); + else if constexpr (requires { hdri.Initialize(); }) + hdri.Initialize(args...); + if (buffer_initialized) UpdateHDRIAtlas(); } + template int AddMesh( const std::vector>& positions, const std::vector>& uv2s, @@ -968,7 +1007,8 @@ namespace dz { const std::vector>& bitangents, int material_index, int& out_index, - const std::string& name + const std::string& name, + const Args&... args ) { MeshProviderT mesh_data; auto position_index = buffer_group_get_buffer_element_count(buffer_group, VertexPositions_Str); @@ -990,7 +1030,7 @@ namespace dz { if (!bitangents.empty()) mesh_data.bitangent_offset = bitangent_index; - auto mesh_id = AddProvider(-1, mesh_data, mesh_group_vector, out_index, name); + auto mesh_id = AddProvider(-1, mesh_data, mesh_group_vector, out_index, name, args...); if (mesh_data.position_offset != -1) { buffer_group_set_buffer_element_count(buffer_group, VertexPositions_Str, mesh_data.position_offset + positions.size()); @@ -1043,29 +1083,33 @@ namespace dz { return mesh_id; } - template - int AddLight(int parent_id, const TLight& light_data, const std::string& name) { + template + int AddLight(int parent_id, const TLight& light_data, const std::string& name, const Args&... args) { auto parent_group_ptr = FindParentGroupPtr(parent_id); int out_index = -1; return AddProvider(parent_id, light_data, - parent_group_ptr ? parent_group_ptr->GetChildren() : reflectable_group_root_vector, out_index, name); + parent_group_ptr ? parent_group_ptr->GetChildren() : reflectable_group_root_vector, out_index, name, args...); + } + + template + int AddLight(const TLight& light_data, const std::string& name, const Args&... args) { + return AddLight(-1, light_data, name, args...); } - template - int AddLight(const TLight& light_data, const std::string& name) { - return AddLight(-1, light_data, name); + LightProviderT& GetLight(size_t light_id) { + return GetProviderData(light_id); } - template - int AddSkyBox(int parent_id, const TSkyBox& skybox_data, const std::string& name) { + template + int AddSkyBox(int parent_id, const TSkyBox& skybox_data, const std::string& name, const Args&... args) { auto parent_group_ptr = FindParentGroupPtr(parent_id); int out_index = -1; return AddProvider(parent_id, skybox_data, - parent_group_ptr ? parent_group_ptr->GetChildren() : reflectable_group_root_vector, out_index, name); + parent_group_ptr ? parent_group_ptr->GetChildren() : reflectable_group_root_vector, out_index, name, args...); } - template - int AddSkyBox(const TSkyBox& skybox_data, const std::string& name) { + template + int AddSkyBox(const TSkyBox& skybox_data, const std::string& name, const Args&... args) { return AddSkyBox(-1, skybox_data, name); } @@ -1177,7 +1221,7 @@ namespace dz { auto& camera_group = GetGroupByID(camera_id); camera_group.update_draw_list_fn = [&]() { - draw_mg.MarkDirty(); + MarkDirty(); }; camera_group.NotifyNameChanged(); @@ -1249,15 +1293,28 @@ namespace dz { return buffer_group_ptr; } + template + void RunProviderShaderTweak(Shader* shader) { + TProvider::RunShaderTweak(shader); + } + + void AddShaderDefines(Shader* shader_ptr) { + for (auto& [provider_id, provider_group] : pid_provider_groups) + shader_set_define(shader_ptr, "CID_" + provider_group.name, std::to_string(provider_id)); + } + Shader* GenerateMainShader() { auto shader_ptr = shader_create(); + AddShaderDefines(shader_ptr); shader_add_buffer_group(shader_ptr, buffer_group); shader_add_module(shader_ptr, ShaderModuleType::Vertex, GenerateMainVertexShaderCode()); shader_add_module(shader_ptr, ShaderModuleType::Fragment, GenerateMainFragmentShaderCode()); + (RunProviderShaderTweak(shader_ptr), ...); + raster_shaders.push_back(shader_ptr); return shader_ptr; } @@ -1265,6 +1322,8 @@ namespace dz { Shader* GenerateSkyBoxShader() { auto shader_ptr = shader_create(); + AddShaderDefines(shader_ptr); + shader_add_buffer_group(shader_ptr, buffer_group); shader_set_depth_test(shader_ptr, true); @@ -1276,6 +1335,8 @@ namespace dz { shader_add_module(shader_ptr, ShaderModuleType::Vertex, GenerateSkyBoxVertexShaderCode()); shader_add_module(shader_ptr, ShaderModuleType::Fragment, GenerateSkyBoxFragmentShaderCode()); + (RunProviderShaderTweak(shader_ptr), ...); + raster_shaders.push_back(shader_ptr); return shader_ptr; } @@ -1283,13 +1344,14 @@ namespace dz { Shader* GenerateModelComputeShader() { auto shader_ptr = shader_create(); - for (auto& [provider_id, provider_group] : pid_provider_groups) - shader_set_define(shader_ptr, "CID_" + provider_group.name, std::to_string(provider_id)); + AddShaderDefines(shader_ptr); shader_add_buffer_group(shader_ptr, buffer_group); shader_add_module(shader_ptr, ShaderModuleType::Compute, GenerateModelComputeShaderCode()); + (RunProviderShaderTweak(shader_ptr), ...); + window_register_compute_dispatch(window_ptr, 0.0f, shader_ptr, [&]() { return buffer_group_get_buffer_element_count(buffer_group, buffer_name); }); @@ -1301,13 +1363,14 @@ namespace dz { Shader* GenerateCameraMatComputeShader() { auto shader_ptr = shader_create(); - for (auto& [provider_id, provider_group] : pid_provider_groups) - shader_set_define(shader_ptr, "CID_" + provider_group.name, std::to_string(provider_id)); + AddShaderDefines(shader_ptr); shader_add_buffer_group(shader_ptr, buffer_group); shader_add_module(shader_ptr, ShaderModuleType::Compute, GenerateCameraMatComputeShaderCode()); + (RunProviderShaderTweak(shader_ptr), ...); + window_register_compute_dispatch(window_ptr, -10.0f, shader_ptr, [&]() { return buffer_group_get_buffer_element_count(buffer_group, Cameras_Str); }); @@ -1368,6 +1431,65 @@ namespace dz { } } + shader_header += R"( +#define STACK_CAPACITY 8 + +struct Int_Stack +{ + int data[STACK_CAPACITY]; + int top; +}; + +void Int_Stack_Init(inout Int_Stack stack) +{ + stack.top = -1; +} + +bool Int_Stack_IsEmpty(in Int_Stack stack) +{ + return stack.top < 0; +} + +bool Int_Stack_IsFull(in Int_Stack stack) +{ + return stack.top >= STACK_CAPACITY - 1; +} + +bool Int_Stack_Push(inout Int_Stack stack, int value) +{ + if (Int_Stack_IsFull(stack)) + { + return false; + } + stack.top = stack.top + 1; + stack.data[stack.top] = value; + return true; +} + +bool Int_Stack_Pop(inout Int_Stack stack, out int value) +{ + if (Int_Stack_IsEmpty(stack)) + { + value = int(0.0); + return false; + } + value = stack.data[stack.top]; + stack.top = stack.top - 1; + return true; +} + +bool Int_Stack_Peek(in Int_Stack stack, out int value) +{ + if (Int_Stack_IsEmpty(stack)) + { + value = int(0.0); + return false; + } + value = stack.data[stack.top]; + return true; +} +)"; + // Mesh Buffers shader_header += R"( layout(std430, binding = )" + std::to_string(binding_index++) + R"() buffer VertexPositionsBuffer { @@ -1400,7 +1522,6 @@ layout(std430, binding = )" + std::to_string(binding_index++) + R"() buffer Vert for (auto& provider_id : provider_ids) { auto& provider_group = pid_provider_groups[provider_id]; if (provider_group.buffer_host_type != BufferHost::GPU) { - shader_header += provider_group.glsl_methods[moduleType]; continue; } shader_header += R"( @@ -1421,6 +1542,112 @@ layout(std430, binding = )" + std::to_string(binding_index++) + ") buffer " + pr return )" + provider_group.struct_name + R"(s.data[t_provider_index]; } )"; + } + } + + // Methods + + shader_header += R"( + +void GetParentID_CID(in int current_index, in int current_cid, out int next_index, out int next_cid); +void GetSceneParentID_CID(in int current_index, out int next_index, out int next_cid); +void GetEntityParentID_CID(in int current_index, out int next_index, out int next_cid); +void GetCameraParentID_CID(in int current_index, out int next_index, out int next_cid); +void GetSubMeshParentID_CID(in int current_index, out int next_index, out int next_cid); +void GetLightParentID_CID(in int current_index, out int next_index, out int next_cid); +void GetSkyBoxParentID_CID(in int current_index, out int next_index, out int next_cid); +void GetTopLevelNode(int in_index, int in_cid, out int out_index, out int out_cid) { + int current_cid = 0, current_index = -1; + int next_cid = in_cid, next_index = in_index; + // Goto Top + while (next_cid != 0) { + current_cid = next_cid; + current_index = next_index; + GetParentID_CID(current_index, current_cid, next_index, next_cid); + } + out_index = current_index; + out_cid = current_cid; + return; +} +void GetTopNodeByCID(int in_index, int in_cid, out int out_index, out int out_cid, in int search_cid) { + int current_cid = 0, current_index = -1; + int next_cid = in_cid, next_index = in_index; + Int_Stack index_stack; + Int_Stack cid_stack; + Int_Stack_Init(index_stack); + Int_Stack_Init(cid_stack); + // Goto Top + while (next_cid != 0) { + current_cid = next_cid; + current_index = next_index; + Int_Stack_Push(index_stack, current_index); + Int_Stack_Push(cid_stack, current_cid); + GetParentID_CID(current_index, current_cid, next_index, next_cid); + } + // Goto First CID in stack + while (current_cid != search_cid && !Int_Stack_IsEmpty(index_stack)) { + Int_Stack_Pop(index_stack, current_index); + Int_Stack_Pop(cid_stack, current_cid); + } + out_index = current_index; + out_cid = current_cid; + return; +} +void GetParentID_CID(in int current_index, in int current_cid, out int next_index, out int next_cid) { + switch (current_cid) { + case CID_Scene: + GetSceneParentID_CID(current_index, next_index, next_cid); + return; + case CID_Entity: + GetEntityParentID_CID(current_index, next_index, next_cid); + return; + case CID_Camera: + GetCameraParentID_CID(current_index, next_index, next_cid); + return; + case CID_SubMesh: + GetSubMeshParentID_CID(current_index, next_index, next_cid); + return; + case CID_Light: + GetLightParentID_CID(current_index, next_index, next_cid); + return; + case CID_SkyBox: + GetSkyBoxParentID_CID(current_index, next_index, next_cid); + return; + default: + next_cid = 0; + next_index = -1; + return; + } +} +void GetSceneParentID_CID(in int current_index, out int next_index, out int next_cid) { + next_index = Scenes.data[current_index].parent_index; + next_cid = Scenes.data[current_index].parent_cid; +} +void GetEntityParentID_CID(in int current_index, out int next_index, out int next_cid) { + next_index = Entitys.data[current_index].parent_index; + next_cid = Entitys.data[current_index].parent_cid; +} +void GetCameraParentID_CID(in int current_index, out int next_index, out int next_cid) { + next_index = Cameras.data[current_index].parent_index; + next_cid = Cameras.data[current_index].parent_cid; +} +void GetSubMeshParentID_CID(in int current_index, out int next_index, out int next_cid) { + next_index = SubMeshs.data[current_index].parent_index; + next_cid = SubMeshs.data[current_index].parent_cid; +} +void GetLightParentID_CID(in int current_index, out int next_index, out int next_cid) { + next_index = Lights.data[current_index].parent_index; + next_cid = Lights.data[current_index].parent_cid; +} +void GetSkyBoxParentID_CID(in int current_index, out int next_index, out int next_cid) { + next_index = SkyBoxs.data[current_index].parent_index; + next_cid = SkyBoxs.data[current_index].parent_cid; +} +)"; + + for (auto& [priority, provider_ids] : prioritized_provider_ids) { + for (auto& provider_id : provider_ids) { + auto& provider_group = pid_provider_groups[provider_id]; shader_header += provider_group.glsl_methods[moduleType]; } } @@ -1507,17 +1734,20 @@ bool HasComponentWithType(in Entity entity, int entity_index, int type, out int std::string GenerateMainVertexShaderCode() { std::string shader_string = R"( #version 450 -layout(location = 0) out int outID; -layout(location = 1) out vec4 outColor; -layout(location = 2) out vec3 outPosition; -layout(location = 3) out vec3 outLocalPosition; -layout(location = 4) out vec3 outNormal; -layout(location = 5) out vec3 outViewPosition; -layout(location = 6) out vec2 outUV2; -layout(location = 7) out vec3 outTangent; -layout(location = 8) out vec3 outBitangent; +layout(location = 0) out int outIndex; +layout(location = 1) out int outCID; +layout(location = 2) out vec4 outColor; +layout(location = 3) out vec3 outPosition; +layout(location = 4) out vec3 outLocalPosition; +layout(location = 5) out vec3 outNormal; +layout(location = 6) out vec3 outViewPosition; +layout(location = 7) out vec2 outUV2; +layout(location = 8) out vec3 outTangent; +layout(location = 9) out vec3 outBitangent; +layout(location = 10) out int outTopNodeIndex; +layout(location = 11) out int outTopNodeCID; )"; - int out_location = 9; + int out_location = 12; int in_location = 0; shader_string += GenerateShaderLayout(ShaderModuleType::Vertex, in_location, out_location); @@ -1531,7 +1761,9 @@ layout(push_constant) uniform PushConstants { // Main shader_string += R"( void main() { - int submesh_index = outID = gl_InstanceIndex; + int submesh_index = outIndex = gl_InstanceIndex; + outCID = CID_SubMesh; + GetTopNodeByCID(outIndex, outCID, outTopNodeIndex, outTopNodeCID, CID_Scene); SubMesh submesh = GetSubMeshData(submesh_index); Mesh mesh = GetMeshData(submesh.mesh_index); Entity entity = GetEntityData(submesh.parent_index); @@ -1559,19 +1791,22 @@ void main() { std::string GenerateMainFragmentShaderCode() { std::string shader_string = R"( #version 450 -layout(location = 0) flat in int inID; -layout(location = 1) in vec4 inColor; -layout(location = 2) in vec3 inPosition; -layout(location = 3) in vec3 inLocalPosition; -layout(location = 4) in vec3 inNormal; -layout(location = 5) in vec3 inViewPosition; -layout(location = 6) in vec2 inUV2; -layout(location = 7) in vec3 inTangent; -layout(location = 8) in vec3 inBitangent; +layout(location = 0) flat in int inIndex; +layout(location = 1) flat in int inCID; +layout(location = 2) in vec4 inColor; +layout(location = 3) in vec3 inPosition; +layout(location = 4) in vec3 inLocalPosition; +layout(location = 5) in vec3 inNormal; +layout(location = 6) in vec3 inViewPosition; +layout(location = 7) in vec2 inUV2; +layout(location = 8) in vec3 inTangent; +layout(location = 9) in vec3 inBitangent; +layout(location = 10) flat in int inTopNodeIndex; +layout(location = 11) flat in int inTopNodeCID; layout(location = 0) out vec4 FragColor; )"; - int in_location = 9; + int in_location = 12; int out_location = 1; shader_string += GenerateShaderLayout(ShaderModuleType::Fragment, in_location, out_location); @@ -1585,7 +1820,7 @@ layout(push_constant) uniform PushConstants { shader_string += R"( void main() { - int submesh_index = inID; + int submesh_index = inIndex; SubMesh submesh = GetSubMeshData(submesh_index); Entity entity = GetEntityData(submesh.parent_index); int t_component_index = -1; @@ -1605,10 +1840,13 @@ void main() { std::string GenerateSkyBoxVertexShaderCode() { std::string shader_string = R"( #version 450 -layout(location = 0) out int outID; -layout(location = 1) out vec3 outLocalPosition; +layout(location = 0) out int outIndex; +layout(location = 1) out int outCID; +layout(location = 2) out int outTopNodeIndex; +layout(location = 3) out int outTopNodeCID; +layout(location = 4) out vec3 outLocalPosition; )"; - int out_location = 2; + int out_location = 5; int in_location = 0; shader_string += GenerateShaderLayout(ShaderModuleType::Vertex, in_location, out_location); @@ -1642,7 +1880,9 @@ vec3 positions[36] = vec3[]( ); void main() { - int skybox_index = outID = gl_InstanceIndex; + int skybox_index = outIndex = gl_InstanceIndex; + outCID = CID_SkyBox; + GetTopLevelNode(outIndex, outCID, outTopNodeIndex, outTopNodeCID); Camera camera = GetCameraData(pc.camera_index); vec3 pos = positions[gl_VertexIndex]; mat4 viewNoTranslation = mat4(mat3(camera.view)); @@ -1658,8 +1898,11 @@ void main() { std::string GenerateSkyBoxFragmentShaderCode() { std::string shader_string = R"( #version 450 -layout(location = 0) flat in int inID; -layout(location = 1) in vec3 inLocalPosition; +layout(location = 0) flat in int inIndex; +layout(location = 1) flat in int inCID; +layout(location = 2) flat in int inTopNodeIndex; +layout(location = 3) flat in int inTopNodeCID; +layout(location = 4) in vec3 inLocalPosition; vec3 inTangent = vec3(0.0); vec3 inBitangent = vec3(0.0); @@ -1667,7 +1910,7 @@ vec3 inNormal = vec3(0.0); layout(location = 0) out vec4 FragColor; )"; - int in_location = 2; + int in_location = 5; int out_location = 1; shader_string += GenerateShaderLayout(ShaderModuleType::Fragment, in_location, out_location); @@ -1681,10 +1924,10 @@ layout(push_constant) uniform PushConstants { shader_string += R"( void main() { - SkyBox skybox = GetSkyBoxData(inID); + SkyBox skybox = GetSkyBoxData(inIndex); vec3 direction = normalize(inLocalPosition); direction.y = -direction.y; - FragColor = SampleHDRI(skybox.hdri_index, direction);; + FragColor = SampleHDRI(skybox.hdri_index, direction); } )"; return shader_string; @@ -1809,6 +2052,7 @@ void main() { void MarkDirty() { draw_mg.MarkDirty(); + skybox_mg.MarkDirty(); } }; } \ No newline at end of file diff --git a/include/dz/ECS/Camera.hpp b/include/dz/ECS/Camera.hpp index 93a35afe..30749699 100644 --- a/include/dz/ECS/Camera.hpp +++ b/include/dz/ECS/Camera.hpp @@ -283,7 +283,7 @@ void GetCameraModel(int camera_index, out mat4 out_model, out int parent_index, void NotifyChange(int prop_index) override; }; - struct CameraReflectableGroup : ReflectableGroup { + struct CameraReflectableGroup : ::ReflectableGroup { BufferGroup* buffer_group = nullptr; std::string name; std::string imgui_name; diff --git a/include/dz/ECS/Entity.hpp b/include/dz/ECS/Entity.hpp index 14a8b5b0..7ed3747b 100644 --- a/include/dz/ECS/Entity.hpp +++ b/include/dz/ECS/Entity.hpp @@ -130,7 +130,7 @@ void GetEntityModel(int entity_index, out mat4 out_model, out int parent_index, void NotifyChange(int prop_index) override; }; - struct EntityReflectableGroup : ReflectableGroup { + struct EntityReflectableGroup : ::ReflectableGroup { BufferGroup* buffer_group = nullptr; std::string name; std::vector> reflectable_children; diff --git a/include/dz/ECS/HDRI.hpp b/include/dz/ECS/HDRI.hpp index d93ac27a..4d7b2539 100644 --- a/include/dz/ECS/HDRI.hpp +++ b/include/dz/ECS/HDRI.hpp @@ -7,12 +7,20 @@ namespace dz::ecs { + void set_radiance_control_block(Shader*, void*); + struct RadianceControlBlock { + float roughness; + int mip; + }; + struct HDRIIndexReflectable { int hdri_index = 0; }; struct HDRI : Provider { vec hdri_atlas_pack = {-1.0f, -1.0f, -1.0f, -1.0f}; + vec irradiance_atlas_pack = {-1.0f, -1.0f, -1.0f, -1.0f}; + vec radiance_atlas_pack = {-1.0f, -1.0f, -1.0f, -1.0f}; inline static constexpr size_t PID = 11; inline static float Priority = 2.6f; @@ -23,6 +31,8 @@ namespace dz::ecs { inline static std::string GLSLStruct = R"( struct HDRI { vec4 hdri_atlas_pack; + vec4 irradiance_atlas_pack; + vec4 radiance_atlas_pack; }; )"; inline static std::unordered_map GLSLMethods = { @@ -43,12 +53,28 @@ vec4 SampleHDRI(in int hdri_index, in vec3 v) { vec2 packed_rect = HDRIs.data[hdri_index].hdri_atlas_pack.zw; return SampleAtlas(SampleSphericalMap(v), image_size, packed_rect, HDRIAtlas); } +vec4 SampleIrradiance(in int hdri_index, in vec3 v) { + vec2 image_size = HDRIs.data[hdri_index].irradiance_atlas_pack.xy; + if (image_size.x == -1.0) + return vec4(0.0); + vec2 packed_rect = HDRIs.data[hdri_index].irradiance_atlas_pack.zw; + return SampleAtlas(SampleSphericalMap(v), image_size, packed_rect, IrradianceAtlas); +} +vec4 SampleRadiance(in int hdri_index, in vec3 v) { + vec2 image_size = HDRIs.data[hdri_index].radiance_atlas_pack.xy; + if (image_size.x == -1.0) + return vec4(0.0); + vec2 packed_rect = HDRIs.data[hdri_index].radiance_atlas_pack.zw; + return SampleAtlas(SampleSphericalMap(v), image_size, packed_rect, RadianceAtlas); +} )" } }; inline static std::vector GLSLBindings = { R"( layout(binding = @BINDING@) uniform sampler2D HDRIAtlas; +layout(binding = @BINDING@) uniform sampler2D IrradianceAtlas; +layout(binding = @BINDING@) uniform sampler2D RadianceAtlas; )" }; @@ -57,6 +83,8 @@ layout(binding = @BINDING@) uniform sampler2D HDRIAtlas; )", ShaderModuleType::Vertex}, {0.5f, R"( vec4 hdri_sample = SampleHDRI(0, inLocalPosition); + vec4 irradiance_sample = SampleIrradiance(0, inLocalPosition); + vec4 radiance_sample = SampleRadiance(0, inLocalPosition); )", ShaderModuleType::Fragment} }; @@ -87,7 +115,7 @@ layout(binding = @BINDING@) uniform sampler2D HDRIAtlas; void NotifyChange(int prop_index) override; }; - struct HDRIReflectableGroup : ReflectableGroup { + struct HDRIReflectableGroup : ::ReflectableGroup { BufferGroup* buffer_group = nullptr; std::string name; std::vector reflectables; @@ -95,6 +123,12 @@ layout(binding = @BINDING@) uniform sampler2D HDRIAtlas; Image* hdri_image = nullptr; VkDescriptorSet hdri_frame_image_ds = VK_NULL_HANDLE; + Image* irradiance_image = nullptr; + VkDescriptorSet irradiance_frame_image_ds = VK_NULL_HANDLE; + + Image* radiance_image = nullptr; + VkDescriptorSet radiance_frame_image_ds = VK_NULL_HANDLE; + HDRIReflectableGroup(BufferGroup* buffer_group): buffer_group(buffer_group), name("HDRI") @@ -144,5 +178,411 @@ layout(binding = @BINDING@) uniform sampler2D HDRIAtlas; }; using ReflectableGroup = HDRIReflectableGroup; + + template + void Initialize(TECS& ecs, ::ReflectableGroup& generic_group) { + auto& hdri_group = dynamic_cast(generic_group); + + if (!hdri_group.hdri_image) + return; + if (hdri_group.irradiance_image) + return; + if (hdri_group.radiance_image) + return; + + auto hdri_width = image_get_width(hdri_group.hdri_image); + auto hdri_height = image_get_height(hdri_group.hdri_image); + + // Setup irradiance image + hdri_group.irradiance_image = image_create({ + .width = hdri_width / 32, + .height = hdri_height / 32, + .format = VK_FORMAT_R16G16B16A16_SFLOAT, + .usage = VK_IMAGE_USAGE_STORAGE_BIT | VK_IMAGE_USAGE_SAMPLED_BIT | VK_IMAGE_USAGE_TRANSFER_DST_BIT | VK_IMAGE_USAGE_TRANSFER_SRC_BIT + }); + // ds and atlas + hdri_group.irradiance_frame_image_ds = image_create_descriptor_set(hdri_group.irradiance_image).second; + ecs.irradiance_atlas_pack.addImage(hdri_group.irradiance_image); + + // generate irradiance + auto irradianceGenerationShader = GetIrradianceGenerationShader(ecs, hdri_group); + transition_image_layout(hdri_group.irradiance_image, VK_IMAGE_LAYOUT_GENERAL); + shader_dispatch( + irradianceGenerationShader, + ceil(image_get_width(hdri_group.irradiance_image) / 8), + ceil(image_get_height(hdri_group.irradiance_image) / 8), + 1 + ); + transition_image_layout(hdri_group.irradiance_image, VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL); + + + // Setup radiance image + uint32_t mipLevels = static_cast(std::floor(std::log2(std::max(hdri_width, hdri_height)))) + 1; + hdri_group.radiance_image = image_create({ + .width = hdri_width, + .height = hdri_height, + .format = VK_FORMAT_R16G16B16A16_SFLOAT, + .usage = VK_IMAGE_USAGE_STORAGE_BIT | VK_IMAGE_USAGE_SAMPLED_BIT | VK_IMAGE_USAGE_TRANSFER_DST_BIT | VK_IMAGE_USAGE_TRANSFER_SRC_BIT, + .mip_levels = mipLevels + }); + // ds and atlas + hdri_group.radiance_frame_image_ds = image_create_descriptor_set(hdri_group.radiance_image).second; + ecs.radiance_atlas_pack.addImage(hdri_group.radiance_image); + + static RadianceControlBlock controlBlock; + + // generate radiance + auto radianceGenerationShader = GetRadianceGenerationShader(ecs, hdri_group); + for (uint32_t mip = 0; mip < mipLevels; ++mip) + { + transition_image_layout(hdri_group.radiance_image, VK_IMAGE_LAYOUT_GENERAL, mip); + uint32_t mipWidth = (std::max)(1u, hdri_width >> mip); + uint32_t mipHeight = (std::max)(1u, hdri_height >> mip); + + controlBlock.roughness = float(mip) / float(mipLevels - 1); + controlBlock.mip = mip; + + uint32_t groupsX = (mipWidth + 7) / 8; + uint32_t groupsY = (mipHeight + 7) / 8; + shader_dispatch( + radianceGenerationShader, + ceil(groupsX), + ceil(groupsY), + 1, + set_radiance_control_block, + 0, + &controlBlock + ); + transition_image_layout(hdri_group.radiance_image, VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL, mip); + } + + // + } + + static BufferGroup* GetIrradianceBufferGroup() { + static BufferGroup* buffer_group = nullptr; + + if (buffer_group) + return buffer_group; + + buffer_group = buffer_group_create("IrradianceGroup"); + + buffer_group_restrict_to_keys(buffer_group, { + "inHDRI", + "outIrradiance" + }); + + return buffer_group; + } + + static BufferGroup* GetRadianceBufferGroup() { + static BufferGroup* buffer_group = nullptr; + + if (buffer_group) + return buffer_group; + + buffer_group = buffer_group_create("RadianceGroup"); + + buffer_group_restrict_to_keys(buffer_group, { + "inHDRI", + "outRadiance" + }); + + return buffer_group; + } + + template + static Shader* GetIrradianceGenerationShader(TECS& ecs, HDRIReflectableGroup& hdri_group) { + static Shader* shader = nullptr; + + if (shader) { + shader_use_image(shader, "inHDRI", hdri_group.hdri_image); + shader_use_image(shader, "outIrradiance", hdri_group.irradiance_image); + + shader_update_descriptor_sets(shader); + return shader; + } + + auto buffer_group = GetIrradianceBufferGroup(); + + shader = shader_create(); + + shader_add_buffer_group(shader, buffer_group); + + shader_use_image(shader, "inHDRI", hdri_group.hdri_image); + shader_use_image(shader, "outIrradiance", hdri_group.irradiance_image); + + shader_add_module(shader, ShaderModuleType::Compute, GenerateIrradianceGenerationShader(ecs)); + + shader_initialize(shader); + + shader_update_descriptor_sets(shader); + + return shader; + } + + template + static Shader* GetRadianceGenerationShader(TECS& ecs, HDRIReflectableGroup& hdri_group) { + static Shader* shader = nullptr; + + if (shader) { + shader_use_image(shader, "inHDRI", hdri_group.hdri_image); + shader_use_image(shader, "outRadiance", hdri_group.radiance_image); + + shader_update_descriptor_sets(shader); + return shader; + } + + auto buffer_group = GetRadianceBufferGroup(); + + shader = shader_create(); + + shader_add_buffer_group(shader, buffer_group); + + shader_use_image(shader, "inHDRI", hdri_group.hdri_image); + shader_use_image(shader, "outRadiance", hdri_group.radiance_image); + + shader_add_module(shader, ShaderModuleType::Compute, GenerateRadianceGenerationShader(ecs)); + + shader_initialize(shader); + + shader_update_descriptor_sets(shader); + + return shader; + } + + template + static std::string GenerateIrradianceGenerationShader(TECS& ecs) { + std::string shader_string; + + shader_string += R"( +#version 450 core + +layout (local_size_x = 8, local_size_y = 8, local_size_z = 1) in; + +const float PI = 3.14159265359; + +layout(binding = 0) uniform sampler2D inHDRI; +layout(binding = 1) uniform writeonly image2D outIrradiance; + +// Convert UV to direction (spherical) +vec3 DirectionFromEquirect(vec2 uv) +{ + float phi = uv.x * 2.0 * PI; + float theta = (1.0 - uv.y) * PI; + float x = sin(theta) * cos(phi); + float y = cos(theta); + float z = sin(theta) * sin(phi); + return normalize(vec3(x, y, z)); +} + +// Convert direction to UV (used if needed) +const vec2 invAtan = vec2(0.1591, 0.3183); +vec2 SampleSphericalMap(vec3 v) +{ + vec2 uv = vec2(atan(v.z, v.x), asin(v.y)); + uv *= invAtan; + uv += 0.5; + uv.y = clamp(uv.y, 0.0, 1.0 - 0.0); + return uv; +} + +// Coordinate frame from normal (Tangent-space) +void CreateTangentSpace(in vec3 N, out vec3 T, out vec3 B) +{ + vec3 up = abs(N.y) < 0.999 ? vec3(0.0, 1.0, 0.0) : vec3(1.0, 0.0, 0.0); + T = normalize(cross(up, N)); + B = cross(N, T); +} + +// Cosine-weighted hemisphere sampling (uniform distribution) +vec3 SampleHemisphereCosine(float u1, float u2) +{ + float r = sqrt(u1); + float theta = 2.0 * PI * u2; + float x = r * cos(theta); + float z = r * sin(theta); + float y = sqrt(max(0.0, 1.0 - u1)); + return vec3(x, y, z); +} + +void main() +{ + ivec2 size = imageSize(outIrradiance); + ivec2 pix = ivec2(gl_GlobalInvocationID.xy); + + if (pix.x >= size.x || pix.y >= size.y) + return; + + vec2 uv = (vec2(pix) + 0.5) / vec2(size); // Center of texel + vec3 N = DirectionFromEquirect(uv); // Surface normal + + vec3 T, B; + CreateTangentSpace(N, T, B); + + const uint SAMPLE_COUNT = 128u; + vec3 irradiance = vec3(0.0); + float weight = 0.0; + + for (uint i = 0u; i < SAMPLE_COUNT; ++i) + { + float u1 = float(i) / float(SAMPLE_COUNT); + float u2 = fract(sin(dot(vec2(i, i + 1), vec2(12.9898, 78.233))) * 43758.5453); + vec3 sampleVec = SampleHemisphereCosine(u1, u2); + + // Transform to world space + vec3 worldSample = normalize( + sampleVec.x * T + + sampleVec.y * N + + sampleVec.z * B + ); + + vec2 sampleUV = SampleSphericalMap(worldSample); + sampleUV.y = -sampleUV.y; + vec3 sampleColor = texture(inHDRI, sampleUV).rgb; + + irradiance += sampleColor * sampleVec.y; // * cosine + weight += sampleVec.y; + } + + irradiance = (PI * irradiance) / max(weight, 0.0001); + imageStore(outIrradiance, pix, vec4(irradiance, 1.0)); +} +)"; + + return shader_string; + } + + template + static std::string GenerateRadianceGenerationShader(TECS& ecs) { + std::string shader_string; + + shader_string += R"( +#version 450 core + +#extension GL_EXT_nonuniform_qualifier : require + +layout (local_size_x = 8, local_size_y = 8) in; + +layout (binding = 0) uniform sampler2D inHDRI; +layout (binding = 1) writeonly uniform image2D outRadiance[]; +layout (push_constant) uniform PushConstants { + float roughness; + int mip; +} push; + +const float PI = 3.14159265359; + +vec3 DirectionFromEquirect(vec2 uv) +{ + float phi = uv.x * 2.0 * 3.14159265359; + float theta = (1.0 - uv.y) * 3.14159265359; + float sinTheta = sin(theta); + vec3 R; + R.x = -sinTheta * cos(phi); + R.y = -cos(theta); + R.z = -sinTheta * sin(phi); + return R; +} + +vec2 SampleSphericalMap(vec3 dir) +{ + // Normalize input direction + dir = normalize(dir); + + // Calculate azimuthal angle (phi) from x and z + float phi = atan(dir.z, dir.x); + + // Calculate polar angle (theta) from y + float theta = acos(dir.y); + + // Convert to UV coordinates + // phi from [-π, π] to [0, 1] + // theta from [0, π] to [0, 1] + vec2 uv; + uv.x = (phi / (2.0 * 3.14159265359)) + 0.5; + uv.y = theta / 3.14159265359; + + return uv; +} + +float DistributionGGX(vec3 N, vec3 H, float roughness) +{ + float a = roughness * roughness; + float a2 = a * a; + float NoH = max(dot(N, H), 0.0); + float NoH2 = NoH * NoH; + + float denom = NoH2 * (a2 - 1.0) + 1.0; + return a2 / (PI * denom * denom + 1e-5); +} + +vec3 ImportanceSampleGGX(float u1, float u2, float roughness, vec3 N) +{ + float a = roughness * roughness; + + float phi = 2.0 * PI * u1; + float cosTheta = sqrt((1.0 - u2) / (1.0 + (a * a - 1.0) * u2)); + float sinTheta = sqrt(1.0 - cosTheta * cosTheta); + + vec3 H; + H.x = cos(phi) * sinTheta; + H.y = cosTheta; + H.z = sin(phi) * sinTheta; + + vec3 up = vec3(0.0, 1.0, 0.0); //abs(N.y) < 0.999 ? vec3(0.0, 1.0, 0.0) : vec3(1.0, 0.0, 0.0); + vec3 T = normalize(cross(up, N)); + vec3 B = cross(N, T); + + return normalize(T * H.x + N * H.y + B * H.z); +} + +void main() +{ + ivec2 size = imageSize(outRadiance[push.mip]); + ivec2 pix = ivec2(gl_GlobalInvocationID.xy); + if (pix.x >= size.x || pix.y >= size.y) + return; + + vec2 uv = (vec2(pix) + 0.5) / vec2(size); + vec3 R = DirectionFromEquirect(uv); + vec3 N = R; + + const uint SAMPLE_COUNT = 1024u; + vec3 prefilteredColor = vec3(0.0); + float totalWeight = 0.0; + + for (uint i = 0u; i < SAMPLE_COUNT; ++i) + { + float u1 = float(i) / float(SAMPLE_COUNT); + float u2 = fract(sin(float(i) * 12.9898) * 43758.5453); + + vec3 H = ImportanceSampleGGX(u1, u2, push.roughness, N); + vec3 L = normalize(reflect(-R, H)); + float NoL = max(dot(N, L), 0.0); + + if (NoL > 0.0) + { + vec2 sampleUV = SampleSphericalMap(L); + vec3 sampleColor = texture(inHDRI, sampleUV).rgb; + + float NoH = max(dot(N, H), 0.0); + float HoL = max(dot(H, L), 0.0); + float D = DistributionGGX(N, H, push.roughness); + float pdf = (D * NoH) / (4.0 * HoL + 1e-5); + float omegaS = 1.0 / (pdf + 1e-5); + + prefilteredColor += sampleColor * NoL * omegaS; + totalWeight += NoL * omegaS; + } + } + + prefilteredColor = prefilteredColor / max(totalWeight, 1e-5); + imageStore(outRadiance[push.mip], pix, vec4(prefilteredColor, 1.0)); +} +)"; + + return shader_string; + } }; } \ No newline at end of file diff --git a/include/dz/ECS/Light.hpp b/include/dz/ECS/Light.hpp index 4ee7767b..9c4cee4d 100644 --- a/include/dz/ECS/Light.hpp +++ b/include/dz/ECS/Light.hpp @@ -14,14 +14,15 @@ namespace dz::ecs { float range; // for point/spot float innerCone; // for spot vec position; - int padding1 = 0; + int parent_index = -1; vec direction; // normalized, for directional/spot - int padding2 = 0; + int parent_cid = 0; vec color; // RGBA float outerCone; // for spot inline static constexpr size_t PID = 7; inline static float Priority = 3.5f; inline static constexpr BufferHost BufferHostType = BufferHost::GPU; + inline static constexpr bool IsLightProvider = true; inline static std::string ProviderName = "Light"; inline static std::string StructName = "Light"; inline static std::string GLSLStruct = R"( @@ -31,9 +32,9 @@ struct Light { float range; float innerCone; vec3 position; - int padding1; + int parent_index; vec3 direction; - int padding2; + int parent_cid; vec3 color; float outerCone; }; @@ -51,10 +52,12 @@ LightingParams lParams; inline static std::vector> GLSLMain = { {3.5f, R"( + vec3 N = normalize(current_normal); + vec3 V = normalize(camera.position - inPosition); lParams.worldPosition = inPosition; - lParams.viewDirection = normalize(camera.position - lParams.worldPosition); - lParams.NdotV = max(dot(current_normal, lParams.viewDirection), 0.0); - lParams.normal = current_normal; + lParams.viewDirection = V; + lParams.NdotV = max(dot(N, V), 0.0); + lParams.normal = N; lParams.lightsSize = Lights.data.length(); )", ShaderModuleType::Fragment} }; @@ -120,7 +123,7 @@ LightingParams lParams; void NotifyChange(int prop_index) override; }; - struct LightReflectableGroup : ReflectableGroup { + struct LightReflectableGroup : ::ReflectableGroup { BufferGroup* buffer_group = nullptr; std::string name; std::vector reflectables; diff --git a/include/dz/ECS/Material.hpp b/include/dz/ECS/Material.hpp index 306e3f3c..d0b38df0 100644 --- a/include/dz/ECS/Material.hpp +++ b/include/dz/ECS/Material.hpp @@ -18,6 +18,10 @@ namespace dz::ecs { vec metalness_atlas_pack = {-1.0f, -1.0f, -1.0f, -1.0f}; vec shininess_atlas_pack = {-1.0f, -1.0f, -1.0f, -1.0f}; vec albedo_color = {1.0f, 1.0f, 1.0f, 1.0f}; + float metalness = 0; + float roughness = 0; + int padding1 = 0; + int padding2 = 0; inline static constexpr size_t PID = 6; inline static float Priority = 2.5f; @@ -34,6 +38,10 @@ struct Material { vec4 metalness_atlas_pack; vec4 shininess_atlas_pack; vec4 albedo_color; + float metalness; + float roughness; + int padding1; + int padding2; }; struct MaterialParams { @@ -109,6 +117,8 @@ layout(binding = @BINDING@) uniform sampler2D RoughnessAtlas; layout(binding = @BINDING@) uniform sampler2D MetalnessAtlas; layout(binding = @BINDING@) uniform sampler2D MetalnessRoughnessAtlas; layout(binding = @BINDING@) uniform sampler2D ShininessAtlas; + +layout(binding = @BINDING@) uniform sampler2D brdfLUT; )" }; @@ -119,8 +129,8 @@ layout(binding = @BINDING@) uniform sampler2D ShininessAtlas; {0.5f, R"( EnsureMaterialFragColor(inUV2, submesh, current_color); EnsureMaterialNormal(inUV2, submesh, current_normal); - float metalness = 0.0; - float roughness = 0.0; + float metalness = Materials.data[submesh.material_index].metalness; + float roughness = Materials.data[submesh.material_index].roughness; EnsureMaterialMetalnessRoughness(inUV2, submesh, metalness, roughness); mParams.albedo = vec3(current_color); mParams.normal = current_normal; @@ -128,6 +138,12 @@ layout(binding = @BINDING@) uniform sampler2D ShininessAtlas; mParams.roughness = roughness; )", ShaderModuleType::Fragment} }; + + static Shader* ensure_brdfLUT_shader(Image* brdfLUT_image); + + static void StaticInitialize(); + + static void ShaderTweak(Shader*); struct MaterialReflectable : ::Reflectable { @@ -136,16 +152,24 @@ layout(binding = @BINDING@) uniform sampler2D ShininessAtlas; int uid; std::string name; inline static std::unordered_map> prop_name_indexes = { - {"Albedo Color", {0, 0}} + {"Albedo Color", {0, 0}}, + {"Metalness", {1, 0}}, + {"Roughness", {2, 0}} }; inline static std::unordered_map prop_index_names = { - {0, "Albedo Color"} + {0, "Albedo Color"}, + {1, "Metalness"}, + {2, "Roughness"} }; inline static std::vector prop_names = { - "Albedo Color" + "Albedo Color", + "Metalness", + "Roughness" }; inline static const std::vector typeinfos = { - &typeid(color_vec) + &typeid(color_vec), + &typeid(float), + &typeid(float) }; public: @@ -160,7 +184,7 @@ layout(binding = @BINDING@) uniform sampler2D ShininessAtlas; void NotifyChange(int prop_index) override; }; - struct MaterialReflectableGroup : ReflectableGroup { + struct MaterialReflectableGroup : ::ReflectableGroup { BufferGroup* buffer_group = nullptr; std::string name; std::vector reflectables; diff --git a/include/dz/ECS/Mesh.hpp b/include/dz/ECS/Mesh.hpp index bf36ed57..99260f0f 100644 --- a/include/dz/ECS/Mesh.hpp +++ b/include/dz/ECS/Mesh.hpp @@ -117,7 +117,7 @@ vec3 GetMeshBitangent(in Mesh mesh) { void NotifyChange(int prop_index) override; }; - struct MeshReflectableGroup : ReflectableGroup { + struct MeshReflectableGroup : ::ReflectableGroup { BufferGroup* buffer_group = nullptr; std::string name; std::vector reflectables; diff --git a/include/dz/ECS/PhongLighting.hpp b/include/dz/ECS/PhongLighting.hpp index 2c2fe232..799c9814 100644 --- a/include/dz/ECS/PhongLighting.hpp +++ b/include/dz/ECS/PhongLighting.hpp @@ -55,7 +55,7 @@ vec3 CalculatePhongLighting(in Light light) { )", ShaderModuleType::Fragment} }; - struct PhongLightingReflectableGroup : ReflectableGroup { + struct PhongLightingReflectableGroup : ::ReflectableGroup { BufferGroup* buffer_group = nullptr; std::string name; std::vector reflectables; diff --git a/include/dz/ECS/PhysicallyBasedLighting.hpp b/include/dz/ECS/PhysicallyBasedLighting.hpp index f3bd8512..625926e4 100644 --- a/include/dz/ECS/PhysicallyBasedLighting.hpp +++ b/include/dz/ECS/PhysicallyBasedLighting.hpp @@ -21,6 +21,11 @@ vec3 fresnelSchlick(vec3 F0, float cosTheta) return F0 + (1.0 - F0) * pow(1.0 - cosTheta, 5.0); } +vec3 fresnelSchlickRoughness(vec3 F0, float cosTheta, float roughness) +{ + return F0 + (max(vec3(1.0 - roughness), F0) - F0) * pow(1.0 - cosTheta, 5.0); +} + // GGX/Towbridge-Reitz normal distribution function. // Uses Disney's reparametrization of alpha = roughness^2 float ndfGGX(float cosLh, float roughness) @@ -46,24 +51,23 @@ float gaSchlickGGX(float cosLi, float NdotV, float roughness) return gaSchlickG1(cosLi, k) * gaSchlickG1(NdotV, k); } -vec3 IBL(vec3 F0, vec3 Lr) -{ - // vec3 irradiance = texture(u_EnvIrradianceTex, m_Params.Normal).rgb; - // vec3 F = fresnelSchlickRoughness(F0, m_Params.NdotV, m_Params.Roughness); - // vec3 kd = (1.0 - F) * (1.0 - m_Params.Metalness); - // vec3 diffuseIBL = m_Params.Albedo * irradiance; - - // int u_EnvRadianceTexLevels = textureQueryLevels(u_EnvRadianceTex); - // float NoV = clamp(m_Params.NdotV, 0.0, 1.0); - // vec3 R = 2.0 * dot(m_Params.View, m_Params.Normal) * m_Params.Normal - m_Params.View; - // vec3 specularIrradiance = textureLod(u_EnvRadianceTex, RotateVectorAboutY(u_EnvMapRotation, Lr), (m_Params.Roughness * m_Params.Roughness) * u_EnvRadianceTexLevels).rgb; - - // // Sample BRDF Lut, 1.0 - roughness for y-coord because texture was generated (in Sparky) for gloss model - // vec2 specularBRDF = texture(u_BRDFLUTTexture, vec2(m_Params.NdotV, 1.0 - m_Params.Roughness)).rg; - // vec3 specularIBL = specularIrradiance * (F * specularBRDF.x + specularBRDF.y); - - // return kd * diffuseIBL + specularIBL; - return vec3(0.0); +vec3 IBL(vec3 F0, vec3 N, vec3 V) { + // diffuse IBL (view-independent) // + vec3 irradiance = SampleIrradiance( 0, N ).rgb; + vec3 diffuseBRDF = mParams.albedo / PI; + vec3 diffuse = diffuseBRDF * irradiance; + + // specular IBL (view-dependent) // + float NdotV = max( dot( N, V ), 0.0 ); + vec3 R = reflect( -V, N ); // reflection vector + int maxMips = textureQueryLevels( RadianceAtlas ); + float mip = mParams.roughness * mParams.roughness * float( maxMips ); + vec3 radiance = SampleRadiance( 0, R ).rgb; + vec2 brdfLUT = texture( brdfLUT, vec2( NdotV, 1.0 - mParams.roughness ) ).rg; + vec3 F = fresnelSchlickRoughness( F0, NdotV, mParams.roughness ); + vec3 specular = radiance * ( F * brdfLUT.x + brdfLUT.y ); + + return diffuse + specular; } vec3 PBDL(vec3 F0, in Light light) { @@ -93,6 +97,11 @@ vec3 PBDL(vec3 F0, in Light light) { vec3 PBL(vec3 F0) { vec3 result = vec3(0.0); for (int light_index = 0; light_index < lParams.lightsSize; light_index++) { + int lightTopIndex = -1; + int lightTopCID = 0; + GetTopNodeByCID(light_index, CID_Light, lightTopIndex, lightTopCID, CID_Scene); + if (lightTopIndex != inTopNodeIndex) + continue; switch (Lights.data[light_index].type) { case 0: result += PBDL(F0, Lights.data[light_index]); @@ -112,13 +121,13 @@ vec3 PBL(vec3 F0) { vec3 F0 = mix(Fdielectric, mParams.albedo, mParams.metalness); vec3 lightContribution = PBL(F0); - vec3 iblContribution = IBL(F0, Lr); + vec3 iblContribution = IBL(F0, N, V); current_color = vec4(lightContribution + iblContribution, 1.0); )", ShaderModuleType::Fragment} }; - struct PhysicallyBasedLightingReflectableGroup : ReflectableGroup { + struct PhysicallyBasedLightingReflectableGroup : ::ReflectableGroup { BufferGroup* buffer_group = nullptr; std::string name; std::vector reflectables; diff --git a/include/dz/ECS/Provider.hpp b/include/dz/ECS/Provider.hpp index da80768c..5f168712 100644 --- a/include/dz/ECS/Provider.hpp +++ b/include/dz/ECS/Provider.hpp @@ -36,6 +36,23 @@ namespace dz { return 0; } + inline static void RunStaticInitialize() { + static bool initialized = false; + if (initialized) + return; + if constexpr (requires { T::StaticInitialize; }) { + T::StaticInitialize(); + } + initialized = true; + return; + } + + inline static void RunShaderTweak(Shader* shader) { + if constexpr (requires { T::ShaderTweak(shader); }) { + T::ShaderTweak(shader); + } + } + inline static constexpr BufferHost GetBufferHostType() { if constexpr (requires { T::BufferHostType; } ) { return T::BufferHostType; @@ -106,6 +123,13 @@ namespace dz { return false; } + inline static constexpr bool GetIsLightProvider() { + if constexpr (requires { T::IsLightProvider; }) { + return T::IsLightProvider; + } + return false; + } + inline static float GetPriority() { if constexpr (requires { T::Priority; }) { return T::Priority; @@ -229,6 +253,12 @@ namespace dz { static constexpr bool value = Provider::GetIsSkyBoxProvider(); }; + template + struct IsLightProvider + { + static constexpr bool value = Provider::GetIsLightProvider(); + }; + template class Trait, typename... Ts> struct FirstMatchingOrDefault; diff --git a/include/dz/ECS/Scene.hpp b/include/dz/ECS/Scene.hpp index 3fad5619..d936c0a2 100644 --- a/include/dz/ECS/Scene.hpp +++ b/include/dz/ECS/Scene.hpp @@ -120,7 +120,7 @@ void GetSceneModel(int scene_index, out mat4 out_model, out int parent_index, ou void NotifyChange(int prop_index) override; }; - struct SceneReflectableGroup : ReflectableGroup { + struct SceneReflectableGroup : ::ReflectableGroup { BufferGroup* buffer_group = nullptr; std::string name; std::vector> reflectable_children; diff --git a/include/dz/ECS/SkyBox.hpp b/include/dz/ECS/SkyBox.hpp index ee457e3a..0c622943 100644 --- a/include/dz/ECS/SkyBox.hpp +++ b/include/dz/ECS/SkyBox.hpp @@ -23,16 +23,8 @@ struct SkyBox { int padding; }; )"; - inline static std::unordered_map GLSLMethods = { - {ShaderModuleType::Fragment, R"( -)" } - }; - inline static std::vector> GLSLMain = { - {4.0f, R"( -)", ShaderModuleType::Fragment} - }; - struct SkyBoxReflectableGroup : ReflectableGroup { + struct SkyBoxReflectableGroup : ::ReflectableGroup { BufferGroup* buffer_group = nullptr; std::string name; std::vector reflectables; diff --git a/include/dz/ECS/SubMesh.hpp b/include/dz/ECS/SubMesh.hpp index feb056c9..637ab541 100644 --- a/include/dz/ECS/SubMesh.hpp +++ b/include/dz/ECS/SubMesh.hpp @@ -69,7 +69,7 @@ struct SubMesh { void NotifyChange(int prop_index) override; }; - struct SubMeshReflectableGroup : ReflectableGroup { + struct SubMeshReflectableGroup : ::ReflectableGroup { BufferGroup* buffer_group = nullptr; std::string name; std::vector reflectables; diff --git a/include/dz/ImGuiLayer.hpp b/include/dz/ImGuiLayer.hpp index e3652837..e1f0cead 100644 --- a/include/dz/ImGuiLayer.hpp +++ b/include/dz/ImGuiLayer.hpp @@ -9,7 +9,7 @@ namespace dz { struct ImGuiLayer { friend WINDOW; - friend std::pair image_create_descriptor_set(Image* image); + friend std::pair image_create_descriptor_set(Image*, uint32_t); using ImmediateDrawFunction = std::function; using ImmediateDrawPair = std::pair; private: diff --git a/include/dz/Image.hpp b/include/dz/Image.hpp index 0e76211c..29d71b50 100644 --- a/include/dz/Image.hpp +++ b/include/dz/Image.hpp @@ -27,7 +27,7 @@ namespace dz uint32_t width = 1; uint32_t height = 1; uint32_t depth = 1; - VkFormat format = VK_FORMAT_R8G8B8A8_UNORM; + VkFormat format = (VkFormat)ColorSpace::SRGB; VkImageUsageFlags usage = VK_IMAGE_USAGE_SAMPLED_BIT | VK_IMAGE_USAGE_TRANSFER_DST_BIT | VK_IMAGE_USAGE_TRANSFER_SRC_BIT; VkImageType image_type = VK_IMAGE_TYPE_2D; VkImageViewType view_type = VK_IMAGE_VIEW_TYPE_2D; @@ -35,8 +35,9 @@ namespace dz VkMemoryPropertyFlags memory_properties = VK_MEMORY_PROPERTY_DEVICE_LOCAL_BIT; VkSampleCountFlagBits multisampling = VK_SAMPLE_COUNT_1_BIT; bool is_framebuffer_attachment = false; - void* data = nullptr; + std::vector> datas; SurfaceType surfaceType = SurfaceType::BaseColor; + uint32_t mip_levels = 1; }; /** @@ -47,11 +48,12 @@ namespace dz Image* image_create(const ImageCreateInfo&); /** - * @brief Uploads new data to an Image + * @brief Uploads new data to an Image at a given mip level * - * @note data must contain the bytes in the correct format + * @note if just image_ptr is provided, will upload whatever exists in CPU buffer + * @note if data provided, data must contain the bytes in the correct format */ - void image_upload_data(Image* image, void* data); + void image_upload_data(Image* image_ptr, uint32_t mip = 0, void* data = nullptr); /** * @brief Resizes a 2D image to the specified dimensions. @@ -84,7 +86,7 @@ namespace dz * * @returns a pair containing the SetLayout and Set for usage */ - std::pair image_create_descriptor_set(Image* image); + std::pair image_create_descriptor_set(Image* image, uint32_t mip_level = 0); /** * @brief Gets the Channels and Size Of Type as a pair @@ -102,4 +104,65 @@ namespace dz * @brief returns the underlying surface type of an Image */ SurfaceType image_get_surface_type(Image*); + + /** + * @brief returns the underlying width of an Image + */ + uint32_t image_get_width(Image*); + + /** + * @brief returns the underlying height of an Image + */ + uint32_t image_get_height(Image*); + + /** + * @brief returns the underlying depth of an Image + */ + uint32_t image_get_depth(Image*); + + /** + * @brief returns the underlying layout of an Image + */ + VkImageLayout image_get_layout(Image*, int mip); + + /** + * @brief returns the underlying format of an Image + */ + VkFormat image_get_format(Image*); + + /** + * @brief transitions the underlying layout of an Image + */ + void transition_image_layout(Image* image_ptr, VkImageLayout new_layout, int mip = 0); + + /** + * @brief helper function to return the pixel size for a given VkFormat in bytes + */ + size_t get_format_pixel_size(VkFormat format); + + /** + * @brief Begins the copy command buffer + * + * @note is not thread safe, i.e. only one image copy can be in process at once + */ + void image_copy_begin(); + + /** + * @brief Reserves 'count' regions in the current copy command queue + * + * @note this must always be called with the exact number of regions you expect to copy, before calling image_copy_image + */ + void image_copy_reserve_regions(uint32_t count); + + /** + * @brief Copys srcImage into dstImage with given region + * + * @note must be called between image_copy_begin and image_copy_end + */ + void image_copy_image(Image* dstImage, Image* srcImage, VkImageCopy region); + + /** + * @brief Ends the copy command buffer + */ + void image_copy_end(); } \ No newline at end of file diff --git a/include/dz/ImagePack.hpp b/include/dz/ImagePack.hpp index 8d4644ea..c92a526e 100644 --- a/include/dz/ImagePack.hpp +++ b/include/dz/ImagePack.hpp @@ -14,23 +14,85 @@ namespace dz { using rect_type = rectpack2D::output_rect_t; Image* atlas = nullptr; bool owns_atlas = true; + VkFormat atlas_format = (VkFormat)ColorSpace::SRGB; + bool format_changed = false; std::vector image_vec; std::vector rect_vec; - std::vector rgba_buffer; + std::vector atlas_buffer_sizes; + std::vector> atlas_buffers; bool is_dirty(); void repack(); + void CPU_Image_Copy(int atlas_width, int atlas_height, size_t pixel_size, uint32_t atlas_mip_levels); + void GPU_Image_Copy(int atlas_width, int atlas_height, size_t pixel_size, uint32_t atlas_mip_levels); size_t findImageIndex(Image* image); + bool enforce_same_format = true; + bool enforce_same_miplvl = true; public: + ~ImagePack(); + void SetOwnAtlas(bool owns = false); + + void SetAtlasFormat(VkFormat new_format); + + void SetEnforceSameFormat(bool enforced = true); + void addImage(Image* image); + bool check(); + Image* getAtlas(); + vec getUV(vec original_uv, Image* image); + std::vector> getUVs(const std::vector>& original_uvs, Image* image); + rect_type& findPackedRect(Image* image); + size_t size() const; + bool empty() const; + + private: + + using ConvertFunc = std::function; + + // Conversion helpers + static void copy_bytes(const void* src, void* dst, size_t size); + + // Float conversion helpers + static uint8_t float_to_u8(float val); + + static float u8_to_float(uint8_t val); + + static void convert_R32G32B32A32_SFLOAT_to_R8G8B8A8_UNORM(const void* src, void* dst); + + static void convert_R8G8B8A8_UNORM_to_R32G32B32A32_SFLOAT(const void* src, void* dst); + + // Extend with more detailed conversions as needed... + + inline static const VkFormat supported_formats[] = { + VK_FORMAT_R8_UNORM, + VK_FORMAT_R8G8_UNORM, + VK_FORMAT_R8G8B8_UNORM, + VK_FORMAT_R8G8B8A8_UNORM, + VK_FORMAT_R32_SFLOAT, + VK_FORMAT_R32G32_SFLOAT, + VK_FORMAT_R32G32B32_SFLOAT, + VK_FORMAT_R32G32B32A32_SFLOAT + }; + + inline static constexpr int FMT_COUNT = sizeof(supported_formats) / sizeof(*supported_formats); + + inline static ConvertFunc conversion_matrix[FMT_COUNT][FMT_COUNT] = {}; + inline static size_t format_sizes[FMT_COUNT] = {}; + + static int format_index(VkFormat fmt); + + static void init_conversion_matrix(); + + void convert_pixel(VkFormat src_format, VkFormat dst_format, const void* src, void* dst); + }; } \ No newline at end of file diff --git a/include/dz/Loaders/Assimp_Loader.hpp b/include/dz/Loaders/Assimp_Loader.hpp index 07791af6..1fbcf153 100644 --- a/include/dz/Loaders/Assimp_Loader.hpp +++ b/include/dz/Loaders/Assimp_Loader.hpp @@ -23,6 +23,9 @@ namespace dz::loaders { using TNormal = vec; using TTangent = vec; using TBitangent = vec; + using TColor = vec; + using TMetalness = float; + using TRoughness = float; using AddSceneFunction = std::function; using AddMaterialFunction = std::function& + const std::vector&, + TColor, + TMetalness, + TRoughness )>; using AddMeshFunction = std::function bytes; size_t bytes_length = 0; + bool load_float = 0; // false loads UNORM, true loads SFLOAT }; struct STB_Image_Loader { using value_type = Image*; diff --git a/include/dz/Renderer.hpp b/include/dz/Renderer.hpp index a2f85a28..52cb8151 100644 --- a/include/dz/Renderer.hpp +++ b/include/dz/Renderer.hpp @@ -1,3 +1,4 @@ +#pragma once #if defined(_WIN32) || defined(__linux__) || defined(__APPLE__) || defined(ANDROID) #define RENDERER_VULKAN #endif @@ -23,3 +24,9 @@ #include #include #include + +enum class ColorSpace : uint32_t { + SRGB = VK_FORMAT_R8G8B8A8_SRGB, + UNORM = VK_FORMAT_R8G8B8A8_UNORM +}; + diff --git a/include/dz/Shader.hpp b/include/dz/Shader.hpp index f2676840..3ff66990 100644 --- a/include/dz/Shader.hpp +++ b/include/dz/Shader.hpp @@ -93,7 +93,7 @@ namespace dz * @param shader Pointer to the Shader. * @param dispatch_layout Layout dimensions as vec. */ - void shader_dispatch(Shader*, vec dispatch_layout); + void shader_dispatch(Shader*, uint32_t x, uint32_t y, uint32_t z, void(*shader_pre_dispatch)(Shader*, void*) = 0, void(*shader_post_dispatch)(Shader*, void*) = 0, void* user_data = nullptr); /** * @brief Compiles the shader source code to SPIR-V. @@ -161,6 +161,13 @@ namespace dz */ void shader_update_push_constant(Shader*, uint32_t pc_index, void* data, uint32_t size); + /** + * @brief Updates the push_constants with the underlying shader pipeline + * + * @note this only needs to be called for Compute shaders, Raster shaders have it done automatically with DrawListManagers + */ + void shader_ensure_push_constants(Shader* shader); + /** * @brief Sets the line width for a Shader * diff --git a/include/dz/Window.hpp b/include/dz/Window.hpp index 30602122..dbeb0f7f 100644 --- a/include/dz/Window.hpp +++ b/include/dz/Window.hpp @@ -38,7 +38,7 @@ namespace dz #endif }; - struct WindowReflectableGroup : ReflectableGroup { + struct WindowReflectableGroup : ::ReflectableGroup { private: WINDOW* window_ptr; diff --git a/models/RedSportsCar.glb b/models/RedSportsCar.glb new file mode 100644 index 00000000..929c8c98 Binary files /dev/null and b/models/RedSportsCar.glb differ diff --git a/models/metal_plane.glb b/models/metal_plane.glb new file mode 100644 index 00000000..c7531764 Binary files /dev/null and b/models/metal_plane.glb differ diff --git a/src/BufferGroup.cpp b/src/BufferGroup.cpp index 361b18ce..d06ec6d1 100644 --- a/src/BufferGroup.cpp +++ b/src/BufferGroup.cpp @@ -23,7 +23,7 @@ namespace dz { auto shader = sp.first; shader->bound_buffer_groups.push_back(buffer_group); shader_initialize(shader); - CreateAndBindShaderBuffers(buffer_group, shader); + shader_buffers_ensure_and_bind(buffer_group, shader); // shader_update_descriptor_sets(shader); } } @@ -50,18 +50,19 @@ namespace dz { return flags; } - Image* buffer_group_define_image_2D(BufferGroup* buffer_group, const std::string& buffer_name, uint32_t image_width, uint32_t image_height, void* data_pointer) { + Image* buffer_group_define_image_2D(BufferGroup* buffer_group, const std::string& buffer_name, uint32_t image_width, uint32_t image_height, const std::vector>& datas) { ShaderImage& shader_image = buffer_group->images[buffer_name]; - ImageCreateInfoInternal create_info{}; - create_info.width = image_width; - create_info.height = image_height; - create_info.depth = 1; - create_info.format = shader_image.format; - create_info.image_type = VK_IMAGE_TYPE_2D; - create_info.view_type = VK_IMAGE_VIEW_TYPE_2D; - create_info.data = data_pointer; - create_info.usage = infer_image_usage_flags(shader_image.descriptor_types); + ImageCreateInfoInternal create_info{ + .width = image_width, + .height = image_height, + .depth = 1, + .format = shader_image.format, + .usage = infer_image_usage_flags(shader_image.descriptor_types), + .image_type = VK_IMAGE_TYPE_2D, + .view_type = VK_IMAGE_VIEW_TYPE_2D, + .datas = datas + }; auto image = image_create_internal(create_info); @@ -70,7 +71,7 @@ namespace dz { return image; } - Image* buffer_group_define_image_3D(BufferGroup* buffer_group, const std::string& buffer_name, uint32_t image_width, uint32_t image_height, uint32_t image_depth, void* data_pointer) { + Image* buffer_group_define_image_3D(BufferGroup* buffer_group, const std::string& buffer_name, uint32_t image_width, uint32_t image_height, uint32_t image_depth, const std::vector>& datas) { ShaderImage& shader_image = buffer_group->images[buffer_name]; ImageCreateInfoInternal create_info{ @@ -81,7 +82,7 @@ namespace dz { .usage = infer_image_usage_flags(shader_image.descriptor_types), .image_type = VK_IMAGE_TYPE_3D, .view_type = VK_IMAGE_VIEW_TYPE_3D, - .data = data_pointer + .datas = datas }; auto image = image_create_internal(create_info); diff --git a/src/Directz.cpp.hpp b/src/Directz.cpp.hpp index 27ac5921..f8ad33b2 100644 --- a/src/Directz.cpp.hpp +++ b/src/Directz.cpp.hpp @@ -99,6 +99,14 @@ struct FormatsSupported { bool R8G8_UNORM; bool R8G8B8_UNORM; bool R8G8B8A8_UNORM; + bool R8_SRGB; + bool R8G8_SRGB; + bool R8G8B8_SRGB; + bool R8G8B8A8_SRGB; + bool R32_SFLOAT; + bool R32G32_SFLOAT; + bool R32G32B32_SFLOAT; + bool R32G32B32A32_SFLOAT; }; struct DirectRegistry @@ -114,12 +122,14 @@ struct DirectRegistry VkQueue graphicsQueue; VkQueue computeQueue; VkQueue presentQueue; + VkQueue copyQueue; int32_t graphicsAndComputeFamily = -1; int32_t presentFamily = -1; Renderer* currentRenderer = 0; VkCommandPool commandPool = VK_NULL_HANDLE; VkCommandBuffer* commandBuffer = 0; VkCommandBuffer computeCommandBuffer = VK_NULL_HANDLE; + VkCommandBuffer copyCommandBuffer = VK_NULL_HANDLE; VkSampleCountFlagBits maxMSAASamples = VK_SAMPLE_COUNT_1_BIT; std::vector window_ptrs; std::vector window_reflectable_entries; @@ -137,6 +147,10 @@ struct DirectRegistry > > formats_supported_map; FormatsSupported formats_supported; + std::vector copyRegions; + std::vector> copySrcImages; + std::vector> copyDstImages; + ColorSpace preferredColorSpace = ColorSpace::SRGB; #ifdef _WIN32 HWND hwnd_root; #endif diff --git a/src/ECS/HDRI.cpp b/src/ECS/HDRI.cpp index 62a78f40..fd5f5f33 100644 --- a/src/ECS/HDRI.cpp +++ b/src/ECS/HDRI.cpp @@ -1,6 +1,14 @@ #include #include +void dz::ecs::set_radiance_control_block(Shader* shader, void* user_data) { + RadianceControlBlock& controlBlock = *(RadianceControlBlock*)user_data; + shader_update_push_constant(shader, 0, (void*)&controlBlock.roughness, sizeof(float)); + shader_update_push_constant(shader, 1, (void*)&controlBlock.mip, sizeof(int)); + shader_ensure_push_constants(shader); + +} + dz::ecs::HDRI::HDRIReflectable::HDRIReflectable(const std::function& get_hdri_function): get_hdri_function(get_hdri_function), uid(int(GlobalUID::GetNew("Reflectable"))), diff --git a/src/ECS/Material.cpp b/src/ECS/Material.cpp index 053a1cf8..fe86ebc4 100644 --- a/src/ECS/Material.cpp +++ b/src/ECS/Material.cpp @@ -21,8 +21,184 @@ void* dz::ecs::Material::MaterialReflectable::GetVoidPropertyByIndex(int prop_in auto& material = *material_ptr; switch (prop_index) { case 0: return &material.albedo_color; + case 1: return &material.metalness; + case 2: return &material.roughness; default: return nullptr; } } -void dz::ecs::Material::MaterialReflectable::NotifyChange(int prop_index) {} \ No newline at end of file +void dz::ecs::Material::MaterialReflectable::NotifyChange(int prop_index) {} + +BufferGroup* ensure_brdfLUT_buffer_group() { + static BufferGroup* buffer_group = nullptr; + if (buffer_group) + return buffer_group; + buffer_group = buffer_group_create("brdfLUT_Group"); + buffer_group_restrict_to_keys(buffer_group, {"brdfLUT"}); + return buffer_group; +} + +std::string Generate_brdfLUT_Compute_Shader(); + +Shader* dz::ecs::Material::ensure_brdfLUT_shader(Image* brdfLUT_image) { + static Shader* shader = nullptr; + + if (shader) + return shader; + + shader = shader_create(); + + auto buffer_group = ensure_brdfLUT_buffer_group(); + + shader_add_buffer_group(shader, buffer_group); + + shader_add_module(shader, ShaderModuleType::Compute, Generate_brdfLUT_Compute_Shader()); + + shader_use_image(shader, "brdfLUT", brdfLUT_image); + + shader_initialize(shader); + + shader_update_descriptor_sets(shader); + + return shader; +} + +static Image* brdfLUT_image = nullptr; + +void dz::ecs::Material::StaticInitialize() { + brdfLUT_image = image_create({ + .width = 512, + .height = 512, + .format = VK_FORMAT_R16G16B16A16_SFLOAT, + .usage = VK_IMAGE_USAGE_STORAGE_BIT | VK_IMAGE_USAGE_SAMPLED_BIT | VK_IMAGE_USAGE_TRANSFER_DST_BIT | VK_IMAGE_USAGE_TRANSFER_SRC_BIT + }); + Shader* brdfLUT_shader = ensure_brdfLUT_shader(brdfLUT_image); + transition_image_layout(brdfLUT_image, VK_IMAGE_LAYOUT_GENERAL); + shader_dispatch(brdfLUT_shader, 512 / 16, 512 / 16, 1); + transition_image_layout(brdfLUT_image, VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL); + return; +} + +void dz::ecs::Material::ShaderTweak(Shader* shader) { + shader_use_image(shader, "brdfLUT", brdfLUT_image); +} + +std::string Generate_brdfLUT_Compute_Shader() { + std::string shader_string; + + shader_string += R"( +#version 450 + +layout (local_size_x = 16, local_size_y = 16) in; + +layout (binding = 0, rgba16f) uniform image2D brdfLUT; + +const float PI = 3.14159265359; + +vec3 ImportanceSampleGGX(vec2 Xi, float Roughness, vec3 N) +{ + float a = Roughness * Roughness; + float phi = 2.0 * PI * Xi.x; + float cosTheta = sqrt((1.0 - Xi.y) / (1.0 + (a * a - 1.0) * Xi.y)); + float sinTheta = sqrt(1.0 - cosTheta * cosTheta); + + vec3 H; + H.x = cos(phi) * sinTheta; + H.y = sin(phi) * sinTheta; + H.z = cosTheta; + + vec3 up = abs(N.z) < 0.999 ? vec3(0.0, 0.0, 1.0) : vec3(1.0, 0.0, 0.0); + vec3 tangentX = normalize(cross(up, N)); + vec3 tangentY = cross(N, tangentX); + vec3 sampleVec = tangentX * H.x + tangentY * H.y + N * H.z; + + return normalize(sampleVec); +} + +float GeometrySchlickGGX(float NdotV, float Roughness) +{ + float a = Roughness; + float k = (a * a) / 2.0; + float denom = NdotV * (1.0 - k) + k; + return NdotV / denom; +} + +float GeometrySmith(float NdotV, float NdotL, float Roughness) +{ + float ggx2 = GeometrySchlickGGX(NdotV, Roughness); + float ggx1 = GeometrySchlickGGX(NdotL, Roughness); + return ggx1 * ggx2; +} + +float RadicalInverse_VdC(uint bits) +{ + bits = (bits << 16u) | (bits >> 16u); + bits = ((bits & 0x55555555u) << 1u) | ((bits & 0xAAAAAAAAu) >> 1u); + bits = ((bits & 0x33333333u) << 2u) | ((bits & 0xCCCCCCCCu) >> 2u); + bits = ((bits & 0x0F0F0F0Fu) << 4u) | ((bits & 0xF0F0F0F0u) >> 4u); + bits = ((bits & 0x00FF00FFu) << 8u) | ((bits & 0xFF00FF00u) >> 8u); + return float(bits) * 2.3283064365386963e-10; +} + +vec2 Hammersley(uint i, uint N) +{ + return vec2(float(i) / float(N), RadicalInverse_VdC(i)); +} + +vec2 IntegrateBRDF(float NdotV, float Roughness) +{ + vec3 V; + V.x = sqrt(1.0 - NdotV * NdotV); + V.y = 0.0; + V.z = NdotV; + + float A = 0.0; + float B = 0.0; + + vec3 N = vec3(0.0, 0.0, 1.0); + + const uint SAMPLE_COUNT = 1024u; + for (uint i = 0u; i < SAMPLE_COUNT; ++i) + { + vec2 Xi = Hammersley(i, SAMPLE_COUNT); + vec3 H = ImportanceSampleGGX(Xi, Roughness, N); + vec3 L = normalize(2.0 * dot(V, H) * H - V); + + float NdotL = max(L.z, 0.0); + float NdotH = max(H.z, 0.0); + float VdotH = max(dot(V, H), 0.0); + + if (NdotL > 0.0) + { + float G = GeometrySmith(NdotV, NdotL, Roughness); + float G_Vis = (G * VdotH) / (NdotH * NdotV); + float Fc = pow(1.0 - VdotH, 5.0); + + A += (1.0 - Fc) * G_Vis; + B += Fc * G_Vis; + } + } + + A /= float(SAMPLE_COUNT); + B /= float(SAMPLE_COUNT); + return vec2(A, B); +} + +void main() +{ + ivec2 pixelCoord = ivec2(gl_GlobalInvocationID.xy); + if (pixelCoord.x >= 512 || pixelCoord.y >= 512) + return; + + vec2 uv = vec2(pixelCoord) / vec2(511.0, 511.0); + float NdotV = uv.x; + float Roughness = uv.y; + + vec2 integratedBRDF = IntegrateBRDF(NdotV, Roughness); + + imageStore(brdfLUT, pixelCoord, vec4(integratedBRDF, 0.0, 1.0)); +} +)"; + + return shader_string; +} \ No newline at end of file diff --git a/src/Framebuffer.cpp b/src/Framebuffer.cpp index 28be55c2..6e657ec0 100644 --- a/src/Framebuffer.cpp +++ b/src/Framebuffer.cpp @@ -93,12 +93,15 @@ namespace dz { auto& image = *framebuffer.pImages[attachment_index]; auto& attachmentType = framebuffer.pAttachmentTypes[attachment_index]; - if (image.imageView == VK_NULL_HANDLE) + assert(image.mip_levels == 1); + + auto& imageView = image.imageViews[0]; + if (imageView == VK_NULL_HANDLE) { throw std::runtime_error("Framebuffer attachment texture image view is null!"); } - vkImageViews.push_back(image.imageView); + vkImageViews.push_back(imageView); UsingAttachmentDescription clearAttachment{}; UsingAttachmentDescription loadAttachment{}; @@ -554,12 +557,15 @@ namespace dz { ) { auto& image = *framebuffer.new_pImages[attachment_index]; - if (image.imageView == VK_NULL_HANDLE) + assert(image.mip_levels == 1); + + auto& imageView = image.imageViews[0]; + if (imageView == VK_NULL_HANDLE) { throw std::runtime_error("Framebuffer attachment texture image view is null!"); } - vkImageViews.push_back(image.imageView); + vkImageViews.push_back(imageView); if (framebuffer.width != image.width || framebuffer.height != image.height) { @@ -761,18 +767,19 @@ namespace dz { { auto& DepthImage = *DepthImage_ptr; static auto new_layout = VK_IMAGE_LAYOUT_DEPTH_STENCIL_ATTACHMENT_OPTIMAL; - if (DepthImage.current_layout != new_layout) + auto& current_layout = DepthImage.current_layouts[0]; + if (current_layout != new_layout) { VkImageMemoryBarrier barrier; VkPipelineStageFlags sourceStage; VkPipelineStageFlags destinationStage; prepareImageBarrier(framebuffer.commandBuffer, DepthImage.image, DepthImage.format, - DepthImage.current_layout, + current_layout, new_layout, VK_IMAGE_ASPECT_DEPTH_BIT, sourceStage, destinationStage, barrier); vkCmdPipelineBarrier(framebuffer.commandBuffer, sourceStage, destinationStage, 0, 0, nullptr, 0, nullptr, 1, &barrier); - DepthImage.current_layout = new_layout; + current_layout = new_layout; } } } @@ -786,14 +793,13 @@ namespace dz { // shadowMapImage->isDirty = true; // auto& textureImpl = *static_cast(shadowMapImage->rendererData); - - if (DepthImage.current_layout != VK_IMAGE_LAYOUT_DEPTH_STENCIL_ATTACHMENT_OPTIMAL && - DepthImage.current_layout != VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL) + auto& current_layout = DepthImage.current_layouts[0]; + if (current_layout != VK_IMAGE_LAYOUT_DEPTH_STENCIL_ATTACHMENT_OPTIMAL && + current_layout != VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL) { std::cerr << "Warning: Directional shadow map layout was not ATTACHMENT_OPTIMAL before transition!" << std::endl; } - auto& current_layout = DepthImage.current_layout; auto new_layout = VK_IMAGE_LAYOUT_DEPTH_STENCIL_READ_ONLY_OPTIMAL; vkCmdSetEvent(framebuffer.commandBuffer, framebuffer.event, VK_PIPELINE_STAGE_LATE_FRAGMENT_TESTS_BIT); @@ -835,7 +841,8 @@ namespace dz { auto& ColorImage = *ColorImage_ptr; static auto new_layout = VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL; - if (ColorImage.current_layout != new_layout) + auto& current_layout = ColorImage.current_layouts[0]; + if (current_layout != new_layout) { VkImageMemoryBarrier colorBarrier = {}; VkPipelineStageFlags sourceStage; @@ -843,10 +850,10 @@ namespace dz { prepareImageBarrier( framebuffer.commandBuffer, ColorImage.image, ColorImage.format, - ColorImage.current_layout, new_layout, + current_layout, new_layout, VK_IMAGE_ASPECT_COLOR_BIT, sourceStage, destinationStage, colorBarrier); vkCmdPipelineBarrier(framebuffer.commandBuffer, sourceStage, destinationStage, 0, 0, nullptr, 0, nullptr, 1, &colorBarrier); - ColorImage.current_layout = new_layout; + current_layout = new_layout; } } } @@ -859,7 +866,7 @@ namespace dz { auto& ColorImage = *ColorImage_ptr; // colorImage->isDirty = true; - auto& current_layout = ColorImage.current_layout; + auto& current_layout = ColorImage.current_layouts[0]; auto new_layout = VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL; VkImageMemoryBarrier barrier{}; @@ -899,17 +906,18 @@ namespace dz { auto& ColorResolveImage = *ColorResolveImage_ptr; static auto new_layout = VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL; - if (ColorResolveImage.current_layout != new_layout) + auto& current_layout = ColorResolveImage.current_layouts[0]; + if (current_layout != new_layout) { VkImageMemoryBarrier colorBarrier = {}; VkPipelineStageFlags sourceStage; VkPipelineStageFlags destinationStage; prepareImageBarrier(framebuffer.commandBuffer, ColorResolveImage.image, ColorResolveImage.format, - ColorResolveImage.current_layout, new_layout, + current_layout, new_layout, VK_IMAGE_ASPECT_COLOR_BIT, sourceStage, destinationStage, colorBarrier); vkCmdPipelineBarrier(framebuffer.commandBuffer, sourceStage, destinationStage, 0, 0, nullptr, 0, nullptr, 1, &colorBarrier); - ColorResolveImage.current_layout = new_layout; + current_layout = new_layout; } } } @@ -922,7 +930,7 @@ namespace dz { auto& ColorResolveImage = *ColorResolveImage_ptr; // colorResolveImage->isDirty = true; - ColorResolveImage.current_layout = VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL; + ColorResolveImage.current_layouts[0] = VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL; } } @@ -934,17 +942,18 @@ namespace dz { auto& DepthResolveImage = *DepthResolveImage_ptr; static auto new_layout = VK_IMAGE_LAYOUT_DEPTH_STENCIL_ATTACHMENT_OPTIMAL; - if (DepthResolveImage.current_layout != new_layout) + auto& current_layout = DepthResolveImage.current_layouts[0]; + if (current_layout != new_layout) { VkImageMemoryBarrier depthBarrier = {}; VkPipelineStageFlags sourceStage; VkPipelineStageFlags destinationStage; prepareImageBarrier(framebuffer.commandBuffer, DepthResolveImage.image, DepthResolveImage.format, - DepthResolveImage.current_layout, new_layout, + current_layout, new_layout, VK_IMAGE_ASPECT_DEPTH_BIT, sourceStage, destinationStage, depthBarrier); vkCmdPipelineBarrier(framebuffer.commandBuffer, sourceStage, destinationStage, 0, 0, nullptr, 0, nullptr, 1, &depthBarrier); - DepthResolveImage.current_layout = new_layout; + current_layout = new_layout; } } } @@ -957,18 +966,19 @@ namespace dz { auto& DepthResolveImage = *DepthResolveImage_ptr; // depthResolveImage->isDirty = true; - DepthResolveImage.current_layout = VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL; + auto& current_layout = DepthResolveImage.current_layouts[0]; + current_layout = VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL; // Ensure the layout we are transitioning *from* is correct - if (DepthResolveImage.current_layout != VK_IMAGE_LAYOUT_DEPTH_STENCIL_ATTACHMENT_OPTIMAL && - DepthResolveImage.current_layout != VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL) + if (current_layout != VK_IMAGE_LAYOUT_DEPTH_STENCIL_ATTACHMENT_OPTIMAL && + current_layout != VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL) { // Log warning or throw? Indicates layout tracking might be off. // For now, let's assume it *should* be ATTACHMENT_OPTIMAL here. std::cerr << "Warning: Directional shadow map layout was not ATTACHMENT_OPTIMAL before transition!" << std::endl; } - VkImageLayout oldLayout = DepthResolveImage.current_layout; + VkImageLayout oldLayout = current_layout; VkImageLayout newLayout = VK_IMAGE_LAYOUT_DEPTH_STENCIL_READ_ONLY_OPTIMAL; VkImageMemoryBarrier barrier; @@ -986,7 +996,7 @@ namespace dz { vkCmdPipelineBarrier(framebuffer.commandBuffer, sourceStage, destinationStage, 0, 0, nullptr, 0, nullptr, 1, &barrier); // Update tracked layout - DepthResolveImage.current_layout = newLayout; + current_layout = newLayout; } } diff --git a/src/Image.cpp b/src/Image.cpp index a8023ff5..cd5fb8f4 100644 --- a/src/Image.cpp +++ b/src/Image.cpp @@ -8,7 +8,7 @@ namespace dz { auto& image = *image_ptr; image.width = width; image.height = height; - image.current_layout = VK_IMAGE_LAYOUT_UNDEFINED; + image.reset_layouts(); } void image_pre_resize_3D_internal(Image* image_ptr, uint32_t width, uint32_t height, uint32_t depth) { @@ -16,7 +16,7 @@ namespace dz { image.width = width; image.height = height; image.depth = depth; - image.current_layout = VK_IMAGE_LAYOUT_UNDEFINED; + image.reset_layouts(); } void image_init(Image* image); @@ -29,6 +29,10 @@ namespace dz { case VK_FORMAT_R8G8_UNORM: case VK_FORMAT_R8G8B8_UNORM: case VK_FORMAT_R8G8B8A8_UNORM: + case VK_FORMAT_R8_SRGB: + case VK_FORMAT_R8G8_SRGB: + case VK_FORMAT_R8G8B8_SRGB: + case VK_FORMAT_R8G8B8A8_SRGB: case VK_FORMAT_R32G32B32A32_SFLOAT: if (info.is_framebuffer_attachment) usage |= VK_IMAGE_USAGE_COLOR_ATTACHMENT_BIT; @@ -53,22 +57,13 @@ namespace dz { .tiling = info.tiling, .memory_properties = info.memory_properties, .multisampling = info.multisampling, - .data = info.data, - .surfaceType = info.surfaceType + .datas = info.datas, + .surfaceType = info.surfaceType, + .mip_levels = info.mip_levels }; return image_create_internal(internal_info); } - void image_cpy_data(Image* image, void* data) { - if (!data) - return; - auto channels = image_get_channels_size_of_t(image); - auto pixel_stride = image_get_sizeof_channels(channels); - auto image_byte_size = image->width * image->height * image->depth * pixel_stride; - image->data = std::shared_ptr(malloc(image_byte_size), free); - memcpy(image->data.get(), data, image_byte_size); - } - Image* image_create_internal(const ImageCreateInfoInternal& info) { Image* result = new Image{ .width = info.width, @@ -81,36 +76,17 @@ namespace dz { .tiling = info.tiling, .memory_properties = info.memory_properties, .multisampling = info.multisampling, - .surfaceType = info.surfaceType + .datas = info.datas, + .surfaceType = info.surfaceType, + .mip_levels = info.mip_levels }; - image_cpy_data(result, info.data); - image_init(result); return result; } - void image_free_internal(Image* image_ptr) { - auto& image = *image_ptr; - auto& device = dr.device; - if (image.image != VK_NULL_HANDLE) { - vkDestroyImage(device, image.image, 0); - image.image = nullptr; - } - if (image.imageView != VK_NULL_HANDLE) { - vkDestroyImageView(device, image.imageView, 0); - image.imageView = nullptr; - } - if (image.memory != VK_NULL_HANDLE) { - vkFreeMemory(device, image.memory, 0); - image.memory = nullptr; - } - if(image.sampler != VK_NULL_HANDLE) { - vkDestroySampler(device, image.sampler, 0); - image.sampler = nullptr; - } - } + void image_free_internal(Image* image_ptr); void image_resize_2D(Image*& image, uint32_t width, uint32_t height, void* data, bool create_new) { if (!image) @@ -127,7 +103,9 @@ namespace dz { .tiling = image->tiling, .memory_properties = image->memory_properties, .multisampling = image->multisampling, - .data = image->data + .datas = image->datas, + .surfaceType = image->surfaceType, + .mip_levels = image->mip_levels }; image_init(new_image); @@ -137,7 +115,6 @@ namespace dz { } image_free_internal(image); image_pre_resize_2D_internal(image, width, height); - image_cpy_data(image, data); image_init(image); } @@ -156,7 +133,9 @@ namespace dz { .tiling = image->tiling, .memory_properties = image->memory_properties, .multisampling = image->multisampling, - .data = image->data + .datas = image->datas, + .surfaceType = image->surfaceType, + .mip_levels = image->mip_levels }; image_init(new_image); @@ -166,12 +145,12 @@ namespace dz { } image_free_internal(image); image_pre_resize_3D_internal(image, width, height, depth); - image_cpy_data(image, data); image_init(image); } void image_init(Image* image_ptr) { auto& image = *image_ptr; + image.reset_layouts(); // VkImageCreateInfoInternal setup VkImageCreateInfo imageInfo{}; imageInfo.sType = VK_STRUCTURE_TYPE_IMAGE_CREATE_INFO; @@ -179,11 +158,11 @@ namespace dz { imageInfo.extent.width = image.width; imageInfo.extent.height = image.height; imageInfo.extent.depth = image.depth; - imageInfo.mipLevels = 1; + imageInfo.mipLevels = image.mip_levels; imageInfo.arrayLayers = 1; imageInfo.format = image.format; imageInfo.tiling = image.tiling; - imageInfo.initialLayout = image.current_layout; + imageInfo.initialLayout = VK_IMAGE_LAYOUT_UNDEFINED; imageInfo.usage = image.usage; imageInfo.samples = image.multisampling; imageInfo.sharingMode = VK_SHARING_MODE_EXCLUSIVE; @@ -202,28 +181,38 @@ namespace dz { vkAllocateMemory(dr.device, &allocInfo, nullptr, &image.memory); vkBindImageMemory(dr.device, image.image, image.memory, 0); - // Upload data if provided - if (!image.data) - { - init_empty_image_data(image_ptr); + image.datas.resize(image.mip_levels); + for (auto mip = 0; mip < image.mip_levels; mip++) { + // Upload data if provided + if (!image.datas[mip]) { + init_empty_image_data(image_ptr, mip); + } + else if (!image_ptr->data_is_cpu_side) { + image_ptr->data_is_cpu_side = true; + } + + image_upload_data(image_ptr, mip); } - upload_image_data(image_ptr); - // Create ImageView - VkImageViewCreateInfo viewInfo{}; - viewInfo.sType = VK_STRUCTURE_TYPE_IMAGE_VIEW_CREATE_INFO; - viewInfo.image = image.image; - viewInfo.viewType = image.view_type; - viewInfo.format = image.format; - viewInfo.subresourceRange.aspectMask = image_get_aspect_mask(image_ptr); - viewInfo.subresourceRange.baseMipLevel = 0; - viewInfo.subresourceRange.levelCount = 1; - viewInfo.subresourceRange.baseArrayLayer = 0; - viewInfo.subresourceRange.layerCount = 1; - - vkCreateImageView(dr.device, &viewInfo, nullptr, &image.imageView); + image.imageViews.resize(image.mip_levels); + for (auto mip = 0; mip < image.mip_levels; ++mip) { + VkImageViewCreateInfo mipViewInfo{ + .sType = VK_STRUCTURE_TYPE_IMAGE_VIEW_CREATE_INFO, + .image = image.image, + .viewType = image.view_type, + .format = image.format, + .subresourceRange = { + .aspectMask = image_get_aspect_mask(image_ptr), + .baseMipLevel = uint32_t(mip), + .levelCount = 1, + .baseArrayLayer = 0, + .layerCount = 1 + } + }; + vkCreateImageView(dr.device, &mipViewInfo, nullptr, &image.imageViews[mip]); + } // Conditionally create sampler if image will be sampled if (image.usage & VK_IMAGE_USAGE_SAMPLED_BIT) { @@ -243,19 +232,22 @@ namespace dz { samplerInfo.mipmapMode = VK_SAMPLER_MIPMAP_MODE_LINEAR; samplerInfo.mipLodBias = 0.0f; samplerInfo.minLod = 0.0f; - samplerInfo.maxLod = 0.0f; + samplerInfo.maxLod = float(image.mip_levels); vkCreateSampler(dr.device, &samplerInfo, nullptr, &image.sampler); } } - void init_empty_image_data(Image* image_ptr) { + void init_empty_image_data(Image* image_ptr, uint32_t mip) { auto& image = *image_ptr; auto channels = image_get_channels_size_of_t(image_ptr); auto pixel_stride = image_get_sizeof_channels(channels); - auto image_size = image.width * image.height * image.depth * pixel_stride; - image.data = std::shared_ptr((char*)malloc(image_size), free); - memset(image.data.get(), 0, image_size); + uint32_t mipWidth = (std::max)(1u, image.width >> mip); + uint32_t mipHeight = (std::max)(1u, image.height >> mip); + auto image_size = mipWidth * mipHeight * image.depth * pixel_stride; + auto& ptr = image.datas[mip]; + ptr = std::shared_ptr((char*)malloc(image_size), free); + memset(ptr.get(), 0, image_size); } uint32_t image_get_aspect_mask(Image* image_ptr) { @@ -264,6 +256,17 @@ namespace dz { case VK_FORMAT_R8G8_UNORM: case VK_FORMAT_R8G8B8_UNORM: case VK_FORMAT_R8G8B8A8_UNORM: + case VK_FORMAT_R8_SRGB: + case VK_FORMAT_R8G8_SRGB: + case VK_FORMAT_R8G8B8_SRGB: + case VK_FORMAT_R8G8B8A8_SRGB: + case VK_FORMAT_R16_SFLOAT: + case VK_FORMAT_R16G16_SFLOAT: + case VK_FORMAT_R16G16B16_SFLOAT: + case VK_FORMAT_R16G16B16A16_SFLOAT: + case VK_FORMAT_R32_SFLOAT: + case VK_FORMAT_R32G32_SFLOAT: + case VK_FORMAT_R32G32B32_SFLOAT: case VK_FORMAT_R32G32B32A32_SFLOAT: return VK_IMAGE_ASPECT_COLOR_BIT; break; @@ -282,12 +285,15 @@ namespace dz { return 0; } - void upload_image_data(Image* image_ptr) + void image_upload_data(Image* image_ptr, uint32_t mip, void* new_data) { auto& image = *image_ptr; auto channels = image_get_channels_size_of_t(image_ptr); auto pixel_stride = image_get_sizeof_channels(channels); - VkDeviceSize image_size = image.width * image.height * image.depth * pixel_stride; + auto image_mip_width = (std::max)(1u, uint32_t(image.width) >> mip); + auto image_mip_height = (std::max)(1u, uint32_t(image.height) >> mip); + auto image_mip_depth = (std::max)(1u, uint32_t(image.depth) >> mip); + VkDeviceSize image_size = image_mip_width * image_mip_height * image_mip_depth * pixel_stride; VkBuffer staging_buffer; VkDeviceMemory staging_buffer_memory; @@ -311,24 +317,31 @@ namespace dz { vkAllocateMemory(dr.device, &alloc_info, nullptr, &staging_buffer_memory); vkBindBufferMemory(dr.device, staging_buffer, staging_buffer_memory, 0); + auto& sh_ptr = image.datas[mip]; + auto ptr = sh_ptr.get(); + if (new_data) { + memcpy(new_data, ptr, image_size); + } + void* mapped_data; vkMapMemory(dr.device, staging_buffer_memory, 0, image_size, 0, &mapped_data); - memcpy(mapped_data, image.data.get(), static_cast(image_size)); + memcpy(mapped_data, ptr, static_cast(image_size)); vkUnmapMemory(dr.device, staging_buffer_memory); VkCommandBuffer command_buffer = begin_single_time_commands(); auto new_layout = VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL; + auto& current_layout = image.current_layouts[mip]; VkImageMemoryBarrier barrier_to_transfer{}; barrier_to_transfer.sType = VK_STRUCTURE_TYPE_IMAGE_MEMORY_BARRIER; - barrier_to_transfer.oldLayout = image.current_layout; + barrier_to_transfer.oldLayout = current_layout; barrier_to_transfer.newLayout = new_layout; barrier_to_transfer.srcQueueFamilyIndex = VK_QUEUE_FAMILY_IGNORED; barrier_to_transfer.dstQueueFamilyIndex = VK_QUEUE_FAMILY_IGNORED; barrier_to_transfer.image = image.image; barrier_to_transfer.subresourceRange.aspectMask = image_get_aspect_mask(image_ptr); - barrier_to_transfer.subresourceRange.baseMipLevel = 0; + barrier_to_transfer.subresourceRange.baseMipLevel = mip; barrier_to_transfer.subresourceRange.levelCount = 1; barrier_to_transfer.subresourceRange.baseArrayLayer = 0; barrier_to_transfer.subresourceRange.layerCount = 1; @@ -345,24 +358,24 @@ namespace dz { 1, &barrier_to_transfer ); - image.current_layout = new_layout; + current_layout = new_layout; VkBufferImageCopy region{}; region.bufferOffset = 0; region.bufferRowLength = 0; region.bufferImageHeight = 0; region.imageSubresource.aspectMask = image_get_aspect_mask(image_ptr); - region.imageSubresource.mipLevel = 0; + region.imageSubresource.mipLevel = mip; region.imageSubresource.baseArrayLayer = 0; region.imageSubresource.layerCount = 1; region.imageOffset = {0, 0, 0}; - region.imageExtent = {image.width, image.height, image.depth}; + region.imageExtent = {image_mip_width, image_mip_height, image_mip_depth}; vkCmdCopyBufferToImage(command_buffer, staging_buffer, image.image, VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL, 1, ®ion); VkImageMemoryBarrier barrier_to_shader{}; barrier_to_shader.sType = VK_STRUCTURE_TYPE_IMAGE_MEMORY_BARRIER; - barrier_to_shader.oldLayout = image.current_layout; + barrier_to_shader.oldLayout = current_layout; new_layout = VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL; barrier_to_shader.newLayout = new_layout; barrier_to_shader.srcQueueFamilyIndex = VK_QUEUE_FAMILY_IGNORED; @@ -382,42 +395,51 @@ namespace dz { 1, &barrier_to_shader ); - image.current_layout = new_layout; + current_layout = new_layout; end_single_time_commands(command_buffer); vkDestroyBuffer(dr.device, staging_buffer, nullptr); vkFreeMemory(dr.device, staging_buffer_memory, nullptr); - } - void image_free(Image* image) - { - if (!image) + image.data_is_gpu_side = true; + } + + void image_free_internal(Image* image_ptr) { + if (!image_ptr) return; + auto& image = *image_ptr; auto& device = dr.device; if (device == VK_NULL_HANDLE) return; - if (image->image != VK_NULL_HANDLE) { - vkDestroyImage(device, image->image, 0); - image->image = nullptr; + if (image.image != VK_NULL_HANDLE) { + vkDestroyImage(device, image.image, 0); + image.image = nullptr; } - if (image->imageView != VK_NULL_HANDLE) { - vkDestroyImageView(device, image->imageView, 0); - image->imageView = nullptr; + for (auto& imageView : image.imageViews) { + if (!imageView) + continue; + vkDestroyImageView(device, imageView, 0); + imageView = nullptr; } - if (image->memory != VK_NULL_HANDLE) { - vkFreeMemory(device, image->memory, 0); - image->memory = nullptr; + if (image.memory != VK_NULL_HANDLE) { + vkFreeMemory(device, image.memory, 0); + image.memory = nullptr; } - if(image->sampler != VK_NULL_HANDLE) { - vkDestroySampler(device, image->sampler, 0); - image->sampler = nullptr; + if(image.sampler != VK_NULL_HANDLE) { + vkDestroySampler(device, image.sampler, 0); + image.sampler = nullptr; } - delete image; + } + + void image_free(Image* image_ptr) + { + image_free_internal(image_ptr); + delete image_ptr; return; } - std::pair image_create_descriptor_set(Image* image) { + std::pair image_create_descriptor_set(Image* image, uint32_t mip_level) { assert(image && "Image* is null"); VkDescriptorSetLayoutBinding binding = {}; @@ -444,7 +466,7 @@ namespace dz { vkAllocateDescriptorSets(dr.device, &allocInfo, &descriptorSet); VkDescriptorImageInfo imageInfo = {}; - imageInfo.imageView = image->imageView; + imageInfo.imageView = image->imageViews[mip_level]; imageInfo.sampler = image->sampler; imageInfo.imageLayout = VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL; @@ -625,10 +647,6 @@ namespace dz { return vec; } - void image_upload_data(Image* image, void* data) { - - } - size_t image_get_sizeof_channels(const std::vector& channels) { float size = 0; for (auto& c : channels) @@ -639,4 +657,170 @@ namespace dz { SurfaceType image_get_surface_type(Image* image_ptr) { return image_ptr->surfaceType; } + + uint32_t image_get_width(Image* image_ptr) { + return image_ptr->width; + } + + uint32_t image_get_height(Image* image_ptr) { + return image_ptr->height; + } + + uint32_t image_get_depth(Image* image_ptr) { + return image_ptr->depth; + } + + VkImageLayout image_get_layout(Image* image_ptr, int mip) { + return image_ptr->current_layouts[mip]; + } + + VkFormat image_get_format(Image* image_ptr) { + return image_ptr->format; + } + + size_t get_format_pixel_size(VkFormat format) { + switch (format) + { + case VK_FORMAT_R8_UNORM: + case VK_FORMAT_R8_SRGB: + case VK_FORMAT_R8_UINT: + case VK_FORMAT_R8_SNORM: + case VK_FORMAT_R8_SINT: + case VK_FORMAT_S8_UINT: + return 1; + + case VK_FORMAT_R8G8_UNORM: + case VK_FORMAT_R8G8_SRGB: + case VK_FORMAT_R8G8_UINT: + case VK_FORMAT_R8G8_SNORM: + case VK_FORMAT_R8G8_SINT: + return 2; + + case VK_FORMAT_R8G8B8_UNORM: + case VK_FORMAT_R8G8B8_SRGB: + case VK_FORMAT_R8G8B8_UINT: + case VK_FORMAT_R8G8B8_SNORM: + case VK_FORMAT_R8G8B8_SINT: + return 3; + + case VK_FORMAT_R8G8B8A8_UNORM: + case VK_FORMAT_R8G8B8A8_UINT: + case VK_FORMAT_R8G8B8A8_SNORM: + case VK_FORMAT_R8G8B8A8_SINT: + case VK_FORMAT_B8G8R8A8_UNORM: + case VK_FORMAT_B8G8R8A8_SRGB: + case VK_FORMAT_R8G8B8A8_SRGB: + return 4; + + case VK_FORMAT_R16G16B16A16_UINT: + case VK_FORMAT_R16G16B16A16_SINT: + case VK_FORMAT_R16G16B16A16_SFLOAT: + return 8; + + case VK_FORMAT_R32G32B32A32_UINT: + case VK_FORMAT_R32G32B32A32_SINT: + case VK_FORMAT_R32G32B32A32_SFLOAT: + return 16; + + case VK_FORMAT_D32_SFLOAT: + return 4; + + case VK_FORMAT_D32_SFLOAT_S8_UINT: + return 5; + + case VK_FORMAT_R32_UINT: + return 4; + + case VK_FORMAT_R5G6B5_UNORM_PACK16: + case VK_FORMAT_R5G5B5A1_UNORM_PACK16: + return 2; + + default: + throw std::runtime_error("Unsupported VkFormat"); // fallback + } + } + + void image_copy_begin() { + static VkCommandBufferBeginInfo beginInfo{ + .sType = VK_STRUCTURE_TYPE_COMMAND_BUFFER_BEGIN_INFO, + .flags = VK_COMMAND_BUFFER_USAGE_ONE_TIME_SUBMIT_BIT + }; + + vkBeginCommandBuffer(dr.copyCommandBuffer, &beginInfo); + + dr.copyRegions.clear(); + dr.copySrcImages.clear(); + dr.copyDstImages.clear(); + } + + void image_copy_reserve_regions(uint32_t count) { + dr.copyRegions.reserve(count); + dr.copySrcImages.reserve(count); + dr.copyDstImages.reserve(count); + } + + void image_copy_image(Image* dstImage, Image* srcImage, VkImageCopy region) { + auto index = dr.copyRegions.size(); + dr.copyRegions.push_back(region); + auto dst_mip = region.dstSubresource.mipLevel; + auto src_mip = region.srcSubresource.mipLevel; + auto& dst_current_layout = dstImage->current_layouts[dst_mip]; + auto& src_current_layout = srcImage->current_layouts[src_mip]; + auto copy_dst_it = std::find_if(dr.copyDstImages.begin(), dr.copyDstImages.end(), [&](auto& tuple) { + auto& image_ptr = std::get<0>(tuple); + auto mip = std::get<2>(tuple); + return image_ptr == dstImage && mip == dst_mip; + }); + auto copy_src_it = std::find_if(dr.copySrcImages.begin(), dr.copySrcImages.end(), [&](auto& tuple) { + auto& image_ptr = std::get<0>(tuple); + auto mip = std::get<2>(tuple); + return image_ptr == srcImage && mip == src_mip; + }); + auto dst_original_layout = copy_dst_it != dr.copyDstImages.end() ? std::get<1>(*copy_dst_it) : dst_current_layout; + auto src_original_layout = copy_src_it != dr.copySrcImages.end() ? std::get<1>(*copy_src_it) : src_current_layout; + dr.copyDstImages.push_back({dstImage, dst_original_layout, dst_mip}); + dr.copySrcImages.push_back({srcImage, src_original_layout, src_mip}); + static auto src_new_layout = VK_IMAGE_LAYOUT_TRANSFER_SRC_OPTIMAL; + static auto dst_new_layout = VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL; + if (dst_current_layout != dst_new_layout) + transition_image_layout(dstImage, dst_new_layout, dst_mip); + if (src_current_layout != src_new_layout) + transition_image_layout(srcImage, src_new_layout, src_mip); + vkCmdCopyImage( + dr.copyCommandBuffer, + srcImage->image, + src_new_layout, + dstImage->image, + dst_new_layout, + 1, + dr.copyRegions.data() + index + ); + } + + void image_copy_end() { + vkEndCommandBuffer(dr.copyCommandBuffer); + + static VkSubmitInfo submitInfo{ + .sType = VK_STRUCTURE_TYPE_SUBMIT_INFO, + .commandBufferCount = 1 + }; + submitInfo.pCommandBuffers = &dr.copyCommandBuffer; + + vkQueueSubmit(dr.copyQueue, 1, &submitInfo, VK_NULL_HANDLE); + vkQueueWaitIdle(dr.copyQueue); + + for (auto& [image_ptr, original_layout, original_mip] : dr.copySrcImages) { + if (image_ptr->current_layouts[original_mip] != original_layout) + transition_image_layout(image_ptr, original_layout, original_mip); + } + + for (auto& [image_ptr, original_layout, original_mip] : dr.copyDstImages) { + if (image_ptr->current_layouts[original_mip] != original_layout) + transition_image_layout(image_ptr, original_layout, original_mip); + } + + dr.copyRegions.clear(); + dr.copySrcImages.clear(); + dr.copyDstImages.clear(); + } } \ No newline at end of file diff --git a/src/Image.cpp.hpp b/src/Image.cpp.hpp index 3e565536..6aa38e4c 100644 --- a/src/Image.cpp.hpp +++ b/src/Image.cpp.hpp @@ -14,14 +14,24 @@ namespace dz { VkImageTiling tiling; VkMemoryPropertyFlags memory_properties; VkImage image = VK_NULL_HANDLE; - VkImageView imageView = VK_NULL_HANDLE; + // VkImageView imageView = VK_NULL_HANDLE; VkBuffer buffer = VK_NULL_HANDLE; VkDeviceMemory memory = VK_NULL_HANDLE; VkSampler sampler = VK_NULL_HANDLE; - VkImageLayout current_layout = VK_IMAGE_LAYOUT_UNDEFINED; + std::vector current_layouts; VkSampleCountFlagBits multisampling; - std::shared_ptr data; + std::vector> datas; SurfaceType surfaceType = SurfaceType::BaseColor; + uint32_t mip_levels = 1; + std::vector imageViews; + bool data_is_cpu_side = false; + bool data_is_gpu_side = false; + bool data_synced_gpu_to_cpu = false; + void reset_layouts() { + current_layouts.resize(mip_levels); + for (auto& current_layout : current_layouts) + current_layout = VK_IMAGE_LAYOUT_UNDEFINED; + } }; struct ImageCreateInfoInternal @@ -29,19 +39,19 @@ namespace dz { uint32_t width = 1; uint32_t height = 1; uint32_t depth = 1; - VkFormat format = VK_FORMAT_R8G8B8A8_UNORM; + VkFormat format = (VkFormat)ColorSpace::SRGB; VkImageUsageFlags usage; VkImageType image_type = VK_IMAGE_TYPE_2D; VkImageViewType view_type = VK_IMAGE_VIEW_TYPE_2D; VkImageTiling tiling = VK_IMAGE_TILING_OPTIMAL; VkMemoryPropertyFlags memory_properties = VK_MEMORY_PROPERTY_DEVICE_LOCAL_BIT; VkSampleCountFlagBits multisampling = VK_SAMPLE_COUNT_1_BIT; - void* data = nullptr; + std::vector> datas; SurfaceType surfaceType = SurfaceType::BaseColor; + uint32_t mip_levels = 1; }; - void upload_image_data(Image*); - void init_empty_image_data(Image*); + void init_empty_image_data(Image*, uint32_t mip = 0); uint32_t image_get_aspect_mask(Image*); Image* image_create_internal(const ImageCreateInfoInternal& info); diff --git a/src/ImagePack.cpp b/src/ImagePack.cpp index a53f4495..a6110ecb 100644 --- a/src/ImagePack.cpp +++ b/src/ImagePack.cpp @@ -12,6 +12,8 @@ #include #include +static constexpr auto PADDING = 0; // PADDING in pixels around each image + // using Format = zg::images::Image::Format; bool dz::ImagePack::is_dirty() { @@ -31,8 +33,6 @@ bool dz::ImagePack::is_dirty() } void dz::ImagePack::repack() { - - constexpr int padding = 0; // padding in pixels around each image size_t image_vec_size = image_vec.size(); rect_vec.resize(image_vec_size); auto image_vec_data = image_vec.data(); @@ -44,8 +44,8 @@ void dz::ImagePack::repack() rect_type rect; rect.x = 0; rect.y = 0; - rect.h = image_ptr->height + 2 * padding; // <--- add vertical padding - rect.w = image_ptr->width + 2 * padding; // <--- add horizontal padding + rect.h = image_ptr->height + 2 * PADDING; // <--- add vertical PADDING + rect.w = image_ptr->width + 2 * PADDING; // <--- add horizontal PADDING return rect; }); @@ -62,158 +62,66 @@ void dz::ImagePack::repack() int atlas_width = result_size.w; int atlas_height = result_size.h; - size_t byte_size = atlas_width * atlas_height * 4; - if (rgba_buffer.size() < byte_size) - rgba_buffer.resize(byte_size); - uint8_t* rgba = rgba_buffer.data(); - memset(rgba, 0, byte_size); + size_t pixel_size = get_format_pixel_size(atlas_format); + + uint32_t atlas_mip_levels = 0; for (size_t index = 0; index < image_vec_size; ++index) { auto& image_ptr = image_vec_data[index]; auto& image = *image_ptr; - auto channels = image_get_channels_size_of_t(image_ptr); - auto sizeof_channels = image_get_sizeof_channels(channels); - auto format = image.format; - auto image_data = (char*)image.data.get(); - auto& rect = rect_vec_data[index]; - - if (image.width != rect.w - 2 * padding || image.height != rect.h - 2 * padding) - { + + if (atlas_mip_levels == 0) { + atlas_mip_levels = image.mip_levels; + } + else if (atlas_mip_levels != image.mip_levels) { + if (enforce_same_miplvl) { + throw std::runtime_error("Atlas Pack: Image index [" + std::to_string(index) + "] does not match atlas mip_levels, failing"); + } continue; } + } - for (int y = 0; y < image.height; ++y) - { - auto dst_row = &rgba[((rect.y + padding + y) * atlas_width + (rect.x + padding)) * 4]; - const auto src_row = &image_data[(y * image.width) * sizeof_channels]; - - for (int x = 0; x < image.width; ++x) - { - const auto src_pixel = src_row + x * sizeof_channels; - auto dst_pixel = dst_row + x * 4; + atlas_buffer_sizes.resize(atlas_mip_levels); + atlas_buffers.resize(atlas_mip_levels); - switch (format) - { - case VK_FORMAT_R8_UNORM: - case VK_FORMAT_R8_UINT: - case VK_FORMAT_R8_SNORM: - case VK_FORMAT_R8_SINT: - case VK_FORMAT_S8_UINT: - dst_pixel[0] = ((const uint8_t*)src_pixel)[0]; - dst_pixel[1] = dst_pixel[2] = 0; - dst_pixel[3] = 255; - break; - - case VK_FORMAT_R8G8_UNORM: - case VK_FORMAT_R8G8_UINT: - case VK_FORMAT_R8G8_SNORM: - case VK_FORMAT_R8G8_SINT: - dst_pixel[0] = ((const uint8_t*)src_pixel)[0]; - dst_pixel[1] = ((const uint8_t*)src_pixel)[1]; - dst_pixel[2] = 0; - dst_pixel[3] = 255; - break; - - case VK_FORMAT_R8G8B8_UNORM: - case VK_FORMAT_R8G8B8_UINT: - case VK_FORMAT_R8G8B8_SNORM: - case VK_FORMAT_R8G8B8_SINT: - dst_pixel[0] = ((const uint8_t*)src_pixel)[0]; - dst_pixel[1] = ((const uint8_t*)src_pixel)[1]; - dst_pixel[2] = ((const uint8_t*)src_pixel)[2]; - dst_pixel[3] = 255; - break; - - case VK_FORMAT_R8G8B8A8_UNORM: - case VK_FORMAT_R8G8B8A8_UINT: - case VK_FORMAT_R8G8B8A8_SNORM: - case VK_FORMAT_R8G8B8A8_SINT: - case VK_FORMAT_B8G8R8A8_UNORM: - case VK_FORMAT_B8G8R8A8_SRGB: - std::memcpy(dst_pixel, src_pixel, 4); - break; - - case VK_FORMAT_R32G32B32A32_SFLOAT: - { - const float* fp = (const float*)src_pixel; - dst_pixel[0] = static_cast(std::clamp(fp[0], 0.0f, 1.0f) * 255.0f); - dst_pixel[1] = static_cast(std::clamp(fp[1], 0.0f, 1.0f) * 255.0f); - dst_pixel[2] = static_cast(std::clamp(fp[2], 0.0f, 1.0f) * 255.0f); - dst_pixel[3] = static_cast(std::clamp(fp[3], 0.0f, 1.0f) * 255.0f); - break; - } - - case VK_FORMAT_D32_SFLOAT: - { - float d = *(const float*)src_pixel; - uint8_t gray = static_cast(std::clamp(d, 0.0f, 1.0f) * 255.0f); - dst_pixel[0] = dst_pixel[1] = dst_pixel[2] = gray; - dst_pixel[3] = 255; - break; - } - - case VK_FORMAT_D32_SFLOAT_S8_UINT: - { - float depth = *(const float*)src_pixel; - const uint8_t* s8 = (const uint8_t*)(src_pixel + sizeof(float)); - dst_pixel[0] = *s8; - dst_pixel[1] = dst_pixel[2] = static_cast(std::clamp(depth, 0.0f, 1.0f) * 255.0f); - dst_pixel[3] = 255; - break; - } - - case VK_FORMAT_R32_UINT: - { - uint32_t val = *(const uint32_t*)src_pixel; - uint8_t clamped = static_cast(std::clamp(val, 0, 255)); - dst_pixel[0] = dst_pixel[1] = dst_pixel[2] = clamped; - dst_pixel[3] = 255; - break; - } - - case VK_FORMAT_R5G6B5_UNORM_PACK16: - { - uint16_t p = *(const uint16_t*)src_pixel; - dst_pixel[0] = ((p >> 11) & 0x1F) << 3; - dst_pixel[1] = ((p >> 5) & 0x3F) << 2; - dst_pixel[2] = (p & 0x1F) << 3; - dst_pixel[3] = 255; - break; - } - - case VK_FORMAT_R5G5B5A1_UNORM_PACK16: - { - uint16_t p = *(const uint16_t*)src_pixel; - dst_pixel[0] = ((p >> 10) & 0x1F) << 3; - dst_pixel[1] = ((p >> 5) & 0x1F) << 3; - dst_pixel[2] = (p & 0x1F) << 3; - dst_pixel[3] = (p & 0x8000) ? 255 : 0; - break; - } - - default: - dst_pixel[0] = dst_pixel[1] = dst_pixel[2] = 0; - dst_pixel[3] = 255; - break; - } + for (auto mip = 0; mip < atlas_mip_levels; mip++) { + auto mipWidth = (std::max)(1, atlas_width >> mip); + auto mipHeight = (std::max)(1, atlas_height >> mip); + size_t byte_size = mipWidth * mipHeight * pixel_size; + auto& atlas_buffer_size = atlas_buffer_sizes[mip]; + auto& atlas_buffer = atlas_buffers[mip]; + if (atlas_buffer_size != byte_size) { + auto new_buffer = std::shared_ptr(malloc(byte_size), free); + if (atlas_buffer) { + memcpy(new_buffer.get(), atlas_buffer.get(), (std::min)(atlas_buffer_size, byte_size)); } + atlas_buffer = new_buffer; + atlas_buffer_size = byte_size; } + memset(atlas_buffer.get(), 0, atlas_buffer_size); } + CPU_Image_Copy(atlas_width, atlas_height, pixel_size, atlas_mip_levels); + if (atlas && atlas->width == atlas_width && atlas->height == atlas_height) { - image_upload_data(atlas, rgba); + for (auto mip = 0; mip < atlas_mip_levels; ++mip) + image_upload_data(atlas, mip, atlas_buffers[mip].get()); } else { atlas = image_create({ .width = uint32_t(atlas_width), .height = uint32_t(atlas_height), - .data = rgba + .format = atlas_format, + .datas = atlas_buffers, + .mip_levels = atlas_mip_levels }); owns_atlas = true; } + + GPU_Image_Copy(atlas_width, atlas_height, pixel_size, atlas_mip_levels); } else if (image_vec_size) { @@ -221,6 +129,134 @@ void dz::ImagePack::repack() owns_atlas = false; } } + +void ImagePack::CPU_Image_Copy(int atlas_width, int atlas_height, size_t pixel_size, uint32_t atlas_mip_levels) { + auto image_vec_size = image_vec.size(); + auto image_vec_data = image_vec.data(); + auto rect_vec_data = rect_vec.data(); + + for (size_t index = 0; index < image_vec_size; ++index) { + auto& image_ptr = image_vec_data[index]; + auto& image = *image_ptr; + auto channels = image_get_channels_size_of_t(image_ptr); + auto sizeof_channels = image_get_sizeof_channels(channels); + auto format = image.format; + + if (enforce_same_format && format != atlas_format) { + std::cout << "Atlas Pack: Image index [" << index << "] is not the same format as atlas_format, skipping" << std::endl; + continue; + } + + if (image.data_is_cpu_side && !image.data_is_gpu_side) { + for (auto mip = 0; mip < atlas_mip_levels; mip++) { + auto image_data = (unsigned char*)image.datas[mip].get(); + auto atlas_data = (unsigned char*)atlas_buffers[mip].get(); + auto& rect = rect_vec_data[index]; + + auto image_mip_width = (std::max)(1, int(image.width) >> mip); + auto image_mip_height = (std::max)(1, int(image.height) >> mip); + auto atlas_mip_width = (std::max)(1, atlas_width >> mip); + auto rect_mip_w = (std::max)(1, rect.w >> mip); + auto rect_mip_h = (std::max)(1, rect.h >> mip); + auto rect_mip_y = (std::max)(1, rect.y >> mip); + auto rect_mip_x = (std::max)(1, rect.x >> mip); + + if (image_mip_width != rect_mip_w - 2 * PADDING || image_mip_height != rect.h - 2 * PADDING) + { + continue; + } + + for (int y = 0; y < image_mip_height; ++y) + { + uint8_t* dst_row = &atlas_data[((rect_mip_y + PADDING + y) * atlas_mip_width + (rect_mip_x + PADDING)) * pixel_size]; + const uint8_t* src_row = reinterpret_cast(&image_data[(y * image_mip_width) * sizeof_channels]); + + for (int x = 0; x < image_mip_width; ++x) + { + const void* src_pixel = &src_row[x * sizeof_channels]; + void* dst_pixel = &dst_row[x * pixel_size]; + + convert_pixel(format, atlas_format, src_pixel, dst_pixel); + } + } + } + } + + } +} + +void ImagePack::GPU_Image_Copy(int atlas_width, int atlas_height, size_t pixel_size, uint32_t atlas_mip_levels) { + auto image_vec_size = image_vec.size(); + auto image_vec_data = image_vec.data(); + auto rect_vec_data = rect_vec.data(); + + image_copy_begin(); + + auto region_count = 0; + for (size_t index = 0; index < image_vec_size; ++index) { + auto& image_ptr = image_vec_data[index]; + auto& image = *image_ptr; + + if (image.data_is_gpu_side) + region_count += atlas_mip_levels; + } + + image_copy_reserve_regions(region_count); + + auto region_index = 0; + for (size_t index = 0; index < image_vec_size; ++index) { + auto& image_ptr = image_vec_data[index]; + auto& image = *image_ptr; + + auto format = image.format; + + if (enforce_same_format && format != atlas_format) { + std::cout << "Atlas Pack: Image index [" << index << "] is not the same format as atlas_format, skipping" << std::endl; + continue; + } + + if (image.data_is_gpu_side) { + for (auto mip = 0; mip < atlas_mip_levels; mip++) { + auto& rect = rect_vec_data[index]; + + auto image_mip_width = (std::max)(1u, image.width >> mip); + auto image_mip_height = (std::max)(1u, image.height >> mip); + auto image_mip_depth = (std::max)(1u, image.depth >> mip); + auto atlas_mip_width = (std::max)(1, atlas_width >> mip); + auto rect_mip_w = (std::max)(1, rect.w >> mip); + auto rect_mip_h = (std::max)(1, rect.h >> mip); + auto rect_mip_y = (std::max)(0, rect.y >> mip); + auto rect_mip_x = (std::max)(0, rect.x >> mip); + + if (image_mip_width != rect_mip_w - 2 * PADDING || image_mip_height != rect_mip_h - 2 * PADDING) + { + continue; + } + + VkImageCopy region{}; + region.srcSubresource.aspectMask = VK_IMAGE_ASPECT_COLOR_BIT; + region.srcSubresource.mipLevel = mip; + region.srcSubresource.baseArrayLayer = 0; + region.srcSubresource.layerCount = 1; + region.srcOffset = { 0, 0, 0 }; + + region.dstSubresource = region.srcSubresource; + region.dstSubresource.mipLevel = mip; + region.dstOffset = { rect_mip_x + PADDING, rect_mip_y + PADDING, 0 }; + region.extent.width = image_mip_width; + region.extent.height = image_mip_height; + region.extent.depth = image_mip_depth; + + image_copy_image(atlas, image_ptr, region); + region_index++; + } + } + + } + + image_copy_end(); +} + size_t dz::ImagePack::findImageIndex(Image* image) { size_t index = 0; @@ -243,6 +279,9 @@ ImagePack::~ImagePack() { void ImagePack::SetOwnAtlas(bool owns) { owns_atlas = owns; } +void ImagePack::SetAtlasFormat(VkFormat new_format) { + atlas_format = new_format; +} void dz::ImagePack::addImage(Image* image) { try @@ -305,3 +344,93 @@ dz::ImagePack::rect_type& dz::ImagePack::findPackedRect(Image* image) } size_t dz::ImagePack::size() const { return image_vec.size(); } bool dz::ImagePack::empty() const { return image_vec.empty(); } + + +void ImagePack::copy_bytes(const void* src, void* dst, size_t size) +{ + std::memcpy(dst, src, size); +} + +uint8_t ImagePack::float_to_u8(float val) +{ + return static_cast(std::clamp(val, 0.0f, 1.0f) * 255.0f); +} + +float ImagePack::u8_to_float(uint8_t val) +{ + return static_cast(val) / 255.0f; +} + +void ImagePack::convert_R32G32B32A32_SFLOAT_to_R8G8B8A8_UNORM(const void* src, void* dst) +{ + const float* f = reinterpret_cast(src); + uint8_t* d = reinterpret_cast(dst); + d[0] = float_to_u8(f[0]); + d[1] = float_to_u8(f[1]); + d[2] = float_to_u8(f[2]); + d[3] = float_to_u8(f[3]); +} + +void ImagePack::convert_R8G8B8A8_UNORM_to_R32G32B32A32_SFLOAT(const void* src, void* dst) +{ + const uint8_t* s = reinterpret_cast(src); + float* f = reinterpret_cast(dst); + f[0] = u8_to_float(s[0]); + f[1] = u8_to_float(s[1]); + f[2] = u8_to_float(s[2]); + f[3] = u8_to_float(s[3]); +} + +int ImagePack::format_index(VkFormat fmt) +{ + for (int i = 0; i < FMT_COUNT; ++i) + if (supported_formats[i] == fmt) + return i; + return -1; +} + +void ImagePack::init_conversion_matrix() +{ + static bool initialized = false; + if (initialized) return; + initialized = true; + + // Set format sizes + format_sizes[format_index(VK_FORMAT_R8_UNORM)] = 1; + format_sizes[format_index(VK_FORMAT_R8G8_UNORM)] = 2; + format_sizes[format_index(VK_FORMAT_R8G8B8_UNORM)] = 3; + format_sizes[format_index(VK_FORMAT_R8G8B8A8_UNORM)] = 4; + format_sizes[format_index(VK_FORMAT_R32_SFLOAT)] = 4; + format_sizes[format_index(VK_FORMAT_R32G32_SFLOAT)] = 8; + format_sizes[format_index(VK_FORMAT_R32G32B32_SFLOAT)] = 12; + format_sizes[format_index(VK_FORMAT_R32G32B32A32_SFLOAT)] = 16; + + // Identity copies + for (int i = 0; i < FMT_COUNT; ++i) + conversion_matrix[i][i] = [=](const void* src, void* dst) { copy_bytes(src, dst, format_sizes[i]); }; + + // Specific conversions + int r8g8b8a8_idx = format_index(VK_FORMAT_R8G8B8A8_UNORM); + int r32g32b32a32_idx = format_index(VK_FORMAT_R32G32B32A32_SFLOAT); + + conversion_matrix[r32g32b32a32_idx][r8g8b8a8_idx] = convert_R32G32B32A32_SFLOAT_to_R8G8B8A8_UNORM; + conversion_matrix[r8g8b8a8_idx][r32g32b32a32_idx] = convert_R8G8B8A8_UNORM_to_R32G32B32A32_SFLOAT; +} + +void ImagePack::convert_pixel(VkFormat src_format, VkFormat dst_format, const void* src, void* dst) +{ + init_conversion_matrix(); + + int src_idx = format_index(src_format); + int dst_idx = format_index(dst_format); + + if (src_idx == -1 || dst_idx == -1) + throw std::runtime_error("Unsupported format in convert_pixel"); + + ConvertFunc func = conversion_matrix[src_idx][dst_idx]; + + if (!func) + throw std::runtime_error("Conversion path not implemented between formats"); + + func(src, dst); +} \ No newline at end of file diff --git a/src/Loaders/Assimp_Loader.cpp b/src/Loaders/Assimp_Loader.cpp index c0da03fe..3e84ca92 100644 --- a/src/Loaders/Assimp_Loader.cpp +++ b/src/Loaders/Assimp_Loader.cpp @@ -228,6 +228,7 @@ namespace dz::loaders::assimp_loader { images_vec.push_back(image_ptr); } } + float metalness = 0, roughness = 0; // Load Albedo Color { aiColor4D aicolor; @@ -235,8 +236,10 @@ namespace dz::loaders::assimp_loader { { albedo_color = AssimpConvert>(aicolor); } + if (AI_SUCCESS == aiGetMaterialFloat(material, AI_MATKEY_METALLIC_FACTOR, &metalness)) { } + if (AI_SUCCESS == aiGetMaterialFloat(material, AI_MATKEY_ROUGHNESS_FACTOR, &roughness)) { } } - material_pair = info.add_material_function(material_name, images_vec); + material_pair = info.add_material_function(material_name, images_vec, albedo_color, metalness, roughness); } // vertex data std::vector positions; diff --git a/src/Loaders/STB_Image_Loader.cpp b/src/Loaders/STB_Image_Loader.cpp index 44873663..5bb5a33a 100644 --- a/src/Loaders/STB_Image_Loader.cpp +++ b/src/Loaders/STB_Image_Loader.cpp @@ -5,68 +5,105 @@ #include #include "../Directz.cpp.hpp" -int STB_Image_minChannels() { +int STB_Image_minChannelsu() { int minChannels = 4; - if (dr.formats_supported.R8G8B8A8_UNORM && dr.formats_supported.R8G8B8_UNORM && - dr.formats_supported.R8G8_UNORM && dr.formats_supported.R8_UNORM) { + if (dr.formats_supported.R8G8B8A8_SRGB && dr.formats_supported.R8G8B8_SRGB && + dr.formats_supported.R8G8_SRGB && dr.formats_supported.R8_SRGB) { minChannels = 0; } return minChannels; } -dz::Image* STB_Image_load_image(const uint8_t* ptr, int width, int height, int nrChannels) { +int STB_Image_minChannelsf() { + int minChannels = 4; + if (dr.formats_supported.R32G32B32A32_SFLOAT && dr.formats_supported.R32G32B32_SFLOAT && + dr.formats_supported.R32G32_SFLOAT && dr.formats_supported.R32_SFLOAT) { + minChannels = 0; + } + return minChannels; +} + +dz::Image* STB_Image_load_image_uf(const std::vector>& datas, int width, int height, int nrChannels, bool load_float) { VkFormat format; switch (nrChannels) { case 1: - format = VK_FORMAT_R8_UNORM; + format = (load_float ? VK_FORMAT_R32_SFLOAT : VK_FORMAT_R8_SRGB); break; case 2: - format = VK_FORMAT_R8G8_UNORM; + format = (load_float ? VK_FORMAT_R32G32_SFLOAT : VK_FORMAT_R8G8_SRGB); break; case 3: - format = VK_FORMAT_R8G8B8_UNORM; + format = (load_float ? VK_FORMAT_R32G32B32_SFLOAT : VK_FORMAT_R8G8B8_SRGB); break; case 4: - format = VK_FORMAT_R8G8B8A8_UNORM; + format = (load_float ? VK_FORMAT_R32G32B32A32_SFLOAT : VK_FORMAT_R8G8B8A8_SRGB); break; } dz::ImageCreateInfo info{ .width = (uint32_t)width, .height = (uint32_t)height, .format = format, - .data = (void*)ptr + .datas = datas }; return dz::image_create(info); } -dz::Image* STB_Image_load_path(const std::filesystem::path& path) { - auto minChannels = STB_Image_minChannels(); - int nrChannels; - int width = 0, height = 0; +dz::Image* STB_Image_load_pathu(const std::filesystem::path& path) { + auto minChannels = STB_Image_minChannelsu(); + int nrChannels = 0, width = 0, height = 0; std::string path_string = path.string(); uint8_t *imageData = stbi_load( path_string.c_str(), &width, &height, &nrChannels, (std::max)(nrChannels, minChannels)); if (!imageData) throw std::runtime_error("Failed to load image from memory."); - return STB_Image_load_image(imageData, width, height, (std::max)(nrChannels, minChannels)); + return STB_Image_load_image_uf({ + {(void*)imageData, [](auto ptr) { stbi_image_free(ptr); }} + }, width, height, (std::max)(nrChannels, minChannels), false); } -dz::Image* STB_Image_load_bytes(const std::shared_ptr& bytes, size_t bytes_length) { - auto minChannels = STB_Image_minChannels(); - int nrChannels; - int width = 0, height = 0; +dz::Image* STB_Image_load_bytesu(const std::shared_ptr& bytes, size_t bytes_length) { + auto minChannels = STB_Image_minChannelsu(); + int nrChannels = 0, width = 0, height = 0; uint8_t *imageData = stbi_load_from_memory( (stbi_uc const *)bytes.get(), bytes_length, &width, &height, &nrChannels, minChannels); if (!imageData) throw std::runtime_error("Failed to load image from memory."); - return STB_Image_load_image(imageData, width, height, (std::max)(nrChannels, minChannels)); + return STB_Image_load_image_uf({ + {(void*)imageData, [](auto ptr) { stbi_image_free(ptr); }} + }, width, height, (std::max)(nrChannels, minChannels), false); +} + +dz::Image* STB_Image_load_pathf(const std::filesystem::path& path) { + auto minChannels = STB_Image_minChannelsf(); + int nrChannels = 0, width = 0, height = 0; + std::string path_string = path.string(); + float *imageData = stbi_loadf( + path_string.c_str(), &width, &height, &nrChannels, (std::max)(nrChannels, minChannels)); + if (!imageData) + throw std::runtime_error("Failed to load image from memory."); + return STB_Image_load_image_uf({ + {(void*)imageData, [](auto ptr) { stbi_image_free(ptr); }} + }, width, height, (std::max)(nrChannels, minChannels), true); +} + +dz::Image* STB_Image_load_bytesf(const std::shared_ptr& bytes, size_t bytes_length) { + auto minChannels = STB_Image_minChannelsf(); + int nrChannels = 0, width = 0, height = 0; + float *imageData = stbi_loadf_from_memory( + (stbi_uc const *)bytes.get(), + bytes_length, &width, &height, &nrChannels, minChannels); + if (!imageData) + throw std::runtime_error("Failed to load image from memory."); + return STB_Image_load_image_uf({ + {(void*)imageData, [](auto ptr) { stbi_image_free(ptr); }} + }, width, height, (std::max)(nrChannels, minChannels), true); } dz::Image* dz::loaders::STB_Image_Loader::Load(const dz::loaders::STB_Image_Info& info) { if (!info.path.empty()) - return STB_Image_load_path(info.path); + return (info.load_float) ? STB_Image_load_pathf(info.path) : STB_Image_load_pathu(info.path); if (info.bytes && info.bytes_length) - return STB_Image_load_bytes(info.bytes, info.bytes_length); + return (info.load_float) ? STB_Image_load_bytesf(info.bytes, info.bytes_length) : STB_Image_load_bytesu(info.bytes, info.bytes_length); throw std::runtime_error("Neither bytes nor path were provided to info!"); } \ No newline at end of file diff --git a/src/Renderer.cpp b/src/Renderer.cpp index cdcfcdb9..c52d0f82 100644 --- a/src/Renderer.cpp +++ b/src/Renderer.cpp @@ -386,6 +386,12 @@ namespace dz { VK_FORMAT_B8G8R8_UNORM, VK_FORMAT_R8G8B8A8_UNORM, VK_FORMAT_B8G8R8A8_UNORM, + VK_FORMAT_R8_SRGB, + VK_FORMAT_R8G8_SRGB, + VK_FORMAT_R8G8B8_SRGB, + VK_FORMAT_B8G8R8_SRGB, + VK_FORMAT_R8G8B8A8_SRGB, + VK_FORMAT_B8G8R8A8_SRGB, VK_FORMAT_R16_SFLOAT, VK_FORMAT_R16G16_SFLOAT, VK_FORMAT_R16G16B16_SFLOAT, @@ -443,6 +449,32 @@ namespace dz { dr.formats_supported.R8G8B8A8_UNORM = dr.formats_supported_map [VK_FORMAT_R8G8B8A8_UNORM][VK_IMAGE_TYPE_2D] [VK_IMAGE_TILING_OPTIMAL][VK_IMAGE_USAGE_SAMPLED_BIT | VK_IMAGE_USAGE_TRANSFER_DST_BIT]; + + dr.formats_supported.R8_SRGB = dr.formats_supported_map + [VK_FORMAT_R8_SRGB][VK_IMAGE_TYPE_2D] + [VK_IMAGE_TILING_OPTIMAL][VK_IMAGE_USAGE_SAMPLED_BIT | VK_IMAGE_USAGE_TRANSFER_DST_BIT]; + dr.formats_supported.R8G8_SRGB = dr.formats_supported_map + [VK_FORMAT_R8G8_SRGB][VK_IMAGE_TYPE_2D] + [VK_IMAGE_TILING_OPTIMAL][VK_IMAGE_USAGE_SAMPLED_BIT | VK_IMAGE_USAGE_TRANSFER_DST_BIT]; + dr.formats_supported.R8G8B8_SRGB = dr.formats_supported_map + [VK_FORMAT_R8G8B8_SRGB][VK_IMAGE_TYPE_2D] + [VK_IMAGE_TILING_OPTIMAL][VK_IMAGE_USAGE_SAMPLED_BIT | VK_IMAGE_USAGE_TRANSFER_DST_BIT]; + dr.formats_supported.R8G8B8A8_SRGB = dr.formats_supported_map + [VK_FORMAT_R8G8B8A8_SRGB][VK_IMAGE_TYPE_2D] + [VK_IMAGE_TILING_OPTIMAL][VK_IMAGE_USAGE_SAMPLED_BIT | VK_IMAGE_USAGE_TRANSFER_DST_BIT]; + + dr.formats_supported.R32_SFLOAT = dr.formats_supported_map + [VK_FORMAT_R32_SFLOAT][VK_IMAGE_TYPE_2D] + [VK_IMAGE_TILING_OPTIMAL][VK_IMAGE_USAGE_SAMPLED_BIT | VK_IMAGE_USAGE_TRANSFER_DST_BIT]; + dr.formats_supported.R32G32_SFLOAT = dr.formats_supported_map + [VK_FORMAT_R32G32_SFLOAT][VK_IMAGE_TYPE_2D] + [VK_IMAGE_TILING_OPTIMAL][VK_IMAGE_USAGE_SAMPLED_BIT | VK_IMAGE_USAGE_TRANSFER_DST_BIT]; + dr.formats_supported.R32G32B32_SFLOAT = dr.formats_supported_map + [VK_FORMAT_R32G32B32_SFLOAT][VK_IMAGE_TYPE_2D] + [VK_IMAGE_TILING_OPTIMAL][VK_IMAGE_USAGE_SAMPLED_BIT | VK_IMAGE_USAGE_TRANSFER_DST_BIT]; + dr.formats_supported.R32G32B32A32_SFLOAT = dr.formats_supported_map + [VK_FORMAT_R32G32B32A32_SFLOAT][VK_IMAGE_TYPE_2D] + [VK_IMAGE_TILING_OPTIMAL][VK_IMAGE_USAGE_SAMPLED_BIT | VK_IMAGE_USAGE_TRANSFER_DST_BIT]; } void direct_registry_ensure_physical_device(DirectRegistry* direct_registry, Renderer* renderer) @@ -602,8 +634,8 @@ namespace dz { VkPhysicalDeviceRobustness2FeaturesEXT robustness2{ .sType = VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_ROBUSTNESS_2_FEATURES_EXT, - .robustBufferAccess2 = false, - .robustImageAccess2 = false, + .robustBufferAccess2 = true, + .robustImageAccess2 = true, .nullDescriptor = true }; @@ -642,7 +674,7 @@ namespace dz { supportedFeatures.features.depthClamp = supportedFeatures.features.depthClamp ? VK_TRUE : VK_FALSE; supportedFeatures.features.depthBiasClamp = supportedFeatures.features.depthBiasClamp ? VK_TRUE : VK_FALSE; supportedFeatures.features.samplerAnisotropy = supportedFeatures.features.samplerAnisotropy ? VK_TRUE : VK_FALSE; - supportedFeatures.features.robustBufferAccess = VK_FALSE; + supportedFeatures.features.robustBufferAccess = robustness2_enabled; supportedFeatures.features.multiDrawIndirect = supportedFeatures.features.multiDrawIndirect ? VK_TRUE : VK_FALSE; supportedFeatures.features.drawIndirectFirstInstance = supportedFeatures.features.drawIndirectFirstInstance ? VK_TRUE : VK_FALSE; @@ -663,6 +695,7 @@ namespace dz { vkGetDeviceQueue(dr.device, dr.graphicsAndComputeFamily, 0, &dr.graphicsQueue); vkGetDeviceQueue(dr.device, dr.presentFamily, 0, &dr.presentQueue); vkGetDeviceQueue(dr.device, dr.graphicsAndComputeFamily, 0, &dr.computeQueue); + vkGetDeviceQueue(dr.device, dr.graphicsAndComputeFamily, 0, &dr.copyQueue); } bool create_swap_chain(Renderer* renderer) @@ -762,7 +795,7 @@ namespace dz { { for (auto& availableFormat : availableFormats) { - if (availableFormat.format == VK_FORMAT_B8G8R8A8_UNORM) + if (availableFormat.format == (VkFormat)dr.preferredColorSpace) { return availableFormat; } @@ -816,24 +849,26 @@ namespace dz { return; } + void allocate_command_buffers(uint32_t count, VkCommandBuffer* pCommandBuffer, VkCommandBufferLevel level = VK_COMMAND_BUFFER_LEVEL_PRIMARY) { + VkCommandBufferAllocateInfo allocInfo{}; + allocInfo.sType = VK_STRUCTURE_TYPE_COMMAND_BUFFER_ALLOCATE_INFO; + allocInfo.commandPool = dr.commandPool; + allocInfo.level = level; + allocInfo.commandBufferCount = count; + vk_check("vkAllocateCommandBuffers", vkAllocateCommandBuffers(dr.device, &allocInfo, pCommandBuffer)); + } + void ensure_command_buffers(Renderer* renderer) { if (!renderer->commandBuffers.empty()) return; + renderer->commandBuffers.resize(MAX_FRAMES_IN_FLIGHT); - VkCommandBufferAllocateInfo allocInfo{}; - allocInfo.sType = VK_STRUCTURE_TYPE_COMMAND_BUFFER_ALLOCATE_INFO; - allocInfo.commandPool = dr.commandPool; - allocInfo.level = VK_COMMAND_BUFFER_LEVEL_PRIMARY; - allocInfo.commandBufferCount = renderer->commandBuffers.size(); - vk_check("vkAllocateCommandBuffers", vkAllocateCommandBuffers(dr.device, &allocInfo, &renderer->commandBuffers[0])); - - VkCommandBufferAllocateInfo computeAllocInfo{}; - computeAllocInfo.sType = VK_STRUCTURE_TYPE_COMMAND_BUFFER_ALLOCATE_INFO; - computeAllocInfo.commandPool = dr.commandPool; - computeAllocInfo.level = VK_COMMAND_BUFFER_LEVEL_PRIMARY; - computeAllocInfo.commandBufferCount = 1; - vk_check("vkAllocateCommandBuffers", vkAllocateCommandBuffers(dr.device, &computeAllocInfo, &dr.computeCommandBuffer)); + allocate_command_buffers(renderer->commandBuffers.size(), &renderer->commandBuffers[0]); + + allocate_command_buffers(1, &dr.computeCommandBuffer); + allocate_command_buffers(1, &dr.copyCommandBuffer); + return; } diff --git a/src/Shader.cpp b/src/Shader.cpp index e6e86ae0..bab1359b 100644 --- a/src/Shader.cpp +++ b/src/Shader.cpp @@ -430,7 +430,7 @@ namespace dz { case SpvImageFormatRgba32f: return VK_FORMAT_R32G32B32A32_SFLOAT; case SpvImageFormatRgba16f: return VK_FORMAT_R16G16B16A16_SFLOAT; case SpvImageFormatR32f: return VK_FORMAT_R32_SFLOAT; - case SpvImageFormatRgba8: return VK_FORMAT_R8G8B8A8_UNORM; + case SpvImageFormatRgba8: return VK_FORMAT_R8G8B8A8_SRGB; case SpvImageFormatRgba8Snorm: return VK_FORMAT_R8G8B8A8_SNORM; case SpvImageFormatRg32f: return VK_FORMAT_R32G32_SFLOAT; case SpvImageFormatRg16f: return VK_FORMAT_R16G16_SFLOAT; @@ -439,9 +439,9 @@ namespace dz { case SpvImageFormatRgba16: return VK_FORMAT_R16G16B16A16_UNORM; case SpvImageFormatRgb10A2: return VK_FORMAT_A2R10G10B10_UNORM_PACK32; case SpvImageFormatRg16: return VK_FORMAT_R16G16_UNORM; - case SpvImageFormatRg8: return VK_FORMAT_R8G8_UNORM; + case SpvImageFormatRg8: return VK_FORMAT_R8G8_SRGB; case SpvImageFormatR16: return VK_FORMAT_R16_UNORM; - case SpvImageFormatR8: return VK_FORMAT_R8_UNORM; + case SpvImageFormatR8: return VK_FORMAT_R8_SRGB; case SpvImageFormatRgba16Snorm: return VK_FORMAT_R16G16B16A16_SNORM; case SpvImageFormatRg16Snorm: return VK_FORMAT_R16G16_SNORM; case SpvImageFormatRg8Snorm: return VK_FORMAT_R8G8_SNORM; @@ -1039,10 +1039,39 @@ namespace dz { continue; } + auto descriptor_type = static_cast(binding_info.descriptor_type); + auto descriptor_count = binding_info.count; + + switch (descriptor_type) { + case VK_DESCRIPTOR_TYPE_SAMPLED_IMAGE: + case VK_DESCRIPTOR_TYPE_STORAGE_IMAGE: + case VK_DESCRIPTOR_TYPE_COMBINED_IMAGE_SAMPLER: + for (auto& [buffer_group, enabled] : shader->buffer_groups) { + if (!enabled) + continue; + Image* img = nullptr; + auto override_it = shader->sampler_key_image_override_map.find(binding_info.name); + if (override_it != shader->sampler_key_image_override_map.end()) { + img = override_it->second; + } + else { + auto image_it = buffer_group->runtime_images.find(binding_info.name); + if (image_it == buffer_group->runtime_images.end()) + continue; + img = image_it->second.get(); + } + descriptor_count = img->mip_levels; + break; + } + break; + default: + break; + } + VkDescriptorSetLayoutBinding layout_binding{}; layout_binding.binding = binding_info.binding; - layout_binding.descriptorType = static_cast(binding_info.descriptor_type); - layout_binding.descriptorCount = binding_info.count; + layout_binding.descriptorType = descriptor_type; + layout_binding.descriptorCount = descriptor_count; layout_binding.stageFlags = GetShaderStageFromModuleType(stage); layout_binding.pImmutableSamplers = nullptr; @@ -1084,8 +1113,35 @@ namespace dz { for (uint32_t i = 0; i < reflect_module->descriptor_binding_count; ++i) { const SpvReflectDescriptorBinding& binding_info = reflect_module->descriptor_bindings[i]; - VkDescriptorType type = static_cast(binding_info.descriptor_type); - descriptor_counts[type] += binding_info.count; + auto descriptor_type = static_cast(binding_info.descriptor_type); + auto descriptor_count = binding_info.count; + + switch (descriptor_type) { + case VK_DESCRIPTOR_TYPE_SAMPLED_IMAGE: + case VK_DESCRIPTOR_TYPE_STORAGE_IMAGE: + case VK_DESCRIPTOR_TYPE_COMBINED_IMAGE_SAMPLER: + for (auto& [buffer_group, enabled] : shader->buffer_groups) { + if (!enabled) + continue; + Image* img = nullptr; + auto override_it = shader->sampler_key_image_override_map.find(binding_info.name); + if (override_it != shader->sampler_key_image_override_map.end()) { + img = override_it->second; + } + else { + auto image_it = buffer_group->runtime_images.find(binding_info.name); + if (image_it == buffer_group->runtime_images.end()) + continue; + img = image_it->second.get(); + } + descriptor_count = img->mip_levels; + break; + } + break; + default: + break; + } + descriptor_counts[descriptor_type] += descriptor_count; } } @@ -1184,7 +1240,7 @@ namespace dz { * @brief The core creation function. It iterates the prepared ShaderBuffer map, creates the * actual Vulkan buffers, copies initial data, and performs the shared_ptr swap. */ - bool CreateAndBindShaderBuffers(BufferGroup* buffer_group, Shader* shader) { + bool shader_buffers_ensure_and_bind(BufferGroup* buffer_group, Shader* shader) { std::vector descriptor_writes; std::vector buffer_infos; std::vector image_infos; @@ -1208,20 +1264,23 @@ namespace dz { auto dstSet = shader_get_descriptor_set(shader, name); - VkWriteDescriptorSet write_set{VK_STRUCTURE_TYPE_WRITE_DESCRIPTOR_SET}; - write_set.dstSet = dstSet; - write_set.dstBinding = buffer.binding; - write_set.dstArrayElement = 0; - write_set.descriptorType = buffer.descriptor_type; - write_set.descriptorCount = 1; - write_set.pBufferInfo = &buffer_infos[i]; - descriptor_writes.push_back(write_set); + descriptor_writes.push_back(VkWriteDescriptorSet{ + .sType = VK_STRUCTURE_TYPE_WRITE_DESCRIPTOR_SET, + .dstSet = dstSet, + .dstBinding = buffer.binding, + .dstArrayElement = 0, + .descriptorCount = 1, + .descriptorType = buffer.descriptor_type, + .pBufferInfo = &buffer_infos[i] + }); i++; } - for (auto& [name, image_ref] : buffer_group->images) { - Image* img = 0; + size_t image_info_count = 0; + for (auto& [name, image_ref] : buffer_group->images) + { + Image* img = nullptr; auto override_it = shader->sampler_key_image_override_map.find(name); if (override_it != shader->sampler_key_image_override_map.end()) { img = override_it->second; @@ -1230,33 +1289,62 @@ namespace dz { auto image_it = buffer_group->runtime_images.find(name); if (image_it == buffer_group->runtime_images.end()) { - std::cerr << "Warning: Buffer group has not image defined for key: " << name << std::endl; + std::cerr << "Warning: Buffer group has no image defined for key: " << name << std::endl; continue; } img = image_it->second.get(); } - image_infos.emplace_back(); - image_infos.back().imageView = img->imageView; - image_infos.back().imageLayout = infer_image_layout(shader, image_ref.descriptor_types); - image_infos.back().sampler = img->sampler; + image_info_count += static_cast(img->imageViews.size()); } - size_t j = 0; - for (auto& [name, image_ref] : buffer_group->images) { + image_infos.reserve(image_info_count); + + size_t image_info_offset = 0; + for (auto& [name, image_ref] : buffer_group->images) + { + Image* img = nullptr; + auto override_it = shader->sampler_key_image_override_map.find(name); + if (override_it != shader->sampler_key_image_override_map.end()) { + img = override_it->second; + } + else { + auto image_it = buffer_group->runtime_images.find(name); + if (image_it == buffer_group->runtime_images.end()) + { + std::cerr << "Warning: Buffer group has no image defined for key: " << name << std::endl; + continue; + } + img = image_it->second.get(); + } + + uint32_t num_mips = static_cast(img->imageViews.size()); + if (num_mips == 0) + continue; + + for (uint32_t mip = 0; mip < num_mips; ++mip) + { + image_infos.push_back(VkDescriptorImageInfo{ + .sampler = img->sampler, + .imageView = img->imageViews[mip], + .imageLayout = infer_image_layout(shader, image_ref.descriptor_types) + }); + } + auto dstSet = shader_get_descriptor_set(shader, name); - - VkWriteDescriptorSet write_set{VK_STRUCTURE_TYPE_WRITE_DESCRIPTOR_SET}; - write_set.dstSet = dstSet; - write_set.dstBinding = image_ref.binding; - write_set.dstArrayElement = 0; auto type = image_ref.descriptor_types[shader]; - write_set.descriptorType = type; - write_set.descriptorCount = 1; - write_set.pImageInfo = &image_infos[j]; - descriptor_writes.push_back(write_set); - j++; + descriptor_writes.push_back(VkWriteDescriptorSet{ + .sType{VK_STRUCTURE_TYPE_WRITE_DESCRIPTOR_SET}, + .dstSet = dstSet, + .dstBinding = image_ref.binding, + .dstArrayElement = 0, + .descriptorCount = num_mips, + .descriptorType = type, + .pImageInfo = image_infos.data() + image_info_offset + }); + + image_info_offset += num_mips; } if (!descriptor_writes.empty()) { @@ -1282,79 +1370,7 @@ namespace dz { for (auto& [buffer_group, bound] : shader->buffer_groups) { if (!bound) continue; - for (auto& [name, buffer] : buffer_group->buffers) { - if (buffer.gpu_buffer.mapped_memory) - continue; - - buffer_group_make_gpu_buffer(name, buffer); - } - for (auto& [name, buffer] : buffer_group->buffers) { - // Prepare the descriptor set write - buffer_infos.emplace_back(); - buffer_infos.back().buffer = buffer.gpu_buffer.buffer; - buffer_infos.back().offset = 0; - buffer_infos.back().range = buffer.gpu_buffer.size; - } - - size_t i = 0; - for (auto& [name, buffer] : buffer_group->buffers) { - - auto dstSet = shader_get_descriptor_set(shader, name); - - VkWriteDescriptorSet write_set{VK_STRUCTURE_TYPE_WRITE_DESCRIPTOR_SET}; - write_set.dstSet = dstSet; - write_set.dstBinding = buffer.binding; - write_set.dstArrayElement = 0; - write_set.descriptorType = buffer.descriptor_type; - write_set.descriptorCount = 1; - write_set.pBufferInfo = &buffer_infos[i]; - descriptor_writes.push_back(write_set); - - i++; - } - - for (auto& [name, image_ref] : buffer_group->images) { - Image* img = 0; - auto override_it = shader->sampler_key_image_override_map.find(name); - if (override_it != shader->sampler_key_image_override_map.end()) { - img = override_it->second; - } - else { - auto image_it = buffer_group->runtime_images.find(name); - if (image_it == buffer_group->runtime_images.end()) - { - std::cerr << "Warning: Buffer group has not image defined for key: " << name << std::endl; - continue; - } - img = image_it->second.get(); - } - - image_infos.emplace_back(); - image_infos.back().imageView = img->imageView; - image_infos.back().imageLayout = infer_image_layout(shader, image_ref.descriptor_types); - image_infos.back().sampler = img->sampler; - } - - size_t j = 0; - for (auto& [name, image_ref] : buffer_group->images) { - auto dstSet = shader_get_descriptor_set(shader, name); - - VkWriteDescriptorSet write_set{VK_STRUCTURE_TYPE_WRITE_DESCRIPTOR_SET}; - write_set.dstSet = dstSet; - write_set.dstBinding = image_ref.binding; - write_set.dstArrayElement = 0; - auto type = image_ref.descriptor_types[shader]; - write_set.descriptorType = type; - write_set.descriptorCount = 1; - write_set.pImageInfo = &image_infos[j]; - descriptor_writes.push_back(write_set); - - j++; - } - } - - if (!descriptor_writes.empty()) { - vkUpdateDescriptorSets(dr.device, descriptor_writes.size(), descriptor_writes.data(), 0, nullptr); + shader_buffers_ensure_and_bind(buffer_group, shader); } } @@ -1370,7 +1386,7 @@ namespace dz { auto& device = dr.device; if (!CreateDescriptorSetLayouts(device, shader)) return; - if (!CreateDescriptorPool(device, shader, 10)) return; // Max 10 sets of each type + if (!CreateDescriptorPool(device, shader, 15)) return; // Max 10 sets of each type if (!AllocateDescriptorSets(device, shader)) return; if (!AllocatePushConstants(shader)) return; @@ -1539,28 +1555,30 @@ namespace dz { // Read/Write only compatibility {{VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL, VK_IMAGE_LAYOUT_TRANSFER_SRC_OPTIMAL}, {VK_ACCESS_SHADER_READ_BIT, VK_ACCESS_TRANSFER_READ_BIT, VK_PIPELINE_STAGE_FRAGMENT_SHADER_BIT, VK_PIPELINE_STAGE_TRANSFER_BIT}}, + {{VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL, VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL}, {VK_ACCESS_SHADER_WRITE_BIT, VK_ACCESS_TRANSFER_WRITE_BIT, VK_PIPELINE_STAGE_FRAGMENT_SHADER_BIT, VK_PIPELINE_STAGE_TRANSFER_BIT}}, {{VK_IMAGE_LAYOUT_TRANSFER_SRC_OPTIMAL, VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL}, {VK_ACCESS_TRANSFER_READ_BIT, VK_ACCESS_SHADER_READ_BIT, VK_PIPELINE_STAGE_TRANSFER_BIT, VK_PIPELINE_STAGE_FRAGMENT_SHADER_BIT}}, }; - void transition_image_layout(Image* image_ptr, VkImageLayout old_layout, VkImageLayout new_layout) { + void transition_image_layout(Image* image_ptr, VkImageLayout new_layout, int mip) { auto image = image_ptr->image; auto device = dr.device; auto command_buffer = begin_single_time_commands(); VkImageMemoryBarrier barrier{}; barrier.sType = VK_STRUCTURE_TYPE_IMAGE_MEMORY_BARRIER; - barrier.oldLayout = old_layout; + auto& current_layout = image_ptr->current_layouts[mip]; + barrier.oldLayout = current_layout; barrier.newLayout = new_layout; barrier.srcQueueFamilyIndex = VK_QUEUE_FAMILY_IGNORED; barrier.dstQueueFamilyIndex = VK_QUEUE_FAMILY_IGNORED; barrier.image = image; barrier.subresourceRange.aspectMask = image_get_aspect_mask(image_ptr); - barrier.subresourceRange.baseMipLevel = 0; + barrier.subresourceRange.baseMipLevel = mip; barrier.subresourceRange.levelCount = 1; barrier.subresourceRange.baseArrayLayer = 0; barrier.subresourceRange.layerCount = 1; - auto it = kLayoutTransitions.find({old_layout, new_layout}); + auto it = kLayoutTransitions.find({barrier.oldLayout, new_layout}); if (it == kLayoutTransitions.end()) { throw std::runtime_error("Unsupported image layout transition!"); } @@ -1580,36 +1598,38 @@ namespace dz { ); end_single_time_commands(command_buffer); + + current_layout = new_layout; } void shader_ensure_image_layouts(Shader* shader) { for (auto& bound_buffer_group : shader->bound_buffer_groups) { - for (auto& [name, shader_image] : bound_buffer_group->images) - { - auto runtime_ite = bound_buffer_group->runtime_images.find(name); - if (runtime_ite == bound_buffer_group->runtime_images.end()) - continue; - - auto image_ptr = runtime_ite->second.get(); + for (auto& [name, image_ptr] : bound_buffer_group->runtime_images) { + auto& image = *image_ptr; - auto exp_lay_ite = shader_image.expected_layouts.find(shader); - if (exp_lay_ite == shader_image.expected_layouts.end()) + auto& expected_layouts = bound_buffer_group->images[name].expected_layouts; + auto exp_lay_ite = expected_layouts.find(shader); + if (exp_lay_ite == expected_layouts.end()) continue; auto required = exp_lay_ite->second; - - if (image.current_layout != required) - { - transition_image_layout(image_ptr, image.current_layout, required); - image.current_layout = required; + auto current_layouts_data = image.current_layouts.data(); + + for (auto mip = 0; mip < image.mip_levels; ++mip) { + if (current_layouts_data[mip] != required) { + transition_image_layout(&image, required, mip); + } } } } } - void shader_dispatch(Shader* shader, vec dispatch_layout) { + void shader_dispatch(Shader* shader, uint32_t x, uint32_t y, uint32_t z, void(*shader_pre_dispatch)(Shader*, void*), void(*shader_post_dispatch)(Shader*, void*), void* user_data) { shader_ensure_image_layouts(shader); + + dr.commandBuffer = &dr.computeCommandBuffer; + VkCommandBufferBeginInfo beginInfo{}; beginInfo.sType = VK_STRUCTURE_TYPE_COMMAND_BUFFER_BEGIN_INFO; vkBeginCommandBuffer(dr.computeCommandBuffer, &beginInfo); @@ -1634,11 +1654,15 @@ namespace dz { sets.data(), 0, nullptr ); + + if (shader_pre_dispatch) + shader_pre_dispatch(shader, user_data); + vkCmdDispatch( dr.computeCommandBuffer, - dispatch_layout[0], - dispatch_layout[1], - dispatch_layout[2] + x, + y, + z ); vkEndCommandBuffer(dr.computeCommandBuffer); VkSubmitInfo submitInfo{}; @@ -1647,6 +1671,11 @@ namespace dz { submitInfo.pCommandBuffers = &dr.computeCommandBuffer; vkQueueSubmit(dr.computeQueue, 1, &submitInfo, VK_NULL_HANDLE); vkQueueWaitIdle(dr.computeQueue); + + dr.commandBuffer = VK_NULL_HANDLE; + + if (shader_post_dispatch) + shader_post_dispatch(shader, user_data); } void shader_compile(Shader* shader) { @@ -1666,13 +1695,28 @@ namespace dz { pipelineLayoutInfo.pSetLayouts = layouts.data(); std::vector ranges; - for (auto& [pc_index, pc] : shader->push_constants) { + static auto AddRangeForStageFlags = [](auto& push_constants, auto& ranges, auto stageFlag) { + auto total_size = 0; + bool hadStage = false; + for (auto& [pc_index, pc] : push_constants) { + if (pc.stageFlags != stageFlag) + continue; + if (!hadStage) + hadStage = true; + total_size += pc.size; + } + if (!hadStage) + return; VkPushConstantRange range; - range.offset = pc.offset; - range.size = pc.size; - range.stageFlags = pc.stageFlags; + range.offset = 0; + range.size = total_size; + range.stageFlags = stageFlag; ranges.push_back(range); - } + }; + AddRangeForStageFlags(shader->push_constants, ranges, VK_SHADER_STAGE_VERTEX_BIT); + AddRangeForStageFlags(shader->push_constants, ranges, VK_SHADER_STAGE_FRAGMENT_BIT); + AddRangeForStageFlags(shader->push_constants, ranges, VK_SHADER_STAGE_VERTEX_BIT | VK_SHADER_STAGE_FRAGMENT_BIT); + AddRangeForStageFlags(shader->push_constants, ranges, VK_SHADER_STAGE_COMPUTE_BIT); pipelineLayoutInfo.pushConstantRangeCount = ranges.size(); pipelineLayoutInfo.pPushConstantRanges = ranges.data(); diff --git a/src/Shader.cpp.hpp b/src/Shader.cpp.hpp index c70e51c3..9fbefebe 100644 --- a/src/Shader.cpp.hpp +++ b/src/Shader.cpp.hpp @@ -26,7 +26,7 @@ namespace dz { uint32_t GetMinimumTypeSizeInBytes(const SpvReflectTypeDescription& type_desc); uint32_t CalculateStructSize(const SpvReflectTypeDescription& type_desc); - bool CreateAndBindShaderBuffers(BufferGroup* buffer_group, Shader* shader); + bool shader_buffers_ensure_and_bind(BufferGroup* buffer_group, Shader* shader); VkShaderStageFlags GetShaderStageFromModuleType(ShaderModuleType type); struct ReflectedVariable { diff --git a/src/Window.cpp b/src/Window.cpp index 32088fd3..2dfd5de4 100644 --- a/src/Window.cpp +++ b/src/Window.cpp @@ -254,7 +254,7 @@ namespace dz { for (auto& [priority, shader_dispatches] : window->priority_shader_dispatches) { for (auto& [shader, dispatch_fn] : shader_dispatches) { auto count = dispatch_fn(); - shader_dispatch(shader, {count, 1, 1}); + shader_dispatch(shader, count, 1, 1); } } if (!window->minimized || !window_get_minimized(window)) diff --git a/tests/ECS.cpp b/tests/ECS.cpp index 8d538303..8f8450bf 100644 --- a/tests/ECS.cpp +++ b/tests/ECS.cpp @@ -86,7 +86,7 @@ int main() { window = window_create({ .title = "ECS Test", .x = 0, - .y = 240, + .y = 120, .width = ORIGINAL_WINDOW_WIDTH, .height = ORIGINAL_WINDOW_HEIGHT, .borderless = true, @@ -103,13 +103,15 @@ int main() { int autumn_hdri_index = -1; auto autumn_hdri_id = ecs.AddHDRI(HDRI{}, autumn_hdri_index, "hdri/autumn_field_puresky_4k"); ecs.SetHDRIImage(autumn_hdri_id, TL::Load(STB_Image_Info{ - .path = "hdri/autumn_field_puresky_4k.hdr" + .path = "hdri/autumn_field_puresky_4k.hdr", + .load_float = true })); int afternoon_hdri_index = -1; auto afternoon_hdri_id = ecs.AddHDRI(HDRI{}, afternoon_hdri_index, "zwartkops_straight_afternoon_4k"); ecs.SetHDRIImage(afternoon_hdri_id, TL::Load(STB_Image_Info{ - .path = "hdri/zwartkops_straight_afternoon_4k.hdr" + .path = "hdri/zwartkops_straight_afternoon_4k.hdr", + .load_float = true })); auto sky_scene_id = ecs.AddScene(Scene{}, "Sky Scene"); @@ -137,26 +139,52 @@ int main() { auto out_id = ecs.AddMesh(positions, uv2s, normals, tangents, bitangents, material_index, out_index, name); return {out_id, out_index}; }, - .add_material_function = [&](const auto& name, const auto& images_vec) -> MaterialPair { + .add_material_function = [&](const auto& name, const auto& images_vec, auto albedo_color, auto metalness, auto roughness) -> MaterialPair { int out_index = -1; - auto out_id = ecs.AddMaterial(Material{}, out_index, name); + auto out_id = ecs.AddMaterial(Material{ + .albedo_color = albedo_color, + .metalness = metalness, + .roughness = roughness + }, out_index, name); ecs.SetMaterialImages(out_id, images_vec); return {out_id, out_index}; } }; + + // Saiyan auto saiyan_one_info = base_info; saiyan_one_info.path = "models/SaiyanOne.glb"; TL::Load(saiyan_one_info); + // Golden sports car + auto golden_sports_car_info = base_info; golden_sports_car_info.path = "models/GoldenSportsCar.glb"; - golden_sports_car_info.root_position[0] += 4.5f; + golden_sports_car_info.root_position[0] += 2.5f; TL::Load(golden_sports_car_info); + // Red sports car + + auto red_sports_car_info = base_info; + red_sports_car_info.path = "models/RedSportsCar.glb"; + + red_sports_car_info.root_position[0] -= 2.5f; + + TL::Load(red_sports_car_info); + + // Metal plane + + auto metal_plane_info = base_info; + metal_plane_info.path = "models/metal_plane.glb"; + + metal_plane_info.root_position[1] -= 0.5f; + + TL::Load(metal_plane_info); + ecs.AddLight(sky_scene_id, Light{ .type = uint32_t(Light::Directional), .intensity = 1.f, @@ -1218,6 +1246,10 @@ void DrawDropTarget(ReflectableGroup& target_group, ReflectableGroup* dragged_gr ExampleECS::SetWhoParent(ecs_ptr->GetSubMesh(dragged_group->id), new_parent_ptr); break; } + case Light::PID: { + ExampleECS::SetWhoParent(ecs_ptr->GetLight(dragged_group->id), new_parent_ptr); + break; + } case SkyBox::PID: { ExampleECS::SetWhoParent(ecs_ptr->GetSkyBox(dragged_group->id), new_parent_ptr); break; diff --git a/tests/Particle2D.cpp b/tests/Particle2D.cpp index 0aa13c25..26690d91 100644 --- a/tests/Particle2D.cpp +++ b/tests/Particle2D.cpp @@ -547,14 +547,14 @@ void main() while (window_poll_events(window)) { - shader_dispatch(image_clear_compute_shader, {(*window_width_ptr + 31)/32, (*window_height_ptr + 31)/32, 1}); - shader_dispatch(clear_density_shader, {(density_field_size + 127)/128, 1, 1}); - shader_dispatch(deposit_mass_shader, {dispatchX, 1, 1}); + shader_dispatch(image_clear_compute_shader, (*window_width_ptr + 31)/32, (*window_height_ptr + 31)/32, 1); + shader_dispatch(clear_density_shader, (density_field_size + 127)/128, 1, 1); + shader_dispatch(deposit_mass_shader, dispatchX, 1, 1); for (uint32_t i = 0; i < numDiffusionPasses; ++i) { - shader_dispatch(diffuse_density_shader, {(uint32_t)(density_field_width + 31)/32, (uint32_t)(density_field_height + 31)/32, 1}); + shader_dispatch(diffuse_density_shader, (uint32_t)(density_field_width + 31)/32, (uint32_t)(density_field_height + 31)/32, 1); } - shader_dispatch(gradient_force_shader, {(uint32_t)(density_field_width + 31)/32, (uint32_t)(density_field_height + 31)/32, 1}); - shader_dispatch(gravity_motion_shader, {dispatchX, 1, 1}); + shader_dispatch(gradient_force_shader, (uint32_t)(density_field_width + 31)/32, (uint32_t)(density_field_height + 31)/32, 1); + shader_dispatch(gravity_motion_shader, dispatchX, 1, 1); window_render(window); } return 0; diff --git a/tests/ShaderReflect.cpp b/tests/ShaderReflect.cpp index c95edb9d..f8bf1648 100644 --- a/tests/ShaderReflect.cpp +++ b/tests/ShaderReflect.cpp @@ -494,7 +494,7 @@ void main() if (esc_pressed) break; - shader_dispatch(update_entity_shader, {entity_ptrs.size(), 1, 1}); + shader_dispatch(update_entity_shader, entity_ptrs.size(), 1, 1); for (auto& ep : entity_ptrs) { diff --git a/tests/x-platform/src/app-lib.cpp b/tests/x-platform/src/app-lib.cpp index 05e3db33..38e95803 100644 --- a/tests/x-platform/src/app-lib.cpp +++ b/tests/x-platform/src/app-lib.cpp @@ -186,6 +186,6 @@ DZ_EXPORT void update() DZ_EXPORT void render() { - shader_dispatch(compute_shader, {quad_ptrs.size(), 1, 1}); + shader_dispatch(compute_shader, quad_ptrs.size(), 1, 1); window_render(cached_window); } \ No newline at end of file