diff --git a/runtime/Includes/Core/Application.inl b/runtime/Includes/Core/Application.inl index 41d4006..7e6cfd1 100644 --- a/runtime/Includes/Core/Application.inl +++ b/runtime/Includes/Core/Application.inl @@ -19,7 +19,7 @@ Error("invalid image ptr (NULL)"); \ retval; \ } \ - else if(!m_image_registry.IsTextureKnown(img)) \ + else if(!m_image_registry.IsTextureKnown(static_cast(img))) \ { \ Error("invalid image ptr"); \ retval; \ diff --git a/runtime/Includes/Core/Graphics.h b/runtime/Includes/Core/Graphics.h index e97ea9d..406a39c 100644 --- a/runtime/Includes/Core/Graphics.h +++ b/runtime/Includes/Core/Graphics.h @@ -29,7 +29,7 @@ namespace mlx inline void LoadFont(const std::filesystem::path& filepath, float scale); - inline void TryEraseTextureFromRegistry(NonOwningPtr texture) noexcept; + inline void TryEraseSpritesInScene(NonOwningPtr texture) noexcept; [[nodiscard]] MLX_FORCEINLINE bool HasWindow() const noexcept { return m_has_window; } [[nodiscard]] MLX_FORCEINLINE Renderer& GetRenderer() { return m_renderer; } @@ -42,6 +42,8 @@ namespace mlx std::shared_ptr p_window; std::unique_ptr p_scene; + std::uint64_t m_current_depth = 0; + std::size_t m_width = 0; std::size_t m_height = 0; diff --git a/runtime/Includes/Core/Graphics.inl b/runtime/Includes/Core/Graphics.inl index e025e03..8e5a0cd 100644 --- a/runtime/Includes/Core/Graphics.inl +++ b/runtime/Includes/Core/Graphics.inl @@ -7,6 +7,7 @@ namespace mlx { MLX_PROFILE_FUNCTION(); p_scene->ResetSprites(); + m_current_depth = 0; } void GraphicsSupport::PixelPut(int x, int y, std::uint32_t color) noexcept @@ -22,6 +23,15 @@ namespace mlx void GraphicsSupport::TexturePut(NonOwningPtr texture, int x, int y) { MLX_PROFILE_FUNCTION(); + NonOwningPtr sprite = p_scene->GetSpriteFromTextureAndPosition(texture, Vec2f{ static_cast(x), static_cast(y) }); + if(!sprite) + { + Sprite& new_sprite = p_scene->CreateSprite(texture); + new_sprite.SetPosition(Vec3f{ static_cast(x), static_cast(y), static_cast(m_current_depth) }); + } + else + sprite->SetPosition(Vec3f{ static_cast(x), static_cast(y), static_cast(m_current_depth) }); + m_current_depth++; } void GraphicsSupport::LoadFont(const std::filesystem::path& filepath, float scale) @@ -29,8 +39,9 @@ namespace mlx MLX_PROFILE_FUNCTION(); } - void GraphicsSupport::TryEraseTextureFromRegistry(NonOwningPtr texture) noexcept + void GraphicsSupport::TryEraseSpritesInScene(NonOwningPtr texture) noexcept { MLX_PROFILE_FUNCTION(); + p_scene->TryEraseSpriteFromTexture(texture); } } diff --git a/runtime/Includes/Graphics/Scene.h b/runtime/Includes/Graphics/Scene.h index 760c6fb..7f1d0ec 100644 --- a/runtime/Includes/Graphics/Scene.h +++ b/runtime/Includes/Graphics/Scene.h @@ -17,7 +17,9 @@ namespace mlx public: Scene(SceneDescriptor desc); - Sprite& CreateSprite(std::shared_ptr texture) noexcept; + Sprite& CreateSprite(NonOwningPtr texture) noexcept; + NonOwningPtr GetSpriteFromTextureAndPosition(NonOwningPtr texture, const Vec2f& position) const; + void TryEraseSpriteFromTexture(NonOwningPtr texture); inline void ResetSprites() { m_sprites.clear(); } diff --git a/runtime/Includes/Graphics/Sprite.h b/runtime/Includes/Graphics/Sprite.h index ce7c7bd..68a1bfb 100644 --- a/runtime/Includes/Graphics/Sprite.h +++ b/runtime/Includes/Graphics/Sprite.h @@ -14,7 +14,7 @@ namespace mlx friend class Render2DPass; public: - Sprite(std::shared_ptr texture); + Sprite(NonOwningPtr texture); inline void SetColor(Vec4f color) noexcept { m_color = color; } inline void SetPosition(Vec3f position) noexcept { m_position = position; } @@ -22,7 +22,7 @@ namespace mlx [[nodiscard]] inline const Vec4f& GetColor() const noexcept { return m_color; } [[nodiscard]] inline const Vec3f& GetPosition() const noexcept { return m_position; } [[nodiscard]] inline std::shared_ptr GetMesh() const { return p_mesh; } - [[nodiscard]] inline std::shared_ptr GetTexture() const { return p_texture; } + [[nodiscard]] inline NonOwningPtr GetTexture() const { return p_texture; } ~Sprite() = default; @@ -43,7 +43,7 @@ namespace mlx private: DescriptorSet m_set; - std::shared_ptr p_texture; + NonOwningPtr p_texture; std::shared_ptr p_mesh; Vec4f m_color = Vec4f{ 1.0f, 1.0f, 1.0f, 1.0f }; Vec3f m_position = Vec3f{ 0.0f, 0.0f, 0.0f }; diff --git a/runtime/Includes/Renderer/Image.h b/runtime/Includes/Renderer/Image.h index 86bff31..31b8921 100644 --- a/runtime/Includes/Renderer/Image.h +++ b/runtime/Includes/Renderer/Image.h @@ -47,7 +47,7 @@ namespace mlx virtual ~Image() = default; - private: + protected: VmaAllocation m_allocation; VkImage m_image = VK_NULL_HANDLE; VkImageView m_image_view = VK_NULL_HANDLE; @@ -84,6 +84,7 @@ namespace mlx { Init(std::move(pixels), width, height, format, is_multisampled); } + inline void Init(CPUBuffer pixels, std::uint32_t width, std::uint32_t height, VkFormat format = VK_FORMAT_R8G8B8A8_SRGB, bool is_multisampled = false) { Image::Init(ImageType::Color, width, height, format, VK_IMAGE_TILING_OPTIMAL, VK_IMAGE_USAGE_TRANSFER_DST_BIT | VK_IMAGE_USAGE_SAMPLED_BIT | VK_IMAGE_USAGE_COLOR_ATTACHMENT_BIT, is_multisampled); @@ -91,12 +92,12 @@ namespace mlx Image::CreateSampler(); if(pixels) { - TransitionLayout(VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL); GPUBuffer staging_buffer; std::size_t size = width * height * kvfFormatSize(format); staging_buffer.Init(BufferType::Staging, size, VK_BUFFER_USAGE_TRANSFER_SRC_BIT, pixels); VkCommandBuffer cmd = kvfCreateCommandBuffer(RenderCore::Get().GetDevice()); kvfBeginCommandBuffer(cmd, VK_COMMAND_BUFFER_USAGE_ONE_TIME_SUBMIT_BIT); + TransitionLayout(VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL, cmd); kvfCopyBufferToImage(cmd, Image::Get(), staging_buffer.Get(), staging_buffer.GetOffset(), VK_IMAGE_ASPECT_COLOR_BIT, { width, height, 1 }); vkEndCommandBuffer(cmd); VkFence fence = kvfCreateFence(RenderCore::Get().GetDevice()); @@ -104,13 +105,26 @@ namespace mlx kvfDestroyFence(RenderCore::Get().GetDevice(), fence); staging_buffer.Destroy(); } - if(!pixels) - TransitionLayout(VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL); - else - TransitionLayout(VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL); + TransitionLayout(VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL); } + + void SetPixel(int x, int y, std::uint32_t color) noexcept; + int GetPixel(int x, int y) noexcept; + + void Update(VkCommandBuffer cmd) const; + ~Texture() override { Destroy(); } + + private: + void OpenCPUBuffer(); + + private: + std::vector m_cpu_buffer; + std::optional m_staging_buffer; + bool m_has_been_modified = false; }; + + Texture* StbTextureLoad(const std::filesystem::path& file, int* w, int* h); } #endif diff --git a/runtime/Sources/Core/Application.cpp b/runtime/Sources/Core/Application.cpp index 9ed05fa..b82cb4f 100644 --- a/runtime/Sources/Core/Application.cpp +++ b/runtime/Sources/Core/Application.cpp @@ -14,7 +14,7 @@ namespace mlx { }, "__Application" }); - m_fps.init(); + m_fps.Init(); SDLManager::Get().Init(); } @@ -26,7 +26,6 @@ namespace mlx { if(!m_fps.Update()) continue; - m_in.Update(); if(f_loop_hook) f_loop_hook(p_param); @@ -37,27 +36,15 @@ namespace mlx gs->Render(); } } - - RenderCore::Get().GetSingleTimeCmdManager().UpdateSingleTimesCmdBuffersSubmitState(); - - for(auto& gs : m_graphics) - { - if(!gs) - continue; - for(int i = 0; i < MAX_FRAMES_IN_FLIGHT; i++) - gs->GetRenderer().GetCmdBuffer(i).WaitForExecution(); - } + RenderCore::Get().WaitDeviceIdle(); } void* Application::NewTexture(int w, int h) { MLX_PROFILE_FUNCTION(); - Texture* texture = new Texture; - #ifdef DEBUG - texture->Create(nullptr, w, h, VK_FORMAT_R8G8B8A8_UNORM, "__mlx_unamed_user_texture"); - #else - texture->Create(nullptr, w, h, VK_FORMAT_R8G8B8A8_UNORM, nullptr); - #endif + Texture* texture; + try { texture = new Texture({}, w, h); } + catch(...) { return NULL; } m_image_registry.RegisterTexture(texture); return texture; } @@ -66,6 +53,8 @@ namespace mlx { MLX_PROFILE_FUNCTION(); Texture* texture = StbTextureLoad(file, w, h); + if(texture == nullptr) + return NULL; // NULL for C compatibility m_image_registry.RegisterTexture(texture); return texture; } @@ -74,7 +63,7 @@ namespace mlx { MLX_PROFILE_FUNCTION(); RenderCore::Get().WaitDeviceIdle(); // TODO : synchronize with another method than waiting for GPU to be idle - if(!m_image_registry.Find(ptr)) + if(!m_image_registry.IsTextureKnown(static_cast(ptr))) { Error("invalid image ptr"); return; @@ -85,10 +74,10 @@ namespace mlx Error("trying to destroy a texture that has already been destroyed"); else texture->Destroy(); - for(auto& gs : _graphics) + for(auto& gs : m_graphics) { if(gs) - gs->TryEraseTextureFromManager(texture); + gs->TryEraseSpritesInScene(texture); } m_image_registry.UnregisterTexture(texture); delete texture; @@ -96,8 +85,6 @@ namespace mlx Application::~Application() { - TextLibrary::Get().ClearLibrary(); - FontLibrary::Get().ClearLibrary(); SDLManager::Get().Shutdown(); } } diff --git a/runtime/Sources/Core/Bridge.cpp b/runtime/Sources/Core/Bridge.cpp index ca0ecef..89d741d 100644 --- a/runtime/Sources/Core/Bridge.cpp +++ b/runtime/Sources/Core/Bridge.cpp @@ -1,7 +1,7 @@ #include #include -#include +#include #include #include @@ -18,7 +18,7 @@ extern "C" { if(__mlx_ptr != nullptr) { - Error("MLX cannot be initialized multiple times"); + mlx::Error("MLX cannot be initialized multiple times"); return nullptr; } mlx::MemManager::Get(); // just to initialize the C garbage collector diff --git a/runtime/Sources/Core/Graphics.cpp b/runtime/Sources/Core/Graphics.cpp index 2f9c720..15d578b 100644 --- a/runtime/Sources/Core/Graphics.cpp +++ b/runtime/Sources/Core/Graphics.cpp @@ -64,7 +64,7 @@ namespace mlx RenderCore::Get().WaitDeviceIdle(); p_scene.reset(); m_scene_renderer.Destroy(); - m_renderer->Destroy(); + m_renderer.Destroy(); if(p_window) p_window->Destroy(); } diff --git a/runtime/Sources/Graphics/Scene.cpp b/runtime/Sources/Graphics/Scene.cpp index c95fee7..46821a9 100644 --- a/runtime/Sources/Graphics/Scene.cpp +++ b/runtime/Sources/Graphics/Scene.cpp @@ -12,10 +12,31 @@ namespace mlx m_depth.Init(m_descriptor.renderer->GetSwapchainImages().back().GetWidth(), m_descriptor.renderer->GetSwapchainImages().back().GetHeight()); } - Sprite& Scene::CreateSprite(std::shared_ptr texture) noexcept + Sprite& Scene::CreateSprite(NonOwningPtr texture) noexcept { std::shared_ptr sprite = std::make_shared(texture); m_sprites.push_back(sprite); return *sprite; } + + NonOwningPtr Scene::GetSpriteFromTextureAndPosition(NonOwningPtr texture, const Vec2f& position) const + { + auto it = std::find_if(m_sprites.begin(), m_sprites.end(), [texture, position](const Sprite& sprite) + { + return sprite.GetPosition().x == position.x && sprite.GetPosition().y == position.y && sprite.GetTexture() == texture; + }); + return (it != m_sprites.end() ? &(*it) : nullptr); + } + + void Scene::TryEraseSpriteFromTexture(NonOwningPtr texture) + { + do + { + auto it = std::find_if(m_sprites.begin(), m_sprites.end(), [texture, position](const Sprite& sprite) + { + return sprite.GetPosition().x == position.x && sprite.GetPosition().y == position.y && sprite.GetTexture() == texture; + }); + m_sprites.erase(it); + } while(it != m_sprites.end()); + } } diff --git a/runtime/Sources/Graphics/Sprite.cpp b/runtime/Sources/Graphics/Sprite.cpp index dbc5e3d..e6792ae 100644 --- a/runtime/Sources/Graphics/Sprite.cpp +++ b/runtime/Sources/Graphics/Sprite.cpp @@ -35,7 +35,7 @@ namespace mlx return mesh; } - Sprite::Sprite(std::shared_ptr texture) + Sprite::Sprite(NonOwningPtr texture) { Verify((bool)texture, "Sprite: invalid texture"); p_mesh = CreateQuad(0, 0, texture->GetWidth(), texture->GetHeight()); diff --git a/runtime/Sources/Platform/Inputs.cpp b/runtime/Sources/Platform/Inputs.cpp index 770c59b..7dc8377 100644 --- a/runtime/Sources/Platform/Inputs.cpp +++ b/runtime/Sources/Platform/Inputs.cpp @@ -4,22 +4,4 @@ namespace mlx { - void Input::update() - { - MLX_PROFILE_FUNCTION(); - _xRel = 0; - _yRel = 0; - - glfwPollEvents(); - - static int i = 0; - i++; - if(i >= 500) - { - auto& hooks = _events_hooks[0]; - auto& win_hook = hooks[MLX_WINDOW_EVENT]; - if(win_hook.hook) - win_hook.hook(0, win_hook.param); - } - } } diff --git a/runtime/Sources/Renderer/Image.cpp b/runtime/Sources/Renderer/Image.cpp index 971e7df..591bb96 100644 --- a/runtime/Sources/Renderer/Image.cpp +++ b/runtime/Sources/Renderer/Image.cpp @@ -50,7 +50,10 @@ namespace mlx return; bool is_single_time_cmd_buffer = (cmd == VK_NULL_HANDLE); if(is_single_time_cmd_buffer) + { cmd = kvfCreateCommandBuffer(RenderCore::Get().GetDevice()); + kvfBeginCommandBuffer(cmd, VK_COMMAND_BUFFER_USAGE_ONE_TIME_SUBMIT_BIT); + } KvfImageType kvf_type = KVF_IMAGE_OTHER; switch(m_type) { @@ -61,6 +64,13 @@ namespace mlx } kvfTransitionImageLayout(RenderCore::Get().GetDevice(), m_image, kvf_type, cmd, m_format, m_layout, new_layout, is_single_time_cmd_buffer); m_layout = new_layout; + if(is_single_time_cmd_buffer) + { + vkEndCommandBuffer(cmd); + VkFence fence = kvfCreateFence(RenderCore::Get().GetDevice()); + kvfSubmitSingleTimeCommandBuffer(RenderCore::Get().GetDevice(), cmd, KVF_GRAPHICS_QUEUE, fence); + kvfDestroyFence(RenderCore::Get().GetDevice(), fence); + } } void Image::Clear(VkCommandBuffer cmd, Vec4f color) @@ -112,4 +122,105 @@ namespace mlx RenderCore::Get().GetAllocator().DestroyImage(m_allocation, m_image); m_image = VK_NULL_HANDLE; } + + void Texture::SetPixel(int x, int y, std::uint32_t color) noexcept + { + MLX_PROFILE_FUNCTION(); + if(x < 0 || y < 0 || static_cast(x) > m_width || static_cast(y) > m_height) + return; + if(!m_staging_buffer.has_value()) + OpenCPUBuffer(); + m_cpu_buffer[(y * m_width) + x] = color; + m_has_been_modified = true; + } + + int GetPixel(int x, int y) noexcept + { + MLX_PROFILE_FUNCTION(); + if(x < 0 || y < 0 || static_cast(x) > getWidth() || static_cast(y) > getHeight()) + return 0; + if(!m_staging_buffer.has_value()) + OpenCPUBuffer(); + std::uint32_t color = m_cpu_buffer[(y * m_width) + x]; + std::uint8_t* bytes = reinterpret_cast(&color); + std::uint8_t tmp = bytes[0]; + bytes[0] = bytes[2]; + bytes[2] = tmp; + return *reinterpret_cast(bytes); + } + + void Update(VkCommandBuffer cmd) const + { + if(!m_has_been_modified) + return; + std::memcpy(m_staging_buffer.GetMap(), m_cpu_buffer.data(), m_cpu_buffer.size() * kvfGetFormatSize(m_format)); + + VkImageLayout old_layout = m_layout; + VkCommandBuffer cmd = kvfCreateCommandBuffer(RenderCore::Get().GetDevice()); + kvfBeginCommandBuffer(cmd, VK_COMMAND_BUFFER_USAGE_ONE_TIME_SUBMIT_BIT); + TransitionLayout(VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL, cmd); + kvfCopyBufferToImage(cmd, Image::Get(), staging_buffer.Get(), staging_buffer.GetOffset(), VK_IMAGE_ASPECT_COLOR_BIT, { width, height, 1 }); + TransitionLayout(old_layout, cmd); + vkEndCommandBuffer(cmd); + VkFence fence = kvfCreateFence(RenderCore::Get().GetDevice()); + kvfSubmitSingleTimeCommandBuffer(RenderCore::Get().GetDevice(), cmd, KVF_GRAPHICS_QUEUE, fence); + kvfDestroyFence(RenderCore::Get().GetDevice(), fence); + + m_has_been_modified = false; + } + + void Texture::OpenCPUBuffer() + { + MLX_PROFILE_FUNCTION(); + if(m_staging_buffer.has_value()) + return; + DebugLog("Texture : enabling CPU mapping"); + m_staging_buffer.emplace(); + std::size_t size = m_width * m_height * kvfGetFormatSize(m_format); + m_staging_buffer->Init(BufferType::Staging, size, VK_BUFFER_USAGE_TRANSFER_SRC_BIT | VK_BUFFER_USAGE_TRANSFER_DST_BIT, {}); + + VkImageLayout old_layout = m_layout; + VkCommandBuffer cmd = kvfCreateCommandBuffer(RenderCore::Get().GetDevice()); + kvfBeginCommandBuffer(cmd, VK_COMMAND_BUFFER_USAGE_ONE_TIME_SUBMIT_BIT); + TransitionLayout(VK_IMAGE_LAYOUT_TRANSFER_SRC_OPTIMAL, cmd); + kvfImageToBuffer(cmd, m_image, m_staging_buffer.Get(), m_staging_buffer.GetOffset(), VK_IMAGE_ASPECT_COLOR_BIT, { m_width, m_height, 1 }); + TransitionLayout(old_layout, cmd); + vkEndCommandBuffer(cmd); + VkFence fence = kvfCreateFence(RenderCore::Get().GetDevice()); + kvfSubmitSingleTimeCommandBuffer(RenderCore::Get().GetDevice(), cmd, KVF_GRAPHICS_QUEUE, fence); + kvfDestroyFence(RenderCore::Get().GetDevice(), fence); + + m_cpu_buffer.resize(m_width * m_height); + std::memcpy(m_cpu_buffer.data(), m_staging_buffer.GetMap(), m_cpu_buffer.size()); + } + + Texture* StbTextureLoad(const std::filesystem::path& file, int* w, int* h) + { + MLX_PROFILE_FUNCTION(); + std::string filename = file.string(); + + if(!std::filesystem::exists(file)) + { + Error("Image : file not found %", file); + return nullptr; + } + if(stbi_is_hdr(filename.c_str())) + { + Error("Texture : unsupported image format %", file); + return nullptr; + } + int dummy_w; + int dummy_h; + int channels; + std::uint8_t* data = stbi_load(filename.c_str(), (w == nullptr ? &dummy_w : w), (h == nullptr ? &dummy_h : h), &channels, 4); + CPUBuffer buffer((w == nullptr ? dummy_w : *w) * (h == nullptr ? dummy_h : *h) * 4); + std::memcpy(buffer.GetData(), data, buffer.GetSize()); + Texture* texture; + + try { texture = new Texture(buffer, (w == nullptr ? dummy_w : *w), (h == nullptr ? dummy_h : *h)); } + catch(...) { return NULL; } + + stbi_image_free(data); + return texture; + } } diff --git a/runtime/Sources/Renderer/RenderPasses/2DPass.cpp b/runtime/Sources/Renderer/RenderPasses/2DPass.cpp index bea3c70..57cff33 100644 --- a/runtime/Sources/Renderer/RenderPasses/2DPass.cpp +++ b/runtime/Sources/Renderer/RenderPasses/2DPass.cpp @@ -102,6 +102,8 @@ namespace mlx sprite_data.color = sprite->GetColor(); if(!sprite->IsSetInit()) sprite->UpdateDescriptorSet(*p_texture_set); + Verify((bool)sprite->GetTexture(), "a sprite has no texture attached"); + sprite->GetTexture()->Update(cmd); sprite->Bind(frame_index, cmd); std::array sets = { p_viewer_data_set->GetSet(frame_index), sprite->GetSet(frame_index) }; vkCmdBindDescriptorSets(cmd, m_pipeline.GetPipelineBindPoint(), m_pipeline.GetPipelineLayout(), 0, sets.size(), sets.data(), 0, nullptr); diff --git a/third_party/kvf.h b/third_party/kvf.h index a8b6008..4878ef9 100755 --- a/third_party/kvf.h +++ b/third_party/kvf.h @@ -92,7 +92,8 @@ void kvfAddLayer(const char* layer); VkInstance kvfCreateInstance(const char** extensionsEnabled, uint32_t extensionsCount); void kvfDestroyInstance(VkInstance instance); -VkPhysicalDevice kvfPickFirstPhysicalDevice(VkInstance instance); +// If surfaces given to theses functions are VK_NULL_HANDLE no present queues will be searched and thus kvfQueuePresentKHR will not work +VkPhysicalDevice kvfPickFirstPhysicalDevice(VkInstance instance, VkSurfaceKHR surface); VkPhysicalDevice kvfPickGoodDefaultPhysicalDevice(VkInstance instance, VkSurfaceKHR surface); VkPhysicalDevice kvfPickGoodPhysicalDevice(VkInstance instance, VkSurfaceKHR surface, const char** deviceExtensions, uint32_t deviceExtensionsCount); @@ -101,7 +102,7 @@ uint32_t kvfGetDeviceQueueFamily(VkDevice device, KvfQueueType queue); bool kvfQueuePresentKHR(VkDevice device, VkSemaphore wait, VkSwapchainKHR swapchain, uint32_t image_index); // return false when the swapchain must be recreated VkDevice kvfCreateDefaultDevice(VkPhysicalDevice physical); -VkDevice kvfCreateDevice(VkPhysicalDevice physical, const char** extensions, uint32_t extensions_count); +VkDevice kvfCreateDevice(VkPhysicalDevice physical, const char** extensions, uint32_t extensions_count, VkPhysicalDeviceFeatures* features); void kvfDestroyDevice(VkDevice device); VkFence kvfCreateFence(VkDevice device); @@ -118,12 +119,12 @@ uint32_t kvfGetSwapchainMinImagesCount(VkSwapchainKHR swapchain); VkExtent2D kvfGetSwapchainImagesSize(VkSwapchainKHR swapchain); void kvfDestroySwapchainKHR(VkDevice device, VkSwapchainKHR swapchain); -VkImage kvfCreateImage(VkDevice device, uint32_t width, uint32_t height, VkFormat format, VkImageTiling tiling, VkImageUsageFlags usage); -void kvfImageBufferToBuffer(VkCommandBuffer cmd, VkBuffer dst, VkImage src, size_t size); +VkImage kvfCreateImage(VkDevice device, uint32_t width, uint32_t height, VkFormat format, VkImageTiling tiling, VkImageUsageFlags usage, KvfImageType type); +void kvfImageToBuffer(VkCommandBuffer cmd, VkBuffer dst, VkImage src, size_t size); void kvfDestroyImage(VkDevice device, VkImage image); -VkImageView kvfCreateImageView(VkDevice device, VkImage image, VkFormat format, VkImageViewType type, VkImageAspectFlags aspect); +VkImageView kvfCreateImageView(VkDevice device, VkImage image, VkFormat format, VkImageViewType type, VkImageAspectFlags aspect, int layer_count); void kvfDestroyImageView(VkDevice device, VkImageView image_view); -void kvfTransitionImageLayout(VkDevice device, VkImage image, VkCommandBuffer cmd, VkFormat format, VkImageLayout old_layout, VkImageLayout new_layout, bool is_single_time_cmd_buffer); +void kvfTransitionImageLayout(VkDevice device, VkImage image, KvfImageType type, VkCommandBuffer cmd, VkFormat format, VkImageLayout old_layout, VkImageLayout new_layout, bool is_single_time_cmd_buffer); VkSampler kvfCreateSampler(VkDevice device, VkFilter filters, VkSamplerAddressMode address_modes, VkSamplerMipmapMode mipmap_mode); void kvfDestroySampler(VkDevice device, VkSampler sampler); @@ -143,10 +144,11 @@ void kvfEndCommandBuffer(VkCommandBuffer buffer); void kvfSubmitCommandBuffer(VkDevice device, VkCommandBuffer buffer, KvfQueueType queue, VkSemaphore signal, VkSemaphore wait, VkFence fence, VkPipelineStageFlags* stages); void kvfSubmitSingleTimeCommandBuffer(VkDevice device, VkCommandBuffer buffer, KvfQueueType queue, VkFence fence); -VkAttachmentDescription kvfBuildAttachmentDescription(KvfImageType type, VkFormat format, VkImageLayout initial, VkImageLayout final, bool clear); +VkAttachmentDescription kvfBuildAttachmentDescription(KvfImageType type, VkFormat format, VkImageLayout initial, VkImageLayout final, bool clear, VkSampleCountFlagBits samples); VkAttachmentDescription kvfBuildSwapchainAttachmentDescription(VkSwapchainKHR swapchain, bool clear); VkRenderPass kvfCreateRenderPass(VkDevice device, VkAttachmentDescription* attachments, size_t attachments_count, VkPipelineBindPoint bind_point); +VkRenderPass kvfCreateRenderPassWithSubpassDependencies(VkDevice device, VkAttachmentDescription* attachments, size_t attachments_count, VkPipelineBindPoint bind_point, VkSubpassDependency* dependencies, size_t dependencies_count); void kvfDestroyRenderPass(VkDevice device, VkRenderPass renderpass); void kvfBeginRenderPass(VkRenderPass pass, VkCommandBuffer cmd, VkFramebuffer framebuffer, VkExtent2D framebuffer_extent, VkClearValue* clears, size_t clears_count); @@ -185,6 +187,8 @@ void kvfGPipelineBuilderReset(KvfGraphicsPipelineBuilder* builder); void kvfGPipelineBuilderSetInputTopology(KvfGraphicsPipelineBuilder* builder, VkPrimitiveTopology topology); void kvfGPipelineBuilderSetPolygonMode(KvfGraphicsPipelineBuilder* builder, VkPolygonMode polygon, float line_width); void kvfGPipelineBuilderSetCullMode(KvfGraphicsPipelineBuilder* builder, VkCullModeFlags cull, VkFrontFace face); +void kvfGPipelineBuilderSetMultisampling(KvfGraphicsPipelineBuilder* builder, VkSampleCountFlagBits count); +void kvfGPipelineBuilderSetMultisamplingShading(KvfGraphicsPipelineBuilder* builder, VkSampleCountFlagBits count, float min_sampling_shading); void kvfGPipelineBuilderDisableBlending(KvfGraphicsPipelineBuilder* builder); void kvfGPipelineBuilderEnableAdditiveBlending(KvfGraphicsPipelineBuilder* builder); void kvfGPipelineBuilderEnableAlphaBlending(KvfGraphicsPipelineBuilder* builder); @@ -197,6 +201,8 @@ void kvfGPipelineBuilderResetShaderStages(KvfGraphicsPipelineBuilder* builder); VkPipeline kvfCreateGraphicsPipeline(VkDevice device, VkPipelineLayout layout, KvfGraphicsPipelineBuilder* builder, VkRenderPass pass); void kvfDestroyPipeline(VkDevice device, VkPipeline pipeline); +void kvfCheckVk(VkResult result); + #ifdef __cplusplus } #endif @@ -287,6 +293,7 @@ struct KvfGraphicsPipelineBuilder VkPipelineRasterizationStateCreateInfo rasterization_state; VkPipelineDepthStencilStateCreateInfo depth_stencil_state; VkPipelineColorBlendAttachmentState color_blend_attachment_state; + VkPipelineMultisampleStateCreateInfo multisampling; size_t shader_stages_count; }; @@ -334,6 +341,11 @@ void __kvfCheckVk(VkResult result, const char* function) #undef __kvfCheckVk #define __kvfCheckVk(res) __kvfCheckVk(res, __FUNCTION__) +void kvfCheckVk(VkResult result) +{ + __kvfCheckVk(result); +} + void __kvfAddDeviceToArray(VkPhysicalDevice device, int32_t graphics_queue, int32_t present_queue) { KVF_ASSERT(device != VK_NULL_HANDLE); @@ -518,7 +530,7 @@ void __kvfDestroyFramebuffer(VkDevice device, VkFramebuffer framebuffer) } } -__KvfFramebuffer* __kvfGetKvfSwapchainFromVkFramebuffer(VkFramebuffer framebuffer) +__KvfFramebuffer* __kvfGetKvfFramebufferFromVkFramebuffer(VkFramebuffer framebuffer) { KVF_ASSERT(framebuffer != VK_NULL_HANDLE); for(size_t i = 0; i < __kvf_internal_framebuffers_size; i++) @@ -1061,22 +1073,6 @@ void kvfDestroyInstance(VkInstance instance) vkDestroyInstance(instance, NULL); } -VkPhysicalDevice kvfPickFirstPhysicalDevice(VkInstance instance) -{ - uint32_t device_count; - VkPhysicalDevice* devices = NULL; - VkPhysicalDevice chosen_one = VK_NULL_HANDLE; - - KVF_ASSERT(instance != VK_NULL_HANDLE); - - vkEnumeratePhysicalDevices(instance, &device_count, NULL); - devices = (VkPhysicalDevice*)KVF_MALLOC(sizeof(VkPhysicalDevice) * device_count + 1); - vkEnumeratePhysicalDevices(instance, &device_count, devices); - chosen_one = devices[0]; - KVF_FREE(devices); - return chosen_one; -} - __KvfQueueFamilies __kvfFindQueueFamilies(VkPhysicalDevice physical, VkSurfaceKHR surface) { __KvfQueueFamilies queues = { -1, -1, -1 }; @@ -1095,17 +1091,39 @@ __KvfQueueFamilies __kvfFindQueueFamilies(VkPhysicalDevice physical, VkSurfaceKH if(queue_families[i].queueFlags & VK_QUEUE_GRAPHICS_BIT) queues.graphics = i; VkBool32 present_support = false; - vkGetPhysicalDeviceSurfaceSupportKHR(physical, i, surface, &present_support); - if(present_support) - queues.present = i; - - if(queues.graphics != -1 && queues.present != -1 && queues.compute != -1) + if(surface != VK_NULL_HANDLE) + { + vkGetPhysicalDeviceSurfaceSupportKHR(physical, i, surface, &present_support); + if(present_support) + queues.present = i; + if(queues.graphics != -1 && queues.present != -1 && queues.compute != -1) + break; + } + else if(queues.graphics != -1 && queues.compute != -1) break; } KVF_FREE(queue_families); return queues; } +VkPhysicalDevice kvfPickFirstPhysicalDevice(VkInstance instance, VkSurfaceKHR surface) +{ + uint32_t device_count; + VkPhysicalDevice* devices = NULL; + VkPhysicalDevice chosen_one = VK_NULL_HANDLE; + + KVF_ASSERT(instance != VK_NULL_HANDLE); + + vkEnumeratePhysicalDevices(instance, &device_count, NULL); + devices = (VkPhysicalDevice*)KVF_MALLOC(sizeof(VkPhysicalDevice) * device_count + 1); + vkEnumeratePhysicalDevices(instance, &device_count, devices); + chosen_one = devices[0]; + KVF_FREE(devices); + __KvfQueueFamilies queues = __kvfFindQueueFamilies(chosen_one, surface); + __kvfAddDeviceToArray(chosen_one, queues.graphics, queues.present); + return chosen_one; +} + VkPhysicalDevice kvfPickGoodDefaultPhysicalDevice(VkInstance instance, VkSurfaceKHR surface) { const char* extensions[] = { VK_KHR_SWAPCHAIN_EXTENSION_NAME }; @@ -1208,10 +1226,11 @@ VkPhysicalDevice kvfPickGoodPhysicalDevice(VkInstance instance, VkSurfaceKHR sur VkDevice kvfCreateDefaultDevice(VkPhysicalDevice physical) { const char* extensions[] = { VK_KHR_SWAPCHAIN_EXTENSION_NAME }; - return kvfCreateDevice(physical, extensions, sizeof(extensions) / sizeof(extensions[0])); + VkPhysicalDeviceFeatures device_features = { VK_FALSE }; + return kvfCreateDevice(physical, extensions, sizeof(extensions) / sizeof(extensions[0]), &device_features); } -VkDevice kvfCreateDevice(VkPhysicalDevice physical, const char** extensions, uint32_t extensions_count) +VkDevice kvfCreateDevice(VkPhysicalDevice physical, const char** extensions, uint32_t extensions_count, VkPhysicalDeviceFeatures* features) { const float queue_priority = 1.0f; @@ -1235,13 +1254,11 @@ VkDevice kvfCreateDevice(VkPhysicalDevice physical, const char** extensions, uin queue_create_info[1].flags = 0; queue_create_info[1].pNext = NULL; - VkPhysicalDeviceFeatures device_features = { VK_FALSE }; - VkDeviceCreateInfo createInfo; createInfo.sType = VK_STRUCTURE_TYPE_DEVICE_CREATE_INFO; createInfo.queueCreateInfoCount = (kvfdevice->queues.graphics == kvfdevice->queues.present ? 1 : 2); createInfo.pQueueCreateInfos = queue_create_info; - createInfo.pEnabledFeatures = &device_features; + createInfo.pEnabledFeatures = features; createInfo.enabledExtensionCount = extensions_count; createInfo.ppEnabledExtensionNames = extensions; createInfo.enabledLayerCount = 0; @@ -1502,7 +1519,7 @@ void kvfDestroySwapchainKHR(VkDevice device, VkSwapchainKHR swapchain) __kvfDestroySwapchain(device, swapchain); } -VkImage kvfCreateImage(VkDevice device, uint32_t width, uint32_t height, VkFormat format, VkImageTiling tiling, VkImageUsageFlags usage) +VkImage kvfCreateImage(VkDevice device, uint32_t width, uint32_t height, VkFormat format, VkImageTiling tiling, VkImageUsageFlags usage, KvfImageType type) { KVF_ASSERT(device != VK_NULL_HANDLE); VkImageCreateInfo image_info = {}; @@ -1519,11 +1536,37 @@ VkImage kvfCreateImage(VkDevice device, uint32_t width, uint32_t height, VkForma image_info.usage = usage; image_info.samples = VK_SAMPLE_COUNT_1_BIT; image_info.sharingMode = VK_SHARING_MODE_EXCLUSIVE; + + switch(type) + { + case KVF_IMAGE_CUBE: image_info.flags = VK_IMAGE_CREATE_CUBE_COMPATIBLE_BIT; image_info.arrayLayers = 6; break; + default: break; + } + VkImage image; __kvfCheckVk(vkCreateImage(device, &image_info, NULL, &image)); return image; } +void kvfImageToBuffer(VkCommandBuffer cmd, VkBuffer dst, VkImage src, size_t buffer_offset, VkImageAspectFlagBits aspect, VkExtent3D extent) +{ + KVF_ASSERT(cmd != VK_NULL_HANDLE); + KVF_ASSERT(dst != VK_NULL_HANDLE); + KVF_ASSERT(src != VK_NULL_HANDLE); + VkOffset3D offset = { 0, 0, 0 }; + VkBufferImageCopy region = {}; + region.bufferOffset = buffer_offset; + region.bufferRowLength = 0; + region.bufferImageHeight = 0; + region.imageSubresource.aspectMask = aspect; + region.imageSubresource.mipLevel = 0; + region.imageSubresource.baseArrayLayer = 0; + region.imageSubresource.layerCount = 1; + region.imageOffset = offset; + region.imageExtent = extent; + vkCmdCopyImageToBuffer(cmd, src, dst, VK_IMAGE_LAYOUT_TRANSFER_SRC_OPTIMAL, 1, ®ion); +} + void kvfDestroyImage(VkDevice device, VkImage image) { if(image == VK_NULL_HANDLE) @@ -1532,7 +1575,7 @@ void kvfDestroyImage(VkDevice device, VkImage image) vkDestroyImage(device, image, NULL); } -VkImageView kvfCreateImageView(VkDevice device, VkImage image, VkFormat format, VkImageViewType type, VkImageAspectFlags aspect) +VkImageView kvfCreateImageView(VkDevice device, VkImage image, VkFormat format, VkImageViewType type, VkImageAspectFlags aspect, int layer_count) { KVF_ASSERT(device != VK_NULL_HANDLE); VkImageViewCreateInfo create_info = {}; @@ -1548,7 +1591,7 @@ VkImageView kvfCreateImageView(VkDevice device, VkImage image, VkFormat format, create_info.subresourceRange.baseMipLevel = 0; create_info.subresourceRange.levelCount = 1; create_info.subresourceRange.baseArrayLayer = 0; - create_info.subresourceRange.layerCount = 1; + create_info.subresourceRange.layerCount = layer_count; VkImageView view; __kvfCheckVk(vkCreateImageView(device, &create_info, NULL, &view)); return view; @@ -1561,7 +1604,7 @@ void kvfDestroyImageView(VkDevice device, VkImageView image_view) vkDestroyImageView(device, image_view, NULL); } -void kvfTransitionImageLayout(VkDevice device, VkImage image, VkCommandBuffer cmd, VkFormat format, VkImageLayout old_layout, VkImageLayout new_layout, bool is_single_time_cmd_buffer) +void kvfTransitionImageLayout(VkDevice device, VkImage image, KvfImageType type, VkCommandBuffer cmd, VkFormat format, VkImageLayout old_layout, VkImageLayout new_layout, bool is_single_time_cmd_buffer) { KVF_ASSERT(device != VK_NULL_HANDLE); @@ -1582,7 +1625,7 @@ void kvfTransitionImageLayout(VkDevice device, VkImage image, VkCommandBuffer cm barrier.subresourceRange.baseMipLevel = 0; barrier.subresourceRange.levelCount = 1; barrier.subresourceRange.baseArrayLayer = 0; - barrier.subresourceRange.layerCount = 1; + barrier.subresourceRange.layerCount = (type == KVF_IMAGE_CUBE ? 6 : 1); barrier.srcAccessMask = kvfLayoutToAccessMask(old_layout, false); barrier.dstAccessMask = kvfLayoutToAccessMask(new_layout, true); if(kvfIsStencilFormat(format)) @@ -1590,7 +1633,7 @@ void kvfTransitionImageLayout(VkDevice device, VkImage image, VkCommandBuffer cm VkPipelineStageFlags source_stage = 0; if(barrier.oldLayout == VK_IMAGE_LAYOUT_PRESENT_SRC_KHR) - source_stage = VK_PIPELINE_STAGE_BOTTOM_OF_PIPE_BIT; + source_stage = VK_PIPELINE_STAGE_COLOR_ATTACHMENT_OUTPUT_BIT; else if(barrier.srcAccessMask != 0) source_stage = kvfAccessFlagsToPipelineStage(barrier.srcAccessMask, VK_PIPELINE_STAGE_VERTEX_SHADER_BIT | VK_PIPELINE_STAGE_FRAGMENT_SHADER_BIT | VK_PIPELINE_STAGE_COMPUTE_SHADER_BIT); else @@ -1598,7 +1641,7 @@ void kvfTransitionImageLayout(VkDevice device, VkImage image, VkCommandBuffer cm VkPipelineStageFlags destination_stage = 0; if(barrier.newLayout == VK_IMAGE_LAYOUT_PRESENT_SRC_KHR) - destination_stage = VK_PIPELINE_STAGE_TOP_OF_PIPE_BIT; + destination_stage = VK_PIPELINE_STAGE_COLOR_ATTACHMENT_OUTPUT_BIT; else if(barrier.dstAccessMask != 0) destination_stage = kvfAccessFlagsToPipelineStage(barrier.dstAccessMask, VK_PIPELINE_STAGE_VERTEX_SHADER_BIT | VK_PIPELINE_STAGE_FRAGMENT_SHADER_BIT | VK_PIPELINE_STAGE_COMPUTE_SHADER_BIT); else @@ -1714,7 +1757,7 @@ VkFramebuffer kvfCreateFramebuffer(VkDevice device, VkRenderPass render_pass, Vk VkExtent2D kvfGetFramebufferSize(VkFramebuffer buffer) { - __KvfFramebuffer* kvf_framebuffer = __kvfGetKvfSwapchainFromVkFramebuffer(buffer); + __KvfFramebuffer* kvf_framebuffer = __kvfGetKvfFramebufferFromVkFramebuffer(buffer); KVF_ASSERT(kvf_framebuffer != NULL); return kvf_framebuffer->extent; } @@ -1805,7 +1848,7 @@ void kvfSubmitSingleTimeCommandBuffer(VkDevice device, VkCommandBuffer buffer, K vkWaitForFences(device, 1, &fence, VK_TRUE, UINT64_MAX); } -VkAttachmentDescription kvfBuildAttachmentDescription(KvfImageType type, VkFormat format, VkImageLayout initial, VkImageLayout final, bool clear) +VkAttachmentDescription kvfBuildAttachmentDescription(KvfImageType type, VkFormat format, VkImageLayout initial, VkImageLayout final, bool clear, VkSampleCountFlagBits samples) { VkAttachmentDescription attachment = {}; @@ -1833,12 +1876,23 @@ VkAttachmentDescription kvfBuildAttachmentDescription(KvfImageType type, VkForma } else { - attachment.loadOp = VK_ATTACHMENT_LOAD_OP_LOAD; - attachment.stencilLoadOp = VK_ATTACHMENT_LOAD_OP_LOAD; + if(samples != VK_SAMPLE_COUNT_1_BIT) + { + attachment.loadOp = VK_ATTACHMENT_LOAD_OP_DONT_CARE; + attachment.stencilLoadOp = VK_ATTACHMENT_LOAD_OP_DONT_CARE; + } + else + { + attachment.loadOp = VK_ATTACHMENT_LOAD_OP_LOAD; + attachment.stencilLoadOp = VK_ATTACHMENT_LOAD_OP_LOAD; + } } - attachment.samples = VK_SAMPLE_COUNT_1_BIT; - attachment.storeOp = VK_ATTACHMENT_STORE_OP_STORE; + attachment.samples = samples; + if(samples != VK_SAMPLE_COUNT_1_BIT) + attachment.storeOp = VK_ATTACHMENT_STORE_OP_DONT_CARE; + else + attachment.storeOp = VK_ATTACHMENT_STORE_OP_STORE; attachment.stencilStoreOp = VK_ATTACHMENT_STORE_OP_DONT_CARE; attachment.flags = 0; @@ -1850,13 +1904,17 @@ VkAttachmentDescription kvfBuildSwapchainAttachmentDescription(VkSwapchainKHR sw __KvfSwapchain* kvf_swapchain = __kvfGetKvfSwapchainFromVkSwapchainKHR(swapchain); KVF_ASSERT(kvf_swapchain != NULL); KVF_ASSERT(kvf_swapchain->images_count != 0); - return kvfBuildAttachmentDescription(KVF_IMAGE_COLOR, kvf_swapchain->images_format, VK_IMAGE_LAYOUT_UNDEFINED,VK_IMAGE_LAYOUT_PRESENT_SRC_KHR, clear); + return kvfBuildAttachmentDescription(KVF_IMAGE_COLOR, kvf_swapchain->images_format, VK_IMAGE_LAYOUT_UNDEFINED,VK_IMAGE_LAYOUT_PRESENT_SRC_KHR, clear, VK_SAMPLE_COUNT_1_BIT); } VkRenderPass kvfCreateRenderPass(VkDevice device, VkAttachmentDescription* attachments, size_t attachments_count, VkPipelineBindPoint bind_point) { KVF_ASSERT(device != VK_NULL_HANDLE); + return kvfCreateRenderPassWithSubpassDependencies(device, attachments, attachments_count, bind_point, NULL, 0); +} +VkRenderPass kvfCreateRenderPassWithSubpassDependencies(VkDevice device, VkAttachmentDescription* attachments, size_t attachments_count, VkPipelineBindPoint bind_point, VkSubpassDependency* dependencies, size_t dependencies_count) +{ size_t color_attachment_count = 0; size_t depth_attachment_count = 0; @@ -1911,8 +1969,8 @@ VkRenderPass kvfCreateRenderPass(VkDevice device, VkAttachmentDescription* attac renderpass_create_info.pAttachments = attachments; renderpass_create_info.subpassCount = 1; renderpass_create_info.pSubpasses = &subpass; - renderpass_create_info.dependencyCount = 0; - renderpass_create_info.pDependencies = NULL; + renderpass_create_info.dependencyCount = dependencies_count; + renderpass_create_info.pDependencies = dependencies; VkRenderPass render_pass = VK_NULL_HANDLE; __kvfCheckVk(vkCreateRenderPass(device, &renderpass_create_info, NULL, &render_pass)); @@ -2144,6 +2202,7 @@ void kvfGPipelineBuilderReset(KvfGraphicsPipelineBuilder* builder) builder->tessellation_state.sType = VK_STRUCTURE_TYPE_PIPELINE_TESSELLATION_STATE_CREATE_INFO; builder->rasterization_state.sType = VK_STRUCTURE_TYPE_PIPELINE_RASTERIZATION_STATE_CREATE_INFO; builder->depth_stencil_state.sType = VK_STRUCTURE_TYPE_PIPELINE_DEPTH_STENCIL_STATE_CREATE_INFO; + builder->multisampling.sType = VK_STRUCTURE_TYPE_PIPELINE_MULTISAMPLE_STATE_CREATE_INFO; } void kvfGPipelineBuilderSetInputTopology(KvfGraphicsPipelineBuilder* builder, VkPrimitiveTopology topology) @@ -2167,6 +2226,20 @@ void kvfGPipelineBuilderSetCullMode(KvfGraphicsPipelineBuilder* builder, VkCullM builder->rasterization_state.frontFace = face; } +void kvfGPipelineBuilderSetMultisampling(KvfGraphicsPipelineBuilder* builder, VkSampleCountFlagBits count) +{ + KVF_ASSERT(builder != NULL); + builder->multisampling.rasterizationSamples = count; +} + +void kvfGPipelineBuilderSetMultisamplingShading(KvfGraphicsPipelineBuilder* builder, VkSampleCountFlagBits count, float min_sampling_shading) +{ + KVF_ASSERT(builder != NULL); + builder->multisampling.rasterizationSamples = count; + builder->multisampling.sampleShadingEnable = VK_TRUE; + builder->multisampling.minSampleShading = min_sampling_shading; +} + void kvfGPipelineBuilderDisableBlending(KvfGraphicsPipelineBuilder* builder) { KVF_ASSERT(builder != NULL); @@ -2298,11 +2371,6 @@ VkPipeline kvfCreateGraphicsPipeline(VkDevice device, VkPipelineLayout layout, K viewport_state.scissorCount = 1; viewport_state.pScissors = NULL; - VkPipelineMultisampleStateCreateInfo multisampling = {}; - multisampling.sType = VK_STRUCTURE_TYPE_PIPELINE_MULTISAMPLE_STATE_CREATE_INFO; - multisampling.sampleShadingEnable = VK_FALSE; - multisampling.rasterizationSamples = VK_SAMPLE_COUNT_1_BIT; - VkGraphicsPipelineCreateInfo pipeline_info = {}; pipeline_info.sType = VK_STRUCTURE_TYPE_GRAPHICS_PIPELINE_CREATE_INFO; pipeline_info.stageCount = builder->shader_stages_count; @@ -2311,7 +2379,7 @@ VkPipeline kvfCreateGraphicsPipeline(VkDevice device, VkPipelineLayout layout, K pipeline_info.pInputAssemblyState = &builder->input_assembly_state; pipeline_info.pViewportState = &viewport_state; pipeline_info.pRasterizationState = &builder->rasterization_state; - pipeline_info.pMultisampleState = &multisampling; + pipeline_info.pMultisampleState = &builder->multisampling; pipeline_info.pColorBlendState = &color_blending; pipeline_info.pDynamicState = &dynamic_states; pipeline_info.layout = layout;