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

Public Types

enum class  PresentModePreference { LowLatency , Vsync }
enum class  RuntimeMode { Windowed , Headless }

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 &event) 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.
VkFormat getSceneColorFormat () const noexcept
 Get the active scene/effect color-attachment format.
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)
bool headless () const noexcept
 Return true when rendering without SDL video, a surface, or presentation.
virtual bool initVulkan (bool validiation)
 Initialize Vulkan state.
void loop ()
 Run the main event/render loop.
 Math3DPongWindow (bool fullscreen, bool enable_vsync, const FramebufferDimensions &framebuffer)
virtual void onPrepareFrameRendering (VkCommandBuffer cmd, uint32_t image_index)
 Record resource transitions that must happen before dynamic rendering begins.
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)
void proc () override
 Execute one processing/update step.
void release ()
 Release Vulkan and SDL resources.
virtual void render ()
 Render one frame.
void saveSnapshot (const std::string &path)
 Save the most recently rendered window contents as a PNG file.
bool screenshotEnabled () const noexcept
 Check whether F10 screenshot capture is enabled.
void setClearColor (float r, float g, float b, float a=1.0f)
 Set the per-frame color attachment clear color.
void setEnableScreenshot (bool enabled) noexcept
 Enable or disable F10 screenshot capture.
void setFont (const std::string &fontPath, int fontSize=24)
 Set the active text-render font.
void setFrameReadbackEnabled (bool enabled) noexcept
 Enable or disable readback of each rendered swapchain frame.
void setFrameReadbackRgba16Enabled (bool enabled) noexcept
 Request normalized RGBA16 callbacks from the final HDR intermediate.
void setHdrRenderIntermediatesEnabled (bool enabled)
 Select RGBA16F scene/effect intermediates while retaining the presentation format.
void setPostProcessingEnabled (bool enabled)
void setPostProcessingPresentFragmentShader (const std::string &path)
 Set an optional fragment shader used only when presenting the final post-processing texture to a window.
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.

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 onFrameReadbackRgba16 (std::vector< std::uint16_t > &rgba_pixels, uint32_t width, uint32_t height)
 Receive normalized RGBA16 pixels from an RGBA16F HDR target.
virtual void onFrameReadbackScheduled ()
 Called after a presented frame has scheduled a readback.
virtual void onRecordCustomRendering (VkCommandBuffer cmd, uint32_t image_index)
 Optional hook for derived classes to record extra draw commands.
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.
virtual void onSwapchainRecreated ()
 Called after swapchain and render resources are recreated.
void pickDevice ()
 Pick a suitable Vulkan physical device.
VkFormat postProcessIntermediateFormat () const noexcept
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
bool frame_readback_rgba16_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
bool hdr_render_intermediates_enabled = false
std::vector< VkDeviceMemory > headless_image_memories {}
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 {}
std::vector< std::uint16_t > latest_frame_readback_rgba16 {}
uint32_t latest_frame_readback_width = 0
uint32_t next_headless_image = 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 {}
std::string post_process_present_shader_path {}
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 {}
VkExtent2D requested_headless_extent {}
std::vector< VkSwapchainKHR > retired_swapchains
RuntimeMode runtime_mode = RuntimeMode::Windowed
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< ScreenshotSaveTask > screenshot_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, SDLWindowDeleter > window {}

Static Protected Attributes

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

Detailed Description

Definition at line 415 of file main.cpp.

Member Enumeration Documentation

◆ PresentModePreference

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

Definition at line 41 of file mxvk.hpp.

41{ LowLatency, Vsync };

◆ RuntimeMode

enum class mxvk::VK_Window::RuntimeMode
stronginherited
Enumerator
Windowed 
Headless 

Definition at line 43 of file mxvk.hpp.

43{ Windowed, Headless };

Constructor & Destructor Documentation

◆ Math3DPongWindow()

example::Math3DPongWindow::Math3DPongWindow ( bool fullscreen,
bool enable_vsync,
const FramebufferDimensions & framebuffer )
inline

Definition at line 417 of file main.cpp.

417 : mxvk::VK_Window("MXVK 3D Math Pong", WINDOW_WIDTH, WINDOW_HEIGHT, fullscreen, MXVK_VALIDATION, enable_vsync), renderer(framebuffer.width, framebuffer.height), cube_mesh(Mesh::cube()), ball_mesh(Mesh::sphere(8, 12)) {
418 setClearColor(0.01f, 0.02f, 0.04f, 1.0f);
420 }
void setClearColor(float r, float g, float b, float a=1.0f)
Set the per-frame color attachment clear color.
Definition mxvk.cpp:637
#define MXVK_VALIDATION
Definition mxvk.hpp:28
void BuildTables()
Rebuild the sine and cosine lookup tables.
Definition mxvk_math.h:98
int width
Software framebuffer width in pixels.
Definition argz.hpp:710
int height
Software framebuffer height in pixels.
Definition argz.hpp:711
static Mesh sphere(int latitude_segments, int longitude_segments)
Definition main.cpp:83

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

1469 {
1470 const std::vector<VK_Sprite *> sprites = attachPostProcessingShaders({PostProcessingEffect{fragmentShaderPath, {p1, p2, p3, p4}, false}});
1471 return sprites.empty() ? nullptr : sprites.front();
1472 }
std::vector< VK_Sprite * > attachPostProcessingShaders(const std::vector< PostProcessingEffect > &effects)
Definition mxvk.cpp:1474
std::vector< std::unique_ptr< VK_Sprite > > sprites
Definition mxvk.hpp:677

◆ attachPostProcessingShaders()

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

Definition at line 1474 of file mxvk.cpp.

1474 {
1476
1477 std::vector<VK_Sprite *> attachedSprites;
1478 attachedSprites.reserve(effects.size());
1479 for (size_t effect_index = 0; effect_index < effects.size(); ++effect_index) {
1480 const PostProcessingEffect &effect = effects[effect_index];
1481 if (effect.fragmentShaderPath.empty()) {
1482 throw mxvk::Exception("Cannot attach a post-processing pass with an empty shader path");
1483 }
1484
1485 const ShaderModuleInfo module_info = inspect_spirv(load_spv(effect.fragmentShaderPath));
1486 const ShaderStage stage = effect.stage == ShaderStage::Unknown ? module_info.stage : effect.stage;
1487 if (stage != ShaderStage::Fragment && stage != ShaderStage::Compute) {
1488 throw mxvk::Exception("Post-processing shaders must contain a fragment or compute entry point");
1489 }
1490 if (effect.stage != ShaderStage::Unknown && effect.stage != module_info.stage) {
1491 throw mxvk::Exception("Post-processing shader stage does not match its SPIR-V entry point");
1492 }
1493
1494 VK_Sprite *sprite = createSprite(1, 1, resolveRuntimeShaderPath("sprite.vert.spv", MXVK_SPRITE_SHADER_DIR), "", effect.spectrumBinCount, effect.spectrumHistoryLayerCount);
1495 sprite->enableExtendedUBO();
1496 if (effect.historySource != nullptr) {
1497 sprite->shareHistoryTexture(*effect.historySource);
1498 }
1499 const bool final_fragment = stage == ShaderStage::Fragment && effect_index + 1U == effects.size();
1500 if (stage == ShaderStage::Fragment) {
1501 if (!final_fragment || hdr_render_intermediates_enabled) {
1502 sprite->setColorAttachmentFormat(postProcessIntermediateFormat());
1503 }
1504 sprite->setFragmentShaderPath(effect.fragmentShaderPath);
1505 } else if (stage == ShaderStage::Compute) {
1507 if (module_info.storageImageFormat != required_format) {
1508 throw mxvk::Exception(std::format("Compute shader '{}' must declare layout({}) for the "
1509 "active {} render path",
1510 effect.fragmentShaderPath,
1511 hdr_render_intermediates_enabled ? "rgba16f" : "rgba8",
1512 hdr_render_intermediates_enabled ? "HDR" : "SDR"));
1513 }
1514 sprite->enableComputeShader(effect.fragmentShaderPath, module_info.localSizeX, module_info.localSizeY, module_info.localSizeZ);
1515 }
1516 const uint32_t black_pixel = 0xFF000000u;
1517 sprite->updateTexture(&black_pixel, 1, 1);
1518 sprite->setShaderParams(effect.params[0], effect.params[1], effect.params[2], effect.params[3]);
1519
1520 attachedSprites.push_back(sprite);
1521 owned_post_process_sprites.push_back(sprite);
1522 post_process_sprites.push_back(sprite);
1523 post_process_effect_params.push_back(effect.params);
1524 post_process_effect_time_enabled.push_back(effect.timeEnabled);
1525 post_process_effect_start_times.push_back(std::chrono::steady_clock::now());
1526 post_process_effect_stages.push_back(stage);
1527 }
1528
1529 if (!post_process_effect_stages.empty()) {
1530 post_process_present_sprite = createSprite(1, 1, resolveRuntimeShaderPath("sprite.vert.spv", MXVK_SPRITE_SHADER_DIR));
1531 const uint32_t black_pixel = 0xFF000000u;
1532 post_process_present_sprite->updateTexture(&black_pixel, 1, 1);
1533 if (!post_process_present_shader_path.empty()) {
1535 }
1537
1539 post_process_composite_sprite->updateTexture(&black_pixel, 1, 1);
1541 }
1542
1543 post_process_sprite = attachedSprites.empty() ? nullptr : attachedSprites.front();
1545 post_process_params = post_process_effect_params.empty() ? std::array<float, 4>{} : post_process_effect_params.front();
1547 post_process_start_time = post_process_effect_start_times.empty() ? std::chrono::steady_clock::now() : post_process_effect_start_times.front();
1548 if (device != VK_NULL_HANDLE && !swapchain_images.empty()) {
1549 createPostProcessTargets();
1550 }
1551 sprite_state_dirty = true;
1552 return attachedSprites;
1553 }
void updateTexture(SDL_Surface *surface)
Replace the sprite texture from an SDL_Surface.
void setFragmentShaderPath(const std::string &path)
Replace the fragment shader path and rebuild the custom pipeline.
std::vector< ShaderStage > post_process_effect_stages
Definition mxvk.hpp:695
VkDevice device
Definition mxvk.hpp:606
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:4455
std::vector< bool > post_process_effect_time_enabled
Definition mxvk.hpp:697
std::vector< std::array< float, 4 > > post_process_effect_params
Definition mxvk.hpp:696
std::chrono::steady_clock::time_point post_process_start_time
Definition mxvk.hpp:689
std::vector< VK_Sprite * > owned_post_process_sprites
Definition mxvk.hpp:691
std::vector< VkImage > swapchain_images
Definition mxvk.hpp:618
bool post_process_time_enabled
Definition mxvk.hpp:687
VK_Sprite * post_process_composite_sprite
Definition mxvk.hpp:694
std::vector< std::chrono::steady_clock::time_point > post_process_effect_start_times
Definition mxvk.hpp:698
bool hdr_render_intermediates_enabled
Definition mxvk.hpp:704
VK_Sprite * post_process_present_sprite
Definition mxvk.hpp:692
std::array< float, 4 > post_process_params
Definition mxvk.hpp:688
void detachPostProcessingShader()
Detach the current post-processing shader and return to direct swapchain rendering.
Definition mxvk.cpp:1566
std::vector< VK_Sprite * > post_process_sprites
Definition mxvk.hpp:690
bool sprite_state_dirty
Definition mxvk.hpp:682
VK_Sprite * post_process_sprite
Definition mxvk.hpp:683
std::string post_process_present_shader_path
Definition mxvk.hpp:693
VK_Sprite * owned_post_process_sprite
Definition mxvk.hpp:684
VkFormat postProcessIntermediateFormat() const noexcept
Definition mxvk.hpp:703
#define MXVK_SPRITE_SHADER_DIR
Definition mxvk.cpp:33
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 841 of file mxvk.cpp.

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

