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

Public Types

enum class  PresentModePreference { LowLatency , Vsync }

Public Member Functions

void applyDifficulty ()
void applyGravity ()
void applyShaderEffectsToggle ()
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)
bool canMoveDown ()
bool canMoveLeft ()
bool canMoveRight ()
bool canRotate ()
int centerX (const char *text)
bool checkGameOver ()
void clearFlashedBlocks ()
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="")
 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 drawblock (float scaleX, float scaleY)
void drawGame ()
void drawmatrix (float scaleX, float scaleY)
void drawnext (float scaleX, float scaleY)
void dropBlock ()
void enablePostProcessing (VK_Sprite *sprite)
bool ensureRenderResources ()
 Ensure deferred render resources are initialized.
void event (SDL_Event &e) override
 Handle one SDL event.
bool findMatches ()
void gameKeypress (SDL_Keycode key)
int getCharWidth ()
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.
int getMenuSpacing ()
VkPhysicalDevice getPhysicalDevice () const noexcept
 Get the Vulkan physical device handle.
VkPipelineCache getPipelineCache () const noexcept
 Get the persistent Vulkan pipeline cache used by framework pipelines.
SDL_Window * getSDLWindow () const noexcept
 Get the underlying SDL window handle.
VkExtent2D getSwapchainExtent () const noexcept
 Get the current swapchain extent.
VkFormat getSwapchainFormat () const noexcept
 Get the swapchain color format.
size_t getSwapchainImageCount () const noexcept
 Get the number of swapchain images currently allocated.
bool getTextDimensions (const std::string &text, int &width, int &height)
 Measure text dimensions in pixels.
bool getTextDimensions (const std::string &text, int &width, int &height, const Font &font)
bool getTextDimensions (const std::string &text, int &width, int &height, TTF_Font *font)
void goToScoresScreen ()
void handleControllerButton (Uint8 button)
void handleTextInput (const char *text)
void initGfx ()
virtual bool initVulkan (bool validiation)
 Initialize Vulkan state.
void logic ()
void loop ()
 Run the main event/render loop.
 MasterPieceWindow (const std::string &path, int wx, int wy, bool full, bool enable_vsync)
void moveBlockDown ()
void moveBlockLeft ()
void moveBlockRight ()
virtual void onPrepareFrameRendering (VkCommandBuffer cmd, uint32_t image_index)
 Record resource transitions that must happen before dynamic rendering begins.
bool openGamepad (SDL_JoystickID id)
void placeBlock ()
void pollController ()
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 rotateBlock ()
void saveSnapshot (const std::string &path)
 Save the most recently rendered window contents as a PNG file.
int scaleY (int baseY)
bool screenshotEnabled () const noexcept
 Check whether F10 screenshot capture is enabled.
void setClearColor (float r, float g, float b, float a=1.0f)
 Set the per-frame color attachment clear color.
void setEnableScreenshot (bool enabled) noexcept
 Enable or disable F10 screenshot capture.
void setFont (const std::string &fontPath, int fontSize=24)
 Set the active text-render font.
void setPostProcessingEnabled (bool enabled)
void setPostProcessingShaderParams (float p1=0.0f, float p2=0.0f, float p3=0.0f, float p4=0.0f)
 Set the post-processing shader params passed as a vec4 push constant.
void setPostProcessingShaderParams (size_t effectIndex, float p1=0.0f, float p2=0.0f, float p3=0.0f, float p4=0.0f)
void setPostProcessingShaderTimeEnabled (bool enabled)
 Keep shader param 1 updated with elapsed render time in seconds.
void setPostProcessingShaderTimeEnabled (size_t effectIndex, bool enabled)
void showCursor (bool on)
void spawnNewBlock ()
void startFlash ()
void trimMemory ()
 Return transient Vulkan command-pool allocations to the driver when supported.
void tryOpenFirstGamepad ()
void updateCredits ()
void updateFontSize ()
void updateGame ()
void updateGameOver ()
void updateIntro ()
void updateOptions ()
void updateScores ()
void updateStart ()
bool validationEnabled () const
 Check whether Vulkan validation layers are currently enabled.
virtual ~MasterPieceWindow ()

Static Public Member Functions

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

Protected Member Functions

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

Static Protected Member Functions

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

Protected Attributes

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

Static Protected Attributes

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

Detailed Description

Definition at line 222 of file acid.drop.cpp.

Member Enumeration Documentation

◆ PresentModePreference

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

Definition at line 39 of file mxvk.hpp.

39 {
40 LowLatency,
41 Vsync
42 };

Constructor & Destructor Documentation

◆ MasterPieceWindow()

MasterPieceWindow::MasterPieceWindow ( const std::string & path,
int wx,
int wy,
bool full,
bool enable_vsync )
inline

Definition at line 261 of file acid.drop.cpp.

262 : mxvk::VK_Window("-[ Acid Drop - Vulkan ]-", wx, wy, full, MXVK_VALIDATION, enable_vsync),
263 current_path((path.empty() || path == ".") ? std::string(puzzle_ASSET_DIR) : path),
264 w(wx),
265 h(wy) {
268 srand((unsigned int)time(0));
269 lastTick = SDL_GetTicks();
270 }
#define puzzle_ASSET_DIR
Definition acid.drop.cpp:20
void tryOpenFirstGamepad()
#define MXVK_VALIDATION
Definition mxvk.hpp:27

◆ ~MasterPieceWindow()

virtual MasterPieceWindow::~MasterPieceWindow ( )
inlinevirtual

Definition at line 272 of file acid.drop.cpp.

272 {
273 if (gamepad != nullptr) {
274 SDL_CloseGamepad(gamepad);
275 gamepad = nullptr;
276 }
277 }

Member Function Documentation

◆ applyDifficulty()

void MasterPieceWindow::applyDifficulty ( )
inline

Definition at line 1062 of file acid.drop.cpp.

1062 {
1063
1064 int baseSpeeds[] = {30, 20, 10};
1065 matrix.Game.speed = baseSpeeds[difficultySetting];
1066 }

◆ applyGravity()

void MasterPieceWindow::applyGravity ( )
inline

Definition at line 1018 of file acid.drop.cpp.

1018 {
1019 bool moved;
1020 do {
1021 moved = false;
1022 for (int i = 0; i < GRID_WIDTH; i++) {
1023 for (int j = GRID_HEIGHT - 2; j >= 0; j--) {
1024 if (matrix.grid[i][j] > 0 && matrix.grid[i][j + 1] == 0) {
1025
1026 matrix.grid[i][j + 1] = matrix.grid[i][j];
1027 matrix.grid[i][j] = 0;
1028 moved = true;
1029 }
1030 }
1031 }
1032 } while (moved);
1033 }
constexpr int GRID_HEIGHT
Definition acid.drop.cpp:26
constexpr int GRID_WIDTH
Definition acid.drop.cpp:25

◆ applyShaderEffectsToggle()

void MasterPieceWindow::applyShaderEffectsToggle ( )
inline

Definition at line 348 of file acid.drop.cpp.

348 {
349 for (int i = 0; i < SHADER_LEVEL_COUNT; i++) {
350 if (gamebg[i])
351 gamebg[i]->setEffectsEnabled(shaderEffectsEnabled);
352 }
353 if (introScreen)
354 introScreen->setEffectsEnabled(shaderEffectsEnabled);
355 if (startScreen)
356 startScreen->setEffectsEnabled(shaderEffectsEnabled);
357 if (creditScreen)
358 creditScreen->setEffectsEnabled(shaderEffectsEnabled);
359 if (scoresBackground)
360 scoresBackground->setEffectsEnabled(shaderEffectsEnabled);
361 for (auto *block : grid_blocks) {
362 if (block)
363 block->setEffectsEnabled(shaderEffectsEnabled);
364 }
365 }
constexpr int SHADER_LEVEL_COUNT
Definition acid.drop.cpp:34

◆ attachPostProcessingShader()

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

Attach a full-screen post-processing fragment shader.

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

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

Definition at line 1154 of file mxvk.cpp.

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

◆ attachPostProcessingShaders()

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

Definition at line 1159 of file mxvk.cpp.

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

◆ canMoveDown()

bool MasterPieceWindow::canMoveDown ( )
inline

Definition at line 786 of file acid.drop.cpp.

786 {
787 int bx = matrix.block.x;
788 int by = matrix.block.y;
789
790 if (matrix.block.horizontal) {
791
792 if (by + 1 >= GRID_HEIGHT)
793 return false;
794 for (int i = 0; i < 3; i++) {
795 if (bx + i >= 0 && bx + i < GRID_WIDTH && matrix.grid[bx + i][by + 1] > 0)
796 return false;
797 }
798 } else {
799
800 if (by + 3 >= GRID_HEIGHT)
801 return false;
802 if (matrix.grid[bx][by + 3] > 0)
803 return false;
804 }
805
806 return true;
807 }

◆ canMoveLeft()

bool MasterPieceWindow::canMoveLeft ( )
inline

Definition at line 809 of file acid.drop.cpp.

809 {
810 if (matrix.block.x <= 0)
811 return false;
812 int bx = matrix.block.x - 1;
813 int by = matrix.block.y;
814
815 if (matrix.block.horizontal) {
816
817 if (by >= 0 && by < GRID_HEIGHT && matrix.grid[bx][by] > 0)
818 return false;
819 } else {
820
821 if (by >= 0 && by < GRID_HEIGHT && matrix.grid[bx][by] > 0)
822 return false;
823 if (by + 1 >= 0 && by + 1 < GRID_HEIGHT && matrix.grid[bx][by + 1] > 0)
824 return false;
825 if (by + 2 >= 0 && by + 2 < GRID_HEIGHT && matrix.grid[bx][by + 2] > 0)
826 return false;
827 }
828
829 return true;
830 }

◆ canMoveRight()

bool MasterPieceWindow::canMoveRight ( )
inline

Definition at line 832 of file acid.drop.cpp.

