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

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.
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, uint32_t spectrumBinCount, uint32_t spectrumHistoryLayerCount)
 Create a blank sprite with live FFT and FFT-history descriptors.
VK_Sprite * createSprite (int width, int height, const std::string &vertexShaderPath="", const std::string &fragmentShaderPath="", uint32_t spectrumBinCount=0)
 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.
VkExtent2D getRenderExtent () const noexcept
 Get the active scene/post-processing extent.
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.
 MoonWindow (const std::string &filename, const std::string &path, const std::string &fragment_path, const std::string &title, int width, int height, bool fullscreen, bool enable_vsync)
virtual void onPrepareFrameRendering (VkCommandBuffer cmd, uint32_t image_index)
 Record resource transitions that must happen before dynamic rendering begins.
void onRecordCustomRendering (VkCommandBuffer cmd, uint32_t imageIndex) override
 Optional hook for derived classes to record extra draw commands.
void onSwapchainRecreated () override
 Called after swapchain and render resources are recreated.
void printPreviewText (const std::string &text, int x, int y, const SDL_Color &col)
 Queue preview-only text after frame readback.
void printPreviewText (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)
 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)
virtual void proc ()
 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 setFrameReadbackEnabled (bool enabled) noexcept
 Enable or disable readback of each rendered swapchain frame.
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 setPostProcessingTextureConsumerEnabled (bool enabled) noexcept
 Route the completed effect chain to a derived renderer as a sampled texture.
void setPreviewFont (const std::string &fontPath, int fontSize=24)
 Set the preview-only HUD font independently of output text.
void setRenderExtent (uint32_t width, uint32_t height)
 Render post-processing at a fixed extent independent of the preview window.
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.
 ~MoonWindow () override

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 destroyFrameReadbackResources ()
void dispatchFrameReadback (FrameReadbackSlot &slot)
void ensureFrameReadbackResources ()
void exit ()
 Request loop termination.
void flushFrameReadbacks ()
 Wait for and deliver all pending pipelined frame readbacks.
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 onFrameReadback (std::vector< std::uint8_t > &rgba_pixels, uint32_t width, uint32_t height)
 Receive one tightly packed RGBA8 copy of the rendered frame.
virtual void onFrameReadbackScheduled ()
 Called after a presented frame has scheduled a readback.
virtual void onRecordPostProcessingTexture (VkCommandBuffer cmd, uint32_t image_index, VkImageView texture_view, VkExtent2D texture_extent)
 Render a completed post-processing chain as custom scene input.
virtual void onSwapchainAboutToRecreate ()
 Called right before swapchain-dependent 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.
void renderStandaloneSprite (VK_Sprite &sprite, VkCommandBuffer cmd, VkExtent2D extent)

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 frame_readback_enabled = false
std::array< FrameReadbackSlot, max_frames_in_flight > frame_readback_slots {}
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
uint32_t latest_frame_readback_height = 0
std::vector< std::uint8_t > latest_frame_readback_rgba {}
uint32_t latest_frame_readback_width = 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
VK_Sprite * post_process_composite_sprite = nullptr
std::vector< std::array< float, 4 > > post_process_effect_params {}
std::vector< ShaderStage > post_process_effect_stages {}
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_present_sprite = nullptr
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_texture_consumer_enabled = false
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
bool preview_text_queued = false
std::unique_ptr< VK_Text > preview_text_renderer {}
VkExtent2D render_extent_override {}
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
bool vulkan_library_loaded = false
std::unique_ptr< SDL_Window, SDLWindowDeleterwindow {}

Static Protected Attributes

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

Detailed Description

Definition at line 92 of file moon.cpp.

Member Enumeration Documentation

◆ PresentModePreference

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

Definition at line 40 of file mxvk.hpp.

40 {
41 LowLatency,
42 Vsync
43 };

Constructor & Destructor Documentation

◆ MoonWindow()

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

Definition at line 94 of file moon.cpp.

101 : mxvk::VK_Window(title, width, height, fullscreen, MXVK_VALIDATION, enable_vsync),
102 asset_root(path.empty() ? std::string(MOON_ASSET_DIR) : path) {
103 const std::string model_path = filename.empty() ? (asset_root + "/data/moon.obj") : filename;
104 const std::string texture_base_path = asset_root + "/data";
105 const std::string vert_path = asset_root + "/data/model.vert.spv";
106 const std::string frag_path = fragment_path.empty() ? (asset_root + "/data/model.frag.spv") : fragment_path;
107
108 model.load(this, model_path, "", texture_base_path, 1.0f);
109 model.setShaders(this, vert_path, frag_path);
110
111 const std::string pyramid_path = asset_root + "/data/pyramid.obj";
112 for (mxvk::VKAbstractModel &pyramid : pyramids) {
113 pyramid.load(this, pyramid_path, "", texture_base_path, 1.0f);
114 pyramid.setShaders(this, vert_path, frag_path);
115 }
116
117 std::unique_ptr<SDL_Surface, decltype(&SDL_DestroySurface)> star_surface(loadColorKeyedPNG(asset_root + "/data/star.png"), SDL_DestroySurface);
118 star_sprite = createSprite3D(star_surface.get());
119 if (star_sprite == nullptr) {
120 throw mxvk::Exception("Failed to create moon starfield sprite batch");
121 }
122 star_sprite->setDepthTestEnabled(true);
123 star_sprite->setDepthWriteEnabled(false);
124 star_sprite->setAlphaDiscardThreshold(0.01f);
125 initStars();
126 }
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.
Definition mxvk.cpp:4735
#define MXVK_VALIDATION
Definition mxvk.hpp:28
std::filesystem::path pyramid_path(const std::string &asset_path)
Definition main.cpp:43
std::filesystem::path model_path(const std::string &filename, const std::string &asset_path)
Definition main.cpp:140
SDL_Surface * loadColorKeyedPNG(const std::string &path, std::uint8_t threshold=12, std::uint8_t softness=48)
Definition moon.cpp:45

◆ ~MoonWindow()

example::MoonWindow::~MoonWindow ( )
inlineoverride

Definition at line 128 of file moon.cpp.

128 {
129 if (device != VK_NULL_HANDLE) {
130 vkDeviceWaitIdle(device);
131 }
132 model.cleanup(this);
133 for (mxvk::VKAbstractModel &pyramid : pyramids) {
134 pyramid.cleanup(this);
135 }
136 if (star_sprite != nullptr) {
137 star_sprite->cleanup();
138 }
139 }
VkDevice device
Definition mxvk.hpp:596

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 1499 of file mxvk.cpp.

1499 {
1500 const std::vector<VK_Sprite *> sprites = attachPostProcessingShaders({PostProcessingEffect{fragmentShaderPath, {p1, p2, p3, p4}, false}});
1501 return sprites.empty() ? nullptr : sprites.front();
1502 }
std::vector< VK_Sprite * > attachPostProcessingShaders(const std::vector< PostProcessingEffect > &effects)
Definition mxvk.cpp:1504
std::vector< std::unique_ptr< VK_Sprite > > sprites
Definition mxvk.hpp:661

◆ attachPostProcessingShaders()

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

Definition at line 1504 of file mxvk.cpp.

1504 {
1506
1507 std::vector<VK_Sprite *> attachedSprites;
1508 attachedSprites.reserve(effects.size());
1509 for (size_t effect_index = 0; effect_index < effects.size();
1510 ++effect_index) {
1511 const PostProcessingEffect &effect = effects[effect_index];
1512 if (effect.fragmentShaderPath.empty()) {
1513 throw mxvk::Exception(
1514 "Cannot attach a post-processing pass with an empty shader path");
1515 }
1516
1517 const ShaderModuleInfo module_info =
1518 inspect_spirv(load_spv(effect.fragmentShaderPath));
1519 const ShaderStage stage = effect.stage == ShaderStage::Unknown
1520 ? module_info.stage
1521 : effect.stage;
1522 if (stage != ShaderStage::Fragment && stage != ShaderStage::Compute) {
1523 throw mxvk::Exception(
1524 "Post-processing shaders must contain a fragment or compute entry point");
1525 }
1526 if (effect.stage != ShaderStage::Unknown &&
1527 effect.stage != module_info.stage) {
1528 throw mxvk::Exception(
1529 "Post-processing shader stage does not match its SPIR-V entry point");
1530 }
1531
1532 VK_Sprite *sprite = createSprite(
1533 1,
1534 1,
1535 resolveRuntimeShaderPath("sprite.vert.spv", MXVK_SPRITE_SHADER_DIR),
1536 "",
1537 effect.spectrumBinCount,
1538 effect.spectrumHistoryLayerCount);
1539 sprite->enableExtendedUBO();
1540 if (effect.historySource != nullptr) {
1541 sprite->shareHistoryTexture(*effect.historySource);
1542 }
1543 const bool final_fragment =
1544 stage == ShaderStage::Fragment &&
1545 effect_index + 1U == effects.size();
1546 if (stage == ShaderStage::Fragment) {
1547 if (!final_fragment) {
1548 sprite->setColorAttachmentFormat(post_process_compute_format);
1549 }
1550 sprite->setFragmentShaderPath(effect.fragmentShaderPath);
1551 } else if (stage == ShaderStage::Compute) {
1552 sprite->enableComputeShader(
1553 effect.fragmentShaderPath, module_info.localSizeX,
1554 module_info.localSizeY, module_info.localSizeZ);
1555 }
1556 const uint32_t black_pixel = 0xFF000000u;
1557 sprite->updateTexture(&black_pixel, 1, 1);
1558 sprite->setShaderParams(effect.params[0], effect.params[1], effect.params[2], effect.params[3]);
1559
1560 attachedSprites.push_back(sprite);
1561 owned_post_process_sprites.push_back(sprite);
1562 post_process_sprites.push_back(sprite);
1563 post_process_effect_params.push_back(effect.params);
1564 post_process_effect_time_enabled.push_back(effect.timeEnabled);
1565 post_process_effect_start_times.push_back(std::chrono::steady_clock::now());
1566 post_process_effect_stages.push_back(stage);
1567 }
1568
1569 if (!post_process_effect_stages.empty()) {
1571 const uint32_t black_pixel = 0xFF000000u;
1572 post_process_present_sprite->updateTexture(&black_pixel, 1, 1);
1574
1576 post_process_composite_sprite->updateTexture(&black_pixel, 1, 1);
1578 }
1579
1580 post_process_sprite = attachedSprites.empty() ? nullptr : attachedSprites.front();
1582 post_process_params = post_process_effect_params.empty() ? std::array<float, 4>{} : post_process_effect_params.front();
1584 post_process_start_time = post_process_effect_start_times.empty() ? std::chrono::steady_clock::now() : post_process_effect_start_times.front();
1585 if (device != VK_NULL_HANDLE && !swapchain_images.empty()) {
1586 createPostProcessTargets();
1587 }
1588 sprite_state_dirty = true;
1589 return attachedSprites;
1590 }
void updateTexture(SDL_Surface *surface)
Replace the sprite texture from an SDL_Surface.
std::vector< ShaderStage > post_process_effect_stages
Definition mxvk.hpp:678
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:4608
std::vector< bool > post_process_effect_time_enabled
Definition mxvk.hpp:680
std::vector< std::array< float, 4 > > post_process_effect_params
Definition mxvk.hpp:679
std::chrono::steady_clock::time_point post_process_start_time
Definition mxvk.hpp:673
std::vector< VK_Sprite * > owned_post_process_sprites
Definition mxvk.hpp:675
static constexpr VkFormat post_process_compute_format
Definition mxvk.hpp:686
std::vector< VkImage > swapchain_images
Definition mxvk.hpp:608
bool post_process_time_enabled
Definition mxvk.hpp:671
VK_Sprite * post_process_composite_sprite
Definition mxvk.hpp:677
std::vector< std::chrono::steady_clock::time_point > post_process_effect_start_times
Definition mxvk.hpp:681
VK_Sprite * post_process_present_sprite
Definition mxvk.hpp:676
std::array< float, 4 > post_process_params
Definition mxvk.hpp:672
void detachPostProcessingShader()
Detach the current post-processing shader and return to direct swapchain rendering.
Definition mxvk.cpp:1592
std::vector< VK_Sprite * > post_process_sprites
Definition mxvk.hpp:674
bool sprite_state_dirty
Definition mxvk.hpp:666
VK_Sprite * post_process_sprite
Definition mxvk.hpp:667
VK_Sprite * owned_post_process_sprite
Definition mxvk.hpp:668
#define MXVK_SPRITE_SHADER_DIR
Definition mxvk.cpp:25
ShaderModuleInfo inspect_spirv(const std::vector< char > &spv_bytes)
Inspect a SPIR-V entry point and compute local workgroup size.
std::vector< char > load_spv(const std::string &path)
Load a SPIR-V file from disk.

◆ 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 827 of file mxvk.cpp.