◆ clearTextQueue()

void mxvk::VK_Window::clearTextQueue ( )
inherited

Clear all queued text draw calls for the current frame.

Definition at line 3964 of file mxvk.cpp.

3964 {
3965 if (text_renderer) {
3966 text_renderer->clearQueue();
3967 }
3969 preview_text_renderer->clearQueue();
3970 }
3971 preview_text_queued = false;
3972 }
std::unique_ptr< VK_Text > preview_text_renderer
Definition mxvk.hpp:707
std::unique_ptr< VK_Text > text_renderer
Definition mxvk.hpp:706
bool preview_text_queued
Definition mxvk.hpp:712

◆ context()

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

Definition at line 80 of file mxvk.cpp.

80 {
81 return VulkanContext{
82 .device = getDevice(),
83 .physical_device = getPhysicalDevice(),
84 .graphics_queue = getGraphicsQueue(),
85 .command_pool = getCommandPool(),
86 };
87 }
VkDevice getDevice() const noexcept
Get the Vulkan logical device handle.
Definition mxvk.hpp:198
VkQueue getGraphicsQueue() const noexcept
Get the graphics queue handle.
Definition mxvk.hpp:204
VkCommandPool getCommandPool() const noexcept
Get the command pool used for graphics/upload work.
Definition mxvk.hpp:207
VkPhysicalDevice getPhysicalDevice() const noexcept
Get the Vulkan physical device handle.
Definition mxvk.hpp:201

◆ createDevice()

void mxvk::VK_Window::createDevice ( )
protectedinherited

Create final device resources.

Definition at line 2028 of file mxvk.cpp.

2028 {
2029 std::cout << "mxvk: entering createDevice\n";
2030 if (device == VK_NULL_HANDLE) {
2031 std::cout << "vk: skipping createDevice because logical device is null\n";
2032 return;
2033 }
2034
2035 const bool sync_ready = std::ranges::all_of(image_available.begin(), image_available.end(), [](VkSemaphore semaphore) { return semaphore != VK_NULL_HANDLE; }) && render_finished.size() == swapchain_images.size() && std::ranges::all_of(render_finished.begin(), render_finished.end(), [](VkSemaphore semaphore) { return semaphore != VK_NULL_HANDLE; }) && std::ranges::all_of(in_flight_fences.begin(), in_flight_fences.end(), [](VkFence fence) { return fence != VK_NULL_HANDLE; });
2036
2037 if (renderTargetsReady() && command_pool != VK_NULL_HANDLE && !command_buffers.empty() && sync_ready && image_fences.size() == swapchain_images.size()) {
2038 std::cout << "vk: createDevice skipped because render resources are already initialized\n";
2039 return;
2040 }
2041
2042 if (headless()) {
2043 std::cout << "vk: creating surface-free color targets\n";
2044 if (!createHeadlessTargets()) {
2045 std::cout << "vk: createHeadlessTargets failed\n";
2046 return;
2047 }
2048 } else {
2049 std::cout << "vk: creating swapchain\n";
2050 if (!createSwapchain(VK_NULL_HANDLE)) {
2051 std::cout << "vk: createSwapchain failed\n";
2052 return;
2053 }
2054 }
2055
2056 std::cout << "vk: creating render resources\n";
2057 if (!createRenderResources()) {
2058 std::cout << "vk: createRenderResources failed\n";
2059 return;
2060 }
2061
2062 std::cout << "vk: creating synchronization objects\n";
2063 if (!createSyncObjects()) {
2064 std::cout << "vk: createSyncObjects failed\n";
2065 return;
2066 }
2067
2068 std::cout << "mxvk: createDevice complete\n";
2069 }
std::vector< VkCommandBuffer > command_buffers
Definition mxvk.hpp:628
std::vector< VkSemaphore > render_finished
Definition mxvk.hpp:631
std::array< VkSemaphore, max_frames_in_flight > image_available
Definition mxvk.hpp:630
std::array< VkFence, max_frames_in_flight > in_flight_fences
Definition mxvk.hpp:632

◆ createLogicalDevice()

void mxvk::VK_Window::createLogicalDevice ( )
protectedinherited

Create a logical Vulkan device from the selected physical device.

Definition at line 1898 of file mxvk.cpp.

1898 {
1899 std::cout << "mxvk: entering createLogicalDevice\n";
1900 if (physical_device == VK_NULL_HANDLE) {
1901 std::cout << "vk: cannot create logical device without a selected physical device\n";
1902 return;
1903 }
1904
1905 std::vector<uint32_t> queue_families{};
1906 queue_families.push_back(graphics_queue_family);
1908 queue_families.push_back(present_queue_family);
1909 }
1910
1911 constexpr float queue_priority = 1.0f;
1912 std::vector<VkDeviceQueueCreateInfo> queue_create_infos{};
1913 queue_create_infos.reserve(queue_families.size());
1914 for (const uint32_t family : queue_families) {
1915 VkDeviceQueueCreateInfo queue_create_info{};
1916 queue_create_info.sType = VK_STRUCTURE_TYPE_DEVICE_QUEUE_CREATE_INFO;
1917 queue_create_info.queueFamilyIndex = family;
1918 queue_create_info.queueCount = 1;
1919 queue_create_info.pQueuePriorities = &queue_priority;
1920 queue_create_infos.push_back(queue_create_info);
1921 }
1922 uint32_t device_extension_count = 0;
1923 vkEnumerateDeviceExtensionProperties(physical_device, nullptr, &device_extension_count, nullptr);
1924 std::vector<VkExtensionProperties> device_extensions(device_extension_count);
1925 if (device_extension_count > 0U) {
1926 vkEnumerateDeviceExtensionProperties(physical_device, nullptr, &device_extension_count, device_extensions.data());
1927 }
1928
1929 [[maybe_unused]] const auto has_device_extension = [&device_extensions](const char *extension_name) { return std::ranges::any_of(device_extensions, [extension_name](const VkExtensionProperties &ext) { return std::strcmp(ext.extensionName, extension_name) == 0; }); };
1930
1931 std::vector<const char *> required_device_extensions{};
1932 if (!headless()) {
1933 required_device_extensions.push_back(VK_KHR_SWAPCHAIN_EXTENSION_NAME);
1934 }
1935#if defined(MXVK_CUDA) && defined(__linux__)
1936 if (has_device_extension(VK_KHR_EXTERNAL_MEMORY_FD_EXTENSION_NAME)) {
1937 if (has_device_extension(VK_KHR_EXTERNAL_MEMORY_EXTENSION_NAME)) {
1938 required_device_extensions.push_back(VK_KHR_EXTERNAL_MEMORY_EXTENSION_NAME);
1939 }
1940 required_device_extensions.push_back(VK_KHR_EXTERNAL_MEMORY_FD_EXTENSION_NAME);
1941 std::cout << "vk: enabling external memory FD support for CUDA interop\n";
1942 } else {
1943 std::cout << "vk: external memory FD support unavailable; CUDA texture interop will fall back\n";
1944 }
1945#endif
1946
1947#if defined(MXVK_USE_MOLTENVK)
1948 const bool has_portability_subset = std::ranges::any_of(device_extensions, [](const VkExtensionProperties &ext) { return std::strcmp(ext.extensionName, VK_KHR_PORTABILITY_SUBSET_EXTENSION_NAME) == 0; });
1949
1950 if (has_portability_subset) {
1951 required_device_extensions.push_back(VK_KHR_PORTABILITY_SUBSET_EXTENSION_NAME);
1952 }
1953#endif
1954 VkPhysicalDeviceFeatures2 features2{};
1955 features2.sType = VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_FEATURES_2;
1956 VkPhysicalDeviceVulkan13Features supported_vulkan13_features{};
1957 supported_vulkan13_features.sType = VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_VULKAN_1_3_FEATURES;
1958 features2.pNext = &supported_vulkan13_features;
1959 vkGetPhysicalDeviceFeatures2(physical_device, &features2);
1960
1961 std::cout << std::format("vk: feature support - synchronization2={}, dynamicRendering={}, shaderFloat64={}, fillModeNonSolid={}, samplerAnisotropy={}\n", supported_vulkan13_features.synchronization2 == VK_TRUE ? "true" : "false", supported_vulkan13_features.dynamicRendering == VK_TRUE ? "true" : "false", features2.features.shaderFloat64 == VK_TRUE ? "true" : "false", features2.features.fillModeNonSolid == VK_TRUE ? "true" : "false", features2.features.samplerAnisotropy == VK_TRUE ? "true" : "false");
1962
1963 if (supported_vulkan13_features.synchronization2 != VK_TRUE) {
1964 std::cout << "vk: synchronization2 is unsupported on selected physical device\n";
1965 return;
1966 }
1967 if (supported_vulkan13_features.dynamicRendering != VK_TRUE) {
1968 std::cout << "vk: dynamic rendering is unsupported on selected physical device\n";
1969 return;
1970 }
1971 VkPhysicalDeviceVulkan13Features vulkan13_features{};
1972 vulkan13_features.sType = VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_VULKAN_1_3_FEATURES;
1973 vulkan13_features.synchronization2 = VK_TRUE;
1974 vulkan13_features.dynamicRendering = VK_TRUE;
1975
1976 VkPhysicalDeviceFeatures2 enabled_features2{};
1977 enabled_features2.sType = VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_FEATURES_2;
1978 enabled_features2.features.shaderFloat64 = features2.features.shaderFloat64;
1979 enabled_features2.features.fillModeNonSolid = features2.features.fillModeNonSolid;
1980 enabled_features2.features.samplerAnisotropy = features2.features.samplerAnisotropy;
1981
1982 enabled_features2.pNext = &vulkan13_features;
1983
1984 VkDeviceCreateInfo create_info{};
1985 create_info.sType = VK_STRUCTURE_TYPE_DEVICE_CREATE_INFO;
1986 create_info.pNext = &enabled_features2;
1987 create_info.queueCreateInfoCount = static_cast<uint32_t>(queue_create_infos.size());
1988 create_info.pQueueCreateInfos = queue_create_infos.data();
1989 create_info.enabledExtensionCount = static_cast<uint32_t>(required_device_extensions.size());
1990 create_info.ppEnabledExtensionNames = required_device_extensions.data();
1991 create_info.enabledLayerCount = 0;
1992 create_info.ppEnabledLayerNames = nullptr;
1993 create_info.pEnabledFeatures = nullptr;
1994
1995 std::cout << "vk: creating logical device\n";
1996 const VkResult create_device_result = vkCreateDevice(physical_device, &create_info, nullptr, &device);
1997 if (create_device_result != VK_SUCCESS) {
1998 std::cerr << std::format("vk: logical device creation failed (VkResult={})", static_cast<int>(create_device_result));
1999 if (!required_device_extensions.empty()) {
2000 std::cerr << "; requested extensions:";
2001 for (const char *extension_name : required_device_extensions) {
2002 std::cerr << ' ' << extension_name;
2003 }
2004 }
2005 std::cerr << '\n';
2006 device = VK_NULL_HANDLE;
2007 return;
2008 }
2009
2010 std::cout << "vk: loading device-level function pointers via volk\n";
2011 volkLoadDevice(device);
2012 if (vkQueueSubmit2 == nullptr || vkCmdBeginRendering == nullptr || vkCmdEndRendering == nullptr || vkCmdPipelineBarrier2 == nullptr) {
2013 std::cout << "vk: required dynamic rendering/synchronization function pointers are unavailable\n";
2014 vkDestroyDevice(device, nullptr);
2015 device = VK_NULL_HANDLE;
2016 return;
2017 }
2018 std::cout << (headless() ? "vk: retrieving graphics queue\n" : "vk: retrieving graphics and present queues\n");
2019 vkGetDeviceQueue(device, graphics_queue_family, 0, &graphics_queue);
2020 if (headless()) {
2022 } else {
2023 vkGetDeviceQueue(device, present_queue_family, 0, &present_queue);
2024 }
2025 std::cout << "mxvk: createLogicalDevice complete\n";
2026 }
uint32_t present_queue_family
Definition mxvk.hpp:608
VkQueue present_queue
Definition mxvk.hpp:610
uint32_t graphics_queue_family
Definition mxvk.hpp:607

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