832 {
833 int by = matrix.block.y;
834
835 if (matrix.block.horizontal) {
836
837 if (matrix.block.x + 3 >= GRID_WIDTH)
838 return false;
839 int bx = matrix.block.x + 3;
840 if (by >= 0 && by < GRID_HEIGHT && matrix.grid[bx][by] > 0)
841 return false;
842 } else {
843
844 if (matrix.block.x >= GRID_WIDTH - 1)
845 return false;
846 int bx = matrix.block.x + 1;
847 if (by >= 0 && by < GRID_HEIGHT && matrix.grid[bx][by] > 0)
848 return false;
849 if (by + 1 >= 0 && by + 1 < GRID_HEIGHT && matrix.grid[bx][by + 1] > 0)
850 return false;
851 if (by + 2 >= 0 && by + 2 < GRID_HEIGHT && matrix.grid[bx][by + 2] > 0)
852 return false;
853 }
854
855 return true;
856 }

◆ canRotate()

bool MasterPieceWindow::canRotate ( )
inline

Definition at line 1092 of file acid.drop.cpp.

1092 {
1093 int bx = matrix.block.x;
1094 int by = matrix.block.y;
1095
1096 if (matrix.block.horizontal) {
1097
1098 if (by + 2 >= GRID_HEIGHT)
1099 return false;
1100
1101 if (by + 1 < GRID_HEIGHT && matrix.grid[bx][by + 1] > 0)
1102 return false;
1103 if (by + 2 < GRID_HEIGHT && matrix.grid[bx][by + 2] > 0)
1104 return false;
1105 } else {
1106
1107 if (bx + 2 >= GRID_WIDTH)
1108 return false;
1109
1110 if (bx + 1 < GRID_WIDTH && by >= 0 && by < GRID_HEIGHT && matrix.grid[bx + 1][by] > 0)
1111 return false;
1112 if (bx + 2 < GRID_WIDTH && by >= 0 && by < GRID_HEIGHT && matrix.grid[bx + 2][by] > 0)
1113 return false;
1114 }
1115 return true;
1116 }

◆ captureSnapshotPixels()

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

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

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

Definition at line 800 of file mxvk.cpp.

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

◆ centerX()

int MasterPieceWindow::centerX ( const char * text)
inline

Definition at line 379 of file acid.drop.cpp.

379 {
380 int width, height;
381 if (getTextDimensions(text, width, height)) {
382 return w / 2 - width / 2;
383 }
384 return w / 2 - (int)(strlen(text) * getCharWidth() / 2);
385 }
bool getTextDimensions(const std::string &text, int &width, int &height)
Measure text dimensions in pixels.
Definition mxvk.cpp:3069

◆ checkGameOver()

bool MasterPieceWindow::checkGameOver ( )
inline

Definition at line 1043 of file acid.drop.cpp.

1043 {
1044
1045 for (int i = 0; i < GRID_WIDTH; i++) {
1046 if (matrix.grid[i][1] > 0) {
1047 return true;
1048 }
1049 }
1050
1051 for (int i = 0; i < GRID_WIDTH; i++) {
1052 for (int j = 0; j < GRID_HEIGHT; j++) {
1053 if (matrix.grid[i][j] == 0) {
1054 return false;
1055 }
1056 }
1057 }
1058
1059 return true;
1060 }

◆ clearFlashedBlocks()

void MasterPieceWindow::clearFlashedBlocks ( )
inline

Definition at line 993 of file acid.drop.cpp.

993 {
994 for (int i = 0; i < GRID_WIDTH; i++) {
995 for (int j = 0; j < GRID_HEIGHT; j++) {
996 if (flashGrid[i][j]) {
997 matrix.grid[i][j] = 0;
998 matrix.Game.addline();
999 }
1000 }
1001 }
1002 matrix.Game.score += matchBonus;
1003 matchBonus = 0;
1004 int newLevel = matrix.Game.lines / SHADER_LEVEL_LINES;
1005 if (newLevel >= SHADER_LEVEL_COUNT)
1006 newLevel = SHADER_LEVEL_COUNT - 1;
1007 if (newLevel != shaderLevel) {
1008 shaderLevel = newLevel;
1009 std::cout << "level increased to: " << shaderLevel << std::endl;
1010 }
1011 memset(flashGrid, false, sizeof(flashGrid));
1012 spawnNewBlock();
1013 if (checkGameOver()) {
1014 currentScreen = SCREEN_GAMEOVER;
1015 }
1016 }
constexpr int SHADER_LEVEL_LINES
Definition acid.drop.cpp:33
@ SCREEN_GAMEOVER
Definition acid.drop.cpp:55

◆ clearTextQueue()

void mxvk::VK_Window::clearTextQueue ( )
inherited

Clear all queued text draw calls for the current frame.

Definition at line 3063 of file mxvk.cpp.

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

◆ context()

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

Definition at line 59 of file mxvk.cpp.

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

◆ createDevice()

void mxvk::VK_Window::createDevice ( )
protectedinherited

Create final device resources.

Definition at line 1645 of file mxvk.cpp.

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

◆ createLogicalDevice()

void mxvk::VK_Window::createLogicalDevice ( )
protectedinherited

Create a logical Vulkan device from the selected physical device.

Definition at line 1514 of file mxvk.cpp.

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

◆ createShaderModule()

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

Create a shader module from SPIR-V bytecode.

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

Definition at line 145 of file mxvk.cpp.

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

◆ createSprite() [1/3]

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

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

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

Definition at line 3477 of file mxvk.cpp.

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

◆ createSprite() [2/3]

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

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

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

Definition at line 3546 of file mxvk.cpp.

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

◆ createSprite() [3/3]

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

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

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

Definition at line 3510 of file mxvk.cpp.

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

◆ createSprite3D() [1/2]

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

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

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

Definition at line 3578 of file mxvk.cpp.

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

◆ createSprite3D() [2/2]

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

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

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

Definition at line 3604 of file mxvk.cpp.

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

◆ debugCallback()

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

Definition at line 44 of file mxvk.cpp.

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

◆ detachPostProcessingShader()

void mxvk::VK_Window::detachPostProcessingShader ( )
inherited

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

Definition at line 1198 of file mxvk.cpp.

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

◆ drawblock()

void MasterPieceWindow::drawblock ( float scaleX,
float scaleY )
inline

Definition at line 671 of file acid.drop.cpp.

671 {
672 int c1 = matrix.block.color.c1;
673 int c2 = matrix.block.color.c2;
674 int c3 = matrix.block.color.c3;
675
676 if (matrix.block.horizontal) {
677
678 int screenY = STARTY + matrix.block.y * (BLOCK_HEIGHT + BLOCK_SPACING) + 10;
679 for (int i = 0; i < 3; i++) {
680 int color = (i == 0) ? c1 : (i == 1) ? c2
681 : c3;
682 int bx = STARTX + (matrix.block.x + i) * (BLOCK_SIZE + BLOCK_SPACING);
683 if (matrix.block.x + i >= 0 && matrix.block.x + i < GRID_WIDTH &&
684 color > 0 && color < BLOCK_COUNT && grid_blocks[color]) {
685 grid_blocks[color]->drawSpriteRect(
686 (int)(bx * scaleX), (int)(screenY * scaleY),
687 (int)(BLOCK_SIZE * scaleX), (int)(BLOCK_HEIGHT * scaleY));
688 }
689 }
690 } else {
691
692 int bx = STARTX + matrix.block.x * (BLOCK_SIZE + BLOCK_SPACING);
693 int y0 = matrix.block.y;
694 int y1 = matrix.block.y + 1;
695 int y2 = matrix.block.y + 2;
696
697 if (y0 >= 0 && c1 > 0 && c1 < BLOCK_COUNT && grid_blocks[c1]) {
698 int screenY = STARTY + y0 * (BLOCK_HEIGHT + BLOCK_SPACING) + 10;
699 grid_blocks[c1]->drawSpriteRect(
700 (int)(bx * scaleX), (int)(screenY * scaleY),
701 (int)(BLOCK_SIZE * scaleX), (int)(BLOCK_HEIGHT * scaleY));
702 }
703 if (y1 >= 0 && c2 > 0 && c2 < BLOCK_COUNT && grid_blocks[c2]) {
704 int screenY = STARTY + y1 * (BLOCK_HEIGHT + BLOCK_SPACING) + 10;
705 grid_blocks[c2]->drawSpriteRect(
706 (int)(bx * scaleX), (int)(screenY * scaleY),
707 (int)(BLOCK_SIZE * scaleX), (int)(BLOCK_HEIGHT * scaleY));
708 }
709 if (y2 >= 0 && c3 > 0 && c3 < BLOCK_COUNT && grid_blocks[c3]) {
710 int screenY = STARTY + y2 * (BLOCK_HEIGHT + BLOCK_SPACING) + 10;
711 grid_blocks[c3]->drawSpriteRect(
712 (int)(bx * scaleX), (int)(screenY * scaleY),
713 (int)(BLOCK_SIZE * scaleX), (int)(BLOCK_HEIGHT * scaleY));
714 }
715 }
716 }
constexpr int BLOCK_HEIGHT
Definition acid.drop.cpp:28
constexpr int BLOCK_SPACING
Definition acid.drop.cpp:29
constexpr int STARTY
Definition acid.drop.cpp:24
@ BLOCK_COUNT
Definition acid.drop.cpp:48
constexpr int BLOCK_SIZE
Definition acid.drop.cpp:27
constexpr int STARTX
Definition acid.drop.cpp:23
int scaleY(int baseY)

◆ drawGame()

void MasterPieceWindow::drawGame ( )
inline

Definition at line 616 of file acid.drop.cpp.