827 {
828 if (!latest_frame_readback_rgba.empty()) {
829 rgba_pixels = latest_frame_readback_rgba;
832 return;
833 }
834 if (device == VK_NULL_HANDLE || command_pool == VK_NULL_HANDLE || graphics_queue == VK_NULL_HANDLE) {
835 throw mxvk::Exception("captureSnapshotPixels called before Vulkan render resources are ready");
836 }
837 if (last_presented_image_index == invalid_queue_index ||
840 throw mxvk::Exception("captureSnapshotPixels called before a frame has been presented");
841 }
842 const bool capture_offscreen =
843 render_extent_override.width > 0U &&
844 render_extent_override.height > 0U &&
845 !post_process_images.empty() &&
846 !post_process_initialized.empty() &&
849 post_process_initialized.front().size() &&
851 VK_NULL_HANDLE &&
853 if (!capture_offscreen && !swapchain_supports_transfer_src) {
854 throw mxvk::Exception(
855 "captureSnapshotPixels requires swapchain transfer-source support");
856 }
857 const VkExtent2D source_extent =
858 capture_offscreen ? getRenderExtent() : swapchain_extent;
859 const VkImage source_image =
860 capture_offscreen
863 const VkImageLayout source_layout =
864 capture_offscreen ? VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL
865 : VK_IMAGE_LAYOUT_PRESENT_SRC_KHR;
866 if (source_extent.width == 0U || source_extent.height == 0U) {
867 throw mxvk::Exception("captureSnapshotPixels cannot capture an empty swapchain extent");
868 }
869
870 const bool format_is_bgra =
871 swapchain_format == VK_FORMAT_B8G8R8A8_UNORM ||
872 swapchain_format == VK_FORMAT_B8G8R8A8_SRGB;
873 const bool format_is_rgba =
874 swapchain_format == VK_FORMAT_R8G8B8A8_UNORM ||
875 swapchain_format == VK_FORMAT_R8G8B8A8_SRGB;
876 if (!format_is_bgra && !format_is_rgba) {
877 throw mxvk::Exception(std::format("captureSnapshotPixels unsupported swapchain format: {}", static_cast<int>(swapchain_format)));
878 }
879
880 const VkDeviceSize row_bytes =
881 static_cast<VkDeviceSize>(source_extent.width) * 4U;
882 const VkDeviceSize image_bytes =
883 row_bytes * static_cast<VkDeviceSize>(source_extent.height);
884
885 VkBuffer readback_buffer = VK_NULL_HANDLE;
886 VkDeviceMemory readback_memory = VK_NULL_HANDLE;
887 VkCommandBuffer command_buffer = VK_NULL_HANDLE;
888 VkFence copy_fence = VK_NULL_HANDLE;
889
890 auto cleanup = [&]() {
891 if (copy_fence != VK_NULL_HANDLE) {
892 vkDestroyFence(device, copy_fence, nullptr);
893 }
894 if (command_buffer != VK_NULL_HANDLE) {
895 vkFreeCommandBuffers(device, command_pool, 1, &command_buffer);
896 }
897 if (readback_buffer != VK_NULL_HANDLE) {
898 vkDestroyBuffer(device, readback_buffer, nullptr);
899 }
900 if (readback_memory != VK_NULL_HANDLE) {
901 vkFreeMemory(device, readback_memory, nullptr);
902 }
903 };
904
905 try {
906 VkBufferCreateInfo buffer_info{};
907 buffer_info.sType = VK_STRUCTURE_TYPE_BUFFER_CREATE_INFO;
908 buffer_info.size = image_bytes;
909 buffer_info.usage = VK_BUFFER_USAGE_TRANSFER_DST_BIT;
910 buffer_info.sharingMode = VK_SHARING_MODE_EXCLUSIVE;
911 if (vkCreateBuffer(device, &buffer_info, nullptr, &readback_buffer) != VK_SUCCESS) {
912 throw mxvk::Exception("captureSnapshotPixels failed to create readback buffer");
913 }
914
915 VkMemoryRequirements memory_requirements{};
916 vkGetBufferMemoryRequirements(device, readback_buffer, &memory_requirements);
917
918 VkPhysicalDeviceMemoryProperties memory_properties{};
919 vkGetPhysicalDeviceMemoryProperties(physical_device, &memory_properties);
920 uint32_t memory_type_index = invalid_queue_index;
921 constexpr VkMemoryPropertyFlags REQUIRED_PROPERTIES =
922 VK_MEMORY_PROPERTY_HOST_VISIBLE_BIT | VK_MEMORY_PROPERTY_HOST_COHERENT_BIT;
923 constexpr VkMemoryPropertyFlags PREFERRED_PROPERTIES =
924 REQUIRED_PROPERTIES | VK_MEMORY_PROPERTY_HOST_CACHED_BIT;
925 for (VkMemoryPropertyFlags properties :
926 {PREFERRED_PROPERTIES, REQUIRED_PROPERTIES}) {
927 for (uint32_t index = 0;
928 index < memory_properties.memoryTypeCount; ++index) {
929 const bool type_matches =
930 (memory_requirements.memoryTypeBits & (1U << index)) != 0U;
931 const bool properties_match =
932 (memory_properties.memoryTypes[index].propertyFlags &
933 properties) == properties;
934 if (type_matches && properties_match) {
935 memory_type_index = index;
936 break;
937 }
938 }
939 if (memory_type_index != invalid_queue_index) {
940 break;
941 }
942 }
943 if (memory_type_index == invalid_queue_index) {
944 throw mxvk::Exception("captureSnapshotPixels failed to find host-visible coherent memory");
945 }
946
947 VkMemoryAllocateInfo allocation_info{};
948 allocation_info.sType = VK_STRUCTURE_TYPE_MEMORY_ALLOCATE_INFO;
949 allocation_info.allocationSize = memory_requirements.size;
950 allocation_info.memoryTypeIndex = memory_type_index;
951 if (vkAllocateMemory(device, &allocation_info, nullptr, &readback_memory) != VK_SUCCESS) {
952 throw mxvk::Exception("captureSnapshotPixels failed to allocate readback memory");
953 }
954 if (vkBindBufferMemory(device, readback_buffer, readback_memory, 0) != VK_SUCCESS) {
955 throw mxvk::Exception("captureSnapshotPixels failed to bind readback memory");
956 }
957
958 VkCommandBufferAllocateInfo command_buffer_info{};
959 command_buffer_info.sType = VK_STRUCTURE_TYPE_COMMAND_BUFFER_ALLOCATE_INFO;
960 command_buffer_info.commandPool = command_pool;
961 command_buffer_info.level = VK_COMMAND_BUFFER_LEVEL_PRIMARY;
962 command_buffer_info.commandBufferCount = 1;
963 if (vkAllocateCommandBuffers(device, &command_buffer_info, &command_buffer) != VK_SUCCESS) {
964 throw mxvk::Exception("captureSnapshotPixels failed to allocate command buffer");
965 }
966
967 VkFenceCreateInfo fence_info{};
968 fence_info.sType = VK_STRUCTURE_TYPE_FENCE_CREATE_INFO;
969 if (vkCreateFence(device, &fence_info, nullptr, &copy_fence) != VK_SUCCESS) {
970 throw mxvk::Exception("captureSnapshotPixels failed to create copy fence");
971 }
972
973 VkFence source_fence = image_fences[last_presented_image_index];
974 if (source_fence != VK_NULL_HANDLE) {
975 const VkResult wait_result = vkWaitForFences(device, 1, &source_fence, VK_TRUE, UINT64_MAX);
976 if (wait_result != VK_SUCCESS) {
977 throw mxvk::Exception(std::format("captureSnapshotPixels failed waiting for rendered frame: {}", static_cast<int>(wait_result)));
978 }
979 }
980
981 const VkResult idle_result = vkDeviceWaitIdle(device);
982 if (idle_result != VK_SUCCESS) {
983 throw mxvk::Exception(std::format("captureSnapshotPixels failed waiting for device idle: {}", static_cast<int>(idle_result)));
984 }
985
986 VkCommandBufferBeginInfo begin_info{};
987 begin_info.sType = VK_STRUCTURE_TYPE_COMMAND_BUFFER_BEGIN_INFO;
988 begin_info.flags = VK_COMMAND_BUFFER_USAGE_ONE_TIME_SUBMIT_BIT;
989 if (vkBeginCommandBuffer(command_buffer, &begin_info) != VK_SUCCESS) {
990 throw mxvk::Exception("captureSnapshotPixels failed to begin copy command buffer");
991 }
992
993 VkImageMemoryBarrier2 to_transfer_barrier{};
994 to_transfer_barrier.sType = VK_STRUCTURE_TYPE_IMAGE_MEMORY_BARRIER_2;
995 to_transfer_barrier.srcStageMask =
996 capture_offscreen ? VK_PIPELINE_STAGE_2_FRAGMENT_SHADER_BIT
997 : VK_PIPELINE_STAGE_2_NONE;
998 to_transfer_barrier.srcAccessMask =
999 capture_offscreen ? VK_ACCESS_2_SHADER_SAMPLED_READ_BIT
1000 : VK_ACCESS_2_NONE;
1001 to_transfer_barrier.dstStageMask = VK_PIPELINE_STAGE_2_TRANSFER_BIT;
1002 to_transfer_barrier.dstAccessMask = VK_ACCESS_2_TRANSFER_READ_BIT;
1003 to_transfer_barrier.oldLayout = source_layout;
1004 to_transfer_barrier.newLayout = VK_IMAGE_LAYOUT_TRANSFER_SRC_OPTIMAL;
1005 to_transfer_barrier.srcQueueFamilyIndex = VK_QUEUE_FAMILY_IGNORED;
1006 to_transfer_barrier.dstQueueFamilyIndex = VK_QUEUE_FAMILY_IGNORED;
1007 to_transfer_barrier.image = source_image;
1008 to_transfer_barrier.subresourceRange.aspectMask = VK_IMAGE_ASPECT_COLOR_BIT;
1009 to_transfer_barrier.subresourceRange.baseMipLevel = 0;
1010 to_transfer_barrier.subresourceRange.levelCount = 1;
1011 to_transfer_barrier.subresourceRange.baseArrayLayer = 0;
1012 to_transfer_barrier.subresourceRange.layerCount = 1;
1013
1014 VkDependencyInfo to_transfer_dependency{};
1015 to_transfer_dependency.sType = VK_STRUCTURE_TYPE_DEPENDENCY_INFO;
1016 to_transfer_dependency.imageMemoryBarrierCount = 1;
1017 to_transfer_dependency.pImageMemoryBarriers = &to_transfer_barrier;
1018 vkCmdPipelineBarrier2(command_buffer, &to_transfer_dependency);
1019
1020 VkBufferImageCopy copy_region{};
1021 copy_region.bufferOffset = 0;
1022 copy_region.bufferRowLength = 0;
1023 copy_region.bufferImageHeight = 0;
1024 copy_region.imageSubresource.aspectMask = VK_IMAGE_ASPECT_COLOR_BIT;
1025 copy_region.imageSubresource.mipLevel = 0;
1026 copy_region.imageSubresource.baseArrayLayer = 0;
1027 copy_region.imageSubresource.layerCount = 1;
1028 copy_region.imageOffset = {0, 0, 0};
1029 copy_region.imageExtent = {source_extent.width, source_extent.height,
1030 1};
1031 vkCmdCopyImageToBuffer(command_buffer,
1032 source_image,
1033 VK_IMAGE_LAYOUT_TRANSFER_SRC_OPTIMAL,
1034 readback_buffer,
1035 1,
1036 &copy_region);
1037
1038 VkImageMemoryBarrier2 to_present_barrier{};
1039 to_present_barrier.sType = VK_STRUCTURE_TYPE_IMAGE_MEMORY_BARRIER_2;
1040 to_present_barrier.srcStageMask = VK_PIPELINE_STAGE_2_TRANSFER_BIT;
1041 to_present_barrier.srcAccessMask = VK_ACCESS_2_TRANSFER_READ_BIT;
1042 to_present_barrier.dstStageMask =
1043 capture_offscreen ? VK_PIPELINE_STAGE_2_FRAGMENT_SHADER_BIT
1044 : VK_PIPELINE_STAGE_2_NONE;
1045 to_present_barrier.dstAccessMask =
1046 capture_offscreen ? VK_ACCESS_2_SHADER_SAMPLED_READ_BIT
1047 : VK_ACCESS_2_NONE;
1048 to_present_barrier.oldLayout = VK_IMAGE_LAYOUT_TRANSFER_SRC_OPTIMAL;
1049 to_present_barrier.newLayout = source_layout;
1050 to_present_barrier.srcQueueFamilyIndex = VK_QUEUE_FAMILY_IGNORED;
1051 to_present_barrier.dstQueueFamilyIndex = VK_QUEUE_FAMILY_IGNORED;
1052 to_present_barrier.image = source_image;
1053 to_present_barrier.subresourceRange = to_transfer_barrier.subresourceRange;
1054
1055 VkDependencyInfo to_present_dependency{};
1056 to_present_dependency.sType = VK_STRUCTURE_TYPE_DEPENDENCY_INFO;
1057 to_present_dependency.imageMemoryBarrierCount = 1;
1058 to_present_dependency.pImageMemoryBarriers = &to_present_barrier;
1059 vkCmdPipelineBarrier2(command_buffer, &to_present_dependency);
1060
1061 if (vkEndCommandBuffer(command_buffer) != VK_SUCCESS) {
1062 throw mxvk::Exception("captureSnapshotPixels failed to end copy command buffer");
1063 }
1064
1065 VkCommandBufferSubmitInfo command_submit_info{};
1066 command_submit_info.sType = VK_STRUCTURE_TYPE_COMMAND_BUFFER_SUBMIT_INFO;
1067 command_submit_info.commandBuffer = command_buffer;
1068
1069 VkSubmitInfo2 submit_info{};
1070 submit_info.sType = VK_STRUCTURE_TYPE_SUBMIT_INFO_2;
1071 submit_info.commandBufferInfoCount = 1;
1072 submit_info.pCommandBufferInfos = &command_submit_info;
1073
1074 const VkResult submit_result = vkQueueSubmit2(graphics_queue, 1, &submit_info, copy_fence);
1075 if (submit_result != VK_SUCCESS) {
1076 throw mxvk::Exception(std::format("captureSnapshotPixels failed to submit copy command: {}", static_cast<int>(submit_result)));
1077 }
1078 const VkResult copy_wait_result = vkWaitForFences(device, 1, &copy_fence, VK_TRUE, UINT64_MAX);
1079 if (copy_wait_result != VK_SUCCESS) {
1080 throw mxvk::Exception(std::format("captureSnapshotPixels failed waiting for copy: {}", static_cast<int>(copy_wait_result)));
1081 }
1082
1083 void *mapped = nullptr;
1084 if (vkMapMemory(device, readback_memory, 0, image_bytes, 0, &mapped) != VK_SUCCESS) {
1085 throw mxvk::Exception("captureSnapshotPixels failed to map readback memory");
1086 }
1087
1088 const auto *src = static_cast<const std::uint8_t *>(mapped);
1089 rgba_pixels.resize(static_cast<std::size_t>(image_bytes));
1090 for (std::size_t i = 0; i < rgba_pixels.size(); i += 4U) {
1091 if (format_is_bgra) {
1092 rgba_pixels[i + 0U] = src[i + 2U];
1093 rgba_pixels[i + 1U] = src[i + 1U];
1094 rgba_pixels[i + 2U] = src[i + 0U];
1095 rgba_pixels[i + 3U] = src[i + 3U];
1096 } else {
1097 rgba_pixels[i + 0U] = src[i + 0U];
1098 rgba_pixels[i + 1U] = src[i + 1U];
1099 rgba_pixels[i + 2U] = src[i + 2U];
1100 rgba_pixels[i + 3U] = src[i + 3U];
1101 }
1102 }
1103 vkUnmapMemory(device, readback_memory);
1104 width = source_extent.width;
1105 height = source_extent.height;
1106 } catch (...) {
1107 cleanup();
1108 throw;
1109 }
1110
1111 cleanup();
1112 }
std::vector< std::vector< bool > > post_process_initialized
Definition mxvk.hpp:685
VkFormat swapchain_format
Definition mxvk.hpp:604
VkExtent2D swapchain_extent
Definition mxvk.hpp:606
uint32_t last_presented_image_index
Definition mxvk.hpp:624
std::vector< VkFence > image_fences
Definition mxvk.hpp:622
VkCommandPool command_pool
Definition mxvk.hpp:616
uint32_t latest_frame_readback_width
Definition mxvk.hpp:647
VkPhysicalDevice physical_device
Definition mxvk.hpp:595
std::vector< std::uint8_t > latest_frame_readback_rgba
Definition mxvk.hpp:646
VkExtent2D render_extent_override
Definition mxvk.hpp:607
VkQueue graphics_queue
Definition mxvk.hpp:599
uint32_t latest_frame_readback_height
Definition mxvk.hpp:648
VkExtent2D getRenderExtent() const noexcept
Get the active scene/post-processing extent.
Definition mxvk.cpp:1492
std::vector< std::vector< VkImage > > post_process_images
Definition mxvk.hpp:682
bool swapchain_supports_transfer_src
Definition mxvk.hpp:625

