MXVK Vulkan Framework 0.24.0
C++20 Vulkan rendering framework for practical 2D and 3D application development with SDL3.
Loading...
Searching...
No Matches
example::ConsoleDemoWindow Class Reference
Inheritance diagram for example::ConsoleDemoWindow:

Public Types

enum class  PresentModePreference { LowLatency , Vsync }

Public Member Functions

VK_Sprite * attachPostProcessingShader (const std::string &fragmentShaderPath, float p1=0.0f, float p2=0.0f, float p3=0.0f, float p4=0.0f)
 Attach a full-screen post-processing fragment shader.
std::vector< VK_Sprite * > attachPostProcessingShaders (const std::vector< PostProcessingEffect > &effects)
void clearTextQueue ()
 Clear all queued text draw calls for the current frame.
 ConsoleDemoWindow (const std::string &path, const std::string &title, const int width, const int height, const bool fullscreen, bool enable_vsync)
VulkanContext context () const
VK_Sprite * createSprite (const std::string &pngPath, const std::string &vertexShaderPath="", const std::string &fragmentShaderPath="")
 Create a sprite from a PNG file and register it with this window.
VK_Sprite * createSprite (int width, int height, const std::string &vertexShaderPath="", const std::string &fragmentShaderPath="")
 Create a blank sprite texture and register it with this window.
VK_Sprite * createSprite (SDL_Surface *surface, const std::string &vertexShaderPath="", const std::string &fragmentShaderPath="")
 Create a sprite from an SDL surface and register it with this window.
VK_Sprite3D * createSprite3D (const std::string &pngPath, const std::string &vertexShaderPath="", const std::string &fragmentShaderPath="")
 Create a world-space billboard sprite from a PNG file.
VK_Sprite3D * createSprite3D (SDL_Surface *surface, const std::string &vertexShaderPath="", const std::string &fragmentShaderPath="")
 Create a world-space billboard sprite from an SDL surface.
void detachPostProcessingShader ()
 Detach the current post-processing shader and return to direct swapchain rendering.
void enablePostProcessing (VK_Sprite *sprite)
bool ensureRenderResources ()
 Ensure deferred render resources are initialized.
void event (SDL_Event &e) override
 Handle one SDL event.
VkCommandPool getCommandPool () const noexcept
 Get the command pool used for graphics/upload work.
VkFormat getDepthFormat () const noexcept
 Get the depth format used for dynamic rendering attachments.
VkDevice getDevice () const noexcept
 Get the Vulkan logical device handle.
VkQueue getGraphicsQueue () const noexcept
 Get the graphics queue handle.
VkPhysicalDevice getPhysicalDevice () const noexcept
 Get the Vulkan physical device handle.
VkPipelineCache getPipelineCache () const noexcept
 Get the persistent Vulkan pipeline cache used by framework pipelines.
SDL_Window * getSDLWindow () const noexcept
 Get the underlying SDL window handle.
VkExtent2D getSwapchainExtent () const noexcept
 Get the current swapchain extent.
VkFormat getSwapchainFormat () const noexcept
 Get the swapchain color format.
size_t getSwapchainImageCount () const noexcept
 Get the number of swapchain images currently allocated.
bool getTextDimensions (const std::string &text, int &width, int &height)
 Measure text dimensions in pixels.
bool getTextDimensions (const std::string &text, int &width, int &height, const Font &font)
bool getTextDimensions (const std::string &text, int &width, int &height, TTF_Font *font)
virtual bool initVulkan (bool validiation)
 Initialize Vulkan state.
void loop ()
 Run the main event/render loop.
virtual void onPrepareFrameRendering (VkCommandBuffer cmd, uint32_t image_index)
 Record resource transitions that must happen before dynamic rendering begins.
void printText (const std::string &text, int x, int y, const SDL_Color &col)
 Queue a text string for rendering during the current frame.
void printText (const std::string &text, int x, int y, const SDL_Color &col, const Font &font)
void printText (const std::string &text, int x, int y, const SDL_Color &col, TTF_Font *font)
void proc () override
 Execute one processing/update step.
void release ()
 Release Vulkan and SDL resources.
virtual void render ()
 Render one frame.
void saveSnapshot (const std::string &path)
 Save the most recently rendered window contents as a PNG file.
bool screenshotEnabled () const noexcept
 Check whether F10 screenshot capture is enabled.
void setClearColor (float r, float g, float b, float a=1.0f)
 Set the per-frame color attachment clear color.
void setEnableScreenshot (bool enabled) noexcept
 Enable or disable F10 screenshot capture.
void setFont (const std::string &fontPath, int fontSize=24)
 Set the active text-render font.
void setPostProcessingEnabled (bool enabled)
void setPostProcessingShaderParams (float p1=0.0f, float p2=0.0f, float p3=0.0f, float p4=0.0f)
 Set the post-processing shader params passed as a vec4 push constant.
void setPostProcessingShaderParams (size_t effectIndex, float p1=0.0f, float p2=0.0f, float p3=0.0f, float p4=0.0f)
void setPostProcessingShaderTimeEnabled (bool enabled)
 Keep shader param 1 updated with elapsed render time in seconds.
void setPostProcessingShaderTimeEnabled (size_t effectIndex, bool enabled)
void showCursor (bool on)
void trimMemory ()
 Return transient Vulkan command-pool allocations to the driver when supported.
bool validationEnabled () const
 Check whether Vulkan validation layers are currently enabled.

Static Public Member Functions

static VkBool32 debugCallback (VkDebugUtilsMessageSeverityFlagBitsEXT severity, VkDebugUtilsMessageTypeFlagsEXT type, const VkDebugUtilsMessengerCallbackDataEXT *callback_data, void *user_data)

Protected Member Functions

void captureSnapshotPixels (std::vector< std::uint8_t > &rgba_pixels, uint32_t &width, uint32_t &height)
 Capture the most recently presented swapchain image as tightly packed RGBA8 pixels.
void createDevice ()
 Create final device resources.
void createLogicalDevice ()
 Create a logical Vulkan device from the selected physical device.
void exit ()
 Request loop termination.
bool initWindow (const std::string &title, int width, int height, SDL_WindowFlags flags)
 Initialize SDL window resources.
virtual void onConfigureDepthStencilAttachments (VkRenderingAttachmentInfo &depth_attachment, VkRenderingAttachmentInfo &stencil_attachment, uint32_t image_index)
 Allow derived classes to customize depth/stencil attachments for the main dynamic rendering pass.
virtual void onRecordCustomRendering (VkCommandBuffer cmd, uint32_t image_index)
 Optional hook for derived classes to record extra draw commands.
virtual void onSwapchainAboutToRecreate ()
 Called right before swapchain-dependent resources are recreated.
virtual void onSwapchainRecreated ()
 Called after swapchain and render resources are recreated.
void pickDevice ()
 Pick a suitable Vulkan physical device.
void renderStandaloneSprite (VK_Sprite &sprite, VkCommandBuffer cmd)
 Render one standalone sprite using the window's shared sprite pipeline.

Static Protected Member Functions

static VkShaderModule createShaderModule (VkDevice device, const std::vector< char > &spv_bytes)
 Create a shader module from SPIR-V bytecode.
static std::vector< char > loadSpv (const std::string &path)
 Load a SPIR-V file from disk.

Protected Attributes

bool active = false
VkClearColorValue clear_color {{0.0f, 0.0f, 0.0f, 1.0f}}
std::vector< VkCommandBuffer > command_buffers {}
VkCommandPool command_pool = VK_NULL_HANDLE
uint32_t current_frame = 0
VkDebugUtilsMessengerEXT debug_messenger = VK_NULL_HANDLE
VkFormat depth_format = VK_FORMAT_UNDEFINED
std::vector< bool > depth_image_initialized {}
std::vector< VkDeviceMemory > depth_image_memories {}
std::vector< VkImageView > depth_image_views {}
std::vector< VkImage > depth_images {}
VkDevice device = VK_NULL_HANDLE
bool font_configured = false
std::string font_path {}
int font_size = 24
bool force_swapchain_recreate = false
bool fps_counter_enabled = false
Font fps_counter_font {}
bool fps_counter_font_ready = false
uint32_t fps_counter_frame_count = 0
std::chrono::steady_clock::time_point fps_counter_sample_time {}
std::string fps_counter_text = "FPS: --"
bool framebuffer_resized = false
VkQueue graphics_queue = VK_NULL_HANDLE
uint32_t graphics_queue_family = invalid_queue_index
std::array< VkSemaphore, max_frames_in_flight > image_available {}
std::vector< VkFence > image_fences {}
std::array< VkFence, max_frames_in_flight > in_flight_fences {}
VkInstance instance = VK_NULL_HANDLE
std::chrono::steady_clock::time_point last_memory_trim_time = std::chrono::steady_clock::now()
uint32_t last_presented_image_index = invalid_queue_index
uint64_t last_resize_event_ms = 0
VK_Sprite * owned_post_process_sprite = nullptr
std::vector< VK_Sprite * > owned_post_process_sprites {}
VkPhysicalDevice physical_device = VK_NULL_HANDLE
VkPipelineCache pipeline_cache = VK_NULL_HANDLE
std::vector< std::array< float, 4 > > post_process_effect_params {}
std::vector< std::chrono::steady_clock::time_point > post_process_effect_start_times {}
std::vector< bool > post_process_effect_time_enabled {}
bool post_process_enabled = true
std::vector< std::vector< VkImage > > post_process_images {}
std::vector< std::vector< bool > > post_process_initialized {}
std::vector< std::vector< VkDeviceMemory > > post_process_memories {}
std::array< float, 4 > post_process_params {}
VK_Sprite * post_process_sprite = nullptr
std::vector< VK_Sprite * > post_process_sprites {}
std::chrono::steady_clock::time_point post_process_start_time = std::chrono::steady_clock::now()
bool post_process_time_enabled = false
std::vector< std::vector< VkImageView > > post_process_views {}
PresentModePreference present_mode_preference = PresentModePreference::LowLatency
VkQueue present_queue = VK_NULL_HANDLE
uint32_t present_queue_family = invalid_queue_index
std::vector< VkSemaphore > render_finished {}
std::vector< VkSwapchainKHR > retired_swapchains
bool screenshot_enabled = defaultEnableScreenshot()
uint32_t screenshot_index = 0
std::string screenshot_prefix = defaultExecutableName()
std::condition_variable screenshot_queue_cv
std::mutex screenshot_queue_mutex
std::deque< ScreenshotSaveTaskscreenshot_save_queue
std::thread screenshot_worker
bool screenshot_worker_stop = false
bool sdl_initialized = false
VkDescriptorSetLayout sprite_descriptor_set_layout = VK_NULL_HANDLE
VkPipeline sprite_pipeline = VK_NULL_HANDLE
VkPipelineLayout sprite_pipeline_layout = VK_NULL_HANDLE
bool sprite_state_dirty = false
std::vector< std::unique_ptr< VK_Sprite > > sprites {}
std::vector< std::unique_ptr< VK_Sprite3D > > sprites3d {}
VkSurfaceKHR surface = VK_NULL_HANDLE
VkSwapchainKHR swapchain = VK_NULL_HANDLE
VkExtent2D swapchain_extent {}
VkFormat swapchain_format = VK_FORMAT_UNDEFINED
std::vector< bool > swapchain_image_initialized {}
std::vector< VkImageView > swapchain_image_views {}
std::vector< VkImage > swapchain_images {}
bool swapchain_supports_transfer_src = false
VkDescriptorSetLayout text_descriptor_set_layout = VK_NULL_HANDLE
VkPipeline text_pipeline = VK_NULL_HANDLE
VkPipelineLayout text_pipeline_layout = VK_NULL_HANDLE
std::unique_ptr< VK_Text > text_renderer {}
bool text_state_dirty = false
bool validation_enabled = false
std::unique_ptr< SDL_Window, SDLWindowDeleterwindow {}

Static Protected Attributes

static constexpr std::chrono::seconds MEMORY_TRIM_INTERVAL {30}
static constexpr uint64_t resize_settle_delay_ms = 150

Detailed Description

Definition at line 13 of file main.cpp.

Member Enumeration Documentation

◆ PresentModePreference

enum class mxvk::VK_Window::PresentModePreference
stronginherited
Enumerator
LowLatency 
Vsync 

Definition at line 39 of file mxvk.hpp.

39 {
40 LowLatency,
41 Vsync
42 };

Constructor & Destructor Documentation

◆ ConsoleDemoWindow()

example::ConsoleDemoWindow::ConsoleDemoWindow ( const std::string & path,
const std::string & title,
const int width,
const int height,
const bool fullscreen,
bool enable_vsync )
inline

Definition at line 15 of file main.cpp.

20 : mxvk::VK_Window(title, width, height, fullscreen, MXVK_VALIDATION, enable_vsync) {
21 const std::string base_path = path.empty() ? std::string(console_demo_ASSET_DIR) : path;
22 const std::string sprite_vert_path = base_path + "/data/sprite.vert.spv";
23 const std::string shader_path = base_path + "/data/background_pulse.frag.spv";
24
25 background = createSprite(base_path + "/data/background.png", sprite_vert_path, shader_path);
26 setFont(base_path + "/data/font.ttf", 20);
27 console.attach(*this, base_path + "/data/font.ttf", 20);
28 console.setSpriteYOriginTopLeft(true);
29 console.setPrompt("mxvk> ");
30 console.printLine("Press F3 to open/close the console.");
31 console.printLine("Type 'help' for built-in commands.");
32
33 console.setCommandCallback([this](mxvk::VK_Window &, const std::vector<std::string> &args, std::ostream &out) {
34 if (args.empty()) {
35 return true;
36 }
37
38 if (args[0] == "echo") {
39 for (std::size_t i = 1; i < args.size(); ++i) {
40 if (i > 1) {
41 out << ' ';
42 }
43 out << args[i];
44 }
45 return true;
46 }
47
48 if (args[0] == "quit" || args[0] == "exit") {
49 out << "Closing window...";
50 exit();
51 return true;
52 }
53
54 if (args[0] == "about") {
55 out << "console_demo: MXVK Vulkan console sample.\n(C) 2026 LostSideDead Software\n";
56 return true;
57 }
58
59 return false;
60 });
61 }
VK_Sprite * createSprite(const std::string &pngPath, const std::string &vertexShaderPath="", const std::string &fragmentShaderPath="")
Create a sprite from a PNG file and register it with this window.
Definition mxvk.cpp:3477
void exit()
Request loop termination.
Definition mxvk.cpp:1126
void setFont(const std::string &fontPath, int fontSize=24)
Set the active text-render font.
Definition mxvk.cpp:2991
#define MXVK_VALIDATION
Definition mxvk.hpp:27

Member Function Documentation

◆ attachPostProcessingShader()

VK_Sprite * mxvk::VK_Window::attachPostProcessingShader ( const std::string & fragmentShaderPath,
float p1 = 0.0f,
float p2 = 0.0f,
float p3 = 0.0f,
float p4 = 0.0f )
inherited

Attach a full-screen post-processing fragment shader.