616 {
617 float scaleX = (float)w / 640.0f;
618 float scaleY = (float)h / 480.0f;
619 if (gamebg[shaderLevel]) {
620 float time_f = static_cast<float>(SDL_GetTicks()) / 1000.0f;
621 gamebg[shaderLevel]->setShaderParams(time_f, 0.0f, 0.0f, 0.0f);
622 gamebg[shaderLevel]->drawSpriteRect(0, 0, w, h);
623 }
624 drawmatrix(scaleX, scaleY);
625 drawblock(scaleX, scaleY);
626 drawnext(scaleX, scaleY);
627 std::ostringstream stream;
628 stream << "Score:" << matrix.Game.score;
629 printText(stream.str().c_str(), (int)(200 * scaleX), (int)(80 * scaleY) - (lastFontSize + (lastFontSize / 4)), {255, 255, 255, 255});
630
631 stream.str("");
632 stream << "Tabs:" << matrix.Game.lines;
633 printText(stream.str().c_str(), (int)(310 * scaleX), (int)(80 * scaleY) - (lastFontSize + (lastFontSize / 4)), {255, 255, 255, 255});
634
635 if (paused) {
636 const char *pausedText = "PAUSED - Press P to Continue";
637 int pausedWidth, pausedHeight;
638 if (!getTextDimensions(pausedText, pausedWidth, pausedHeight)) {
639 pausedWidth = static_cast<int>(std::strlen(pausedText)) * getCharWidth();
640 }
641 printText(pausedText, w / 2 - pausedWidth / 2, h / 2, {255, 255, 0, 255});
642 }
643 }
void drawblock(float scaleX, float scaleY)
void drawnext(float scaleX, float scaleY)
void drawmatrix(float scaleX, float scaleY)
void printText(const std::string &text, int x, int y, const SDL_Color &col)
Queue a text string for rendering during the current frame.
Definition mxvk.cpp:3018

◆ drawmatrix()

void MasterPieceWindow::drawmatrix ( float scaleX,
float scaleY )
inline

Definition at line 645 of file acid.drop.cpp.

645 {
646 Uint32 now = SDL_GetTicks();
647 bool showFlash = flashActive && ((now - flashStartTime) / 50 % 2 == 0);
648
649 for (int i = 0; i < GRID_WIDTH; i++) {
650 for (int j = 0; j < GRID_HEIGHT; j++) {
651 int blockType = matrix.grid[i][j];
652 if (blockType > 0 && blockType < BLOCK_COUNT &&
653 static_cast<size_t>(blockType) < grid_blocks.size()) {
654 int x = STARTX + i * (BLOCK_SIZE + BLOCK_SPACING);
655 int y = STARTY + j * (BLOCK_HEIGHT + BLOCK_SPACING) + 10;
656
657 if (flashActive && flashGrid[i][j] && !showFlash) {
658 continue;
659 }
660
661 if (grid_blocks[blockType]) {
662 grid_blocks[blockType]->drawSpriteRect(
663 (int)(x * scaleX), (int)(y * scaleY) + 10,
664 (int)(BLOCK_SIZE * scaleX), (int)(BLOCK_HEIGHT * scaleY));
665 }
666 }
667 }
668 }
669 }

◆ drawnext()

void MasterPieceWindow::drawnext ( float scaleX,
float scaleY )
inline

Definition at line 718 of file acid.drop.cpp.

718 {
719
720 int bx = NEXT_PANEL_X + 70;
721 int by = NEXT_PANEL_Y + 15;
722
723 int c1 = matrix.nextblock.color.c1;
724 int c2 = matrix.nextblock.color.c2;
725 int c3 = matrix.nextblock.color.c3;
726
727 if (c1 > 0 && c1 < BLOCK_COUNT && grid_blocks[c1]) {
728 grid_blocks[c1]->drawSpriteRect(
729 (int)(bx * scaleX), (int)(by * scaleY),
730 (int)(BLOCK_SIZE * scaleX), (int)(BLOCK_HEIGHT * scaleY));
731 }
732 if (c2 > 0 && c2 < BLOCK_COUNT && grid_blocks[c2]) {
733 grid_blocks[c2]->drawSpriteRect(
734 (int)(bx * scaleX), (int)((by + BLOCK_HEIGHT + BLOCK_SPACING) * scaleY),
735 (int)(BLOCK_SIZE * scaleX), (int)(BLOCK_HEIGHT * scaleY));
736 }
737 if (c3 > 0 && c3 < BLOCK_COUNT && grid_blocks[c3]) {
738 grid_blocks[c3]->drawSpriteRect(
739 (int)(bx * scaleX), (int)((by + (BLOCK_HEIGHT + BLOCK_SPACING) * 2) * scaleY),
740 (int)(BLOCK_SIZE * scaleX), (int)(BLOCK_HEIGHT * scaleY));
741 }
742 }
constexpr int NEXT_PANEL_Y
Definition acid.drop.cpp:31
constexpr int NEXT_PANEL_X
Definition acid.drop.cpp:30

◆ dropBlock()

void MasterPieceWindow::dropBlock ( )
inline

Definition at line 1086 of file acid.drop.cpp.

1086 {
1087 while (canMoveDown()) {
1088 matrix.block.y++;
1089 }
1090 }

◆ enablePostProcessing()

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

Definition at line 1271 of file mxvk.cpp.

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

◆ ensureRenderResources()

bool mxvk::VK_Window::ensureRenderResources ( )
inherited

Ensure deferred render resources are initialized.

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

Definition at line 1406 of file mxvk.cpp.

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

◆ event()

void MasterPieceWindow::event ( SDL_Event & e)
inlineoverridevirtual

Handle one SDL event.

Parameters
eSDL event to process.

Reimplemented from mxvk::VK_Window.

Definition at line 1124 of file acid.drop.cpp.

1124 {
1125 if (e.type == SDL_EVENT_QUIT || (currentScreen == SCREEN_START && e.type == SDL_EVENT_KEY_DOWN && e.key.key == SDLK_ESCAPE)) {
1126 exit();
1127 return;
1128 }
1129
1130 if (e.type == SDL_EVENT_KEY_DOWN) {
1131 gameKeypress(e.key.key);
1132 } else if (e.type == SDL_EVENT_TEXT_INPUT) {
1133 handleTextInput(e.text.text);
1134 }
1135
1136 if (e.type == SDL_EVENT_GAMEPAD_ADDED) {
1137 openGamepad(e.gdevice.which);
1138 } else if (e.type == SDL_EVENT_GAMEPAD_REMOVED) {
1139 if (gamepad != nullptr && e.gdevice.which == gamepadId) {
1140 SDL_CloseGamepad(gamepad);
1141 gamepad = nullptr;
1142 gamepadId = 0;
1143 }
1144 } else if (e.type == SDL_EVENT_GAMEPAD_BUTTON_DOWN) {
1145 handleControllerButton(e.gbutton.button);
1146 }
1147 }
@ SCREEN_START
Definition acid.drop.cpp:53
void handleControllerButton(Uint8 button)
void gameKeypress(SDL_Keycode key)
bool openGamepad(SDL_JoystickID id)
void handleTextInput(const char *text)
void exit()
Request loop termination.
Definition mxvk.cpp:1126

◆ exit()

void mxvk::VK_Window::exit ( )
protectedinherited

Request loop termination.

Definition at line 1126 of file mxvk.cpp.

1126 {
1127 active = false;
1128 }

◆ findMatches()

bool MasterPieceWindow::findMatches ( )
inline

Definition at line 883 of file acid.drop.cpp.

883 {
884
885 memset(flashGrid, false, sizeof(flashGrid));
886 bool foundMatch = false;
887 matchBonus = 0;
888
889 for (int j = 0; j < GRID_HEIGHT; j++) {
890 for (int i = 0; i < GRID_WIDTH; i++) {
891 if (matrix.grid[i][j] > 0) {
892 int color = matrix.grid[i][j];
893 int count = 1;
894
895 while (i + count < GRID_WIDTH && matrix.grid[i + count][j] == color) {
896 count++;
897 }
898 if (count >= 3) {
899 for (int k = 0; k < count; k++) {
900 flashGrid[i + k][j] = true;
901 }
902 foundMatch = true;
903
904 if (count == 4)
905 matchBonus += 25;
906 else if (count >= 5)
907 matchBonus += 50;
908 }
909 }
910 }
911 }
912
913 for (int i = 0; i < GRID_WIDTH; i++) {
914 for (int j = 0; j < GRID_HEIGHT; j++) {
915 if (matrix.grid[i][j] > 0) {
916 int color = matrix.grid[i][j];
917 int count = 1;
918
919 while (j + count < GRID_HEIGHT && matrix.grid[i][j + count] == color) {
920 count++;
921 }
922 if (count >= 3) {
923 for (int k = 0; k < count; k++) {
924 flashGrid[i][j + k] = true;
925 }
926 foundMatch = true;
927
928 if (count == 4)
929 matchBonus += 25;
930 else if (count >= 5)
931 matchBonus += 50;
932 }
933 }
934 }
935 }
936
937 for (int i = 0; i < GRID_WIDTH; i++) {
938 for (int j = 0; j < GRID_HEIGHT; j++) {
939 if (matrix.grid[i][j] > 0) {
940 int color = matrix.grid[i][j];
941 int count = 1;
942 while (i + count < GRID_WIDTH && j + count < GRID_HEIGHT &&
943 matrix.grid[i + count][j + count] == color) {
944 count++;
945 }
946 if (count >= 3) {
947 for (int k = 0; k < count; k++) {
948 flashGrid[i + k][j + k] = true;
949 }
950 foundMatch = true;
951
952 if (count == 4)
953 matchBonus += 35;
954 else if (count >= 5)
955 matchBonus += 75;
956 }
957 }
958 }
959 }
960
961 for (int i = 0; i < GRID_WIDTH; i++) {
962 for (int j = 0; j < GRID_HEIGHT; j++) {
963 if (matrix.grid[i][j] > 0) {
964 int color = matrix.grid[i][j];
965 int count = 1;
966 while (i - count >= 0 && j + count < GRID_HEIGHT &&
967 matrix.grid[i - count][j + count] == color) {
968 count++;
969 }
970 if (count >= 3) {
971 for (int k = 0; k < count; k++) {
972 flashGrid[i - k][j + k] = true;
973 }
974 foundMatch = true;
975
976 if (count == 4)
977 matchBonus += 35;
978 else if (count >= 5)
979 matchBonus += 75;
980 }
981 }
982 }
983 }
984
985 return foundMatch;
986 }