154{ return mxvk::create_shader_module(device, spv_bytes); }
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 4455 of file mxvk.cpp.

4455 {
4456 if (device == VK_NULL_HANDLE) {
4457 throw mxvk::Exception("Cannot create sprite before Vulkan device initialization");
4458 }
4459 if (!renderTargetsReady() || command_pool == VK_NULL_HANDLE) {
4460 createDevice();
4461 }
4462 if (!renderTargetsReady() || command_pool == VK_NULL_HANDLE) {
4463 throw mxvk::Exception("Cannot create sprite before swapchain and command resources are available");
4464 }
4465
4466 if (sprite_descriptor_set_layout == VK_NULL_HANDLE) {
4467 createSpriteDescriptorSetLayout();
4468 }
4469
4470 auto sprite = std::make_unique<VK_Sprite>(device, physical_device, graphics_queue, command_pool);
4471 sprite->setPipelineCache(pipeline_cache);
4472 sprite->setDescriptorSetLayout(sprite_descriptor_set_layout);
4473 sprite->setColorAttachmentFormat(swapchain_format);
4474 sprite->setDepthAttachmentFormat(depth_format);
4475
4476 if (!vertexShaderPath.empty()) {
4477 sprite->setVertexShaderPath(vertexShaderPath);
4478 }
4479
4480 sprite->loadSprite(pngPath, fragmentShaderPath);
4481
4482 VK_Sprite *const sprite_ptr = sprite.get();
4483 sprites.push_back(std::move(sprite));
4484 sprite_state_dirty = true;
4485 return sprite_ptr;
4486 }
VkDescriptorSetLayout sprite_descriptor_set_layout
Definition mxvk.hpp:679
VkFormat depth_format
Definition mxvk.hpp:615
void createDevice()
Create final device resources.
Definition mxvk.cpp:2028
VkPipelineCache pipeline_cache
Definition mxvk.hpp:611

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

4529 {
4530 if (width <= 0 || height <= 0) {
4531 throw mxvk::Exception("Sprite dimensions must be positive");
4532 }
4533 if (device == VK_NULL_HANDLE) {
4534 throw mxvk::Exception("Cannot create sprite before Vulkan device initialization");
4535 }
4536 if (!renderTargetsReady() || command_pool == VK_NULL_HANDLE) {
4537 createDevice();
4538 }
4539 if (!renderTargetsReady() || command_pool == VK_NULL_HANDLE) {
4540 throw mxvk::Exception("Cannot create sprite before swapchain and command resources are available");
4541 }
4542
4543 if (sprite_descriptor_set_layout == VK_NULL_HANDLE) {
4544 createSpriteDescriptorSetLayout();
4545 }
4546
4547 auto sprite = std::make_unique<VK_Sprite>(device, physical_device, graphics_queue, command_pool);
4548 sprite->setPipelineCache(pipeline_cache);
4549 sprite->setDescriptorSetLayout(sprite_descriptor_set_layout);
4550 sprite->setColorAttachmentFormat(swapchain_format);
4551 sprite->setDepthAttachmentFormat(depth_format);
4552 if (!fragmentShaderPath.empty()) {
4553 sprite->enableExtendedUBO();
4554 }
4555 if (spectrumBinCount > 0) {
4556 sprite->enableSpectrumTexture(spectrumBinCount);
4557 }
4558 if (spectrumHistoryLayerCount > 0) {
4559 if (spectrumBinCount == 0) {
4560 throw mxvk::Exception("Spectrum history requires a positive spectrum bin count");
4561 }
4562 sprite->enableSpectrumHistoryTexture(spectrumBinCount, spectrumHistoryLayerCount);
4563 }
4564
4565 sprite->createEmptySprite(width, height, vertexShaderPath, fragmentShaderPath);
4566
4567 VK_Sprite *const sprite_ptr = sprite.get();
4568 sprites.push_back(std::move(sprite));
4569 sprite_state_dirty = true;
4570 return sprite_ptr;
4571 }

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

4527{ return createSprite(width, height, vertexShaderPath, fragmentShaderPath, spectrumBinCount, 0); }

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

4488 {
4489 if (surface == nullptr) {
4490 throw mxvk::Exception("Cannot create sprite from a null SDL surface");
4491 }
4492 if (device == VK_NULL_HANDLE) {
4493 throw mxvk::Exception("Cannot create sprite before Vulkan device initialization");
4494 }
4495 if (!renderTargetsReady() || command_pool == VK_NULL_HANDLE) {
4496 createDevice();
4497 }
4498 if (!renderTargetsReady() || command_pool == VK_NULL_HANDLE) {
4499 throw mxvk::Exception("Cannot create sprite before swapchain and command resources are available");
4500 }
4501
4502 if (sprite_descriptor_set_layout == VK_NULL_HANDLE) {
4503 createSpriteDescriptorSetLayout();
4504 }
4505
4506 auto sprite = std::make_unique<VK_Sprite>(device, physical_device, graphics_queue, command_pool);
4507 sprite->setPipelineCache(pipeline_cache);
4508 sprite->setDescriptorSetLayout(sprite_descriptor_set_layout);
4509 sprite->setColorAttachmentFormat(swapchain_format);
4510 sprite->setDepthAttachmentFormat(depth_format);
4511
4512 if (!vertexShaderPath.empty()) {
4513 sprite->setVertexShaderPath(vertexShaderPath);
4514 }
4515 if (!fragmentShaderPath.empty()) {
4516 sprite->enableExtendedUBO();
4517 }
4518
4519 sprite->loadSprite(surface, fragmentShaderPath);
4520
4521 VK_Sprite *const sprite_ptr = sprite.get();
4522 sprites.push_back(std::move(sprite));
4523 sprite_state_dirty = true;
4524 return sprite_ptr;
4525 }
VkSurfaceKHR surface
Definition mxvk.hpp:604

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

4573 {
4574 if (device == VK_NULL_HANDLE) {
4575 throw mxvk::Exception("Cannot create 3D sprite before Vulkan device initialization");
4576 }
4577 if (!renderTargetsReady() || command_pool == VK_NULL_HANDLE) {
4578 createDevice();
4579 }
4580 if (!renderTargetsReady() || command_pool == VK_NULL_HANDLE) {
4581 throw mxvk::Exception("Cannot create 3D sprite before swapchain and command resources are available");
4582 }
4583
4584 const std::string vertPath = vertexShaderPath.empty() ? resolveRuntimeShaderPath("sprite3d.vert.spv", MXVK_SPRITE3D_SHADER_DIR) : vertexShaderPath;
4585 const std::string fragPath = fragmentShaderPath.empty() ? resolveRuntimeShaderPath("sprite3d.frag.spv", MXVK_SPRITE3D_SHADER_DIR) : fragmentShaderPath;
4586
4587 auto sprite = std::make_unique<VK_Sprite3D>();
4588 sprite->load(this, pngPath, vertPath, fragPath);
4589
4590 VK_Sprite3D *const sprite_ptr = sprite.get();
4591 sprites3d.push_back(std::move(sprite));
4592 return sprite_ptr;
4593 }
std::vector< std::unique_ptr< VK_Sprite3D > > sprites3d
Definition mxvk.hpp:678

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

4595 {
4596 if (surface == nullptr) {
4597 throw mxvk::Exception("Cannot create 3D sprite from a null SDL surface");
4598 }
4599 if (device == VK_NULL_HANDLE) {
4600 throw mxvk::Exception("Cannot create 3D sprite before Vulkan device initialization");
4601 }
4602 if (!renderTargetsReady() || command_pool == VK_NULL_HANDLE) {
4603 createDevice();
4604 }
4605 if (!renderTargetsReady() || command_pool == VK_NULL_HANDLE) {
4606 throw mxvk::Exception("Cannot create 3D sprite before swapchain and command resources are available");
4607 }
4608
4609 const std::string vertPath = vertexShaderPath.empty() ? resolveRuntimeShaderPath("sprite3d.vert.spv", MXVK_SPRITE3D_SHADER_DIR) : vertexShaderPath;
4610 const std::string fragPath = fragmentShaderPath.empty() ? resolveRuntimeShaderPath("sprite3d.frag.spv", MXVK_SPRITE3D_SHADER_DIR) : fragmentShaderPath;
4611
4612 auto sprite = std::make_unique<VK_Sprite3D>();
4613 sprite->load(this, surface, vertPath, fragPath);
4614
4615 VK_Sprite3D *const sprite_ptr = sprite.get();
4616 sprites3d.push_back(std::move(sprite));
4617 return sprite_ptr;
4618 }

◆ debugCallback()

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

Definition at line 67 of file mxvk.cpp.

67 {
68 const char *message = (callback_data != nullptr && callback_data->pMessage != nullptr) ? callback_data->pMessage : "Unknown Vulkan validation message";
69
70 if ((severity & VK_DEBUG_UTILS_MESSAGE_SEVERITY_ERROR_BIT_EXT) != 0U) {
71 std::cerr << std::format("vk validation error: {}\n", message);
72 } else if ((severity & VK_DEBUG_UTILS_MESSAGE_SEVERITY_WARNING_BIT_EXT) != 0U) {
73 std::cerr << std::format("vk validation warning: {}\n", message);
74 } else {
75 std::cout << std::format("vk validation: {}\n", message);
76 }
77 return VK_FALSE;
78 }

◆ destroyFrameReadbackResources()

void mxvk::VK_Window::destroyFrameReadbackResources ( )
protectedinherited

Definition at line 1252 of file mxvk.cpp.

1252 {
1253 for (FrameReadbackSlot &slot : frame_readback_slots) {
1254 if (device != VK_NULL_HANDLE && slot.mapped != nullptr && slot.memory != VK_NULL_HANDLE) {
1255 vkUnmapMemory(device, slot.memory);
1256 }
1257 slot.mapped = nullptr;
1258 if (device != VK_NULL_HANDLE && slot.buffer != VK_NULL_HANDLE) {
1259 vkDestroyBuffer(device, slot.buffer, nullptr);
1260 }
1261 slot.buffer = VK_NULL_HANDLE;
1262 if (device != VK_NULL_HANDLE && slot.memory != VK_NULL_HANDLE) {
1263 vkFreeMemory(device, slot.memory, nullptr);
1264 }
1265 slot.memory = VK_NULL_HANDLE;
1266 slot.size = 0;
1267 slot.width = 0;
1268 slot.height = 0;
1269 slot.format = VK_FORMAT_UNDEFINED;
1270 slot.pending = false;
1271 }
1276 }
std::array< FrameReadbackSlot, max_frames_in_flight > frame_readback_slots
Definition mxvk.hpp:657

◆ detachPostProcessingShader()

void mxvk::VK_Window::detachPostProcessingShader ( )
inherited

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

Definition at line 1566 of file mxvk.cpp.

1566 {
1567 if (device != VK_NULL_HANDLE && !post_process_sprites.empty()) {
1568 vkDeviceWaitIdle(device);
1569 }
1570
1571 post_process_sprite = nullptr;
1572 destroyPostProcessTargets();
1576
1577 if (!owned_post_process_sprites.empty()) {
1578 const std::vector<VK_Sprite *> sprites_to_remove = owned_post_process_sprites;
1579 sprites.erase(std::remove_if(sprites.begin(), sprites.end(), [&sprites_to_remove](const std::unique_ptr<VK_Sprite> &sprite) { return std::ranges::find(sprites_to_remove, sprite.get()) != sprites_to_remove.end(); }), sprites.end());
1580 sprite_state_dirty = true;
1581 }
1582 owned_post_process_sprite = nullptr;
1584 post_process_sprites.clear();
1589 }