◆ clearTextQueue()

void mxvk::VK_Window::clearTextQueue ( )
inherited

Clear all queued text draw calls for the current frame.

Definition at line 4096 of file mxvk.cpp.

4096 {
4097 if (text_renderer) {
4098 text_renderer->clearQueue();
4099 }
4101 preview_text_renderer->clearQueue();
4102 }
4103 preview_text_queued = false;
4104 }
std::unique_ptr< VK_Text > preview_text_renderer
Definition mxvk.hpp:690
std::unique_ptr< VK_Text > text_renderer
Definition mxvk.hpp:689
bool preview_text_queued
Definition mxvk.hpp:695

◆ 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:192
VkQueue getGraphicsQueue() const noexcept
Get the graphics queue handle.
Definition mxvk.hpp:198
VkCommandPool getCommandPool() const noexcept
Get the command pool used for graphics/upload work.
Definition mxvk.hpp:201
VkPhysicalDevice getPhysicalDevice() const noexcept
Get the Vulkan physical device handle.
Definition mxvk.hpp:195

◆ createDevice()

void mxvk::VK_Window::createDevice ( )
protectedinherited

Create final device resources.

Definition at line 2086 of file mxvk.cpp.

2086 {
2087 std::cout << "mxvk: entering createDevice\n";
2088 if (device == VK_NULL_HANDLE) {
2089 std::cout << "vk: skipping createDevice because logical device is null\n";
2090 return;
2091 }
2092
2093 const bool sync_ready =
2094 std::ranges::all_of(image_available.begin(), image_available.end(), [](VkSemaphore semaphore) { return semaphore != VK_NULL_HANDLE; }) &&
2095 render_finished.size() == swapchain_images.size() &&
2096 std::ranges::all_of(render_finished.begin(), render_finished.end(), [](VkSemaphore semaphore) { return semaphore != VK_NULL_HANDLE; }) &&
2097 std::ranges::all_of(in_flight_fences.begin(), in_flight_fences.end(), [](VkFence fence) { return fence != VK_NULL_HANDLE; });
2098
2099 if (swapchain != VK_NULL_HANDLE && command_pool != VK_NULL_HANDLE && !command_buffers.empty() &&
2100 sync_ready && image_fences.size() == swapchain_images.size()) {
2101 std::cout << "vk: createDevice skipped because render resources are already initialized\n";
2102 return;
2103 }
2104
2105 std::cout << "vk: creating swapchain\n";
2106 if (!createSwapchain(VK_NULL_HANDLE)) {
2107 std::cout << "vk: createSwapchain failed\n";
2108 return;
2109 }
2110
2111 std::cout << "vk: creating render resources\n";
2112 if (!createRenderResources()) {
2113 std::cout << "vk: createRenderResources failed\n";
2114 return;
2115 }
2116
2117 std::cout << "vk: creating synchronization objects\n";
2118 if (!createSyncObjects()) {
2119 std::cout << "vk: createSyncObjects failed\n";
2120 return;
2121 }
2122
2123 std::cout << "mxvk: createDevice complete\n";
2124 }
VkSwapchainKHR swapchain
Definition mxvk.hpp:603
std::vector< VkCommandBuffer > command_buffers
Definition mxvk.hpp:617
std::vector< VkSemaphore > render_finished
Definition mxvk.hpp:620
std::array< VkSemaphore, max_frames_in_flight > image_available
Definition mxvk.hpp:619
std::array< VkFence, max_frames_in_flight > in_flight_fences
Definition mxvk.hpp:621

◆ createLogicalDevice()

void mxvk::VK_Window::createLogicalDevice ( )
protectedinherited

Create a logical Vulkan device from the selected physical device.

Definition at line 1955 of file mxvk.cpp.

1955 {
1956 std::cout << "mxvk: entering createLogicalDevice\n";
1957 if (physical_device == VK_NULL_HANDLE) {
1958 std::cout << "vk: cannot create logical device without a selected physical device\n";
1959 return;
1960 }
1961
1962 std::vector<uint32_t> queue_families{};
1963 queue_families.push_back(graphics_queue_family);
1965 queue_families.push_back(present_queue_family);
1966 }
1967
1968 constexpr float queue_priority = 1.0f;
1969 std::vector<VkDeviceQueueCreateInfo> queue_create_infos{};
1970 queue_create_infos.reserve(queue_families.size());
1971 for (const uint32_t family : queue_families) {
1972 VkDeviceQueueCreateInfo queue_create_info{};
1973 queue_create_info.sType = VK_STRUCTURE_TYPE_DEVICE_QUEUE_CREATE_INFO;
1974 queue_create_info.queueFamilyIndex = family;
1975 queue_create_info.queueCount = 1;
1976 queue_create_info.pQueuePriorities = &queue_priority;
1977 queue_create_infos.push_back(queue_create_info);
1978 }
1979 uint32_t device_extension_count = 0;
1980 vkEnumerateDeviceExtensionProperties(physical_device, nullptr, &device_extension_count, nullptr);
1981 std::vector<VkExtensionProperties> device_extensions(device_extension_count);
1982 if (device_extension_count > 0U) {
1983 vkEnumerateDeviceExtensionProperties(physical_device, nullptr, &device_extension_count, device_extensions.data());
1984 }
1985
1986 [[maybe_unused]] const auto has_device_extension = [&device_extensions](const char *extension_name) {
1987 return std::ranges::any_of(
1988 device_extensions,
1989 [extension_name](const VkExtensionProperties &ext) {
1990 return std::strcmp(ext.extensionName, extension_name) == 0;
1991 });
1992 };
1993
1994 std::vector<const char *> required_device_extensions = {VK_KHR_SWAPCHAIN_EXTENSION_NAME};
1995#if defined(MXVK_CUDA) && defined(__linux__)
1996 if (has_device_extension(VK_KHR_EXTERNAL_MEMORY_FD_EXTENSION_NAME)) {
1997 if (has_device_extension(VK_KHR_EXTERNAL_MEMORY_EXTENSION_NAME)) {
1998 required_device_extensions.push_back(VK_KHR_EXTERNAL_MEMORY_EXTENSION_NAME);
1999 }
2000 required_device_extensions.push_back(VK_KHR_EXTERNAL_MEMORY_FD_EXTENSION_NAME);
2001 std::cout << "vk: enabling external memory FD support for CUDA interop\n";
2002 } else {
2003 std::cout << "vk: external memory FD support unavailable; CUDA texture interop will fall back\n";
2004 }
2005#endif
2006
2007#if defined(MXVK_USE_MOLTENVK)
2008 const bool has_portability_subset = std::ranges::any_of(
2009 device_extensions,
2010 [](const VkExtensionProperties &ext) {
2011 return std::strcmp(ext.extensionName, VK_KHR_PORTABILITY_SUBSET_EXTENSION_NAME) == 0;
2012 });
2013
2014 if (has_portability_subset) {
2015 required_device_extensions.push_back(VK_KHR_PORTABILITY_SUBSET_EXTENSION_NAME);
2016 }
2017#endif
2018 VkPhysicalDeviceFeatures2 features2{};
2019 features2.sType = VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_FEATURES_2;
2020 VkPhysicalDeviceVulkan13Features supported_vulkan13_features{};
2021 supported_vulkan13_features.sType = VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_VULKAN_1_3_FEATURES;
2022 features2.pNext = &supported_vulkan13_features;
2023 vkGetPhysicalDeviceFeatures2(physical_device, &features2);
2024
2025 std::cout << std::format(
2026 "vk: feature support - synchronization2={}, dynamicRendering={}, shaderFloat64={}, fillModeNonSolid={}, samplerAnisotropy={}\n",
2027 supported_vulkan13_features.synchronization2 == VK_TRUE ? "true" : "false",
2028 supported_vulkan13_features.dynamicRendering == VK_TRUE ? "true" : "false",
2029 features2.features.shaderFloat64 == VK_TRUE ? "true" : "false",
2030 features2.features.fillModeNonSolid == VK_TRUE ? "true" : "false",
2031 features2.features.samplerAnisotropy == VK_TRUE ? "true" : "false");
2032
2033 if (supported_vulkan13_features.synchronization2 != VK_TRUE) {
2034 std::cout << "vk: synchronization2 is unsupported on selected physical device\n";
2035 return;
2036 }
2037 if (supported_vulkan13_features.dynamicRendering != VK_TRUE) {
2038 std::cout << "vk: dynamic rendering is unsupported on selected physical device\n";
2039 return;
2040 }
2041 VkPhysicalDeviceVulkan13Features vulkan13_features{};
2042 vulkan13_features.sType = VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_VULKAN_1_3_FEATURES;
2043 vulkan13_features.synchronization2 = VK_TRUE;
2044 vulkan13_features.dynamicRendering = VK_TRUE;
2045
2046 VkPhysicalDeviceFeatures2 enabled_features2{};
2047 enabled_features2.sType = VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_FEATURES_2;
2048 enabled_features2.features.shaderFloat64 = features2.features.shaderFloat64;
2049 enabled_features2.features.fillModeNonSolid = features2.features.fillModeNonSolid;
2050 enabled_features2.features.samplerAnisotropy = features2.features.samplerAnisotropy;
2051
2052 enabled_features2.pNext = &vulkan13_features;
2053
2054 VkDeviceCreateInfo create_info{};
2055 create_info.sType = VK_STRUCTURE_TYPE_DEVICE_CREATE_INFO;
2056 create_info.pNext = &enabled_features2;
2057 create_info.queueCreateInfoCount = static_cast<uint32_t>(queue_create_infos.size());
2058 create_info.pQueueCreateInfos = queue_create_infos.data();
2059 create_info.enabledExtensionCount = static_cast<uint32_t>(required_device_extensions.size());
2060 create_info.ppEnabledExtensionNames = required_device_extensions.data();
2061 create_info.enabledLayerCount = 0;
2062 create_info.ppEnabledLayerNames = nullptr;
2063 create_info.pEnabledFeatures = nullptr;
2064
2065 std::cout << "vk: creating logical device\n";
2066 if (vkCreateDevice(physical_device, &create_info, nullptr, &device) != VK_SUCCESS) {
2067 std::cout << "vk: logical device creation failed\n";
2068 device = VK_NULL_HANDLE;
2069 return;
2070 }
2071
2072 std::cout << "vk: loading device-level function pointers via volk\n";
2073 volkLoadDevice(device);
2074 if (vkQueueSubmit2 == nullptr || vkCmdBeginRendering == nullptr || vkCmdEndRendering == nullptr || vkCmdPipelineBarrier2 == nullptr) {
2075 std::cout << "vk: required dynamic rendering/synchronization function pointers are unavailable\n";
2076 vkDestroyDevice(device, nullptr);
2077 device = VK_NULL_HANDLE;
2078 return;
2079 }
2080 std::cout << "vk: retrieving graphics and present queues\n";
2081 vkGetDeviceQueue(device, graphics_queue_family, 0, &graphics_queue);
2082 vkGetDeviceQueue(device, present_queue_family, 0, &present_queue);
2083 std::cout << "mxvk: createLogicalDevice complete\n";
2084 }
uint32_t present_queue_family
Definition mxvk.hpp:598
VkQueue present_queue
Definition mxvk.hpp:600
uint32_t graphics_queue_family
Definition mxvk.hpp:597

◆ 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/4]

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 4608 of file mxvk.cpp.

4608 {
4609 if (device == VK_NULL_HANDLE) {
4610 throw mxvk::Exception("Cannot create sprite before Vulkan device initialization");
4611 }
4612 if (swapchain == VK_NULL_HANDLE || command_pool == VK_NULL_HANDLE) {
4613 createDevice();
4614 }
4615 if (swapchain == VK_NULL_HANDLE || command_pool == VK_NULL_HANDLE) {
4616 throw mxvk::Exception("Cannot create sprite before swapchain and command resources are available");
4617 }
4618
4619 if (sprite_descriptor_set_layout == VK_NULL_HANDLE) {
4620 createSpriteDescriptorSetLayout();
4621 }
4622
4623 auto sprite = std::make_unique<VK_Sprite>(device, physical_device, graphics_queue, command_pool);
4624 sprite->setPipelineCache(pipeline_cache);
4625 sprite->setDescriptorSetLayout(sprite_descriptor_set_layout);
4626 sprite->setColorAttachmentFormat(swapchain_format);
4627 sprite->setDepthAttachmentFormat(depth_format);
4628
4629 if (!vertexShaderPath.empty()) {
4630 sprite->setVertexShaderPath(vertexShaderPath);
4631 }
4632
4633 sprite->loadSprite(pngPath, fragmentShaderPath);
4634
4635 VK_Sprite *const sprite_ptr = sprite.get();
4636 sprites.push_back(std::move(sprite));
4637 sprite_state_dirty = true;
4638 return sprite_ptr;
4639 }
VkDescriptorSetLayout sprite_descriptor_set_layout
Definition mxvk.hpp:663
VkFormat depth_format
Definition mxvk.hpp:605
void createDevice()
Create final device resources.
Definition mxvk.cpp:2086
VkPipelineCache pipeline_cache
Definition mxvk.hpp:601

◆ createSprite() [2/4]

VK_Sprite * mxvk::VK_Window::createSprite ( int width,
int height,
const std::string & vertexShaderPath,
const std::string & fragmentShaderPath,
uint32_t spectrumBinCount,
uint32_t spectrumHistoryLayerCount )
inherited