The window renders the normal scene into an offscreen color target, then draws that target through the supplied sprite-compatible fragment shader into the swapchain. Shader params are passed through the existing sprite push-constant vec4.

Parameters
fragmentShaderPathPost-process fragment shader SPIR-V path.
p1First shader parameter.
p2Second shader parameter.
p3Third shader parameter.
p4Fourth shader parameter.
Returns
Non-owning pointer to the internal post-process sprite for advanced setup.

Definition at line 1154 of file mxvk.cpp.

1154 {
1155 const std::vector<VK_Sprite *> sprites = attachPostProcessingShaders({PostProcessingEffect{fragmentShaderPath, {p1, p2, p3, p4}, false}});
1156 return sprites.empty() ? nullptr : sprites.front();
1157 }
std::vector< VK_Sprite * > attachPostProcessingShaders(const std::vector< PostProcessingEffect > &effects)
Definition mxvk.cpp:1159
std::vector< std::unique_ptr< VK_Sprite > > sprites
Definition mxvk.hpp:540

◆ attachPostProcessingShaders()

std::vector< VK_Sprite * > mxvk::VK_Window::attachPostProcessingShaders ( const std::vector< PostProcessingEffect > & effects)
inherited

Definition at line 1159 of file mxvk.cpp.

1159 {
1161
1162 std::vector<VK_Sprite *> attachedSprites;
1163 attachedSprites.reserve(effects.size());
1164 for (const PostProcessingEffect &effect : effects) {
1165 if (effect.fragmentShaderPath.empty()) {
1166 throw mxvk::Exception("Cannot attach post-processing shader with an empty fragment shader path");
1167 }
1168
1169 VK_Sprite *sprite = createSprite(
1170 1,
1171 1,
1172 resolveRuntimeShaderPath("sprite.vert.spv", MXVK_SPRITE_SHADER_DIR),
1173 effect.fragmentShaderPath);
1174 const uint32_t black_pixel = 0xFF000000u;
1175 sprite->updateTexture(&black_pixel, 1, 1);
1176 sprite->setShaderParams(effect.params[0], effect.params[1], effect.params[2], effect.params[3]);
1177
1178 attachedSprites.push_back(sprite);
1179 owned_post_process_sprites.push_back(sprite);
1180 post_process_sprites.push_back(sprite);
1181 post_process_effect_params.push_back(effect.params);
1182 post_process_effect_time_enabled.push_back(effect.timeEnabled);
1183 post_process_effect_start_times.push_back(std::chrono::steady_clock::now());
1184 }
1185
1186 post_process_sprite = attachedSprites.empty() ? nullptr : attachedSprites.front();
1188 post_process_params = post_process_effect_params.empty() ? std::array<float, 4>{} : post_process_effect_params.front();
1190 post_process_start_time = post_process_effect_start_times.empty() ? std::chrono::steady_clock::now() : post_process_effect_start_times.front();
1191 if (device != VK_NULL_HANDLE && !swapchain_images.empty()) {
1192 createPostProcessTargets();
1193 }
1194 sprite_state_dirty = true;
1195 return attachedSprites;
1196 }
VkDevice device
Definition mxvk.hpp:485
std::vector< bool > post_process_effect_time_enabled
Definition mxvk.hpp:555
std::vector< std::array< float, 4 > > post_process_effect_params
Definition mxvk.hpp:554
std::chrono::steady_clock::time_point post_process_start_time
Definition mxvk.hpp:551
std::vector< VK_Sprite * > owned_post_process_sprites
Definition mxvk.hpp:553
std::vector< VkImage > swapchain_images
Definition mxvk.hpp:496
bool post_process_time_enabled
Definition mxvk.hpp:549
std::vector< std::chrono::steady_clock::time_point > post_process_effect_start_times
Definition mxvk.hpp:556
std::array< float, 4 > post_process_params
Definition mxvk.hpp:550
void detachPostProcessingShader()
Detach the current post-processing shader and return to direct swapchain rendering.
Definition mxvk.cpp:1198
std::vector< VK_Sprite * > post_process_sprites
Definition mxvk.hpp:552
bool sprite_state_dirty
Definition mxvk.hpp:545
VK_Sprite * post_process_sprite
Definition mxvk.hpp:546
VK_Sprite * owned_post_process_sprite
Definition mxvk.hpp:547
#define MXVK_SPRITE_SHADER_DIR
Definition mxvk.cpp:25

◆ captureSnapshotPixels()

void mxvk::VK_Window::captureSnapshotPixels ( std::vector< std::uint8_t > & rgba_pixels,
uint32_t & width,
uint32_t & height )
protectedinherited

Capture the most recently presented swapchain image as tightly packed RGBA8 pixels.

This performs a synchronous GPU readback and may briefly stall rendering.

Definition at line 800 of file mxvk.cpp.

800 {
801 if (device == VK_NULL_HANDLE || command_pool == VK_NULL_HANDLE || graphics_queue == VK_NULL_HANDLE) {
802 throw mxvk::Exception("captureSnapshotPixels called before Vulkan render resources are ready");
803 }
805 throw mxvk::Exception("captureSnapshotPixels requires swapchain transfer-source support");
806 }
807 if (last_presented_image_index == invalid_queue_index ||
810 throw mxvk::Exception("captureSnapshotPixels called before a frame has been presented");
811 }
812 if (swapchain_extent.width == 0U || swapchain_extent.height == 0U) {
813 throw mxvk::Exception("captureSnapshotPixels cannot capture an empty swapchain extent");
814 }
815
816 const bool format_is_bgra =
817 swapchain_format == VK_FORMAT_B8G8R8A8_UNORM ||
818 swapchain_format == VK_FORMAT_B8G8R8A8_SRGB;
819 const bool format_is_rgba =
820 swapchain_format == VK_FORMAT_R8G8B8A8_UNORM ||
821 swapchain_format == VK_FORMAT_R8G8B8A8_SRGB;
822 if (!format_is_bgra && !format_is_rgba) {
823 throw mxvk::Exception(std::format("captureSnapshotPixels unsupported swapchain format: {}", static_cast<int>(swapchain_format)));
824 }
825
826 const VkDeviceSize row_bytes = static_cast<VkDeviceSize>(swapchain_extent.width) * 4U;
827 const VkDeviceSize image_bytes = row_bytes * static_cast<VkDeviceSize>(swapchain_extent.height);
828
829 VkBuffer readback_buffer = VK_NULL_HANDLE;
830 VkDeviceMemory readback_memory = VK_NULL_HANDLE;
831 VkCommandBuffer command_buffer = VK_NULL_HANDLE;
832 VkFence copy_fence = VK_NULL_HANDLE;
833
834 auto cleanup = [&]() {
835 if (copy_fence != VK_NULL_HANDLE) {
836 vkDestroyFence(device, copy_fence, nullptr);
837 }
838 if (command_buffer != VK_NULL_HANDLE) {
839 vkFreeCommandBuffers(device, command_pool, 1, &command_buffer);
840 }
841 if (readback_buffer != VK_NULL_HANDLE) {
842 vkDestroyBuffer(device, readback_buffer, nullptr);
843 }
844 if (readback_memory != VK_NULL_HANDLE) {
845 vkFreeMemory(device, readback_memory, nullptr);
846 }
847 };
848
849 try {
850 VkBufferCreateInfo buffer_info{};
851 buffer_info.sType = VK_STRUCTURE_TYPE_BUFFER_CREATE_INFO;
852 buffer_info.size = image_bytes;
853 buffer_info.usage = VK_BUFFER_USAGE_TRANSFER_DST_BIT;
854 buffer_info.sharingMode = VK_SHARING_MODE_EXCLUSIVE;
855 if (vkCreateBuffer(device, &buffer_info, nullptr, &readback_buffer) != VK_SUCCESS) {
856 throw mxvk::Exception("captureSnapshotPixels failed to create readback buffer");
857 }
858
859 VkMemoryRequirements memory_requirements{};
860 vkGetBufferMemoryRequirements(device, readback_buffer, &memory_requirements);
861
862 VkPhysicalDeviceMemoryProperties memory_properties{};
863 vkGetPhysicalDeviceMemoryProperties(physical_device, &memory_properties);
864 uint32_t memory_type_index = invalid_queue_index;
865 constexpr VkMemoryPropertyFlags required_properties =
866 VK_MEMORY_PROPERTY_HOST_VISIBLE_BIT | VK_MEMORY_PROPERTY_HOST_COHERENT_BIT;
867 for (uint32_t i = 0; i < memory_properties.memoryTypeCount; ++i) {
868 const bool type_matches = (memory_requirements.memoryTypeBits & (1U << i)) != 0U;
869 const bool properties_match =
870 (memory_properties.memoryTypes[i].propertyFlags & required_properties) == required_properties;
871 if (type_matches && properties_match) {
872 memory_type_index = i;
873 break;
874 }
875 }
876 if (memory_type_index == invalid_queue_index) {
877 throw mxvk::Exception("captureSnapshotPixels failed to find host-visible coherent memory");
878 }
879
880 VkMemoryAllocateInfo allocation_info{};
881 allocation_info.sType = VK_STRUCTURE_TYPE_MEMORY_ALLOCATE_INFO;
882 allocation_info.allocationSize = memory_requirements.size;
883 allocation_info.memoryTypeIndex = memory_type_index;
884 if (vkAllocateMemory(device, &allocation_info, nullptr, &readback_memory) != VK_SUCCESS) {
885 throw mxvk::Exception("captureSnapshotPixels failed to allocate readback memory");
886 }
887 if (vkBindBufferMemory(device, readback_buffer, readback_memory, 0) != VK_SUCCESS) {
888 throw mxvk::Exception("captureSnapshotPixels failed to bind readback memory");
889 }
890
891 VkCommandBufferAllocateInfo command_buffer_info{};
892 command_buffer_info.sType = VK_STRUCTURE_TYPE_COMMAND_BUFFER_ALLOCATE_INFO;
893 command_buffer_info.commandPool = command_pool;
894 command_buffer_info.level = VK_COMMAND_BUFFER_LEVEL_PRIMARY;
895 command_buffer_info.commandBufferCount = 1;
896 if (vkAllocateCommandBuffers(device, &command_buffer_info, &command_buffer) != VK_SUCCESS) {
897 throw mxvk::Exception("captureSnapshotPixels failed to allocate command buffer");
898 }
899
900 VkFenceCreateInfo fence_info{};
901 fence_info.sType = VK_STRUCTURE_TYPE_FENCE_CREATE_INFO;
902 if (vkCreateFence(device, &fence_info, nullptr, &copy_fence) != VK_SUCCESS) {
903 throw mxvk::Exception("captureSnapshotPixels failed to create copy fence");
904 }
905
906 VkFence source_fence = image_fences[last_presented_image_index];
907 if (source_fence != VK_NULL_HANDLE) {
908 const VkResult wait_result = vkWaitForFences(device, 1, &source_fence, VK_TRUE, UINT64_MAX);
909 if (wait_result != VK_SUCCESS) {
910 throw mxvk::Exception(std::format("captureSnapshotPixels failed waiting for rendered frame: {}", static_cast<int>(wait_result)));
911 }
912 }
913
914 const VkResult idle_result = vkDeviceWaitIdle(device);
915 if (idle_result != VK_SUCCESS) {
916 throw mxvk::Exception(std::format("captureSnapshotPixels failed waiting for device idle: {}", static_cast<int>(idle_result)));
917 }
918
919 VkCommandBufferBeginInfo begin_info{};
920 begin_info.sType = VK_STRUCTURE_TYPE_COMMAND_BUFFER_BEGIN_INFO;
921 begin_info.flags = VK_COMMAND_BUFFER_USAGE_ONE_TIME_SUBMIT_BIT;
922 if (vkBeginCommandBuffer(command_buffer, &begin_info) != VK_SUCCESS) {
923 throw mxvk::Exception("captureSnapshotPixels failed to begin copy command buffer");
924 }
925
926 VkImageMemoryBarrier2 to_transfer_barrier{};
927 to_transfer_barrier.sType = VK_STRUCTURE_TYPE_IMAGE_MEMORY_BARRIER_2;
928 to_transfer_barrier.srcStageMask = VK_PIPELINE_STAGE_2_NONE;
929 to_transfer_barrier.srcAccessMask = VK_ACCESS_2_NONE;
930 to_transfer_barrier.dstStageMask = VK_PIPELINE_STAGE_2_TRANSFER_BIT;
931 to_transfer_barrier.dstAccessMask = VK_ACCESS_2_TRANSFER_READ_BIT;
932 to_transfer_barrier.oldLayout = VK_IMAGE_LAYOUT_PRESENT_SRC_KHR;
933 to_transfer_barrier.newLayout = VK_IMAGE_LAYOUT_TRANSFER_SRC_OPTIMAL;
934 to_transfer_barrier.srcQueueFamilyIndex = VK_QUEUE_FAMILY_IGNORED;
935 to_transfer_barrier.dstQueueFamilyIndex = VK_QUEUE_FAMILY_IGNORED;
936 to_transfer_barrier.image = swapchain_images[last_presented_image_index];
937 to_transfer_barrier.subresourceRange.aspectMask = VK_IMAGE_ASPECT_COLOR_BIT;
938 to_transfer_barrier.subresourceRange.baseMipLevel = 0;
939 to_transfer_barrier.subresourceRange.levelCount = 1;
940 to_transfer_barrier.subresourceRange.baseArrayLayer = 0;
941 to_transfer_barrier.subresourceRange.layerCount = 1;
942
943 VkDependencyInfo to_transfer_dependency{};
944 to_transfer_dependency.sType = VK_STRUCTURE_TYPE_DEPENDENCY_INFO;
945 to_transfer_dependency.imageMemoryBarrierCount = 1;
946 to_transfer_dependency.pImageMemoryBarriers = &to_transfer_barrier;
947 vkCmdPipelineBarrier2(command_buffer, &to_transfer_dependency);
948
949 VkBufferImageCopy copy_region{};
950 copy_region.bufferOffset = 0;
951 copy_region.bufferRowLength = 0;
952 copy_region.bufferImageHeight = 0;
953 copy_region.imageSubresource.aspectMask = VK_IMAGE_ASPECT_COLOR_BIT;
954 copy_region.imageSubresource.mipLevel = 0;
955 copy_region.imageSubresource.baseArrayLayer = 0;
956 copy_region.imageSubresource.layerCount = 1;
957 copy_region.imageOffset = {0, 0, 0};
958 copy_region.imageExtent = {swapchain_extent.width, swapchain_extent.height, 1};
959 vkCmdCopyImageToBuffer(command_buffer,
961 VK_IMAGE_LAYOUT_TRANSFER_SRC_OPTIMAL,
962 readback_buffer,
963 1,
964 &copy_region);
965
966 VkImageMemoryBarrier2 to_present_barrier{};
967 to_present_barrier.sType = VK_STRUCTURE_TYPE_IMAGE_MEMORY_BARRIER_2;
968 to_present_barrier.srcStageMask = VK_PIPELINE_STAGE_2_TRANSFER_BIT;
969 to_present_barrier.srcAccessMask = VK_ACCESS_2_TRANSFER_READ_BIT;
970 to_present_barrier.dstStageMask = VK_PIPELINE_STAGE_2_NONE;
971 to_present_barrier.dstAccessMask = VK_ACCESS_2_NONE;
972 to_present_barrier.oldLayout = VK_IMAGE_LAYOUT_TRANSFER_SRC_OPTIMAL;
973 to_present_barrier.newLayout = VK_IMAGE_LAYOUT_PRESENT_SRC_KHR;
974 to_present_barrier.srcQueueFamilyIndex = VK_QUEUE_FAMILY_IGNORED;
975 to_present_barrier.dstQueueFamilyIndex = VK_QUEUE_FAMILY_IGNORED;
976 to_present_barrier.image = swapchain_images[last_presented_image_index];
977 to_present_barrier.subresourceRange = to_transfer_barrier.subresourceRange;
978
979 VkDependencyInfo to_present_dependency{};
980 to_present_dependency.sType = VK_STRUCTURE_TYPE_DEPENDENCY_INFO;
981 to_present_dependency.imageMemoryBarrierCount = 1;
982 to_present_dependency.pImageMemoryBarriers = &to_present_barrier;
983 vkCmdPipelineBarrier2(command_buffer, &to_present_dependency);
984
985 if (vkEndCommandBuffer(command_buffer) != VK_SUCCESS) {
986 throw mxvk::Exception("captureSnapshotPixels failed to end copy command buffer");
987 }
988
989 VkCommandBufferSubmitInfo command_submit_info{};
990 command_submit_info.sType = VK_STRUCTURE_TYPE_COMMAND_BUFFER_SUBMIT_INFO;
991 command_submit_info.commandBuffer = command_buffer;
992
993 VkSubmitInfo2 submit_info{};
994 submit_info.sType = VK_STRUCTURE_TYPE_SUBMIT_INFO_2;
995 submit_info.commandBufferInfoCount = 1;
996 submit_info.pCommandBufferInfos = &command_submit_info;
997
998 const VkResult submit_result = vkQueueSubmit2(graphics_queue, 1, &submit_info, copy_fence);
999 if (submit_result != VK_SUCCESS) {
1000 throw mxvk::Exception(std::format("captureSnapshotPixels failed to submit copy command: {}", static_cast<int>(submit_result)));
1001 }
1002 const VkResult copy_wait_result = vkWaitForFences(device, 1, &copy_fence, VK_TRUE, UINT64_MAX);
1003 if (copy_wait_result != VK_SUCCESS) {
1004 throw mxvk::Exception(std::format("captureSnapshotPixels failed waiting for copy: {}", static_cast<int>(copy_wait_result)));
1005 }
1006
1007 void *mapped = nullptr;
1008 if (vkMapMemory(device, readback_memory, 0, image_bytes, 0, &mapped) != VK_SUCCESS) {
1009 throw mxvk::Exception("captureSnapshotPixels failed to map readback memory");
1010 }
1011
1012 const auto *src = static_cast<const std::uint8_t *>(mapped);
1013 rgba_pixels.resize(static_cast<std::size_t>(image_bytes));
1014 for (std::size_t i = 0; i < rgba_pixels.size(); i += 4U) {
1015 if (format_is_bgra) {
1016 rgba_pixels[i + 0U] = src[i + 2U];
1017 rgba_pixels[i + 1U] = src[i + 1U];
1018 rgba_pixels[i + 2U] = src[i + 0U];
1019 rgba_pixels[i + 3U] = src[i + 3U];
1020 } else {
1021 rgba_pixels[i + 0U] = src[i + 0U];
1022 rgba_pixels[i + 1U] = src[i + 1U];
1023 rgba_pixels[i + 2U] = src[i + 2U];
1024 rgba_pixels[i + 3U] = src[i + 3U];
1025 }
1026 }
1027 vkUnmapMemory(device, readback_memory);
1028 width = swapchain_extent.width;
1029 height = swapchain_extent.height;
1030 } catch (...) {
1031 cleanup();
1032 throw;
1033 }
1034
1035 cleanup();
1036 }
VkFormat swapchain_format
Definition mxvk.hpp:493
VkExtent2D swapchain_extent
Definition mxvk.hpp:495
uint32_t last_presented_image_index
Definition mxvk.hpp:512
std::vector< VkFence > image_fences
Definition mxvk.hpp:510
VkCommandPool command_pool
Definition mxvk.hpp:504
VkPhysicalDevice physical_device
Definition mxvk.hpp:484
VkQueue graphics_queue
Definition mxvk.hpp:488
bool swapchain_supports_transfer_src
Definition mxvk.hpp:513