◆ dispatchFrameReadback()

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

Definition at line 1095 of file mxvk.cpp.

1095 {
1096 if (!slot.pending) {
1097 return;
1098 }
1099 if (slot.mapped == nullptr || slot.width == 0U || slot.height == 0U) {
1100 slot.pending = false;
1101 return;
1102 }
1103
1104 if (slot.format == VK_FORMAT_R16G16B16A16_SFLOAT) {
1105 const auto *source = static_cast<const std::uint16_t *>(slot.mapped);
1106 const std::size_t component_count = static_cast<std::size_t>(slot.width) * slot.height * 4U;
1107 latest_frame_readback_rgba16.resize(component_count);
1108 for (std::size_t component = 0; component < component_count; ++component) {
1109 const float normalized = std::clamp(half_to_float(source[component]), 0.0F, 1.0F);
1110 latest_frame_readback_rgba16[component] = static_cast<std::uint16_t>(std::lround(normalized * 65535.0F));
1111 }
1112 latest_frame_readback_width = slot.width;
1113 latest_frame_readback_height = slot.height;
1114 slot.pending = false;
1116 return;
1117 }
1118
1119 const auto *source = static_cast<const std::uint8_t *>(slot.mapped);
1120 const std::size_t pixel_bytes = static_cast<std::size_t>(slot.width) * slot.height * 4U;
1121 latest_frame_readback_rgba.resize(pixel_bytes);
1122 const bool format_is_bgra = slot.format == VK_FORMAT_B8G8R8A8_UNORM || slot.format == VK_FORMAT_B8G8R8A8_SRGB;
1123 for (std::size_t offset = 0; offset < pixel_bytes; offset += 4U) {
1124 if (format_is_bgra) {
1125 latest_frame_readback_rgba[offset + 0U] = source[offset + 2U];
1126 latest_frame_readback_rgba[offset + 1U] = source[offset + 1U];
1127 latest_frame_readback_rgba[offset + 2U] = source[offset + 0U];
1128 latest_frame_readback_rgba[offset + 3U] = source[offset + 3U];
1129 } else {
1130 latest_frame_readback_rgba[offset + 0U] = source[offset + 0U];
1131 latest_frame_readback_rgba[offset + 1U] = source[offset + 1U];
1132 latest_frame_readback_rgba[offset + 2U] = source[offset + 2U];
1133 latest_frame_readback_rgba[offset + 3U] = source[offset + 3U];
1134 }
1135 }
1136
1137 latest_frame_readback_width = slot.width;
1138 latest_frame_readback_height = slot.height;
1139 slot.pending = false;
1141 }
virtual void onFrameReadbackRgba16(std::vector< std::uint16_t > &rgba_pixels, uint32_t width, uint32_t height)
Receive normalized RGBA16 pixels from an RGBA16F HDR target.
Definition mxvk.cpp:1091
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:1089
float half_to_float(uint16_t value) noexcept
Definition mxvk.cpp:46

◆ enablePostProcessing()

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

Definition at line 1635 of file mxvk.cpp.

1635 {
1636 if (owned_post_process_sprite != nullptr && sprite != owned_post_process_sprite) {
1637 std::vector<VK_Sprite *> sprites_to_remove = owned_post_process_sprites;
1638 if (sprites_to_remove.empty()) {
1639 sprites_to_remove.push_back(owned_post_process_sprite);
1640 }
1641 sprites.erase(std::remove_if(sprites.begin(), sprites.end(), [&sprites_to_remove](const std::unique_ptr<VK_Sprite> &existing_sprite) { return std::ranges::find(sprites_to_remove, existing_sprite.get()) != sprites_to_remove.end(); }), sprites.end());
1642 owned_post_process_sprite = nullptr;
1644 sprite_state_dirty = true;
1645 }
1646 post_process_sprite = sprite;
1647 post_process_sprites = sprite == nullptr ? std::vector<VK_Sprite *>{} : std::vector<VK_Sprite *>{sprite};
1651 if (device != VK_NULL_HANDLE && !swapchain_images.empty()) {
1652 createPostProcessTargets();
1653 }
1654 }

◆ ensureFrameReadbackResources()

void mxvk::VK_Window::ensureFrameReadbackResources ( )
protectedinherited

Definition at line 1162 of file mxvk.cpp.

1162 {
1164 return;
1165 }
1167 throw mxvk::Exception("frame readback requires swapchain transfer-source support");
1168 }
1169
1170 const VkExtent2D readback_extent = getRenderExtent();
1171 const VkDeviceSize required_size = static_cast<VkDeviceSize>(readback_extent.width) * readback_extent.height * (frame_readback_rgba16_enabled ? 8U : 4U);
1172 if (required_size == 0U) {
1173 throw mxvk::Exception("frame readback cannot use an empty swapchain extent");
1174 }
1175 const bool resources_ready = std::ranges::all_of(frame_readback_slots, [&](const FrameReadbackSlot &slot) { return slot.buffer != VK_NULL_HANDLE && slot.mapped != nullptr && slot.size >= required_size; });
1176 if (resources_ready) {
1177 return;
1178 }
1179
1180 if (device != VK_NULL_HANDLE) {
1182 vkDeviceWaitIdle(device);
1183 }
1185
1186 VkBufferCreateInfo buffer_info{};
1187 buffer_info.sType = VK_STRUCTURE_TYPE_BUFFER_CREATE_INFO;
1188 buffer_info.size = required_size;
1189 buffer_info.usage = VK_BUFFER_USAGE_TRANSFER_DST_BIT;
1190 buffer_info.sharingMode = VK_SHARING_MODE_EXCLUSIVE;
1191 VkPhysicalDeviceMemoryProperties memory_properties{};
1192 vkGetPhysicalDeviceMemoryProperties(physical_device, &memory_properties);
1193 constexpr VkMemoryPropertyFlags REQUIRED_PROPERTIES = VK_MEMORY_PROPERTY_HOST_VISIBLE_BIT | VK_MEMORY_PROPERTY_HOST_COHERENT_BIT;
1194 constexpr VkMemoryPropertyFlags PREFERRED_PROPERTIES = REQUIRED_PROPERTIES | VK_MEMORY_PROPERTY_HOST_CACHED_BIT;
1195 bool memory_type_logged = false;
1196 try {
1197 for (FrameReadbackSlot &slot : frame_readback_slots) {
1198 if (vkCreateBuffer(device, &buffer_info, nullptr, &slot.buffer) != VK_SUCCESS) {
1199 throw mxvk::Exception("failed to create pipelined frame-readback buffer");
1200 }
1201
1202 VkMemoryRequirements memory_requirements{};
1203 vkGetBufferMemoryRequirements(device, slot.buffer, &memory_requirements);
1204 uint32_t memory_type_index = invalid_queue_index;
1205 for (VkMemoryPropertyFlags properties : {PREFERRED_PROPERTIES, REQUIRED_PROPERTIES}) {
1206 for (uint32_t index = 0; index < memory_properties.memoryTypeCount; ++index) {
1207 const bool type_matches = (memory_requirements.memoryTypeBits & (1U << index)) != 0U;
1208 const bool properties_match = (memory_properties.memoryTypes[index].propertyFlags & properties) == properties;
1209 if (type_matches && properties_match) {
1210 memory_type_index = index;
1211 break;
1212 }
1213 }
1214 if (memory_type_index != invalid_queue_index) {
1215 break;
1216 }
1217 }
1218 if (memory_type_index == invalid_queue_index) {
1219 throw mxvk::Exception("failed to find host-visible frame-readback memory");
1220 }
1221 if (!memory_type_logged) {
1222 const VkMemoryPropertyFlags selected_properties = memory_properties.memoryTypes[memory_type_index].propertyFlags;
1223 std::cout << std::format("mxvk: frame readback memory type {} ({})\n", memory_type_index, (selected_properties & VK_MEMORY_PROPERTY_HOST_CACHED_BIT) != 0U ? "host cached" : "host coherent fallback");
1224 memory_type_logged = true;
1225 }
1226
1227 VkMemoryAllocateInfo allocation_info{};
1228 allocation_info.sType = VK_STRUCTURE_TYPE_MEMORY_ALLOCATE_INFO;
1229 allocation_info.allocationSize = memory_requirements.size;
1230 allocation_info.memoryTypeIndex = memory_type_index;
1231 if (vkAllocateMemory(device, &allocation_info, nullptr, &slot.memory) != VK_SUCCESS) {
1232 throw mxvk::Exception("failed to allocate frame-readback memory");
1233 }
1234 if (vkBindBufferMemory(device, slot.buffer, slot.memory, 0) != VK_SUCCESS) {
1235 throw mxvk::Exception("failed to bind frame-readback memory");
1236 }
1237 if (vkMapMemory(device, slot.memory, 0, required_size, 0, &slot.mapped) != VK_SUCCESS) {
1238 throw mxvk::Exception("failed to map frame-readback memory");
1239 }
1240 slot.size = required_size;
1241 }
1242 } catch (...) {
1244 throw;
1245 }
1250 }
void flushFrameReadbacks()
Wait for and deliver all pending pipelined frame readbacks.
Definition mxvk.cpp:1143
bool frame_readback_enabled
Definition mxvk.hpp:655
bool frame_readback_rgba16_enabled
Definition mxvk.hpp:656
void destroyFrameReadbackResources()
Definition mxvk.cpp:1252

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

1787 {
1788 const auto sync_ready = [this]() { return std::ranges::all_of(image_available.begin(), image_available.end(), [](VkSemaphore semaphore) { return semaphore != VK_NULL_HANDLE; }) && render_finished.size() == swapchain_images.size() && std::ranges::all_of(render_finished.begin(), render_finished.end(), [](VkSemaphore semaphore) { return semaphore != VK_NULL_HANDLE; }) && std::ranges::all_of(in_flight_fences.begin(), in_flight_fences.end(), [](VkFence fence) { return fence != VK_NULL_HANDLE; }); };
1789
1790 if (device == VK_NULL_HANDLE) {
1791 return false;
1792 }
1793
1794 if (!renderTargetsReady() || command_pool == VK_NULL_HANDLE || command_buffers.empty() || !sync_ready() || image_fences.size() != swapchain_images.size()) {
1795 createDevice();
1796 }
1797
1798 return (renderTargetsReady() && command_pool != VK_NULL_HANDLE && !command_buffers.empty() && sync_ready() && image_fences.size() == swapchain_images.size());
1799 }

◆ event()

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

Handle one SDL event.

Parameters
eSDL event to process.

Reimplemented from mxvk::VK_Window.

Definition at line 422 of file main.cpp.

422 {
423 if (event.type == SDL_EVENT_KEY_DOWN && !event.key.repeat) {
424 switch (event.key.key) {
425 case SDLK_ESCAPE:
426 exit();
427 break;
428 case SDLK_SPACE:
429 game.toggle_pause();
430 break;
431 case SDLK_R:
432 game.reset();
433 break;
434 default:
435 break;
436 }
437 }
438 if (event.type == SDL_EVENT_MOUSE_MOTION) {
439 const float normalized = 1.0f - 2.0f * event.motion.y / static_cast<float>(std::max(1, output_height));
440 game.set_player_position(normalized * COURT_HALF_HEIGHT);
441 }
442 }
void event(SDL_Event &event) override
Handle one SDL event.
Definition main.cpp:422
void exit()
Request loop termination.
Definition mxvk.cpp:1378

◆ exit()

void mxvk::VK_Window::exit ( )
protectedinherited

Request loop termination.