Create a blank sprite with live FFT and FFT-history descriptors.

Parameters
widthTexture width in pixels.
heightTexture height in pixels.
vertexShaderPathOptional custom vertex shader SPIR-V path.
fragmentShaderPathOptional custom fragment shader SPIR-V path.
spectrumBinCountBinding-3 1-D spectrum size.
spectrumHistoryLayerCountRequested binding-4 history depth.
Returns
Non-owning pointer to the created sprite.

Definition at line 4685 of file mxvk.cpp.

4689 {
4690 if (width <= 0 || height <= 0) {
4691 throw mxvk::Exception("Sprite dimensions must be positive");
4692 }
4693 if (device == VK_NULL_HANDLE) {
4694 throw mxvk::Exception("Cannot create sprite before Vulkan device initialization");
4695 }
4696 if (swapchain == VK_NULL_HANDLE || command_pool == VK_NULL_HANDLE) {
4697 createDevice();
4698 }
4699 if (swapchain == VK_NULL_HANDLE || command_pool == VK_NULL_HANDLE) {
4700 throw mxvk::Exception("Cannot create sprite before swapchain and command resources are available");
4701 }
4702
4703 if (sprite_descriptor_set_layout == VK_NULL_HANDLE) {
4704 createSpriteDescriptorSetLayout();
4705 }
4706
4707 auto sprite = std::make_unique<VK_Sprite>(device, physical_device, graphics_queue, command_pool);
4708 sprite->setPipelineCache(pipeline_cache);
4709 sprite->setDescriptorSetLayout(sprite_descriptor_set_layout);
4710 sprite->setColorAttachmentFormat(swapchain_format);
4711 sprite->setDepthAttachmentFormat(depth_format);
4712 if (!fragmentShaderPath.empty()) {
4713 sprite->enableExtendedUBO();
4714 }
4715 if (spectrumBinCount > 0) {
4716 sprite->enableSpectrumTexture(spectrumBinCount);
4717 }
4718 if (spectrumHistoryLayerCount > 0) {
4719 if (spectrumBinCount == 0) {
4720 throw mxvk::Exception(
4721 "Spectrum history requires a positive spectrum bin count");
4722 }
4723 sprite->enableSpectrumHistoryTexture(
4724 spectrumBinCount, spectrumHistoryLayerCount);
4725 }
4726
4727 sprite->createEmptySprite(width, height, vertexShaderPath, fragmentShaderPath);
4728
4729 VK_Sprite *const sprite_ptr = sprite.get();
4730 sprites.push_back(std::move(sprite));
4731 sprite_state_dirty = true;
4732 return sprite_ptr;
4733 }

◆ createSprite() [3/4]

VK_Sprite * mxvk::VK_Window::createSprite ( int width,
int height,
const std::string & vertexShaderPath = "",
const std::string & fragmentShaderPath = "",
uint32_t spectrumBinCount = 0 )
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.
spectrumBinCountOptional binding-3 1-D spectrum size to create before the pipeline.
Returns
Non-owning pointer to the created sprite.

Definition at line 4680 of file mxvk.cpp.

4680 {
4681 return createSprite(width, height, vertexShaderPath, fragmentShaderPath,
4682 spectrumBinCount, 0);
4683 }

◆ createSprite() [4/4]

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 4641 of file mxvk.cpp.

4641 {
4642 if (surface == nullptr) {
4643 throw mxvk::Exception("Cannot create sprite from a null SDL surface");
4644 }
4645 if (device == VK_NULL_HANDLE) {
4646 throw mxvk::Exception("Cannot create sprite before Vulkan device initialization");
4647 }
4648 if (swapchain == VK_NULL_HANDLE || command_pool == VK_NULL_HANDLE) {
4649 createDevice();
4650 }
4651 if (swapchain == VK_NULL_HANDLE || command_pool == VK_NULL_HANDLE) {
4652 throw mxvk::Exception("Cannot create sprite before swapchain and command resources are available");
4653 }
4654
4655 if (sprite_descriptor_set_layout == VK_NULL_HANDLE) {
4656 createSpriteDescriptorSetLayout();
4657 }
4658
4659 auto sprite = std::make_unique<VK_Sprite>(device, physical_device, graphics_queue, command_pool);
4660 sprite->setPipelineCache(pipeline_cache);
4661 sprite->setDescriptorSetLayout(sprite_descriptor_set_layout);
4662 sprite->setColorAttachmentFormat(swapchain_format);
4663 sprite->setDepthAttachmentFormat(depth_format);
4664
4665 if (!vertexShaderPath.empty()) {
4666 sprite->setVertexShaderPath(vertexShaderPath);
4667 }
4668 if (!fragmentShaderPath.empty()) {
4669 sprite->enableExtendedUBO();
4670 }
4671
4672 sprite->loadSprite(surface, fragmentShaderPath);
4673
4674 VK_Sprite *const sprite_ptr = sprite.get();
4675 sprites.push_back(std::move(sprite));
4676 sprite_state_dirty = true;
4677 return sprite_ptr;
4678 }
VkSurfaceKHR surface
Definition mxvk.hpp:594

◆ 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 4735 of file mxvk.cpp.

4735 {
4736 if (device == VK_NULL_HANDLE) {
4737 throw mxvk::Exception("Cannot create 3D sprite before Vulkan device initialization");
4738 }
4739 if (swapchain == VK_NULL_HANDLE || command_pool == VK_NULL_HANDLE) {
4740 createDevice();
4741 }
4742 if (swapchain == VK_NULL_HANDLE || command_pool == VK_NULL_HANDLE) {
4743 throw mxvk::Exception("Cannot create 3D sprite before swapchain and command resources are available");
4744 }
4745
4746 const std::string vertPath = vertexShaderPath.empty()
4747 ? resolveRuntimeShaderPath("sprite3d.vert.spv", MXVK_SPRITE3D_SHADER_DIR)
4748 : vertexShaderPath;
4749 const std::string fragPath = fragmentShaderPath.empty()
4750 ? resolveRuntimeShaderPath("sprite3d.frag.spv", MXVK_SPRITE3D_SHADER_DIR)
4751 : fragmentShaderPath;
4752
4753 auto sprite = std::make_unique<VK_Sprite3D>();
4754 sprite->load(this, pngPath, vertPath, fragPath);
4755
4756 VK_Sprite3D *const sprite_ptr = sprite.get();
4757 sprites3d.push_back(std::move(sprite));
4758 return sprite_ptr;
4759 }
std::vector< std::unique_ptr< VK_Sprite3D > > sprites3d
Definition mxvk.hpp:662

◆ 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 4761 of file mxvk.cpp.

4761 {
4762 if (surface == nullptr) {
4763 throw mxvk::Exception("Cannot create 3D sprite from a null SDL surface");
4764 }
4765 if (device == VK_NULL_HANDLE) {
4766 throw mxvk::Exception("Cannot create 3D sprite before Vulkan device initialization");
4767 }
4768 if (swapchain == VK_NULL_HANDLE || command_pool == VK_NULL_HANDLE) {
4769 createDevice();
4770 }
4771 if (swapchain == VK_NULL_HANDLE || command_pool == VK_NULL_HANDLE) {
4772 throw mxvk::Exception("Cannot create 3D sprite before swapchain and command resources are available");
4773 }
4774
4775 const std::string vertPath = vertexShaderPath.empty()
4776 ? resolveRuntimeShaderPath("sprite3d.vert.spv", MXVK_SPRITE3D_SHADER_DIR)
4777 : vertexShaderPath;
4778 const std::string fragPath = fragmentShaderPath.empty()
4779 ? resolveRuntimeShaderPath("sprite3d.frag.spv", MXVK_SPRITE3D_SHADER_DIR)
4780 : fragmentShaderPath;
4781
4782 auto sprite = std::make_unique<VK_Sprite3D>();
4783 sprite->load(this, surface, vertPath, fragPath);
4784
4785 VK_Sprite3D *const sprite_ptr = sprite.get();
4786 sprites3d.push_back(std::move(sprite));
4787 return sprite_ptr;
4788 }

◆ 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 }

◆ destroyFrameReadbackResources()

void mxvk::VK_Window::destroyFrameReadbackResources ( )
protectedinherited

Definition at line 1301 of file mxvk.cpp.

1301 {
1302 for (FrameReadbackSlot &slot : frame_readback_slots) {
1303 if (device != VK_NULL_HANDLE && slot.mapped != nullptr &&
1304 slot.memory != VK_NULL_HANDLE) {
1305 vkUnmapMemory(device, slot.memory);
1306 }
1307 slot.mapped = nullptr;
1308 if (device != VK_NULL_HANDLE && slot.buffer != VK_NULL_HANDLE) {
1309 vkDestroyBuffer(device, slot.buffer, nullptr);
1310 }
1311 slot.buffer = VK_NULL_HANDLE;
1312 if (device != VK_NULL_HANDLE && slot.memory != VK_NULL_HANDLE) {
1313 vkFreeMemory(device, slot.memory, nullptr);
1314 }
1315 slot.memory = VK_NULL_HANDLE;
1316 slot.size = 0;
1317 slot.width = 0;
1318 slot.height = 0;
1319 slot.format = VK_FORMAT_UNDEFINED;
1320 slot.pending = false;
1321 }
1325 }
std::array< FrameReadbackSlot, max_frames_in_flight > frame_readback_slots
Definition mxvk.hpp:645

◆ detachPostProcessingShader()

void mxvk::VK_Window::detachPostProcessingShader ( )
inherited

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

Definition at line 1592 of file mxvk.cpp.

1592 {
1593 if (device != VK_NULL_HANDLE && !post_process_sprites.empty()) {
1594 vkDeviceWaitIdle(device);
1595 }
1596
1597 post_process_sprite = nullptr;
1598 destroyPostProcessTargets();
1602
1603 if (!owned_post_process_sprites.empty()) {
1604 const std::vector<VK_Sprite *> sprites_to_remove = owned_post_process_sprites;
1605 sprites.erase(
1606 std::remove_if(
1607 sprites.begin(),
1608 sprites.end(),
1609 [&sprites_to_remove](const std::unique_ptr<VK_Sprite> &sprite) {
1610 return std::ranges::find(sprites_to_remove, sprite.get()) != sprites_to_remove.end();
1611 }),
1612 sprites.end());
1613 sprite_state_dirty = true;
1614 }
1615 owned_post_process_sprite = nullptr;
1617 post_process_sprites.clear();
1622 }

◆ dispatchFrameReadback()

void mxvk::VK_Window::dispatchFrameReadback ( FrameReadbackSlot & slot)
protectedinherited

Definition at line 1122 of file mxvk.cpp.

1122 {
1123 if (!slot.pending) {
1124 return;
1125 }
1126 if (slot.mapped == nullptr || slot.width == 0U || slot.height == 0U) {
1127 slot.pending = false;
1128 return;
1129 }
1130
1131 const auto *source = static_cast<const std::uint8_t *>(slot.mapped);
1132 const std::size_t pixel_bytes =
1133 static_cast<std::size_t>(slot.width) * slot.height * 4U;
1134 latest_frame_readback_rgba.resize(pixel_bytes);
1135 const bool format_is_bgra =
1136 slot.format == VK_FORMAT_B8G8R8A8_UNORM ||
1137 slot.format == VK_FORMAT_B8G8R8A8_SRGB;
1138 for (std::size_t offset = 0; offset < pixel_bytes; offset += 4U) {
1139 if (format_is_bgra) {
1140 latest_frame_readback_rgba[offset + 0U] = source[offset + 2U];
1141 latest_frame_readback_rgba[offset + 1U] = source[offset + 1U];
1142 latest_frame_readback_rgba[offset + 2U] = source[offset + 0U];
1143 latest_frame_readback_rgba[offset + 3U] = source[offset + 3U];
1144 } else {
1145 latest_frame_readback_rgba[offset + 0U] = source[offset + 0U];
1146 latest_frame_readback_rgba[offset + 1U] = source[offset + 1U];
1147 latest_frame_readback_rgba[offset + 2U] = source[offset + 2U];
1148 latest_frame_readback_rgba[offset + 3U] = source[offset + 3U];
1149 }
1150 }
1151
1152 latest_frame_readback_width = slot.width;
1153 latest_frame_readback_height = slot.height;
1154 slot.pending = false;
1157 }
virtual void onFrameReadback(std::vector< std::uint8_t > &rgba_pixels, uint32_t width, uint32_t height)
Receive one tightly packed RGBA8 copy of the rendered frame.
Definition mxvk.cpp:1114

◆ enablePostProcessing()

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

Definition at line 1668 of file mxvk.cpp.

1668 {
1669 if (owned_post_process_sprite != nullptr && sprite != owned_post_process_sprite) {
1670 std::vector<VK_Sprite *> sprites_to_remove = owned_post_process_sprites;
1671 if (sprites_to_remove.empty()) {
1672 sprites_to_remove.push_back(owned_post_process_sprite);
1673 }
1674 sprites.erase(
1675 std::remove_if(
1676 sprites.begin(),
1677 sprites.end(),
1678 [&sprites_to_remove](const std::unique_ptr<VK_Sprite> &existing_sprite) {
1679 return std::ranges::find(sprites_to_remove, existing_sprite.get()) != sprites_to_remove.end();
1680 }),
1681 sprites.end());
1682 owned_post_process_sprite = nullptr;
1684 sprite_state_dirty = true;
1685 }
1686 post_process_sprite = sprite;
1687 post_process_sprites = sprite == nullptr ? std::vector<VK_Sprite *>{} : std::vector<VK_Sprite *>{sprite};
1691 if (device != VK_NULL_HANDLE && !swapchain_images.empty()) {
1692 createPostProcessTargets();
1693 }
1694 }

◆ ensureFrameReadbackResources()

void mxvk::VK_Window::ensureFrameReadbackResources ( )
protectedinherited

Definition at line 1183 of file mxvk.cpp.