◆ clearTextQueue()

void mxvk::VK_Window::clearTextQueue ( )
inherited

Clear all queued text draw calls for the current frame.

Definition at line 3063 of file mxvk.cpp.

3063 {
3064 if (text_renderer) {
3065 text_renderer->clearQueue();
3066 }
3067 }
std::unique_ptr< VK_Text > text_renderer
Definition mxvk.hpp:562

◆ context()

VulkanContext mxvk::VK_Window::context ( ) const
inherited

Definition at line 59 of file mxvk.cpp.

59 {
60 return VulkanContext{
61 .device = getDevice(),
62 .physical_device = getPhysicalDevice(),
63 .graphics_queue = getGraphicsQueue(),
64 .command_pool = getCommandPool(),
65 };
66 }
VkDevice getDevice() const noexcept
Get the Vulkan logical device handle.
Definition mxvk.hpp:168
VkQueue getGraphicsQueue() const noexcept
Get the graphics queue handle.
Definition mxvk.hpp:174
VkCommandPool getCommandPool() const noexcept
Get the command pool used for graphics/upload work.
Definition mxvk.hpp:177
VkPhysicalDevice getPhysicalDevice() const noexcept
Get the Vulkan physical device handle.
Definition mxvk.hpp:171

◆ createDevice()

void mxvk::VK_Window::createDevice ( )
protectedinherited

Create final device resources.

Definition at line 1645 of file mxvk.cpp.

1645 {
1646 std::cout << "mxvk: entering createDevice\n";
1647 if (device == VK_NULL_HANDLE) {
1648 std::cout << "vk: skipping createDevice because logical device is null\n";
1649 return;
1650 }
1651
1652 const bool sync_ready =
1653 std::ranges::all_of(image_available.begin(), image_available.end(), [](VkSemaphore semaphore) { return semaphore != VK_NULL_HANDLE; }) &&
1654 render_finished.size() == swapchain_images.size() &&
1655 std::ranges::all_of(render_finished.begin(), render_finished.end(), [](VkSemaphore semaphore) { return semaphore != VK_NULL_HANDLE; }) &&
1656 std::ranges::all_of(in_flight_fences.begin(), in_flight_fences.end(), [](VkFence fence) { return fence != VK_NULL_HANDLE; });
1657
1658 if (swapchain != VK_NULL_HANDLE && command_pool != VK_NULL_HANDLE && !command_buffers.empty() &&
1659 sync_ready && image_fences.size() == swapchain_images.size()) {
1660 std::cout << "vk: createDevice skipped because render resources are already initialized\n";
1661 return;
1662 }
1663
1664 std::cout << "vk: creating swapchain\n";
1665 if (!createSwapchain(VK_NULL_HANDLE)) {
1666 std::cout << "vk: createSwapchain failed\n";
1667 return;
1668 }
1669
1670 std::cout << "vk: creating render resources\n";
1671 if (!createRenderResources()) {
1672 std::cout << "vk: createRenderResources failed\n";
1673 return;
1674 }
1675
1676 std::cout << "vk: creating synchronization objects\n";
1677 if (!createSyncObjects()) {
1678 std::cout << "vk: createSyncObjects failed\n";
1679 return;
1680 }
1681
1682 std::cout << "mxvk: createDevice complete\n";
1683 }
VkSwapchainKHR swapchain
Definition mxvk.hpp:492
std::vector< VkCommandBuffer > command_buffers
Definition mxvk.hpp:505
std::vector< VkSemaphore > render_finished
Definition mxvk.hpp:508
std::array< VkSemaphore, max_frames_in_flight > image_available
Definition mxvk.hpp:507
std::array< VkFence, max_frames_in_flight > in_flight_fences
Definition mxvk.hpp:509

◆ createLogicalDevice()

void mxvk::VK_Window::createLogicalDevice ( )
protectedinherited

Create a logical Vulkan device from the selected physical device.

Definition at line 1514 of file mxvk.cpp.

1514 {
1515 std::cout << "mxvk: entering createLogicalDevice\n";
1516 if (physical_device == VK_NULL_HANDLE) {
1517 std::cout << "vk: cannot create logical device without a selected physical device\n";
1518 return;
1519 }
1520
1521 std::vector<uint32_t> queue_families{};
1522 queue_families.push_back(graphics_queue_family);
1524 queue_families.push_back(present_queue_family);
1525 }
1526
1527 constexpr float queue_priority = 1.0f;
1528 std::vector<VkDeviceQueueCreateInfo> queue_create_infos{};
1529 queue_create_infos.reserve(queue_families.size());
1530 for (const uint32_t family : queue_families) {
1531 VkDeviceQueueCreateInfo queue_create_info{};
1532 queue_create_info.sType = VK_STRUCTURE_TYPE_DEVICE_QUEUE_CREATE_INFO;
1533 queue_create_info.queueFamilyIndex = family;
1534 queue_create_info.queueCount = 1;
1535 queue_create_info.pQueuePriorities = &queue_priority;
1536 queue_create_infos.push_back(queue_create_info);
1537 }
1538 uint32_t device_extension_count = 0;
1539 vkEnumerateDeviceExtensionProperties(physical_device, nullptr, &device_extension_count, nullptr);
1540 std::vector<VkExtensionProperties> device_extensions(device_extension_count);
1541 if (device_extension_count > 0U) {
1542 vkEnumerateDeviceExtensionProperties(physical_device, nullptr, &device_extension_count, device_extensions.data());
1543 }
1544
1545 [[maybe_unused]] const auto has_device_extension = [&device_extensions](const char *extension_name) {
1546 return std::ranges::any_of(
1547 device_extensions,
1548 [extension_name](const VkExtensionProperties &ext) {
1549 return std::strcmp(ext.extensionName, extension_name) == 0;
1550 });
1551 };
1552
1553 std::vector<const char *> required_device_extensions = {VK_KHR_SWAPCHAIN_EXTENSION_NAME};
1554#if defined(MXVK_CUDA) && defined(__linux__)
1555 if (has_device_extension(VK_KHR_EXTERNAL_MEMORY_FD_EXTENSION_NAME)) {
1556 if (has_device_extension(VK_KHR_EXTERNAL_MEMORY_EXTENSION_NAME)) {
1557 required_device_extensions.push_back(VK_KHR_EXTERNAL_MEMORY_EXTENSION_NAME);
1558 }
1559 required_device_extensions.push_back(VK_KHR_EXTERNAL_MEMORY_FD_EXTENSION_NAME);
1560 std::cout << "vk: enabling external memory FD support for CUDA interop\n";
1561 } else {
1562 std::cout << "vk: external memory FD support unavailable; CUDA texture interop will fall back\n";
1563 }
1564#endif
1565
1566#if defined(MXVK_USE_MOLTENVK)
1567 const bool has_portability_subset = std::ranges::any_of(
1568 device_extensions,
1569 [](const VkExtensionProperties &ext) {
1570 return std::strcmp(ext.extensionName, VK_KHR_PORTABILITY_SUBSET_EXTENSION_NAME) == 0;
1571 });
1572
1573 if (has_portability_subset) {
1574 required_device_extensions.push_back(VK_KHR_PORTABILITY_SUBSET_EXTENSION_NAME);
1575 }
1576#endif
1577 VkPhysicalDeviceFeatures2 features2{};
1578 features2.sType = VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_FEATURES_2;
1579 VkPhysicalDeviceVulkan13Features supported_vulkan13_features{};
1580 supported_vulkan13_features.sType = VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_VULKAN_1_3_FEATURES;
1581 features2.pNext = &supported_vulkan13_features;
1582 vkGetPhysicalDeviceFeatures2(physical_device, &features2);
1583
1584 std::cout << std::format(
1585 "vk: feature support - synchronization2={}, dynamicRendering={}, shaderFloat64={}, fillModeNonSolid={}, samplerAnisotropy={}\n",
1586 supported_vulkan13_features.synchronization2 == VK_TRUE ? "true" : "false",
1587 supported_vulkan13_features.dynamicRendering == VK_TRUE ? "true" : "false",
1588 features2.features.shaderFloat64 == VK_TRUE ? "true" : "false",
1589 features2.features.fillModeNonSolid == VK_TRUE ? "true" : "false",
1590 features2.features.samplerAnisotropy == VK_TRUE ? "true" : "false");
1591
1592 if (supported_vulkan13_features.synchronization2 != VK_TRUE) {
1593 std::cout << "vk: synchronization2 is unsupported on selected physical device\n";
1594 return;
1595 }
1596 if (supported_vulkan13_features.dynamicRendering != VK_TRUE) {
1597 std::cout << "vk: dynamic rendering is unsupported on selected physical device\n";
1598 return;
1599 }
1600 VkPhysicalDeviceVulkan13Features vulkan13_features{};
1601 vulkan13_features.sType = VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_VULKAN_1_3_FEATURES;
1602 vulkan13_features.synchronization2 = VK_TRUE;
1603 vulkan13_features.dynamicRendering = VK_TRUE;
1604
1605 VkPhysicalDeviceFeatures2 enabled_features2{};
1606 enabled_features2.sType = VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_FEATURES_2;
1607 enabled_features2.features.shaderFloat64 = features2.features.shaderFloat64;
1608 enabled_features2.features.fillModeNonSolid = features2.features.fillModeNonSolid;
1609 enabled_features2.features.samplerAnisotropy = features2.features.samplerAnisotropy;
1610
1611 enabled_features2.pNext = &vulkan13_features;
1612
1613 VkDeviceCreateInfo create_info{};
1614 create_info.sType = VK_STRUCTURE_TYPE_DEVICE_CREATE_INFO;
1615 create_info.pNext = &enabled_features2;
1616 create_info.queueCreateInfoCount = static_cast<uint32_t>(queue_create_infos.size());
1617 create_info.pQueueCreateInfos = queue_create_infos.data();
1618 create_info.enabledExtensionCount = static_cast<uint32_t>(required_device_extensions.size());
1619 create_info.ppEnabledExtensionNames = required_device_extensions.data();
1620 create_info.enabledLayerCount = 0;
1621 create_info.ppEnabledLayerNames = nullptr;
1622 create_info.pEnabledFeatures = nullptr;
1623
1624 std::cout << "vk: creating logical device\n";
1625 if (vkCreateDevice(physical_device, &create_info, nullptr, &device) != VK_SUCCESS) {
1626 std::cout << "vk: logical device creation failed\n";
1627 device = VK_NULL_HANDLE;
1628 return;
1629 }
1630
1631 std::cout << "vk: loading device-level function pointers via volk\n";
1632 volkLoadDevice(device);
1633 if (vkQueueSubmit2 == nullptr || vkCmdBeginRendering == nullptr || vkCmdEndRendering == nullptr || vkCmdPipelineBarrier2 == nullptr) {
1634 std::cout << "vk: required dynamic rendering/synchronization function pointers are unavailable\n";
1635 vkDestroyDevice(device, nullptr);
1636 device = VK_NULL_HANDLE;
1637 return;
1638 }
1639 std::cout << "vk: retrieving graphics and present queues\n";
1640 vkGetDeviceQueue(device, graphics_queue_family, 0, &graphics_queue);
1641 vkGetDeviceQueue(device, present_queue_family, 0, &present_queue);
1642 std::cout << "mxvk: createLogicalDevice complete\n";
1643 }
uint32_t present_queue_family
Definition mxvk.hpp:487
VkQueue present_queue
Definition mxvk.hpp:489
uint32_t graphics_queue_family
Definition mxvk.hpp:486