◆ gameKeypress()

void MasterPieceWindow::gameKeypress ( SDL_Keycode key)
inline

Definition at line 1301 of file acid.drop.cpp.

1301 {
1302 switch (currentScreen) {
1303 case SCREEN_INTRO:
1304 if (key == SDLK_RETURN) {
1305 currentScreen = SCREEN_START;
1306 }
1307 break;
1308 case SCREEN_START:
1309 if (key == SDLK_UP) {
1310 cursorPos = (cursorPos - 1 + 5) % 5;
1311 } else if (key == SDLK_DOWN) {
1312 cursorPos = (cursorPos + 1) % 5;
1313 } else if (key == SDLK_RETURN) {
1314 if (cursorPos == 0) {
1315 matrix.init_matrix();
1316 shaderLevel = 0;
1318 matrix.block.x = GRID_WIDTH / 2;
1319 matrix.block.y = 1;
1320 matrix.block.horizontal = false;
1321 currentScreen = SCREEN_GAME;
1322 } else if (cursorPos == 1) {
1323 finalScore = 0;
1324 enteringName = false;
1325 currentScreen = SCREEN_SCORES;
1326 } else if (cursorPos == 2) {
1327 currentScreen = SCREEN_OPTIONS;
1328 } else if (cursorPos == 3) {
1329 currentScreen = SCREEN_CREDITS;
1330 } else if (cursorPos == 4) {
1331 exit();
1332 }
1333 }
1334 break;
1335 case SCREEN_GAME:
1336 if (key == SDLK_LEFT) {
1337 moveBlockLeft();
1338 } else if (key == SDLK_RIGHT) {
1340 } else if (key == SDLK_DOWN) {
1341 moveBlockDown();
1342 } else if (key == SDLK_UP) {
1343 matrix.block.color.shiftcolor(true);
1344 } else if (key == SDLK_SPACE) {
1345 rotateBlock();
1346 } else if (key == SDLK_Z) {
1347 matrix.block.color.shiftcolor(false);
1348 } else if (key == SDLK_P) {
1349 paused = !paused;
1350 } else if (key == SDLK_L) {
1351 /*if (shaderLevel < SHADER_LEVEL_COUNT - 1) {
1352 shaderLevel++;
1353 std::cout << "level increased to: " << shaderLevel << std::endl;
1354 }*/
1355 } else if (key == SDLK_ESCAPE) {
1356 currentScreen = SCREEN_START;
1357 }
1358 break;
1359 case SCREEN_GAMEOVER:
1360 if (key == SDLK_RETURN) {
1362 } else if (key == SDLK_ESCAPE) {
1363 currentScreen = SCREEN_START;
1364 }
1365 break;
1366 case SCREEN_OPTIONS:
1367 if (key == SDLK_UP) {
1368 optionsCursor = (optionsCursor - 1 + 3) % 3;
1369 } else if (key == SDLK_DOWN) {
1370 optionsCursor = (optionsCursor + 1) % 3;
1371 } else if (key == SDLK_LEFT) {
1372 if (optionsCursor == 0) {
1373 difficultySetting = (difficultySetting - 1 + 3) % 3;
1374 } else if (optionsCursor == 1) {
1375 shaderEffectsEnabled = !shaderEffectsEnabled;
1377 }
1378 } else if (key == SDLK_RIGHT) {
1379 if (optionsCursor == 0) {
1380 difficultySetting = (difficultySetting + 1) % 3;
1381 } else if (optionsCursor == 1) {
1382 shaderEffectsEnabled = !shaderEffectsEnabled;
1384 }
1385 } else if (key == SDLK_RETURN) {
1386 if (optionsCursor == 2) {
1387 currentScreen = SCREEN_START;
1388 }
1389 }
1390 break;
1391 case SCREEN_CREDITS:
1392 if (key == SDLK_RETURN) {
1393 currentScreen = SCREEN_START;
1394 }
1395 break;
1396 case SCREEN_SCORES:
1397 if (enteringName) {
1398 if (key == SDLK_RETURN && !playerName.empty()) {
1399
1400 scores.addScore(playerName, finalScore);
1401 enteringName = false;
1402 } else if (key == SDLK_BACKSPACE && !playerName.empty()) {
1403 playerName.pop_back();
1404 } else if (key == SDLK_ESCAPE) {
1405 enteringName = false;
1406 }
1407 } else {
1408 if (key == SDLK_RETURN || key == SDLK_ESCAPE) {
1409 currentScreen = SCREEN_START;
1410 }
1411 }
1412 break;
1413 }
1414 }
@ SCREEN_SCORES
Definition acid.drop.cpp:58
@ SCREEN_GAME
Definition acid.drop.cpp:54
@ SCREEN_OPTIONS
Definition acid.drop.cpp:56
@ SCREEN_INTRO
Definition acid.drop.cpp:52
@ SCREEN_CREDITS
Definition acid.drop.cpp:57
void applyShaderEffectsToggle()

◆ getCharWidth()

int MasterPieceWindow::getCharWidth ( )
inline

Definition at line 367 of file acid.drop.cpp.

367 {
368 return (int)(lastFontSize * 0.5f);
369 }

◆ getCommandPool()

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

Get the command pool used for graphics/upload work.

Definition at line 177 of file mxvk.hpp.

177{ return command_pool; }

◆ getDepthFormat()

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

Get the depth format used for dynamic rendering attachments.

Definition at line 189 of file mxvk.hpp.

189{ return depth_format; }

◆ getDevice()

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

Get the Vulkan logical device handle.

Definition at line 168 of file mxvk.hpp.

168{ return device; }

◆ getGraphicsQueue()

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

Get the graphics queue handle.

Definition at line 174 of file mxvk.hpp.

174{ return graphics_queue; }

◆ getMenuSpacing()

int MasterPieceWindow::getMenuSpacing ( )
inline

Definition at line 371 of file acid.drop.cpp.

371 {
372 return (int)(40.0f * ((float)h / 480.0f));
373 }

◆ getPhysicalDevice()

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

Get the Vulkan physical device handle.

Definition at line 171 of file mxvk.hpp.

171{ return physical_device; }

◆ getPipelineCache()

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

Get the persistent Vulkan pipeline cache used by framework pipelines.

Definition at line 180 of file mxvk.hpp.

180{ return pipeline_cache; }

◆ getSDLWindow()

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

Get the underlying SDL window handle.

Definition at line 165 of file mxvk.hpp.

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

◆ getSwapchainExtent()

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

Get the current swapchain extent.

Definition at line 186 of file mxvk.hpp.

186{ return swapchain_extent; }

◆ getSwapchainFormat()

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

Get the swapchain color format.

Definition at line 183 of file mxvk.hpp.

183{ return swapchain_format; }

◆ getSwapchainImageCount()

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

Get the number of swapchain images currently allocated.

Definition at line 192 of file mxvk.hpp.

192{ return swapchain_images.size(); }

◆ getTextDimensions() [1/3]

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

Measure text dimensions in pixels.

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

Definition at line 3069 of file mxvk.cpp.

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

◆ getTextDimensions() [2/3]

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

Definition at line 3101 of file mxvk.cpp.

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

◆ getTextDimensions() [3/3]

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

Definition at line 3085 of file mxvk.cpp.

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

◆ goToScoresScreen()

void MasterPieceWindow::goToScoresScreen ( )
inline

Definition at line 483 of file acid.drop.cpp.

483 {
484 finalScore = matrix.Game.score;
485 playerName = "";
486 enteringName = scores.qualifiesForHighScore(finalScore);
487 currentScreen = SCREEN_SCORES;
488 }

◆ handleControllerButton()

void MasterPieceWindow::handleControllerButton ( Uint8 button)
inline

Definition at line 1149 of file acid.drop.cpp.