1183 {
1185 return;
1186 }
1188 throw mxvk::Exception("frame readback requires swapchain transfer-source support");
1189 }
1190
1191 const VkExtent2D readback_extent = getRenderExtent();
1192 const VkDeviceSize required_size =
1193 static_cast<VkDeviceSize>(readback_extent.width) *
1194 readback_extent.height * 4U;
1195 if (required_size == 0U) {
1196 throw mxvk::Exception("frame readback cannot use an empty swapchain extent");
1197 }
1198 const bool resources_ready = std::ranges::all_of(
1199 frame_readback_slots, [&](const FrameReadbackSlot &slot) {
1200 return slot.buffer != VK_NULL_HANDLE && slot.mapped != nullptr &&
1201 slot.size >= required_size;
1202 });
1203 if (resources_ready) {
1204 return;
1205 }
1206
1207 if (device != VK_NULL_HANDLE) {
1209 vkDeviceWaitIdle(device);
1210 }
1212
1213 VkBufferCreateInfo buffer_info{};
1214 buffer_info.sType = VK_STRUCTURE_TYPE_BUFFER_CREATE_INFO;
1215 buffer_info.size = required_size;
1216 buffer_info.usage = VK_BUFFER_USAGE_TRANSFER_DST_BIT;
1217 buffer_info.sharingMode = VK_SHARING_MODE_EXCLUSIVE;
1218 VkPhysicalDeviceMemoryProperties memory_properties{};
1219 vkGetPhysicalDeviceMemoryProperties(physical_device, &memory_properties);
1220 constexpr VkMemoryPropertyFlags REQUIRED_PROPERTIES =
1221 VK_MEMORY_PROPERTY_HOST_VISIBLE_BIT | VK_MEMORY_PROPERTY_HOST_COHERENT_BIT;
1222 constexpr VkMemoryPropertyFlags PREFERRED_PROPERTIES =
1223 REQUIRED_PROPERTIES | VK_MEMORY_PROPERTY_HOST_CACHED_BIT;
1224 bool memory_type_logged = false;
1225 try {
1226 for (FrameReadbackSlot &slot : frame_readback_slots) {
1227 if (vkCreateBuffer(device, &buffer_info, nullptr, &slot.buffer) !=
1228 VK_SUCCESS) {
1229 throw mxvk::Exception(
1230 "failed to create pipelined frame-readback buffer");
1231 }
1232
1233 VkMemoryRequirements memory_requirements{};
1234 vkGetBufferMemoryRequirements(device, slot.buffer,
1235 &memory_requirements);
1236 uint32_t memory_type_index = invalid_queue_index;
1237 for (VkMemoryPropertyFlags properties :
1238 {PREFERRED_PROPERTIES, REQUIRED_PROPERTIES}) {
1239 for (uint32_t index = 0;
1240 index < memory_properties.memoryTypeCount; ++index) {
1241 const bool type_matches =
1242 (memory_requirements.memoryTypeBits & (1U << index)) != 0U;
1243 const bool properties_match =
1244 (memory_properties.memoryTypes[index].propertyFlags &
1245 properties) == properties;
1246 if (type_matches && properties_match) {
1247 memory_type_index = index;
1248 break;
1249 }
1250 }
1251 if (memory_type_index != invalid_queue_index) {
1252 break;
1253 }
1254 }
1255 if (memory_type_index == invalid_queue_index) {
1256 throw mxvk::Exception(
1257 "failed to find host-visible frame-readback memory");
1258 }
1259 if (!memory_type_logged) {
1260 const VkMemoryPropertyFlags selected_properties =
1261 memory_properties.memoryTypes[memory_type_index]
1262 .propertyFlags;
1263 std::cout << std::format(
1264 "mxvk: frame readback memory type {} ({})\n",
1265 memory_type_index,
1266 (selected_properties & VK_MEMORY_PROPERTY_HOST_CACHED_BIT) !=
1267 0U
1268 ? "host cached"
1269 : "host coherent fallback");
1270 memory_type_logged = true;
1271 }
1272
1273 VkMemoryAllocateInfo allocation_info{};
1274 allocation_info.sType = VK_STRUCTURE_TYPE_MEMORY_ALLOCATE_INFO;
1275 allocation_info.allocationSize = memory_requirements.size;
1276 allocation_info.memoryTypeIndex = memory_type_index;
1277 if (vkAllocateMemory(device, &allocation_info, nullptr,
1278 &slot.memory) != VK_SUCCESS) {
1279 throw mxvk::Exception(
1280 "failed to allocate frame-readback memory");
1281 }
1282 if (vkBindBufferMemory(device, slot.buffer, slot.memory, 0) !=
1283 VK_SUCCESS) {
1284 throw mxvk::Exception("failed to bind frame-readback memory");
1285 }
1286 if (vkMapMemory(device, slot.memory, 0, required_size, 0,
1287 &slot.mapped) != VK_SUCCESS) {
1288 throw mxvk::Exception("failed to map frame-readback memory");
1289 }
1290 slot.size = required_size;
1291 }
1292 } catch (...) {
1294 throw;
1295 }
1299 }
void flushFrameReadbacks()
Wait for and deliver all pending pipelined frame readbacks.
Definition mxvk.cpp:1159
bool frame_readback_enabled
Definition mxvk.hpp:644
void destroyFrameReadbackResources()
Definition mxvk.cpp:1301

◆ 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 1847 of file mxvk.cpp.

1847 {
1848 const auto sync_ready = [this]() {
1849 return std::ranges::all_of(image_available.begin(), image_available.end(), [](VkSemaphore semaphore) { return semaphore != VK_NULL_HANDLE; }) &&
1850 render_finished.size() == swapchain_images.size() &&
1851 std::ranges::all_of(render_finished.begin(), render_finished.end(), [](VkSemaphore semaphore) { return semaphore != VK_NULL_HANDLE; }) &&
1852 std::ranges::all_of(in_flight_fences.begin(), in_flight_fences.end(), [](VkFence fence) { return fence != VK_NULL_HANDLE; });
1853 };
1854
1855 if (device == VK_NULL_HANDLE) {
1856 return false;
1857 }
1858
1859 if (swapchain == VK_NULL_HANDLE || command_pool == VK_NULL_HANDLE || command_buffers.empty() || !sync_ready() ||
1860 image_fences.size() != swapchain_images.size()) {
1861 createDevice();
1862 }
1863
1864 return (swapchain != VK_NULL_HANDLE && command_pool != VK_NULL_HANDLE && !command_buffers.empty() && sync_ready() &&
1865 image_fences.size() == swapchain_images.size());
1866 }

◆ event()

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

Handle one SDL event.

Parameters
eSDL event to process.

Reimplemented from mxvk::VK_Window.

Definition at line 141 of file moon.cpp.

141 {
142 if (e.type == SDL_EVENT_KEY_DOWN && e.key.key == SDLK_ESCAPE) {
143 exit();
144 return;
145 }
146
147 if (e.type == SDL_EVENT_KEY_DOWN && e.key.key == SDLK_SPACE) {
148 if (!e.key.repeat) {
149 auto_spin_enabled = !auto_spin_enabled;
150 }
151 return;
152 }
153
154 if (e.type == SDL_EVENT_MOUSE_BUTTON_DOWN && e.button.button == SDL_BUTTON_LEFT) {
155 mouse_dragging = true;
156 last_mouse_x = static_cast<int>(e.button.x);
157 last_mouse_y = static_cast<int>(e.button.y);
158 return;
159 }
160
161 if (e.type == SDL_EVENT_MOUSE_BUTTON_UP && e.button.button == SDL_BUTTON_LEFT) {
162 mouse_dragging = false;
163 return;
164 }
165
166 if (e.type == SDL_EVENT_MOUSE_MOTION && mouse_dragging) {
167 const int x = static_cast<int>(e.motion.x);
168 const int y = static_cast<int>(e.motion.y);
169 const int delta_x = x - last_mouse_x;
170 const int delta_y = y - last_mouse_y;
171
172 yaw_degrees += static_cast<float>(delta_x) * mouse_sensitivity;
173 pitch_degrees += static_cast<float>(delta_y) * mouse_sensitivity;
174 pitch_degrees = std::clamp(pitch_degrees, -80.0f, 80.0f);
175
176 last_mouse_x = x;
177 last_mouse_y = y;
178 return;
179 }
180
181 if (e.type == SDL_EVENT_MOUSE_WHEEL) {
182 const float delta = (e.wheel.y != 0.0f) ? e.wheel.y : static_cast<float>(e.wheel.integer_y);
183 camera_distance -= delta * 0.45f;
184 camera_distance = std::clamp(camera_distance, 1.8f, 12.0f);
185 return;
186 }
187 }
void exit()
Request loop termination.
Definition mxvk.cpp:1429

◆ exit()

void mxvk::VK_Window::exit ( )
protectedinherited

Request loop termination.

Definition at line 1429 of file mxvk.cpp.

1429 {
1430 active = false;
1431 }

◆ flushFrameReadbacks()

void mxvk::VK_Window::flushFrameReadbacks ( )
protectedinherited

Wait for and deliver all pending pipelined frame readbacks.

Derived recorders should call this before closing their output writer.

Definition at line 1159 of file mxvk.cpp.

1159 {
1160 if (device == VK_NULL_HANDLE) {
1161 return;
1162 }
1163 const bool has_pending = std::ranges::any_of(
1165 [](const FrameReadbackSlot &slot) { return slot.pending; });
1166 if (!has_pending) {
1167 return;
1168 }
1169
1170 const VkResult wait_result = vkDeviceWaitIdle(device);
1171 if (wait_result != VK_SUCCESS) {
1172 throw mxvk::Exception(std::format(
1173 "frame readback flush failed waiting for the device (VkResult={})",
1174 static_cast<int>(wait_result)));
1175 }
1176 for (uint32_t offset = 0; offset < max_frames_in_flight; ++offset) {
1177 const uint32_t slot_index =
1178 (current_frame + offset) % max_frames_in_flight;
1180 }
1181 }
void dispatchFrameReadback(FrameReadbackSlot &slot)
Definition mxvk.cpp:1122
uint32_t current_frame
Definition mxvk.hpp:623

◆ getCommandPool()

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

Get the command pool used for graphics/upload work.

Definition at line 201 of file mxvk.hpp.

201{ return command_pool; }

◆ getDepthFormat()

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

Get the depth format used for dynamic rendering attachments.

Definition at line 224 of file mxvk.hpp.

224{ return depth_format; }

◆ getDevice()

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

Get the Vulkan logical device handle.

Definition at line 192 of file mxvk.hpp.

192{ return device; }

◆ getGraphicsQueue()

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

Get the graphics queue handle.

Definition at line 198 of file mxvk.hpp.

198{ return graphics_queue; }

◆ getPhysicalDevice()

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

Get the Vulkan physical device handle.

Definition at line 195 of file mxvk.hpp.

195{ return physical_device; }

◆ getPipelineCache()

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

Get the persistent Vulkan pipeline cache used by framework pipelines.

Definition at line 204 of file mxvk.hpp.

204{ return pipeline_cache; }

◆ getRenderExtent()

VkExtent2D mxvk::VK_Window::getRenderExtent ( ) const
nodiscardnoexceptinherited

Get the active scene/post-processing extent.

Definition at line 1492 of file mxvk.cpp.

1492 {
1493 return render_extent_override.width > 0U &&
1494 render_extent_override.height > 0U
1497 }

◆ getSDLWindow()

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

Get the underlying SDL window handle.

Definition at line 189 of file mxvk.hpp.

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

◆ getSwapchainExtent()

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

Get the current swapchain extent.

Definition at line 210 of file mxvk.hpp.

210{ return swapchain_extent; }

◆ getSwapchainFormat()

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

Get the swapchain color format.

Definition at line 207 of file mxvk.hpp.

207{ return swapchain_format; }

◆ getSwapchainImageCount()

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

Get the number of swapchain images currently allocated.

Definition at line 227 of file mxvk.hpp.

227{ 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 4142 of file mxvk.cpp.

4142 {
4143 if (!font_configured) {
4144 throw mxvk::Exception("getTextDimensions requires setFont() to be called first");
4145 }
4146
4147 if (!text_renderer) {
4148 ensureTextRenderer();
4149 }
4150 if (!text_renderer) {
4151 width = 0;
4152 height = 0;
4153 return false;
4154 }
4155 return text_renderer->getTextDimensions(text, width, height);
4156 }
bool font_configured
Definition mxvk.hpp:696

◆ getTextDimensions() [2/3]

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

Definition at line 4174 of file mxvk.cpp.

4174 {
4175 if (!font) {
4176 width = 0;
4177 height = 0;
4178 return false;
4179 }
4180
4181 ensureTextRenderer(font.path(), font.size());
4182 if (!text_renderer) {
4183 width = 0;
4184 height = 0;
4185 return false;
4186 }
4187 return text_renderer->getTextDimensions(text, width, height, font);
4188 }

◆ getTextDimensions() [3/3]

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

Definition at line 4158 of file mxvk.cpp.

4158 {
4159 if (font == nullptr) {
4160 width = 0;
4161 height = 0;
4162 return false;
4163 }
4164
4165 ensureTextRenderer(resolveDefaultFontPath(), font_size);
4166 if (!text_renderer) {
4167 width = 0;
4168 height = 0;
4169 return false;
4170 }
4171 return text_renderer->getTextDimensions(text, width, height, font);
4172 }

◆ 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 334 of file mxvk.cpp.

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

◆ 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 1348 of file mxvk.cpp.

1348 {
1349 std::cout << std::format("mxvk: entering initWindow (title='{}', width={}, height={})\n", title, width, height);
1350 if (width <= 0 || height <= 0) {
1351 std::cerr << "mxvk: Window dimensions must be positive\n";
1352 return false;
1353 }
1354
1355 if (!sdl_initialized) {
1356 constexpr SDL_InitFlags sdlInitFlags = SDL_INIT_VIDEO | SDL_INIT_GAMEPAD;
1357 std::cout << "SDL3: initializing video/gamepad subsystems\n";
1358 if (!SDL_Init(sdlInitFlags)) {
1359 std::cerr << "mxvk: Error on SDL init: " << SDL_GetError() << "\n";
1360 return false;
1361 }
1362 sdl_initialized = true;
1363 SDL_SetGamepadEventsEnabled(true);
1364 SDL_SetJoystickEventsEnabled(true);
1365 std::cout << "SDL3: video/gamepad subsystems initialized\n";
1366 }
1367
1368 if (!vulkan_library_loaded) {
1369 std::cout << "SDL3: loading the Vulkan loader library\n";
1370 if (!SDL_Vulkan_LoadLibrary(nullptr)) {
1371 std::cerr << std::format(
1372 "mxvk: SDL_Vulkan_LoadLibrary failed: {}\n", SDL_GetError());
1373 SDL_QuitSubSystem(SDL_INIT_VIDEO | SDL_INIT_GAMEPAD);
1374 sdl_initialized = false;
1375 return false;
1376 }
1377 vulkan_library_loaded = true;
1378 }
1379
1380 const bool fullscreen = (flags & SDL_WINDOW_FULLSCREEN) != 0;
1381 const SDL_DisplayMode *fullscreen_mode = nullptr;
1382 if (fullscreen) {
1383 const SDL_DisplayID display = SDL_GetPrimaryDisplay();
1384 fullscreen_mode = display != 0 ? SDL_GetCurrentDisplayMode(display) : nullptr;
1385 if (fullscreen_mode != nullptr && fullscreen_mode->w > 0 && fullscreen_mode->h > 0) {
1386 width = fullscreen_mode->w;
1387 height = fullscreen_mode->h;
1388 std::cout << std::format("SDL3: using current display mode for fullscreen window: {}x{}\n", width, height);
1389 }
1390 }
1391
1392 std::cout << "SDL3: creating SDL window with Vulkan capability\n";
1393 SDL_Window *raw_window = SDL_CreateWindow(title.c_str(), width, height, flags);
1394 if (raw_window == nullptr) {
1395 std::cerr << std::format("Error creating window: {}\n", SDL_GetError());
1396 SDL_Vulkan_UnloadLibrary();
1397 vulkan_library_loaded = false;
1398 std::cout << "SDL3: rolling back SDL video/gamepad subsystems after window creation failure\n";
1399 SDL_QuitSubSystem(SDL_INIT_VIDEO | SDL_INIT_GAMEPAD);
1400 sdl_initialized = false;
1401 return false;
1402 }
1403
1404 window.reset(raw_window);
1405 if (fullscreen && fullscreen_mode != nullptr && !SDL_SetWindowFullscreenMode(window.get(), fullscreen_mode)) {
1406 std::cerr << std::format("mxvk: SDL_SetWindowFullscreenMode failed: {}\n", SDL_GetError());
1407 }
1408 std::cout << "SDL3: showing created window\n";
1409 SDL_ShowWindow(window.get());
1410 if (fullscreen && !SDL_SyncWindow(window.get())) {
1411 std::cerr << std::format("mxvk: SDL_SyncWindow failed after fullscreen show: {}\n", SDL_GetError());
1412 }
1413 if (fullscreen) {
1414 for (int attempt = 0; attempt < 50; ++attempt) {
1415 int pixel_width = 0;
1416 int pixel_height = 0;
1417 SDL_GetWindowSizeInPixels(window.get(), &pixel_width, &pixel_height);
1418 if (pixel_width == width && pixel_height == height) {
1419 break;
1420 }
1421 SDL_PumpEvents();
1422 SDL_Delay(10);
1423 }
1424 }
1425 std::cout << "mxvk: initWindow complete\n";
1426 return true;
1427 }
bool vulkan_library_loaded
Definition mxvk.hpp:628
bool sdl_initialized
Definition mxvk.hpp:627

◆ 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 }