◆ createShaderModule()

VkShaderModule mxvk::VK_Window::createShaderModule ( VkDevice device,
const std::vector< char > & spv_bytes )
staticprotectedinherited

Create a shader module from SPIR-V bytecode.

Parameters
deviceLogical Vulkan device used to create the module.
spv_bytesSPIR-V bytecode payload.
Returns
Created shader module handle.
Exceptions
mxvk::Exceptionwhen inputs are invalid or module creation fails.

Definition at line 145 of file mxvk.cpp.

145 {
146 return mxvk::create_shader_module(device, spv_bytes);
147 }
VkShaderModule create_shader_module(VkDevice device, const std::vector< char > &spv_bytes)
Create a shader module from SPIR-V bytecode.

◆ createSprite() [1/3]

VK_Sprite * mxvk::VK_Window::createSprite ( const std::string & pngPath,
const std::string & vertexShaderPath = "",
const std::string & fragmentShaderPath = "" )
inherited

Create a sprite from a PNG file and register it with this window.

Parameters
pngPathPath to the PNG file.
vertexShaderPathOptional custom vertex shader SPIR-V path.
fragmentShaderPathOptional custom fragment shader SPIR-V path.
Returns
Non-owning pointer to the created sprite.

Definition at line 3477 of file mxvk.cpp.

3477 {
3478 if (device == VK_NULL_HANDLE) {
3479 throw mxvk::Exception("Cannot create sprite before Vulkan device initialization");
3480 }
3481 if (swapchain == VK_NULL_HANDLE || command_pool == VK_NULL_HANDLE) {
3482 createDevice();
3483 }
3484 if (swapchain == VK_NULL_HANDLE || command_pool == VK_NULL_HANDLE) {
3485 throw mxvk::Exception("Cannot create sprite before swapchain and command resources are available");
3486 }
3487
3488 if (sprite_descriptor_set_layout == VK_NULL_HANDLE) {
3489 createSpriteDescriptorSetLayout();
3490 }
3491
3492 auto sprite = std::make_unique<VK_Sprite>(device, physical_device, graphics_queue, command_pool);
3493 sprite->setPipelineCache(pipeline_cache);
3494 sprite->setDescriptorSetLayout(sprite_descriptor_set_layout);
3495 sprite->setColorAttachmentFormat(swapchain_format);
3496 sprite->setDepthAttachmentFormat(depth_format);
3497
3498 if (!vertexShaderPath.empty()) {
3499 sprite->setVertexShaderPath(vertexShaderPath);
3500 }
3501
3502 sprite->loadSprite(pngPath, fragmentShaderPath);
3503
3504 VK_Sprite *const sprite_ptr = sprite.get();
3505 sprites.push_back(std::move(sprite));
3506 sprite_state_dirty = true;
3507 return sprite_ptr;
3508 }
VkDescriptorSetLayout sprite_descriptor_set_layout
Definition mxvk.hpp:542
VkFormat depth_format
Definition mxvk.hpp:494
void createDevice()
Create final device resources.
Definition mxvk.cpp:1645
VkPipelineCache pipeline_cache
Definition mxvk.hpp:490

◆ createSprite() [2/3]

VK_Sprite * mxvk::VK_Window::createSprite ( int width,
int height,
const std::string & vertexShaderPath = "",
const std::string & fragmentShaderPath = "" )
inherited

Create a blank sprite texture and register it with this window.

Parameters
widthTexture width in pixels.
heightTexture height in pixels.
vertexShaderPathOptional custom vertex shader SPIR-V path.
fragmentShaderPathOptional custom fragment shader SPIR-V path.
Returns
Non-owning pointer to the created sprite.

Definition at line 3546 of file mxvk.cpp.

3546 {
3547 if (width <= 0 || height <= 0) {
3548 throw mxvk::Exception("Sprite dimensions must be positive");
3549 }
3550 if (device == VK_NULL_HANDLE) {
3551 throw mxvk::Exception("Cannot create sprite before Vulkan device initialization");
3552 }
3553 if (swapchain == VK_NULL_HANDLE || command_pool == VK_NULL_HANDLE) {
3554 createDevice();
3555 }
3556 if (swapchain == VK_NULL_HANDLE || command_pool == VK_NULL_HANDLE) {
3557 throw mxvk::Exception("Cannot create sprite before swapchain and command resources are available");
3558 }
3559
3560 if (sprite_descriptor_set_layout == VK_NULL_HANDLE) {
3561 createSpriteDescriptorSetLayout();
3562 }
3563
3564 auto sprite = std::make_unique<VK_Sprite>(device, physical_device, graphics_queue, command_pool);
3565 sprite->setPipelineCache(pipeline_cache);
3566 sprite->setDescriptorSetLayout(sprite_descriptor_set_layout);
3567 sprite->setColorAttachmentFormat(swapchain_format);
3568 sprite->setDepthAttachmentFormat(depth_format);
3569
3570 sprite->createEmptySprite(width, height, vertexShaderPath, fragmentShaderPath);
3571
3572 VK_Sprite *const sprite_ptr = sprite.get();
3573 sprites.push_back(std::move(sprite));
3574 sprite_state_dirty = true;
3575 return sprite_ptr;
3576 }

◆ createSprite() [3/3]

VK_Sprite * mxvk::VK_Window::createSprite ( SDL_Surface * surface,
const std::string & vertexShaderPath = "",
const std::string & fragmentShaderPath = "" )
inherited

Create a sprite from an SDL surface and register it with this window.

Parameters
surfaceSource surface pointer.
vertexShaderPathOptional custom vertex shader SPIR-V path.
fragmentShaderPathOptional custom fragment shader SPIR-V path.
Returns
Non-owning pointer to the created sprite.

Definition at line 3510 of file mxvk.cpp.

3510 {
3511 if (surface == nullptr) {
3512 throw mxvk::Exception("Cannot create sprite from a null SDL surface");
3513 }
3514 if (device == VK_NULL_HANDLE) {
3515 throw mxvk::Exception("Cannot create sprite before Vulkan device initialization");
3516 }
3517 if (swapchain == VK_NULL_HANDLE || command_pool == VK_NULL_HANDLE) {
3518 createDevice();
3519 }
3520 if (swapchain == VK_NULL_HANDLE || command_pool == VK_NULL_HANDLE) {
3521 throw mxvk::Exception("Cannot create sprite before swapchain and command resources are available");
3522 }
3523
3524 if (sprite_descriptor_set_layout == VK_NULL_HANDLE) {
3525 createSpriteDescriptorSetLayout();
3526 }
3527
3528 auto sprite = std::make_unique<VK_Sprite>(device, physical_device, graphics_queue, command_pool);
3529 sprite->setPipelineCache(pipeline_cache);
3530 sprite->setDescriptorSetLayout(sprite_descriptor_set_layout);
3531 sprite->setColorAttachmentFormat(swapchain_format);
3532 sprite->setDepthAttachmentFormat(depth_format);
3533
3534 if (!vertexShaderPath.empty()) {
3535 sprite->setVertexShaderPath(vertexShaderPath);
3536 }
3537
3538 sprite->loadSprite(surface, fragmentShaderPath);
3539
3540 VK_Sprite *const sprite_ptr = sprite.get();
3541 sprites.push_back(std::move(sprite));
3542 sprite_state_dirty = true;
3543 return sprite_ptr;
3544 }
VkSurfaceKHR surface
Definition mxvk.hpp:483

◆ createSprite3D() [1/2]

VK_Sprite3D * mxvk::VK_Window::createSprite3D ( const std::string & pngPath,
const std::string & vertexShaderPath = "",
const std::string & fragmentShaderPath = "" )
inherited

Create a world-space billboard sprite from a PNG file.

Parameters
pngPathPath to the PNG file.
vertexShaderPathOptional custom vertex shader SPIR-V path.
fragmentShaderPathOptional custom fragment shader SPIR-V path.
Returns
Non-owning pointer to the created 3D sprite batch.

Definition at line 3578 of file mxvk.cpp.

3578 {
3579 if (device == VK_NULL_HANDLE) {
3580 throw mxvk::Exception("Cannot create 3D sprite before Vulkan device initialization");
3581 }
3582 if (swapchain == VK_NULL_HANDLE || command_pool == VK_NULL_HANDLE) {
3583 createDevice();
3584 }
3585 if (swapchain == VK_NULL_HANDLE || command_pool == VK_NULL_HANDLE) {
3586 throw mxvk::Exception("Cannot create 3D sprite before swapchain and command resources are available");
3587 }
3588
3589 const std::string vertPath = vertexShaderPath.empty()
3590 ? resolveRuntimeShaderPath("sprite3d.vert.spv", MXVK_SPRITE3D_SHADER_DIR)
3591 : vertexShaderPath;
3592 const std::string fragPath = fragmentShaderPath.empty()
3593 ? resolveRuntimeShaderPath("sprite3d.frag.spv", MXVK_SPRITE3D_SHADER_DIR)
3594 : fragmentShaderPath;
3595
3596 auto sprite = std::make_unique<VK_Sprite3D>();
3597 sprite->load(this, pngPath, vertPath, fragPath);
3598
3599 VK_Sprite3D *const sprite_ptr = sprite.get();
3600 sprites3d.push_back(std::move(sprite));
3601 return sprite_ptr;
3602 }
std::vector< std::unique_ptr< VK_Sprite3D > > sprites3d
Definition mxvk.hpp:541

◆ createSprite3D() [2/2]

VK_Sprite3D * mxvk::VK_Window::createSprite3D ( SDL_Surface * surface,
const std::string & vertexShaderPath = "",
const std::string & fragmentShaderPath = "" )
inherited

Create a world-space billboard sprite from an SDL surface.

Parameters
surfaceSource surface pointer.
vertexShaderPathOptional custom vertex shader SPIR-V path.
fragmentShaderPathOptional custom fragment shader SPIR-V path.
Returns
Non-owning pointer to the created 3D sprite batch.

Definition at line 3604 of file mxvk.cpp.

3604 {
3605 if (surface == nullptr) {
3606 throw mxvk::Exception("Cannot create 3D sprite from a null SDL surface");
3607 }
3608 if (device == VK_NULL_HANDLE) {
3609 throw mxvk::Exception("Cannot create 3D sprite before Vulkan device initialization");
3610 }
3611 if (swapchain == VK_NULL_HANDLE || command_pool == VK_NULL_HANDLE) {
3612 createDevice();
3613 }
3614 if (swapchain == VK_NULL_HANDLE || command_pool == VK_NULL_HANDLE) {
3615 throw mxvk::Exception("Cannot create 3D sprite before swapchain and command resources are available");
3616 }
3617
3618 const std::string vertPath = vertexShaderPath.empty()
3619 ? resolveRuntimeShaderPath("sprite3d.vert.spv", MXVK_SPRITE3D_SHADER_DIR)
3620 : vertexShaderPath;
3621 const std::string fragPath = fragmentShaderPath.empty()
3622 ? resolveRuntimeShaderPath("sprite3d.frag.spv", MXVK_SPRITE3D_SHADER_DIR)
3623 : fragmentShaderPath;
3624
3625 auto sprite = std::make_unique<VK_Sprite3D>();
3626 sprite->load(this, surface, vertPath, fragPath);
3627
3628 VK_Sprite3D *const sprite_ptr = sprite.get();
3629 sprites3d.push_back(std::move(sprite));
3630 return sprite_ptr;
3631 }

◆ debugCallback()

VkBool32 mxvk::VK_Window::debugCallback ( VkDebugUtilsMessageSeverityFlagBitsEXT severity,
VkDebugUtilsMessageTypeFlagsEXT type,
const VkDebugUtilsMessengerCallbackDataEXT * callback_data,
void * user_data )
staticinherited

Definition at line 44 of file mxvk.cpp.

44 {
45 const char *message = (callback_data != nullptr && callback_data->pMessage != nullptr)
46 ? callback_data->pMessage
47 : "Unknown Vulkan validation message";
48
49 if ((severity & VK_DEBUG_UTILS_MESSAGE_SEVERITY_ERROR_BIT_EXT) != 0U) {
50 std::cerr << std::format("vk validation error: {}\n", message);
51 } else if ((severity & VK_DEBUG_UTILS_MESSAGE_SEVERITY_WARNING_BIT_EXT) != 0U) {
52 std::cerr << std::format("vk validation warning: {}\n", message);
53 } else {
54 std::cout << std::format("vk validation: {}\n", message);
55 }
56 return VK_FALSE;
57 }

◆ detachPostProcessingShader()

void mxvk::VK_Window::detachPostProcessingShader ( )
inherited

Detach the current post-processing shader and return to direct swapchain rendering.

Definition at line 1198 of file mxvk.cpp.

1198 {
1199 if (device != VK_NULL_HANDLE && !post_process_sprites.empty()) {
1200 vkDeviceWaitIdle(device);
1201 }
1202
1203 post_process_sprite = nullptr;
1204 destroyPostProcessTargets();
1206
1207 if (!owned_post_process_sprites.empty()) {
1208 const std::vector<VK_Sprite *> sprites_to_remove = owned_post_process_sprites;
1209 sprites.erase(
1210 std::remove_if(
1211 sprites.begin(),
1212 sprites.end(),
1213 [&sprites_to_remove](const std::unique_ptr<VK_Sprite> &sprite) {
1214 return std::ranges::find(sprites_to_remove, sprite.get()) != sprites_to_remove.end();
1215 }),
1216 sprites.end());
1217 sprite_state_dirty = true;
1218 }
1219 owned_post_process_sprite = nullptr;
1221 post_process_sprites.clear();
1225 }

◆ enablePostProcessing()

void mxvk::VK_Window::enablePostProcessing ( VK_Sprite * sprite)
inherited

Definition at line 1271 of file mxvk.cpp.

