diff --git a/.gitmodules b/.gitmodules index 7f12414..60367a7 100644 --- a/.gitmodules +++ b/.gitmodules @@ -32,4 +32,6 @@ [submodule "destrum/third_party/tinygltf"] path = destrum/third_party/tinygltf url = https://github.com/syoyo/tinygltf.git - +[submodule "destrum/third_party/tinyexr"] + path = destrum/third_party/tinyexr + url = https://github.com/syoyo/tinyexr.git diff --git a/destrum/CMakeLists.txt b/destrum/CMakeLists.txt index 7a19922..039288a 100644 --- a/destrum/CMakeLists.txt +++ b/destrum/CMakeLists.txt @@ -72,6 +72,7 @@ target_link_libraries(destrum PRIVATE freetype::freetype + tinyexr ) target_compile_definitions(destrum diff --git a/destrum/assets_src/shaders/skybox.frag b/destrum/assets_src/shaders/skybox.frag index 5f0c40a..8db3bc0 100644 --- a/destrum/assets_src/shaders/skybox.frag +++ b/destrum/assets_src/shaders/skybox.frag @@ -15,21 +15,15 @@ layout(push_constant) uniform SkyboxPC { void main() { - // Fullscreen-triangle trick gives uv in [0..2]. Convert to [0..1]. - vec2 uv01 = uv * 0.5; + vec2 ndcXY = uv * 2.0 - 1.0; - // Build an NDC point on the far plane. - // Vulkan NDC is x,y in [-1..1], z in [0..1]. Using z=1 means "far". - vec4 ndc = vec4(uv01 * 2.0 - 1.0, 1.0, 1.0); + vec4 ndc = vec4(ndcXY, 1.0, 1.0); - // Unproject to world space vec4 world = pcs.invViewProj * ndc; vec3 worldPos = world.xyz / world.w; - // Direction from camera through this pixel vec3 dir = normalize(worldPos - pcs.cameraPos.xyz); + dir.y *= -1.0; - // Sample cubemap directly outColor = sampleTextureCubeLinear(pcs.skyboxTextureId, dir); - // outColor = sampleTextureCubeNearest(pcs.skyboxTextureId, dir); } diff --git a/destrum/assets_src/textures/mars.jpg b/destrum/assets_src/textures/mars.jpg new file mode 100644 index 0000000..3c24226 Binary files /dev/null and b/destrum/assets_src/textures/mars.jpg differ diff --git a/destrum/include/destrum/Components/OrbitAndSpin.h b/destrum/include/destrum/Components/OrbitAndSpin.h index e1a0f8b..4dbeecf 100644 --- a/destrum/include/destrum/Components/OrbitAndSpin.h +++ b/destrum/include/destrum/Components/OrbitAndSpin.h @@ -45,6 +45,9 @@ private: glm::vec3 m_BaseScale{1.0f}; float m_GrowSpeed = 1.0f; // rad/sec + float m_GrowMin = 0.05f; + float m_GrowMax = 0.3f; + // self spin glm::vec3 m_SpinAxis{0,1,0}; float m_SpinSpeed = 2.0f; // rad/sec diff --git a/destrum/include/destrum/Graphics/GfxDevice.h b/destrum/include/destrum/Graphics/GfxDevice.h index ca9c307..1052259 100644 --- a/destrum/include/destrum/Graphics/GfxDevice.h +++ b/destrum/include/destrum/Graphics/GfxDevice.h @@ -50,9 +50,10 @@ public: struct EndFrameProps { - const VkClearColorValue clearColor{ {0.f, 0.f, 0.f, 1.f} }; + const VkClearColorValue clearColor{{0.f, 0.f, 0.f, 1.f}}; glm::ivec4 drawImageBlitRect{}; // where to blit draw image to }; + void endFrame(VkCommandBuffer cmd, const GPUImage& drawImage, const EndFrameProps& props); void cleanup(); @@ -67,8 +68,7 @@ public: vkb::Device getDevice() const { return device; } - std::uint32_t getCurrentFrameIndex() const - { + std::uint32_t getCurrentFrameIndex() const { return frameNumber % FRAMES_IN_FLIGHT; } @@ -91,18 +91,20 @@ public: ImageID createImage(const vkutil::CreateImageInfo& createInfo, const std::string& debugName = "", void* pixelData = nullptr, ImageID imageId = NULL_IMAGE_ID); ImageID createDrawImage(VkFormat format, glm::ivec2 size, const std::string& debugName = "", ImageID imageId = NULL_IMAGE_ID); - ImageID loadImageFromFile(const std::filesystem::path& path, VkFormat format = VK_FORMAT_R8G8B8A8_SRGB, VkImageUsageFlags usage = VK_IMAGE_USAGE_SAMPLED_BIT, bool mipMap = false); + ImageID loadImageFromFile(const std::filesystem::path& path, VkImageUsageFlags usage = VK_IMAGE_USAGE_SAMPLED_BIT, bool mipMap = false, TextureIntent intent = TextureIntent::ColorSrgb); + ImageID addImageToCache(GPUImage image); - const GPUImage& getImage(ImageID id) const; - void uploadImageData(const GPUImage& image, void* pixelData, std::uint32_t layer = 0) const; + [[nodiscard]] const GPUImage& getImage(ImageID id) const; + // void uploadImageData(const GPUImage& image, void* pixelData, std::uint32_t layer = 0) const; + void uploadImageDataSized(const GPUImage& image, const void* pixelData, std::size_t byteSize, std::uint32_t layer) const; - GPUImage createImageRaw(const vkutil::CreateImageInfo& createInfo, std::optional customAllocationCreateInfo = std::nullopt) const; - GPUImage loadImageFromFileRaw(const std::filesystem::path& path, VkFormat format, VkImageUsageFlags usage, bool mipMap) const; + [[nodiscard]] GPUImage createImageRaw(const vkutil::CreateImageInfo& createInfo, std::optional customAllocationCreateInfo = std::nullopt) const; + GPUImage loadImageFromFileRaw(const std::filesystem::path& path, VkImageUsageFlags usage, bool mipMap, TextureIntent intent) const; void destroyImage(const GPUImage& image) const; - ImageID getWhiteTextureID() { return whiteImageId; } + [[nodiscard]] ImageID getWhiteTextureID() const { return whiteImageId; } private: vkb::Instance instance; @@ -129,17 +131,16 @@ private: static uint32_t BytesPerTexel(VkFormat fmt) { switch (fmt) { - case VK_FORMAT_R8_UNORM: return 1; - case VK_FORMAT_R8G8B8A8_UNORM: return 4; - case VK_FORMAT_B8G8R8A8_SRGB: return 4; - case VK_FORMAT_R16G16B16A16_SFLOAT: return 8; - case VK_FORMAT_R32G32B32A32_SFLOAT: return 16; - case VK_FORMAT_R8G8B8A8_SRGB: return 4; + case VK_FORMAT_R8_UNORM: return 1; + case VK_FORMAT_R8G8B8A8_UNORM: return 4; + case VK_FORMAT_B8G8R8A8_SRGB: return 4; + case VK_FORMAT_R16G16B16A16_SFLOAT: return 8; + case VK_FORMAT_R32G32B32A32_SFLOAT: return 16; + case VK_FORMAT_R8G8B8A8_SRGB: return 4; default: throw std::runtime_error("BytesPerTexel: unsupported format"); } } - }; diff --git a/destrum/include/destrum/Graphics/ImageCache.h b/destrum/include/destrum/Graphics/ImageCache.h index 363b913..8f21377 100644 --- a/destrum/include/destrum/Graphics/ImageCache.h +++ b/destrum/include/destrum/Graphics/ImageCache.h @@ -7,11 +7,10 @@ #include #include -#include -#include #include - -// #include +#include +#include +#include class GfxDevice; @@ -19,19 +18,18 @@ class ImageCache { friend class ResourcesInspector; public: - ImageCache(GfxDevice& gfxDevice); + explicit ImageCache(GfxDevice& gfxDevice); ImageID loadImageFromFile( const std::filesystem::path& path, - VkFormat format, VkImageUsageFlags usage, - bool mipMap); + bool mipMap, TextureIntent intent = TextureIntent::ColorSrgb); ImageID addImage(GPUImage image); ImageID addImage(ImageID id, GPUImage image); - const GPUImage& getImage(ImageID id) const; + [[nodiscard]] const GPUImage& getImage(ImageID id) const; - ImageID getFreeImageId() const; + [[nodiscard]] ImageID getFreeImageId() const; void destroyImages(); @@ -55,7 +53,7 @@ private: struct LoadedImageInfo { std::filesystem::path path; - VkFormat format; + TextureIntent intent; VkImageUsageFlags usage; bool mipMap; }; diff --git a/destrum/include/destrum/Graphics/Renderer.h b/destrum/include/destrum/Graphics/Renderer.h index 3c61d38..0f37c37 100644 --- a/destrum/include/destrum/Graphics/Renderer.h +++ b/destrum/include/destrum/Graphics/Renderer.h @@ -46,12 +46,15 @@ public: void setSkyboxTexture(ImageID skyboxImageId); + void flushMaterialUpdates(GfxDevice& gfxDevice); private: void createDrawImage(GfxDevice& gfxDevice, const glm::ivec2& drawImageSize, bool firstCreate); MeshCache& meshCache; MaterialCache& materialCache; + std::vector pendingMaterialUploads; + std::vector meshDrawCommands; std::vector sortedMeshDrawCommands; diff --git a/destrum/include/destrum/Graphics/TextureIntent.h b/destrum/include/destrum/Graphics/TextureIntent.h new file mode 100644 index 0000000..ea48505 --- /dev/null +++ b/destrum/include/destrum/Graphics/TextureIntent.h @@ -0,0 +1,11 @@ +#ifndef TEXTUREINTENT_H +#define TEXTUREINTENT_H + +#include + +enum class TextureIntent : std::uint8_t { + ColorSrgb, // albedo/UI + DataLinear, // normal/roughness/metalness/etc +}; + +#endif //TEXTUREINTENT_H diff --git a/destrum/include/destrum/Graphics/Util.h b/destrum/include/destrum/Graphics/Util.h index a5c769e..98baa5e 100644 --- a/destrum/include/destrum/Graphics/Util.h +++ b/destrum/include/destrum/Graphics/Util.h @@ -51,6 +51,12 @@ namespace vkutil { int destH, VkFilter filter); + void bufferHostWriteToShaderReadBarrier( + VkCommandBuffer cmd, + VkBuffer buffer, + VkDeviceSize offset = 0, + VkDeviceSize size = VK_WHOLE_SIZE); + void addDebugLabel(VkDevice device, VkImage image, const char* label); void addDebugLabel(VkDevice device, VkImageView imageView, const char* label); void addDebugLabel(VkDevice device, VkShaderModule shaderModule, const char* label); @@ -76,6 +82,5 @@ namespace vkutil { RenderInfo createRenderingInfo(const RenderingInfoParams& params); VkShaderModule loadShaderModule(const std::filesystem::path& path, VkDevice device); - } #endif //UTIL_H diff --git a/destrum/include/destrum/Graphics/imageLoader.h b/destrum/include/destrum/Graphics/imageLoader.h index 339be54..8cf715d 100644 --- a/destrum/include/destrum/Graphics/imageLoader.h +++ b/destrum/include/destrum/Graphics/imageLoader.h @@ -1,36 +1,73 @@ #ifndef IMAGELOADER_H #define IMAGELOADER_H -#include +#include +#include +#include + +#include "destrum/Graphics/TextureIntent.h" struct ImageData { ImageData() = default; ~ImageData(); - // move only - ImageData(ImageData&& o) = default; - ImageData& operator=(ImageData&& o) = default; + // move only (CUSTOM!) + ImageData(ImageData&& o) noexcept { *this = std::move(o); } - // no copies - ImageData(const ImageData& o) = delete; - ImageData& operator=(const ImageData& o) = delete; - // data + ImageData& operator=(ImageData&& o) noexcept { + if (this == &o) return *this; + + // free current contents first + this->~ImageData(); + + // steal + pixels = o.pixels; + hdrPixels = o.hdrPixels; + hdr = o.hdr; + width = o.width; + height = o.height; + channels = o.channels; + comp = o.comp; + shouldSTBFree = o.shouldSTBFree; + shouldEXRFree = o.shouldEXRFree; + vkFormat = o.vkFormat; + byteSize = o.byteSize; + + // neuter source so its destructor won't free our memory + o.pixels = nullptr; + o.hdrPixels = nullptr; + o.shouldSTBFree = false; + o.shouldEXRFree = false; + o.hdr = false; + o.width = o.height = o.channels = o.comp = 0; + o.vkFormat = VK_FORMAT_UNDEFINED; + o.byteSize = 0; + + return *this; + } + + ImageData(const ImageData&) = delete; + ImageData& operator=(const ImageData&) = delete; + unsigned char* pixels{nullptr}; + float* hdrPixels{nullptr}; + bool hdr{false}; + int width{0}; int height{0}; int channels{0}; - - // HDR only - float* hdrPixels{nullptr}; - bool hdr{false}; int comp{0}; bool shouldSTBFree{false}; + bool shouldEXRFree{false}; + + VkFormat vkFormat{VK_FORMAT_UNDEFINED}; + std::size_t byteSize{0}; }; namespace util { - ImageData loadImage(const std::filesystem::path& p); + ImageData loadImage(const std::filesystem::path& p, TextureIntent intent); } -#endif //IMAGELOADER_H +#endif // IMAGELOADER_H diff --git a/destrum/src/Components/OrbitAndSpin.cpp b/destrum/src/Components/OrbitAndSpin.cpp index 41616cf..1eed17e 100644 --- a/destrum/src/Components/OrbitAndSpin.cpp +++ b/destrum/src/Components/OrbitAndSpin.cpp @@ -28,6 +28,11 @@ void OrbitAndSpin::Randomize(uint32_t seed) std::uniform_real_distribution spinSpeedDist(0.5f, 6.0f); std::uniform_real_distribution phaseDist(0.0f, 6.28318530718f); + // grow randomness + std::uniform_real_distribution growMinDist(0.03f, 0.08f); + std::uniform_real_distribution growMaxDist(0.2f, 0.4f); + std::uniform_real_distribution growSpeedDist(0.5f, 2.5f); + m_OrbitAxis = RandomUnitVector(rng); m_SpinAxis = RandomUnitVector(rng); @@ -35,6 +40,15 @@ void OrbitAndSpin::Randomize(uint32_t seed) m_SpinSpeed = spinSpeedDist(rng); m_OrbitPhase = phaseDist(rng); + m_GrowMin = growMinDist(rng); + m_GrowMax = growMaxDist(rng); + m_GrowSpeed = growSpeedDist(rng); + m_GrowPhase = phaseDist(rng); // random start offset + + // safety: ensure min < max + if (m_GrowMin > m_GrowMax) + std::swap(m_GrowMin, m_GrowMax); + BuildOrbitBasis(); } @@ -70,19 +84,27 @@ void OrbitAndSpin::Update() // grow (always positive) m_GrowPhase += m_GrowSpeed * dt; - float t = 0.5f * (std::sin(m_GrowPhase) + 1.0f); // 0..1 - float s = 1.50 + t * 0.70f; // 0.05..0.15 (pick what you want) + m_GrowPhase += m_GrowSpeed * dt; - GetTransform().SetLocalScale(glm::vec3(std::sin(m_GrowPhase))); + // 0..1 + float t = 0.5f * (std::sin(m_GrowPhase) + 1.0f); + + // random per-object range + float s = glm::mix(m_GrowMin, m_GrowMax, t); + + // respect original scale + GetTransform().SetLocalScale(glm::vec3(s)); + + // GetTransform().SetLocalScale(glm::vec3(std::sin(m_GrowPhase))); // material color -// auto& mat = GameState::GetInstance().Renderer().getMaterialMutable(m_MaterialID); -// mat.baseColor = glm::vec3( -// 0.5f + 0.5f * std::sin(m_OrbitAngle * 2.0f), -// 0.5f + 0.5f * std::sin(m_OrbitAngle * 3.0f + 2.0f), -// 0.5f + 0.5f * std::sin(m_OrbitAngle * 4.0f + 4.0f) -// ); -// GameState::GetInstance().Renderer().updateMaterialGPU(m_MaterialID); + auto& mat = GameState::GetInstance().Renderer().getMaterialMutable(m_MaterialID); + mat.baseColor = glm::vec3( + 0.5f + 0.5f * std::sin(m_OrbitAngle * 2.0f), + 0.5f + 0.5f * std::sin(m_OrbitAngle * 3.0f + 2.0f), + 0.5f + 0.5f * std::sin(m_OrbitAngle * 4.0f + 4.0f) + ); + GameState::GetInstance().Renderer().updateMaterialGPU(m_MaterialID); } void OrbitAndSpin::Start() { diff --git a/destrum/src/Graphics/GfxDevice.cpp b/destrum/src/Graphics/GfxDevice.cpp index 9189379..f452db9 100644 --- a/destrum/src/Graphics/GfxDevice.cpp +++ b/destrum/src/Graphics/GfxDevice.cpp @@ -104,9 +104,8 @@ void GfxDevice::init(SDL_Window* window, const std::string& appName, bool vSync) VkPhysicalDeviceProperties props{}; vkGetPhysicalDeviceProperties(physicalDevice, &props); - imageCache.bindlessSetManager.init(device, props.limits.maxSamplerAnisotropy); - - { // create white texture + imageCache.bindlessSetManager.init(device, props.limits.maxSamplerAnisotropy); { + // create white texture std::uint32_t pixel = 0xFFFFFFFF; whiteImageId = createImage( { @@ -170,7 +169,7 @@ VulkanImmediateExecutor& GfxDevice::GetImmediateExecuter() { } void GfxDevice::endFrame(VkCommandBuffer cmd, const GPUImage& drawImage, const EndFrameProps& props) { - // get swapchain image + // get swapchain image const auto [swapchainImage, swapchainImageIndex] = swapchain.acquireNextImage(getCurrentFrameIndex()); if (swapchainImage == VK_NULL_HANDLE) { spdlog::info("Swapchain is freaky, skipping frame..."); @@ -180,9 +179,7 @@ void GfxDevice::endFrame(VkCommandBuffer cmd, const GPUImage& drawImage, const E // Fences are reset here to prevent the deadlock in case swapchain becomes dirty swapchain.resetFences(getCurrentFrameIndex()); - auto swapchainLayout = VK_IMAGE_LAYOUT_UNDEFINED; - - { + auto swapchainLayout = VK_IMAGE_LAYOUT_UNDEFINED; { const VkImageSubresourceRange clearRange = vkinit::imageSubresourceRange(VK_IMAGE_ASPECT_COLOR_BIT); vkutil::transitionImage(cmd, swapchainImage, swapchainLayout, VK_IMAGE_LAYOUT_GENERAL); swapchainLayout = VK_IMAGE_LAYOUT_GENERAL; @@ -277,8 +274,7 @@ void GfxDevice::immediateSubmit(ImmediateExecuteFunction&& f) const { GPUBuffer GfxDevice::createBuffer( std::size_t allocSize, VkBufferUsageFlags usage, - VmaMemoryUsage memoryUsage) const -{ + VmaMemoryUsage memoryUsage) const { const auto bufferInfo = VkBufferCreateInfo{ .sType = VK_STRUCTURE_TYPE_BUFFER_CREATE_INFO, .size = allocSize, @@ -305,8 +301,7 @@ GPUBuffer GfxDevice::createBuffer( return buffer; } -VkDeviceAddress GfxDevice::getBufferAddress(const GPUBuffer& buffer) const -{ +VkDeviceAddress GfxDevice::getBufferAddress(const GPUBuffer& buffer) const { const auto deviceAdressInfo = VkBufferDeviceAddressInfo{ .sType = VK_STRUCTURE_TYPE_BUFFER_DEVICE_ADDRESS_INFO, .buffer = buffer.buffer, @@ -314,10 +309,10 @@ VkDeviceAddress GfxDevice::getBufferAddress(const GPUBuffer& buffer) const return vkGetBufferDeviceAddress(device, &deviceAdressInfo); } -void GfxDevice::destroyBuffer(const GPUBuffer& buffer) const -{ +void GfxDevice::destroyBuffer(const GPUBuffer& buffer) const { vmaDestroyBuffer(allocator, buffer.buffer, buffer.allocation); } + // // GPUImage GfxDevice::loadImageFromFileRaw( // const std::filesystem::path& path, @@ -356,15 +351,20 @@ ImageID GfxDevice::createImage( const vkutil::CreateImageInfo& createInfo, const std::string& debugName, void* pixelData, - ImageID imageId) -{ + ImageID imageId) { auto image = createImageRaw(createInfo); if (!debugName.empty()) { vkutil::addDebugLabel(device, image.image, debugName.c_str()); image.debugName = debugName; } if (pixelData) { - uploadImageData(image, pixelData); + const std::size_t bytes = + std::size_t(image.extent.width) * + std::size_t(image.extent.height) * + std::size_t(image.extent.depth) * + BytesPerTexel(image.format); + + uploadImageDataSized(image, pixelData, bytes, 0); } if (imageId != NULL_IMAGE_ID) { return imageCache.addImage(imageId, std::move(image)); @@ -377,8 +377,7 @@ ImageID GfxDevice::createDrawImage( VkFormat format, glm::ivec2 size, const std::string& debugName, - ImageID imageId) -{ + ImageID imageId) { assert(size.x > 0 && size.y > 0); const auto extent = VkExtent3D{ .width = (std::uint32_t)size.x, @@ -400,29 +399,21 @@ ImageID GfxDevice::createDrawImage( return createImage(createImageInfo, debugName, nullptr, imageId); } -ImageID GfxDevice::loadImageFromFile( - const std::filesystem::path& path, - VkFormat format, - VkImageUsageFlags usage, - bool mipMap) -{ - return imageCache.loadImageFromFile(path, format, usage, mipMap); +ImageID GfxDevice::loadImageFromFile(const std::filesystem::path& path, VkImageUsageFlags usage, bool mipMap, TextureIntent intent) { + return imageCache.loadImageFromFile(path, usage, mipMap, intent); } -const GPUImage& GfxDevice::getImage(ImageID id) const -{ +const GPUImage& GfxDevice::getImage(ImageID id) const { return imageCache.getImage(id); } -ImageID GfxDevice::addImageToCache(GPUImage img) -{ +ImageID GfxDevice::addImageToCache(GPUImage img) { return imageCache.addImage(std::move(img)); } GPUImage GfxDevice::createImageRaw( const vkutil::CreateImageInfo& createInfo, - std::optional customAllocationCreateInfo) const -{ + std::optional customAllocationCreateInfo) const { std::uint32_t mipLevels = 1; if (createInfo.mipMap) { const auto maxExtent = std::max(createInfo.extent.width, createInfo.extent.height); @@ -452,9 +443,7 @@ GPUImage GfxDevice::createImageRaw( .usage = VMA_MEMORY_USAGE_AUTO, .requiredFlags = VK_MEMORY_PROPERTY_DEVICE_LOCAL_BIT, }; - const auto allocInfo = customAllocationCreateInfo.has_value() ? - customAllocationCreateInfo.value() : - defaultAllocInfo; + const auto allocInfo = customAllocationCreateInfo.has_value() ? customAllocationCreateInfo.value() : defaultAllocInfo; GPUImage image{}; image.format = createInfo.format; @@ -464,8 +453,7 @@ GPUImage GfxDevice::createImageRaw( image.numLayers = createInfo.numLayers; image.isCubemap = createInfo.isCubemap; - VK_CHECK( - vmaCreateImage(allocator, &imgInfo, &allocInfo, &image.image, &image.allocation, nullptr)); + VK_CHECK(vmaCreateImage(allocator, &imgInfo, &allocInfo, &image.image, &image.allocation, nullptr)); // create view only when usage flags allow it bool shouldCreateView = ((createInfo.usage & VK_IMAGE_USAGE_SAMPLED_BIT) != 0) || @@ -475,12 +463,13 @@ GPUImage GfxDevice::createImageRaw( if (shouldCreateView) { VkImageAspectFlags aspectFlag = VK_IMAGE_ASPECT_COLOR_BIT; - if (createInfo.format == VK_FORMAT_D32_SFLOAT) { // TODO: support other depth formats + if (createInfo.format == VK_FORMAT_D32_SFLOAT) { + // TODO: support other depth formats aspectFlag = VK_IMAGE_ASPECT_DEPTH_BIT; } auto viewType = - createInfo.numLayers == 1 ? VK_IMAGE_VIEW_TYPE_2D : VK_IMAGE_VIEW_TYPE_2D_ARRAY; + createInfo.numLayers == 1 ? VK_IMAGE_VIEW_TYPE_2D : VK_IMAGE_VIEW_TYPE_2D_ARRAY; if (createInfo.isCubemap && createInfo.numLayers == 6) { viewType = VK_IMAGE_VIEW_TYPE_CUBE; } @@ -491,13 +480,13 @@ GPUImage GfxDevice::createImageRaw( .viewType = viewType, .format = createInfo.format, .subresourceRange = - VkImageSubresourceRange{ - .aspectMask = aspectFlag, - .baseMipLevel = 0, - .levelCount = mipLevels, - .baseArrayLayer = 0, - .layerCount = createInfo.numLayers, - }, + VkImageSubresourceRange{ + .aspectMask = aspectFlag, + .baseMipLevel = 0, + .levelCount = mipLevels, + .baseArrayLayer = 0, + .layerCount = createInfo.numLayers, + }, }; VK_CHECK(vkCreateImageView(device, &viewCreateInfo, nullptr, &image.imageView)); @@ -506,104 +495,176 @@ GPUImage GfxDevice::createImageRaw( return image; } -void GfxDevice::uploadImageData(const GPUImage& image, void* pixelData, std::uint32_t layer) const -{ - VkDeviceSize dataSize = - VkDeviceSize(image.extent.depth) * - image.extent.width * - image.extent.height * - BytesPerTexel(image.format); - - auto uploadBuffer = createBuffer(dataSize, VK_BUFFER_USAGE_TRANSFER_SRC_BIT); - memcpy(uploadBuffer.info.pMappedData, pixelData, size_t(dataSize)); - - executor.immediateSubmit([&](VkCommandBuffer cmd) { - assert( - (image.usage & VK_IMAGE_USAGE_TRANSFER_DST_BIT) != 0 && - "Image needs to have VK_IMAGE_USAGE_TRANSFER_DST_BIT to upload data to it"); - vkutil::transitionImage( - cmd, image.image, VK_IMAGE_LAYOUT_UNDEFINED, VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL); - - const auto copyRegion = VkBufferImageCopy{ - .bufferOffset = 0, - .bufferRowLength = 0, - .bufferImageHeight = 0, - .imageSubresource = - { - .aspectMask = VK_IMAGE_ASPECT_COLOR_BIT, - .mipLevel = 0, - .baseArrayLayer = layer, - .layerCount = 1, - }, - .imageExtent = image.extent, - }; - - vkCmdCopyBufferToImage( - cmd, - uploadBuffer.buffer, - image.image, - VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL, - 1, - ©Region); - - if (image.mipLevels > 1) { - assert( - (image.usage & VK_IMAGE_USAGE_TRANSFER_DST_BIT) != 0 && - (image.usage & VK_IMAGE_USAGE_TRANSFER_SRC_BIT) != 0 && - "Image needs to have VK_IMAGE_USAGE_TRANSFER_{DST,SRC}_BIT to generate mip maps"); - // graphics::generateMipmaps( - // cmd, - // image.image, - // VkExtent2D{image.extent.width, image.extent.height}, - // image.mipLevels); - spdlog::warn("Yea dawg, i ain't written this yet :pray:"); - } else { - vkutil::transitionImage( - cmd, - image.image, - VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL, - VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL); - } - }); - - destroyBuffer(uploadBuffer); -} - GPUImage GfxDevice::loadImageFromFileRaw( const std::filesystem::path& path, - VkFormat format, VkImageUsageFlags usage, - bool mipMap) const -{ - auto data = util::loadImage(path); - if (!data.pixels) { - fmt::println("[error] failed to load image from '{}'", path.string()); + bool mipMap, + TextureIntent intent) const { + // 1) Decode file to CPU pixels (stb for png/jpg/hdr, tinyexr for exr) + // IMPORTANT: util::loadImage should fill: + // - width/height/channels(=4) + // - hdr + (pixels OR hdrPixels) + // - vkFormat (RGBA8_SRGB or RGBA8_UNORM or RGBA32F) + // - byteSize (exact bytes to upload) + const auto data = util::loadImage(path, intent); + if (data.vkFormat == VK_FORMAT_UNDEFINED || data.byteSize == 0 || + (data.hdr ? (data.hdrPixels == nullptr) : (data.pixels == nullptr))) { + spdlog::error("Failed to load image '{}'", path.string()); return getImage(errorImageId); } + // 2) Create GPU image using the format the loader chose (matches CPU memory layout) auto image = createImageRaw({ - .format = format, - .usage = usage | // - VK_IMAGE_USAGE_TRANSFER_DST_BIT | // for uploading pixel data to image - VK_IMAGE_USAGE_TRANSFER_SRC_BIT, // for generating mips - .extent = - VkExtent3D{ - .width = (std::uint32_t)data.width, - .height = (std::uint32_t)data.height, - .depth = 1, - }, + .format = data.vkFormat, + .usage = usage | + VK_IMAGE_USAGE_TRANSFER_DST_BIT | + (mipMap ? VK_IMAGE_USAGE_TRANSFER_SRC_BIT : 0), + .extent = VkExtent3D{ + .width = static_cast(data.width), + .height = static_cast(data.height), + .depth = 1, + }, .mipMap = mipMap, }); - uploadImageData(image, data.pixels); + // 3) Upload *exactly* byteSize bytes from the correct pointer + const void* src = data.hdr + ? static_cast(data.hdrPixels) + : static_cast(data.pixels); + + // Use the "sized" upload to avoid BytesPerTexel mismatches + uploadImageDataSized(image, src, data.byteSize, 0); + + // 4) Debug label image.debugName = path.string(); vkutil::addDebugLabel(device, image.image, path.string().c_str()); return image; } -void GfxDevice::destroyImage(const GPUImage& image) const -{ +// void GfxDevice::uploadImageData(const GPUImage& image, void* pixelData, std::uint32_t layer) const { +// VkDeviceSize dataSize = +// VkDeviceSize(image.extent.depth) * +// image.extent.width * +// image.extent.height * +// BytesPerTexel(image.format); +// +// auto uploadBuffer = createBuffer(dataSize, VK_BUFFER_USAGE_TRANSFER_SRC_BIT); +// memcpy(uploadBuffer.info.pMappedData, pixelData, size_t(dataSize)); +// +// executor.immediateSubmit([&] (VkCommandBuffer cmd) { +// assert( +// (image.usage & VK_IMAGE_USAGE_TRANSFER_DST_BIT) != 0 && +// "Image needs to have VK_IMAGE_USAGE_TRANSFER_DST_BIT to upload data to it"); +// vkutil::transitionImage( +// cmd, image.image, VK_IMAGE_LAYOUT_UNDEFINED, VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL); +// +// const auto copyRegion = VkBufferImageCopy{ +// .bufferOffset = 0, +// .bufferRowLength = 0, +// .bufferImageHeight = 0, +// .imageSubresource = +// { +// .aspectMask = VK_IMAGE_ASPECT_COLOR_BIT, +// .mipLevel = 0, +// .baseArrayLayer = layer, +// .layerCount = 1, +// }, +// .imageExtent = image.extent, +// }; +// +// vkCmdCopyBufferToImage( +// cmd, +// uploadBuffer.buffer, +// image.image, +// VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL, +// 1, +// ©Region); +// +// if (image.mipLevels > 1) { +// assert( +// (image.usage & VK_IMAGE_USAGE_TRANSFER_DST_BIT) != 0 && +// (image.usage & VK_IMAGE_USAGE_TRANSFER_SRC_BIT) != 0 && +// "Image needs to have VK_IMAGE_USAGE_TRANSFER_{DST,SRC}_BIT to generate mip maps"); +// // graphics::generateMipmaps( +// // cmd, +// // image.image, +// // VkExtent2D{image.extent.width, image.extent.height}, +// // image.mipLevels); +// spdlog::warn("Yea dawg, i ain't written this yet :pray:"); +// } else { +// vkutil::transitionImage( +// cmd, +// image.image, +// VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL, +// VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL); +// } +// }); +// +// destroyBuffer(uploadBuffer); +// } + +void GfxDevice::uploadImageDataSized(const GPUImage& image, const void* pixelData, std::size_t byteSize, std::uint32_t layer) const { + auto uploadBuffer = createBuffer(byteSize, VK_BUFFER_USAGE_TRANSFER_SRC_BIT, VMA_MEMORY_USAGE_CPU_TO_GPU); + + // safety checks + assert(uploadBuffer.info.pMappedData); + assert(pixelData); + assert(byteSize > 0); + + std::memcpy(uploadBuffer.info.pMappedData, pixelData, byteSize); + + executor.immediateSubmit([&] (VkCommandBuffer cmd) { + vkutil::transitionImage(cmd, image.image, VK_IMAGE_LAYOUT_UNDEFINED, VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL); + + VkBufferImageCopy copyRegion{}; + copyRegion.bufferOffset = 0; + copyRegion.imageSubresource.aspectMask = VK_IMAGE_ASPECT_COLOR_BIT; + copyRegion.imageSubresource.mipLevel = 0; + copyRegion.imageSubresource.baseArrayLayer = layer; + copyRegion.imageSubresource.layerCount = 1; + copyRegion.imageExtent = image.extent; + + vkCmdCopyBufferToImage(cmd, uploadBuffer.buffer, image.image, VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL, 1, ©Region); + + vkutil::transitionImage(cmd, image.image, VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL, VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL); + }); + + destroyBuffer(uploadBuffer); +} + +// GPUImage GfxDevice::loadImageFromFileRaw(const std::filesystem::path& path, VkImageUsageFlags usage, bool mipMap) const { +// const auto data = util::loadImage(path); +// const bool isHdr = data.hdr && data.hdrPixels; +// const bool isLdr = !data.hdr && data.pixels; +// +// if (!isHdr && !isLdr || data.vkFormat == VK_FORMAT_UNDEFINED || data.byteSize == 0) { +// spdlog::error("failed to load image from '{}'", path.string()); +// return getImage(errorImageId); +// } +// +// auto image = createImageRaw({ +// .format = data.vkFormat, +// .usage = usage | +// VK_IMAGE_USAGE_TRANSFER_DST_BIT | +// (mipMap ? VK_IMAGE_USAGE_TRANSFER_SRC_BIT : 0), +// .extent = VkExtent3D{ +// .width = (std::uint32_t)data.width, +// .height = (std::uint32_t)data.height, +// .depth = 1, +// }, +// .mipMap = mipMap, +// }); +// +// const void* src = isHdr ? (const void*)data.hdrPixels : (const void*)data.pixels; +// uploadImageDataSized(image, src, data.byteSize, 0); +// +// image.debugName = path.string(); +// vkutil::addDebugLabel(device, image.image, path.string().c_str()); +// return image; +// } + +void GfxDevice::destroyImage(const GPUImage& image) const { vkDestroyImageView(device, image.imageView, nullptr); vmaDestroyImage(allocator, image.image, image.allocation); // TODO: if image has bindless id, update the set diff --git a/destrum/src/Graphics/ImageCache.cpp b/destrum/src/Graphics/ImageCache.cpp index 5f68381..1dd2d56 100644 --- a/destrum/src/Graphics/ImageCache.cpp +++ b/destrum/src/Graphics/ImageCache.cpp @@ -7,20 +7,21 @@ ImageCache::ImageCache(GfxDevice& gfxDevice) : gfxDevice(gfxDevice) ImageID ImageCache::loadImageFromFile( const std::filesystem::path& path, - VkFormat format, VkImageUsageFlags usage, - bool mipMap) + bool mipMap, + TextureIntent intent) { for (const auto& [id, info] : loadedImagesInfo) { - // TODO: calculate some hash to not have to linear search every time? - if (info.path == path && info.format == format && info.usage == usage && - info.mipMap == mipMap) { - // std::cout << "Already loaded: " << path << std::endl; + if (info.path == path && + info.intent == intent && + info.usage == usage && + info.mipMap == mipMap) + { return id; - } + } } - auto image = gfxDevice.loadImageFromFileRaw(path, format, usage, mipMap); + auto image = gfxDevice.loadImageFromFileRaw(path, usage, mipMap, intent); if (image.isInitialized() && image.getBindlessId() == errorImageId) { return errorImageId; } @@ -32,10 +33,11 @@ ImageID ImageCache::loadImageFromFile( id, LoadedImageInfo{ .path = path, - .format = format, + .intent = intent, .usage = usage, .mipMap = mipMap, }); + return id; } @@ -59,6 +61,7 @@ ImageID ImageCache::addImage(ImageID id, GPUImage image) const GPUImage& ImageCache::getImage(ImageID id) const { + assert(id != NULL_IMAGE_ID && id < images.size()); return images.at(id); } diff --git a/destrum/src/Graphics/ImageLoader.cpp b/destrum/src/Graphics/ImageLoader.cpp index e5f2b19..d317f54 100644 --- a/destrum/src/Graphics/ImageLoader.cpp +++ b/destrum/src/Graphics/ImageLoader.cpp @@ -2,30 +2,118 @@ #define STB_IMAGE_IMPLEMENTATION #include -#include + +#define TINYEXR_IMPLEMENTATION +#include "tinyexr.h" + +#include // fprintf +#include // free + +#include "spdlog/spdlog.h" ImageData::~ImageData() { if (shouldSTBFree) { - stbi_image_free(pixels); - stbi_image_free(hdrPixels); + if (pixels) stbi_image_free(pixels); + if (hdrPixels) stbi_image_free(hdrPixels); + spdlog::debug("Freed STB image memory"); + } + if (shouldEXRFree) { + if (hdrPixels) free(hdrPixels); + spdlog::debug("Freed TinyEXR image memory"); } } namespace util { - ImageData loadImage(const std::filesystem::path& p) + static bool isExrExt(const std::filesystem::path& p) + { + auto ext = p.extension().string(); + for (auto& c : ext) c = char(::tolower(c)); + return ext == ".exr"; + } + + ImageData loadImage(const std::filesystem::path& p, TextureIntent intent) { ImageData data; + + // ---------- EXR ---------- + if (isExrExt(p)) { + float* out = nullptr; + int w = 0, h = 0; + const char* err = nullptr; + + const int ret = LoadEXR(&out, &w, &h, p.string().c_str(), &err); + if (ret != TINYEXR_SUCCESS || !out || w <= 0 || h <= 0) { + if (err) { + spdlog::error("TinyEXR error: {}", err); + FreeEXRErrorMessage(err); + } + return {}; + } + + data.hdr = true; + data.hdrPixels = out; + data.width = w; + data.height = h; + data.channels = 4; + data.shouldEXRFree = true; + + data.vkFormat = VK_FORMAT_R32G32B32A32_SFLOAT; + data.byteSize = std::size_t(w) * std::size_t(h) * 4 * sizeof(float); + return data; + } + + // ---------- STB HDR (.hdr etc) ---------- data.shouldSTBFree = true; + if (stbi_is_hdr(p.string().c_str())) { data.hdr = true; - data.hdrPixels = stbi_loadf(p.string().c_str(), &data.width, &data.height, &data.comp, 4); - } else { - data.pixels = stbi_load(p.string().c_str(), &data.width, &data.height, &data.channels, 4); + data.hdrPixels = stbi_loadf( + p.string().c_str(), + &data.width, + &data.height, + &data.comp, + 4 + ); + + if (!data.hdrPixels || data.width <= 0 || data.height <= 0) { + return {}; + } + + data.channels = 4; + data.vkFormat = VK_FORMAT_R32G32B32A32_SFLOAT; + data.byteSize = std::size_t(data.width) * std::size_t(data.height) * 4 * sizeof(float); + return data; } + + // ---------- STB LDR (png/jpg/...) ---------- + data.pixels = stbi_load( + p.string().c_str(), + &data.width, + &data.height, + &data.channels, + 4 + ); + + if (!data.pixels || data.width <= 0 || data.height <= 0) { + return {}; + } + data.channels = 4; + + // Choose color space for LDR based on intent + switch (intent) { + case TextureIntent::ColorSrgb: + data.vkFormat = VK_FORMAT_R8G8B8A8_SRGB; + break; + case TextureIntent::DataLinear: + data.vkFormat = VK_FORMAT_R8G8B8A8_UNORM; + break; + } + + data.byteSize = std::size_t(data.width) * std::size_t(data.height) * 4 * sizeof(unsigned char); return data; } -} // namespace util \ No newline at end of file +} // namespace util diff --git a/destrum/src/Graphics/Renderer.cpp b/destrum/src/Graphics/Renderer.cpp index b1a683c..7bc7612 100644 --- a/destrum/src/Graphics/Renderer.cpp +++ b/destrum/src/Graphics/Renderer.cpp @@ -28,6 +28,7 @@ void GameRenderer::init(GfxDevice& gfxDevice, const glm::ivec2& drawImageSize) { } void GameRenderer::beginDrawing(GfxDevice& gfxDevice) { + flushMaterialUpdates(gfxDevice); meshDrawCommands.clear(); } @@ -47,8 +48,13 @@ void GameRenderer::draw(VkCommandBuffer cmd, GfxDevice& gfxDevice, const Camera& .fogDensity = sceneData.fogDensity, .materialsBuffer = materialCache.getMaterialDataBufferAddress(), }; - sceneDataBuffer.uploadNewData( - cmd, gfxDevice.getCurrentFrameIndex(), (void*)&gpuSceneData, sizeof(GPUSceneData)); + vkutil::bufferHostWriteToShaderReadBarrier( + cmd, + materialCache.getMaterialDataBuffer().buffer, + 0, + VK_WHOLE_SIZE + ); + sceneDataBuffer.uploadNewData(cmd, gfxDevice.getCurrentFrameIndex(), (void*)&gpuSceneData, sizeof(GPUSceneData)); const auto& drawImage = gfxDevice.getImage(drawImageId); const auto& depthImage = gfxDevice.getImage(depthImageId); @@ -122,7 +128,7 @@ Material& GameRenderer::getMaterialMutable(MaterialID id) { void GameRenderer::updateMaterialGPU(MaterialID id) { assert(id != NULL_MATERIAL_ID); - materialCache.updateMaterialGPU(GameState::GetInstance().Gfx(), id); + pendingMaterialUploads.push_back(id); } void GameRenderer::setSkyboxTexture(ImageID skyboxImageId) { @@ -130,6 +136,14 @@ void GameRenderer::setSkyboxTexture(ImageID skyboxImageId) { skyboxPipeline->setSkyboxImage(skyboxImageId); } +void GameRenderer::flushMaterialUpdates(GfxDevice& gfxDevice) { + for (MaterialID id : pendingMaterialUploads) { + materialCache.updateMaterialGPU(gfxDevice, id); + // if non-coherent: flush mapped range for that id here + } + pendingMaterialUploads.clear(); +} + void GameRenderer::createDrawImage(GfxDevice& gfxDevice, const glm::ivec2& drawImageSize, bool firstCreate) diff --git a/destrum/src/Graphics/Resources/Cubemap.cpp b/destrum/src/Graphics/Resources/Cubemap.cpp index 40146f4..17c860d 100644 --- a/destrum/src/Graphics/Resources/Cubemap.cpp +++ b/destrum/src/Graphics/Resources/Cubemap.cpp @@ -30,7 +30,8 @@ CubeMap::~CubeMap() { } void CubeMap::LoadCubeMap(const std::filesystem::path& directoryPath) { - m_hdrImage = GameState::GetInstance().Gfx().loadImageFromFile(directoryPath, VK_FORMAT_R8G8B8A8_SRGB, VK_IMAGE_USAGE_SAMPLED_BIT | VK_IMAGE_USAGE_TRANSFER_DST_BIT | VK_IMAGE_USAGE_COLOR_ATTACHMENT_BIT, false); + // m_hdrImage = GameState::GetInstance().Gfx().loadImageFromFile(directoryPath, VK_FORMAT_R8G8B8A8_SRGB, VK_IMAGE_USAGE_SAMPLED_BIT | VK_IMAGE_USAGE_TRANSFER_DST_BIT | VK_IMAGE_USAGE_COLOR_ATTACHMENT_BIT, false); + m_hdrImage = GameState::GetInstance().Gfx().loadImageFromFile(directoryPath, VK_IMAGE_USAGE_SAMPLED_BIT | VK_IMAGE_USAGE_TRANSFER_DST_BIT | VK_IMAGE_USAGE_COLOR_ATTACHMENT_BIT, false); } void CubeMap::RenderToCubemap(ImageID inputImage, diff --git a/destrum/src/Graphics/Util.cpp b/destrum/src/Graphics/Util.cpp index f58e702..67df28c 100644 --- a/destrum/src/Graphics/Util.cpp +++ b/destrum/src/Graphics/Util.cpp @@ -10,9 +10,9 @@ void vkutil::transitionImage(VkCommandBuffer cmd, VkImage image, VkImageLayout c VkImageAspectFlags aspectMask = (currentLayout == VK_IMAGE_LAYOUT_DEPTH_ATTACHMENT_OPTIMAL || newLayout == VK_IMAGE_LAYOUT_DEPTH_ATTACHMENT_OPTIMAL || - newLayout == VK_IMAGE_LAYOUT_DEPTH_READ_ONLY_STENCIL_ATTACHMENT_OPTIMAL) ? - VK_IMAGE_ASPECT_DEPTH_BIT : - VK_IMAGE_ASPECT_COLOR_BIT; + newLayout == VK_IMAGE_LAYOUT_DEPTH_READ_ONLY_STENCIL_ATTACHMENT_OPTIMAL) + ? VK_IMAGE_ASPECT_DEPTH_BIT + : VK_IMAGE_ASPECT_COLOR_BIT; VkImageMemoryBarrier2 imageBarrier{ .sType = VK_STRUCTURE_TYPE_IMAGE_MEMORY_BARRIER_2, .srcStageMask = VK_PIPELINE_STAGE_2_ALL_COMMANDS_BIT, @@ -81,6 +81,27 @@ void vkutil::copyImageToImage(VkCommandBuffer cmd, VkImage source, VkImage desti vkCmdBlitImage2(cmd, &blitInfo); } +void vkutil::bufferHostWriteToShaderReadBarrier( + VkCommandBuffer cmd, VkBuffer buffer, VkDeviceSize offset, VkDeviceSize size) { + VkBufferMemoryBarrier2 barrier{VK_STRUCTURE_TYPE_BUFFER_MEMORY_BARRIER_2}; + barrier.srcStageMask = VK_PIPELINE_STAGE_2_HOST_BIT; + barrier.srcAccessMask = VK_ACCESS_2_HOST_WRITE_BIT; + barrier.dstStageMask = VK_PIPELINE_STAGE_2_VERTEX_SHADER_BIT | + VK_PIPELINE_STAGE_2_FRAGMENT_SHADER_BIT | + VK_PIPELINE_STAGE_2_COMPUTE_SHADER_BIT; + barrier.dstAccessMask = VK_ACCESS_2_SHADER_READ_BIT; + barrier.buffer = buffer; + barrier.offset = offset; + barrier.size = size; + + VkDependencyInfo dep{VK_STRUCTURE_TYPE_DEPENDENCY_INFO}; + dep.bufferMemoryBarrierCount = 1; + dep.pBufferMemoryBarriers = &barrier; + + vkCmdPipelineBarrier2(cmd, &dep); +} + + void vkutil::addDebugLabel(VkDevice device, VkImage image, const char* label) { const auto nameInfo = VkDebugUtilsObjectNameInfoEXT{ .sType = VK_STRUCTURE_TYPE_DEBUG_UTILS_OBJECT_NAME_INFO_EXT, @@ -135,8 +156,7 @@ vkutil::RenderInfo vkutil::createRenderingInfo(const RenderingInfoParams& params .sType = VK_STRUCTURE_TYPE_RENDERING_ATTACHMENT_INFO, .imageView = params.colorImageView, .imageLayout = VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL, - .loadOp = params.colorImageClearValue ? VK_ATTACHMENT_LOAD_OP_CLEAR : - VK_ATTACHMENT_LOAD_OP_LOAD, + .loadOp = params.colorImageClearValue ? VK_ATTACHMENT_LOAD_OP_CLEAR : VK_ATTACHMENT_LOAD_OP_LOAD, .storeOp = VK_ATTACHMENT_STORE_OP_STORE, }; if (params.colorImageClearValue) { @@ -150,8 +170,7 @@ vkutil::RenderInfo vkutil::createRenderingInfo(const RenderingInfoParams& params .sType = VK_STRUCTURE_TYPE_RENDERING_ATTACHMENT_INFO, .imageView = params.depthImageView, .imageLayout = VK_IMAGE_LAYOUT_DEPTH_ATTACHMENT_OPTIMAL, - .loadOp = params.depthImageClearValue ? VK_ATTACHMENT_LOAD_OP_CLEAR : - VK_ATTACHMENT_LOAD_OP_LOAD, + .loadOp = params.depthImageClearValue ? VK_ATTACHMENT_LOAD_OP_CLEAR : VK_ATTACHMENT_LOAD_OP_LOAD, .storeOp = VK_ATTACHMENT_STORE_OP_STORE, }; if (params.depthImageClearValue) { @@ -162,10 +181,10 @@ vkutil::RenderInfo vkutil::createRenderingInfo(const RenderingInfoParams& params ri.renderingInfo = VkRenderingInfo{ .sType = VK_STRUCTURE_TYPE_RENDERING_INFO, .renderArea = - VkRect2D{ - .offset = {}, - .extent = params.renderExtent, - }, + VkRect2D{ + .offset = {}, + .extent = params.renderExtent, + }, .layerCount = 1, .colorAttachmentCount = params.colorImageView ? 1u : 0u, .pColorAttachments = params.colorImageView ? &ri.colorAttachment : nullptr, @@ -203,8 +222,7 @@ VkShaderModule vkutil::loadShaderModule(const std::filesystem::path& path, VkDev return shaderModule; } -void addDebugLabel(VkDevice device, VkImage image, const char* label) -{ +void addDebugLabel(VkDevice device, VkImage image, const char* label) { const auto nameInfo = VkDebugUtilsObjectNameInfoEXT{ .sType = VK_STRUCTURE_TYPE_DEBUG_UTILS_OBJECT_NAME_INFO_EXT, .objectType = VK_OBJECT_TYPE_IMAGE, @@ -214,8 +232,7 @@ void addDebugLabel(VkDevice device, VkImage image, const char* label) vkSetDebugUtilsObjectNameEXT(device, &nameInfo); } -void addDebugLabel(VkDevice device, VkImageView imageView, const char* label) -{ +void addDebugLabel(VkDevice device, VkImageView imageView, const char* label) { const auto nameInfo = VkDebugUtilsObjectNameInfoEXT{ .sType = VK_STRUCTURE_TYPE_DEBUG_UTILS_OBJECT_NAME_INFO_EXT, .objectType = VK_OBJECT_TYPE_IMAGE_VIEW, @@ -225,8 +242,7 @@ void addDebugLabel(VkDevice device, VkImageView imageView, const char* label) vkSetDebugUtilsObjectNameEXT(device, &nameInfo); } -void addDebugLabel(VkDevice device, VkShaderModule shaderModule, const char* label) -{ +void addDebugLabel(VkDevice device, VkShaderModule shaderModule, const char* label) { const auto nameInfo = VkDebugUtilsObjectNameInfoEXT{ .sType = VK_STRUCTURE_TYPE_DEBUG_UTILS_OBJECT_NAME_INFO_EXT, .objectType = VK_OBJECT_TYPE_SHADER_MODULE, @@ -236,8 +252,7 @@ void addDebugLabel(VkDevice device, VkShaderModule shaderModule, const char* lab vkSetDebugUtilsObjectNameEXT(device, &nameInfo); } -void addDebugLabel(VkDevice device, VkPipeline pipeline, const char* label) -{ +void addDebugLabel(VkDevice device, VkPipeline pipeline, const char* label) { const auto nameInfo = VkDebugUtilsObjectNameInfoEXT{ .sType = VK_STRUCTURE_TYPE_DEBUG_UTILS_OBJECT_NAME_INFO_EXT, .objectType = VK_OBJECT_TYPE_PIPELINE, @@ -247,8 +262,7 @@ void addDebugLabel(VkDevice device, VkPipeline pipeline, const char* label) vkSetDebugUtilsObjectNameEXT(device, &nameInfo); } -void addDebugLabel(VkDevice device, VkPipelineLayout layout, const char* label) -{ +void addDebugLabel(VkDevice device, VkPipelineLayout layout, const char* label) { const auto nameInfo = VkDebugUtilsObjectNameInfoEXT{ .sType = VK_STRUCTURE_TYPE_DEBUG_UTILS_OBJECT_NAME_INFO_EXT, .objectType = VK_OBJECT_TYPE_PIPELINE_LAYOUT, @@ -258,8 +272,7 @@ void addDebugLabel(VkDevice device, VkPipelineLayout layout, const char* label) vkSetDebugUtilsObjectNameEXT(device, &nameInfo); } -void addDebugLabel(VkDevice device, VkBuffer buffer, const char* label) -{ +void addDebugLabel(VkDevice device, VkBuffer buffer, const char* label) { const auto nameInfo = VkDebugUtilsObjectNameInfoEXT{ .sType = VK_STRUCTURE_TYPE_DEBUG_UTILS_OBJECT_NAME_INFO_EXT, .objectType = VK_OBJECT_TYPE_BUFFER, @@ -269,8 +282,7 @@ void addDebugLabel(VkDevice device, VkBuffer buffer, const char* label) vkSetDebugUtilsObjectNameEXT(device, &nameInfo); } -void addDebugLabel(VkDevice device, VkSampler sampler, const char* label) -{ +void addDebugLabel(VkDevice device, VkSampler sampler, const char* label) { const auto nameInfo = VkDebugUtilsObjectNameInfoEXT{ .sType = VK_STRUCTURE_TYPE_DEBUG_UTILS_OBJECT_NAME_INFO_EXT, .objectType = VK_OBJECT_TYPE_SAMPLER, @@ -280,8 +292,7 @@ void addDebugLabel(VkDevice device, VkSampler sampler, const char* label) vkSetDebugUtilsObjectNameEXT(device, &nameInfo); } -vkutil::RenderInfo createRenderingInfo(const vkutil::RenderingInfoParams& params) -{ +vkutil::RenderInfo createRenderingInfo(const vkutil::RenderingInfoParams& params) { assert( (params.colorImageView || params.depthImageView != nullptr) && "Either draw or depth image should be present"); @@ -295,8 +306,7 @@ vkutil::RenderInfo createRenderingInfo(const vkutil::RenderingInfoParams& params .sType = VK_STRUCTURE_TYPE_RENDERING_ATTACHMENT_INFO, .imageView = params.colorImageView, .imageLayout = VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL, - .loadOp = params.colorImageClearValue ? VK_ATTACHMENT_LOAD_OP_CLEAR : - VK_ATTACHMENT_LOAD_OP_LOAD, + .loadOp = params.colorImageClearValue ? VK_ATTACHMENT_LOAD_OP_CLEAR : VK_ATTACHMENT_LOAD_OP_LOAD, .storeOp = VK_ATTACHMENT_STORE_OP_STORE, }; if (params.colorImageClearValue) { @@ -310,8 +320,7 @@ vkutil::RenderInfo createRenderingInfo(const vkutil::RenderingInfoParams& params .sType = VK_STRUCTURE_TYPE_RENDERING_ATTACHMENT_INFO, .imageView = params.depthImageView, .imageLayout = VK_IMAGE_LAYOUT_DEPTH_ATTACHMENT_OPTIMAL, - .loadOp = params.depthImageClearValue ? VK_ATTACHMENT_LOAD_OP_CLEAR : - VK_ATTACHMENT_LOAD_OP_LOAD, + .loadOp = params.depthImageClearValue ? VK_ATTACHMENT_LOAD_OP_CLEAR : VK_ATTACHMENT_LOAD_OP_LOAD, .storeOp = VK_ATTACHMENT_STORE_OP_STORE, }; if (params.depthImageClearValue) { @@ -322,10 +331,10 @@ vkutil::RenderInfo createRenderingInfo(const vkutil::RenderingInfoParams& params ri.renderingInfo = VkRenderingInfo{ .sType = VK_STRUCTURE_TYPE_RENDERING_INFO, .renderArea = - VkRect2D{ - .offset = {}, - .extent = params.renderExtent, - }, + VkRect2D{ + .offset = {}, + .extent = params.renderExtent, + }, .layerCount = 1, .colorAttachmentCount = params.colorImageView ? 1u : 0u, .pColorAttachments = params.colorImageView ? &ri.colorAttachment : nullptr, diff --git a/destrum/third_party/CMakeLists.txt b/destrum/third_party/CMakeLists.txt index 2fc3045..137acb1 100644 --- a/destrum/third_party/CMakeLists.txt +++ b/destrum/third_party/CMakeLists.txt @@ -52,4 +52,7 @@ add_subdirectory(json) add_subdirectory(spdlog) -add_subdirectory(tinygltf) \ No newline at end of file +add_subdirectory(tinygltf) + +add_subdirectory(tinyexr) +target_include_directories(tinyexr PUBLIC "${CMAKE_CURRENT_LIST_DIR}/tinyexr") diff --git a/destrum/third_party/tinyexr b/destrum/third_party/tinyexr new file mode 160000 index 0000000..90a147c --- /dev/null +++ b/destrum/third_party/tinyexr @@ -0,0 +1 @@ +Subproject commit 90a147c7114af250c5ac58cb04950c46e509cb8a diff --git a/lightkeeper/assets_src/starmap_2020_4k.exr b/lightkeeper/assets_src/starmap_2020_4k.exr new file mode 100644 index 0000000..62bb12b Binary files /dev/null and b/lightkeeper/assets_src/starmap_2020_4k.exr differ diff --git a/lightkeeper/assets_src/starmap_2020_8k.exr b/lightkeeper/assets_src/starmap_2020_8k.exr new file mode 100644 index 0000000..ad66a45 Binary files /dev/null and b/lightkeeper/assets_src/starmap_2020_8k.exr differ diff --git a/lightkeeper/src/Lightkeeper.cpp b/lightkeeper/src/Lightkeeper.cpp index 81b54a2..aa5b427 100644 --- a/lightkeeper/src/Lightkeeper.cpp +++ b/lightkeeper/src/Lightkeeper.cpp @@ -25,20 +25,13 @@ void LightKeeper::customInit() { const float aspectRatio = static_cast(m_params.renderSize.x) / static_cast(m_params.renderSize.y); camera.setAspectRatio(aspectRatio); - auto file = AssetFS::GetInstance().ReadBytes("engine://assetfstest.txt"); - std::string fileStr(file.begin(), file.end()); - spdlog::info("Read from assetfstest.txt: {}", fileStr); testMesh.name = "Test Mesh"; - // testMesh.vertices = vertices; - // testMesh.indices = indices; - auto list_of_models = ModelLoader::LoadGLTF_CPUMeshes_MergedPerMesh(AssetFS::GetInstance().GetFullPath("game://kitty.glb").generic_string()); testMesh = list_of_models[0]; testMeshID = meshCache.addMesh(gfxDevice, testMesh); spdlog::info("TestMesh uploaded with id: {}", testMeshID); - const auto testimgpath = AssetFS::GetInstance().GetFullPath("game://kitty.png"); - auto testimgID = gfxDevice.loadImageFromFile(testimgpath); + auto testimgID = gfxDevice.loadImageFromFile(AssetFS::GetInstance().GetFullPath("game://kitty.png")); spdlog::info("Test image loaded with id: {}", testimgID); testMaterialID = materialCache.addMaterial(gfxDevice, { .baseColor = glm::vec3(1.f), @@ -46,8 +39,6 @@ void LightKeeper::customInit() { }); spdlog::info("Test material created with id: {}", testMaterialID); - renderer.setSkyboxTexture(testimgID); - camera.SetRotation(glm::radians(glm::vec2(90.f, 0.f))); auto& scene = SceneManager::GetInstance().CreateScene("Main"); @@ -55,7 +46,26 @@ void LightKeeper::customInit() { auto testCube = std::make_shared("TestCube"); auto meshComp = testCube->AddComponent(); meshComp->SetMeshID(testMeshID); - meshComp->SetMaterialID(testMaterialID); + meshComp->SetMaterialID(testMaterialID);const int count = 100; + const float radius = 5.0f; + + const float orbitRadius = 5.0f; + + for (int i = 0; i < count; ++i) { + auto childCube = std::make_shared(fmt::format("ChildCube{}", i)); + + auto childMeshComp = childCube->AddComponent(); + childMeshComp->SetMeshID(testMeshID); + childMeshComp->SetMaterialID(testMaterialID); + + childCube->GetTransform().SetWorldScale(glm::vec3(0.1f)); + + // Add orbit + self spin + auto orbit = childCube->AddComponent(orbitRadius, glm::vec3(0.0f)); + orbit->Randomize(1337u + (uint32_t)i); // stable random per index + + scene.Add(childCube); + } testCube->AddComponent(glm::vec3(0, 1, 0), glm::radians(10.0f)); // spin around Y, rad/sec //rotate 180 around X axis testCube->GetTransform().SetLocalRotation(glm::quat(glm::vec3(glm::radians(180.0f), 0.0f, 0.0f))); @@ -65,46 +75,25 @@ void LightKeeper::customInit() { globeRoot->AddComponent(glm::vec3(0, 1, 0), 1.0f); // spin around Y, rad/sec scene.Add(globeRoot); - // const int count = 100; - // const float radius = 5.0f; - // - // const float orbitRadius = 5.0f; - // - // for (int i = 0; i < count; ++i) { - // auto childCube = std::make_shared(fmt::format("ChildCube{}", i)); - // - // auto childMeshComp = childCube->AddComponent(); - // childMeshComp->SetMeshID(testMeshID); - // childMeshComp->SetMaterialID(testMaterialID); - // - // childCube->GetTransform().SetWorldScale(glm::vec3(0.1f)); - // - // // Add orbit + self spin - // auto orbit = childCube->AddComponent(orbitRadius, glm::vec3(0.0f)); - // orbit->Randomize(1337u + (uint32_t)i); // stable random per index - // - // scene.Add(childCube); - // } - - // testCube->AddComponent(10, 5); scene.Add(testCube); // const auto skyboxID = AssetFS::GetInstance().GetFullPath("engine://textures/skybox.jpg"); - const auto skyboxID = AssetFS::GetInstance().GetFullPath("engine://textures/test-skybox.png"); - auto skyboxIDs = gfxDevice.loadImageFromFile(testimgpath); - + // const auto skyboxID = AssetFS::GetInstance().GetFullPath("engine://textures/mars.jpg"); + const auto skyboxID = AssetFS::GetInstance().GetCookedPathForFile("game://starmap_2020_4k.exr"); + // + // const auto skyboxID = AssetFS::GetInstance().GetFullPath("engine://textures/test-skybox.png"); + // const auto vertShaderPath = AssetFS::GetInstance().GetCookedPathForFile("engine://shaders/cubemap.vert"); const auto fragShaderPath = AssetFS::GetInstance().GetCookedPathForFile("engine://shaders/cubemap.frag"); - + // skyboxCubemap = std::make_unique(); skyboxCubemap->LoadCubeMap(skyboxID.generic_string()); skyboxCubemap->InitCubemapPipeline(vertShaderPath.generic_string(), fragShaderPath.generic_string()); skyboxCubemap->CreateCubeMap(); renderer.setSkyboxTexture(skyboxCubemap->GetCubeMapImageID()); - // skyboxCubemap->CreateCubeMap(); } void LightKeeper::customUpdate(float dt) {