Definition at line 1378 of file mxvk.cpp.

1378{ active = false; }

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

1143 {
1144 if (device == VK_NULL_HANDLE) {
1145 return;
1146 }
1147 const bool has_pending = std::ranges::any_of(frame_readback_slots, [](const FrameReadbackSlot &slot) { return slot.pending; });
1148 if (!has_pending) {
1149 return;
1150 }
1151
1152 const VkResult wait_result = vkDeviceWaitIdle(device);
1153 if (wait_result != VK_SUCCESS) {
1154 throw mxvk::Exception(std::format("frame readback flush failed waiting for the device (VkResult={})", static_cast<int>(wait_result)));
1155 }
1156 for (uint32_t offset = 0; offset < max_frames_in_flight; ++offset) {
1157 const uint32_t slot_index = (current_frame + offset) % max_frames_in_flight;
1159 }
1160 }
void dispatchFrameReadback(FrameReadbackSlot &slot)
Definition mxvk.cpp:1095
uint32_t current_frame
Definition mxvk.hpp:634

◆ getCommandPool()

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

Get the command pool used for graphics/upload work.

Definition at line 207 of file mxvk.hpp.

207{ return command_pool; }

◆ getDepthFormat()

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

Get the depth format used for dynamic rendering attachments.

Definition at line 236 of file mxvk.hpp.

236{ return depth_format; }

◆ getDevice()

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

Get the Vulkan logical device handle.

Definition at line 198 of file mxvk.hpp.

198{ return device; }

◆ getGraphicsQueue()

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

Get the graphics queue handle.

Definition at line 204 of file mxvk.hpp.

204{ return graphics_queue; }

◆ getPhysicalDevice()

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

Get the Vulkan physical device handle.

Definition at line 201 of file mxvk.hpp.

201{ return physical_device; }

◆ getPipelineCache()

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

Get the persistent Vulkan pipeline cache used by framework pipelines.

Definition at line 210 of file mxvk.hpp.

210{ return pipeline_cache; }

◆ getRenderExtent()

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

Get the active scene/post-processing extent.

Definition at line 1451 of file mxvk.cpp.

◆ getSceneColorFormat()

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

Get the active scene/effect color-attachment format.

Definition at line 219 of file mxvk.hpp.

219{ return hdr_render_intermediates_enabled ? VK_FORMAT_R16G16B16A16_SFLOAT : swapchain_format; }

◆ getSDLWindow()

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

Get the underlying SDL window handle.

Definition at line 192 of file mxvk.hpp.

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

◆ getSwapchainExtent()

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

Get the current swapchain extent.

Definition at line 222 of file mxvk.hpp.

222{ return swapchain_extent; }

◆ getSwapchainFormat()

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

Get the swapchain color format.

Definition at line 213 of file mxvk.hpp.

213{ return swapchain_format; }

◆ getSwapchainImageCount()

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

Get the number of swapchain images currently allocated.

Definition at line 239 of file mxvk.hpp.

239{ 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 4004 of file mxvk.cpp.

4004 {
4005 if (!font_configured) {
4006 throw mxvk::Exception("getTextDimensions requires setFont() to be called first");
4007 }
4008
4009 if (!text_renderer) {
4010 ensureTextRenderer();
4011 }
4012 if (!text_renderer) {
4013 width = 0;
4014 height = 0;
4015 return false;
4016 }
4017 return text_renderer->getTextDimensions(text, width, height);
4018 }
bool font_configured
Definition mxvk.hpp:713

◆ getTextDimensions() [2/3]

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

Definition at line 4036 of file mxvk.cpp.

4036 {
4037 if (!font) {
4038 width = 0;
4039 height = 0;
4040 return false;
4041 }
4042
4043 ensureTextRenderer(font.path(), font.size());
4044 if (!text_renderer) {
4045 width = 0;
4046 height = 0;
4047 return false;
4048 }
4049 return text_renderer->getTextDimensions(text, width, height, font);
4050 }

◆ getTextDimensions() [3/3]

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

Definition at line 4020 of file mxvk.cpp.

4020 {
4021 if (font == nullptr) {
4022 width = 0;
4023 height = 0;
4024 return false;
4025 }
4026
4027 ensureTextRenderer(resolveDefaultFontPath(), font_size);
4028 if (!text_renderer) {
4029 width = 0;
4030 height = 0;
4031 return false;
4032 }
4033 return text_renderer->getTextDimensions(text, width, height, font);
4034 }

◆ headless()

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

Return true when rendering without SDL video, a surface, or presentation.

Definition at line 195 of file mxvk.hpp.

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

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

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

1298 {
1299 std::cout << std::format("mxvk: entering initWindow (title='{}', width={}, height={})\n", title, width, height);
1300 if (width <= 0 || height <= 0) {
1301 std::cerr << "mxvk: Window dimensions must be positive\n";
1302 return false;
1303 }
1304
1305 if (!sdl_initialized) {
1306 constexpr SDL_InitFlags sdlInitFlags = SDL_INIT_VIDEO | SDL_INIT_GAMEPAD;
1307 std::cout << "SDL3: initializing video/gamepad subsystems\n";
1308 if (!SDL_Init(sdlInitFlags)) {
1309 std::cerr << "mxvk: Error on SDL init: " << SDL_GetError() << "\n";
1310 return false;
1311 }
1312 sdl_initialized = true;
1313 SDL_SetGamepadEventsEnabled(true);
1314 SDL_SetJoystickEventsEnabled(true);
1315 std::cout << "SDL3: video/gamepad subsystems initialized\n";
1316 }
1317
1318 if (!vulkan_library_loaded) {
1319 std::cout << "SDL3: loading the Vulkan loader library\n";
1320 if (!SDL_Vulkan_LoadLibrary(nullptr)) {
1321 std::cerr << std::format("mxvk: SDL_Vulkan_LoadLibrary failed: {}\n", SDL_GetError());
1322 SDL_QuitSubSystem(SDL_INIT_VIDEO | SDL_INIT_GAMEPAD);
1323 sdl_initialized = false;
1324 return false;
1325 }
1326 vulkan_library_loaded = true;
1327 }
1328
1329 const bool fullscreen = (flags & SDL_WINDOW_FULLSCREEN) != 0;
1330 const SDL_DisplayMode *fullscreen_mode = nullptr;
1331 if (fullscreen) {
1332 const SDL_DisplayID display = SDL_GetPrimaryDisplay();
1333 fullscreen_mode = display != 0 ? SDL_GetCurrentDisplayMode(display) : nullptr;
1334 if (fullscreen_mode != nullptr && fullscreen_mode->w > 0 && fullscreen_mode->h > 0) {
1335 width = fullscreen_mode->w;
1336 height = fullscreen_mode->h;
1337 std::cout << std::format("SDL3: using current display mode for fullscreen window: {}x{}\n", width, height);
1338 }
1339 }
1340
1341 std::cout << "SDL3: creating SDL window with Vulkan capability\n";
1342 SDL_Window *raw_window = SDL_CreateWindow(title.c_str(), width, height, flags);
1343 if (raw_window == nullptr) {
1344 std::cerr << std::format("Error creating window: {}\n", SDL_GetError());
1345 SDL_Vulkan_UnloadLibrary();
1346 vulkan_library_loaded = false;
1347 std::cout << "SDL3: rolling back SDL video/gamepad subsystems after window creation failure\n";
1348 SDL_QuitSubSystem(SDL_INIT_VIDEO | SDL_INIT_GAMEPAD);
1349 sdl_initialized = false;
1350 return false;
1351 }
1352
1353 window.reset(raw_window);
1354 if (fullscreen && fullscreen_mode != nullptr && !SDL_SetWindowFullscreenMode(window.get(), fullscreen_mode)) {
1355 std::cerr << std::format("mxvk: SDL_SetWindowFullscreenMode failed: {}\n", SDL_GetError());
1356 }
1357 std::cout << "SDL3: showing created window\n";
1358 SDL_ShowWindow(window.get());
1359 if (fullscreen && !SDL_SyncWindow(window.get())) {
1360 std::cerr << std::format("mxvk: SDL_SyncWindow failed after fullscreen show: {}\n", SDL_GetError());
1361 }
1362 if (fullscreen) {
1363 for (int attempt = 0; attempt < 50; ++attempt) {
1364 int pixel_width = 0;
1365 int pixel_height = 0;
1366 SDL_GetWindowSizeInPixels(window.get(), &pixel_width, &pixel_height);
1367 if (pixel_width == width && pixel_height == height) {
1368 break;
1369 }
1370 SDL_PumpEvents();
1371 SDL_Delay(10);
1372 }
1373 }
1374 std::cout << "mxvk: initWindow complete\n";
1375 return true;
1376 }
bool vulkan_library_loaded
Definition mxvk.hpp:639
bool sdl_initialized
Definition mxvk.hpp:638

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

152{ return mxvk::load_spv(path); }

◆ loop()

void mxvk::VK_Window::loop ( )
inherited

Run the main event/render loop.

Definition at line 651 of file mxvk.cpp.

651 {
652 if (headless()) {
653 active = true;
654 while (active) {
655 proc();
656 render();
657 maybeTrimMemory();
658 }
660 return;
661 }
662
663 SDL_Event e;
664 active = true;
665 while (active) {
666 while (SDL_PollEvent(&e)) {
667 switch (e.type) {
668 case SDL_EVENT_QUIT:
669 active = false;
670 break;
671 case SDL_EVENT_WINDOW_PIXEL_SIZE_CHANGED:
672 if (window != nullptr) {
673 framebuffer_resized = true;
674 last_resize_event_ms = SDL_GetTicks();
676 }
677 break;
678 case SDL_EVENT_KEY_DOWN:
679 if ((e.key.key == SDLK_F12 || e.key.scancode == SDL_SCANCODE_F12) && !e.key.repeat) {
680 toggleFpsCounter();
681 }
682 if (screenshot_enabled && (e.key.key == SDLK_F10 || e.key.scancode == SDL_SCANCODE_F10) && !e.key.repeat) {
683 try {
684 saveScreenshot();
685 } catch (const mxvk::Exception &ex) {
686 std::cerr << std::format("mxvk: screenshot failed: {}\n", ex.text());
687 } catch (const std::exception &ex) {
688 std::cerr << std::format("mxvk: screenshot failed: {}\n", ex.what());
689 }
690 continue;
691 }
692 break;
693 default:
694 break;
695 }
696 event(e);
697 }
699 const uint64_t now_ms = SDL_GetTicks();
701 proc();
702 render();
703 SDL_Delay(1);
704 continue;
705 }
706 recreateSwapchain();
707 }
708 proc();
709 render();
710 maybeTrimMemory();
711 }
712 }
std::string text() const
bool force_swapchain_recreate
Definition mxvk.hpp:671
virtual void event(SDL_Event &e)
Handle one SDL event.
Definition mxvk.cpp:626
static constexpr uint64_t resize_settle_delay_ms
Definition mxvk.hpp:673
bool screenshot_enabled
Definition mxvk.hpp:641
bool framebuffer_resized
Definition mxvk.hpp:670
virtual void render()
Render one frame.
Definition mxvk.cpp:824
virtual void proc()
Execute one processing/update step.
Definition mxvk.cpp:1278
uint64_t last_resize_event_ms
Definition mxvk.hpp:672

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

1390{}

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

Reimplemented in anonymous_namespace{mxvk_nanobind.cpp}::PythonWindow, and example::HeadlessExample.

Definition at line 1089 of file mxvk.cpp.

1089{}

◆ onFrameReadbackRgba16()

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

Receive normalized RGBA16 pixels from an RGBA16F HDR target.

Definition at line 1091 of file mxvk.cpp.

1091{}

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

Reimplemented in anonymous_namespace{mxvk_nanobind.cpp}::PythonWindow.