◆ loop()

void mxvk::VK_Window::loop ( )
inherited

Run the main event/render loop.

Definition at line 627 of file mxvk.cpp.

627 {
628 SDL_Event e;
629 active = true;
630 while (active) {
631 while (SDL_PollEvent(&e)) {
632 switch (e.type) {
633 case SDL_EVENT_QUIT:
634 active = false;
635 break;
636 case SDL_EVENT_WINDOW_PIXEL_SIZE_CHANGED:
637 if (window != nullptr) {
638 framebuffer_resized = true;
639 last_resize_event_ms = SDL_GetTicks();
641 }
642 break;
643 case SDL_EVENT_KEY_DOWN:
644 if ((e.key.key == SDLK_F12 || e.key.scancode == SDL_SCANCODE_F12) && !e.key.repeat) {
645 toggleFpsCounter();
646 }
647 if (screenshot_enabled &&
648 (e.key.key == SDLK_F10 || e.key.scancode == SDL_SCANCODE_F10) &&
649 !e.key.repeat) {
650 try {
651 saveScreenshot();
652 } catch (const mxvk::Exception &ex) {
653 std::cerr << std::format("mxvk: screenshot failed: {}\n", ex.text());
654 } catch (const std::exception &ex) {
655 std::cerr << std::format("mxvk: screenshot failed: {}\n", ex.what());
656 }
657 continue;
658 }
659 break;
660 default:
661 break;
662 }
663 event(e);
664 }
666 const uint64_t now_ms = SDL_GetTicks();
670 proc();
671 render();
672 SDL_Delay(1);
673 continue;
674 }
675 recreateSwapchain();
676 }
677 proc();
678 render();
679 maybeTrimMemory();
680 }
681 }
std::string text() const
bool force_swapchain_recreate
Definition mxvk.hpp:655
virtual void event(SDL_Event &e)
Handle one SDL event.
Definition mxvk.cpp:602
static constexpr uint64_t resize_settle_delay_ms
Definition mxvk.hpp:657
bool screenshot_enabled
Definition mxvk.hpp:630
bool framebuffer_resized
Definition mxvk.hpp:654
virtual void render()
Render one frame.
Definition mxvk.cpp:805
virtual void proc()
Execute one processing/update step.
Definition mxvk.cpp:1327
uint64_t last_resize_event_ms
Definition mxvk.hpp:656

◆ 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 1447 of file mxvk.cpp.

1449 {}

◆ onFrameReadback()

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

Receive one tightly packed RGBA8 copy of the rendered frame.

Parameters
rgba_pixelsFrame pixels in top-to-bottom row order.
widthFrame width in pixels.
heightFrame height in pixels.

Definition at line 1114 of file mxvk.cpp.

1116 {
1117 }

◆ onFrameReadbackScheduled()

void mxvk::VK_Window::onFrameReadbackScheduled ( )
protectedvirtualinherited

Called after a presented frame has scheduled a readback.

Derived classes can use this hook to queue frame-specific metadata that must remain associated with the later onFrameReadback() callback.

Definition at line 1119 of file mxvk.cpp.

1119 {
1120 }

◆ 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 1437 of file mxvk.cpp.

1437{}

◆ onRecordCustomRendering()

void example::MoonWindow::onRecordCustomRendering ( VkCommandBuffer cmd,
uint32_t image_index )
inlineoverridevirtual

Optional hook for derived classes to record extra draw commands.

This callback is invoked inside the dynamic-rendering scope started by the window, after viewport/scissor are configured and before rendering ends.

Parameters
cmdActive command buffer in recording state.
image_indexCurrent swapchain image index.

Reimplemented from mxvk::VK_Window.

Definition at line 199 of file moon.cpp.

199 {
200 const auto now = std::chrono::steady_clock::now();
201 const float elapsed_seconds = std::chrono::duration<float>(now - start_time).count();
202 const float delta_seconds = std::chrono::duration<float>(now - last_frame_time).count();
203 last_frame_time = now;
204 if (auto_spin_enabled) {
205 auto_spin_radians += delta_seconds * auto_spin_speed;
206 }
207
208 const VkExtent2D extent = getSwapchainExtent();
209 const float aspect = (extent.height > 0U)
210 ? static_cast<float>(extent.width) / static_cast<float>(extent.height)
211 : 1.0f;
212
213 glm::mat4 moon_rotation = glm::rotate(glm::mat4(1.0f), glm::radians(pitch_degrees), glm::vec3(1.0f, 0.0f, 0.0f));
214 moon_rotation = glm::rotate(moon_rotation, glm::radians(yaw_degrees), glm::vec3(0.0f, 1.0f, 0.0f));
215 moon_rotation = glm::rotate(moon_rotation, auto_spin_radians, glm::vec3(0.0f, 1.0f, 0.0f));
216
217 mxvk::UniformBufferObject ubo{};
218 ubo.model = moon_rotation;
219 ubo.model = glm::scale(ubo.model, glm::vec3(model.modelRenderScale()));
220 ubo.model = glm::translate(ubo.model, model.modelCenterOffset());
221 ubo.view = glm::lookAt(glm::vec3(0.0f, 0.1f, camera_distance), glm::vec3(0.0f), glm::vec3(0.0f, 1.0f, 0.0f));
222 ubo.proj = glm::perspective(glm::radians(45.0f), aspect, 0.1f, 100.0f);
223 ubo.proj[1][1] *= -1.0f;
224 ubo.fx = glm::vec4(elapsed_seconds, 0.0f, 0.0f, 0.37f);
225
226 model.updateUBO(imageIndex, ubo);
227 model.render(cmd, imageIndex, false);
228
229 for (size_t i = 0; i < pyramids.size(); ++i) {
230 mxvk::UniformBufferObject pyramid_ubo = ubo;
231 pyramid_ubo.model = pyramidTransform(moon_rotation, pyramids[i], pyramid_placements[i]);
232 pyramids[i].updateUBO(imageIndex, pyramid_ubo);
233 pyramids[i].render(cmd, imageIndex, false);
234 }
235
236 if (star_sprite != nullptr) {
237 updateStars(elapsed_seconds);
238 star_sprite->updateCamera(imageIndex, ubo.view, ubo.proj);
239 for (const StarParticle &star : stars) {
240 const float twinkle = 0.72f + 0.28f * std::sin((elapsed_seconds * star.twinkle_speed) + star.twinkle_phase);
241 const float depth_fade = std::clamp((-star.position.z - 8.0f) / 42.0f, 0.2f, 1.0f);
242 const float alpha = std::clamp(star.brightness * twinkle * depth_fade, 0.08f, 0.95f);
243 const float size = star.size * 1.8f * (0.72f + (1.0f - depth_fade) * 0.38f) * twinkle;
244 star_sprite->drawSprite(star.position, glm::vec2(size), glm::vec4(star.color, alpha));
245 }
246 star_sprite->render(cmd, imageIndex);
247 star_sprite->clearQueue();
248 }
249 }
VkExtent2D getSwapchainExtent() const noexcept
Get the current swapchain extent.
Definition mxvk.hpp:210

◆ onRecordPostProcessingTexture()

void mxvk::VK_Window::onRecordPostProcessingTexture ( VkCommandBuffer cmd,
uint32_t image_index,
VkImageView texture_view,
VkExtent2D texture_extent )
protectedvirtualinherited

Render a completed post-processing chain as custom scene input.

Called inside a fresh final-color rendering scope when texture-consumer mode is enabled. The supplied view is in shader-read-only layout.

Definition at line 1441 of file mxvk.cpp.

1445 {}

◆ 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 1433 of file mxvk.cpp.

1433{}

◆ onSwapchainRecreated()

void example::MoonWindow::onSwapchainRecreated ( )
inlineoverridevirtual

Called after swapchain and render resources are recreated.

Derived classes can rebuild swapchain-dependent resources here.

Reimplemented from mxvk::VK_Window.

Definition at line 189 of file moon.cpp.

189 {
190 model.resize(this);
191 for (mxvk::VKAbstractModel &pyramid : pyramids) {
192 pyramid.resize(this);
193 }
194 if (star_sprite != nullptr) {
195 star_sprite->resize(this);
196 }
197 }

◆ pickDevice()

void mxvk::VK_Window::pickDevice ( )
protectedinherited

Pick a suitable Vulkan physical device.

Definition at line 1868 of file mxvk.cpp.

1868 {
1869 std::cout << "mxvk: entering pickDevice\n";
1870 if (instance == VK_NULL_HANDLE || surface == VK_NULL_HANDLE) {
1871 std::cout << "vk: cannot pick device because instance or surface is missing\n";
1872 return;
1873 }
1874
1875 uint32_t device_count = 0;
1876 std::cout << "vk: enumerating physical devices\n";
1877 vkEnumeratePhysicalDevices(instance, &device_count, nullptr);
1878 if (device_count == 0U) {
1879 std::cout << "vk: no physical devices found\n";
1880 return;
1881 }
1882 std::cout << std::format("vk: found {} physical device(s)\n", device_count);
1883
1884 std::vector<VkPhysicalDevice> devices(device_count);
1885 vkEnumeratePhysicalDevices(instance, &device_count, devices.data());
1886
1887 for (const VkPhysicalDevice candidate : devices) {
1888 std::cout << "vk: evaluating candidate physical device\n";
1889 uint32_t queue_family_count = 0;
1890 vkGetPhysicalDeviceQueueFamilyProperties(candidate, &queue_family_count, nullptr);
1891 std::vector<VkQueueFamilyProperties> queue_families(queue_family_count);
1892 vkGetPhysicalDeviceQueueFamilyProperties(candidate, &queue_family_count, queue_families.data());
1893
1894 uint32_t candidate_graphics = invalid_queue_index;
1895 uint32_t candidate_present = invalid_queue_index;
1896
1897 for (uint32_t i = 0; i < queue_family_count; ++i) {
1898 if ((queue_families[i].queueFlags & VK_QUEUE_GRAPHICS_BIT) != 0U) {
1899 candidate_graphics = i;
1900 }
1901
1902 VkBool32 present_support = VK_FALSE;
1903 vkGetPhysicalDeviceSurfaceSupportKHR(candidate, i, surface, &present_support);
1904 if (present_support == VK_TRUE) {
1905 candidate_present = i;
1906 }
1907 }
1908
1909 if (candidate_graphics == invalid_queue_index || candidate_present == invalid_queue_index) {
1910 std::cout << "vk: candidate rejected due to missing graphics/present queue support\n";
1911 continue;
1912 }
1913
1914 const SwapchainSupport swapchain_support = querySwapchainSupport(candidate, surface);
1915 if (swapchain_support.formats.empty() || swapchain_support.present_modes.empty()) {
1916 std::cout << "vk: candidate rejected due to incomplete swapchain support\n";
1917 continue;
1918 }
1919
1920 VkPhysicalDeviceProperties properties{};
1921 vkGetPhysicalDeviceProperties(candidate, &properties);
1922
1923 const char *device_type = "unknown";
1924 switch (properties.deviceType) {
1925 case VK_PHYSICAL_DEVICE_TYPE_INTEGRATED_GPU:
1926 device_type = "integrated";
1927 break;
1928 case VK_PHYSICAL_DEVICE_TYPE_DISCRETE_GPU:
1929 device_type = "discrete";
1930 break;
1931 case VK_PHYSICAL_DEVICE_TYPE_VIRTUAL_GPU:
1932 device_type = "virtual";
1933 break;
1934 case VK_PHYSICAL_DEVICE_TYPE_CPU:
1935 device_type = "cpu";
1936 break;
1937 default:
1938 break;
1939 }
1940
1941 physical_device = candidate;
1942 graphics_queue_family = candidate_graphics;
1943 present_queue_family = candidate_present;
1944 std::cout << std::format(
1945 "vk: selected GPU='{}' type={} vendor=0x{:04x} device=0x{:04x}\n",
1946 properties.deviceName,
1947 device_type,
1948 properties.vendorID,
1949 properties.deviceID);
1950 return;
1951 }
1952
1953 std::cout << "vk: no suitable physical device selected\n";
1954 }

◆ printPreviewText() [1/2]

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

Queue preview-only text after frame readback.

Unlike printText(), this text is visible in the presentation window but excluded from screenshots and frame-readback consumers.

Definition at line 4106 of file mxvk.cpp.

4107 {
4108 if (text.empty()) {
4109 return;
4110 }
4111 if (!font_configured) {
4112 throw mxvk::Exception(
4113 "printPreviewText requires setFont() to be called first");
4114 }
4115 ensurePreviewTextRenderer();
4116 if (!preview_text_renderer) {
4117 throw mxvk::Exception(
4118 "printPreviewText could not initialize the text renderer");
4119 }
4120 preview_text_renderer->printTextG_Solid(text, x, y, col);
4121 preview_text_queued = true;
4122 }

◆ printPreviewText() [2/2]

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

Definition at line 4124 of file mxvk.cpp.

4125 {
4126 if (text.empty()) {
4127 return;
4128 }
4129 if (!font) {
4130 throw mxvk::Exception(
4131 "printPreviewText requires a valid mxvk::Font");
4132 }
4133 ensurePreviewTextRenderer(font.path(), font.size());
4134 if (!preview_text_renderer) {
4135 throw mxvk::Exception(
4136 "printPreviewText could not initialize the text renderer");
4137 }
4138 preview_text_renderer->printTextG_Solid(text, x, y, col, font);
4139 preview_text_queued = true;
4140 }

◆ 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 4051 of file mxvk.cpp.

4051 {
4052 if (text.empty()) {
4053 return;
4054 }
4055
4056 if (!font_configured) {
4057 throw mxvk::Exception("printText requires setFont() to be called first");
4058 }
4059
4060 ensureTextRenderer();
4061 text_renderer->printTextG_Solid(text, x, y, col);
4062 }

◆ 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 4080 of file mxvk.cpp.

4080 {
4081 if (text.empty()) {
4082 return;
4083 }
4084
4085 if (!font) {
4086 throw mxvk::Exception("printText requires a valid mxvk::Font");
4087 }
4088
4089 ensureTextRenderer(font.path(), font.size());
4090 if (!text_renderer) {
4091 throw mxvk::Exception("printText with an explicit font could not initialize the text renderer");
4092 }
4093 text_renderer->printTextG_Solid(text, x, y, col, font);
4094 }

◆ 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 4064 of file mxvk.cpp.

4064 {
4065 if (text.empty()) {
4066 return;
4067 }
4068
4069 if (font == nullptr) {
4070 throw mxvk::Exception("printText requires a non-null TTF_Font");
4071 }
4072
4073 ensureTextRenderer(resolveDefaultFontPath(), font_size);
4074 if (!text_renderer) {
4075 throw mxvk::Exception("printText with an explicit font could not initialize the text renderer");
4076 }
4077 text_renderer->printTextG_Solid(text, x, y, col, font);
4078 }

◆ proc()

◆ 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
228
229 post_process_sprite = nullptr;
233 post_process_sprites.clear();
239
240 if (!sprites.empty()) {
241 std::cout << std::format("vk: releasing {} sprite(s)\n", sprites.size());
242 sprites.clear();
243 std::cout << "vk: sprite resources released\n";
244 }
245 if (!sprites3d.empty()) {
246 std::cout << std::format("vk: releasing {} 3D sprite batch(es)\n", sprites3d.size());
247 sprites3d.clear();
248 }
249 sprite_state_dirty = false;
250 destroySpritePipeline();
251 if (sprite_descriptor_set_layout != VK_NULL_HANDLE && device != VK_NULL_HANDLE) {
252 vkDestroyDescriptorSetLayout(device, sprite_descriptor_set_layout, nullptr);
253 sprite_descriptor_set_layout = VK_NULL_HANDLE;
254 }
255
256 if (text_renderer) {
257 text_renderer.reset();
258 }
260 preview_text_renderer.reset();
261 }
262 preview_text_queued = false;
263 text_state_dirty = false;
264 destroyTextPipeline();
265 if (text_descriptor_set_layout != VK_NULL_HANDLE && device != VK_NULL_HANDLE) {
266 vkDestroyDescriptorSetLayout(device, text_descriptor_set_layout, nullptr);
267 text_descriptor_set_layout = VK_NULL_HANDLE;
268 }
269
270 cleanupSyncObjects();
271
272 if (!retired_swapchains.empty()) {
273 for (VkSwapchainKHR retired : retired_swapchains) {
274 vkDestroySwapchainKHR(device, retired, nullptr);
275 }
276 retired_swapchains.clear();
277 }
278 std::cout << "vk: tearing down swapchain-dependent resources\n";
279 cleanupSwapchain(true);
280
281 if (command_pool != VK_NULL_HANDLE && device != VK_NULL_HANDLE) {
282 std::cout << "vk: destroying command pool\n";
283 vkDestroyCommandPool(device, command_pool, nullptr);
284 command_pool = VK_NULL_HANDLE;
285 }
286
287 if (device != VK_NULL_HANDLE) {
288 savePipelineCache();
289 destroyPipelineCache();
290 std::cout << "vk: destroying logical device\n";
291 vkDestroyDevice(device, nullptr);
292 device = VK_NULL_HANDLE;
293 graphics_queue = VK_NULL_HANDLE;
294 present_queue = VK_NULL_HANDLE;
295 }
296
297 graphics_queue_family = invalid_queue_index;
298 present_queue_family = invalid_queue_index;
299 physical_device = VK_NULL_HANDLE;
300
301 if (surface != VK_NULL_HANDLE && instance != VK_NULL_HANDLE) {
302 std::cout << "vk: destroying presentation surface\n";
303 vkDestroySurfaceKHR(instance, surface, nullptr);
304 surface = VK_NULL_HANDLE;
305 }
306
307 cleanupDebugMessenger();
308
309 if (instance != VK_NULL_HANDLE) {
310 std::cout << "vk: destroying Vulkan instance\n";
311 vkDestroyInstance(instance, nullptr);
312 instance = VK_NULL_HANDLE;
313 }
314
315 std::cout << "SDL3: destroying SDL window handle\n";
316 window.reset();
317
319 std::cout << "SDL3: unloading Vulkan loader library\n";
320 SDL_Vulkan_UnloadLibrary();
321 vulkan_library_loaded = false;
322 }
323
324 if (sdl_initialized) {
325 std::cout << "SDL3: shutting down SDL video/gamepad subsystems\n";
326 SDL_QuitSubSystem(SDL_INIT_VIDEO | SDL_INIT_GAMEPAD);
327 sdl_initialized = false;
328 }
329
330 active = false;
331 std::cout << "mxvk: resource teardown complete\n";
332 }
VkDescriptorSetLayout text_descriptor_set_layout
Definition mxvk.hpp:691
bool text_state_dirty
Definition mxvk.hpp:694
std::vector< VkSwapchainKHR > retired_swapchains
Definition mxvk.hpp:706

◆ render()

void mxvk::VK_Window::render ( )
virtualinherited

Render one frame.

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

Definition at line 805 of file mxvk.cpp.

805 {
806 drawFrame();
807 }

◆ renderStandaloneSprite() [1/2]

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 1451 of file mxvk.cpp.

1451 {
1453 }
void renderStandaloneSprite(VK_Sprite &sprite, VkCommandBuffer cmd)
Render one standalone sprite using the window's shared sprite pipeline.
Definition mxvk.cpp:1451

◆ renderStandaloneSprite() [2/2]

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

Definition at line 1455 of file mxvk.cpp.

1456 {
1457 if (device == VK_NULL_HANDLE || extent.width == 0U || extent.height == 0U) {
1458 return;
1459 }
1460
1461 if (sprite_pipeline == VK_NULL_HANDLE) {
1462 return;
1463 }
1464 vkCmdBindPipeline(cmd, VK_PIPELINE_BIND_POINT_GRAPHICS, sprite_pipeline);
1465 sprite.renderSprites(cmd, sprite_pipeline_layout, extent.width, extent.height);
1466 }
VkPipelineLayout sprite_pipeline_layout
Definition mxvk.hpp:664
VkPipeline sprite_pipeline
Definition mxvk.hpp:665

◆ 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 809 of file mxvk.cpp.

809 {
810 if (path.empty()) {
811 throw mxvk::Exception("saveSnapshot requires a non-empty output path");
812 }
813
814 std::vector<std::uint8_t> rgba{};
815 uint32_t width = 0;
816 uint32_t height = 0;
817 captureSnapshotPixels(rgba, width, height);
818
819 if (!mxvk::SavePNG_RGBA(path.c_str(),
820 rgba.data(),
821 static_cast<int>(width),
822 static_cast<int>(height))) {
823 throw mxvk::Exception("saveSnapshot failed to write PNG: " + path);
824 }
825 }
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:827
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 176 of file mxvk.hpp.

176{ 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 613 of file mxvk.cpp.

613 {
614 clear_color.float32[0] = std::clamp(r, 0.0f, 1.0f);
615 clear_color.float32[1] = std::clamp(g, 0.0f, 1.0f);
616 clear_color.float32[2] = std::clamp(b, 0.0f, 1.0f);
617 clear_color.float32[3] = std::clamp(a, 0.0f, 1.0f);
618 }
VkClearColorValue clear_color
Definition mxvk.hpp:705

◆ setEnableScreenshot()

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

Enable or disable F10 screenshot capture.

Definition at line 620 of file mxvk.cpp.

620 {
621 screenshot_enabled = enabled;
622 if (enabled) {
624 }
625 }

◆ 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 4002 of file mxvk.cpp.

4002 {
4003 if (fontPath.empty() || fontSize <= 0) {
4004 throw mxvk::Exception("setFont requires a non-empty path and positive font size");
4005 }
4006
4007 font_path = fontPath;
4008 font_size = fontSize;
4009 font_configured = true;
4010
4011 if (device == VK_NULL_HANDLE) {
4012 throw mxvk::Exception("Cannot set font before Vulkan device initialization");
4013 }
4014 if (swapchain == VK_NULL_HANDLE || command_pool == VK_NULL_HANDLE) {
4015 createDevice();
4016 }
4017 if (swapchain == VK_NULL_HANDLE || command_pool == VK_NULL_HANDLE) {
4018 throw mxvk::Exception("Cannot initialize text renderer before swapchain and command resources are available");
4019 }
4020
4021 if (!text_renderer) {
4022 ensureTextRenderer();
4023 } else {
4025 }
4028 }
4029 text_state_dirty = true;
4030 }
std::string font_path
Definition mxvk.hpp:697

◆ setFrameReadbackEnabled()

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

Enable or disable readback of each rendered swapchain frame.

Readback is recorded while the swapchain image is acquired and delivered through onFrameReadback() after the corresponding frame-in-flight fence completes. Readbacks are pipelined so recording does not add a same-frame GPU wait.

Definition at line 186 of file mxvk.hpp.

186{ frame_readback_enabled = enabled; }

◆ setPostProcessingEnabled()

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

Definition at line 339 of file mxvk.hpp.

339{ post_process_enabled = enabled; }
bool post_process_enabled
Definition mxvk.hpp:669

◆ 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 1624 of file mxvk.cpp.

1624 {
1625 post_process_params = {p1, p2, p3, p4};
1626 if (post_process_sprites.empty()) {
1627 return;
1628 }
1629 setPostProcessingShaderParams(0, p1, p2, p3, p4);
1630 }
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:1624

◆ 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 1632 of file mxvk.cpp.

1632 {
1633 if (effectIndex >= post_process_sprites.size()) {
1634 return;
1635 }
1636 post_process_params = {p1, p2, p3, p4};
1637 if (effectIndex < post_process_effect_params.size()) {
1639 }
1640 if (post_process_sprites[effectIndex] != nullptr) {
1641 post_process_sprites[effectIndex]->setShaderParams(p1, p2, p3, p4);
1642 }
1643 }

◆ 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 1645 of file mxvk.cpp.

1645 {
1646 post_process_time_enabled = enabled;
1647 post_process_start_time = std::chrono::steady_clock::now();
1648 if (post_process_sprites.empty()) {
1649 return;
1650 }
1652 }
void setPostProcessingShaderTimeEnabled(bool enabled)
Keep shader param 1 updated with elapsed render time in seconds.
Definition mxvk.cpp:1645

◆ setPostProcessingShaderTimeEnabled() [2/2]

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

Definition at line 1654 of file mxvk.cpp.

1654 {
1655 if (effectIndex >= post_process_sprites.size()) {
1656 return;
1657 }
1658 post_process_time_enabled = enabled;
1659 post_process_start_time = std::chrono::steady_clock::now();
1660 if (effectIndex < post_process_effect_time_enabled.size()) {
1661 post_process_effect_time_enabled[effectIndex] = enabled;
1662 }
1663 if (effectIndex < post_process_effect_start_times.size()) {
1665 }
1666 }

◆ setPostProcessingTextureConsumerEnabled()

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

Route the completed effect chain to a derived renderer as a sampled texture.

Definition at line 342 of file mxvk.hpp.

342 {
344 }
bool post_process_texture_consumer_enabled
Definition mxvk.hpp:670

◆ setPreviewFont()

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

Set the preview-only HUD font independently of output text.

Definition at line 4032 of file mxvk.cpp.

4032 {
4033 if (fontPath.empty() || fontSize <= 0) {
4034 throw mxvk::Exception(
4035 "setPreviewFont requires a non-empty path and positive font size");
4036 }
4037 if (device == VK_NULL_HANDLE) {
4038 throw mxvk::Exception(
4039 "Cannot set preview font before Vulkan device initialization");
4040 }
4041 if (!preview_text_renderer) {
4042 ensurePreviewTextRenderer(fontPath, fontSize);
4043 }
4044 if (!preview_text_renderer) {
4045 throw mxvk::Exception("Cannot initialize preview text renderer");
4046 }
4047 preview_text_renderer->setFont(fontPath, fontSize);
4048 text_state_dirty = true;
4049 }

◆ setRenderExtent()

void mxvk::VK_Window::setRenderExtent ( uint32_t width,
uint32_t height )
inherited

Render post-processing at a fixed extent independent of the preview window.

The final source-sized image is read back before a fitted copy is presented to the swapchain. Pass zero dimensions to return to swapchain-sized rendering.