1149 {
1150 switch (currentScreen) {
1151 case SCREEN_INTRO:
1152 if (button == SDL_GAMEPAD_BUTTON_SOUTH || button == SDL_GAMEPAD_BUTTON_START) {
1153 currentScreen = SCREEN_START;
1154 }
1155 break;
1156 case SCREEN_START:
1157 if (button == SDL_GAMEPAD_BUTTON_DPAD_UP) {
1158 cursorPos = (cursorPos - 1 + 5) % 5;
1159 } else if (button == SDL_GAMEPAD_BUTTON_DPAD_DOWN) {
1160 cursorPos = (cursorPos + 1) % 5;
1161 } else if (button == SDL_GAMEPAD_BUTTON_SOUTH || button == SDL_GAMEPAD_BUTTON_START) {
1162 if (cursorPos == 0) {
1163 matrix.init_matrix();
1164 shaderLevel = 0;
1166 matrix.block.x = GRID_WIDTH / 2;
1167 matrix.block.y = 1;
1168 matrix.block.horizontal = false;
1169 currentScreen = SCREEN_GAME;
1170 } else if (cursorPos == 1) {
1171 finalScore = 0;
1172 enteringName = false;
1173 currentScreen = SCREEN_SCORES;
1174 } else if (cursorPos == 2) {
1175 currentScreen = SCREEN_OPTIONS;
1176 } else if (cursorPos == 3) {
1177 currentScreen = SCREEN_CREDITS;
1178 } else if (cursorPos == 4) {
1179 exit();
1180 }
1181 } else if (button == SDL_GAMEPAD_BUTTON_EAST) {
1182 exit();
1183 }
1184 break;
1185 case SCREEN_GAME:
1186 if (button == SDL_GAMEPAD_BUTTON_DPAD_LEFT) {
1187 moveBlockLeft();
1188 } else if (button == SDL_GAMEPAD_BUTTON_DPAD_RIGHT) {
1190 } else if (button == SDL_GAMEPAD_BUTTON_DPAD_DOWN) {
1191 moveBlockDown();
1192 } else if (button == SDL_GAMEPAD_BUTTON_DPAD_UP) {
1193 matrix.block.color.shiftcolor(true);
1194 } else if (button == SDL_GAMEPAD_BUTTON_SOUTH) {
1195 rotateBlock();
1196 } else if (button == SDL_GAMEPAD_BUTTON_EAST) {
1197 matrix.block.color.shiftcolor(false);
1198 } else if (button == SDL_GAMEPAD_BUTTON_NORTH) {
1199 dropBlock();
1200 } else if (button == SDL_GAMEPAD_BUTTON_START) {
1201 paused = !paused;
1202 } else if (button == SDL_GAMEPAD_BUTTON_BACK) {
1203 currentScreen = SCREEN_START;
1204 }
1205 break;
1206 case SCREEN_GAMEOVER:
1207 if (button == SDL_GAMEPAD_BUTTON_SOUTH || button == SDL_GAMEPAD_BUTTON_START) {
1209 } else if (button == SDL_GAMEPAD_BUTTON_EAST) {
1210 currentScreen = SCREEN_START;
1211 }
1212 break;
1213 case SCREEN_OPTIONS:
1214 if (button == SDL_GAMEPAD_BUTTON_DPAD_UP) {
1215 optionsCursor = (optionsCursor - 1 + 3) % 3;
1216 } else if (button == SDL_GAMEPAD_BUTTON_DPAD_DOWN) {
1217 optionsCursor = (optionsCursor + 1) % 3;
1218 } else if (button == SDL_GAMEPAD_BUTTON_DPAD_LEFT) {
1219 if (optionsCursor == 0)
1220 difficultySetting = (difficultySetting - 1 + 3) % 3;
1221 if (optionsCursor == 1) {
1222 shaderEffectsEnabled = !shaderEffectsEnabled;
1224 }
1225 } else if (button == SDL_GAMEPAD_BUTTON_DPAD_RIGHT) {
1226 if (optionsCursor == 0)
1227 difficultySetting = (difficultySetting + 1) % 3;
1228 if (optionsCursor == 1) {
1229 shaderEffectsEnabled = !shaderEffectsEnabled;
1231 }
1232 } else if (button == SDL_GAMEPAD_BUTTON_SOUTH || button == SDL_GAMEPAD_BUTTON_START) {
1233 if (optionsCursor == 2)
1234 currentScreen = SCREEN_START;
1235 } else if (button == SDL_GAMEPAD_BUTTON_EAST) {
1236 currentScreen = SCREEN_START;
1237 }
1238 break;
1239 case SCREEN_CREDITS:
1240 if (button == SDL_GAMEPAD_BUTTON_SOUTH || button == SDL_GAMEPAD_BUTTON_EAST || button == SDL_GAMEPAD_BUTTON_START) {
1241 currentScreen = SCREEN_START;
1242 }
1243 break;
1244 case SCREEN_SCORES:
1245 if (!enteringName) {
1246 if (button == SDL_GAMEPAD_BUTTON_SOUTH || button == SDL_GAMEPAD_BUTTON_EAST || button == SDL_GAMEPAD_BUTTON_START) {
1247 currentScreen = SCREEN_START;
1248 }
1249 } else if (enteringName) {
1250 if (button == SDL_GAMEPAD_BUTTON_BACK)
1251 currentScreen = SCREEN_START;
1252 }
1253 break;
1254 }
1255 }

◆ handleTextInput()

void MasterPieceWindow::handleTextInput ( const char * text)
inline

Definition at line 1416 of file acid.drop.cpp.

1416 {
1417 if (currentScreen == SCREEN_SCORES && enteringName) {
1418
1419 if (playerName.length() < 15) {
1420 playerName += text;
1421 }
1422 }
1423 }

◆ initGfx()

void MasterPieceWindow::initGfx ( )
inline

Definition at line 279 of file acid.drop.cpp.

279 {
280 std::string vertShader = current_path + "/data/sprite_vert.spv";
281 std::string fragShader = current_path + "/data/sprite_kaleidoscope.spv";
282 const char *fragShaders[SHADER_LEVEL_COUNT] = {
283 "sprite_kaleidoscope.spv",
284 "sprite_kaleidoscope2.spv",
285 "sprite_kaleidoscope3.spv",
286 "sprite_kaleidoscope4.spv"};
287 for (int i = 0; i < SHADER_LEVEL_COUNT; i++) {
288 gamebg[i] = createSprite(current_path + "/data/gamebg.png", vertShader, current_path + "/data/" + fragShaders[i]);
289 }
290 introScreen = createSprite(current_path + "/data/intro.png", current_path + "/data/sprite_vert.spv", current_path + "/data/sprite_bubble.spv");
291 startScreen = createSprite(current_path + "/data/universe.png", current_path + "/data/sprite_vert.spv", fragShader);
292 creditScreen = createSprite(current_path + "/data/logo.png", current_path + "/data/sprite_vert.spv", current_path + "/data/sprite_time.spv");
293 scoresBackground = createSprite(current_path + "/data/bg.png", current_path + "/data/sprite_vert.spv", current_path + "/data/sprite_time.spv");
294
295 const char *blockFiles[] = {
296 "block_black.png", "block_yellow.png", "block_orange.png", "block_ltblue.png",
297 "block_dblue.png", "block_purple.png", "block_pink.png", "block_gray.png",
298 "block_red.png", "block_green.png", "block_clear.png"};
299 for (int i = 0; i < BLOCK_COUNT; i++) {
300 grid_blocks.push_back(createSprite(current_path + "/data/" + blockFiles[i], current_path + "/data/sprite_vert.spv", current_path + "/data/sprite_frag.spv"));
301 }
303 }

◆ initVulkan()

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

Initialize Vulkan state.

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

Definition at line 318 of file mxvk.cpp.

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

◆ initWindow()

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

Initialize SDL window resources.

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

Definition at line 1059 of file mxvk.cpp.

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

◆ loadSpv()

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

Load a SPIR-V file from disk.

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

Definition at line 141 of file mxvk.cpp.

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

◆ logic()

void MasterPieceWindow::logic ( )
inline

Definition at line 744 of file acid.drop.cpp.

744 {
745 static Uint32 lastMove = 0;
746 Uint32 now = SDL_GetTicks();
747
748 if (flashActive) {
749 if (now - flashStartTime >= FLASH_DURATION) {
750
752 flashActive = false;
753 applyGravity();
754
755 if (findMatches()) {
756 startFlash();
757 }
758 }
759 return;
760 }
761
762 if (now - lastMove > (Uint32)(matrix.Game.speed * 50)) {
763 lastMove = now;
764
765 if (canMoveDown()) {
766 matrix.block.y++;
767 } else {
768
769 placeBlock();
770 applyGravity();
771
772 if (findMatches()) {
773 startFlash();
774 } else {
775
777
778 if (checkGameOver()) {
779 currentScreen = SCREEN_GAMEOVER;
780 }
781 }
782 }
783 }
784 }
constexpr int FLASH_DURATION
Definition acid.drop.cpp:32
void clearFlashedBlocks()

◆ loop()

void mxvk::VK_Window::loop ( )
inherited

Run the main event/render loop.

Definition at line 600 of file mxvk.cpp.

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

◆ moveBlockDown()

void MasterPieceWindow::moveBlockDown ( )
inline

Definition at line 1080 of file acid.drop.cpp.

1080 {
1081 if (canMoveDown()) {
1082 matrix.block.y++;
1083 }
1084 }

◆ moveBlockLeft()

void MasterPieceWindow::moveBlockLeft ( )
inline

Definition at line 1068 of file acid.drop.cpp.

1068 {
1069 if (canMoveLeft()) {
1070 matrix.block.x--;
1071 }
1072 }

◆ moveBlockRight()

void MasterPieceWindow::moveBlockRight ( )
inline

Definition at line 1074 of file acid.drop.cpp.

1074 {
1075 if (canMoveRight()) {
1076 matrix.block.x++;
1077 }
1078 }

◆ onConfigureDepthStencilAttachments()

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

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

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

Reimplemented in example::StencilWindow.

Definition at line 1138 of file mxvk.cpp.

1140 {}

◆ onPrepareFrameRendering()

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

Record resource transitions that must happen before dynamic rendering begins.

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

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

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

Definition at line 1134 of file mxvk.cpp.

1134{}

◆ onRecordCustomRendering()

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

◆ onSwapchainAboutToRecreate()

void mxvk::VK_Window::onSwapchainAboutToRecreate ( )
protectedvirtualinherited

Called right before swapchain-dependent resources are recreated.

Derived classes can release swapchain-dependent resources here.

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

Definition at line 1130 of file mxvk.cpp.

1130{}

◆ onSwapchainRecreated()

◆ openGamepad()

bool MasterPieceWindow::openGamepad ( SDL_JoystickID id)
inline

Definition at line 315 of file acid.drop.cpp.

315 {
316 if (gamepad != nullptr && gamepadId == id) {
317 return true;
318 }
319 if (gamepad != nullptr) {
320 SDL_CloseGamepad(gamepad);
321 gamepad = nullptr;
322 gamepadId = 0;
323 }
324 gamepad = SDL_OpenGamepad(id);
325 if (gamepad == nullptr) {
326 return false;
327 }
328 gamepadId = id;
329 return true;
330 }

◆ pickDevice()

void mxvk::VK_Window::pickDevice ( )
protectedinherited

Pick a suitable Vulkan physical device.

Definition at line 1427 of file mxvk.cpp.

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

◆ placeBlock()

void MasterPieceWindow::placeBlock ( )
inline

Definition at line 858 of file acid.drop.cpp.