Definition at line 1093 of file mxvk.cpp.

1093{}

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

1384{}

◆ onRecordCustomRendering()

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

1388{}

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

1380{}

◆ onSwapchainRecreated()

◆ pickDevice()

void mxvk::VK_Window::pickDevice ( )
protectedinherited

Pick a suitable Vulkan physical device.

Definition at line 1808 of file mxvk.cpp.

1808 {
1809 std::cout << "mxvk: entering pickDevice\n";
1810 if (instance == VK_NULL_HANDLE || (!headless() && surface == VK_NULL_HANDLE)) {
1811 std::cout << "vk: cannot pick device because required instance state is missing\n";
1812 return;
1813 }
1814
1815 uint32_t device_count = 0;
1816 std::cout << "vk: enumerating physical devices\n";
1817 vkEnumeratePhysicalDevices(instance, &device_count, nullptr);
1818 if (device_count == 0U) {
1819 std::cout << "vk: no physical devices found\n";
1820 return;
1821 }
1822 std::cout << std::format("vk: found {} physical device(s)\n", device_count);
1823
1824 std::vector<VkPhysicalDevice> devices(device_count);
1825 vkEnumeratePhysicalDevices(instance, &device_count, devices.data());
1826
1827 for (const VkPhysicalDevice candidate : devices) {
1828 std::cout << "vk: evaluating candidate physical device\n";
1829 uint32_t queue_family_count = 0;
1830 vkGetPhysicalDeviceQueueFamilyProperties(candidate, &queue_family_count, nullptr);
1831 std::vector<VkQueueFamilyProperties> queue_families(queue_family_count);
1832 vkGetPhysicalDeviceQueueFamilyProperties(candidate, &queue_family_count, queue_families.data());
1833
1834 uint32_t candidate_graphics = invalid_queue_index;
1835 uint32_t candidate_present = invalid_queue_index;
1836
1837 for (uint32_t i = 0; i < queue_family_count; ++i) {
1838 if ((queue_families[i].queueFlags & VK_QUEUE_GRAPHICS_BIT) != 0U) {
1839 candidate_graphics = i;
1840 }
1841
1842 if (!headless()) {
1843 VkBool32 present_support = VK_FALSE;
1844 vkGetPhysicalDeviceSurfaceSupportKHR(candidate, i, surface, &present_support);
1845 if (present_support == VK_TRUE) {
1846 candidate_present = i;
1847 }
1848 }
1849 }
1850
1851 if (headless()) {
1852 candidate_present = candidate_graphics;
1853 }
1854
1855 if (candidate_graphics == invalid_queue_index || candidate_present == invalid_queue_index) {
1856 std::cout << "vk: candidate rejected due to missing graphics/present queue support\n";
1857 continue;
1858 }
1859
1860 if (!headless()) {
1861 const SwapchainSupport swapchain_support = querySwapchainSupport(candidate, surface);
1862 if (swapchain_support.formats.empty() || swapchain_support.present_modes.empty()) {
1863 std::cout << "vk: candidate rejected due to incomplete swapchain support\n";
1864 continue;
1865 }
1866 }
1867
1868 VkPhysicalDeviceProperties properties{};
1869 vkGetPhysicalDeviceProperties(candidate, &properties);
1870
1871 const char *device_type = "unknown";
1872 switch (properties.deviceType) {
1873 case VK_PHYSICAL_DEVICE_TYPE_INTEGRATED_GPU:
1874 device_type = "integrated";
1875 break;
1876 case VK_PHYSICAL_DEVICE_TYPE_DISCRETE_GPU:
1877 device_type = "discrete";
1878 break;
1879 case VK_PHYSICAL_DEVICE_TYPE_VIRTUAL_GPU:
1880 device_type = "virtual";
1881 break;
1882 case VK_PHYSICAL_DEVICE_TYPE_CPU:
1883 device_type = "cpu";
1884 break;
1885 default:
1886 break;
1887 }
1888
1889 physical_device = candidate;
1890 graphics_queue_family = candidate_graphics;
1891 present_queue_family = candidate_present;
1892 std::cout << std::format("vk: selected GPU='{}' type={} vendor=0x{:04x} device=0x{:04x}\n", properties.deviceName, device_type, properties.vendorID, properties.deviceID);
1893 return;
1894 }
1895
1896 std::cout << "vk: no suitable physical device selected\n";
1897 }

◆ postProcessIntermediateFormat()

VkFormat mxvk::VK_Window::postProcessIntermediateFormat ( ) const
inlinenodiscardprotectednoexceptinherited

Definition at line 703 of file mxvk.hpp.

703{ return hdr_render_intermediates_enabled ? VK_FORMAT_R16G16B16A16_SFLOAT : VK_FORMAT_R8G8B8A8_UNORM; }

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

3974 {
3975 if (text.empty()) {
3976 return;
3977 }
3978 if (!font_configured) {
3979 throw mxvk::Exception("printPreviewText requires setFont() to be called first");
3980 }
3981 ensurePreviewTextRenderer();
3982 if (!preview_text_renderer) {
3983 throw mxvk::Exception("printPreviewText could not initialize the text renderer");
3984 }
3985 preview_text_renderer->printTextG_Solid(text, x, y, col);
3986 preview_text_queued = true;
3987 }

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

3989 {
3990 if (text.empty()) {
3991 return;
3992 }
3993 if (!font) {
3994 throw mxvk::Exception("printPreviewText requires a valid mxvk::Font");
3995 }
3996 ensurePreviewTextRenderer(font.path(), font.size());
3997 if (!preview_text_renderer) {
3998 throw mxvk::Exception("printPreviewText could not initialize the text renderer");
3999 }
4000 preview_text_renderer->printTextG_Solid(text, x, y, col, font);
4001 preview_text_queued = true;
4002 }

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

3919 {
3920 if (text.empty()) {
3921 return;
3922 }
3923
3924 if (!font_configured) {
3925 throw mxvk::Exception("printText requires setFont() to be called first");
3926 }
3927
3928 ensureTextRenderer();
3929 text_renderer->printTextG_Solid(text, x, y, col);
3930 }

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

3948 {
3949 if (text.empty()) {
3950 return;
3951 }
3952
3953 if (!font) {
3954 throw mxvk::Exception("printText requires a valid mxvk::Font");
3955 }
3956
3957 ensureTextRenderer(font.path(), font.size());
3958 if (!text_renderer) {
3959 throw mxvk::Exception("printText with an explicit font could not initialize the text renderer");
3960 }
3961 text_renderer->printTextG_Solid(text, x, y, col, font);
3962 }

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

3932 {
3933 if (text.empty()) {
3934 return;
3935 }
3936
3937 if (font == nullptr) {
3938 throw mxvk::Exception("printText requires a non-null TTF_Font");
3939 }
3940
3941 ensureTextRenderer(resolveDefaultFontPath(), font_size);
3942 if (!text_renderer) {
3943 throw mxvk::Exception("printText with an explicit font could not initialize the text renderer");
3944 }
3945 text_renderer->printTextG_Solid(text, x, y, col, font);
3946 }

◆ proc()

void example::Math3DPongWindow::proc ( )
inlineoverridevirtual

Execute one processing/update step.

Reimplemented from mxvk::VK_Window.

Definition at line 444 of file main.cpp.

444 {
445 output_width = swapchain_extent.width > 0U ? static_cast<int>(swapchain_extent.width) : WINDOW_WIDTH;
446 output_height = swapchain_extent.height > 0U ? static_cast<int>(swapchain_extent.height) : WINDOW_HEIGHT;
447 ensure_sprite();
448 update_game();
449 draw_game();
450 frame_sprite->updateTexture(renderer.surface());
451 frame_sprite->drawSpriteRect(0, 0, output_width, output_height);
452 }
constexpr int WINDOW_HEIGHT
Definition main.cpp:29
constexpr int WINDOW_WIDTH
Definition main.cpp:28

◆ release()

void mxvk::VK_Window::release ( )
inherited

Release Vulkan and SDL resources.

Safe to call multiple times.

Definition at line 247 of file mxvk.cpp.

247 {
248 std::cout << "mxvk: starting resource teardown\n";
249 stopScreenshotWorker();
250
251 if (device != VK_NULL_HANDLE) {
252 std::cout << "vk: waiting for device idle before teardown\n";
253 vkDeviceWaitIdle(device);
254 }
255
257
258 post_process_sprite = nullptr;
262 post_process_sprites.clear();
268
269 if (!sprites.empty()) {
270 std::cout << std::format("vk: releasing {} sprite(s)\n", sprites.size());
271 sprites.clear();
272 std::cout << "vk: sprite resources released\n";
273 }
274 if (!sprites3d.empty()) {
275 std::cout << std::format("vk: releasing {} 3D sprite batch(es)\n", sprites3d.size());
276 sprites3d.clear();
277 }
278 sprite_state_dirty = false;
279 destroySpritePipeline();
280 if (sprite_descriptor_set_layout != VK_NULL_HANDLE && device != VK_NULL_HANDLE) {
281 vkDestroyDescriptorSetLayout(device, sprite_descriptor_set_layout, nullptr);
282 sprite_descriptor_set_layout = VK_NULL_HANDLE;
283 }
284
285 if (text_renderer) {
286 text_renderer.reset();
287 }
289 preview_text_renderer.reset();
290 }
291 preview_text_queued = false;
292 text_state_dirty = false;
293 destroyTextPipeline();
294 if (text_descriptor_set_layout != VK_NULL_HANDLE && device != VK_NULL_HANDLE) {
295 vkDestroyDescriptorSetLayout(device, text_descriptor_set_layout, nullptr);
296 text_descriptor_set_layout = VK_NULL_HANDLE;
297 }
298
299 cleanupSyncObjects();
300
301 if (!retired_swapchains.empty()) {
302 for (VkSwapchainKHR retired : retired_swapchains) {
303 vkDestroySwapchainKHR(device, retired, nullptr);
304 }
305 retired_swapchains.clear();
306 }
307 std::cout << "vk: tearing down swapchain-dependent resources\n";
308 cleanupSwapchain(true);
309
310 if (command_pool != VK_NULL_HANDLE && device != VK_NULL_HANDLE) {
311 std::cout << "vk: destroying command pool\n";
312 vkDestroyCommandPool(device, command_pool, nullptr);
313 command_pool = VK_NULL_HANDLE;
314 }
315
316 if (device != VK_NULL_HANDLE) {
317 savePipelineCache();
318 destroyPipelineCache();
319 std::cout << "vk: destroying logical device\n";
320 vkDestroyDevice(device, nullptr);
321 device = VK_NULL_HANDLE;
322 graphics_queue = VK_NULL_HANDLE;
323 present_queue = VK_NULL_HANDLE;
324 }
325
326 graphics_queue_family = invalid_queue_index;
327 present_queue_family = invalid_queue_index;
328 physical_device = VK_NULL_HANDLE;
329
330 if (surface != VK_NULL_HANDLE && instance != VK_NULL_HANDLE) {
331 std::cout << "vk: destroying presentation surface\n";
332 vkDestroySurfaceKHR(instance, surface, nullptr);
333 surface = VK_NULL_HANDLE;
334 }
335
336 cleanupDebugMessenger();
337
338 if (instance != VK_NULL_HANDLE) {
339 std::cout << "vk: destroying Vulkan instance\n";
340 vkDestroyInstance(instance, nullptr);
341 instance = VK_NULL_HANDLE;
342 }
343
344 if (window != nullptr) {
345 std::cout << "SDL3: destroying SDL window handle\n";
346 window.reset();
347 }
348
350 std::cout << "SDL3: unloading Vulkan loader library\n";
351 SDL_Vulkan_UnloadLibrary();
352 vulkan_library_loaded = false;
353 }
354
355 if (sdl_initialized) {
356 std::cout << "SDL3: shutting down SDL video/gamepad subsystems\n";
357 SDL_QuitSubSystem(SDL_INIT_VIDEO | SDL_INIT_GAMEPAD);
358 sdl_initialized = false;
359 }
360
361 active = false;
362 std::cout << "mxvk: resource teardown complete\n";
363 }
VkDescriptorSetLayout text_descriptor_set_layout
Definition mxvk.hpp:708
bool text_state_dirty
Definition mxvk.hpp:711
std::vector< VkSwapchainKHR > retired_swapchains
Definition mxvk.hpp:723

◆ render()

void mxvk::VK_Window::render ( )
virtualinherited

Render one frame.

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

Definition at line 824 of file mxvk.cpp.

824{ drawFrame(); }

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

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

◆ renderStandaloneSprite() [2/2]

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

Definition at line 1394 of file mxvk.cpp.

1394 {
1395 if (device == VK_NULL_HANDLE || extent.width == 0U || extent.height == 0U) {
1396 return;
1397 }
1398
1399 if (sprite_pipeline == VK_NULL_HANDLE) {
1400 return;
1401 }
1402 vkCmdBindPipeline(cmd, VK_PIPELINE_BIND_POINT_GRAPHICS, sprite_pipeline);
1403 sprite.renderSprites(cmd, sprite_pipeline_layout, extent.width, extent.height);
1404 }
VkPipelineLayout sprite_pipeline_layout
Definition mxvk.hpp:680
VkPipeline sprite_pipeline
Definition mxvk.hpp:681

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

826 {
827 if (path.empty()) {
828 throw mxvk::Exception("saveSnapshot requires a non-empty output path");
829 }
830
831 std::vector<std::uint8_t> rgba{};
832 uint32_t width = 0;
833 uint32_t height = 0;
834 captureSnapshotPixels(rgba, width, height);
835
836 if (!mxvk::SavePNG_RGBA(path.c_str(), rgba.data(), static_cast<int>(width), static_cast<int>(height))) {
837 throw mxvk::Exception("saveSnapshot failed to write PNG: " + path);
838 }
839 }
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:841
bool SavePNG_RGBA(const char *filename, void *buffer, int w, int h, int compression_level=6)
Save a raw RGBA pixel buffer to a PNG file.
Definition mxvk_png.cpp:279

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

637 {
638 clear_color.float32[0] = std::clamp(r, 0.0f, 1.0f);
639 clear_color.float32[1] = std::clamp(g, 0.0f, 1.0f);
640 clear_color.float32[2] = std::clamp(b, 0.0f, 1.0f);
641 clear_color.float32[3] = std::clamp(a, 0.0f, 1.0f);
642 }
VkClearColorValue clear_color
Definition mxvk.hpp:722

◆ setEnableScreenshot()

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

Enable or disable F10 screenshot capture.

Definition at line 644 of file mxvk.cpp.

644 {
645 screenshot_enabled = enabled;
646 if (enabled) {
648 }
649 }

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

3872 {
3873 if (fontPath.empty() || fontSize <= 0) {
3874 throw mxvk::Exception("setFont requires a non-empty path and positive font size");
3875 }
3876
3877 font_path = fontPath;
3878 font_size = fontSize;
3879 font_configured = true;
3880
3881 if (device == VK_NULL_HANDLE) {
3882 throw mxvk::Exception("Cannot set font before Vulkan device initialization");
3883 }
3884 if (!renderTargetsReady() || command_pool == VK_NULL_HANDLE) {
3885 createDevice();
3886 }
3887 if (!renderTargetsReady() || command_pool == VK_NULL_HANDLE) {
3888 throw mxvk::Exception("Cannot initialize text renderer before swapchain and command resources are available");
3889 }
3890
3891 if (!text_renderer) {
3892 ensureTextRenderer();
3893 } else {
3895 }
3898 }
3899 text_state_dirty = true;
3900 }
std::string font_path
Definition mxvk.hpp:714

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

◆ setFrameReadbackRgba16Enabled()

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

Request normalized RGBA16 callbacks from the final HDR intermediate.

Definition at line 189 of file mxvk.hpp.

◆ setHdrRenderIntermediatesEnabled()

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

Select RGBA16F scene/effect intermediates while retaining the presentation format.

Definition at line 1453 of file mxvk.cpp.

1453 {
1454 if (hdr_render_intermediates_enabled == enabled) {
1455 return;
1456 }
1457 if (device != VK_NULL_HANDLE) {
1458 vkDeviceWaitIdle(device);
1459 destroyPostProcessTargets();
1460 }
1462 sprite_state_dirty = true;
1463 if (device != VK_NULL_HANDLE && !swapchain_images.empty() && !post_process_sprites.empty()) {
1464 createPostProcessTargets();
1465 }
1466 std::cout << "mxvk: HDR render intermediates " << (enabled ? "enabled (RGBA16F)\n" : "disabled (RGBA8)\n");
1467 }

◆ setPostProcessingEnabled()

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

Definition at line 348 of file mxvk.hpp.

348{ post_process_enabled = enabled; }
bool post_process_enabled
Definition mxvk.hpp:685

◆ setPostProcessingPresentFragmentShader()

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

Set an optional fragment shader used only when presenting the final post-processing texture to a window.

The offscreen texture and frame readback remain unchanged. Pass an empty path for the normal copy.

Definition at line 1555 of file mxvk.cpp.

1555 {
1557 return;
1558 }
1560 if (post_process_present_sprite != nullptr) {
1562 sprite_state_dirty = true;
1563 }
1564 }

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

1591 {
1592 post_process_params = {p1, p2, p3, p4};
1593 if (post_process_sprites.empty()) {
1594 return;
1595 }
1596 setPostProcessingShaderParams(0, p1, p2, p3, p4);
1597 }
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:1591

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

1599 {
1600 if (effectIndex >= post_process_sprites.size()) {
1601 return;
1602 }
1603 post_process_params = {p1, p2, p3, p4};
1604 if (effectIndex < post_process_effect_params.size()) {
1606 }
1607 if (post_process_sprites[effectIndex] != nullptr) {
1608 post_process_sprites[effectIndex]->setShaderParams(p1, p2, p3, p4);
1609 }
1610 }

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

1612 {
1613 post_process_time_enabled = enabled;
1614 post_process_start_time = std::chrono::steady_clock::now();
1615 if (post_process_sprites.empty()) {
1616 return;
1617 }
1619 }
void setPostProcessingShaderTimeEnabled(bool enabled)
Keep shader param 1 updated with elapsed render time in seconds.
Definition mxvk.cpp:1612

◆ setPostProcessingShaderTimeEnabled() [2/2]

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

Definition at line 1621 of file mxvk.cpp.

1621 {
1622 if (effectIndex >= post_process_sprites.size()) {
1623 return;
1624 }
1625 post_process_time_enabled = enabled;
1626 post_process_start_time = std::chrono::steady_clock::now();
1627 if (effectIndex < post_process_effect_time_enabled.size()) {
1628 post_process_effect_time_enabled[effectIndex] = enabled;
1629 }
1630 if (effectIndex < post_process_effect_start_times.size()) {
1632 }
1633 }

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

bool post_process_texture_consumer_enabled
Definition mxvk.hpp:686

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

3902 {
3903 if (fontPath.empty() || fontSize <= 0) {
3904 throw mxvk::Exception("setPreviewFont requires a non-empty path and positive font size");
3905 }
3906 if (device == VK_NULL_HANDLE) {
3907 throw mxvk::Exception("Cannot set preview font before Vulkan device initialization");
3908 }
3909 if (!preview_text_renderer) {
3910 ensurePreviewTextRenderer(fontPath, fontSize);
3911 }
3912 if (!preview_text_renderer) {
3913 throw mxvk::Exception("Cannot initialize preview text renderer");
3914 }
3915 preview_text_renderer->setFont(fontPath, fontSize);
3916 text_state_dirty = true;
3917 }

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

1406 {
1407 if ((width == 0U) != (height == 0U)) {
1408 throw mxvk::Exception("render extent requires two positive dimensions or two zeros");
1409 }
1410 if (render_extent_override.width == width && render_extent_override.height == height) {
1411 return;
1412 }
1413 if (headless()) {
1414 if (width == 0U && height == 0U) {
1415 return;
1416 }
1417 if (requested_headless_extent.width == width && requested_headless_extent.height == height) {
1418 return;
1419 }
1420 if (device != VK_NULL_HANDLE) {
1421 vkDeviceWaitIdle(device);
1424 cleanupSyncObjects();
1425 cleanupSwapchain(false);
1426 }
1427 requested_headless_extent = {width, height};
1429 sprite_state_dirty = true;
1430 text_state_dirty = true;
1431 if (device != VK_NULL_HANDLE) {
1432 createDevice();
1434 }
1435 std::cout << std::format("mxvk: surface-free render extent {}x{}\n", width, height);
1436 return;
1437 }
1438 if (device != VK_NULL_HANDLE && swapchain != VK_NULL_HANDLE) {
1439 vkDeviceWaitIdle(device);
1441 destroyPostProcessTargets();
1442 }
1443 render_extent_override = {width, height};
1444 sprite_state_dirty = true;
1445 if (device != VK_NULL_HANDLE && !swapchain_images.empty() && !post_process_sprites.empty()) {
1446 createPostProcessTargets();
1447 }
1448 std::cout << std::format("mxvk: fixed render extent {}\n", width > 0U ? std::format("{}x{}", width, height) : "disabled");
1449 }
VkSwapchainKHR swapchain
Definition mxvk.hpp:613
virtual void onSwapchainRecreated()
Called after swapchain and render resources are recreated.
Definition mxvk.cpp:1382
virtual void onSwapchainAboutToRecreate()
Called right before swapchain-dependent resources are recreated.
Definition mxvk.cpp:1380
VkExtent2D requested_headless_extent
Definition mxvk.hpp:667

◆ showCursor()

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

Definition at line 4620 of file mxvk.cpp.

4620 {
4621 if (on)
4622 SDL_ShowCursor();
4623 else
4624 SDL_HideCursor();
4625 }

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

1280 {
1281 if (device == VK_NULL_HANDLE || command_pool == VK_NULL_HANDLE || vkTrimCommandPool == nullptr) {
1282 return;
1283 }
1284
1285 vkTrimCommandPool(device, command_pool, 0);
1286 }

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

3812{ return validation_enabled; }

Member Data Documentation

◆ active

bool mxvk::VK_Window::active = false
protectedinherited

Definition at line 640 of file mxvk.hpp.

◆ clear_color

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

Definition at line 722 of file mxvk.hpp.

◆ command_buffers

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

Definition at line 628 of file mxvk.hpp.

◆ command_pool

VkCommandPool mxvk::VK_Window::command_pool = VK_NULL_HANDLE
protectedinherited

Definition at line 627 of file mxvk.hpp.

◆ current_frame

uint32_t mxvk::VK_Window::current_frame = 0
protectedinherited

Definition at line 634 of file mxvk.hpp.

◆ debug_messenger

VkDebugUtilsMessengerEXT mxvk::VK_Window::debug_messenger = VK_NULL_HANDLE
protectedinherited

Definition at line 603 of file mxvk.hpp.

◆ depth_format

VkFormat mxvk::VK_Window::depth_format = VK_FORMAT_UNDEFINED
protectedinherited

Definition at line 615 of file mxvk.hpp.

◆ depth_image_initialized

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

Definition at line 625 of file mxvk.hpp.

◆ depth_image_memories

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

Definition at line 623 of file mxvk.hpp.

◆ depth_image_views

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

Definition at line 624 of file mxvk.hpp.

◆ depth_images

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

Definition at line 622 of file mxvk.hpp.

◆ device

VkDevice mxvk::VK_Window::device = VK_NULL_HANDLE
protectedinherited