1271 {
1272 if (owned_post_process_sprite != nullptr && sprite != owned_post_process_sprite) {
1273 std::vector<VK_Sprite *> sprites_to_remove = owned_post_process_sprites;
1274 if (sprites_to_remove.empty()) {
1275 sprites_to_remove.push_back(owned_post_process_sprite);
1276 }
1277 sprites.erase(
1278 std::remove_if(
1279 sprites.begin(),
1280 sprites.end(),
1281 [&sprites_to_remove](const std::unique_ptr<VK_Sprite> &existing_sprite) {
1282 return std::ranges::find(sprites_to_remove, existing_sprite.get()) != sprites_to_remove.end();
1283 }),
1284 sprites.end());
1285 owned_post_process_sprite = nullptr;
1287 sprite_state_dirty = true;
1288 }
1289 post_process_sprite = sprite;
1290 post_process_sprites = sprite == nullptr ? std::vector<VK_Sprite *>{} : std::vector<VK_Sprite *>{sprite};
1294 if (device != VK_NULL_HANDLE && !swapchain_images.empty()) {
1295 createPostProcessTargets();
1296 }
1297 }

◆ ensureRenderResources()

bool mxvk::VK_Window::ensureRenderResources ( )
inherited

Ensure deferred render resources are initialized.

Returns
true when swapchain, command pool, and sync objects are ready.

Definition at line 1406 of file mxvk.cpp.

1406 {
1407 const auto sync_ready = [this]() {
1408 return std::ranges::all_of(image_available.begin(), image_available.end(), [](VkSemaphore semaphore) { return semaphore != VK_NULL_HANDLE; }) &&
1409 render_finished.size() == swapchain_images.size() &&
1410 std::ranges::all_of(render_finished.begin(), render_finished.end(), [](VkSemaphore semaphore) { return semaphore != VK_NULL_HANDLE; }) &&
1411 std::ranges::all_of(in_flight_fences.begin(), in_flight_fences.end(), [](VkFence fence) { return fence != VK_NULL_HANDLE; });
1412 };
1413
1414 if (device == VK_NULL_HANDLE) {
1415 return false;
1416 }
1417
1418 if (swapchain == VK_NULL_HANDLE || command_pool == VK_NULL_HANDLE || command_buffers.empty() || !sync_ready() ||
1419 image_fences.size() != swapchain_images.size()) {
1420 createDevice();
1421 }
1422
1423 return (swapchain != VK_NULL_HANDLE && command_pool != VK_NULL_HANDLE && !command_buffers.empty() && sync_ready() &&
1424 image_fences.size() == swapchain_images.size());
1425 }

◆ event()

void example::ConsoleDemoWindow::event ( SDL_Event & e)
inlineoverridevirtual

Handle one SDL event.

Parameters
eSDL event to process.

Reimplemented from mxvk::VK_Window.

Definition at line 63 of file main.cpp.

63 {
64 console.handleEvent(e);
65
66 if (e.type == SDL_EVENT_KEY_DOWN && e.key.key == SDLK_ESCAPE && !console.isVisible()) {
67 exit();
68 }
69 }

◆ exit()

void mxvk::VK_Window::exit ( )
protectedinherited

Request loop termination.

Definition at line 1126 of file mxvk.cpp.

1126 {
1127 active = false;
1128 }

◆ getCommandPool()

VkCommandPool mxvk::VK_Window::getCommandPool ( ) const
inlinenodiscardnoexceptinherited

Get the command pool used for graphics/upload work.

Definition at line 177 of file mxvk.hpp.

177{ return command_pool; }

◆ getDepthFormat()

VkFormat mxvk::VK_Window::getDepthFormat ( ) const
inlinenodiscardnoexceptinherited

Get the depth format used for dynamic rendering attachments.

Definition at line 189 of file mxvk.hpp.

189{ return depth_format; }

◆ getDevice()

VkDevice mxvk::VK_Window::getDevice ( ) const
inlinenodiscardnoexceptinherited

Get the Vulkan logical device handle.

Definition at line 168 of file mxvk.hpp.

168{ return device; }

◆ getGraphicsQueue()

VkQueue mxvk::VK_Window::getGraphicsQueue ( ) const
inlinenodiscardnoexceptinherited

Get the graphics queue handle.

Definition at line 174 of file mxvk.hpp.

174{ return graphics_queue; }

◆ getPhysicalDevice()

VkPhysicalDevice mxvk::VK_Window::getPhysicalDevice ( ) const
inlinenodiscardnoexceptinherited

Get the Vulkan physical device handle.

Definition at line 171 of file mxvk.hpp.

171{ return physical_device; }

◆ getPipelineCache()

VkPipelineCache mxvk::VK_Window::getPipelineCache ( ) const
inlinenodiscardnoexceptinherited

Get the persistent Vulkan pipeline cache used by framework pipelines.

Definition at line 180 of file mxvk.hpp.

180{ return pipeline_cache; }

◆ getSDLWindow()

SDL_Window * mxvk::VK_Window::getSDLWindow ( ) const
inlinenodiscardnoexceptinherited

Get the underlying SDL window handle.

Definition at line 165 of file mxvk.hpp.

165{ return window.get(); }
std::unique_ptr< SDL_Window, SDLWindowDeleter > window
Definition mxvk.hpp:480

◆ getSwapchainExtent()

VkExtent2D mxvk::VK_Window::getSwapchainExtent ( ) const
inlinenodiscardnoexceptinherited

Get the current swapchain extent.

Definition at line 186 of file mxvk.hpp.

186{ return swapchain_extent; }

◆ getSwapchainFormat()

VkFormat mxvk::VK_Window::getSwapchainFormat ( ) const
inlinenodiscardnoexceptinherited

Get the swapchain color format.

Definition at line 183 of file mxvk.hpp.

183{ return swapchain_format; }

◆ getSwapchainImageCount()

size_t mxvk::VK_Window::getSwapchainImageCount ( ) const
inlinenodiscardnoexceptinherited

Get the number of swapchain images currently allocated.

Definition at line 192 of file mxvk.hpp.

192{ return swapchain_images.size(); }

◆ getTextDimensions() [1/3]

bool mxvk::VK_Window::getTextDimensions ( const std::string & text,
int & width,
int & height )
nodiscardinherited

Measure text dimensions in pixels.

Parameters
textText string to measure.
widthOutput width in pixels.
heightOutput height in pixels.
Returns
true when measurement succeeded.

Definition at line 3069 of file mxvk.cpp.

3069 {
3070 if (!font_configured) {
3071 throw mxvk::Exception("getTextDimensions requires setFont() to be called first");
3072 }
3073
3074 if (!text_renderer) {
3075 ensureTextRenderer();
3076 }
3077 if (!text_renderer) {
3078 width = 0;
3079 height = 0;
3080 return false;
3081 }
3082 return text_renderer->getTextDimensions(text, width, height);
3083 }
bool font_configured
Definition mxvk.hpp:567

◆ getTextDimensions() [2/3]

bool mxvk::VK_Window::getTextDimensions ( const std::string & text,
int & width,
int & height,
const Font & font )
nodiscardinherited

Definition at line 3101 of file mxvk.cpp.

3101 {
3102 if (!font) {
3103 width = 0;
3104 height = 0;
3105 return false;
3106 }
3107
3108 ensureTextRenderer(font.path(), font.size());
3109 if (!text_renderer) {
3110 width = 0;
3111 height = 0;
3112 return false;
3113 }
3114 return text_renderer->getTextDimensions(text, width, height, font);
3115 }

◆ getTextDimensions() [3/3]

bool mxvk::VK_Window::getTextDimensions ( const std::string & text,
int & width,
int & height,
TTF_Font * font )
nodiscardinherited

Definition at line 3085 of file mxvk.cpp.

3085 {
3086 if (font == nullptr) {
3087 width = 0;
3088 height = 0;
3089 return false;
3090 }
3091
3092 ensureTextRenderer(resolveDefaultFontPath(), font_size);
3093 if (!text_renderer) {
3094 width = 0;
3095 height = 0;
3096 return false;
3097 }
3098 return text_renderer->getTextDimensions(text, width, height, font);
3099 }

◆ initVulkan()

bool mxvk::VK_Window::initVulkan ( bool validiation)
virtualinherited

Initialize Vulkan state.

Parameters
validiationEnables validation-related behavior when true.
Returns
true on success, false otherwise.

Definition at line 318 of file mxvk.cpp.

318 {
319 std::cout << std::format("mxvk: entering initVulkan (validation={})\n", validiation);
320 validation_enabled = validiation;
321
322 if (window == nullptr) {
323 std::cerr << "mxvk: Cannot initialize Vulkan without an SDL window\n";
324 return false;
325 }
326
327 if (instance != VK_NULL_HANDLE) {
328 std::cout << "vk: instance already initialized; skipping initVulkan\n";
329 return true;
330 }
331
332 std::cout << "vk: initializing volk loader\n";
333 if (volkInitialize() != VK_SUCCESS) {
334 std::cerr << "mxvk: Failed to initialize volk\n";
335 return false;
336 }
337
338 unsigned int extension_count = 0;
339 std::cout << "SDL3: querying Vulkan instance extensions required by SDL\n";
340 const char *const *extensions = SDL_Vulkan_GetInstanceExtensions(&extension_count);
341 if (extensions == nullptr || extension_count == 0U) {
342 std::cerr << std::format("mxvk: Failed to get Vulkan instance extensions: {}\n", SDL_GetError());
343 return false;
344 }
345 std::cout << std::format("vk: SDL provided {} required instance extension(s)\n", extension_count);
346
347 std::vector<const char *> enabled_extensions(extensions, extensions + extension_count);
348
349#if defined(MXVK_USE_MOLTENVK)
350 const auto append_instance_extension_if_missing = [&enabled_extensions](const char *extension_name) {
351 const bool exists = std::ranges::any_of(
352 enabled_extensions,
353 [extension_name](const char *existing) { return std::strcmp(existing, extension_name) == 0; });
354 if (!exists) {
355 enabled_extensions.push_back(extension_name);
356 }
357 };
358 append_instance_extension_if_missing(VK_KHR_GET_PHYSICAL_DEVICE_PROPERTIES_2_EXTENSION_NAME);
359 append_instance_extension_if_missing(VK_KHR_PORTABILITY_ENUMERATION_EXTENSION_NAME);
360#endif
361
362 std::vector<const char *> enabled_layers{};
363 VkDebugUtilsMessengerCreateInfoEXT debug_create_info{};
364 if (validation_enabled) {
365 if (!hasValidationLayerSupport()) {
367 std::cerr << std::format(
368 "mxvk: validation layer '{}' is not available; continuing without validation\n",
369 validation_layer_name);
370 }
371 validation_enabled = false;
372 } else {
373 enabled_layers.push_back(validation_layer_name);
374 enabled_extensions.push_back(VK_EXT_DEBUG_UTILS_EXTENSION_NAME);
375 const std::optional<VkDebugUtilsMessengerCreateInfoEXT> maybe_debug_create_info =
376 makeDebugMessengerCreateInfo();
377 if (!maybe_debug_create_info.has_value()) {
378 std::cerr << "mxvk: failed to construct debug messenger create info\n";
379 validation_enabled = false;
380 enabled_layers.clear();
381 } else {
382 debug_create_info = maybe_debug_create_info.value();
383 }
384 }
385 }
386
387 VkApplicationInfo app_info{};
388 app_info.sType = VK_STRUCTURE_TYPE_APPLICATION_INFO;
389 app_info.pApplicationName = "mxvk";
390 app_info.applicationVersion = VK_MAKE_VERSION(0, 1, 0);
391 app_info.pEngineName = "mxvk";
392 app_info.engineVersion = VK_MAKE_VERSION(MXVK_VERSION_CODE_MAJOR, MXVK_VERSION_CODE_MINOR, MXVK_VERSION_CODE_PATCH);
393 app_info.apiVersion = VK_API_VERSION_1_4;
394
395 VkInstanceCreateInfo create_info{};
396 create_info.sType = VK_STRUCTURE_TYPE_INSTANCE_CREATE_INFO;
397 create_info.pApplicationInfo = &app_info;
398 create_info.enabledExtensionCount = extension_count;
399 create_info.ppEnabledExtensionNames = enabled_extensions.data();
400 create_info.enabledLayerCount = static_cast<uint32_t>(enabled_layers.size());
401 create_info.ppEnabledLayerNames = enabled_layers.empty() ? nullptr : enabled_layers.data();
402 create_info.pNext = validation_enabled ? &debug_create_info : nullptr;
403
404 create_info.enabledExtensionCount = static_cast<uint32_t>(enabled_extensions.size());
405#if defined(MXVK_USE_MOLTENVK)
406 create_info.flags |= VK_INSTANCE_CREATE_ENUMERATE_PORTABILITY_BIT_KHR;
407#endif
408
409 std::cout << "vk: creating Vulkan instance\n";
410 if (vkCreateInstance(&create_info, nullptr, &instance) != VK_SUCCESS) {
411 std::cerr << "mxvk: Failed to create Vulkan instance\n";
412 instance = VK_NULL_HANDLE;
413 return false;
414 }
415
416 std::cout << "vk: loading Vulkan instance function pointers via volk\n";
417 volkLoadInstance(instance);
418
419 setupDebugMessenger();
420
421 uint32_t instanceVersion = 0;
422 if (vkEnumerateInstanceVersion != nullptr) {
423 vkEnumerateInstanceVersion(&instanceVersion);
424 }
425 std::cout << "vk: Vulkan instance version: "
426 << VK_VERSION_MAJOR(instanceVersion) << "."
427 << VK_VERSION_MINOR(instanceVersion) << "."
428 << VK_VERSION_PATCH(instanceVersion) << "\n";
429 std::cout << "mxvk: engine version: "
430 << MXVK_VERSION_CODE_MAJOR << "."
431 << MXVK_VERSION_CODE_MINOR << "."
432 << MXVK_VERSION_CODE_PATCH << "\n";
433
434 std::cout << "SDL3: creating Vulkan presentation surface from SDL window\n";
435 if (!SDL_Vulkan_CreateSurface(window.get(), instance, nullptr, &surface)) {
436 std::cerr << std::format("mxvk: Failed to create Vulkan surface: {}\n", SDL_GetError());
437 cleanupDebugMessenger();
438 vkDestroyInstance(instance, nullptr);
439 instance = VK_NULL_HANDLE;
440 surface = VK_NULL_HANDLE;
441 return false;
442 }
443
444 std::cout << "mxvk: selecting suitable physical device\n";
445 pickDevice();
446 if (physical_device == VK_NULL_HANDLE) {
447 std::cerr << "mxvk: Failed to find a Vulkan physical device with present support\n";
448 return false;
449 }
450
451 std::cout << "mxvk: creating logical device and queues\n";
453 if (device == VK_NULL_HANDLE) {
454 std::cerr << "mxvk: Failed to create Vulkan logical device\n";
455 return false;
456 }
457 createPipelineCache();
458
459 std::cout << "mxvk: deferring swapchain/render/sync resource creation until first frame\n";
460
461 std::cout << "mxvk: initVulkan complete\n";
462 return true;
463 }
void pickDevice()
Pick a suitable Vulkan physical device.
Definition mxvk.cpp:1427
void createLogicalDevice()
Create a logical Vulkan device from the selected physical device.
Definition mxvk.cpp:1514
VkInstance instance
Definition mxvk.hpp:481
bool validation_enabled
Definition mxvk.hpp:531

◆ initWindow()

bool mxvk::VK_Window::initWindow ( const std::string & title,
int width,
int height,
SDL_WindowFlags flags )
protectedinherited

Initialize SDL window resources.