858 {
859 int bx = matrix.block.x;
860 int by = matrix.block.y;
861
862 if (matrix.block.horizontal) {
863
864 if (by >= 0 && by < GRID_HEIGHT) {
865 if (bx >= 0 && bx < GRID_WIDTH)
866 matrix.grid[bx][by] = matrix.block.color.c1;
867 if (bx + 1 >= 0 && bx + 1 < GRID_WIDTH)
868 matrix.grid[bx + 1][by] = matrix.block.color.c2;
869 if (bx + 2 >= 0 && bx + 2 < GRID_WIDTH)
870 matrix.grid[bx + 2][by] = matrix.block.color.c3;
871 }
872 } else {
873
874 if (by >= 0 && by < GRID_HEIGHT)
875 matrix.grid[bx][by] = matrix.block.color.c1;
876 if (by + 1 >= 0 && by + 1 < GRID_HEIGHT)
877 matrix.grid[bx][by + 1] = matrix.block.color.c2;
878 if (by + 2 >= 0 && by + 2 < GRID_HEIGHT)
879 matrix.grid[bx][by + 2] = matrix.block.color.c3;
880 }
881 }

◆ pollController()

void MasterPieceWindow::pollController ( )
inline

Definition at line 1257 of file acid.drop.cpp.

1257 {
1258 if (gamepad == nullptr)
1259 return;
1260 Uint32 now = SDL_GetTicks();
1261
1262 if (currentScreen == SCREEN_GAME && !paused) {
1263 Sint16 lx = SDL_GetGamepadAxis(gamepad, SDL_GAMEPAD_AXIS_LEFTX);
1264 Sint16 ly = SDL_GetGamepadAxis(gamepad, SDL_GAMEPAD_AXIS_LEFTY);
1265
1266 if (lx < -JOYSTICK_DEAD_ZONE) {
1267 if (now - joyRepeatLeft > JOY_REPEAT_DELAY) {
1268 moveBlockLeft();
1269 joyRepeatLeft = now;
1270 }
1271 } else {
1272 joyRepeatLeft = 0;
1273 }
1274 if (lx > JOYSTICK_DEAD_ZONE) {
1275 if (now - joyRepeatRight > JOY_REPEAT_DELAY) {
1277 joyRepeatRight = now;
1278 }
1279 } else {
1280 joyRepeatRight = 0;
1281 }
1282 if (ly > JOYSTICK_DEAD_ZONE) {
1283 if (now - joyRepeatDown > JOY_REPEAT_DELAY) {
1284 moveBlockDown();
1285 joyRepeatDown = now;
1286 }
1287 } else {
1288 joyRepeatDown = 0;
1289 }
1290 if (ly < -JOYSTICK_DEAD_ZONE) {
1291 if (now - joyRepeatUp > JOY_REPEAT_DELAY) {
1292 matrix.block.color.shiftcolor(true);
1293 joyRepeatUp = now;
1294 }
1295 } else {
1296 joyRepeatUp = 0;
1297 }
1298 }
1299 }

◆ printText() [1/3]

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

Queue a text string for rendering during the current frame.

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

Definition at line 3018 of file mxvk.cpp.

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

◆ printText() [2/3]

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

Definition at line 3047 of file mxvk.cpp.

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

◆ printText() [3/3]

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

Definition at line 3031 of file mxvk.cpp.

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

◆ proc()

void MasterPieceWindow::proc ( )
inlineoverridevirtual

Execute one processing/update step.

Reimplemented from mxvk::VK_Window.

Definition at line 387 of file acid.drop.cpp.

387 {
388 if (window != nullptr) {
389 SDL_GetWindowSizeInPixels(window.get(), &w, &h);
390 }
391 if (startScreen == nullptr) {
392 initGfx();
393 }
395 switch (currentScreen) {
396 case SCREEN_INTRO:
397 updateIntro();
398 break;
399 case SCREEN_START:
400 updateStart();
401 break;
402 case SCREEN_GAME:
403 updateGame();
404 break;
405 case SCREEN_GAMEOVER:
407 break;
408 case SCREEN_OPTIONS:
410 break;
411 case SCREEN_CREDITS:
413 break;
414 case SCREEN_SCORES:
415 updateScores();
416 break;
417 }
418 }

◆ release()

void mxvk::VK_Window::release ( )
inherited

Release Vulkan and SDL resources.

Safe to call multiple times.

Definition at line 218 of file mxvk.cpp.

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

◆ render()

void mxvk::VK_Window::render ( )
virtualinherited

Render one frame.

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

Definition at line 778 of file mxvk.cpp.

778 {
779 drawFrame();
780 }

◆ renderStandaloneSprite()

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

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

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

Definition at line 1142 of file mxvk.cpp.

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

◆ rotateBlock()

void MasterPieceWindow::rotateBlock ( )
inline

Definition at line 1118 of file acid.drop.cpp.

1118 {
1119 if (canRotate()) {
1120 matrix.block.horizontal = !matrix.block.horizontal;
1121 }
1122 }

◆ saveSnapshot()

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

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

Parameters
pathDestination PNG path.

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

Definition at line 782 of file mxvk.cpp.

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

◆ scaleY()

int MasterPieceWindow::scaleY ( int baseY)
inline

Definition at line 375 of file acid.drop.cpp.

375 {
376 return (int)(baseY * ((float)h / 480.0f));
377 }

◆ screenshotEnabled()

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

Check whether F10 screenshot capture is enabled.

Definition at line 162 of file mxvk.hpp.

162{ return screenshot_enabled; }

◆ setClearColor()

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

Set the per-frame color attachment clear color.

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

Definition at line 593 of file mxvk.cpp.

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

◆ setEnableScreenshot()

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

Enable or disable F10 screenshot capture.

Definition at line 159 of file mxvk.hpp.

159{ screenshot_enabled = enabled; }

◆ setFont()

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

Set the active text-render font.

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

Definition at line 2991 of file mxvk.cpp.

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

◆ setPostProcessingEnabled()

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

Definition at line 284 of file mxvk.hpp.

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

◆ setPostProcessingShaderParams() [1/2]

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

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

Definition at line 1227 of file mxvk.cpp.

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

◆ setPostProcessingShaderParams() [2/2]

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

Definition at line 1235 of file mxvk.cpp.

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

◆ setPostProcessingShaderTimeEnabled() [1/2]

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

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

Definition at line 1248 of file mxvk.cpp.

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

◆ setPostProcessingShaderTimeEnabled() [2/2]

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

Definition at line 1257 of file mxvk.cpp.

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

◆ showCursor()

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

Definition at line 3633 of file mxvk.cpp.

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

◆ spawnNewBlock()

void MasterPieceWindow::spawnNewBlock ( )
inline

Definition at line 1035 of file acid.drop.cpp.

1035 {
1036 matrix.block.color = matrix.nextblock.color;
1037 matrix.nextblock.color.randcolor();
1038 matrix.block.x = GRID_WIDTH / 2;
1039 matrix.block.y = 1;
1040 matrix.block.horizontal = false;
1041 }

◆ startFlash()

void MasterPieceWindow::startFlash ( )
inline

Definition at line 988 of file acid.drop.cpp.

988 {
989 flashActive = true;
990 flashStartTime = SDL_GetTicks();
991 }

◆ trimMemory()

void mxvk::VK_Window::trimMemory ( )
inherited

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

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

Definition at line 1041 of file mxvk.cpp.

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

◆ tryOpenFirstGamepad()

void MasterPieceWindow::tryOpenFirstGamepad ( )
inline

Definition at line 332 of file acid.drop.cpp.

332 {
333 if (gamepad != nullptr) {
334 return;
335 }
336 int count = 0;
337 SDL_JoystickID *ids = SDL_GetGamepads(&count);
338 if (ids == nullptr || count <= 0) {
339 if (ids != nullptr) {
340 SDL_free(ids);
341 }
342 return;
343 }
344 openGamepad(ids[0]);
345 SDL_free(ids);
346 }

◆ updateCredits()

void MasterPieceWindow::updateCredits ( )
inline

Definition at line 542 of file acid.drop.cpp.

542 {
543 if (creditScreen) {
544 float time_f = static_cast<float>(SDL_GetTicks()) / 1000.0f;
545 creditScreen->setShaderParams(time_f, 0.0f, 0.0f, 0.0f);
546 creditScreen->drawSpriteRect(0, 0, w, h);
547 }
548
549 const char *credReturn = "Press Return to return";
550 printText(credReturn, centerX(credReturn), scaleY(360), {255, 255, 0, 255});
551 }
int centerX(const char *text)

◆ updateFontSize()

void MasterPieceWindow::updateFontSize ( )
inline

Definition at line 305 of file acid.drop.cpp.

305 {
306 int fontSize = (int)(20.0f * ((float)h / 480.0f));
307 fontSize = std::max(16, std::min(128, fontSize));
308 if (fontSize != lastFontSize) {
309 lastFontSize = fontSize;
310 setFont(current_path + "/data/font.ttf", fontSize);
312 }
313 }
void clearTextQueue()
Clear all queued text draw calls for the current frame.
Definition mxvk.cpp:3063
void setFont(const std::string &fontPath, int fontSize=24)
Set the active text-render font.
Definition mxvk.cpp:2991

◆ updateGame()

void MasterPieceWindow::updateGame ( )
inline

Definition at line 452 of file acid.drop.cpp.

452 {
453 if (!paused) {
454 logic();
455 }
456 drawGame();
457 }

◆ updateGameOver()

void MasterPieceWindow::updateGameOver ( )
inline

Definition at line 459 of file acid.drop.cpp.

