7#include "gfx_surface_vulkan_intf.hpp"
8#include "gfx_surface_delegate_vulkan.hpp"
9#include "gfx_system_vulkan_intf.hpp"
10#include "gfx_device_vulkan_impl.hpp"
11#include "gfx_pipeline_box_vulkan_intf.hpp"
12#include "gfx_pipeline_image_vulkan_intf.hpp"
13#include "gfx_pipeline_SDF_vulkan_intf.hpp"
14#include "gfx_pipeline_override_vulkan_intf.hpp"
15#include "gfx_pipeline_tone_mapper_vulkan_intf.hpp"
16#include "../telemetry/telemetry.hpp"
17#include "../utility/utility.hpp"
18#include "../macros.hpp"
20#include <vulkan/vulkan.hpp>
22hi_export_module(hikogui.GFX : gfx_surface_impl);
39 loss = gfx_surface_loss::device_lost;
45 _present_queue =
std::addressof(_device->get_present_queue(intrinsic));
46 _graphics_queue =
std::addressof(_device->get_graphics_queue(intrinsic));
51 auto const lock = std::scoped_lock(gfx_system_mutex);
53 hi_assert_not_null(delegate);
54 auto&
delegate_info = _delegates.emplace_back(delegate, _device->createSemaphore());
56 if (state >= gfx_surface_state::has_device) {
62 if (state >= gfx_surface_state::has_swapchain) {
65 for (
auto const&
image_info : swapchain_image_infos) {
73hi_inline
void gfx_surface::remove_delegate(gfx_surface_delegate *delegate)
noexcept
75 auto const lock = std::scoped_lock(gfx_system_mutex);
77 hi_assert_not_null(delegate);
78 auto it =
std::find_if(_delegates.begin(), _delegates.end(), [delegate](
auto const&
item) {
79 return item.delegate == delegate;
82 if (state >= gfx_surface_state::has_swapchain) {
83 it->delegate->teardown_for_swapchain_lost();
85 if (state >= gfx_surface_state::has_device) {
86 it->delegate->teardown_for_device_lost();
89 _device->destroy(
it->semaphore);
99hi_inline
void gfx_surface::wait_idle()
104 if (renderFinishedFence) {
108 hi_log_info(
"/waitIdle");
111hi_inline std::optional<uint32_t> gfx_surface::acquire_next_image_from_swapchain()
119 auto const result = _device->acquireNextImageKHR(swapchain, 0, imageAvailableSemaphore, vk::Fence(), &
frameBufferIndex);
123 case vk::Result::eSuccess:
126 case vk::Result::eSuboptimalKHR:
130 hi_log_info(
"acquireNextImageKHR() eSuboptimalKHR");
131 loss = gfx_surface_loss::swapchain_lost;
134 case vk::Result::eNotReady:
140 case vk::Result::eTimeout:
144 hi_log_info(
"acquireNextImageKHR() eTimeout");
147 case vk::Result::eErrorOutOfDateKHR:
148 hi_log_info(
"acquireNextImageKHR() eErrorOutOfDateKHR");
149 loss = gfx_surface_loss::swapchain_lost;
152 case vk::Result::eErrorSurfaceLostKHR:
153 hi_log_info(
"acquireNextImageKHR() eErrorSurfaceLostKHR");
154 loss = gfx_surface_loss::window_lost;
158 throw gui_error(std::format(
"Unknown result from acquireNextImageKHR(). '{}'",
to_string(
result)));
162hi_inline
void gfx_surface::present_image_to_queue(uint32_t
frameBufferIndex, vk::Semaphore semaphore)
166 hi_assert_not_null(_device);
175 auto const result = _present_queue->queue.presentKHR(
183 case vk::Result::eSuccess:
186 case vk::Result::eSuboptimalKHR:
187 hi_log_info(
"presentKHR() eSuboptimalKHR");
188 loss = gfx_surface_loss::swapchain_lost;
192 throw gui_error(std::format(
"Unknown result from presentKHR(). '{}'",
to_string(
result)));
195 }
catch (vk::OutOfDateKHRError
const&) {
196 hi_log_info(
"presentKHR() eErrorOutOfDateKHR");
197 loss = gfx_surface_loss::swapchain_lost;
200 }
catch (vk::SurfaceLostKHRError
const&) {
201 hi_log_info(
"presentKHR() eErrorSurfaceLostKHR");
202 loss = gfx_surface_loss::window_lost;
209 if (_device->score(intrinsic) <= 0) {
210 return gfx_surface_loss::device_lost;
213 box_pipeline->build_for_new_device();
214 image_pipeline->build_for_new_device();
215 SDF_pipeline->build_for_new_device();
216 override_pipeline->build_for_new_device();
217 tone_mapper_pipeline->build_for_new_device();
220 for (
auto [delegate, semaphore] : _delegates) {
221 hi_assert_not_null(delegate);
223 delegate->build_for_new_device(
227 return gfx_surface_loss::none;
236 return gfx_surface_loss::swapchain_lost;
247 teardown_swapchain();
248 return gfx_surface_loss::swapchain_lost;
251 build_render_passes();
252 build_frame_buffers();
253 build_command_buffers();
255 hi_assert_not_null(box_pipeline);
256 hi_assert_not_null(image_pipeline);
257 hi_assert_not_null(SDF_pipeline);
258 hi_assert_not_null(override_pipeline);
259 hi_assert_not_null(tone_mapper_pipeline);
260 box_pipeline->build_for_new_swapchain(renderPass, 0, swapchainImageExtent);
261 image_pipeline->build_for_new_swapchain(renderPass, 1, swapchainImageExtent);
262 SDF_pipeline->build_for_new_swapchain(renderPass, 2, swapchainImageExtent);
263 override_pipeline->build_for_new_swapchain(renderPass, 3, swapchainImageExtent);
264 tone_mapper_pipeline->build_for_new_swapchain(renderPass, 4, swapchainImageExtent);
268 for (
auto const&
image_info : swapchain_image_infos) {
272 for (
auto [delegate, semaphore] : _delegates) {
273 hi_assert_not_null(delegate);
274 delegate->build_for_new_swapchain(
image_views, swapchainImageExtent, swapchainImageFormat);
277 return gfx_surface_loss::none;
279 }
catch (vk::SurfaceLostKHRError
const&) {
282 return gfx_surface_loss::window_lost;
286hi_inline
void gfx_surface::build(extent2
new_size)
noexcept
289 hi_assert(loss == gfx_surface_loss::none);
291 if (state == gfx_surface_state::has_window) {
293 if (loss = build_for_new_device(); loss != gfx_surface_loss::none) {
296 state = gfx_surface_state::has_device;
300 if (state == gfx_surface_state::has_device) {
301 if (
auto const tmp = build_for_new_swapchain(
new_size);
tmp == gfx_surface_loss::swapchain_lost) {
305 }
else if (loss =
tmp;
tmp != gfx_surface_loss::none) {
309 state = gfx_surface_state::has_swapchain;
313hi_inline
void gfx_surface::teardown_for_swapchain_lost()
noexcept
315 hi_log_info(
"Tearing down because the window lost the swapchain.");
318 for (
auto [delegate, semaphore] : _delegates) {
319 hi_assert_not_null(delegate);
320 delegate->teardown_for_swapchain_lost();
323 tone_mapper_pipeline->teardown_for_swapchain_lost();
324 override_pipeline->teardown_for_swapchain_lost();
325 SDF_pipeline->teardown_for_swapchain_lost();
326 image_pipeline->teardown_for_swapchain_lost();
327 box_pipeline->teardown_for_swapchain_lost();
328 teardown_semaphores();
329 teardown_command_buffers();
330 teardown_frame_buffers();
331 teardown_render_passes();
332 teardown_swapchain();
335hi_inline
void gfx_surface::teardown_for_device_lost()
noexcept
337 hi_log_info(
"Tearing down because the window lost the vulkan device.");
338 for (
auto [delegate, semaphore] : _delegates) {
339 hi_assert_not_null(delegate);
340 delegate->teardown_for_device_lost();
342 tone_mapper_pipeline->teardown_for_device_lost();
343 override_pipeline->teardown_for_device_lost();
344 SDF_pipeline->teardown_for_device_lost();
345 image_pipeline->teardown_for_device_lost();
346 box_pipeline->teardown_for_device_lost();
350hi_inline
void gfx_surface::teardown_for_window_lost()
noexcept
352 gfx_system::global().destroySurfaceKHR(intrinsic);
355hi_inline
void gfx_surface::teardown()
noexcept
359 if (state == gfx_surface_state::has_swapchain
and loss >= gfx_surface_loss::swapchain_lost) {
360 teardown_for_swapchain_lost();
361 state = gfx_surface_state::has_device;
364 if (state == gfx_surface_state::has_device
and loss >= gfx_surface_loss::device_lost) {
365 teardown_for_device_lost();
366 state = gfx_surface_state::has_window;
369 if (state == gfx_surface_state::has_window
and loss >= gfx_surface_loss::window_lost) {
370 hi_log_info(
"Tearing down because the window doesn't exist anymore.");
371 teardown_for_window_lost();
372 state = gfx_surface_state::no_window;
374 loss = gfx_surface_loss::none;
377hi_inline
void gfx_surface::update(extent2
new_size)
noexcept
379 auto const lock = std::scoped_lock(gfx_system_mutex);
381 if (size() !=
new_size and state == gfx_surface_state::has_swapchain) {
383 loss = gfx_surface_loss::swapchain_lost;
391hi_inline draw_context gfx_surface::render_start(aarectangle redraw_rectangle)
394 redraw_rectangle =
ceil(redraw_rectangle, _render_area_granularity);
396 auto const lock = std::scoped_lock(gfx_system_mutex);
398 auto r = draw_context{
400 box_pipeline->vertexBufferData,
401 image_pipeline->vertexBufferData,
402 SDF_pipeline->vertexBufferData,
403 override_pipeline->vertexBufferData};
406 if (state != gfx_surface_state::has_swapchain
or not redraw_rectangle) {
421 auto&
current_image = swapchain_image_infos.at(r.frame_buffer_index);
426 r.scissor_rectangle =
427 std::accumulate(swapchain_image_infos.cbegin(), swapchain_image_infos.cend(), aarectangle{}, [](
auto const& sum,
auto const&
item) {
428 return sum | item.redraw_rectangle;
435 _device->resetFences({renderFinishedFence});
440hi_inline
void gfx_surface::render_finish(draw_context
const&
context)
442 auto const lock = std::scoped_lock(gfx_system_mutex);
450 _device->transition_layout(
451 current_image.image, swapchainImageFormat.format, vk::ImageLayout::eUndefined, vk::ImageLayout::ePresentSrcKHR);
459 aarectangle{0, 0, narrow_cast<float>(swapchainImageExtent.width), narrow_cast<float>(swapchainImageExtent.height)});
472 hi_assert_not_null(delegate);
492hi_inline
void gfx_surface::fill_command_buffer(
499 auto t = trace<
"fill_command_buffer">{};
501 commandBuffer.reset(vk::CommandBufferResetFlagBits::eReleaseResources);
502 commandBuffer.begin({vk::CommandBufferUsageFlagBits::eSimultaneousUse});
518 commandBuffer.setScissor(0,
scissors);
520 commandBuffer.beginRenderPass(
522 vk::SubpassContents::eInline);
524 box_pipeline->draw_in_command_buffer(commandBuffer,
context);
525 commandBuffer.nextSubpass(vk::SubpassContents::eInline);
526 image_pipeline->draw_in_command_buffer(commandBuffer,
context);
527 commandBuffer.nextSubpass(vk::SubpassContents::eInline);
528 SDF_pipeline->draw_in_command_buffer(commandBuffer,
context);
529 commandBuffer.nextSubpass(vk::SubpassContents::eInline);
530 override_pipeline->draw_in_command_buffer(commandBuffer,
context);
531 commandBuffer.nextSubpass(vk::SubpassContents::eInline);
532 tone_mapper_pipeline->draw_in_command_buffer(commandBuffer,
context);
534 commandBuffer.endRenderPass();
544 auto const waitStages =
std::array{vk::PipelineStageFlags{vk::PipelineStageFlagBits::eColorAttachmentOutput}};
560 _graphics_queue->queue.submit(
submitInfo, vk::Fence());
579 auto const max_size = extent2{
592 hi_log_info(
"Building swap chain");
594 auto const sharingMode = _graphics_queue == _present_queue ? vk::SharingMode::eExclusive : vk::SharingMode::eConcurrent;
597 _graphics_queue->family_queue_index, _present_queue->family_queue_index};
599 swapchainImageFormat = _device->get_surface_format(intrinsic);
603 vk::SwapchainCreateFlagsKHR(),
606 swapchainImageFormat.format,
607 swapchainImageFormat.colorSpace,
608 swapchainImageExtent,
610 vk::ImageUsageFlagBits::eColorAttachment,
616 _device->get_present_mode(intrinsic),
622 case vk::Result::eSuccess:
625 case vk::Result::eErrorSurfaceLostKHR:
626 return gfx_surface_loss::window_lost;
629 throw gui_error(std::format(
"Unknown result from createSwapchainKHR(). '{}'",
to_string(
result)));
632 hi_log_info(
"Finished building swap chain");
635 " - colorSpace={}, format={}",
643 vk::ImageCreateFlags(),
649 vk::SampleCountFlagBits::e1,
650 vk::ImageTiling::eOptimal,
651 vk::ImageUsageFlagBits::eDepthStencilAttachment | _device->transientImageUsageFlags,
652 vk::SharingMode::eExclusive,
655 vk::ImageLayout::eUndefined};
662 _device->setDebugUtilsObjectNameEXT(depthImage,
"vk::Image depth attachment");
666 vk::ImageCreateFlags(),
672 vk::SampleCountFlagBits::e1,
673 vk::ImageTiling::eOptimal,
674 vk::ImageUsageFlagBits::eColorAttachment | vk::ImageUsageFlagBits::eInputAttachment | _device->transientImageUsageFlags,
675 vk::SharingMode::eExclusive,
678 vk::ImageLayout::eUndefined};
686 _device->setDebugUtilsObjectNameEXT(colorImages[0],
"vk::Image color attachment");
688 return gfx_surface_loss::none;
691hi_inline
void gfx_surface::teardown_swapchain()
695 _device->destroy(swapchain);
696 _device->destroyImage(depthImage, depthImageAllocation);
698 for (
std::size_t i = 0; i != colorImages.size(); ++i) {
699 _device->destroyImage(colorImages[i], colorImageAllocations[i]);
703hi_inline
void gfx_surface::build_frame_buffers()
707 depthImageView = _device->createImageView(
708 {vk::ImageViewCreateFlags(),
710 vk::ImageViewType::e2D,
712 vk::ComponentMapping(),
713 {vk::ImageAspectFlagBits::eDepth, 0, 1, 0, 1}});
715 for (
std::size_t i = 0; i != colorImageViews.size(); ++i) {
716 colorImageViews[i] = _device->createImageView(
717 {vk::ImageViewCreateFlags(),
719 vk::ImageViewType::e2D,
721 vk::ComponentMapping(),
722 {vk::ImageAspectFlagBits::eColor, 0, 1, 0, 1}});
724 colorDescriptorImageInfos[i] = {vk::Sampler(), colorImageViews[i], vk::ImageLayout::eShaderReadOnlyOptimal};
729 auto image_view = _device->createImageView(
730 {vk::ImageViewCreateFlags(),
732 vk::ImageViewType::e2D,
733 swapchainImageFormat.format,
734 vk::ComponentMapping(),
735 {vk::ImageAspectFlagBits::eColor, 0, 1, 0, 1}});
739 auto const frame_buffer = _device->createFramebuffer({
740 vk::FramebufferCreateFlags(),
744 swapchainImageExtent.width,
745 swapchainImageExtent.height,
749 swapchain_image_infos.emplace_back(
756hi_inline
void gfx_surface::teardown_frame_buffers()
760 for (
auto&
info : swapchain_image_infos) {
761 _device->destroy(
info.frame_buffer);
762 _device->destroy(
info.image_view);
764 swapchain_image_infos.clear();
766 _device->destroy(depthImageView);
767 for (
std::size_t i = 0; i != colorImageViews.size(); ++i) {
768 _device->destroy(colorImageViews[i]);
784hi_inline
void gfx_surface::build_render_passes()
789 vk::AttachmentDescription{
791 vk::AttachmentDescriptionFlags(),
793 vk::SampleCountFlagBits::e1,
794 vk::AttachmentLoadOp::eClear,
795 vk::AttachmentStoreOp::eDontCare,
796 vk::AttachmentLoadOp::eDontCare,
797 vk::AttachmentStoreOp::eDontCare,
798 vk::ImageLayout::eUndefined,
799 vk::ImageLayout::eDepthStencilAttachmentOptimal
801 vk::AttachmentDescription{
803 vk::AttachmentDescriptionFlags(),
805 vk::SampleCountFlagBits::e1,
806 vk::AttachmentLoadOp::eClear,
807 vk::AttachmentStoreOp::eDontCare,
808 vk::AttachmentLoadOp::eDontCare,
809 vk::AttachmentStoreOp::eDontCare,
810 vk::ImageLayout::eUndefined,
811 vk::ImageLayout::eColorAttachmentOptimal
813 vk::AttachmentDescription{
815 vk::AttachmentDescriptionFlags(),
816 swapchainImageFormat.format,
817 vk::SampleCountFlagBits::e1,
818 vk::AttachmentLoadOp::eLoad,
819 vk::AttachmentStoreOp::eStore,
820 vk::AttachmentLoadOp::eDontCare,
821 vk::AttachmentStoreOp::eDontCare,
822 vk::ImageLayout::ePresentSrcKHR,
823 vk::ImageLayout::ePresentSrcKHR
832 vk::SubpassDescription{
833 vk::SubpassDescriptionFlags(),
834 vk::PipelineBindPoint::eGraphics,
843 vk::SubpassDescription{
844 vk::SubpassDescriptionFlags(),
845 vk::PipelineBindPoint::eGraphics,
854 vk::SubpassDescription{
855 vk::SubpassDescriptionFlags(),
856 vk::PipelineBindPoint::eGraphics,
865 vk::SubpassDescription{
866 vk::SubpassDescriptionFlags(),
867 vk::PipelineBindPoint::eGraphics,
876 vk::SubpassDescription{
877 vk::SubpassDescriptionFlags(),
878 vk::PipelineBindPoint::eGraphics,
887 vk::SubpassDependency{
890 vk::PipelineStageFlagBits::eBottomOfPipe,
891 vk::PipelineStageFlagBits::eColorAttachmentOutput,
892 vk::AccessFlagBits::eMemoryRead,
893 vk::AccessFlagBits::eColorAttachmentRead | vk::AccessFlagBits::eColorAttachmentWrite,
894 vk::DependencyFlagBits::eByRegion},
896 vk::SubpassDependency{
899 vk::PipelineStageFlagBits::eColorAttachmentOutput,
900 vk::PipelineStageFlagBits::eColorAttachmentOutput,
901 vk::AccessFlagBits::eColorAttachmentWrite,
902 vk::AccessFlagBits::eColorAttachmentRead,
903 vk::DependencyFlagBits::eByRegion},
905 vk::SubpassDependency{
908 vk::PipelineStageFlagBits::eColorAttachmentOutput,
909 vk::PipelineStageFlagBits::eColorAttachmentOutput,
910 vk::AccessFlagBits::eColorAttachmentWrite,
911 vk::AccessFlagBits::eColorAttachmentRead,
912 vk::DependencyFlagBits::eByRegion},
914 vk::SubpassDependency{
917 vk::PipelineStageFlagBits::eColorAttachmentOutput,
918 vk::PipelineStageFlagBits::eFragmentShader,
919 vk::AccessFlagBits::eColorAttachmentWrite,
920 vk::AccessFlagBits::eShaderRead | vk::AccessFlagBits::eInputAttachmentRead,
921 vk::DependencyFlagBits::eByRegion},
923 vk::SubpassDependency{
926 vk::PipelineStageFlagBits::eColorAttachmentOutput,
927 vk::PipelineStageFlagBits::eFragmentShader,
928 vk::AccessFlagBits::eColorAttachmentWrite,
929 vk::AccessFlagBits::eShaderRead | vk::AccessFlagBits::eInputAttachmentRead,
930 vk::DependencyFlagBits::eByRegion},
932 vk::SubpassDependency{
935 vk::PipelineStageFlagBits::eColorAttachmentOutput,
936 vk::PipelineStageFlagBits::eBottomOfPipe,
937 vk::AccessFlagBits::eColorAttachmentRead | vk::AccessFlagBits::eColorAttachmentWrite,
938 vk::AccessFlagBits::eMemoryRead,
939 vk::DependencyFlagBits::eByRegion}};
942 vk::RenderPassCreateFlags(),
952 auto const granularity = _device->getRenderAreaGranularity(renderPass);
956hi_inline
void gfx_surface::teardown_render_passes()
960 _device->destroy(renderPass);
963hi_inline
void gfx_surface::build_semaphores()
967 imageAvailableSemaphore = _device->createSemaphore();
968 renderFinishedSemaphore = _device->createSemaphore();
973 renderFinishedFence = _device->createFence({vk::FenceCreateFlagBits::eSignaled});
976hi_inline
void gfx_surface::teardown_semaphores()
980 _device->destroy(renderFinishedSemaphore);
981 _device->destroy(imageAvailableSemaphore);
982 _device->destroy(renderFinishedFence);
985hi_inline
void gfx_surface::build_command_buffers()
989 auto const commandBuffers = _device->allocateCommandBuffers({_graphics_queue->command_pool, vk::CommandBufferLevel::ePrimary, 1});
994hi_inline
void gfx_surface::teardown_command_buffers()
999 _device->freeCommandBuffers(_graphics_queue->command_pool,
commandBuffers);
1004 auto const lock = std::scoped_lock(gfx_system_mutex);
1017 throw gfx_error(
"Could not find a vulkan-device matching this surface");
1019 surface->set_device(device);
DOXYGEN BUG.
Definition algorithm_misc.hpp:20
hi_inline gfx_device * find_best_device(gfx_surface const &surface)
Find the best device for a surface.
Definition gfx_surface_vulkan_intf.hpp:191
hi_inline unfair_recursive_mutex gfx_system_mutex
Global mutex for GUI elements, like gfx_system, gfx_device, Windows and Widgets.
Definition gfx_system_globals.hpp:18
gfx_surface_loss
Definition gfx_surface_state.hpp:20
constexpr Out narrow_cast(In const &rhs) noexcept
Cast numeric values without loss of precision.
Definition cast.hpp:378
Definition gfx_device_vulkan_intf.hpp:26
A delegate for drawing on a window below the HikoGUI user interface.
Definition gfx_surface_delegate_vulkan.hpp:24