Parameters
titleWindow title string.
widthWindow width in pixels.
heightWindow height in pixels.
flagsAdditional SDL window creation flags.
Returns
true on success, false otherwise.

Definition at line 1059 of file mxvk.cpp.

1059 {
1060 std::cout << std::format("mxvk: entering initWindow (title='{}', width={}, height={})\n", title, width, height);
1061 if (width <= 0 || height <= 0) {
1062 std::cerr << "mxvk: Window dimensions must be positive\n";
1063 return false;
1064 }
1065
1066 if (!sdl_initialized) {
1067 constexpr SDL_InitFlags sdlInitFlags = SDL_INIT_VIDEO | SDL_INIT_GAMEPAD;
1068 std::cout << "SDL3: initializing video/gamepad subsystems\n";
1069 if (!SDL_Init(sdlInitFlags)) {
1070 std::cerr << "mxvk: Error on SDL init: " << SDL_GetError() << "\n";
1071 return false;
1072 }
1073 sdl_initialized = true;
1074 SDL_SetGamepadEventsEnabled(true);
1075 SDL_SetJoystickEventsEnabled(true);
1076 std::cout << "SDL3: video/gamepad subsystems initialized\n";
1077 }
1078
1079 const bool fullscreen = (flags & SDL_WINDOW_FULLSCREEN) != 0;
1080 const SDL_DisplayMode *fullscreen_mode = nullptr;
1081 if (fullscreen) {
1082 const SDL_DisplayID display = SDL_GetPrimaryDisplay();
1083 fullscreen_mode = display != 0 ? SDL_GetCurrentDisplayMode(display) : nullptr;
1084 if (fullscreen_mode != nullptr && fullscreen_mode->w > 0 && fullscreen_mode->h > 0) {
1085 width = fullscreen_mode->w;
1086 height = fullscreen_mode->h;
1087 std::cout << std::format("SDL3: using current display mode for fullscreen window: {}x{}\n", width, height);
1088 }
1089 }
1090
1091 std::cout << "SDL3: creating SDL window with Vulkan capability\n";
1092 SDL_Window *raw_window = SDL_CreateWindow(title.c_str(), width, height, flags);
1093 if (raw_window == nullptr) {
1094 std::cerr << std::format("Error creating window: {}\n", SDL_GetError());
1095 std::cout << "SDL3: rolling back SDL video/gamepad subsystems after window creation failure\n";
1096 SDL_QuitSubSystem(SDL_INIT_VIDEO | SDL_INIT_GAMEPAD);
1097 sdl_initialized = false;
1098 return false;
1099 }
1100
1101 window.reset(raw_window);
1102 if (fullscreen && fullscreen_mode != nullptr && !SDL_SetWindowFullscreenMode(window.get(), fullscreen_mode)) {
1103 std::cerr << std::format("mxvk: SDL_SetWindowFullscreenMode failed: {}\n", SDL_GetError());
1104 }
1105 std::cout << "SDL3: showing created window\n";
1106 SDL_ShowWindow(window.get());
1107 if (fullscreen && !SDL_SyncWindow(window.get())) {
1108 std::cerr << std::format("mxvk: SDL_SyncWindow failed after fullscreen show: {}\n", SDL_GetError());
1109 }
1110 if (fullscreen) {
1111 for (int attempt = 0; attempt < 50; ++attempt) {
1112 int pixel_width = 0;
1113 int pixel_height = 0;
1114 SDL_GetWindowSizeInPixels(window.get(), &pixel_width, &pixel_height);
1115 if (pixel_width == width && pixel_height == height) {
1116 break;
1117 }
1118 SDL_PumpEvents();
1119 SDL_Delay(10);
1120 }
1121 }
1122 std::cout << "mxvk: initWindow complete\n";
1123 return true;
1124 }
bool sdl_initialized
Definition mxvk.hpp:515

◆ loadSpv()

std::vector< char > mxvk::VK_Window::loadSpv ( const std::string & path)
staticprotectedinherited

Load a SPIR-V file from disk.

Parameters
pathAbsolute or relative path to a .spv file.
Returns
Raw bytes loaded from file.
Exceptions
mxvk::Exceptionwhen the file cannot be opened, is empty, or is not 4-byte aligned.

Definition at line 141 of file mxvk.cpp.

141 {
142 return mxvk::load_spv(path);
143 }
std::vector< char > load_spv(const std::string &path)
Load a SPIR-V file from disk.

◆ loop()

void mxvk::VK_Window::loop ( )
inherited

Run the main event/render loop.

Definition at line 600 of file mxvk.cpp.

600 {
601 SDL_Event e;
602 active = true;
603 while (active) {
604 while (SDL_PollEvent(&e)) {
605 switch (e.type) {
606 case SDL_EVENT_QUIT:
607 active = false;
608 break;
609 case SDL_EVENT_WINDOW_PIXEL_SIZE_CHANGED:
610 if (window != nullptr) {
611 framebuffer_resized = true;
612 last_resize_event_ms = SDL_GetTicks();
614 }
615 break;
616 case SDL_EVENT_KEY_DOWN:
617 if ((e.key.key == SDLK_F12 || e.key.scancode == SDL_SCANCODE_F12) && !e.key.repeat) {
618 toggleFpsCounter();
619 }
620 if (screenshot_enabled &&
621 (e.key.key == SDLK_F10 || e.key.scancode == SDL_SCANCODE_F10) &&
622 !e.key.repeat) {
623 try {
624 saveScreenshot();
625 } catch (const mxvk::Exception &ex) {
626 std::cerr << std::format("mxvk: screenshot failed: {}\n", ex.text());
627 } catch (const std::exception &ex) {
628 std::cerr << std::format("mxvk: screenshot failed: {}\n", ex.what());
629 }
630 continue;
631 }
632 break;
633 default:
634 break;
635 }
636 event(e);
637 }
639 const uint64_t now_ms = SDL_GetTicks();
643 proc();
644 render();
645 SDL_Delay(1);
646 continue;
647 }
648 recreateSwapchain();
649 }
650 proc();
651 render();
652 maybeTrimMemory();
653 }
654 }
std::string text() const
bool force_swapchain_recreate
Definition mxvk.hpp:534
virtual void event(SDL_Event &e)
Handle one SDL event.
Definition mxvk.cpp:582
static constexpr uint64_t resize_settle_delay_ms
Definition mxvk.hpp:536
bool screenshot_enabled
Definition mxvk.hpp:517
bool framebuffer_resized
Definition mxvk.hpp:533
virtual void render()
Render one frame.
Definition mxvk.cpp:778
virtual void proc()
Execute one processing/update step.
Definition mxvk.cpp:1038
uint64_t last_resize_event_ms
Definition mxvk.hpp:535

◆ onConfigureDepthStencilAttachments()

void mxvk::VK_Window::onConfigureDepthStencilAttachments ( VkRenderingAttachmentInfo & depth_attachment,
VkRenderingAttachmentInfo & stencil_attachment,
uint32_t image_index )
protectedvirtualinherited

Allow derived classes to customize depth/stencil attachments for the main dynamic rendering pass.

Override this when custom rendering needs hardware stencil testing or a custom depth/stencil image. The callback is invoked immediately before vkCmdBeginRendering.

Reimplemented in example::StencilWindow.

Definition at line 1138 of file mxvk.cpp.

1140 {}

◆ onPrepareFrameRendering()

void mxvk::VK_Window::onPrepareFrameRendering ( VkCommandBuffer cmd,
uint32_t image_index )
virtualinherited

Record resource transitions that must happen before dynamic rendering begins.

This callback is invoked after the command buffer begins recording and before vkCmdBeginRendering. Override this for resources that need per-frame image transitions or uploads before they are sampled by custom rendering.

Parameters
cmdActive command buffer in recording state, outside a rendering scope.
image_indexCurrent swapchain image index.

Reimplemented in defender::DefenderWindow, example::StencilWindow, and example::WaterWindow.

Definition at line 1134 of file mxvk.cpp.

1134{}

◆ onRecordCustomRendering()

void mxvk::VK_Window::onRecordCustomRendering ( VkCommandBuffer cmd,
uint32_t image_index )
protectedvirtualinherited

◆ onSwapchainAboutToRecreate()

void mxvk::VK_Window::onSwapchainAboutToRecreate ( )
protectedvirtualinherited

Called right before swapchain-dependent resources are recreated.

Derived classes can release swapchain-dependent resources here.

Reimplemented in anonymous_namespace{pong.cpp}::PongWindow, example::ExampleWindow, example::FractalWindow, example::PlanetWindow, example::StaticWindow, example::StencilWindow, and example::WaterWindow.

Definition at line 1130 of file mxvk.cpp.

1130{}

◆ onSwapchainRecreated()

◆ pickDevice()

void mxvk::VK_Window::pickDevice ( )
protectedinherited

Pick a suitable Vulkan physical device.

Definition at line 1427 of file mxvk.cpp.

1427 {
1428 std::cout << "mxvk: entering pickDevice\n";
1429 if (instance == VK_NULL_HANDLE || surface == VK_NULL_HANDLE) {
1430 std::cout << "vk: cannot pick device because instance or surface is missing\n";
1431 return;
1432 }
1433
1434 uint32_t device_count = 0;
1435 std::cout << "vk: enumerating physical devices\n";
1436 vkEnumeratePhysicalDevices(instance, &device_count, nullptr);
1437 if (device_count == 0U) {
1438 std::cout << "vk: no physical devices found\n";
1439 return;
1440 }
1441 std::cout << std::format("vk: found {} physical device(s)\n", device_count);
1442
1443 std::vector<VkPhysicalDevice> devices(device_count);
1444 vkEnumeratePhysicalDevices(instance, &device_count, devices.data());
1445
1446 for (const VkPhysicalDevice candidate : devices) {
1447 std::cout << "vk: evaluating candidate physical device\n";
1448 uint32_t queue_family_count = 0;
1449 vkGetPhysicalDeviceQueueFamilyProperties(candidate, &queue_family_count, nullptr);
1450 std::vector<VkQueueFamilyProperties> queue_families(queue_family_count);
1451 vkGetPhysicalDeviceQueueFamilyProperties(candidate, &queue_family_count, queue_families.data());
1452
1453 uint32_t candidate_graphics = invalid_queue_index;
1454 uint32_t candidate_present = invalid_queue_index;
1455
1456 for (uint32_t i = 0; i < queue_family_count; ++i) {
1457 if ((queue_families[i].queueFlags & VK_QUEUE_GRAPHICS_BIT) != 0U) {
1458 candidate_graphics = i;
1459 }
1460
1461 VkBool32 present_support = VK_FALSE;
1462 vkGetPhysicalDeviceSurfaceSupportKHR(candidate, i, surface, &present_support);
1463 if (present_support == VK_TRUE) {
1464 candidate_present = i;
1465 }
1466 }
1467
1468 if (candidate_graphics == invalid_queue_index || candidate_present == invalid_queue_index) {
1469 std::cout << "vk: candidate rejected due to missing graphics/present queue support\n";
1470 continue;
1471 }
1472
1473 const SwapchainSupport swapchain_support = querySwapchainSupport(candidate, surface);
1474 if (swapchain_support.formats.empty() || swapchain_support.present_modes.empty()) {
1475 std::cout << "vk: candidate rejected due to incomplete swapchain support\n";
1476 continue;
1477 }
1478
1479 VkPhysicalDeviceProperties properties{};
1480 vkGetPhysicalDeviceProperties(candidate, &properties);
1481
1482 const char *device_type = "unknown";
1483 switch (properties.deviceType) {
1484 case VK_PHYSICAL_DEVICE_TYPE_INTEGRATED_GPU:
1485 device_type = "integrated";
1486 break;
1487 case VK_PHYSICAL_DEVICE_TYPE_DISCRETE_GPU:
1488 device_type = "discrete";
1489 break;
1490 case VK_PHYSICAL_DEVICE_TYPE_VIRTUAL_GPU:
1491 device_type = "virtual";
1492 break;
1493 case VK_PHYSICAL_DEVICE_TYPE_CPU:
1494 device_type = "cpu";
1495 break;
1496 default:
1497 break;
1498 }
1499
1500 physical_device = candidate;
1501 graphics_queue_family = candidate_graphics;
1502 present_queue_family = candidate_present;
1503 std::cout << std::format(
1504 "vk: selected GPU='{}' type={} vendor=0x{:04x} device=0x{:04x}\n",
1505 properties.deviceName,
1506 device_type,
1507 properties.vendorID,
1508 properties.deviceID);
1509 return;
1510 }
1511
1512 std::cout << "vk: no suitable physical device selected\n";
1513 }

◆ printText() [1/3]

void mxvk::VK_Window::printText ( const std::string & text,
int x,
int y,
const SDL_Color & col )
inherited

Queue a text string for rendering during the current frame.

Parameters
textUTF-8 text.
xPixel X coordinate.
yPixel Y coordinate.
colText color.

Definition at line 3018 of file mxvk.cpp.

3018 {
3019 if (text.empty()) {
3020 return;
3021 }
3022
3023 if (!font_configured) {
3024 throw mxvk::Exception("printText requires setFont() to be called first");
3025 }
3026
3027 ensureTextRenderer();
3028 text_renderer->printTextG_Solid(text, x, y, col);
3029 }

◆ printText() [2/3]

void mxvk::VK_Window::printText ( const std::string & text,
int x,
int y,
const SDL_Color & col,
const Font & font )
inherited

Definition at line 3047 of file mxvk.cpp.

3047 {
3048 if (text.empty()) {
3049 return;
3050 }
3051
3052 if (!font) {
3053 throw mxvk::Exception("printText requires a valid mxvk::Font");
3054 }
3055
3056 ensureTextRenderer(font.path(), font.size());
3057 if (!text_renderer) {
3058 throw mxvk::Exception("printText with an explicit font could not initialize the text renderer");
3059 }
3060 text_renderer->printTextG_Solid(text, x, y, col, font);
3061 }

◆ printText() [3/3]

void mxvk::VK_Window::printText ( const std::string & text,
int x,
int y,
const SDL_Color & col,
TTF_Font * font )
inherited

Definition at line 3031 of file mxvk.cpp.

3031 {
3032 if (text.empty()) {
3033 return;
3034 }
3035
3036 if (font == nullptr) {
3037 throw mxvk::Exception("printText requires a non-null TTF_Font");
3038 }
3039
3040 ensureTextRenderer(resolveDefaultFontPath(), font_size);
3041 if (!text_renderer) {
3042 throw mxvk::Exception("printText with an explicit font could not initialize the text renderer");
3043 }
3044 text_renderer->printTextG_Solid(text, x, y, col, font);
3045 }

◆ proc()

void example::ConsoleDemoWindow::proc ( )
inlineoverridevirtual

Execute one processing/update step.

Reimplemented from mxvk::VK_Window.

Definition at line 71 of file main.cpp.