459 {
460 if (gamebg[shaderLevel]) {
461 float time_f = static_cast<float>(SDL_GetTicks()) / 1000.0f;
462 gamebg[shaderLevel]->setShaderParams(time_f, 0.0f, 0.0f, 0.0f);
463 gamebg[shaderLevel]->drawSpriteRect(0, 0, w, h);
464 }
465
466 const char *gameOverText = "GAME OVER!";
467 printText(gameOverText, centerX(gameOverText), h / 2 - 80, {255, 0, 0, 255});
468
469 std::ostringstream stream;
470 stream << "Final Score: " << matrix.Game.score;
471 std::string scoreStr = stream.str();
472 printText(scoreStr.c_str(), centerX(scoreStr.c_str()), h / 2 - 20, {255, 255, 255, 255});
473
474 stream.str("");
475 stream << "Tabs: " << matrix.Game.lines;
476 std::string linesStr = stream.str();
477 printText(linesStr.c_str(), centerX(linesStr.c_str()), h / 2 + 40, {255, 255, 255, 255});
478
479 const char *returnText = "Press ENTER to view High Scores";
480 printText(returnText, centerX(returnText), h / 2 + 120, {255, 255, 0, 255});
481 }

◆ updateIntro()

void MasterPieceWindow::updateIntro ( )
inline

Definition at line 420 of file acid.drop.cpp.

420 {
421 if (introScreen) {
422 float time_f = static_cast<float>(SDL_GetTicks()) / 1000.0f;
423 introScreen->setShaderParams(time_f, 1.0f, 0.8f, 1.2f);
424 introScreen->drawSpriteRect(0, 0, w, h);
425 }
426 Uint32 currentTick = SDL_GetTicks();
427 if (currentTick - lastTick > 5000) {
428 currentScreen = SCREEN_START;
429 lastTick = currentTick;
430 }
431 }

◆ updateOptions()

void MasterPieceWindow::updateOptions ( )
inline

Definition at line 490 of file acid.drop.cpp.

490 {
491 if (startScreen) {
492 float time_f = static_cast<float>(SDL_GetTicks()) / 1000.0f;
493 startScreen->setShaderParams(time_f, 0.0f, 0.0f, 0.0f);
494 startScreen->drawSpriteRect(0, 0, w, h);
495 }
496
497 int titleY = scaleY(100);
498 int optStartY = scaleY(180);
499 int spacing = getMenuSpacing();
500
501 const char *optTitle = "OPTIONS";
502 printText(optTitle, centerX(optTitle), titleY, {255, 255, 0, 255});
503
504 const char *difficultyLabels[] = {"Easy", "Normal", "Hard"};
505 std::string diffText = std::string("< Difficulty: ") + difficultyLabels[difficultySetting] + " >";
506 SDL_Color diffColor = (optionsCursor == 0) ? SDL_Color{255, 255, 0, 255} : SDL_Color{255, 255, 255, 255};
507 printText(diffText.c_str(), centerX(diffText.c_str()), optStartY, diffColor);
508 if (optionsCursor == 0) {
509 int diffWidth, diffHeight;
510 if (!getTextDimensions(diffText.c_str(), diffWidth, diffHeight)) {
511 diffWidth = static_cast<int>(diffText.size()) * getCharWidth();
512 }
513 printText(">>", w / 2 - diffWidth / 2 - scaleY(30), optStartY, {255, 255, 0, 255});
514 }
515
516 std::string fxText = std::string("< Shader Effects: ") + (shaderEffectsEnabled ? "On" : "Off") + " >";
517 SDL_Color fxColor = (optionsCursor == 1) ? SDL_Color{255, 255, 0, 255} : SDL_Color{255, 255, 255, 255};
518 printText(fxText.c_str(), centerX(fxText.c_str()), optStartY + spacing, fxColor);
519 if (optionsCursor == 1) {
520 int fxWidth, fxHeight;
521 if (!getTextDimensions(fxText.c_str(), fxWidth, fxHeight)) {
522 fxWidth = static_cast<int>(fxText.size()) * getCharWidth();
523 }
524 printText(">>", w / 2 - fxWidth / 2 - scaleY(30), optStartY + spacing, {255, 255, 0, 255});
525 }
526
527 const char *backText = "Back";
528 SDL_Color backColor = (optionsCursor == 2) ? SDL_Color{255, 255, 0, 255} : SDL_Color{255, 255, 255, 255};
529 printText(backText, centerX(backText), optStartY + spacing * 2, backColor);
530 if (optionsCursor == 2) {
531 int backWidth, backHeight;
532 if (!getTextDimensions(backText, backWidth, backHeight)) {
533 backWidth = static_cast<int>(std::strlen(backText)) * getCharWidth();
534 }
535 printText(">>", w / 2 - backWidth / 2 - scaleY(30), optStartY + spacing * 2, {255, 255, 0, 255});
536 }
537
538 const char *instructions = "UP/DOWN: Select LEFT/RIGHT: Change ENTER: Confirm";
539 printText(instructions, centerX(instructions), scaleY(350), {200, 200, 200, 255});
540 }

◆ updateScores()

void MasterPieceWindow::updateScores ( )
inline

Definition at line 553 of file acid.drop.cpp.

553 {
554
555 if (scoresBackground) {
556 float time_f = static_cast<float>(SDL_GetTicks()) / 1000.0f;
557 scoresBackground->setShaderParams(time_f, 0.0f, 0.0f, 0.0f);
558 scoresBackground->drawSpriteRect(0, 0, w, h);
559 }
560
561 int titleY = scaleY(40);
562 int listStartY = scaleY(90);
563 int lineHeight = scaleY(32);
564
565 const char *title = "HIGH SCORES";
566 printText(title, centerX(title), titleY, {255, 255, 0, 255});
567
568 const auto &scoreList = scores.getScores();
569 for (size_t i = 0; i < scoreList.size() && i < 10; ++i) {
570 std::ostringstream stream;
571 stream << (i + 1) << ". " << scoreList[i].name << " " << scoreList[i].score;
572 std::string scoreStr = stream.str();
573
574 SDL_Color color = {255, 0, 150, 255};
575
576 if (enteringName && finalScore > 0) {
577
578 int rank = 0;
579 for (size_t j = 0; j < scoreList.size(); ++j) {
580 if (finalScore > scoreList[j].score) {
581 rank = j;
582 break;
583 }
584 rank = j + 1;
585 }
586 if ((int)i == rank) {
587 color = {0, 255, 0, 255};
588 }
589 }
590
591 printText(scoreStr.c_str(), scaleY(100), listStartY + (int)i * lineHeight, color);
592 }
593
594 if (enteringName) {
595 int entryY = scaleY(420);
596
597 std::ostringstream stream;
598 stream << "Your Score: " << finalScore;
599 std::string yourScore = stream.str();
600 printText(yourScore.c_str(), centerX(yourScore.c_str()), entryY - lineHeight * 2, {255, 255, 0, 255});
601
602 const char *prompt = "Enter your name:";
603 printText(prompt, centerX(prompt), entryY - lineHeight, {255, 255, 255, 255});
604
605 std::string displayName = playerName + "_";
606 printText(displayName.c_str(), centerX(displayName.c_str()), entryY, {0, 255, 255, 255});
607
608 const char *instructions = "Type name, ENTER to confirm, BACKSPACE to delete or Escape/Back";
609 printText(instructions, centerX(instructions), entryY + lineHeight, {200, 0, 150, 255});
610 } else {
611 const char *returnText = "Press ENTER to return to menu";
612 printText(returnText, centerX(returnText), scaleY(440), {255, 0, 0, 255});
613 }
614 }

◆ updateStart()

void MasterPieceWindow::updateStart ( )
inline

Definition at line 433 of file acid.drop.cpp.

433 {
434 if (startScreen) {
435 float time_f = static_cast<float>(SDL_GetTicks()) / 1000.0f;
436 startScreen->setShaderParams(time_f, 0.0f, 0.0f, 0.0f);
437 startScreen->drawSpriteRect(0, 0, w, h);
438 }
439 int titleY = scaleY(100);
440 int menuStartY = scaleY(180);
441 int spacing = getMenuSpacing();
442 const char *title = "Liquid Acid Drop";
443 printText(title, centerX(title), titleY, {255, 255, 0, 255});
444 const char *menuItems[] = {"New Game", "High Scores", "Options", "Credits", "Quit"};
445 for (int i = 0; i < 5; i++) {
446 SDL_Color col = (i == cursorPos) ? SDL_Color{255, 255, 0, 255} : SDL_Color{255, 255, 255, 255};
447 printText(menuItems[i], centerX(menuItems[i]), menuStartY + i * spacing, col);
448 }
449 printText(">>", centerX(menuItems[cursorPos]) - scaleY(30), menuStartY + cursorPos * spacing, {255, 255, 0, 255});
450 }

◆ validationEnabled()

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

Check whether Vulkan validation layers are currently enabled.

Returns
true if validation layers are enabled, false otherwise.

Definition at line 2911 of file mxvk.cpp.

2911 {
2912 return validation_enabled;
2913 }

Member Data Documentation

◆ active

bool mxvk::VK_Window::active = false
protectedinherited

Definition at line 516 of file mxvk.hpp.

◆ clear_color

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

Definition at line 576 of file mxvk.hpp.

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

◆ command_buffers

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

Definition at line 505 of file mxvk.hpp.

505{};

◆ command_pool

VkCommandPool mxvk::VK_Window::command_pool = VK_NULL_HANDLE
protectedinherited

Definition at line 504 of file mxvk.hpp.

◆ current_frame

uint32_t mxvk::VK_Window::current_frame = 0
protectedinherited

Definition at line 511 of file mxvk.hpp.

◆ debug_messenger

VkDebugUtilsMessengerEXT mxvk::VK_Window::debug_messenger = VK_NULL_HANDLE
protectedinherited

Definition at line 482 of file mxvk.hpp.

◆ depth_format

VkFormat mxvk::VK_Window::depth_format = VK_FORMAT_UNDEFINED
protectedinherited

Definition at line 494 of file mxvk.hpp.

◆ depth_image_initialized

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

Definition at line 502 of file mxvk.hpp.

502{};

◆ depth_image_memories

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

Definition at line 500 of file mxvk.hpp.

500{};

◆ depth_image_views

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

Definition at line 501 of file mxvk.hpp.

501{};

◆ depth_images

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