Definition at line 606 of file mxvk.hpp.

◆ font_configured

bool mxvk::VK_Window::font_configured = false
protectedinherited

Definition at line 713 of file mxvk.hpp.

◆ font_path

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

Definition at line 714 of file mxvk.hpp.

◆ font_size

int mxvk::VK_Window::font_size = 24
protectedinherited

Definition at line 715 of file mxvk.hpp.

◆ force_swapchain_recreate

bool mxvk::VK_Window::force_swapchain_recreate = false
protectedinherited

Definition at line 671 of file mxvk.hpp.

◆ fps_counter_enabled

bool mxvk::VK_Window::fps_counter_enabled = false
protectedinherited

Definition at line 716 of file mxvk.hpp.

◆ fps_counter_font

Font mxvk::VK_Window::fps_counter_font {}
protectedinherited

Definition at line 718 of file mxvk.hpp.

◆ fps_counter_font_ready

bool mxvk::VK_Window::fps_counter_font_ready = false
protectedinherited

Definition at line 717 of file mxvk.hpp.

◆ fps_counter_frame_count

uint32_t mxvk::VK_Window::fps_counter_frame_count = 0
protectedinherited

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

◆ fps_counter_text

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

Definition at line 721 of file mxvk.hpp.

◆ frame_readback_enabled

bool mxvk::VK_Window::frame_readback_enabled = false
protectedinherited

Definition at line 655 of file mxvk.hpp.

◆ frame_readback_rgba16_enabled

bool mxvk::VK_Window::frame_readback_rgba16_enabled = false
protectedinherited

Definition at line 656 of file mxvk.hpp.

◆ frame_readback_slots

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

Definition at line 657 of file mxvk.hpp.

◆ framebuffer_resized

bool mxvk::VK_Window::framebuffer_resized = false
protectedinherited

Definition at line 670 of file mxvk.hpp.

◆ graphics_queue

VkQueue mxvk::VK_Window::graphics_queue = VK_NULL_HANDLE
protectedinherited

Definition at line 609 of file mxvk.hpp.

◆ graphics_queue_family

uint32_t mxvk::VK_Window::graphics_queue_family = invalid_queue_index
protectedinherited

Definition at line 607 of file mxvk.hpp.

◆ hdr_render_intermediates_enabled

bool mxvk::VK_Window::hdr_render_intermediates_enabled = false
protectedinherited

Definition at line 704 of file mxvk.hpp.

◆ headless_image_memories

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

Definition at line 619 of file mxvk.hpp.

◆ image_available

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

Definition at line 630 of file mxvk.hpp.

◆ image_fences

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

Definition at line 633 of file mxvk.hpp.

◆ in_flight_fences

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

Definition at line 632 of file mxvk.hpp.

◆ instance

VkInstance mxvk::VK_Window::instance = VK_NULL_HANDLE
protectedinherited

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

◆ last_presented_image_index

uint32_t mxvk::VK_Window::last_presented_image_index = invalid_queue_index
protectedinherited

Definition at line 635 of file mxvk.hpp.

◆ last_resize_event_ms

uint64_t mxvk::VK_Window::last_resize_event_ms = 0
protectedinherited

Definition at line 672 of file mxvk.hpp.

◆ latest_frame_readback_height

uint32_t mxvk::VK_Window::latest_frame_readback_height = 0
protectedinherited

Definition at line 661 of file mxvk.hpp.

◆ latest_frame_readback_rgba

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

Definition at line 658 of file mxvk.hpp.

◆ latest_frame_readback_rgba16

std::vector<std::uint16_t> mxvk::VK_Window::latest_frame_readback_rgba16 {}
protectedinherited

Definition at line 659 of file mxvk.hpp.

◆ latest_frame_readback_width

uint32_t mxvk::VK_Window::latest_frame_readback_width = 0
protectedinherited

Definition at line 660 of file mxvk.hpp.

◆ MEMORY_TRIM_INTERVAL

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

Definition at line 675 of file mxvk.hpp.

◆ next_headless_image

uint32_t mxvk::VK_Window::next_headless_image = 0
protectedinherited

Definition at line 668 of file mxvk.hpp.

◆ owned_post_process_sprite

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

Definition at line 684 of file mxvk.hpp.

◆ owned_post_process_sprites

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

Definition at line 691 of file mxvk.hpp.

◆ physical_device

VkPhysicalDevice mxvk::VK_Window::physical_device = VK_NULL_HANDLE
protectedinherited

Definition at line 605 of file mxvk.hpp.

◆ pipeline_cache

VkPipelineCache mxvk::VK_Window::pipeline_cache = VK_NULL_HANDLE
protectedinherited

Definition at line 611 of file mxvk.hpp.

◆ post_process_composite_sprite

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

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

◆ post_process_effect_stages

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

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

◆ post_process_effect_time_enabled

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

Definition at line 697 of file mxvk.hpp.

◆ post_process_enabled

bool mxvk::VK_Window::post_process_enabled = true
protectedinherited

Definition at line 685 of file mxvk.hpp.

◆ post_process_images

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

Definition at line 699 of file mxvk.hpp.

◆ post_process_initialized

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

Definition at line 702 of file mxvk.hpp.

◆ post_process_memories

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

Definition at line 700 of file mxvk.hpp.

◆ post_process_params

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

Definition at line 688 of file mxvk.hpp.

◆ post_process_present_shader_path

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

Definition at line 693 of file mxvk.hpp.

◆ post_process_present_sprite

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

Definition at line 692 of file mxvk.hpp.

◆ post_process_sprite

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

Definition at line 683 of file mxvk.hpp.

◆ post_process_sprites

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

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

◆ post_process_texture_consumer_enabled

bool mxvk::VK_Window::post_process_texture_consumer_enabled = false
protectedinherited

Definition at line 686 of file mxvk.hpp.

◆ post_process_time_enabled

bool mxvk::VK_Window::post_process_time_enabled = false
protectedinherited

Definition at line 687 of file mxvk.hpp.

◆ post_process_views

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

Definition at line 701 of file mxvk.hpp.

◆ present_mode_preference

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

Definition at line 669 of file mxvk.hpp.

◆ present_queue

VkQueue mxvk::VK_Window::present_queue = VK_NULL_HANDLE
protectedinherited

Definition at line 610 of file mxvk.hpp.

◆ present_queue_family

uint32_t mxvk::VK_Window::present_queue_family = invalid_queue_index
protectedinherited

Definition at line 608 of file mxvk.hpp.

◆ preview_text_queued

bool mxvk::VK_Window::preview_text_queued = false
protectedinherited

Definition at line 712 of file mxvk.hpp.

◆ preview_text_renderer

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

Definition at line 707 of file mxvk.hpp.

◆ render_extent_override

VkExtent2D mxvk::VK_Window::render_extent_override {}
protectedinherited

Definition at line 617 of file mxvk.hpp.

◆ render_finished

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

Definition at line 631 of file mxvk.hpp.

◆ requested_headless_extent

VkExtent2D mxvk::VK_Window::requested_headless_extent {}
protectedinherited

Definition at line 667 of file mxvk.hpp.

◆ resize_settle_delay_ms

uint64_t mxvk::VK_Window::resize_settle_delay_ms = 150
staticconstexprprotectedinherited

Definition at line 673 of file mxvk.hpp.

◆ retired_swapchains

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

Definition at line 723 of file mxvk.hpp.

◆ runtime_mode

RuntimeMode mxvk::VK_Window::runtime_mode = RuntimeMode::Windowed
protectedinherited

Definition at line 666 of file mxvk.hpp.

◆ screenshot_enabled

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

Definition at line 641 of file mxvk.hpp.

◆ screenshot_index

uint32_t mxvk::VK_Window::screenshot_index = 0
protectedinherited

Definition at line 643 of file mxvk.hpp.

◆ screenshot_prefix

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

Definition at line 642 of file mxvk.hpp.

◆ screenshot_queue_cv

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

Definition at line 651 of file mxvk.hpp.

◆ screenshot_queue_mutex

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

Definition at line 650 of file mxvk.hpp.

◆ screenshot_save_queue

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

Definition at line 652 of file mxvk.hpp.

◆ screenshot_worker

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

Definition at line 653 of file mxvk.hpp.

◆ screenshot_worker_stop

bool mxvk::VK_Window::screenshot_worker_stop = false
protectedinherited

Definition at line 654 of file mxvk.hpp.

◆ sdl_initialized

bool mxvk::VK_Window::sdl_initialized = false
protectedinherited

Definition at line 638 of file mxvk.hpp.

◆ sprite_descriptor_set_layout

VkDescriptorSetLayout mxvk::VK_Window::sprite_descriptor_set_layout = VK_NULL_HANDLE
protectedinherited

Definition at line 679 of file mxvk.hpp.

◆ sprite_pipeline

VkPipeline mxvk::VK_Window::sprite_pipeline = VK_NULL_HANDLE
protectedinherited

Definition at line 681 of file mxvk.hpp.

◆ sprite_pipeline_layout

VkPipelineLayout mxvk::VK_Window::sprite_pipeline_layout = VK_NULL_HANDLE
protectedinherited

Definition at line 680 of file mxvk.hpp.

◆ sprite_state_dirty

bool mxvk::VK_Window::sprite_state_dirty = false
protectedinherited

Definition at line 682 of file mxvk.hpp.

◆ sprites

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

Definition at line 677 of file mxvk.hpp.

◆ sprites3d

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

Definition at line 678 of file mxvk.hpp.

◆ surface

VkSurfaceKHR mxvk::VK_Window::surface = VK_NULL_HANDLE
protectedinherited

Definition at line 604 of file mxvk.hpp.

◆ swapchain

VkSwapchainKHR mxvk::VK_Window::swapchain = VK_NULL_HANDLE
protectedinherited

Definition at line 613 of file mxvk.hpp.

◆ swapchain_extent

VkExtent2D mxvk::VK_Window::swapchain_extent {}
protectedinherited

Definition at line 616 of file mxvk.hpp.

◆ swapchain_format

VkFormat mxvk::VK_Window::swapchain_format = VK_FORMAT_UNDEFINED
protectedinherited

Definition at line 614 of file mxvk.hpp.

◆ swapchain_image_initialized

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

Definition at line 621 of file mxvk.hpp.

◆ swapchain_image_views

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

Definition at line 620 of file mxvk.hpp.

◆ swapchain_images

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

Definition at line 618 of file mxvk.hpp.

◆ swapchain_supports_transfer_src

bool mxvk::VK_Window::swapchain_supports_transfer_src = false
protectedinherited

Definition at line 636 of file mxvk.hpp.

◆ text_descriptor_set_layout

VkDescriptorSetLayout mxvk::VK_Window::text_descriptor_set_layout = VK_NULL_HANDLE
protectedinherited

Definition at line 708 of file mxvk.hpp.

◆ text_pipeline

VkPipeline mxvk::VK_Window::text_pipeline = VK_NULL_HANDLE
protectedinherited

Definition at line 710 of file mxvk.hpp.

◆ text_pipeline_layout

VkPipelineLayout mxvk::VK_Window::text_pipeline_layout = VK_NULL_HANDLE
protectedinherited

Definition at line 709 of file mxvk.hpp.

◆ text_renderer

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

Definition at line 706 of file mxvk.hpp.

◆ text_state_dirty

bool mxvk::VK_Window::text_state_dirty = false
protectedinherited

Definition at line 711 of file mxvk.hpp.

◆ validation_enabled

bool mxvk::VK_Window::validation_enabled = false
protectedinherited

Definition at line 665 of file mxvk.hpp.

◆ vulkan_library_loaded

bool mxvk::VK_Window::vulkan_library_loaded = false
protectedinherited

Definition at line 639 of file mxvk.hpp.

◆ window

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

Definition at line 601 of file mxvk.hpp.


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