71 {
72 if (background != nullptr) {
73 const VkExtent2D extent = getSwapchainExtent();
74 const int target_w = static_cast<int>(extent.width);
75 const int target_h = static_cast<int>(extent.height);
76 if (target_w > 0 && target_h > 0) {
77 const float elapsed = std::chrono::duration<float>(std::chrono::steady_clock::now() - start).count();
78 background->setShaderParams(elapsed, 0.0f, 0.0f, 0.0f);
79 background->drawSpriteRect(0, 0, target_w, target_h);
80 }
81 }
82
83 if (!console.isVisible()) {
84 printText("MXVK Console Demo", 14, 12, SDL_Color{255, 255, 255, 255});
85 printText("Press F3 to toggle console. Press ESC to quit when console is hidden.",
86 14,
87 38,
88 SDL_Color{180, 180, 220, 255});
89 }
90 console.draw();
91 }
VkExtent2D getSwapchainExtent() const noexcept
Get the current swapchain extent.
Definition mxvk.hpp:186
void printText(const std::string &text, int x, int y, const SDL_Color &col)
Queue a text string for rendering during the current frame.
Definition mxvk.cpp:3018

◆ release()

void mxvk::VK_Window::release ( )
inherited

Release Vulkan and SDL resources.

Safe to call multiple times.

Definition at line 218 of file mxvk.cpp.

218 {
219 std::cout << "mxvk: starting resource teardown\n";
220 stopScreenshotWorker();
221
222 if (device != VK_NULL_HANDLE) {
223 std::cout << "vk: waiting for device idle before teardown\n";
224 vkDeviceWaitIdle(device);
225 }
226
227 post_process_sprite = nullptr;
229 post_process_sprites.clear();
234
235 if (!sprites.empty()) {
236 std::cout << std::format("vk: releasing {} sprite(s)\n", sprites.size());
237 sprites.clear();
238 }
239 if (!sprites3d.empty()) {
240 std::cout << std::format("vk: releasing {} 3D sprite batch(es)\n", sprites3d.size());
241 sprites3d.clear();
242 }
243 sprite_state_dirty = false;
244 destroySpritePipeline();
245 if (sprite_descriptor_set_layout != VK_NULL_HANDLE && device != VK_NULL_HANDLE) {
246 vkDestroyDescriptorSetLayout(device, sprite_descriptor_set_layout, nullptr);
247 sprite_descriptor_set_layout = VK_NULL_HANDLE;
248 }
249
250 if (text_renderer) {
251 text_renderer.reset();
252 }
253 text_state_dirty = false;
254 destroyTextPipeline();
255 if (text_descriptor_set_layout != VK_NULL_HANDLE && device != VK_NULL_HANDLE) {
256 vkDestroyDescriptorSetLayout(device, text_descriptor_set_layout, nullptr);
257 text_descriptor_set_layout = VK_NULL_HANDLE;
258 }
259
260 cleanupSyncObjects();
261
262 if (!retired_swapchains.empty()) {
263 for (VkSwapchainKHR retired : retired_swapchains) {
264 vkDestroySwapchainKHR(device, retired, nullptr);
265 }
266 retired_swapchains.clear();
267 }
268 std::cout << "vk: tearing down swapchain-dependent resources\n";
269 cleanupSwapchain(true);
270
271 if (command_pool != VK_NULL_HANDLE && device != VK_NULL_HANDLE) {
272 std::cout << "vk: destroying command pool\n";
273 vkDestroyCommandPool(device, command_pool, nullptr);
274 command_pool = VK_NULL_HANDLE;
275 }
276
277 if (device != VK_NULL_HANDLE) {
278 savePipelineCache();
279 destroyPipelineCache();
280 std::cout << "vk: destroying logical device\n";
281 vkDestroyDevice(device, nullptr);
282 device = VK_NULL_HANDLE;
283 graphics_queue = VK_NULL_HANDLE;
284 present_queue = VK_NULL_HANDLE;
285 }
286
287 graphics_queue_family = invalid_queue_index;
288 present_queue_family = invalid_queue_index;
289 physical_device = VK_NULL_HANDLE;
290
291 if (surface != VK_NULL_HANDLE && instance != VK_NULL_HANDLE) {
292 std::cout << "vk: destroying presentation surface\n";
293 vkDestroySurfaceKHR(instance, surface, nullptr);
294 surface = VK_NULL_HANDLE;
295 }
296
297 cleanupDebugMessenger();
298
299 if (instance != VK_NULL_HANDLE) {
300 std::cout << "vk: destroying Vulkan instance\n";
301 vkDestroyInstance(instance, nullptr);
302 instance = VK_NULL_HANDLE;
303 }
304
305 std::cout << "SDL3: destroying SDL window handle\n";
306 window.reset();
307
308 if (sdl_initialized) {
309 std::cout << "SDL3: shutting down SDL video/gamepad subsystems\n";
310 SDL_QuitSubSystem(SDL_INIT_VIDEO | SDL_INIT_GAMEPAD);
311 sdl_initialized = false;
312 }
313
314 active = false;
315 std::cout << "mxvk: resource teardown complete\n";
316 }
VkDescriptorSetLayout text_descriptor_set_layout
Definition mxvk.hpp:563
bool text_state_dirty
Definition mxvk.hpp:566
std::vector< VkSwapchainKHR > retired_swapchains
Definition mxvk.hpp:577

◆ render()

void mxvk::VK_Window::render ( )
virtualinherited

Render one frame.

Reimplemented in example::FractalWindow, and viewer::ModelViewerWindow.

Definition at line 778 of file mxvk.cpp.

778 {
779 drawFrame();
780 }

◆ renderStandaloneSprite()

void mxvk::VK_Window::renderStandaloneSprite ( VK_Sprite & sprite,
VkCommandBuffer cmd )
protectedinherited

Render one standalone sprite using the window's shared sprite pipeline.

This is intended for derived classes that want to draw a sprite before or after their own scene content without registering it in the window-managed sprite list.

Definition at line 1142 of file mxvk.cpp.

1142 {
1143 if (device == VK_NULL_HANDLE || swapchain_extent.width == 0U || swapchain_extent.height == 0U) {
1144 return;
1145 }
1146
1147 if (sprite_pipeline == VK_NULL_HANDLE) {
1148 return;
1149 }
1150 vkCmdBindPipeline(cmd, VK_PIPELINE_BIND_POINT_GRAPHICS, sprite_pipeline);
1151 sprite.renderSprites(cmd, sprite_pipeline_layout, swapchain_extent.width, swapchain_extent.height);
1152 }
VkPipelineLayout sprite_pipeline_layout
Definition mxvk.hpp:543
VkPipeline sprite_pipeline
Definition mxvk.hpp:544

◆ saveSnapshot()

void mxvk::VK_Window::saveSnapshot ( const std::string & path)
inherited

Save the most recently rendered window contents as a PNG file.

Parameters
pathDestination PNG path.

This performs a synchronous GPU readback and may briefly stall rendering.

Definition at line 782 of file mxvk.cpp.

782 {
783 if (path.empty()) {
784 throw mxvk::Exception("saveSnapshot requires a non-empty output path");
785 }
786
787 std::vector<std::uint8_t> rgba{};
788 uint32_t width = 0;
789 uint32_t height = 0;
790 captureSnapshotPixels(rgba, width, height);
791
792 if (!mxvk::SavePNG_RGBA(path.c_str(),
793 rgba.data(),
794 static_cast<int>(width),
795 static_cast<int>(height))) {
796 throw mxvk::Exception("saveSnapshot failed to write PNG: " + path);
797 }
798 }
void captureSnapshotPixels(std::vector< std::uint8_t > &rgba_pixels, uint32_t &width, uint32_t &height)
Capture the most recently presented swapchain image as tightly packed RGBA8 pixels.
Definition mxvk.cpp:800
bool SavePNG_RGBA(const char *filename, void *buffer, int w, int h)
Save a raw RGBA pixel buffer to a PNG file.
Definition mxvk_png.cpp:300

◆ screenshotEnabled()

bool mxvk::VK_Window::screenshotEnabled ( ) const
inlinenodiscardnoexceptinherited

Check whether F10 screenshot capture is enabled.

Definition at line 162 of file mxvk.hpp.

162{ return screenshot_enabled; }

◆ setClearColor()

void mxvk::VK_Window::setClearColor ( float r,
float g,
float b,
float a = 1.0f )
inherited

Set the per-frame color attachment clear color.

Parameters
rRed channel [0, 1].
gGreen channel [0, 1].
bBlue channel [0, 1].
aAlpha channel [0, 1].

Definition at line 593 of file mxvk.cpp.

593 {
594 clear_color.float32[0] = std::clamp(r, 0.0f, 1.0f);
595 clear_color.float32[1] = std::clamp(g, 0.0f, 1.0f);
596 clear_color.float32[2] = std::clamp(b, 0.0f, 1.0f);
597 clear_color.float32[3] = std::clamp(a, 0.0f, 1.0f);
598 }
VkClearColorValue clear_color
Definition mxvk.hpp:576

◆ setEnableScreenshot()

void mxvk::VK_Window::setEnableScreenshot ( bool enabled)
inlinenoexceptinherited

Enable or disable F10 screenshot capture.

Definition at line 159 of file mxvk.hpp.

159{ screenshot_enabled = enabled; }

◆ setFont()

void mxvk::VK_Window::setFont ( const std::string & fontPath,
int fontSize = 24 )
inherited

Set the active text-render font.

Parameters
fontPathPath to a TTF font file.
fontSizeFont point size.

Definition at line 2991 of file mxvk.cpp.

2991 {
2992 if (fontPath.empty() || fontSize <= 0) {
2993 throw mxvk::Exception("setFont requires a non-empty path and positive font size");
2994 }
2995
2996 font_path = fontPath;
2997 font_size = fontSize;
2998 font_configured = true;
2999
3000 if (device == VK_NULL_HANDLE) {
3001 throw mxvk::Exception("Cannot set font before Vulkan device initialization");
3002 }
3003 if (swapchain == VK_NULL_HANDLE || command_pool == VK_NULL_HANDLE) {
3004 createDevice();
3005 }
3006 if (swapchain == VK_NULL_HANDLE || command_pool == VK_NULL_HANDLE) {
3007 throw mxvk::Exception("Cannot initialize text renderer before swapchain and command resources are available");
3008 }
3009
3010 if (!text_renderer) {
3011 ensureTextRenderer();
3012 } else {
3014 }
3015 text_state_dirty = true;
3016 }
std::string font_path
Definition mxvk.hpp:568

◆ setPostProcessingEnabled()

void mxvk::VK_Window::setPostProcessingEnabled ( bool enabled)
inlineinherited

Definition at line 284 of file mxvk.hpp.

284{ post_process_enabled = enabled; }
bool post_process_enabled
Definition mxvk.hpp:548

◆ setPostProcessingShaderParams() [1/2]

void mxvk::VK_Window::setPostProcessingShaderParams ( float p1 = 0.0f,
float p2 = 0.0f,
float p3 = 0.0f,
float p4 = 0.0f )
inherited

Set the post-processing shader params passed as a vec4 push constant.

Definition at line 1227 of file mxvk.cpp.

1227 {
1228 post_process_params = {p1, p2, p3, p4};
1229 if (post_process_sprites.empty()) {
1230 return;
1231 }
1232 setPostProcessingShaderParams(0, p1, p2, p3, p4);
1233 }
void setPostProcessingShaderParams(float p1=0.0f, float p2=0.0f, float p3=0.0f, float p4=0.0f)
Set the post-processing shader params passed as a vec4 push constant.
Definition mxvk.cpp:1227

◆ setPostProcessingShaderParams() [2/2]

void mxvk::VK_Window::setPostProcessingShaderParams ( size_t effectIndex,
float p1 = 0.0f,
float p2 = 0.0f,
float p3 = 0.0f,
float p4 = 0.0f )
inherited

Definition at line 1235 of file mxvk.cpp.

1235 {
1236 if (effectIndex >= post_process_sprites.size()) {
1237 return;
1238 }
1239 post_process_params = {p1, p2, p3, p4};
1240 if (effectIndex < post_process_effect_params.size()) {
1242 }
1243 if (post_process_sprites[effectIndex] != nullptr) {
1244 post_process_sprites[effectIndex]->setShaderParams(p1, p2, p3, p4);
1245 }
1246 }

◆ setPostProcessingShaderTimeEnabled() [1/2]

void mxvk::VK_Window::setPostProcessingShaderTimeEnabled ( bool enabled)
inherited

Keep shader param 1 updated with elapsed render time in seconds.

Definition at line 1248 of file mxvk.cpp.

1248 {
1249 post_process_time_enabled = enabled;
1250 post_process_start_time = std::chrono::steady_clock::now();
1251 if (post_process_sprites.empty()) {
1252 return;
1253 }
1255 }
void setPostProcessingShaderTimeEnabled(bool enabled)
Keep shader param 1 updated with elapsed render time in seconds.
Definition mxvk.cpp:1248

◆ setPostProcessingShaderTimeEnabled() [2/2]

void mxvk::VK_Window::setPostProcessingShaderTimeEnabled ( size_t effectIndex,
bool enabled )
inherited

Definition at line 1257 of file mxvk.cpp.

1257 {
1258 if (effectIndex >= post_process_sprites.size()) {
1259 return;
1260 }
1261 post_process_time_enabled = enabled;
1262 post_process_start_time = std::chrono::steady_clock::now();
1263 if (effectIndex < post_process_effect_time_enabled.size()) {
1264 post_process_effect_time_enabled[effectIndex] = enabled;
1265 }
1266 if (effectIndex < post_process_effect_start_times.size()) {
1268 }
1269 }

◆ showCursor()

void mxvk::VK_Window::showCursor ( bool on)
inherited

Definition at line 3633 of file mxvk.cpp.

3633 {
3634 if (on)
3635 SDL_ShowCursor();
3636 else
3637 SDL_HideCursor();
3638 }

◆ trimMemory()

void mxvk::VK_Window::trimMemory ( )
inherited

Return transient Vulkan command-pool allocations to the driver when supported.

MXVK currently uses direct Vulkan memory allocations rather than VMA, so VMA defragmentation is not applicable. This hook schedules the available Vulkan memory maintenance operation and is safe to call during idle/loading points.

Definition at line 1041 of file mxvk.cpp.

1041 {
1042 if (device == VK_NULL_HANDLE || command_pool == VK_NULL_HANDLE || vkTrimCommandPool == nullptr) {
1043 return;
1044 }
1045
1046 vkTrimCommandPool(device, command_pool, 0);
1047 }

◆ validationEnabled()

bool mxvk::VK_Window::validationEnabled ( ) const
nodiscardinherited

Check whether Vulkan validation layers are currently enabled.

Returns
true if validation layers are enabled, false otherwise.

Definition at line 2911 of file mxvk.cpp.

2911 {
2912 return validation_enabled;
2913 }

Member Data Documentation

◆ active

bool mxvk::VK_Window::active = false
protectedinherited

Definition at line 516 of file mxvk.hpp.

◆ clear_color

VkClearColorValue mxvk::VK_Window::clear_color {{0.0f, 0.0f, 0.0f, 1.0f}}
protectedinherited

Definition at line 576 of file mxvk.hpp.

576{{0.0f, 0.0f, 0.0f, 1.0f}};

◆ command_buffers