Definition at line 499 of file mxvk.hpp.

499{};

◆ device

VkDevice mxvk::VK_Window::device = VK_NULL_HANDLE
protectedinherited

Definition at line 485 of file mxvk.hpp.

◆ font_configured

bool mxvk::VK_Window::font_configured = false
protectedinherited

Definition at line 567 of file mxvk.hpp.

◆ font_path

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

Definition at line 568 of file mxvk.hpp.

568{};

◆ font_size

int mxvk::VK_Window::font_size = 24
protectedinherited

Definition at line 569 of file mxvk.hpp.

◆ force_swapchain_recreate

bool mxvk::VK_Window::force_swapchain_recreate = false
protectedinherited

Definition at line 534 of file mxvk.hpp.

◆ fps_counter_enabled

bool mxvk::VK_Window::fps_counter_enabled = false
protectedinherited

Definition at line 570 of file mxvk.hpp.

◆ fps_counter_font

Font mxvk::VK_Window::fps_counter_font {}
protectedinherited

Definition at line 572 of file mxvk.hpp.

572{};

◆ fps_counter_font_ready

bool mxvk::VK_Window::fps_counter_font_ready = false
protectedinherited

Definition at line 571 of file mxvk.hpp.

◆ fps_counter_frame_count

uint32_t mxvk::VK_Window::fps_counter_frame_count = 0
protectedinherited

Definition at line 574 of file mxvk.hpp.

◆ fps_counter_sample_time

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

Definition at line 573 of file mxvk.hpp.

573{};

◆ fps_counter_text

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

Definition at line 575 of file mxvk.hpp.

◆ framebuffer_resized

bool mxvk::VK_Window::framebuffer_resized = false
protectedinherited

Definition at line 533 of file mxvk.hpp.

◆ graphics_queue

VkQueue mxvk::VK_Window::graphics_queue = VK_NULL_HANDLE
protectedinherited

Definition at line 488 of file mxvk.hpp.

◆ graphics_queue_family

uint32_t mxvk::VK_Window::graphics_queue_family = invalid_queue_index
protectedinherited

Definition at line 486 of file mxvk.hpp.

◆ image_available

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

Definition at line 507 of file mxvk.hpp.

507{};

◆ image_fences

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

Definition at line 510 of file mxvk.hpp.

510{};

◆ in_flight_fences

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

Definition at line 509 of file mxvk.hpp.

509{};

◆ instance

VkInstance mxvk::VK_Window::instance = VK_NULL_HANDLE
protectedinherited

Definition at line 481 of file mxvk.hpp.

◆ last_memory_trim_time

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

Definition at line 537 of file mxvk.hpp.

◆ last_presented_image_index

uint32_t mxvk::VK_Window::last_presented_image_index = invalid_queue_index
protectedinherited

Definition at line 512 of file mxvk.hpp.

◆ last_resize_event_ms

uint64_t mxvk::VK_Window::last_resize_event_ms = 0
protectedinherited

Definition at line 535 of file mxvk.hpp.

◆ MEMORY_TRIM_INTERVAL

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

Definition at line 538 of file mxvk.hpp.

538{30};

◆ owned_post_process_sprite

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

Definition at line 547 of file mxvk.hpp.

◆ owned_post_process_sprites

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

Definition at line 553 of file mxvk.hpp.

553{};

◆ physical_device

VkPhysicalDevice mxvk::VK_Window::physical_device = VK_NULL_HANDLE
protectedinherited

Definition at line 484 of file mxvk.hpp.

◆ pipeline_cache

VkPipelineCache mxvk::VK_Window::pipeline_cache = VK_NULL_HANDLE
protectedinherited

Definition at line 490 of file mxvk.hpp.

◆ post_process_effect_params

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

Definition at line 554 of file mxvk.hpp.

554{};

◆ post_process_effect_start_times

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

Definition at line 556 of file mxvk.hpp.

556{};

◆ post_process_effect_time_enabled

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

Definition at line 555 of file mxvk.hpp.

555{};

◆ post_process_enabled

bool mxvk::VK_Window::post_process_enabled = true
protectedinherited

Definition at line 548 of file mxvk.hpp.

◆ post_process_images

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

Definition at line 557 of file mxvk.hpp.

557{};

◆ post_process_initialized

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

Definition at line 560 of file mxvk.hpp.

560{};

◆ post_process_memories

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

Definition at line 558 of file mxvk.hpp.

558{};

◆ post_process_params

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

Definition at line 550 of file mxvk.hpp.

550{};

◆ post_process_sprite

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

Definition at line 546 of file mxvk.hpp.

◆ post_process_sprites

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

Definition at line 552 of file mxvk.hpp.

552{};

◆ post_process_start_time

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

Definition at line 551 of file mxvk.hpp.

◆ post_process_time_enabled

bool mxvk::VK_Window::post_process_time_enabled = false
protectedinherited

Definition at line 549 of file mxvk.hpp.

◆ post_process_views

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

Definition at line 559 of file mxvk.hpp.

559{};

◆ present_mode_preference

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

Definition at line 532 of file mxvk.hpp.

◆ present_queue

VkQueue mxvk::VK_Window::present_queue = VK_NULL_HANDLE
protectedinherited

Definition at line 489 of file mxvk.hpp.

◆ present_queue_family

uint32_t mxvk::VK_Window::present_queue_family = invalid_queue_index
protectedinherited

Definition at line 487 of file mxvk.hpp.

◆ render_finished

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

Definition at line 508 of file mxvk.hpp.

508{};

◆ resize_settle_delay_ms

uint64_t mxvk::VK_Window::resize_settle_delay_ms = 150
staticconstexprprotectedinherited

Definition at line 536 of file mxvk.hpp.

◆ retired_swapchains

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

Definition at line 577 of file mxvk.hpp.

◆ screenshot_enabled

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

Definition at line 517 of file mxvk.hpp.

◆ screenshot_index

uint32_t mxvk::VK_Window::screenshot_index = 0
protectedinherited

Definition at line 519 of file mxvk.hpp.

◆ screenshot_prefix

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

Definition at line 518 of file mxvk.hpp.

◆ screenshot_queue_cv

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

Definition at line 527 of file mxvk.hpp.

◆ screenshot_queue_mutex

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

Definition at line 526 of file mxvk.hpp.

◆ screenshot_save_queue

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

Definition at line 528 of file mxvk.hpp.

◆ screenshot_worker

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

Definition at line 529 of file mxvk.hpp.

◆ screenshot_worker_stop

bool mxvk::VK_Window::screenshot_worker_stop = false
protectedinherited

Definition at line 530 of file mxvk.hpp.

◆ sdl_initialized

bool mxvk::VK_Window::sdl_initialized = false
protectedinherited

Definition at line 515 of file mxvk.hpp.

◆ sprite_descriptor_set_layout

VkDescriptorSetLayout mxvk::VK_Window::sprite_descriptor_set_layout = VK_NULL_HANDLE
protectedinherited

Definition at line 542 of file mxvk.hpp.

◆ sprite_pipeline

VkPipeline mxvk::VK_Window::sprite_pipeline = VK_NULL_HANDLE
protectedinherited

Definition at line 544 of file mxvk.hpp.

◆ sprite_pipeline_layout

VkPipelineLayout mxvk::VK_Window::sprite_pipeline_layout = VK_NULL_HANDLE
protectedinherited

Definition at line 543 of file mxvk.hpp.

◆ sprite_state_dirty

bool mxvk::VK_Window::sprite_state_dirty = false
protectedinherited

Definition at line 545 of file mxvk.hpp.

◆ sprites

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

Definition at line 540 of file mxvk.hpp.

540{};

◆ sprites3d

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

Definition at line 541 of file mxvk.hpp.

541{};

◆ surface

VkSurfaceKHR mxvk::VK_Window::surface = VK_NULL_HANDLE
protectedinherited

Definition at line 483 of file mxvk.hpp.

◆ swapchain

VkSwapchainKHR mxvk::VK_Window::swapchain = VK_NULL_HANDLE
protectedinherited

Definition at line 492 of file mxvk.hpp.

◆ swapchain_extent

VkExtent2D mxvk::VK_Window::swapchain_extent {}
protectedinherited

Definition at line 495 of file mxvk.hpp.

495{};

◆ swapchain_format

VkFormat mxvk::VK_Window::swapchain_format = VK_FORMAT_UNDEFINED
protectedinherited

Definition at line 493 of file mxvk.hpp.

◆ swapchain_image_initialized

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

Definition at line 498 of file mxvk.hpp.

498{};

◆ swapchain_image_views

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

Definition at line 497 of file mxvk.hpp.

497{};

◆ swapchain_images

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

Definition at line 496 of file mxvk.hpp.

496{};

◆ swapchain_supports_transfer_src

bool mxvk::VK_Window::swapchain_supports_transfer_src = false
protectedinherited

Definition at line 513 of file mxvk.hpp.

◆ text_descriptor_set_layout

VkDescriptorSetLayout mxvk::VK_Window::text_descriptor_set_layout = VK_NULL_HANDLE
protectedinherited

Definition at line 563 of file mxvk.hpp.

◆ text_pipeline

VkPipeline mxvk::VK_Window::text_pipeline = VK_NULL_HANDLE
protectedinherited

Definition at line 565 of file mxvk.hpp.

◆ text_pipeline_layout

VkPipelineLayout mxvk::VK_Window::text_pipeline_layout = VK_NULL_HANDLE
protectedinherited

Definition at line 564 of file mxvk.hpp.

◆ text_renderer

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

Definition at line 562 of file mxvk.hpp.

562{};

◆ text_state_dirty

bool mxvk::VK_Window::text_state_dirty = false
protectedinherited

Definition at line 566 of file mxvk.hpp.

◆ validation_enabled

bool mxvk::VK_Window::validation_enabled = false
protectedinherited

Definition at line 531 of file mxvk.hpp.

◆ window

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

Definition at line 480 of file mxvk.hpp.

480{};

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