Definition at line 1468 of file mxvk.cpp.

1468 {
1469 if ((width == 0U) != (height == 0U)) {
1470 throw mxvk::Exception("render extent requires two positive dimensions or two zeros");
1471 }
1472 if (render_extent_override.width == width &&
1473 render_extent_override.height == height) {
1474 return;
1475 }
1476 if (device != VK_NULL_HANDLE && swapchain != VK_NULL_HANDLE) {
1477 vkDeviceWaitIdle(device);
1479 destroyPostProcessTargets();
1480 }
1481 render_extent_override = {width, height};
1482 sprite_state_dirty = true;
1483 if (device != VK_NULL_HANDLE && !swapchain_images.empty() &&
1484 !post_process_sprites.empty()) {
1485 createPostProcessTargets();
1486 }
1487 std::cout << std::format("mxvk: fixed render extent {}\n",
1488 width > 0U ? std::format("{}x{}", width, height)
1489 : "disabled");
1490 }

◆ showCursor()

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

Definition at line 4790 of file mxvk.cpp.

4790 {
4791 if (on)
4792 SDL_ShowCursor();
4793 else
4794 SDL_HideCursor();
4795 }

◆ 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 1330 of file mxvk.cpp.

1330 {
1331 if (device == VK_NULL_HANDLE || command_pool == VK_NULL_HANDLE || vkTrimCommandPool == nullptr) {
1332 return;
1333 }
1334
1335 vkTrimCommandPool(device, command_pool, 0);
1336 }

◆ 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 3922 of file mxvk.cpp.

3922 {
3923 return validation_enabled;
3924 }

Member Data Documentation

◆ active

bool mxvk::VK_Window::active = false
protectedinherited

Definition at line 629 of file mxvk.hpp.

◆ clear_color

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

Definition at line 705 of file mxvk.hpp.

◆ command_buffers

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

Definition at line 617 of file mxvk.hpp.

◆ command_pool

VkCommandPool mxvk::VK_Window::command_pool = VK_NULL_HANDLE
protectedinherited

Definition at line 616 of file mxvk.hpp.

◆ current_frame

uint32_t mxvk::VK_Window::current_frame = 0
protectedinherited

Definition at line 623 of file mxvk.hpp.

◆ debug_messenger

VkDebugUtilsMessengerEXT mxvk::VK_Window::debug_messenger = VK_NULL_HANDLE
protectedinherited

Definition at line 593 of file mxvk.hpp.

◆ depth_format

VkFormat mxvk::VK_Window::depth_format = VK_FORMAT_UNDEFINED
protectedinherited

Definition at line 605 of file mxvk.hpp.

◆ depth_image_initialized

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

Definition at line 614 of file mxvk.hpp.

◆ depth_image_memories

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

Definition at line 612 of file mxvk.hpp.

◆ depth_image_views

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

Definition at line 613 of file mxvk.hpp.

◆ depth_images

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

Definition at line 611 of file mxvk.hpp.

◆ device

VkDevice mxvk::VK_Window::device = VK_NULL_HANDLE
protectedinherited

Definition at line 596 of file mxvk.hpp.

◆ font_configured

bool mxvk::VK_Window::font_configured = false
protectedinherited

Definition at line 696 of file mxvk.hpp.

◆ font_path

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

Definition at line 697 of file mxvk.hpp.

◆ font_size

int mxvk::VK_Window::font_size = 24
protectedinherited

Definition at line 698 of file mxvk.hpp.

◆ force_swapchain_recreate

bool mxvk::VK_Window::force_swapchain_recreate = false
protectedinherited

Definition at line 655 of file mxvk.hpp.

◆ fps_counter_enabled

bool mxvk::VK_Window::fps_counter_enabled = false
protectedinherited

Definition at line 699 of file mxvk.hpp.

◆ fps_counter_font

Font mxvk::VK_Window::fps_counter_font {}
protectedinherited

Definition at line 701 of file mxvk.hpp.

◆ fps_counter_font_ready

bool mxvk::VK_Window::fps_counter_font_ready = false
protectedinherited

Definition at line 700 of file mxvk.hpp.

◆ fps_counter_frame_count

uint32_t mxvk::VK_Window::fps_counter_frame_count = 0
protectedinherited

Definition at line 703 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 702 of file mxvk.hpp.

◆ fps_counter_text

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

Definition at line 704 of file mxvk.hpp.

◆ frame_readback_enabled

bool mxvk::VK_Window::frame_readback_enabled = false
protectedinherited

Definition at line 644 of file mxvk.hpp.

◆ frame_readback_slots

std::array<FrameReadbackSlot, max_frames_in_flight> mxvk::VK_Window::frame_readback_slots {}
protectedinherited

Definition at line 645 of file mxvk.hpp.

◆ framebuffer_resized

bool mxvk::VK_Window::framebuffer_resized = false
protectedinherited

Definition at line 654 of file mxvk.hpp.

◆ graphics_queue

VkQueue mxvk::VK_Window::graphics_queue = VK_NULL_HANDLE
protectedinherited

Definition at line 599 of file mxvk.hpp.

◆ graphics_queue_family

uint32_t mxvk::VK_Window::graphics_queue_family = invalid_queue_index
protectedinherited

Definition at line 597 of file mxvk.hpp.

◆ image_available

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

Definition at line 619 of file mxvk.hpp.

◆ image_fences

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

Definition at line 622 of file mxvk.hpp.

◆ in_flight_fences

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

Definition at line 621 of file mxvk.hpp.

◆ instance

VkInstance mxvk::VK_Window::instance = VK_NULL_HANDLE
protectedinherited

Definition at line 592 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 658 of file mxvk.hpp.

◆ last_presented_image_index

uint32_t mxvk::VK_Window::last_presented_image_index = invalid_queue_index
protectedinherited

Definition at line 624 of file mxvk.hpp.

◆ last_resize_event_ms

uint64_t mxvk::VK_Window::last_resize_event_ms = 0
protectedinherited

Definition at line 656 of file mxvk.hpp.

◆ latest_frame_readback_height

uint32_t mxvk::VK_Window::latest_frame_readback_height = 0
protectedinherited

Definition at line 648 of file mxvk.hpp.

◆ latest_frame_readback_rgba

std::vector<std::uint8_t> mxvk::VK_Window::latest_frame_readback_rgba {}
protectedinherited

Definition at line 646 of file mxvk.hpp.

◆ latest_frame_readback_width

uint32_t mxvk::VK_Window::latest_frame_readback_width = 0
protectedinherited

Definition at line 647 of file mxvk.hpp.

◆ MEMORY_TRIM_INTERVAL

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

Definition at line 659 of file mxvk.hpp.

◆ owned_post_process_sprite

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

Definition at line 668 of file mxvk.hpp.

◆ owned_post_process_sprites

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

Definition at line 675 of file mxvk.hpp.

◆ physical_device

VkPhysicalDevice mxvk::VK_Window::physical_device = VK_NULL_HANDLE
protectedinherited

Definition at line 595 of file mxvk.hpp.

◆ pipeline_cache

VkPipelineCache mxvk::VK_Window::pipeline_cache = VK_NULL_HANDLE
protectedinherited

Definition at line 601 of file mxvk.hpp.

◆ post_process_composite_sprite

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

Definition at line 677 of file mxvk.hpp.

◆ post_process_compute_format

VkFormat mxvk::VK_Window::post_process_compute_format
staticconstexprprotectedinherited
Initial value:
=
VK_FORMAT_R8G8B8A8_UNORM

Definition at line 686 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 679 of file mxvk.hpp.

◆ post_process_effect_stages

std::vector<ShaderStage> mxvk::VK_Window::post_process_effect_stages {}
protectedinherited

Definition at line 678 of file mxvk.hpp.

◆ 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 681 of file mxvk.hpp.

◆ post_process_effect_time_enabled

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

Definition at line 680 of file mxvk.hpp.

◆ post_process_enabled

bool mxvk::VK_Window::post_process_enabled = true
protectedinherited

Definition at line 669 of file mxvk.hpp.

◆ post_process_images

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

Definition at line 682 of file mxvk.hpp.

◆ post_process_initialized

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

Definition at line 685 of file mxvk.hpp.

◆ post_process_memories

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

Definition at line 683 of file mxvk.hpp.

◆ post_process_params

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

Definition at line 672 of file mxvk.hpp.

◆ post_process_present_sprite

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

Definition at line 676 of file mxvk.hpp.

◆ post_process_sprite

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

Definition at line 667 of file mxvk.hpp.

◆ post_process_sprites

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

Definition at line 674 of file mxvk.hpp.

◆ 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 673 of file mxvk.hpp.

◆ post_process_texture_consumer_enabled

bool mxvk::VK_Window::post_process_texture_consumer_enabled = false
protectedinherited

Definition at line 670 of file mxvk.hpp.

◆ post_process_time_enabled

bool mxvk::VK_Window::post_process_time_enabled = false
protectedinherited

Definition at line 671 of file mxvk.hpp.

◆ post_process_views

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

Definition at line 684 of file mxvk.hpp.

◆ present_mode_preference

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

Definition at line 653 of file mxvk.hpp.

◆ present_queue

VkQueue mxvk::VK_Window::present_queue = VK_NULL_HANDLE
protectedinherited

Definition at line 600 of file mxvk.hpp.

◆ present_queue_family

uint32_t mxvk::VK_Window::present_queue_family = invalid_queue_index
protectedinherited

Definition at line 598 of file mxvk.hpp.

◆ preview_text_queued

bool mxvk::VK_Window::preview_text_queued = false
protectedinherited

Definition at line 695 of file mxvk.hpp.

◆ preview_text_renderer

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

Definition at line 690 of file mxvk.hpp.

◆ render_extent_override

VkExtent2D mxvk::VK_Window::render_extent_override {}
protectedinherited

Definition at line 607 of file mxvk.hpp.

◆ render_finished

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

Definition at line 620 of file mxvk.hpp.

◆ resize_settle_delay_ms

uint64_t mxvk::VK_Window::resize_settle_delay_ms = 150
staticconstexprprotectedinherited

Definition at line 657 of file mxvk.hpp.

◆ retired_swapchains

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

Definition at line 706 of file mxvk.hpp.

◆ screenshot_enabled

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

Definition at line 630 of file mxvk.hpp.

◆ screenshot_index

uint32_t mxvk::VK_Window::screenshot_index = 0
protectedinherited

Definition at line 632 of file mxvk.hpp.

◆ screenshot_prefix

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

Definition at line 631 of file mxvk.hpp.

◆ screenshot_queue_cv

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

Definition at line 640 of file mxvk.hpp.

◆ screenshot_queue_mutex

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

Definition at line 639 of file mxvk.hpp.

◆ screenshot_save_queue

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

Definition at line 641 of file mxvk.hpp.

◆ screenshot_worker

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

Definition at line 642 of file mxvk.hpp.

◆ screenshot_worker_stop

bool mxvk::VK_Window::screenshot_worker_stop = false
protectedinherited

Definition at line 643 of file mxvk.hpp.

◆ sdl_initialized

bool mxvk::VK_Window::sdl_initialized = false
protectedinherited

Definition at line 627 of file mxvk.hpp.

◆ sprite_descriptor_set_layout

VkDescriptorSetLayout mxvk::VK_Window::sprite_descriptor_set_layout = VK_NULL_HANDLE
protectedinherited

Definition at line 663 of file mxvk.hpp.

◆ sprite_pipeline

VkPipeline mxvk::VK_Window::sprite_pipeline = VK_NULL_HANDLE
protectedinherited

Definition at line 665 of file mxvk.hpp.

◆ sprite_pipeline_layout

VkPipelineLayout mxvk::VK_Window::sprite_pipeline_layout = VK_NULL_HANDLE
protectedinherited

Definition at line 664 of file mxvk.hpp.

◆ sprite_state_dirty

bool mxvk::VK_Window::sprite_state_dirty = false
protectedinherited

Definition at line 666 of file mxvk.hpp.

◆ sprites

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

Definition at line 661 of file mxvk.hpp.

◆ sprites3d

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

Definition at line 662 of file mxvk.hpp.

◆ surface

VkSurfaceKHR mxvk::VK_Window::surface = VK_NULL_HANDLE
protectedinherited

Definition at line 594 of file mxvk.hpp.

◆ swapchain

VkSwapchainKHR mxvk::VK_Window::swapchain = VK_NULL_HANDLE
protectedinherited

Definition at line 603 of file mxvk.hpp.

◆ swapchain_extent

VkExtent2D mxvk::VK_Window::swapchain_extent {}
protectedinherited

Definition at line 606 of file mxvk.hpp.

◆ swapchain_format

VkFormat mxvk::VK_Window::swapchain_format = VK_FORMAT_UNDEFINED
protectedinherited

Definition at line 604 of file mxvk.hpp.

◆ swapchain_image_initialized

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

Definition at line 610 of file mxvk.hpp.

◆ swapchain_image_views

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

Definition at line 609 of file mxvk.hpp.

◆ swapchain_images

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

Definition at line 608 of file mxvk.hpp.

◆ swapchain_supports_transfer_src

bool mxvk::VK_Window::swapchain_supports_transfer_src = false
protectedinherited

Definition at line 625 of file mxvk.hpp.

◆ text_descriptor_set_layout

VkDescriptorSetLayout mxvk::VK_Window::text_descriptor_set_layout = VK_NULL_HANDLE
protectedinherited

Definition at line 691 of file mxvk.hpp.

◆ text_pipeline

VkPipeline mxvk::VK_Window::text_pipeline = VK_NULL_HANDLE
protectedinherited

Definition at line 693 of file mxvk.hpp.

◆ text_pipeline_layout

VkPipelineLayout mxvk::VK_Window::text_pipeline_layout = VK_NULL_HANDLE
protectedinherited

Definition at line 692 of file mxvk.hpp.

◆ text_renderer

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

Definition at line 689 of file mxvk.hpp.

◆ text_state_dirty

bool mxvk::VK_Window::text_state_dirty = false
protectedinherited

Definition at line 694 of file mxvk.hpp.

◆ validation_enabled

bool mxvk::VK_Window::validation_enabled = false
protectedinherited

Definition at line 652 of file mxvk.hpp.

◆ vulkan_library_loaded

bool mxvk::VK_Window::vulkan_library_loaded = false
protectedinherited

Definition at line 628 of file mxvk.hpp.

◆ window

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

Definition at line 591 of file mxvk.hpp.


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