std::vector<VkCommandBuffer> mxvk::VK_Window::command_buffers {}
protectedinherited

Definition at line 505 of file mxvk.hpp.

505{};

◆ command_pool

VkCommandPool mxvk::VK_Window::command_pool = VK_NULL_HANDLE
protectedinherited

Definition at line 504 of file mxvk.hpp.

◆ current_frame

uint32_t mxvk::VK_Window::current_frame = 0
protectedinherited

Definition at line 511 of file mxvk.hpp.

◆ debug_messenger

VkDebugUtilsMessengerEXT mxvk::VK_Window::debug_messenger = VK_NULL_HANDLE
protectedinherited

Definition at line 482 of file mxvk.hpp.

◆ depth_format

VkFormat mxvk::VK_Window::depth_format = VK_FORMAT_UNDEFINED
protectedinherited

Definition at line 494 of file mxvk.hpp.

◆ depth_image_initialized

std::vector<bool> mxvk::VK_Window::depth_image_initialized {}
protectedinherited

Definition at line 502 of file mxvk.hpp.

502{};

◆ depth_image_memories

std::vector<VkDeviceMemory> mxvk::VK_Window::depth_image_memories {}
protectedinherited

Definition at line 500 of file mxvk.hpp.

500{};

◆ depth_image_views

std::vector<VkImageView> mxvk::VK_Window::depth_image_views {}
protectedinherited

Definition at line 501 of file mxvk.hpp.

501{};

◆ depth_images

std::vector<VkImage> mxvk::VK_Window::depth_images {}
protectedinherited

Definition at line 499 of file mxvk.hpp.

499{};

◆ device

VkDevice mxvk::VK_Window::device = VK_NULL_HANDLE
protectedinherited

Definition at line 485 of file mxvk.hpp.

◆ font_configured

bool mxvk::VK_Window::font_configured = false
protectedinherited

Definition at line 567 of file mxvk.hpp.

◆ font_path

std::string mxvk::VK_Window::font_path {}
protectedinherited

Definition at line 568 of file mxvk.hpp.

568{};

◆ font_size

int mxvk::VK_Window::font_size = 24
protectedinherited

Definition at line 569 of file mxvk.hpp.

◆ force_swapchain_recreate

bool mxvk::VK_Window::force_swapchain_recreate = false
protectedinherited

Definition at line 534 of file mxvk.hpp.

◆ fps_counter_enabled

bool mxvk::VK_Window::fps_counter_enabled = false
protectedinherited

Definition at line 570 of file mxvk.hpp.

◆ fps_counter_font

Font mxvk::VK_Window::fps_counter_font {}
protectedinherited

Definition at line 572 of file mxvk.hpp.

572{};

◆ fps_counter_font_ready

bool mxvk::VK_Window::fps_counter_font_ready = false
protectedinherited

Definition at line 571 of file mxvk.hpp.

◆ fps_counter_frame_count

uint32_t mxvk::VK_Window::fps_counter_frame_count = 0
protectedinherited

Definition at line 574 of file mxvk.hpp.

◆ fps_counter_sample_time

std::chrono::steady_clock::time_point mxvk::VK_Window::fps_counter_sample_time {}
protectedinherited

Definition at line 573 of file mxvk.hpp.

573{};

◆ fps_counter_text

std::string mxvk::VK_Window::fps_counter_text = "FPS: --"
protectedinherited

Definition at line 575 of file mxvk.hpp.

◆ framebuffer_resized

bool mxvk::VK_Window::framebuffer_resized = false
protectedinherited

Definition at line 533 of file mxvk.hpp.

◆ graphics_queue

VkQueue mxvk::VK_Window::graphics_queue = VK_NULL_HANDLE
protectedinherited

Definition at line 488 of file mxvk.hpp.

◆ graphics_queue_family

uint32_t mxvk::VK_Window::graphics_queue_family = invalid_queue_index
protectedinherited

Definition at line 486 of file mxvk.hpp.

◆ image_available

std::array<VkSemaphore, max_frames_in_flight> mxvk::VK_Window::image_available {}
protectedinherited

Definition at line 507 of file mxvk.hpp.

507{};

◆ image_fences

std::vector<VkFence> mxvk::VK_Window::image_fences {}
protectedinherited

Definition at line 510 of file mxvk.hpp.

510{};

◆ in_flight_fences

std::array<VkFence, max_frames_in_flight> mxvk::VK_Window::in_flight_fences {}
protectedinherited

Definition at line 509 of file mxvk.hpp.

509{};

◆ instance

VkInstance mxvk::VK_Window::instance = VK_NULL_HANDLE
protectedinherited

Definition at line 481 of file mxvk.hpp.

◆ last_memory_trim_time

std::chrono::steady_clock::time_point mxvk::VK_Window::last_memory_trim_time = std::chrono::steady_clock::now()
protectedinherited

Definition at line 537 of file mxvk.hpp.

◆ last_presented_image_index

uint32_t mxvk::VK_Window::last_presented_image_index = invalid_queue_index
protectedinherited

Definition at line 512 of file mxvk.hpp.

◆ last_resize_event_ms

uint64_t mxvk::VK_Window::last_resize_event_ms = 0
protectedinherited

Definition at line 535 of file mxvk.hpp.

◆ MEMORY_TRIM_INTERVAL

std::chrono::seconds mxvk::VK_Window::MEMORY_TRIM_INTERVAL {30}
staticconstexprprotectedinherited

Definition at line 538 of file mxvk.hpp.

538{30};

◆ owned_post_process_sprite

VK_Sprite* mxvk::VK_Window::owned_post_process_sprite = nullptr
protectedinherited

Definition at line 547 of file mxvk.hpp.

◆ owned_post_process_sprites

std::vector<VK_Sprite *> mxvk::VK_Window::owned_post_process_sprites {}
protectedinherited

Definition at line 553 of file mxvk.hpp.

553{};

◆ physical_device

VkPhysicalDevice mxvk::VK_Window::physical_device = VK_NULL_HANDLE
protectedinherited

Definition at line 484 of file mxvk.hpp.

◆ pipeline_cache

VkPipelineCache mxvk::VK_Window::pipeline_cache = VK_NULL_HANDLE
protectedinherited

Definition at line 490 of file mxvk.hpp.

◆ post_process_effect_params

std::vector<std::array<float, 4> > mxvk::VK_Window::post_process_effect_params {}
protectedinherited

Definition at line 554 of file mxvk.hpp.

554{};

◆ post_process_effect_start_times

std::vector<std::chrono::steady_clock::time_point> mxvk::VK_Window::post_process_effect_start_times {}
protectedinherited

Definition at line 556 of file mxvk.hpp.

556{};

◆ post_process_effect_time_enabled

std::vector<bool> mxvk::VK_Window::post_process_effect_time_enabled {}
protectedinherited

Definition at line 555 of file mxvk.hpp.

555{};

◆ post_process_enabled

bool mxvk::VK_Window::post_process_enabled = true
protectedinherited

Definition at line 548 of file mxvk.hpp.

◆ post_process_images

std::vector<std::vector<VkImage> > mxvk::VK_Window::post_process_images {}
protectedinherited

Definition at line 557 of file mxvk.hpp.

557{};

◆ post_process_initialized

std::vector<std::vector<bool> > mxvk::VK_Window::post_process_initialized {}
protectedinherited

Definition at line 560 of file mxvk.hpp.

560{};

◆ post_process_memories

std::vector<std::vector<VkDeviceMemory> > mxvk::VK_Window::post_process_memories {}
protectedinherited

Definition at line 558 of file mxvk.hpp.

558{};

◆ post_process_params

std::array<float, 4> mxvk::VK_Window::post_process_params {}
protectedinherited

Definition at line 550 of file mxvk.hpp.

550{};

◆ post_process_sprite

VK_Sprite* mxvk::VK_Window::post_process_sprite = nullptr
protectedinherited

Definition at line 546 of file mxvk.hpp.

◆ post_process_sprites

std::vector<VK_Sprite *> mxvk::VK_Window::post_process_sprites {}
protectedinherited

Definition at line 552 of file mxvk.hpp.

552{};

◆ post_process_start_time

std::chrono::steady_clock::time_point mxvk::VK_Window::post_process_start_time = std::chrono::steady_clock::now()
protectedinherited

Definition at line 551 of file mxvk.hpp.

◆ post_process_time_enabled

bool mxvk::VK_Window::post_process_time_enabled = false
protectedinherited

Definition at line 549 of file mxvk.hpp.

◆ post_process_views

std::vector<std::vector<VkImageView> > mxvk::VK_Window::post_process_views {}
protectedinherited

Definition at line 559 of file mxvk.hpp.

559{};

◆ present_mode_preference

PresentModePreference mxvk::VK_Window::present_mode_preference = PresentModePreference::LowLatency
protectedinherited

Definition at line 532 of file mxvk.hpp.

◆ present_queue

VkQueue mxvk::VK_Window::present_queue = VK_NULL_HANDLE
protectedinherited

Definition at line 489 of file mxvk.hpp.

◆ present_queue_family

uint32_t mxvk::VK_Window::present_queue_family = invalid_queue_index
protectedinherited

Definition at line 487 of file mxvk.hpp.

◆ render_finished

std::vector<VkSemaphore> mxvk::VK_Window::render_finished {}
protectedinherited

Definition at line 508 of file mxvk.hpp.

508{};

◆ resize_settle_delay_ms

uint64_t mxvk::VK_Window::resize_settle_delay_ms = 150
staticconstexprprotectedinherited

Definition at line 536 of file mxvk.hpp.

◆ retired_swapchains

std::vector<VkSwapchainKHR> mxvk::VK_Window::retired_swapchains
protectedinherited

Definition at line 577 of file mxvk.hpp.

◆ screenshot_enabled

bool mxvk::VK_Window::screenshot_enabled = defaultEnableScreenshot()
protectedinherited

Definition at line 517 of file mxvk.hpp.

◆ screenshot_index

uint32_t mxvk::VK_Window::screenshot_index = 0
protectedinherited

Definition at line 519 of file mxvk.hpp.

◆ screenshot_prefix

std::string mxvk::VK_Window::screenshot_prefix = defaultExecutableName()
protectedinherited

Definition at line 518 of file mxvk.hpp.

◆ screenshot_queue_cv

std::condition_variable mxvk::VK_Window::screenshot_queue_cv
protectedinherited

Definition at line 527 of file mxvk.hpp.

◆ screenshot_queue_mutex

std::mutex mxvk::VK_Window::screenshot_queue_mutex
protectedinherited

Definition at line 526 of file mxvk.hpp.

◆ screenshot_save_queue

std::deque<ScreenshotSaveTask> mxvk::VK_Window::screenshot_save_queue
protectedinherited

Definition at line 528 of file mxvk.hpp.

◆ screenshot_worker

std::thread mxvk::VK_Window::screenshot_worker
protectedinherited

Definition at line 529 of file mxvk.hpp.

◆ screenshot_worker_stop

bool mxvk::VK_Window::screenshot_worker_stop = false
protectedinherited

Definition at line 530 of file mxvk.hpp.

◆ sdl_initialized

bool mxvk::VK_Window::sdl_initialized = false
protectedinherited

Definition at line 515 of file mxvk.hpp.

◆ sprite_descriptor_set_layout

VkDescriptorSetLayout mxvk::VK_Window::sprite_descriptor_set_layout = VK_NULL_HANDLE
protectedinherited

Definition at line 542 of file mxvk.hpp.

◆ sprite_pipeline

VkPipeline mxvk::VK_Window::sprite_pipeline = VK_NULL_HANDLE
protectedinherited

Definition at line 544 of file mxvk.hpp.

◆ sprite_pipeline_layout

VkPipelineLayout mxvk::VK_Window::sprite_pipeline_layout = VK_NULL_HANDLE
protectedinherited

Definition at line 543 of file mxvk.hpp.

◆ sprite_state_dirty

bool mxvk::VK_Window::sprite_state_dirty = false
protectedinherited

Definition at line 545 of file mxvk.hpp.

◆ sprites

std::vector<std::unique_ptr<VK_Sprite> > mxvk::VK_Window::sprites {}
protectedinherited

Definition at line 540 of file mxvk.hpp.

540{};

◆ sprites3d

std::vector<std::unique_ptr<VK_Sprite3D> > mxvk::VK_Window::sprites3d {}
protectedinherited

Definition at line 541 of file mxvk.hpp.

541{};

◆ surface

VkSurfaceKHR mxvk::VK_Window::surface = VK_NULL_HANDLE
protectedinherited

Definition at line 483 of file mxvk.hpp.

◆ swapchain

VkSwapchainKHR mxvk::VK_Window::swapchain = VK_NULL_HANDLE
protectedinherited

Definition at line 492 of file mxvk.hpp.

◆ swapchain_extent

VkExtent2D mxvk::VK_Window::swapchain_extent {}
protectedinherited

Definition at line 495 of file mxvk.hpp.

495{};

◆ swapchain_format

VkFormat mxvk::VK_Window::swapchain_format = VK_FORMAT_UNDEFINED
protectedinherited

Definition at line 493 of file mxvk.hpp.

◆ swapchain_image_initialized

std::vector<bool> mxvk::VK_Window::swapchain_image_initialized {}
protectedinherited

Definition at line 498 of file mxvk.hpp.

498{};

◆ swapchain_image_views

std::vector<VkImageView> mxvk::VK_Window::swapchain_image_views {}
protectedinherited

Definition at line 497 of file mxvk.hpp.

497{};

◆ swapchain_images

std::vector<VkImage> mxvk::VK_Window::swapchain_images {}
protectedinherited

Definition at line 496 of file mxvk.hpp.

496{};

◆ swapchain_supports_transfer_src

bool mxvk::VK_Window::swapchain_supports_transfer_src = false
protectedinherited

Definition at line 513 of file mxvk.hpp.

◆ text_descriptor_set_layout

VkDescriptorSetLayout mxvk::VK_Window::text_descriptor_set_layout = VK_NULL_HANDLE
protectedinherited

Definition at line 563 of file mxvk.hpp.

◆ text_pipeline

VkPipeline mxvk::VK_Window::text_pipeline = VK_NULL_HANDLE
protectedinherited

Definition at line 565 of file mxvk.hpp.

◆ text_pipeline_layout

VkPipelineLayout mxvk::VK_Window::text_pipeline_layout = VK_NULL_HANDLE
protectedinherited

Definition at line 564 of file mxvk.hpp.

◆ text_renderer

std::unique_ptr<VK_Text> mxvk::VK_Window::text_renderer {}
protectedinherited

Definition at line 562 of file mxvk.hpp.

562{};

◆ text_state_dirty

bool mxvk::VK_Window::text_state_dirty = false
protectedinherited

Definition at line 566 of file mxvk.hpp.

◆ validation_enabled

bool mxvk::VK_Window::validation_enabled = false
protectedinherited

Definition at line 531 of file mxvk.hpp.

◆ window

std::unique_ptr<SDL_Window, SDLWindowDeleter> mxvk::VK_Window::window {}
protectedinherited

Definition at line 480 of file mxvk.hpp.

480{};

The documentation for this class was generated from the following file: