From 04258c0fad0bea4b6393893965a764a9fa18d395 Mon Sep 17 00:00:00 2001 From: Steven French Date: Fri, 8 Aug 2025 19:17:28 +1200 Subject: [PATCH 01/13] add GammaCorrection Provider Change-Id: Ic51242fd112ba3eda73e263875290c5539d1a740 --- include/dz/ECS.hpp | 1 + include/dz/ECS/GammaCorrection.hpp | 60 ++++++++++++++++++++++++++++++ tests/ECS.cpp | 1 + 3 files changed, 62 insertions(+) create mode 100644 include/dz/ECS/GammaCorrection.hpp diff --git a/include/dz/ECS.hpp b/include/dz/ECS.hpp index 3b8417f6..6a5a4fb0 100644 --- a/include/dz/ECS.hpp +++ b/include/dz/ECS.hpp @@ -11,6 +11,7 @@ #include "State.hpp" #include "ECS/Provider.hpp" #include "ECS/Light.hpp" +#include "ECS/GammaCorrection.hpp" #include "ECS/PhongLighting.hpp" #include "ECS/PhysicallyBasedLighting.hpp" #include "ECS/Scene.hpp" diff --git a/include/dz/ECS/GammaCorrection.hpp b/include/dz/ECS/GammaCorrection.hpp new file mode 100644 index 00000000..be4a0e18 --- /dev/null +++ b/include/dz/ECS/GammaCorrection.hpp @@ -0,0 +1,60 @@ +#pragma once +#include "Provider.hpp" +#include "../Reflectable.hpp" +#include "../Shader.hpp" +namespace dz::ecs { + struct GammaCorrection : Provider { + inline static constexpr size_t PID = 12; + inline static float Priority = 1000.0f; + inline static constexpr BufferHost BufferHostType = BufferHost::NoBuffer; + inline static std::string ProviderName = "GammaCorrection"; + inline static std::string StructName = "GammaCorrection"; + inline static std::unordered_map GLSLMethods = { + {ShaderModuleType::Fragment, R"( +vec3 LinearToSRGBAccurate(in vec3 linearColor) +{ + vec3 srgbLow = linearColor * 12.92; + vec3 srgbHigh = pow(linearColor, vec3(1.0 / 2.4)) * 1.055 - 0.055; + bvec3 cutoff = lessThanEqual(linearColor, vec3(0.0031308)); + return mix(srgbHigh, srgbLow, cutoff); +} +)" } + }; + inline static std::vector> GLSLMain = { + {1000.0f, R"( + current_color = vec4(LinearToSRGBAccurate(vec3(current_color)), 1.0); +)", ShaderModuleType::Fragment} + }; + + struct GammaCorrectionReflectableGroup : ::ReflectableGroup { + std::string name; + GammaCorrectionReflectableGroup(BufferGroup* buffer_group): + name("GammaCorrection") + {} + GammaCorrectionReflectableGroup(BufferGroup* buffer_group, Serial& serial) + { + restore(serial); + } + GroupType GetGroupType() override { + return ReflectableGroup::Generic; + } + std::string& GetName() override { + return name; + } + bool backup(Serial& serial) const override { + if (!backup_internal(serial)) + return false; + serial << name; + return true; + } + bool restore(Serial& serial) override { + if (!restore_internal(serial)) + return false; + serial >> name; + return true; + } + }; + + using ReflectableGroup = GammaCorrectionReflectableGroup; + }; +} \ No newline at end of file diff --git a/tests/ECS.cpp b/tests/ECS.cpp index 8f8450bf..bc1ace4d 100644 --- a/tests/ECS.cpp +++ b/tests/ECS.cpp @@ -26,6 +26,7 @@ using ExampleECS = ECS< #ifdef ENABLE_LIGHTS , Light, PhysicallyBasedLighting #endif + , GammaCorrection >; From 2f0808ee48a67be38c9a068f5940927004ff3a1c Mon Sep 17 00:00:00 2001 From: Steven French Date: Fri, 8 Aug 2025 19:18:54 +1200 Subject: [PATCH 02/13] image serialization and ECS backup/restore updates (incomplete) Change-Id: Ie0748e8ecb244c62c9c95fe0200a639c54cd21fa --- include/dz/ECS.hpp | 92 ++++++++++++++--- include/dz/Image.hpp | 39 +++++++- src/Image.cpp | 231 +++++++++++++++++++++++++++++++++++++++++-- src/Image.cpp.hpp | 2 + 4 files changed, 340 insertions(+), 24 deletions(-) diff --git a/include/dz/ECS.hpp b/include/dz/ECS.hpp index 6a5a4fb0..975f5c51 100644 --- a/include/dz/ECS.hpp +++ b/include/dz/ECS.hpp @@ -157,21 +157,20 @@ namespace dz { Shader* skybox_shader = nullptr; // ! Shader* model_compute_shader = nullptr; // ! Shader* camera_mat_compute_shader = nullptr; // ! - std::vector raster_shaders; - std::vector compute_shaders; - - bool material_browser_open = true; - - bool atlas_dirty = false; - ImagePack albedo_atlas_pack; - ImagePack normal_atlas_pack; - ImagePack roughness_atlas_pack; - ImagePack metalness_atlas_pack; - ImagePack metalness_roughness_atlas_pack; - ImagePack shininess_atlas_pack; - ImagePack hdri_atlas_pack; - ImagePack irradiance_atlas_pack; - ImagePack radiance_atlas_pack; + std::vector raster_shaders; // ! + std::vector compute_shaders; // ! + + bool material_browser_open = true; // Y + + ImagePack albedo_atlas_pack; // Y + ImagePack normal_atlas_pack; // Y + ImagePack roughness_atlas_pack; // Y + ImagePack metalness_atlas_pack; // Y + ImagePack metalness_roughness_atlas_pack; // Y + ImagePack shininess_atlas_pack; // Y + ImagePack hdri_atlas_pack; // Y + ImagePack irradiance_atlas_pack; // Y + ImagePack radiance_atlas_pack; // Y auto GenerateSkyBoxDrawFunction() { return [&](auto buffer_group, auto& skybox) -> DrawTuple { @@ -476,6 +475,9 @@ namespace dz { return false; if (!BackupBuffers(serial)) return false; + serial << material_browser_open; + if (!BackupAtli(serial)) + return false; return true; } @@ -501,6 +503,9 @@ namespace dz { return false; if (!RestoreBuffers(serial)) return false; + serial >> material_browser_open; + if (!RestoreAtli(serial)) + return false; UpdateGroupsChildren(); return (loaded_from_io = true); } @@ -613,6 +618,63 @@ namespace dz { return true; } + bool BackupAtlas(Serial& serial, ImagePack& atlas) { + atlas.getAtlas(); + return image_serialize(atlas.getAtlas(), serial); + } + + bool BackupAtli(Serial& serial) { + if (!BackupAtlas(serial, albedo_atlas_pack)) + return false; + if (!BackupAtlas(serial, normal_atlas_pack)) + return false; + if (!BackupAtlas(serial, roughness_atlas_pack)) + return false; + if (!BackupAtlas(serial, metalness_atlas_pack)) + return false; + if (!BackupAtlas(serial, metalness_roughness_atlas_pack)) + return false; + if (!BackupAtlas(serial, shininess_atlas_pack)) + return false; + if (!BackupAtlas(serial, hdri_atlas_pack)) + return false; + if (!BackupAtlas(serial, irradiance_atlas_pack)) + return false; + if (!BackupAtlas(serial, radiance_atlas_pack)) + return false; + return true; + } + + bool RestoreAtlas(Serial& serial, ImagePack& atlas) { + auto atlas_image = image_from_serial(serial); + if (!atlas_image) + return false; + // atlas.SetAtlasImage(atlas_image); + return true; + } + + bool RestoreAtli(Serial& serial) { + if (!RestoreAtlas(serial, albedo_atlas_pack)) + return false; + if (!RestoreAtlas(serial, normal_atlas_pack)) + return false; + if (!RestoreAtlas(serial, roughness_atlas_pack)) + return false; + if (!RestoreAtlas(serial, metalness_atlas_pack)) + return false; + if (!RestoreAtlas(serial, metalness_roughness_atlas_pack)) + return false; + if (!RestoreAtlas(serial, shininess_atlas_pack)) + return false; + if (!RestoreAtlas(serial, hdri_atlas_pack)) + return false; + if (!RestoreAtlas(serial, irradiance_atlas_pack)) + return false; + if (!RestoreAtlas(serial, radiance_atlas_pack)) + return false; + return true; + } + void RegisterProviders() { (RegisterProvider(), ...); } diff --git a/include/dz/Image.hpp b/include/dz/Image.hpp index 29d71b50..a681e9e3 100644 --- a/include/dz/Image.hpp +++ b/include/dz/Image.hpp @@ -5,6 +5,7 @@ #pragma once #include "Renderer.hpp" #include +#include namespace dz { @@ -89,7 +90,12 @@ namespace dz std::pair image_create_descriptor_set(Image* image, uint32_t mip_level = 0); /** - * @brief Gets the Channels and Size Of Type as a pair + * @brief Gets the per channel sizes from a VkFormat + */ + std::vector format_get_channels_size_of_t(VkFormat format); + + /** + * @brief Gets the per channel sizes from an Image */ std::vector image_get_channels_size_of_t(Image* image); @@ -165,4 +171,35 @@ namespace dz * @brief Ends the copy command buffer */ void image_copy_end(); + + /** + * @brief Attempts to serialize an Image + * + * @returns bool value indicating success + */ + bool image_serialize(Image* image_ptr, Serial&); + + /** + * @brief attempts to load an Image from Serial + * + * @returns Image pointer as if created via `image_create` + */ + Image* image_from_serial(Serial&); + + /** + * @brief returns a ImageCreateInfo structure based on the given Image + */ + ImageCreateInfo image_to_info(Image* image_ptr); + + /** + * @brief copies a given mip level into CPU memory + * + * @note must call image_free_copied_data when done using data + */ + void* image_get_data(Image* image_ptr, int mip = 0); + + /** + * @brief unmaps + */ + void image_free_copied_data(void* ptr); } \ No newline at end of file diff --git a/src/Image.cpp b/src/Image.cpp index cd5fb8f4..10281f44 100644 --- a/src/Image.cpp +++ b/src/Image.cpp @@ -57,6 +57,7 @@ namespace dz { .tiling = info.tiling, .memory_properties = info.memory_properties, .multisampling = info.multisampling, + .is_framebuffer_attachment = info.is_framebuffer_attachment, .datas = info.datas, .surfaceType = info.surfaceType, .mip_levels = info.mip_levels @@ -76,6 +77,7 @@ namespace dz { .tiling = info.tiling, .memory_properties = info.memory_properties, .multisampling = info.multisampling, + .is_framebuffer_attachment = info.is_framebuffer_attachment, .datas = info.datas, .surfaceType = info.surfaceType, .mip_levels = info.mip_levels @@ -187,12 +189,10 @@ namespace dz { 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); + image.datas[mip].reset(); } + image_ptr->data_is_cpu_side = false; // Create ImageView image.imageViews.resize(image.mip_levels); @@ -484,13 +484,12 @@ namespace dz { return {layout, descriptorSet}; } - - std::vector image_get_channels_size_of_t(Image* image) - { + + std::vector format_get_channels_size_of_t(VkFormat format) { int channels = 0; float sizeoftype = 0; - switch (image->format) + switch (format) { case VK_FORMAT_R4G4_UNORM_PACK8: channels = 2; @@ -646,6 +645,11 @@ namespace dz { return vec; } + + std::vector image_get_channels_size_of_t(Image* image) + { + return format_get_channels_size_of_t(image->format); + } size_t image_get_sizeof_channels(const std::vector& channels) { float size = 0; @@ -823,4 +827,215 @@ namespace dz { dr.copySrcImages.clear(); dr.copyDstImages.clear(); } + + bool serialize_ImageCreateInfo(Serial& serial, const ImageCreateInfo& info) { + serial << info.width << info.height << info.depth + << info.format << info.usage << info.image_type + << info.view_type << info.tiling << info.memory_properties + << info.multisampling << info.is_framebuffer_attachment + << info.surfaceType << info.mip_levels; + auto channels = format_get_channels_size_of_t(info.format); + auto pixel_stride = image_get_sizeof_channels(channels); + assert(info.datas.size() == info.mip_levels); + auto info_datas_data = info.datas.data(); + auto mip = 0; + for (auto& data_ptr : info.datas) { + uint32_t mipWidth = (std::max)(1u, info.width >> mip); + uint32_t mipHeight = (std::max)(1u, info.height >> mip); + uint32_t mipDepth = (std::max)(1u, info.depth >> mip); + auto mip_byte_size = mipWidth * mipHeight * mipDepth * pixel_stride; + auto& bytes = info_datas_data[mip]; + serial.writeBytes((char*)(bytes.get()), mip_byte_size); + mip++; + } + return true; + } + + ImageCreateInfo deserialize_ImageCreateInfo(Serial& serial) { + ImageCreateInfo info; + serial >> info.width >> info.height >> info.depth + >> info.format >> info.usage >> info.image_type + >> info.view_type >> info.tiling >> info.memory_properties + >> info.multisampling >> info.is_framebuffer_attachment + >> info.surfaceType >> info.mip_levels; + auto channels = format_get_channels_size_of_t(info.format); + auto pixel_stride = image_get_sizeof_channels(channels); + info.datas.resize(info.mip_levels); + auto info_datas_data = info.datas.data(); + auto mip = 0; + for (auto& data_ptr : info.datas) { + uint32_t mipWidth = (std::max)(1u, info.width >> mip); + uint32_t mipHeight = (std::max)(1u, info.height >> mip); + uint32_t mipDepth = (std::max)(1u, info.depth >> mip); + auto mip_byte_size = mipWidth * mipHeight * mipDepth * pixel_stride; + auto& bytes = (info_datas_data[mip] = std::shared_ptr(malloc(mip_byte_size), free)); + serial.readBytes((char*)(bytes.get()), mip_byte_size); + mip++; + } + return info; + } + + bool image_serialize(Image* image_ptr, Serial& serial) { + auto info = image_to_info(image_ptr); + return serialize_ImageCreateInfo(serial, info); + } + + Image* image_from_serial(Serial& serial) { + auto info = deserialize_ImageCreateInfo(serial); + return image_create(info); + } + + ImageCreateInfo image_to_info(Image* image_ptr) { + ImageCreateInfo info{ + .width = image_ptr->width, + .height = image_ptr->height, + .depth = image_ptr->depth, + .format = image_ptr->format, + .usage = image_ptr->usage, + .image_type = image_ptr->image_type, + .view_type = image_ptr->view_type, + .tiling = image_ptr->tiling, + .memory_properties = image_ptr->memory_properties, + .multisampling = image_ptr->multisampling, + .is_framebuffer_attachment = image_ptr->is_framebuffer_attachment, + .surfaceType = image_ptr->surfaceType, + .mip_levels = image_ptr->mip_levels + }; + info.datas.reserve(info.mip_levels); + for (auto mip = 0; mip < info.mip_levels; mip++) { + auto mip_data = image_get_data(image_ptr, mip); + info.datas.push_back(std::shared_ptr(mip_data, image_free_copied_data)); + } + return info; + } + + void* image_get_data(Image* image_ptr, int mip) + { + VkBuffer stagingBuffer = VK_NULL_HANDLE; + VkDeviceMemory stagingBufferMemory = VK_NULL_HANDLE; + VkMemoryRequirements imageMemRequirements; + vkGetImageMemoryRequirements(dr.device, image_ptr->image, &imageMemRequirements); + VkDeviceSize bufferSize = imageMemRequirements.size; + char* imageData = (char*)malloc(bufferSize); + + // 2. Create staging buffer + VkBufferCreateInfo bufferInfo{}; + bufferInfo.sType = VK_STRUCTURE_TYPE_BUFFER_CREATE_INFO; + bufferInfo.size = bufferSize; + bufferInfo.usage = VK_BUFFER_USAGE_TRANSFER_DST_BIT; // We copy from image TO this buffer + bufferInfo.sharingMode = VK_SHARING_MODE_EXCLUSIVE; + vk_check("vkCreateBuffer", vkCreateBuffer(dr.device, &bufferInfo, nullptr, &stagingBuffer)); + + // 3. Allocate memory for staging buffer + VkMemoryRequirements stagingMemRequirements; + vkGetBufferMemoryRequirements(dr.device, stagingBuffer, &stagingMemRequirements); + VkMemoryAllocateInfo allocInfo{}; + allocInfo.sType = VK_STRUCTURE_TYPE_MEMORY_ALLOCATE_INFO; + allocInfo.allocationSize = stagingMemRequirements.size; // Use actual requirements for staging buffer + allocInfo.memoryTypeIndex = find_memory_type(stagingMemRequirements.memoryTypeBits, + VK_MEMORY_PROPERTY_HOST_VISIBLE_BIT | VK_MEMORY_PROPERTY_HOST_COHERENT_BIT); + vk_check("vkAllocateMemory", vkAllocateMemory(dr.device, &allocInfo, nullptr, &stagingBufferMemory)); + + // 4. Bind staging buffer memory + vk_check("vkBindBufferMemory", vkBindBufferMemory(dr.device, stagingBuffer, stagingBufferMemory, 0)); + + // 5. Record and execute copy commands + VkCommandBuffer commandBuffer = begin_single_time_commands(); + + auto aspect_mask = image_get_aspect_mask(image_ptr); + + auto& current_layout = image_ptr->current_layouts[mip]; + auto originalLayout = current_layout; + auto transferLayout = VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL; + + VkImageMemoryBarrier copyBarrier{}; + copyBarrier.sType = VK_STRUCTURE_TYPE_IMAGE_MEMORY_BARRIER; + copyBarrier.oldLayout = current_layout; + copyBarrier.newLayout = transferLayout; + copyBarrier.srcAccessMask = VK_ACCESS_SHADER_READ_BIT | VK_ACCESS_COLOR_ATTACHMENT_WRITE_BIT; + copyBarrier.dstAccessMask = VK_ACCESS_TRANSFER_READ_BIT; + copyBarrier.srcQueueFamilyIndex = VK_QUEUE_FAMILY_IGNORED; + copyBarrier.dstQueueFamilyIndex = VK_QUEUE_FAMILY_IGNORED; + copyBarrier.image = image_ptr->image; + copyBarrier.subresourceRange.aspectMask = aspect_mask; + copyBarrier.subresourceRange.baseMipLevel = mip; + copyBarrier.subresourceRange.levelCount = 1; + copyBarrier.subresourceRange.baseArrayLayer = 0; + copyBarrier.subresourceRange.layerCount = 1; + + vkCmdPipelineBarrier( + commandBuffer, + VK_PIPELINE_STAGE_FRAGMENT_SHADER_BIT | VK_PIPELINE_STAGE_COLOR_ATTACHMENT_OUTPUT_BIT, + VK_PIPELINE_STAGE_TRANSFER_BIT, + 0, + 0, nullptr, + 0, nullptr, + 1, ©Barrier + ); + + current_layout = transferLayout; + + transferLayout = VK_IMAGE_LAYOUT_TRANSFER_SRC_OPTIMAL; + + copyBarrier.oldLayout = current_layout; + copyBarrier.newLayout = transferLayout; + copyBarrier.srcAccessMask = VK_ACCESS_SHADER_READ_BIT | VK_ACCESS_COLOR_ATTACHMENT_WRITE_BIT; + copyBarrier.dstAccessMask = VK_ACCESS_TRANSFER_READ_BIT; + copyBarrier.srcQueueFamilyIndex = VK_QUEUE_FAMILY_IGNORED; + copyBarrier.dstQueueFamilyIndex = VK_QUEUE_FAMILY_IGNORED; + copyBarrier.image = image_ptr->image; + copyBarrier.subresourceRange.aspectMask = aspect_mask; + copyBarrier.subresourceRange.baseMipLevel = mip; + copyBarrier.subresourceRange.levelCount = 1; + copyBarrier.subresourceRange.baseArrayLayer = 0; + copyBarrier.subresourceRange.layerCount = 1; + + vkCmdPipelineBarrier( + commandBuffer, + VK_PIPELINE_STAGE_FRAGMENT_SHADER_BIT | VK_PIPELINE_STAGE_COLOR_ATTACHMENT_OUTPUT_BIT, + VK_PIPELINE_STAGE_TRANSFER_BIT, + 0, + 0, nullptr, + 0, nullptr, + 1, ©Barrier + ); + + current_layout = transferLayout; + + // Setup the copy region for base mip level, all array layers + VkBufferImageCopy region{}; + region.bufferOffset = 0; + region.bufferRowLength = 0; // 0 indicates tightly packed + region.bufferImageHeight = 0; // 0 indicates tightly packed + region.imageSubresource.aspectMask = aspect_mask; + region.imageSubresource.mipLevel = mip; + region.imageSubresource.baseArrayLayer = 0; + region.imageSubresource.layerCount = 1; + region.imageOffset = {0, 0, 0}; + region.imageExtent = {uint32_t(image_ptr->width), uint32_t(image_ptr->height), 1}; + + vkCmdCopyImageToBuffer(commandBuffer, image_ptr->image, VK_IMAGE_LAYOUT_TRANSFER_SRC_OPTIMAL, + stagingBuffer, 1, ®ion); + + + end_single_time_commands(commandBuffer); + commandBuffer = VK_NULL_HANDLE; + + transition_image_layout(image_ptr, originalLayout, mip); + + void* mappedMemory = nullptr; + vk_check("vkMapMemory", vkMapMemory(dr.device, stagingBufferMemory, 0, bufferSize, 0, &mappedMemory)); + + memcpy(imageData, mappedMemory, bufferSize); + + vkUnmapMemory(dr.device, stagingBufferMemory); + vkFreeMemory(dr.device, stagingBufferMemory, 0); + vkDestroyBuffer(dr.device, stagingBuffer, 0); + mappedMemory = nullptr; + return imageData; + } + void image_free_copied_data(void* ptr) + { + free(ptr); + } } \ No newline at end of file diff --git a/src/Image.cpp.hpp b/src/Image.cpp.hpp index 6aa38e4c..04517a81 100644 --- a/src/Image.cpp.hpp +++ b/src/Image.cpp.hpp @@ -20,6 +20,7 @@ namespace dz { VkSampler sampler = VK_NULL_HANDLE; std::vector current_layouts; VkSampleCountFlagBits multisampling; + bool is_framebuffer_attachment = false; std::vector> datas; SurfaceType surfaceType = SurfaceType::BaseColor; uint32_t mip_levels = 1; @@ -46,6 +47,7 @@ namespace dz { VkImageTiling tiling = VK_IMAGE_TILING_OPTIMAL; VkMemoryPropertyFlags memory_properties = VK_MEMORY_PROPERTY_DEVICE_LOCAL_BIT; VkSampleCountFlagBits multisampling = VK_SAMPLE_COUNT_1_BIT; + bool is_framebuffer_attachment = false; std::vector> datas; SurfaceType surfaceType = SurfaceType::BaseColor; uint32_t mip_levels = 1; From b7f8d68905ee5db2e575cfc4e2aed36c2351bd59 Mon Sep 17 00:00:00 2001 From: Steven French Date: Fri, 8 Aug 2025 19:19:20 +1200 Subject: [PATCH 03/13] PBL shader tweaks to PBDL func Change-Id: If68aecfda009a6f2a0c96958c944d4a2d639530d --- include/dz/ECS/PhysicallyBasedLighting.hpp | 52 +++++++++++++++++++--- 1 file changed, 45 insertions(+), 7 deletions(-) diff --git a/include/dz/ECS/PhysicallyBasedLighting.hpp b/include/dz/ECS/PhysicallyBasedLighting.hpp index c7105449..54b8478a 100644 --- a/include/dz/ECS/PhysicallyBasedLighting.hpp +++ b/include/dz/ECS/PhysicallyBasedLighting.hpp @@ -70,9 +70,9 @@ vec3 IBL(vec3 F0, vec3 N, vec3 V) { // --- BRDF LUT and Fresnel --- // clamp / bias coordinates to safe range - float safeNdotV = clamp(lParams.NdotV, 0.0001, 1.0); // avoid exact zero - float safeRoughness = clamp(mParams.roughness, 0.0, 1.0); - vec2 brdfUV = vec2(safeNdotV, safeRoughness); + // float safeNdotV = clamp(lParams.NdotV, 0.0001, 1.0); // avoid exact zero + // float safeRoughness = clamp(mParams.roughness, 0.0, 1.0); + vec2 brdfUV = vec2(lParams.NdotV, mParams.roughness); // Use explicit LOD 0 for BRDF LUT (typical LUT has no mips) vec2 brdf = textureLod(brdfLUT, brdfUV, 0.0).rg; @@ -80,7 +80,7 @@ vec3 IBL(vec3 F0, vec3 N, vec3 V) { // ensure brdf components are sane (clamp to avoid negative / huge values) brdf = clamp(brdf, vec2(0.0), vec2(16.0)); - vec3 F = fresnelSchlickRoughness(F0, safeNdotV, safeRoughness); + vec3 F = fresnelSchlickRoughness(F0, lParams.NdotV, mParams.roughness); // combine vec3 specular = radiance * (F * brdf.x + brdf.y); @@ -96,8 +96,8 @@ vec3 IBL(vec3 F0, vec3 N, vec3 V) { vec3 PBDL(vec3 F0, in Light light) { vec3 result = vec3(0.0); - vec3 Li = -light.direction; - vec3 Lradiance = vec3(light.intensity); + vec3 Li = normalize(-light.direction); + vec3 Lradiance = light.color * (light.intensity / PI); vec3 Lh = normalize(Li + lParams.viewDirection); float cosLi = max(0.0, dot(lParams.normal, Li)); @@ -108,13 +108,51 @@ vec3 PBDL(vec3 F0, in Light light) { float G = gaSchlickGGX(cosLi, lParams.NdotV, mParams.roughness); vec3 kd = (1.0 - F) * (1.0 - mParams.metalness); - vec3 diffuseBRDF = kd * mParams.albedo; + vec3 diffuseBRDF = (kd * mParams.albedo) / (PI * 0.85); vec3 specularBRDF = (F * D * G) / max(Epsilon, 4.0 * cosLi * lParams.NdotV); result += (diffuseBRDF + specularBRDF) * Lradiance * cosLi; return result; + // vec3 result = vec3(0.0); + + // // Light direction vector (pointing from surface to light) + // vec3 L = light.direction;//light.position - lParams.worldPosition; + // // float distance = length(L); + // vec3 Li = normalize(L); + + // // // Attenuation based on distance and range + // // float attenuation = 1.0 / max(distance * distance, Epsilon); // Inverse square attenuation + // // if (light.range > 0.0) + // // { + // // float rangeAtten = clamp(1.0 - (distance * distance) / (light.range * light.range), 0.0, 1.0); + // // attenuation *= rangeAtten; + // // } + + // // Radiance from light color and intensity, apply attenuation + // vec3 Lradiance = light.color * (light.intensity / PI);// * attenuation; + + // // Half vector between light direction and view direction + // vec3 Lh = normalize(Li + lParams.viewDirection); + + // float cosLi = max(0.0, dot(lParams.normal, Li)); + // float cosLh = max(0.0, dot(lParams.normal, Lh)); + + // vec3 F = fresnelSchlick(F0, max(0.0, dot(Lh, lParams.viewDirection))); + // float D = ndfGGX(cosLh, mParams.roughness); + // float G = gaSchlickGGX(cosLi, lParams.NdotV, mParams.roughness); + + // vec3 kd = (1.0 - F) * (1.0 - mParams.metalness); + + // // Diffuse and specular BRDF terms + // vec3 diffuseBRDF = (kd * mParams.albedo) / (PI * 0.85); + // vec3 specularBRDF = (F * D * G) / max(Epsilon, 4.0 * cosLi * lParams.NdotV); + + // // Final light contribution scaled by radiance and angle cosine + // result += (diffuseBRDF + specularBRDF) * Lradiance * cosLi; + + // return result; } vec3 PBL(vec3 F0) { vec3 result = vec3(0.0); From f044c2596464377675a69cf050227507a2ebc91e Mon Sep 17 00:00:00 2001 From: Steven French Date: Fri, 8 Aug 2025 19:19:37 +1200 Subject: [PATCH 04/13] refactor Provider (moved a method) Change-Id: I3e0add44f61007e190e7dbb08e4c30003f81fa8c --- include/dz/ECS/Provider.hpp | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/include/dz/ECS/Provider.hpp b/include/dz/ECS/Provider.hpp index 5f168712..52d3addc 100644 --- a/include/dz/ECS/Provider.hpp +++ b/include/dz/ECS/Provider.hpp @@ -22,6 +22,13 @@ namespace dz { return 0; } + inline static float GetPriority() { + if constexpr (requires { T::Priority; }) { + return T::Priority; + } + return 0.0f; + } + inline static constexpr bool GetIsComponent() { if constexpr (requires { T::IsComponent; }) { return T::IsComponent; @@ -130,13 +137,6 @@ namespace dz { return false; } - inline static float GetPriority() { - if constexpr (requires { T::Priority; }) { - return T::Priority; - } - return 0.0f; - } - inline static const std::string& GetProviderName() { if constexpr (requires { T::ProviderName; }) { return T::ProviderName; From 71e0a40c0220bf4d94ea82d13ccb5003aedf073a Mon Sep 17 00:00:00 2001 From: Steven French Date: Sun, 10 Aug 2025 11:45:15 +1200 Subject: [PATCH 05/13] Serialization: major update to core Provider serialization Change-Id: I8ccb2afb3f7458720da53ba4f21d4b61ef984ab9 --- include/dz/ECS.hpp | 225 ++----- include/dz/ECS/Camera.hpp | 19 +- include/dz/ECS/Entity.hpp | 8 +- include/dz/ECS/GammaCorrection.hpp | 8 +- include/dz/ECS/HDRI.hpp | 682 +++++++++++---------- include/dz/ECS/Light.hpp | 8 +- include/dz/ECS/Material.hpp | 83 ++- include/dz/ECS/Mesh.hpp | 9 +- include/dz/ECS/PhongLighting.hpp | 9 +- include/dz/ECS/PhysicallyBasedLighting.hpp | 9 +- include/dz/ECS/Provider.hpp | 37 +- include/dz/ECS/Scene.hpp | 8 +- include/dz/ECS/SkyBox.hpp | 8 +- include/dz/ECS/SubMesh.hpp | 8 +- include/dz/Reflectable.hpp | 60 +- src/Image.cpp | 14 +- src/Loaders/STB_Image_Loader.cpp | 23 + tests/ECS.cpp | 4 +- 18 files changed, 620 insertions(+), 602 deletions(-) diff --git a/include/dz/ECS.hpp b/include/dz/ECS.hpp index 975f5c51..40ffd8b3 100644 --- a/include/dz/ECS.hpp +++ b/include/dz/ECS.hpp @@ -115,7 +115,14 @@ namespace dz { std::map> prioritized_provider_ids; // ! std::unordered_map>> priority_glsl_mains; // ! - std::vector restricted_keys{ + std::vector buffer_keys{ + VertexPositions_Str, + VertexUV2s_Str, + VertexNormals_Str, + VertexTangents_Str, + VertexBitangents_Str + }; // ! + std::vector image_keys{ AlbedoAtlas_Str, NormalAtlas_Str, RoughnessAtlas_Str, @@ -125,11 +132,6 @@ namespace dz { HDRIAtlas_Str, IrradianceAtlas_Str, RadianceAtlas_Str, - VertexPositions_Str, - VertexUV2s_Str, - VertexNormals_Str, - VertexTangents_Str, - VertexBitangents_Str, brdfLUT_Str }; // ! std::map registered_component_map; // ! @@ -465,6 +467,8 @@ namespace dz { return false; if (!Backup_pid_reflectable_vecs_sizes(serial)) return false; + if (!BackupBuffers(serial)) + return false; if (!BackupGroupVector(serial, reflectable_group_root_vector)) return false; if (!BackupGroupVector(serial, material_group_vector)) @@ -473,11 +477,7 @@ namespace dz { return false; if (!BackupGroupVector(serial, mesh_group_vector)) return false; - if (!BackupBuffers(serial)) - return false; serial << material_browser_open; - if (!BackupAtli(serial)) - return false; return true; } @@ -491,6 +491,8 @@ namespace dz { ReflectableGroup::pid_id_index_maps_ptr = &pid_id_index_maps; if (!Restore_pid_reflectable_vecs_sizes(serial)) return false; + if (!RestoreBuffers(serial)) + return false; if (!RestoreGroupVector(serial, reflectable_group_root_vector, buffer_group)) return false; if (!RestoreGroupVector(serial, material_group_vector, buffer_group)) @@ -501,11 +503,7 @@ namespace dz { return false; if (!EnsureGroupVectorParentPtrs(reflectable_group_root_vector, nullptr)) return false; - if (!RestoreBuffers(serial)) - return false; serial >> material_browser_open; - if (!RestoreAtli(serial)) - return false; UpdateGroupsChildren(); return (loaded_from_io = true); } @@ -577,26 +575,26 @@ namespace dz { bool RestoreBuffers(Serial& serial) { if (!buffer_group) return false; - auto keys_size = restricted_keys.size(); + auto keys_size = buffer_keys.size(); serial >> keys_size; for (size_t key_count = 1; key_count <= keys_size; ++key_count) { - std::string restricted_key; - serial >> restricted_key; + std::string buffer_key; + serial >> buffer_key; uint32_t element_count, element_size; serial >> element_count >> element_size; auto buffer_size = element_count * element_size; - auto r_key_it = std::find(restricted_keys.begin(), restricted_keys.end(), restricted_key); - if (r_key_it == restricted_keys.end()) { + auto r_key_it = std::find(buffer_keys.begin(), buffer_keys.end(), buffer_key); + if (r_key_it == buffer_keys.end()) { auto bytes = (char*)malloc(buffer_size); serial.readBytes((char*)bytes, buffer_size); free(bytes); continue; } - buffer_group_set_buffer_element_count(buffer_group, restricted_key, element_count); - auto actual_element_size = buffer_group_get_buffer_element_size(buffer_group, restricted_key); + buffer_group_set_buffer_element_count(buffer_group, buffer_key, element_count); + auto actual_element_size = buffer_group_get_buffer_element_size(buffer_group, buffer_key); if (actual_element_size != element_size) throw std::runtime_error("Incompatible buffer element sizes"); - auto buffer_ptr = buffer_group_get_buffer_data_ptr(buffer_group, restricted_key); + auto buffer_ptr = buffer_group_get_buffer_data_ptr(buffer_group, buffer_key); serial.readBytes((char*)buffer_ptr.get(), buffer_size); } return true; @@ -605,76 +603,19 @@ namespace dz { bool BackupBuffers(Serial& serial) { if (!buffer_group) return false; - auto keys_size = restricted_keys.size(); + auto keys_size = buffer_keys.size(); serial << keys_size; - for (auto& restricted_key : restricted_keys) { - serial << restricted_key; - auto element_count = buffer_group_get_buffer_element_count(buffer_group, restricted_key); - auto element_size = buffer_group_get_buffer_element_size(buffer_group, restricted_key); + for (auto& buffer_key : buffer_keys) { + serial << buffer_key; + auto element_count = buffer_group_get_buffer_element_count(buffer_group, buffer_key); + auto element_size = buffer_group_get_buffer_element_size(buffer_group, buffer_key); serial << element_count << element_size; - auto buffer_ptr = buffer_group_get_buffer_data_ptr(buffer_group, restricted_key); + auto buffer_ptr = buffer_group_get_buffer_data_ptr(buffer_group, buffer_key); serial.writeBytes((const char*)buffer_ptr.get(), element_count * element_size); } return true; } - bool BackupAtlas(Serial& serial, ImagePack& atlas) { - atlas.getAtlas(); - return image_serialize(atlas.getAtlas(), serial); - } - - bool BackupAtli(Serial& serial) { - if (!BackupAtlas(serial, albedo_atlas_pack)) - return false; - if (!BackupAtlas(serial, normal_atlas_pack)) - return false; - if (!BackupAtlas(serial, roughness_atlas_pack)) - return false; - if (!BackupAtlas(serial, metalness_atlas_pack)) - return false; - if (!BackupAtlas(serial, metalness_roughness_atlas_pack)) - return false; - if (!BackupAtlas(serial, shininess_atlas_pack)) - return false; - if (!BackupAtlas(serial, hdri_atlas_pack)) - return false; - if (!BackupAtlas(serial, irradiance_atlas_pack)) - return false; - if (!BackupAtlas(serial, radiance_atlas_pack)) - return false; - return true; - } - - bool RestoreAtlas(Serial& serial, ImagePack& atlas) { - auto atlas_image = image_from_serial(serial); - if (!atlas_image) - return false; - // atlas.SetAtlasImage(atlas_image); - return true; - } - - bool RestoreAtli(Serial& serial) { - if (!RestoreAtlas(serial, albedo_atlas_pack)) - return false; - if (!RestoreAtlas(serial, normal_atlas_pack)) - return false; - if (!RestoreAtlas(serial, roughness_atlas_pack)) - return false; - if (!RestoreAtlas(serial, metalness_atlas_pack)) - return false; - if (!RestoreAtlas(serial, metalness_roughness_atlas_pack)) - return false; - if (!RestoreAtlas(serial, shininess_atlas_pack)) - return false; - if (!RestoreAtlas(serial, hdri_atlas_pack)) - return false; - if (!RestoreAtlas(serial, irradiance_atlas_pack)) - return false; - if (!RestoreAtlas(serial, radiance_atlas_pack)) - return false; - return true; - } - void RegisterProviders() { (RegisterProvider(), ...); } @@ -715,8 +656,8 @@ namespace dz { provider_group.component_id = int(TProvider::GetComponentID()); RegisterComponent(); } - pid_create_from_serial[pid] = [](auto buffer_group, auto& serial) { - return TProvider::TryMakeGroupFromSerial(buffer_group, serial); + pid_create_from_serial[pid] = [&](auto buffer_group, auto& serial) { + return TProvider::TryMakeGroupFromSerial(*this, buffer_group, serial); }; } @@ -828,7 +769,7 @@ namespace dz { reflectable_group_vector.push_back(group_ptr); - auto& group = *group_ptr; + auto& group = dynamic_cast(*group_ptr); group.cid = pid; group.id = ecs_id; @@ -884,24 +825,22 @@ namespace dz { if (parent_id != -1) { auto& parent_group = GetGenericGroupByID(parent_id); - if constexpr (std::is_same_v) - SetWhoParent(GetEntity(id), &parent_group); - else if constexpr (std::is_same_v) - SetWhoParent(GetCamera(id), &parent_group); - else if constexpr (std::is_same_v) - SetWhoParent(GetScene(id), &parent_group); - else if constexpr (std::is_same_v) - SetWhoParent(GetSubMesh(id), &parent_group); - 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 ( + std::is_same_v || + std::is_same_v || + std::is_same_v || + std::is_same_v || + std::is_same_v || + std::is_same_v + ) { + SetWhoParent(GetProviderData(id), &parent_group); + } } - if constexpr (requires { data.Initialize(*this, group); }) - data.Initialize(*this, group, args...); - else if constexpr (requires { data.Initialize(); }) - data.Initialize(args...); + if constexpr (requires { group.Initialize(*this, data, args...); }) + group.Initialize(*this, data, args...); + else if constexpr (requires { group.Initialize(args...); }) + group.Initialize(args...); group.UpdateChildren(); @@ -988,51 +927,6 @@ namespace dz { return GetProviderData(material_id); } - void SetMaterialImages(size_t material_id, const std::vector images_vec) { - auto& material_group = GetGroupByID(material_id); - - for (auto& image_ptr : images_vec) { - auto frame_ds_pair = image_create_descriptor_set(image_ptr); - auto surfaceType = image_get_surface_type(image_ptr); - switch (surfaceType) { - case SurfaceType::BaseColor: - case SurfaceType::Diffuse: - material_group.albedo_image = image_ptr; - material_group.albedo_frame_image_ds = frame_ds_pair.second; - albedo_atlas_pack.addImage(image_ptr); - break; - case SurfaceType::DiffuseRoughness: - material_group.roughness_image = image_ptr; - material_group.roughness_frame_image_ds = frame_ds_pair.second; - roughness_atlas_pack.addImage(image_ptr); - break; - case SurfaceType::Metalness: - material_group.metalness_image = image_ptr; - material_group.metalness_frame_image_ds = frame_ds_pair.second; - metalness_atlas_pack.addImage(image_ptr); - break; - case SurfaceType::MetalnessRoughness: - material_group.metalness_roughness_image = image_ptr; - material_group.metalness_roughness_frame_image_ds = frame_ds_pair.second; - metalness_roughness_atlas_pack.addImage(image_ptr); - break; - case SurfaceType::Normal: - material_group.normal_image = image_ptr; - material_group.normal_frame_image_ds = frame_ds_pair.second; - normal_atlas_pack.addImage(image_ptr); - break; - case SurfaceType::Shininess: - material_group.shininess_image = image_ptr; - material_group.shininess_frame_image_ds = frame_ds_pair.second; - shininess_atlas_pack.addImage(image_ptr); - break; - } - } - - if (buffer_initialized) - UpdateAtlases(); - } - 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...); @@ -1042,25 +936,6 @@ namespace dz { return GetProviderData(hdri_id); } - 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, @@ -1348,10 +1223,15 @@ namespace dz { BufferGroup* CreateBufferGroup() { auto buffer_group_ptr = buffer_group_create("ECS:Group"); for (auto& [provider_id, provider_group] : pid_provider_groups) { - restricted_keys.push_back(provider_group.buffer_name); + if (provider_group.buffer_host_type != BufferHost::GPU) + continue; + buffer_keys.push_back(provider_group.buffer_name); if (provider_group.is_component) - restricted_keys.push_back(provider_group.sparse_name); + buffer_keys.push_back(provider_group.sparse_name); } + std::vector restricted_keys; + restricted_keys.insert(restricted_keys.end(), buffer_keys.begin(), buffer_keys.end()); + restricted_keys.insert(restricted_keys.end(), image_keys.begin(), image_keys.end()); buffer_group_restrict_to_keys(buffer_group_ptr, restricted_keys); return buffer_group_ptr; } @@ -2102,13 +1982,14 @@ void main() { if (!width || !height) return false; + auto& camera_group = GetGroupByID(camera_id); auto& camera = GetCamera(camera_id); camera.width = width; camera.height = height; - if constexpr (requires { camera.Initialize(); }) - camera.Initialize(); + if constexpr (requires { camera_group.Initialize(*this, camera); }) + camera_group.Initialize(*this, camera); return true; } diff --git a/include/dz/ECS/Camera.hpp b/include/dz/ECS/Camera.hpp index 30749699..edc2bc42 100644 --- a/include/dz/ECS/Camera.hpp +++ b/include/dz/ECS/Camera.hpp @@ -417,18 +417,20 @@ void GetCameraModel(int camera_index, out mat4 out_model, out int parent_index, for (auto& shader : shader_vec) shader_set_render_pass(shader, framebuffer); } - bool backup(Serial& serial) const override { - if (!backup_internal(serial)) - return false; + bool backup_virtual(Serial& serial) const override { serial << name << imgui_name; return true; } - bool restore(Serial& serial) override { - if (!restore_internal(serial)) - return false; + bool restore_virtual(Serial& serial) override { serial >> name >> imgui_name; return true; } + + + template + void Initialize(TECS& ecs, dz::ecs::Camera& camera) { + camera.Initialize(); + } }; using ReflectableGroup = CameraReflectableGroup; @@ -445,11 +447,6 @@ void GetCameraModel(int camera_index, out mat4 out_model, out int parent_index, default: break; } } - - template - void Initialize(TECS& ecs, ::ReflectableGroup& camera_group) { - Initialize(); - } }; } \ No newline at end of file diff --git a/include/dz/ECS/Entity.hpp b/include/dz/ECS/Entity.hpp index 7ed3747b..3c5610a6 100644 --- a/include/dz/ECS/Entity.hpp +++ b/include/dz/ECS/Entity.hpp @@ -185,9 +185,7 @@ void GetEntityModel(int entity_index, out mat4 out_model, out int parent_index, reflectables.push_back(component_reflectable_ptr); } } - bool backup(Serial& serial) const override { - if (!backup_internal(serial)) - return false; + bool backup_virtual(Serial& serial) const override { serial << name; if (!BackupGroupVector(serial, reflectable_children)) return false; @@ -195,9 +193,7 @@ void GetEntityModel(int entity_index, out mat4 out_model, out int parent_index, return false; return true; } - bool restore(Serial& serial) override { - if (!restore_internal(serial)) - return false; + bool restore_virtual(Serial& serial) override { serial >> name; if (!RestoreGroupVector(serial, reflectable_children, buffer_group)) return false; diff --git a/include/dz/ECS/GammaCorrection.hpp b/include/dz/ECS/GammaCorrection.hpp index be4a0e18..5d804a4c 100644 --- a/include/dz/ECS/GammaCorrection.hpp +++ b/include/dz/ECS/GammaCorrection.hpp @@ -41,15 +41,11 @@ vec3 LinearToSRGBAccurate(in vec3 linearColor) std::string& GetName() override { return name; } - bool backup(Serial& serial) const override { - if (!backup_internal(serial)) - return false; + bool backup_virtual(Serial& serial) const override { serial << name; return true; } - bool restore(Serial& serial) override { - if (!restore_internal(serial)) - return false; + bool restore_virtual(Serial& serial) override { serial >> name; return true; } diff --git a/include/dz/ECS/HDRI.hpp b/include/dz/ECS/HDRI.hpp index cc14b402..e5db773b 100644 --- a/include/dz/ECS/HDRI.hpp +++ b/include/dz/ECS/HDRI.hpp @@ -4,6 +4,7 @@ #include "../Reflectable.hpp" #include "../BufferGroup.hpp" #include "../Image.hpp" +#include "../Loaders/STB_Image_Loader.hpp" namespace dz::ecs { @@ -117,6 +118,8 @@ layout(binding = @BINDING@) uniform sampler2D RadianceAtlas; std::string name; std::vector reflectables; + std::filesystem::path hdri_path; + Image* hdri_image = nullptr; VkDescriptorSet hdri_frame_image_ds = VK_NULL_HANDLE; @@ -159,427 +162,448 @@ layout(binding = @BINDING@) uniform sampler2D RadianceAtlas; })); } } - bool backup(Serial& serial) const override { - if (!backup_internal(serial)) - return false; + bool backup_virtual(Serial& serial) const override { serial << name; return true; } - bool restore(Serial& serial) override{ - if (!restore_internal(serial)) - return false; + bool restore_virtual(Serial& serial) override{ serial >> name; return true; } - }; + bool SerializeArgs(Serial& serial) const override { + dz::loaders::STB_Image_Info info; + if (hdri_path.empty()) { + std::shared_ptr bytes; + size_t bytes_length = 0; + info = { + .bytes = bytes, + .bytes_length = bytes_length, + .load_float = true + }; + } + else { + info = { + .path = hdri_path, + .load_float = true + }; + } + serial << info; + return true; + } - using ReflectableGroup = HDRIReflectableGroup; + inline static auto DeserializeArgsToTuple(Serial& serial) { + std::tuple tuple; + serial >> std::get<0>(tuple); + return tuple; + } - 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); + template + void Initialize(TECS& ecs, HDRI& hdri, const dz::loaders::STB_Image_Info& hdri_image_info) { + hdri_path = hdri_image_info.path; - controlBlock.roughness = float(mip) / float(mipLevels - 1); - controlBlock.mip = mip; + hdri_image = dz::loaders::STB_Image_Loader::Load(hdri_image_info); - 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); - } + hdri_frame_image_ds = image_create_descriptor_set(hdri_image).second; + ecs.hdri_atlas_pack.addImage(hdri_image); - // - } + auto hdri_width = image_get_width(hdri_image); + auto hdri_height = image_get_height(hdri_image); - static BufferGroup* GetIrradianceBufferGroup() { - static BufferGroup* buffer_group = nullptr; + // Setup irradiance image + 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 + irradiance_frame_image_ds = image_create_descriptor_set(irradiance_image).second; + ecs.irradiance_atlas_pack.addImage(irradiance_image); - if (buffer_group) - return buffer_group; + // generate irradiance + auto irradianceGenerationShader = GetIrradianceGenerationShader(ecs); + transition_image_layout(irradiance_image, VK_IMAGE_LAYOUT_GENERAL); + shader_dispatch( + irradianceGenerationShader, + ceil(image_get_width(irradiance_image) / 8), + ceil(image_get_height(irradiance_image) / 8), + 1 + ); + transition_image_layout(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; + 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 + radiance_frame_image_ds = image_create_descriptor_set(radiance_image).second; + ecs.radiance_atlas_pack.addImage(radiance_image); + + static RadianceControlBlock controlBlock; + + // generate radiance + auto radianceGenerationShader = GetRadianceGenerationShader(ecs); + for (uint32_t mip = 0; mip < mipLevels; ++mip) + { + transition_image_layout(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(radiance_image, VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL, mip); + } + + // + } - buffer_group = buffer_group_create("IrradianceGroup"); + static BufferGroup* GetIrradianceBufferGroup() { + static BufferGroup* buffer_group = nullptr; - buffer_group_restrict_to_keys(buffer_group, { - "inHDRI", - "outIrradiance" - }); + if (buffer_group) + return buffer_group; - return buffer_group; - } + buffer_group = buffer_group_create("IrradianceGroup"); - static BufferGroup* GetRadianceBufferGroup() { - static BufferGroup* buffer_group = nullptr; + buffer_group_restrict_to_keys(buffer_group, { + "inHDRI", + "outIrradiance" + }); - if (buffer_group) return buffer_group; + } - buffer_group = buffer_group_create("RadianceGroup"); - - buffer_group_restrict_to_keys(buffer_group, { - "inHDRI", - "outRadiance" - }); + static BufferGroup* GetRadianceBufferGroup() { + static BufferGroup* buffer_group = nullptr; - return buffer_group; - } + if (buffer_group) + return buffer_group; - template - static Shader* GetIrradianceGenerationShader(TECS& ecs, HDRIReflectableGroup& hdri_group) { - static Shader* shader = nullptr; + buffer_group = buffer_group_create("RadianceGroup"); - if (shader) { - shader_use_image(shader, "inHDRI", hdri_group.hdri_image); - shader_use_image(shader, "outIrradiance", hdri_group.irradiance_image); + buffer_group_restrict_to_keys(buffer_group, { + "inHDRI", + "outRadiance" + }); - shader_update_descriptor_sets(shader); - return shader; + return buffer_group; } - auto buffer_group = GetIrradianceBufferGroup(); + template + Shader* GetIrradianceGenerationShader(TECS& ecs) { + static Shader* shader = nullptr; - shader = shader_create(); + if (shader) { + shader_use_image(shader, "inHDRI", hdri_image); + shader_use_image(shader, "outIrradiance", irradiance_image); - shader_add_buffer_group(shader, buffer_group); + shader_update_descriptor_sets(shader); + return shader; + } - shader_use_image(shader, "inHDRI", hdri_group.hdri_image); - shader_use_image(shader, "outIrradiance", hdri_group.irradiance_image); + auto buffer_group = GetIrradianceBufferGroup(); - shader_add_module(shader, ShaderModuleType::Compute, GenerateIrradianceGenerationShader(ecs)); + shader = shader_create(); - shader_initialize(shader); + shader_add_buffer_group(shader, buffer_group); - shader_update_descriptor_sets(shader); + shader_use_image(shader, "inHDRI", hdri_image); + shader_use_image(shader, "outIrradiance", irradiance_image); - return shader; - } + shader_add_module(shader, ShaderModuleType::Compute, GenerateIrradianceGenerationShader(ecs)); - 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_initialize(shader); shader_update_descriptor_sets(shader); + return shader; } - auto buffer_group = GetRadianceBufferGroup(); + template + Shader* GetRadianceGenerationShader(TECS& ecs) { + static Shader* shader = nullptr; - shader = shader_create(); + if (shader) { + shader_use_image(shader, "inHDRI", hdri_image); + shader_use_image(shader, "outRadiance", radiance_image); - shader_add_buffer_group(shader, buffer_group); + shader_update_descriptor_sets(shader); + return shader; + } - shader_use_image(shader, "inHDRI", hdri_group.hdri_image); - shader_use_image(shader, "outRadiance", hdri_group.radiance_image); + auto buffer_group = GetRadianceBufferGroup(); - shader_add_module(shader, ShaderModuleType::Compute, GenerateRadianceGenerationShader(ecs)); + shader = shader_create(); - shader_initialize(shader); + shader_add_buffer_group(shader, buffer_group); - shader_update_descriptor_sets(shader); + shader_use_image(shader, "inHDRI", hdri_image); + shader_use_image(shader, "outRadiance", radiance_image); - return shader; - } + shader_add_module(shader, ShaderModuleType::Compute, GenerateRadianceGenerationShader(ecs)); - template - static std::string GenerateIrradianceGenerationShader(TECS& ecs) { - std::string shader_string; + shader_initialize(shader); - shader_string += R"( -#version 450 core + shader_update_descriptor_sets(shader); -layout (local_size_x = 8, local_size_y = 8, local_size_z = 1) in; + return shader; + } -const float PI = 3.14159265359; + template + static std::string GenerateIrradianceGenerationShader(TECS& ecs) { + std::string shader_string; -layout(binding = 0) uniform sampler2D inHDRI; -layout(binding = 1) uniform writeonly image2D outIrradiance; + shader_string += R"( + #version 450 core -// 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)); -} + layout (local_size_x = 8, local_size_y = 8, local_size_z = 1) in; -// 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; -} + const float PI = 3.14159265359; -// 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); -} + layout(binding = 0) uniform sampler2D inHDRI; + layout(binding = 1) uniform writeonly image2D outIrradiance; -// 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); -} + // 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; + } -void main() -{ - ivec2 size = imageSize(outIrradiance); - ivec2 pix = ivec2(gl_GlobalInvocationID.xy); + // 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); + } - if (pix.x >= size.x || pix.y >= size.y) - return; + // 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); + } - vec2 uv = (vec2(pix) + 0.5) / vec2(size); // Center of texel - vec3 N = DirectionFromEquirect(uv); // Surface normal + void main() + { + ivec2 size = imageSize(outIrradiance); + ivec2 pix = ivec2(gl_GlobalInvocationID.xy); - vec3 T, B; - CreateTangentSpace(N, T, B); + if (pix.x >= size.x || pix.y >= size.y) + return; - const uint SAMPLE_COUNT = 128u; - vec3 irradiance = vec3(0.0); - float weight = 0.0; + vec2 uv = (vec2(pix) + 0.5) / vec2(size); // Center of texel + vec3 N = DirectionFromEquirect(uv); // Surface normal - 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; - } + vec3 T, B; + CreateTangentSpace(N, T, B); - irradiance = (PI * irradiance) / max(weight, 0.0001); - imageStore(outIrradiance, pix, vec4(irradiance, 1.0)); -} -)"; + const uint SAMPLE_COUNT = 128u; + vec3 irradiance = vec3(0.0); + float weight = 0.0; - return shader_string; - } + 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 + ); - template - static std::string GenerateRadianceGenerationShader(TECS& ecs) { - std::string shader_string; + vec2 sampleUV = SampleSphericalMap(worldSample); + sampleUV.y = -sampleUV.y; + vec3 sampleColor = texture(inHDRI, sampleUV).rgb; - shader_string += R"( -#version 450 core + irradiance += sampleColor * sampleVec.y; // * cosine + weight += sampleVec.y; + } -#extension GL_EXT_nonuniform_qualifier : require + irradiance = (PI * irradiance) / max(weight, 0.0001); + imageStore(outIrradiance, pix, vec4(irradiance, 1.0)); + } + )"; -layout (local_size_x = 8, local_size_y = 8) in; + return shader_string; + } -layout (binding = 0) uniform sampler2D inHDRI; -layout (binding = 1) writeonly uniform image2D outRadiance[]; -layout (push_constant) uniform PushConstants { - float roughness; - int mip; -} push; + template + static std::string GenerateRadianceGenerationShader(TECS& ecs) { + std::string shader_string; -const float PI = 3.14159265359; + shader_string += R"( + #version 450 core -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; -} + #extension GL_EXT_nonuniform_qualifier : require -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; -} + layout (local_size_x = 8, local_size_y = 8) in; -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; + layout (binding = 0) uniform sampler2D inHDRI; + layout (binding = 1) writeonly uniform image2D outRadiance[]; + layout (push_constant) uniform PushConstants { + float roughness; + int mip; + } push; - float denom = NoH2 * (a2 - 1.0) + 1.0; - return a2 / (PI * denom * denom + 1e-5); -} + 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; + } -vec3 ImportanceSampleGGX(float u1, float u2, float roughness, vec3 N) -{ - float a = roughness * roughness; + 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 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); + 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; - vec3 H; - H.x = cos(phi) * sinTheta; - H.y = cosTheta; - H.z = sin(phi) * sinTheta; + float denom = NoH2 * (a2 - 1.0) + 1.0; + return a2 / (PI * denom * denom + 1e-5); + } - 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); + vec3 ImportanceSampleGGX(float u1, float u2, float roughness, vec3 N) + { + float a = roughness * roughness; - return normalize(T * H.x + N * H.y + B * H.z); -} + 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); -void main() -{ - ivec2 size = imageSize(outRadiance[push.mip]); - ivec2 pix = ivec2(gl_GlobalInvocationID.xy); - if (pix.x >= size.x || pix.y >= size.y) - return; + vec3 H; + H.x = cos(phi) * sinTheta; + H.y = cosTheta; + H.z = sin(phi) * sinTheta; - vec2 uv = (vec2(pix) + 0.5) / vec2(size); - vec3 R = DirectionFromEquirect(uv); - vec3 N = R; + 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); - const uint SAMPLE_COUNT = 1024u; - vec3 prefilteredColor = vec3(0.0); - float totalWeight = 0.0; + return normalize(T * H.x + N * H.y + B * H.z); + } - for (uint i = 0u; i < SAMPLE_COUNT; ++i) + void main() { - float u1 = float(i) / float(SAMPLE_COUNT); - float u2 = fract(sin(float(i) * 12.9898) * 43758.5453); + 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; - vec3 H = ImportanceSampleGGX(u1, u2, push.roughness, N); - vec3 L = normalize(reflect(-R, H)); - float NoL = max(dot(N, L), 0.0); + const uint SAMPLE_COUNT = 1024u; + vec3 prefilteredColor = vec3(0.0); + float totalWeight = 0.0; - if (NoL > 0.0) + for (uint i = 0u; i < SAMPLE_COUNT; ++i) { - vec2 sampleUV = SampleSphericalMap(L); - vec3 sampleColor = texture(inHDRI, sampleUV).rgb; + float u1 = float(i) / float(SAMPLE_COUNT); + float u2 = fract(sin(float(i) * 12.9898) * 43758.5453); - 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); + vec3 H = ImportanceSampleGGX(u1, u2, push.roughness, N); + vec3 L = normalize(reflect(-R, H)); + float NoL = max(dot(N, L), 0.0); - prefilteredColor += sampleColor * NoL * omegaS; - totalWeight += NoL * omegaS; + 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)); } + )"; - prefilteredColor = prefilteredColor / max(totalWeight, 1e-5); - imageStore(outRadiance[push.mip], pix, vec4(prefilteredColor, 1.0)); -} -)"; + return shader_string; + } - return shader_string; - } + }; + + using ReflectableGroup = HDRIReflectableGroup; }; } \ No newline at end of file diff --git a/include/dz/ECS/Light.hpp b/include/dz/ECS/Light.hpp index 9c4cee4d..9fe62671 100644 --- a/include/dz/ECS/Light.hpp +++ b/include/dz/ECS/Light.hpp @@ -164,15 +164,11 @@ LightingParams lParams; })); } } - bool backup(Serial& serial) const override { - if (!backup_internal(serial)) - return false; + bool backup_virtual(Serial& serial) const override { serial << name; return true; } - bool restore(Serial& serial) override { - if (!restore_internal(serial)) - return false; + bool restore_virtual(Serial& serial) override { serial >> name; return true; } diff --git a/include/dz/ECS/Material.hpp b/include/dz/ECS/Material.hpp index 4b06c5cd..34089968 100644 --- a/include/dz/ECS/Material.hpp +++ b/include/dz/ECS/Material.hpp @@ -247,19 +247,90 @@ layout(binding = @BINDING@) uniform sampler2D brdfLUT; })); } } - bool backup(Serial& serial) const override { - if (!backup_internal(serial)) - return false; + bool backup_virtual(Serial& serial) const override { serial << name; return true; } - bool restore(Serial& serial) override{ - if (!restore_internal(serial)) - return false; + bool restore_virtual(Serial& serial) override{ serial >> name; return true; } + bool SerializeArgs(Serial& serial) const override { + static auto add_image_ptr = [](auto& images_vec, auto image_ptr) { + if (image_ptr) + images_vec.push_back(image_ptr); + }; + std::vector images_vec; + add_image_ptr(images_vec, albedo_image); + add_image_ptr(images_vec, normal_image); + add_image_ptr(images_vec, metalness_roughness_image); + add_image_ptr(images_vec, roughness_image); + add_image_ptr(images_vec, metalness_image); + add_image_ptr(images_vec, shininess_image); + auto images_vec_size = images_vec.size(); + serial << images_vec_size; + auto images_vec_data = images_vec.data(); + for (size_t i = 0; i < images_vec_size; i++) { + image_serialize(images_vec_data[i], serial); + } + return true; + } + + inline static auto DeserializeArgsToTuple(Serial& serial) { + std::tuple> tuple; + auto& images_vec = std::get<0>(tuple); + auto images_vec_size = images_vec.size(); + serial >> images_vec_size; + images_vec.resize(images_vec_size); + auto images_vec_data = images_vec.data(); + for (size_t i = 0 ; i < images_vec_size; ++i) { + images_vec_data[i] = image_from_serial(serial); + } + return tuple; + } + + template + void Initialize(TECS& ecs, Material& material, const std::vector images_vec) { + for (auto& image_ptr : images_vec) { + auto frame_ds_pair = image_create_descriptor_set(image_ptr); + auto surfaceType = image_get_surface_type(image_ptr); + switch (surfaceType) { + case SurfaceType::BaseColor: + case SurfaceType::Diffuse: + albedo_image = image_ptr; + albedo_frame_image_ds = frame_ds_pair.second; + ecs.albedo_atlas_pack.addImage(image_ptr); + break; + case SurfaceType::DiffuseRoughness: + roughness_image = image_ptr; + roughness_frame_image_ds = frame_ds_pair.second; + ecs.roughness_atlas_pack.addImage(image_ptr); + break; + case SurfaceType::Metalness: + metalness_image = image_ptr; + metalness_frame_image_ds = frame_ds_pair.second; + ecs.metalness_atlas_pack.addImage(image_ptr); + break; + case SurfaceType::MetalnessRoughness: + metalness_roughness_image = image_ptr; + metalness_roughness_frame_image_ds = frame_ds_pair.second; + ecs.metalness_roughness_atlas_pack.addImage(image_ptr); + break; + case SurfaceType::Normal: + normal_image = image_ptr; + normal_frame_image_ds = frame_ds_pair.second; + ecs.normal_atlas_pack.addImage(image_ptr); + break; + case SurfaceType::Shininess: + shininess_image = image_ptr; + shininess_frame_image_ds = frame_ds_pair.second; + ecs.shininess_atlas_pack.addImage(image_ptr); + break; + } + } + } + }; using ReflectableGroup = MaterialReflectableGroup; diff --git a/include/dz/ECS/Mesh.hpp b/include/dz/ECS/Mesh.hpp index 99260f0f..f67ed012 100644 --- a/include/dz/ECS/Mesh.hpp +++ b/include/dz/ECS/Mesh.hpp @@ -123,7 +123,6 @@ vec3 GetMeshBitangent(in Mesh mesh) { std::vector reflectables; std::vector> reflectable_children; int material_index = -1; - Image* image = nullptr; MeshReflectableGroup(BufferGroup* buffer_group): buffer_group(buffer_group), name("Mesh") @@ -160,17 +159,13 @@ vec3 GetMeshBitangent(in Mesh mesh) { })); } } - bool backup(Serial& serial) const override { - if (!backup_internal(serial)) - return false; + bool backup_virtual(Serial& serial) const override { serial << name << material_index; if (!BackupGroupVector(serial, reflectable_children)) return false; return true; } - bool restore(Serial& serial) override{ - if (!restore_internal(serial)) - return false; + bool restore_virtual(Serial& serial) override{ serial >> name >> material_index; if (!RestoreGroupVector(serial, reflectable_children, buffer_group)) return false; diff --git a/include/dz/ECS/PhongLighting.hpp b/include/dz/ECS/PhongLighting.hpp index 799c9814..9a1d7f87 100644 --- a/include/dz/ECS/PhongLighting.hpp +++ b/include/dz/ECS/PhongLighting.hpp @@ -7,6 +7,7 @@ namespace dz::ecs { struct PhongLighting : Provider { inline static constexpr size_t PID = 8; inline static float Priority = 4.0f; + inline static constexpr BufferHost BufferHostType = BufferHost::NoBuffer; inline static std::string ProviderName = "PhongLighting"; inline static std::string StructName = "PhongLighting"; inline static std::unordered_map GLSLMethods = { @@ -74,15 +75,11 @@ vec3 CalculatePhongLighting(in Light light) { std::string& GetName() override { return name; } - bool backup(Serial& serial) const override { - if (!backup_internal(serial)) - return false; + bool backup_virtual(Serial& serial) const override { serial << name; return true; } - bool restore(Serial& serial) override { - if (!restore_internal(serial)) - return false; + bool restore_virtual(Serial& serial) override { serial >> name; return true; } diff --git a/include/dz/ECS/PhysicallyBasedLighting.hpp b/include/dz/ECS/PhysicallyBasedLighting.hpp index 54b8478a..6f7f106a 100644 --- a/include/dz/ECS/PhysicallyBasedLighting.hpp +++ b/include/dz/ECS/PhysicallyBasedLighting.hpp @@ -7,6 +7,7 @@ namespace dz::ecs { struct PhysicallyBasedLighting : Provider { inline static constexpr size_t PID = 9; inline static float Priority = 4.0f; + inline static constexpr BufferHost BufferHostType = BufferHost::NoBuffer; inline static std::string ProviderName = "PhysicallyBasedLighting"; inline static std::string StructName = "PhysicallyBasedLighting"; inline static std::unordered_map GLSLMethods = { @@ -206,15 +207,11 @@ vec3 PBL(vec3 F0) { std::string& GetName() override { return name; } - bool backup(Serial& serial) const override { - if (!backup_internal(serial)) - return false; + bool backup_virtual(Serial& serial) const override { serial << name; return true; } - bool restore(Serial& serial) override { - if (!restore_internal(serial)) - return false; + bool restore_virtual(Serial& serial) override { serial >> name; return true; } diff --git a/include/dz/ECS/Provider.hpp b/include/dz/ECS/Provider.hpp index 52d3addc..51c44f17 100644 --- a/include/dz/ECS/Provider.hpp +++ b/include/dz/ECS/Provider.hpp @@ -2,6 +2,7 @@ #include #include #include +#include #include namespace dz { @@ -13,6 +14,19 @@ namespace dz { GPU }; + template + struct HasInitializeTuple; + + template + struct HasInitializeTuple> + { + static constexpr bool value = requires(Group& g, TECS& ecs, Data& data, Args&&... args) + { + g.Initialize(ecs, data, std::forward(args)...); + }; + }; + + template struct Provider { inline static constexpr size_t GetPID() { @@ -194,8 +208,27 @@ namespace dz { return std::make_shared(buffer_group); } - inline static std::shared_ptr TryMakeGroupFromSerial(BufferGroup* buffer_group, Serial& serial) { - return std::make_shared(buffer_group, serial); + template + inline static std::shared_ptr TryMakeGroupFromSerial(TECS& ecs, BufferGroup* buffer_group, Serial& serial) { + auto group_sh_ptr = std::make_shared(buffer_group, serial); + auto args = T::ReflectableGroup::RunDeserializeArgsToTuple(serial); + auto& group = *group_sh_ptr; + auto& data = ecs.template GetProviderData(group.id); + + using ArgsTuple = decltype(args); + + if constexpr (HasInitializeTuple::value) + { + std::apply( + [&](auto&&... unpackedArgs) + { + group.Initialize(ecs, data, std::forward(unpackedArgs)...); + }, + args + ); + } + + return group_sh_ptr; } }; diff --git a/include/dz/ECS/Scene.hpp b/include/dz/ECS/Scene.hpp index d936c0a2..b1340615 100644 --- a/include/dz/ECS/Scene.hpp +++ b/include/dz/ECS/Scene.hpp @@ -164,17 +164,13 @@ void GetSceneModel(int scene_index, out mat4 out_model, out int parent_index, ou })); } } - bool backup(Serial& serial) const override { - if (!backup_internal(serial)) - return false; + bool backup_virtual(Serial& serial) const override { serial << name; if (!BackupGroupVector(serial, reflectable_children)) return false; return true; } - bool restore(Serial& serial) override{ - if (!restore_internal(serial)) - return false; + bool restore_virtual(Serial& serial) override{ serial >> name; if (!RestoreGroupVector(serial, reflectable_children, buffer_group)) return false; diff --git a/include/dz/ECS/SkyBox.hpp b/include/dz/ECS/SkyBox.hpp index 0c622943..6c714769 100644 --- a/include/dz/ECS/SkyBox.hpp +++ b/include/dz/ECS/SkyBox.hpp @@ -43,15 +43,11 @@ struct SkyBox { std::string& GetName() override { return name; } - bool backup(Serial& serial) const override { - if (!backup_internal(serial)) - return false; + bool backup_virtual(Serial& serial) const override { serial << name; return true; } - bool restore(Serial& serial) override { - if (!restore_internal(serial)) - return false; + bool restore_virtual(Serial& serial) override { serial >> name; return true; } diff --git a/include/dz/ECS/SubMesh.hpp b/include/dz/ECS/SubMesh.hpp index 637ab541..71dbcebe 100644 --- a/include/dz/ECS/SubMesh.hpp +++ b/include/dz/ECS/SubMesh.hpp @@ -111,17 +111,13 @@ struct SubMesh { })); } } - bool backup(Serial& serial) const override { - if (!backup_internal(serial)) - return false; + bool backup_virtual(Serial& serial) const override { serial << name; if (!BackupGroupVector(serial, reflectable_children)) return false; return true; } - bool restore(Serial& serial) override{ - if (!restore_internal(serial)) - return false; + bool restore_virtual(Serial& serial) override{ serial >> name; if (!RestoreGroupVector(serial, reflectable_children, buffer_group)) return false; diff --git a/include/dz/Reflectable.hpp b/include/dz/Reflectable.hpp index 7f2dda9a..83f9566e 100644 --- a/include/dz/Reflectable.hpp +++ b/include/dz/Reflectable.hpp @@ -10,6 +10,8 @@ #include "GlobalUID.hpp" #include +#define SRL_CHK(CALL) if (!CALL) return false + namespace dz { struct BufferGroup; } @@ -90,50 +92,62 @@ struct ReflectableGroup { return dummy_children; } virtual void UpdateChildren() {} - bool backup_internal(Serial& serial) const { + bool backup(Serial& serial) const { serial << cid << disabled << id << index << is_child << parent_id << tree_node_open; + SRL_CHK(backup_virtual(serial)); + SRL_CHK(SerializeArgs(serial)); return true; } - virtual bool backup(Serial& serial) const { - if (!backup_internal(serial)) - return false; + virtual bool backup_virtual(Serial& serial) const { return true; } - bool restore_internal(Serial& serial) { + bool restore(Serial& serial) { serial >> cid >> disabled >> id >> index >> is_child >> parent_id >> tree_node_open; + SRL_CHK(restore_virtual(serial)); return true; } - virtual bool restore(Serial& serial) { - if (!restore_internal(serial)) - return false; + virtual bool restore_virtual(Serial& serial) { return true; } + bool IsDescendantOfRecurse(ReflectableGroup* current) + { + for (auto& child_ptr : current->GetChildren()) + { + ReflectableGroup* child = child_ptr.get(); + if (child == this) + return true; + if (IsDescendantOfRecurse(child)) + return true; + } + return false; + } + bool IsDescendantOf(ReflectableGroup* other) { if (other == nullptr || other == this) return false; - std::function recurse; - recurse = [&](ReflectableGroup* current) -> bool - { - for (auto& child_ptr : current->GetChildren()) - { - ReflectableGroup* child = child_ptr.get(); - if (child == this) - return true; - if (recurse(child)) - return true; - } - return false; - }; - - return recurse(other); + return IsDescendantOfRecurse(other); } inline static std::recursive_mutex cid_restore_mutex = {}; inline static std::unordered_map(dz::BufferGroup*, Serial&)>>* cid_restore_map_ptr = nullptr; inline static std::map>* pid_reflectable_vecs_ptr = nullptr; inline static std::unordered_map>* pid_id_index_maps_ptr = nullptr; + + virtual bool SerializeArgs(Serial& serial) const { + return true; + } + + template + inline static auto RunDeserializeArgsToTuple(Serial& serial) { + if constexpr (requires { TRG::DeserializeArgsToTuple(serial); }) { + return TRG::DeserializeArgsToTuple(serial); + } + else { + return std::tuple<>{}; + } + } }; bool BackupGroupVector(Serial& serial, const std::vector>& group_vector); diff --git a/src/Image.cpp b/src/Image.cpp index 10281f44..7904b240 100644 --- a/src/Image.cpp +++ b/src/Image.cpp @@ -868,19 +868,29 @@ namespace dz { uint32_t mipHeight = (std::max)(1u, info.height >> mip); uint32_t mipDepth = (std::max)(1u, info.depth >> mip); auto mip_byte_size = mipWidth * mipHeight * mipDepth * pixel_stride; - auto& bytes = (info_datas_data[mip] = std::shared_ptr(malloc(mip_byte_size), free)); - serial.readBytes((char*)(bytes.get()), mip_byte_size); + auto compressed_bytes = std::shared_ptr(malloc(mip_byte_size), free); + serial.readBytes((char*)(compressed_bytes.get()), mip_byte_size); + // use info.format and mip sizes to determine parameters to pass to stbi_load + info_datas_data[mip] = compressed_bytes; mip++; } return info; } bool image_serialize(Image* image_ptr, Serial& serial) { + bool valid_image = image_ptr; + serial << valid_image; + if (!valid_image) + return true; auto info = image_to_info(image_ptr); return serialize_ImageCreateInfo(serial, info); } Image* image_from_serial(Serial& serial) { + bool valid_image = false; + serial >> valid_image; + if (!valid_image) + return nullptr; auto info = deserialize_ImageCreateInfo(serial); return image_create(info); } diff --git a/src/Loaders/STB_Image_Loader.cpp b/src/Loaders/STB_Image_Loader.cpp index 5bb5a33a..26969fda 100644 --- a/src/Loaders/STB_Image_Loader.cpp +++ b/src/Loaders/STB_Image_Loader.cpp @@ -106,4 +106,27 @@ dz::Image* dz::loaders::STB_Image_Loader::Load(const dz::loaders::STB_Image_Info if (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!"); +} + +template <> +Serial& deserialize(Serial& serial, dz::loaders::STB_Image_Info& info) +{ + serial >> info.load_float; + serial >> info.path; + serial >> info.bytes_length; + if (info.bytes_length) { + info.bytes = std::shared_ptr((char*)malloc(info.bytes_length), free); + serial.readBytes(info.bytes.get(), info.bytes_length); + } + return serial; +} +template<> +Serial& serialize(Serial& serial, const dz::loaders::STB_Image_Info& info) +{ + serial << info.load_float; + serial << info.path; + serial << info.bytes_length; + if (info.bytes_length) + serial.writeBytes(info.bytes.get(), info.bytes_length); + return serial; } \ No newline at end of file diff --git a/tests/ECS.cpp b/tests/ECS.cpp index bc1ace4d..52e2f4cd 100644 --- a/tests/ECS.cpp +++ b/tests/ECS.cpp @@ -146,8 +146,7 @@ int main() { .albedo_color = albedo_color, .metalness = metalness, .roughness = roughness - }, out_index, name); - ecs.SetMaterialImages(out_id, images_vec); + }, out_index, name, images_vec); return {out_id, out_index}; } }; @@ -224,6 +223,7 @@ int main() { if (!free_state()) std::cerr << "Failed to Free State" << std::endl; } + ecs_ptr.reset(); }); auto& imgui = get_ImGuiLayer(); From c240c883de5f92b39020852f434c5e82947bfe5c Mon Sep 17 00:00:00 2001 From: Steven French Date: Sun, 10 Aug 2025 11:45:55 +1200 Subject: [PATCH 06/13] PBL: sample brdf with 1.0 - rough Change-Id: I9c435c721688383b2f8f8208c526b7c4b1265a73 --- include/dz/ECS/PhysicallyBasedLighting.hpp | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/include/dz/ECS/PhysicallyBasedLighting.hpp b/include/dz/ECS/PhysicallyBasedLighting.hpp index 6f7f106a..c057be71 100644 --- a/include/dz/ECS/PhysicallyBasedLighting.hpp +++ b/include/dz/ECS/PhysicallyBasedLighting.hpp @@ -71,9 +71,7 @@ vec3 IBL(vec3 F0, vec3 N, vec3 V) { // --- BRDF LUT and Fresnel --- // clamp / bias coordinates to safe range - // float safeNdotV = clamp(lParams.NdotV, 0.0001, 1.0); // avoid exact zero - // float safeRoughness = clamp(mParams.roughness, 0.0, 1.0); - vec2 brdfUV = vec2(lParams.NdotV, mParams.roughness); + vec2 brdfUV = vec2(lParams.NdotV, 1.0 - mParams.roughness); // Use explicit LOD 0 for BRDF LUT (typical LUT has no mips) vec2 brdf = textureLod(brdfLUT, brdfUV, 0.0).rg; From bf5cf5a1758802e6f3fcf47bdfa53e4c1f0ce09e Mon Sep 17 00:00:00 2001 From: Steven French Date: Sun, 10 Aug 2025 11:46:19 +1200 Subject: [PATCH 07/13] image: use mip_sizes everywhere Change-Id: I9cb3f3ccbd7801d25eeae4253fc8af02d623a1c5 --- src/Image.cpp | 10 ++++++++-- 1 file changed, 8 insertions(+), 2 deletions(-) diff --git a/src/Image.cpp b/src/Image.cpp index 7904b240..aeae0028 100644 --- a/src/Image.cpp +++ b/src/Image.cpp @@ -244,7 +244,8 @@ namespace dz { auto pixel_stride = image_get_sizeof_channels(channels); 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; + uint32_t mipDepth = (std::max)(1u, image.depth >> mip); + auto image_size = mipWidth * mipHeight * mipDepth * pixel_stride; auto& ptr = image.datas[mip]; ptr = std::shared_ptr((char*)malloc(image_size), free); memset(ptr.get(), 0, image_size); @@ -845,6 +846,7 @@ namespace dz { uint32_t mipDepth = (std::max)(1u, info.depth >> mip); auto mip_byte_size = mipWidth * mipHeight * mipDepth * pixel_stride; auto& bytes = info_datas_data[mip]; + // use info.format and mip sizes to determine parameters to pass to stbi_write serial.writeBytes((char*)(bytes.get()), mip_byte_size); mip++; } @@ -1022,7 +1024,11 @@ namespace dz { region.imageSubresource.baseArrayLayer = 0; region.imageSubresource.layerCount = 1; region.imageOffset = {0, 0, 0}; - region.imageExtent = {uint32_t(image_ptr->width), uint32_t(image_ptr->height), 1}; + + uint32_t mip_width = std::max(1u, image_ptr->width >> mip); + uint32_t mip_height = std::max(1u, image_ptr->height >> mip); + uint32_t mip_depth = std::max(1u, image_ptr->depth >> mip); + region.imageExtent = {mip_width, mip_height, mip_depth}; vkCmdCopyImageToBuffer(commandBuffer, image_ptr->image, VK_IMAGE_LAYOUT_TRANSFER_SRC_OPTIMAL, stagingBuffer, 1, ®ion); From 27e19a81171139531ec1d953f099ead3d8b9792d Mon Sep 17 00:00:00 2001 From: Steven French Date: Sun, 10 Aug 2025 11:46:40 +1200 Subject: [PATCH 08/13] main_viewport: PlatformWindowCreated = false Change-Id: I03bea25e45d41a829e582d547ce60d88ce20fe2a --- src/Window.cpp | 1 + 1 file changed, 1 insertion(+) diff --git a/src/Window.cpp b/src/Window.cpp index 2dfd5de4..abdacfe9 100644 --- a/src/Window.cpp +++ b/src/Window.cpp @@ -132,6 +132,7 @@ namespace dz { main_viewport->Size.x = width; main_viewport->Size.y = height; window->imguiViewport = main_viewport; + main_viewport->PlatformWindowCreated = false; #ifdef _WIN32 dr.hwnd_root = window->hwnd; #endif From 16585c7030fec3f925a9aa4c5af90d9149c5a059 Mon Sep 17 00:00:00 2001 From: Steven French Date: Sun, 10 Aug 2025 11:47:07 +1200 Subject: [PATCH 09/13] ECS-Test: call addHDRI with STB_Image_Info Change-Id: Ic1e4f7cd17918f3b2b2e4f675a94104425bd540d --- tests/ECS.cpp | 14 ++++++-------- 1 file changed, 6 insertions(+), 8 deletions(-) diff --git a/tests/ECS.cpp b/tests/ECS.cpp index 52e2f4cd..a8d2710e 100644 --- a/tests/ECS.cpp +++ b/tests/ECS.cpp @@ -102,18 +102,16 @@ int main() { if (!state_loaded) { 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", + auto autumn_hdri_id = ecs.AddHDRI(HDRI{}, autumn_hdri_index, "hdri/autumn_field_puresky_4k", STB_Image_Info{ + .path = "hdri/zwartkops_straight_afternoon_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", + auto afternoon_hdri_id = ecs.AddHDRI(HDRI{}, afternoon_hdri_index, "zwartkops_straight_afternoon_4k", STB_Image_Info{ + .path = "hdri/autumn_field_puresky_4k.hdr", .load_float = true - })); + }); auto sky_scene_id = ecs.AddScene(Scene{}, "Sky Scene"); From 56cbc19f2fd5a95ad9577c250fcb86a5a45d5a22 Mon Sep 17 00:00:00 2001 From: Steven French Date: Sun, 10 Aug 2025 13:32:28 +1200 Subject: [PATCH 10/13] Provider: typename T::... Change-Id: Id843a7d1e1dd31f04e1625dd64f2cd6dc28dd80c --- include/dz/ECS/Provider.hpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/include/dz/ECS/Provider.hpp b/include/dz/ECS/Provider.hpp index 51c44f17..84cf5088 100644 --- a/include/dz/ECS/Provider.hpp +++ b/include/dz/ECS/Provider.hpp @@ -211,7 +211,7 @@ namespace dz { template inline static std::shared_ptr TryMakeGroupFromSerial(TECS& ecs, BufferGroup* buffer_group, Serial& serial) { auto group_sh_ptr = std::make_shared(buffer_group, serial); - auto args = T::ReflectableGroup::RunDeserializeArgsToTuple(serial); + auto args = T::ReflectableGroup::RunDeserializeArgsToTuple(serial); auto& group = *group_sh_ptr; auto& data = ecs.template GetProviderData(group.id); From 2efa8673bdb5fb899d5dda46f9099c91258d27a3 Mon Sep 17 00:00:00 2001 From: Steven French Date: Sun, 10 Aug 2025 13:33:33 +1200 Subject: [PATCH 11/13] Util: update wstr conversions to use non-deprecated code Change-Id: I9b4d012eefe1cb4aca174c036bd1dea52a4b08ae --- include/dz/Util.hpp | 27 ++++++++++++++++++++++++--- 1 file changed, 24 insertions(+), 3 deletions(-) diff --git a/include/dz/Util.hpp b/include/dz/Util.hpp index dd971ca0..0dc30530 100644 --- a/include/dz/Util.hpp +++ b/include/dz/Util.hpp @@ -1,7 +1,8 @@ #pragma once #include +#include +#include #include -#include namespace dz { inline static std::string to_lower(const std::string& str) { @@ -13,8 +14,28 @@ namespace dz { return nstr; } + inline static std::string to_upper(const std::string& str) { + std::string nstr(str.size(), 0); + size_t i = 0; + for (auto& elem : str) { + nstr[i++] = std::toupper(elem); + } + return nstr; + } + inline static std::wstring string_to_wstring(const std::string& str) { - static std::wstring_convert> converter = {}; - return converter.from_bytes(str); + std::wstring wstr; + size_t size; + wstr.resize(str.length()); + mbstowcs_s(&size,&wstr[0],wstr.size()+1,str.c_str(),str.size()); + return wstr; + } + + inline static std::string wstring_to_string(const std::wstring& wstr) { + std::string str; + size_t size; + str.resize(wstr.length()); + wcstombs_s(&size, &str[0], str.size() + 1, wstr.c_str(), wstr.size()); + return str; } } \ No newline at end of file From 330741f25c1a13094c8155bdc37e98f998d3d9e5 Mon Sep 17 00:00:00 2001 From: Steven French Date: Sun, 10 Aug 2025 13:43:59 +1200 Subject: [PATCH 12/13] Provider: fix RunDeserializeArgsToTuple call Change-Id: I17bf34f43a484be13f0cef85522379840d6ce061 --- include/dz/ECS/Provider.hpp | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/include/dz/ECS/Provider.hpp b/include/dz/ECS/Provider.hpp index 84cf5088..130bc832 100644 --- a/include/dz/ECS/Provider.hpp +++ b/include/dz/ECS/Provider.hpp @@ -211,7 +211,8 @@ namespace dz { template inline static std::shared_ptr TryMakeGroupFromSerial(TECS& ecs, BufferGroup* buffer_group, Serial& serial) { auto group_sh_ptr = std::make_shared(buffer_group, serial); - auto args = T::ReflectableGroup::RunDeserializeArgsToTuple(serial); + using RG = typename T::ReflectableGroup; + auto args = T::ReflectableGroup::template RunDeserializeArgsToTuple(serial); auto& group = *group_sh_ptr; auto& data = ecs.template GetProviderData(group.id); From b55f2d0bbbaf08d86698a69bb793d206434a3d90 Mon Sep 17 00:00:00 2001 From: Steven French Date: Sun, 10 Aug 2025 14:25:05 +1200 Subject: [PATCH 13/13] wide string conversion fixes Change-Id: If664f92f3997a0f99b5548051dd2534a3bfb005f --- include/dz/Util.hpp | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/include/dz/Util.hpp b/include/dz/Util.hpp index 0dc30530..402df3fa 100644 --- a/include/dz/Util.hpp +++ b/include/dz/Util.hpp @@ -1,6 +1,6 @@ #pragma once #include -#include +#include #include #include @@ -27,7 +27,7 @@ namespace dz { std::wstring wstr; size_t size; wstr.resize(str.length()); - mbstowcs_s(&size,&wstr[0],wstr.size()+1,str.c_str(),str.size()); + mbstowcs(wstr.data(), str.c_str(), str.size()); return wstr; } @@ -35,7 +35,7 @@ namespace dz { std::string str; size_t size; str.resize(wstr.length()); - wcstombs_s(&size, &str[0], str.size() + 1, wstr.c_str(), wstr.size()); + wcstombs(str.data(), wstr.c_str(), wstr.size()); return str; } } \ No newline at end of file