diff --git a/examples/imgui_usage/CMakeLists.txt b/examples/imgui_usage/CMakeLists.txt index 1edb424..d3109c1 100644 --- a/examples/imgui_usage/CMakeLists.txt +++ b/examples/imgui_usage/CMakeLists.txt @@ -53,6 +53,20 @@ if (imgui_SOURCE_DIR) set_target_properties(imgui PROPERTIES FOLDER "dependencies") endif() +## STB_IMAGE ############################################################### + +FetchContent_Declare(stb_image + GIT_REPOSITORY https://github.com/Thomas-Chqt/stb_image.git + GIT_TAG a09b799a2ba95c14f511493db4ea59811b2fcc97 + GIT_SHALLOW 1 + GIT_PROGRESS TRUE + FIND_PACKAGE_ARGS +) +FetchContent_MakeAvailable(stb_image) +if (stb_image_SOURCE_DIR) + set_target_properties(stb_image PROPERTIES FOLDER "dependencies") +endif() + ################################################################## add_executable(imgui_usage) @@ -86,9 +100,10 @@ target_include_directories(imgui_usage PRIVATE "${CMAKE_CURRENT_SOURCE_DIR}") if(CMAKE_GENERATOR STREQUAL "Xcode") target_compile_definitions(imgui_usage PRIVATE "__XCODE__") endif() +target_compile_definitions(imgui_usage PRIVATE "RESOURCE_DIR=\"${CMAKE_CURRENT_SOURCE_DIR}/resources\"") target_compile_definitions(imgui_usage PRIVATE "GLFW_INCLUDE_NONE") -target_link_libraries(imgui_usage PRIVATE Graphics imgui) +target_link_libraries(imgui_usage PRIVATE Graphics imgui stb_image) if(APPLE AND NOT CMAKE_GENERATOR STREQUAL "Xcode") set(CODESIGN_IDENTITY "-" CACHE STRING "Codesigning identity for imgui_usage") diff --git a/examples/imgui_usage/imgui_usage.cpp b/examples/imgui_usage/imgui_usage.cpp index 3a48db4..36facbe 100644 --- a/examples/imgui_usage/imgui_usage.cpp +++ b/examples/imgui_usage/imgui_usage.cpp @@ -15,14 +15,17 @@ #include "Graphics/Surface.hpp" #include "Graphics/Enums.hpp" #include "Graphics/Swapchain.hpp" -#include "imgui.h" +#include "Graphics/Texture.hpp" #include +#include #include +#include #include #include #include +#include #if __XCODE__ #include @@ -99,6 +102,41 @@ class Application m_commandBufferPools.at(i) = m_device->newCommandBufferPool(); } + int width = 0; + int height = 0; + stbi_uc* textureBytes = stbi_load(RESOURCE_DIR"/MyImage01.jpg", &width, &height, nullptr, STBI_rgb_alpha); + assert(textureBytes); + + m_texture = m_device->newTexture(gfx::Texture::Descriptor{ + .type = gfx::TextureType::texture2d, + .width = static_cast(width), + .height = static_cast(height), + .pixelFormat = gfx::PixelFormat::RGBA8Unorm, + .usages = gfx::TextureUsage::copyDestination | gfx::TextureUsage::shaderRead, + .storageMode = gfx::ResourceStorageMode::deviceLocal + }); + assert(m_texture); + + std::shared_ptr stagingBuffer = m_device->newBuffer(gfx::Buffer::Descriptor{ + .size = static_cast(width) * static_cast(height) * pixelFormatSize(gfx::PixelFormat::RGBA8Unorm), + .usages = gfx::BufferUsage::copySource, + .storageMode = gfx::ResourceStorageMode::hostVisible + }); + assert(stagingBuffer); + + auto* bufferData = stagingBuffer->content(); + std::memcpy(bufferData, textureBytes, stagingBuffer->size()); + + stbi_image_free(textureBytes); + + std::shared_ptr commandBuffer = m_commandBufferPools.at(m_frameIdx)->get(); + commandBuffer->beginBlitPass(); + { + commandBuffer->copyBufferToTexture(stagingBuffer, 0, m_texture, 0); + } + commandBuffer->endBlitPass(); + m_device->submitCommandBuffers(commandBuffer); + ImGui::CreateContext(); ImGuiIO& io = ImGui::GetIO(); @@ -116,6 +154,7 @@ class Application } m_device->imguiInit({gfx::PixelFormat::BGRA8Unorm}); + } void loop() @@ -144,16 +183,21 @@ class Application if (m_lastCommandBuffers.at(m_frameIdx) != nullptr) { m_device->waitCommandBuffer(*m_lastCommandBuffers.at(m_frameIdx)); - m_lastCommandBuffers.at(m_frameIdx).reset(); m_commandBufferPools.at(m_frameIdx)->reset(); } m_device->imguiNewFrame(); ImGui_ImplGlfw_NewFrame(); + ImGui::NewFrame(); { - static bool showDemoWindow = true; - ImGui::ShowDemoWindow(&showDemoWindow); + ImGui::ShowDemoWindow(); + + ImGui::Begin("texture"); + if (m_texture->imTextureId().has_value() == false) + m_texture->initImTextureId(); + ImGui::Image(*m_texture->imTextureId(), ImVec2((float)m_texture->width(), (float)m_texture->height())); + ImGui::End(); } ImGui::Render(); @@ -177,19 +221,17 @@ class Application commandBuffer->beginRenderPass(framebuffer); { + commandBuffer->addSampledTexture(m_texture); commandBuffer->imGuiRenderDrawData(ImGui::GetDrawData()); } commandBuffer->endRenderPass(); commandBuffer->presentDrawable(drawable); - m_lastCommandBuffers.at(m_frameIdx) = commandBuffer; m_device->submitCommandBuffers(commandBuffer); + m_lastCommandBuffers.at(m_frameIdx) = commandBuffer.get(); - if (ImGui::GetIO().ConfigFlags & ImGuiConfigFlags_ViewportsEnable) - { - ImGui::UpdatePlatformWindows(); - ImGui::RenderPlatformWindowsDefault(); - } + ImGui::UpdatePlatformWindows(); + ImGui::RenderPlatformWindowsDefault(); m_frameIdx = (m_frameIdx + 1) % maxFrameInFlight; } @@ -197,8 +239,13 @@ class Application void clean() { + m_texture.reset(); + m_device->waitIdle(); + for (auto& pool : m_commandBufferPools) + pool->reset(); m_device->imguiShutdown(); + ImGui_ImplGlfw_Shutdown(); ImGui::DestroyContext(); glfwDestroyWindow(m_window); @@ -212,9 +259,11 @@ class Application std::unique_ptr m_device; std::unique_ptr m_swapchain; + std::shared_ptr m_texture; + uint8_t m_frameIdx = 0; std::array, maxFrameInFlight> m_commandBufferPools; - std::array, maxFrameInFlight> m_lastCommandBuffers = {}; + std::array m_lastCommandBuffers = {}; }; int main() diff --git a/examples/imgui_usage/resources/MyImage01.jpg b/examples/imgui_usage/resources/MyImage01.jpg new file mode 100644 index 0000000..ac02db2 Binary files /dev/null and b/examples/imgui_usage/resources/MyImage01.jpg differ diff --git a/include/Graphics/CommandBuffer.hpp b/include/Graphics/CommandBuffer.hpp index c91dbcc..d977af3 100644 --- a/include/Graphics/CommandBuffer.hpp +++ b/include/Graphics/CommandBuffer.hpp @@ -62,6 +62,8 @@ class CommandBuffer virtual void presentDrawable(const std::shared_ptr&) = 0; + virtual void addSampledTexture(const std::shared_ptr&) = 0; // for imgui + virtual ~CommandBuffer() = default; protected: diff --git a/include/Graphics/Texture.hpp b/include/Graphics/Texture.hpp index ce9c070..5145d1f 100644 --- a/include/Graphics/Texture.hpp +++ b/include/Graphics/Texture.hpp @@ -13,6 +13,7 @@ #include "Graphics/Enums.hpp" #include +#include namespace gfx { @@ -41,6 +42,11 @@ class Texture virtual TextureUsages usages() const = 0; virtual ResourceStorageMode storageMode() const = 0; +#if defined (GFX_IMGUI_ENABLED) + virtual void initImTextureId() = 0; + virtual std::optional imTextureId() const = 0; +#endif + virtual ~Texture() = default; protected: diff --git a/src/Metal/MetalCommandBuffer.hpp b/src/Metal/MetalCommandBuffer.hpp index 901d199..7ff01db 100644 --- a/src/Metal/MetalCommandBuffer.hpp +++ b/src/Metal/MetalCommandBuffer.hpp @@ -68,6 +68,8 @@ class MetalCommandBuffer : public CommandBuffer void presentDrawable(const std::shared_ptr&) override; + void addSampledTexture(const std::shared_ptr&) override; + inline id mtlCommandBuffer() const { return m_mtlCommandBuffer; } inline id commandEncoder() const { return m_commandEncoder; } diff --git a/src/Metal/MetalCommandBuffer.mm b/src/Metal/MetalCommandBuffer.mm index f2cab9b..4a8729d 100644 --- a/src/Metal/MetalCommandBuffer.mm +++ b/src/Metal/MetalCommandBuffer.mm @@ -19,6 +19,7 @@ #include "Metal/MetalBuffer.hpp" #include "Metal/MetalSampler.hpp" #include "Metal/MetalTexture.hpp" +#include #if defined(GFX_IMGUI_ENABLED) # include "Metal/imgui_impl_metal.h" #endif @@ -267,6 +268,12 @@ [m_mtlCommandBuffer presentDrawable:drawable->mtlDrawable()]; }} +void MetalCommandBuffer::addSampledTexture(const std::shared_ptr& aTexture) +{ + auto texture = std::dynamic_pointer_cast(aTexture); + m_usedTextures.insert(texture); +} + MetalCommandBuffer& MetalCommandBuffer::operator = (MetalCommandBuffer&& other) noexcept { @autoreleasepool { if (this != &other) diff --git a/src/Metal/MetalTexture.hpp b/src/Metal/MetalTexture.hpp index 27c8522..342578c 100644 --- a/src/Metal/MetalTexture.hpp +++ b/src/Metal/MetalTexture.hpp @@ -41,6 +41,11 @@ class MetalTexture : public Texture inline TextureUsages usages() const override { return m_usages; }; inline ResourceStorageMode storageMode() const override { return m_storageMode; }; +#if defined (GFX_IMGUI_ENABLED) + inline void initImTextureId() override {} // no-op + inline std::optional imTextureId() const override { return std::bit_cast((__bridge void*)m_mtlTexture); } +#endif + inline id mtltexture() const { return m_mtlTexture; } inline void setMtlTexture(const id& t) { m_mtlTexture = t; } diff --git a/src/Vulkan/VulkanCommandBuffer.cpp b/src/Vulkan/VulkanCommandBuffer.cpp index 2fa236c..d3222b9 100644 --- a/src/Vulkan/VulkanCommandBuffer.cpp +++ b/src/Vulkan/VulkanCommandBuffer.cpp @@ -20,6 +20,7 @@ #include "Vulkan/VulkanSampler.hpp" #include "Vulkan/VulkanTexture.hpp" #include "Vulkan/VulkanEnums.hpp" +#include #if defined(GFX_IMGUI_ENABLED) # include "Vulkan/imgui_impl_vulkan.h" #endif @@ -539,4 +540,33 @@ void VulkanCommandBuffer::presentDrawable(const std::shared_ptr& aDraw m_presentedDrawables.insert(drawable); } +void VulkanCommandBuffer::addSampledTexture(const std::shared_ptr& aTexture) +{ + auto texture = std::dynamic_pointer_cast(aTexture); + std::vector imageMemoryBarriers; + + ImageSyncRequest syncReq{}; + syncReq.stageMask = vk::PipelineStageFlagBits2::eFragmentShader; + syncReq.accessMask = vk::AccessFlagBits2::eShaderRead; + syncReq.layout = vk::ImageLayout::eShaderReadOnlyOptimal; + syncReq.preserveContent = true; + + auto it = m_imageFinalSyncStates.find(texture); + if (it != m_imageFinalSyncStates.end()) { + auto barrier = syncImage(it->second, syncReq); // will update the final sync state + if (barrier.has_value()) { + barrier->setImage(texture->vkImage()); + barrier->setSubresourceRange(texture->subresourceRange()); + imageMemoryBarriers.push_back(*barrier); + } + } else { + auto [it1, res1] = m_imageSyncRequests.insert(std::make_pair(texture, syncReq)); + assert(res1); + (void)res1; + auto [it2, res2] = m_imageFinalSyncStates.insert(std::make_pair(texture, imageStateAfterSync(syncReq))); + assert(res2); + (void)res2; + } +} + } diff --git a/src/Vulkan/VulkanCommandBuffer.hpp b/src/Vulkan/VulkanCommandBuffer.hpp index 8dbcc3c..4a122d7 100644 --- a/src/Vulkan/VulkanCommandBuffer.hpp +++ b/src/Vulkan/VulkanCommandBuffer.hpp @@ -16,12 +16,14 @@ #include "Graphics/Buffer.hpp" #include "Graphics/ParameterBlock.hpp" +#include "Graphics/Texture.hpp" #include "Vulkan/VulkanBuffer.hpp" #include "Vulkan/VulkanGraphicsPipeline.hpp" #include "Vulkan/VulkanSampler.hpp" #include "Vulkan/VulkanTexture.hpp" #include "Vulkan/VulkanDrawable.hpp" #include "Vulkan/VulkanParameterBlock.hpp" +#include namespace gfx { @@ -64,6 +66,8 @@ class VulkanCommandBuffer : public CommandBuffer void presentDrawable(const std::shared_ptr&) override; + void addSampledTexture(const std::shared_ptr&) override; // for imgui + const vk::CommandBuffer& vkCommandBuffer() const { return m_vkCommandBuffer; } diff --git a/src/Vulkan/VulkanTexture.cpp b/src/Vulkan/VulkanTexture.cpp index c06fc5a..b6f42a4 100644 --- a/src/Vulkan/VulkanTexture.cpp +++ b/src/Vulkan/VulkanTexture.cpp @@ -9,8 +9,15 @@ #include "Vulkan/VulkanTexture.hpp" #include "Graphics/Enums.hpp" +#include "Graphics/Sampler.hpp" +#include "Graphics/Texture.hpp" #include "Vulkan/VulkanDevice.hpp" #include "Vulkan/VulkanEnums.hpp" +#include "Vulkan/VulkanSampler.hpp" + +#if defined (GFX_IMGUI_ENABLED) +#include "imgui_impl_vulkan.h" +#endif namespace gfx { @@ -98,8 +105,26 @@ VulkanTexture::VulkanTexture(const VulkanDevice* device, const Texture::Descript m_vkImageView = m_device->vkDevice().createImageView(imageViewCreateInfo); } +#if defined (GFX_IMGUI_ENABLED) +void VulkanTexture::initImTextureId() +{ + std::shared_ptr aSampler = m_device->newSampler(Sampler::Descriptor{}); + auto vulkanSampler = std::dynamic_pointer_cast(aSampler); + assert(vulkanSampler); + + m_imTextureIdSampler = vulkanSampler; + if (m_imTextureId.has_value()) + ImGui_ImplVulkan_RemoveTexture(std::bit_cast(*m_imTextureId)); + m_imTextureId = std::bit_cast(ImGui_ImplVulkan_AddTexture(m_imTextureIdSampler->vkSampler(), m_vkImageView, VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL)); +} +#endif + VulkanTexture::~VulkanTexture() { +#if defined (GFX_IMGUI_ENABLED) + if (m_imTextureId.has_value()) + ImGui_ImplVulkan_RemoveTexture(std::bit_cast(*m_imTextureId)); +#endif m_device->vkDevice().destroyImageView(m_vkImageView); if (m_allocation != VK_NULL_HANDLE) vmaDestroyImage(m_device->allocator(), m_vkImage, m_allocation); diff --git a/src/Vulkan/VulkanTexture.hpp b/src/Vulkan/VulkanTexture.hpp index a1a2d14..080b446 100644 --- a/src/Vulkan/VulkanTexture.hpp +++ b/src/Vulkan/VulkanTexture.hpp @@ -12,8 +12,10 @@ #include "Graphics/Texture.hpp" #include "Graphics/Enums.hpp" +#include "Graphics/Sampler.hpp" #include "Vulkan/Sync.hpp" +#include "Vulkan/VulkanSampler.hpp" namespace gfx { @@ -37,6 +39,11 @@ class VulkanTexture : public Texture inline TextureUsages usages() const override { return m_usages; }; inline ResourceStorageMode storageMode() const override { return m_storageMode; }; +#if defined (GFX_IMGUI_ENABLED) + void initImTextureId() override; + inline std::optional imTextureId() const override { return m_imTextureId; } +#endif + inline const vk::Image& vkImage() const { return m_vkImage; } inline const vk::ImageSubresourceRange& subresourceRange() const { return m_subresourceRange; } @@ -63,6 +70,11 @@ class VulkanTexture : public Texture ImageSyncState m_syncState; +#if defined (GFX_IMGUI_ENABLED) + std::optional m_imTextureId; +#endif + std::shared_ptr m_imTextureIdSampler; + public: VulkanTexture& operator=(const VulkanTexture&) = delete; VulkanTexture& operator=(VulkanTexture&&) = delete;