MXVK Vulkan Framework 0.35.0
C++20 Vulkan rendering framework for practical 2D and 3D application development with SDL3.
Loading...
Searching...
No Matches
mxvk Namespace Reference

Utilities for loading and saving PNG images. More...

Namespaces

namespace  anonymous_namespace{mxvk.cpp}
namespace  anonymous_namespace{mxvk_abstract_model.cpp}
namespace  anonymous_namespace{mxvk_ff_capture.cpp}
namespace  anonymous_namespace{mxvk_model.cpp}
namespace  anonymous_namespace{mxvk_resource.cpp}
namespace  anonymous_namespace{mxvk_shader_module.cpp}
namespace  anonymous_namespace{mxvk_sprite.cpp}
namespace  detail

Classes

struct  BufferResource
 Owned Vulkan buffer allocation with optional persistent host mapping. More...
struct  CachedTexture
 GPU texture resources cached for a rendered text string. More...
struct  CacheKey
 Cache key combining text content and colour. More...
struct  CacheKeyHash
 Hash functor for CacheKey. More...
class  Camera
 Camera and projection data for the simple software 3D pipeline. More...
struct  CyType
 Cylindrical coordinate. More...
class  Exception
class  Font
 Small RAII wrapper for an SDL_ttf font handle. More...
class  Mat1D
 Two-element column-vector storage used by 2x2 linear solves. More...
class  Mat1x3D
 Three-element column-vector storage used by 3x3 linear solves. More...
class  Mat1x4D
 Four-element column-vector storage. More...
class  Mat2D
 Two-by-two matrix with arithmetic, determinant, inverse, and solve helpers. More...
class  Mat3D
 Three-by-three matrix with multiplication, vector transform, inverse, and solve helpers. More...
class  Mat4D
 Four-by-four homogeneous transform matrix. More...
class  Mat4x3D
 Four-by-three matrix storage. More...
struct  ModelFragmentPushConstants
 Sprite-compatible fragment parameters for UV-based model effects. More...
struct  ModelFragmentUniforms
 Extended shader-viewer uniforms available to fragment shaders at binding 1. More...
struct  MXMaterial
 Parsed subset of Wavefront MTL material fields. More...
class  MXModel
 Loads OBJ/MXMOD meshes and uploads them to Vulkan buffers. More...
class  mxObject
 Simple mesh object loaded from PLG-style indexed triangle data. More...
struct  OBJMaterial
 Wavefront material data used by the software rasterizer. More...
struct  paramLine2D
 Parametric 2D line segment represented by a start point, end point, and direction. More...
struct  paramLine3D
 Parametric 3D line segment represented by a start point, end point, and direction. More...
class  PipeLine
 Clipped software rasterization pipeline for lines and filled triangles. More...
struct  Plane3D
 Plane represented by a point and a normal vector. More...
struct  PointSpriteVertex
 Vertex layout consumed by VK_PointSpriteBatch. More...
struct  Polar
 2D polar coordinate. More...
class  QuatType
 Quaternion used for 3D rotations. More...
class  RenderList
 Flat list of triangles prepared for transformation and rasterization. More...
struct  SDLRendererPixelPlotter
 Pixel plotter adapter that writes packed MXVK colors to an SDL renderer. More...
struct  ShaderModuleInfo
struct  SpType
 Spherical coordinate. More...
struct  SubMesh
 One indexed sub-range that can reference a dedicated texture slot. More...
struct  TextureResource
 Owned sampled 2D texture resources. More...
struct  Triangle
 Triangle primitive used by the simple software rendering pipeline. More...
struct  UniformBufferObject
 Default transform UBO payload for model shaders. More...
class  vec2D
 Two-dimensional float vector with common arithmetic helpers. More...
class  vec3D
 Three-dimensional float vector with arithmetic, dot, and cross-product helpers. More...
class  vec4D
 Four-dimensional float vector used for homogeneous 3D coordinates. More...
class  VK_Capture
 Vulkan OpenCV video capture source. More...
class  VK_Config
 Parses and manages an INI-style configuration file. More...
class  VK_ConfigItem
 A single key/value pair read from a configuration file. More...
class  VK_Console
 Lightweight command console renderer for mxvk::VK_Window. More...
class  VK_Controller
 RAII wrapper for SDL_Gamepad (standard layout mapping). More...
class  VK_FF_Capture
 FFmpeg-backed video-file capture source. More...
class  VK_IOWindow
class  VK_Joystick
 RAII wrapper for a raw SDL_Joystick device. More...
class  VK_Mixer
 SDL3_mixer-based audio manager. More...
class  VK_PointSpriteBatch
 Reusable Vulkan point-list renderer for textured particle effects. More...
class  VK_Sprite
class  VK_Sprite3D
 Depth-tested 3D billboard sprite batch. More...
class  VK_Stencil
 Owns a stencil attachment and two fullscreen pipelines for stencil-masked rendering. More...
class  VK_Text
class  VK_Window
 Main Vulkan window wrapper for MXVK. More...
class  VKAbstractModel
 Convenience wrapper that owns mesh, textures, descriptors, and pipeline state. More...
struct  VKSpritePixelPlotter
 Pixel plotter adapter that draws square pixels into a VK_Sprite. More...
struct  VKVertex
 Vertex payload consumed by MXVK model shaders. More...
struct  VKVertexHash
 Hash functor for VKVertex. More...
struct  VulkanContext
 Minimal Vulkan handles required by MXVK resource helpers. More...
class  Wrapper
 A nullable smart wrapper around a raw pointer. More...

Concepts

concept  WrapType
 Constrains Wrapper<T> to pointer types only.

Typedefs

using ConfigFile = VK_Config
using Console = VK_Console
using Controller = VK_Controller
using Item = VK_ConfigItem
using Joystick = VK_Joystick
using Mixer = VK_Mixer
using MXCOLOR = std::uint32_t
 Packed 32-bit color in ARGB byte order.
using VK_AbstractModel = VKAbstractModel

Enumerations

enum  { MX_ACTIVE = 0x1 , MX_VISIBLE = 0x2 , MX_BACKFACE = 0x4 , MX_CULLED = 0x8 }
 Object and polygon state bit flags. More...
enum  { MX_ACTIVE = 0x1 , MX_VISIBLE = 0x2 , MX_BACKFACE = 0x4 , MX_CULLED = 0x8 }
 Object and polygon state bit flags. More...
enum class  ShaderStage { Unknown , Vertex , Fragment , Compute }
enum class  StorageImageFormat { Unknown , Rgba8 , Rgba16Float }

Functions

VkCommandBuffer begin_one_time_commands (const VulkanContext &context)
 Allocate and begin a primary one-time command buffer.
void bind_nanobind_module (nanobind::module_ &module)
 Register MXVK's Python-facing types and functions on a nanobind module.
void bind_nanobind_module (nb::module_ &module)
std::array< float, 361 > build_cos_table ()
std::array< float, 361 > build_sin_table ()
void BuildTables ()
 Rebuild the sine and cosine lookup tables.
constexpr std::uint8_t color_a (MXCOLOR color)
 Extract the alpha component from a packed ARGB color.
constexpr std::uint8_t color_b (MXCOLOR color)
 Extract the blue component from a packed ARGB color.
constexpr std::uint8_t color_g (MXCOLOR color)
 Extract the green component from a packed ARGB color.
constexpr std::uint8_t color_r (MXCOLOR color)
 Extract the red component from a packed ARGB color.
void copy_buffer (const VulkanContext &context, VkBuffer source, VkBuffer destination, VkDeviceSize size)
 Copy one buffer into another with a one-shot Vulkan 1.3 copy command.
void copy_buffer_to_image (VkCommandBuffer command_buffer, VkBuffer buffer, VkImage image, uint32_t width, uint32_t height)
 Record a Vulkan 1.3 vkCmdCopyBufferToImage2 copy for a full 2D image.
void create_buffer (const VulkanContext &context, VkDeviceSize size, VkBufferUsageFlags usage, VkMemoryPropertyFlags properties, BufferResource &buffer)
 Create and bind a Vulkan buffer allocation.
void create_image (const VulkanContext &context, uint32_t width, uint32_t height, VkFormat format, VkImageTiling tiling, VkImageUsageFlags usage, VkMemoryPropertyFlags properties, VkImage &image, VkDeviceMemory &memory)
 Create a 2D VkImage and allocate/bind its memory.
VkImageView create_image_view (VkDevice device, VkImage image, VkFormat format, VkImageAspectFlags aspect)
 Create a 2D image view for an image.
VkShaderModule create_shader_module (VkDevice device, const std::vector< char > &spv_bytes)
 Create a shader module from SPIR-V bytecode.
void create_texture_from_png (const VulkanContext &context, const std::string &path, TextureResource &texture, VkFormat format=VK_FORMAT_R8G8B8A8_UNORM)
 Load a PNG and upload it into a sampled 2D texture.
void create_texture_from_surface (const VulkanContext &context, SDL_Surface *surface, TextureResource &texture, VkFormat format=VK_FORMAT_R8G8B8A8_UNORM)
 Upload an SDL surface into a sampled 2D texture.
bool defaultEnableScreenshot ()
bool & defaultEnableScreenshotStorage ()
const std::string & defaultExecutableName ()
std::string & defaultExecutableNameStorage ()
const std::string & defaultShaderDirectory ()
std::string & defaultShaderDirectoryStorage ()
float deg2rad (float ang)
 Convert degrees to radians.
void destroy_buffer (VkDevice device, BufferResource &buffer)
 Unmap and destroy a BufferResource.
void destroy_texture (VkDevice device, TextureResource &texture)
 Destroy every Vulkan handle owned by a TextureResource.
template<typename PlotPixel>
void draw_filled_triangle (const vec2D &p0, const vec2D &p1, const vec2D &p2, MXCOLOR color, PlotPixel &&plot_pixel)
 Rasterize a filled triangle by testing pixels against edge functions.
void draw_filled_triangle (SDL_Renderer *renderer, const vec2D &p0, const vec2D &p1, const vec2D &p2, MXCOLOR color)
 Draw a filled triangle directly to an SDL renderer.
void draw_filled_triangle (VK_Sprite &sprite, const vec2D &p0, const vec2D &p1, const vec2D &p2, MXCOLOR color, int pixel_size=1)
 Draw a filled triangle directly to a VK_Sprite with optional square pixel size.
template<typename DrawSpan>
void draw_filled_triangle_spans (vec2D p0, vec2D p1, vec2D p2, MXCOLOR color, DrawSpan &&draw_span)
template<typename DrawSpan>
void draw_filled_triangle_spans_clipped (vec2D p0, vec2D p1, vec2D p2, int clip_min_y, int clip_max_y, MXCOLOR color, DrawSpan &&draw_span)
 Rasterize a filled triangle as horizontal spans.
template<typename PlotPixel>
void draw_line (int x0, int y0, int x1, int y1, MXCOLOR color, PlotPixel &&plot_pixel)
 Draw a line with Bresenham-style integer stepping.
void draw_line (SDL_Renderer *renderer, int x0, int y0, int x1, int y1, MXCOLOR color)
 Draw a line directly to an SDL renderer.
void draw_line (VK_Sprite &sprite, int x0, int y0, int x1, int y1, MXCOLOR color, int pixel_size=1)
 Draw a line directly to a VK_Sprite with optional square pixel size.
float edge_function (const vec2D &a, const vec2D &b, const vec2D &p)
 Compute the signed edge function value for point p relative to edge a-b.
void end_one_time_commands (const VulkanContext &context, VkCommandBuffer command_buffer)
 End, submit with vkQueueSubmit2, wait idle, and free a one-time command buffer.
float fast_cosf (float theta_degrees)
 Approximate cosine using the degree lookup table with linear interpolation.
float fast_sinf (float theta_degrees)
 Approximate sine using the degree lookup table with linear interpolation.
uint32_t find_memory_type (VkPhysicalDevice physical_device, uint32_t type_filter, VkMemoryPropertyFlags properties)
 Find a memory type satisfying a Vulkan memory type mask and property set.
bool HasPngExtension (const char *file)
ShaderModuleInfo inspect_spirv (const std::vector< char > &spv_bytes)
 Inspect a SPIR-V entry point and compute local workgroup size.
std::vector< char > load_spv (const std::string &path)
 Load a SPIR-V file from disk.
SDL_Surface * LoadPNG (const char *file)
 Load a PNG file into an SDL_Surface.
void map_buffer (VkDevice device, BufferResource &buffer)
 Persistently map a host-visible buffer allocation.
constexpr MXCOLOR MXVK_RGB (int r, int g, int b)
 Build an opaque ARGB color from red, green, and blue components.
std::ostream & operator<< (std::ostream &out, const vec2D &v)
 Write a 2D vector to a stream using vec2D::Print().
std::ostream & operator<< (std::ostream &out, const vec3D &v)
 Write a 3D vector to a stream using vec3D::Print().
std::ostream & operator<< (std::ostream &out, const vec4D &v)
 Write a 4D vector to a stream using vec4D::Print().
std::istream & operator>> (std::istream &in, vec2D &v)
 Read a 2D vector from a stream as two scalar coordinates.
std::istream & operator>> (std::istream &in, vec3D &v)
 Read a 3D vector from a stream as three scalar coordinates.
std::istream & operator>> (std::istream &in, vec4D &v)
 Read a 4D vector from a stream as four scalar coordinates.
float rad2deg (float rad)
 Convert radians to degrees.
int rrand (int x, int y)
 Return a pseudo-random integer in the inclusive range between two bounds.
bool SavePNG (SDL_Texture *texture, SDL_Renderer *renderer, const char *filename, int compression_level=6)
 Save an SDL_Texture to a PNG file.
bool SavePNG_RGBA (const char *filename, void *buffer, int w, int h, int compression_level=6)
 Save a raw RGBA pixel buffer to a PNG file.
bool SavePNG_RGBA16 (const char *filename, const void *buffer, int w, int h, int compression_level=6)
 Save a raw 16-bit RGBA pixel buffer to a PNG file.
bool SaveRawBytes (const char *filename, const void *buffer, size_t w, size_t h, size_t bpp)
 Save raw data to a file.
void setDefaultEnableScreenshot (bool enabled)
void setDefaultExecutableName (const std::string &name)
void setDefaultShaderDirectory (const std::string &directory)
MXCOLOR shade_color (MXCOLOR color, float intensity)
 Scale the RGB channels of a color while preserving alpha.
void transition_image_layout (VkCommandBuffer command_buffer, VkImage image, VkImageLayout old_layout, VkImageLayout new_layout, VkImageAspectFlags aspect=VK_IMAGE_ASPECT_COLOR_BIT)
 Record a Vulkan 1.3 synchronization2 image layout transition.
void unmap_buffer (VkDevice device, BufferResource &buffer)
 Unmap a BufferResource if it is currently mapped.
bool WriteRgbaPng (const char *filename, const std::uint8_t *pixels, const int width, const int height, const int pitch, const int bit_depth, const bool swap_16bit_endianness, int compression_level)

Variables

std::array< float, 361 > cos_look = build_cos_table()
 Cosine lookup table with one entry per degree from 0 through 360.
constexpr float EPSILON = 1.0e-5f
 Default tolerance used for floating-point singularity and zero-length checks.
constexpr float PI = 3.14159265358979323846f
 Mathematical constant pi as a single-precision value.
std::array< float, 361 > sin_look = build_sin_table()
 Sine lookup table with one entry per degree from 0 through 360.

Detailed Description

Utilities for loading and saving PNG images.

Typedef Documentation

◆ ConfigFile

Definition at line 99 of file mxvk_cfg.hpp.

◆ Console

Definition at line 182 of file mxvk_console.hpp.

◆ Controller

Definition at line 209 of file mxvk_controller.hpp.

◆ Item

Definition at line 98 of file mxvk_cfg.hpp.

◆ Joystick

Definition at line 210 of file mxvk_controller.hpp.

◆ Mixer

Definition at line 110 of file mxvk_sound.hpp.

◆ MXCOLOR

using mxvk::MXCOLOR = std::uint32_t

Packed 32-bit color in ARGB byte order.

Definition at line 40 of file mxvk_math.h.

◆ VK_AbstractModel

Enumeration Type Documentation

◆ anonymous enum

anonymous enum

Object and polygon state bit flags.

Enumerator
MX_ACTIVE 

Active object or polygon.

MX_VISIBLE 

Visible object or polygon.

MX_BACKFACE 

Polygon is marked as a backface.

MX_CULLED 

Object or polygon is culled.

Definition at line 1217 of file mxvk_math.h.

1217 {
1218 /// Active object or polygon.
1219 MX_ACTIVE = 0x1,
1220
1221 /// Visible object or polygon.
1222 MX_VISIBLE = 0x2,
1223
1224 /// Polygon is marked as a backface.
1225 MX_BACKFACE = 0x4,
1226
1227 /// Object or polygon is culled.
1228 MX_CULLED = 0x8
1229 };
@ MX_ACTIVE
Active object or polygon.
Definition mxvk_math.h:1219
@ MX_CULLED
Object or polygon is culled.
Definition mxvk_math.h:1228
@ MX_BACKFACE
Polygon is marked as a backface.
Definition mxvk_math.h:1225
@ MX_VISIBLE
Visible object or polygon.
Definition mxvk_math.h:1222

◆ anonymous enum

anonymous enum

Object and polygon state bit flags.

Enumerator
MX_ACTIVE 

Active object or polygon.

MX_VISIBLE 

Visible object or polygon.

MX_BACKFACE 

Polygon is marked as a backface.

MX_CULLED 

Object or polygon is culled.

Definition at line 1183 of file mxvk_math_eigen.hpp.

1183 {
1184 /// Active object or polygon.
1185 MX_ACTIVE = 0x1,
1186
1187 /// Visible object or polygon.
1188 MX_VISIBLE = 0x2,
1189
1190 /// Polygon is marked as a backface.
1191 MX_BACKFACE = 0x4,
1192
1193 /// Object or polygon is culled.
1194 MX_CULLED = 0x8
1195 };

◆ ShaderStage

enum class mxvk::ShaderStage
strong
Enumerator
Unknown 
Vertex 
Fragment 
Compute 

Definition at line 9 of file mxvk_shader_module.hpp.

◆ StorageImageFormat

enum class mxvk::StorageImageFormat
strong
Enumerator
Unknown 
Rgba8 
Rgba16Float 

Definition at line 16 of file mxvk_shader_module.hpp.

Function Documentation

◆ begin_one_time_commands()

VkCommandBuffer mxvk::begin_one_time_commands ( const VulkanContext & context)
nodiscard

Allocate and begin a primary one-time command buffer.

Parameters
contextValid upload context with device, queue, and command pool.
Returns
Recording command buffer.
Exceptions
mxvk::Exceptionif allocation or begin fails.

Definition at line 211 of file mxvk_resource.cpp.

211 {
212 validate_upload_context(context);
213 VkCommandBufferAllocateInfo alloc_info{};
214 alloc_info.sType = VK_STRUCTURE_TYPE_COMMAND_BUFFER_ALLOCATE_INFO;
215 alloc_info.level = VK_COMMAND_BUFFER_LEVEL_PRIMARY;
216 alloc_info.commandPool = context.command_pool;
217 alloc_info.commandBufferCount = 1;
218
219 VkCommandBuffer command_buffer = VK_NULL_HANDLE;
220 if (vkAllocateCommandBuffers(context.device, &alloc_info, &command_buffer) != VK_SUCCESS) {
221 throw mxvk::Exception("failed to allocate one-time command buffer");
222 }
223
224 VkCommandBufferBeginInfo begin_info{};
225 begin_info.sType = VK_STRUCTURE_TYPE_COMMAND_BUFFER_BEGIN_INFO;
226 begin_info.flags = VK_COMMAND_BUFFER_USAGE_ONE_TIME_SUBMIT_BIT;
227 if (vkBeginCommandBuffer(command_buffer, &begin_info) != VK_SUCCESS) {
228 vkFreeCommandBuffers(context.device, context.command_pool, 1, &command_buffer);
229 throw mxvk::Exception("failed to begin one-time command buffer");
230 }
231
232 return command_buffer;
233 }
VkDevice device
Logical device used to create and destroy resources.
VkCommandPool command_pool
Command pool used for transient upload command buffers.

◆ bind_nanobind_module() [1/2]

void mxvk::bind_nanobind_module ( nanobind::module_ & module)

Register MXVK's Python-facing types and functions on a nanobind module.

◆ bind_nanobind_module() [2/2]

void mxvk::bind_nanobind_module ( nb::module_ & module)

Definition at line 332 of file mxvk_nanobind.cpp.

332 {
333 module.doc() = "Python bindings for the MXVK Vulkan framework.";
334
335 nb::exception<Exception>(module, "MXVKError", PyExc_RuntimeError);
336
337 const std::filesystem::path module_directory = python_module_directory();
338 const std::array<std::filesystem::path, 3> shader_directories{module_directory / "share" / "mxvk" / "shaders", module_directory / "mxvk" / "shaders", module_directory / "shaders"};
339 for (const std::filesystem::path &shader_directory : shader_directories) {
340 if (std::filesystem::is_directory(shader_directory)) {
341 setDefaultShaderDirectory(shader_directory.string());
342 break;
343 }
344 }
345
346 module.attr("version") = nb::make_tuple(MXVK_VERSION_CODE_MAJOR, MXVK_VERSION_CODE_MINOR, MXVK_VERSION_CODE_PATCH);
347 module.attr("has_cv") = false;
348 module.attr("has_ffmpeg_capture") = false;
349 module.attr("has_mixer") = false;
350 module.attr("has_jpeg") = false;
351 module.attr("EVENT_KEY_DOWN") = static_cast<uint32_t>(SDL_EVENT_KEY_DOWN);
352 module.attr("EVENT_KEY_UP") = static_cast<uint32_t>(SDL_EVENT_KEY_UP);
353 module.attr("EVENT_MOUSE_MOTION") = static_cast<uint32_t>(SDL_EVENT_MOUSE_MOTION);
354 module.attr("EVENT_MOUSE_BUTTON_DOWN") = static_cast<uint32_t>(SDL_EVENT_MOUSE_BUTTON_DOWN);
355 module.attr("EVENT_MOUSE_BUTTON_UP") = static_cast<uint32_t>(SDL_EVENT_MOUSE_BUTTON_UP);
356 module.attr("EVENT_MOUSE_WHEEL") = static_cast<uint32_t>(SDL_EVENT_MOUSE_WHEEL);
357 module.attr("KEY_ESCAPE") = static_cast<int32_t>(SDLK_ESCAPE);
358 module.attr("MOUSE_BUTTON_LEFT") = SDL_BUTTON_LEFT;
359 module.attr("MOUSE_BUTTON_MIDDLE") = SDL_BUTTON_MIDDLE;
360 module.attr("MOUSE_BUTTON_RIGHT") = SDL_BUTTON_RIGHT;
361 module.attr("MOUSE_BUTTON_X1") = SDL_BUTTON_X1;
362 module.attr("MOUSE_BUTTON_X2") = SDL_BUTTON_X2;
363 module.attr("MOD_SHIFT") = static_cast<uint16_t>(SDL_KMOD_SHIFT);
364 module.attr("MOD_CTRL") = static_cast<uint16_t>(SDL_KMOD_CTRL);
365 module.attr("MOD_ALT") = static_cast<uint16_t>(SDL_KMOD_ALT);
366 module.attr("MOD_GUI") = static_cast<uint16_t>(SDL_KMOD_GUI);
367
368 module.def("set_default_enable_screenshot", &setDefaultEnableScreenshot, nb::arg("enabled"));
369 module.def("default_enable_screenshot", &defaultEnableScreenshot);
370 module.def("set_default_executable_name", &setDefaultExecutableName, nb::arg("name"));
371 module.def("default_executable_name", &defaultExecutableName, nb::rv_policy::copy);
372 module.def("set_default_shader_directory", &setDefaultShaderDirectory, nb::arg("directory"));
373 module.def("default_shader_directory", &defaultShaderDirectory, nb::rv_policy::copy);
374 module.def(
375 "key_code",
376 [](const std::string &name) {
377 const SDL_Keycode key = SDL_GetKeyFromName(name.c_str());
378 if (key == SDLK_UNKNOWN)
379 throw std::invalid_argument("unknown SDL key name: " + name);
380 return static_cast<int32_t>(key);
381 },
382 nb::arg("name"));
383 module.def(
384 "key_name",
385 [](int32_t key) -> nb::object {
386 const char *name = SDL_GetKeyName(static_cast<SDL_Keycode>(key));
387 return name == nullptr || name[0] == '\0' ? nb::none() : nb::cast(std::string(name));
388 },
389 nb::arg("key"));
390 module.def("save_png_rgba", [](const std::string &path, nb::ndarray<uint8_t, nb::c_contig, nb::device::cpu> pixels, int width, int height) { return SavePNG_RGBA(path.c_str(), pixels.data(), width, height); }, nb::arg("path"), nb::arg("pixels"), nb::arg("width"), nb::arg("height"));
391 module.def("save_png_rgba16", [](const std::string &path, nb::ndarray<uint16_t, nb::c_contig, nb::device::cpu> pixels, int width, int height) { return SavePNG_RGBA16(path.c_str(), pixels.data(), width, height); }, nb::arg("path"), nb::arg("pixels"), nb::arg("width"), nb::arg("height"));
392 module.def("inspect_spirv_file", [](const std::string &path) { return inspect_spirv(load_spv(path)); }, nb::arg("path"));
393
394 nb::enum_<StorageImageFormat>(module, "StorageImageFormat").value("unknown", StorageImageFormat::Unknown).value("rgba8", StorageImageFormat::Rgba8).value("rgba16_float", StorageImageFormat::Rgba16Float);
395 nb::class_<ShaderModuleInfo>(module, "ShaderModuleInfo").def_ro("stage", &ShaderModuleInfo::stage).def_ro("local_size_x", &ShaderModuleInfo::localSizeX).def_ro("local_size_y", &ShaderModuleInfo::localSizeY).def_ro("local_size_z", &ShaderModuleInfo::localSizeZ).def_ro("uses_history_texture", &ShaderModuleInfo::usesHistoryTexture).def_ro("uses_spectrum_texture", &ShaderModuleInfo::usesSpectrumTexture).def_ro("uses_spectrum_history_texture", &ShaderModuleInfo::usesSpectrumHistoryTexture).def_ro("storage_image_format", &ShaderModuleInfo::storageImageFormat);
396
397 nb::class_<PythonBuffer>(module, "GpuBuffer").def("write", &PythonBuffer::write, nb::arg("bytes")).def("close", &PythonBuffer::close).def_prop_ro("size", &PythonBuffer::size).def_prop_ro("valid", &PythonBuffer::valid);
398 nb::class_<PythonTexture>(module, "GpuTexture").def("close", &PythonTexture::close).def_prop_ro("width", &PythonTexture::width).def_prop_ro("height", &PythonTexture::height).def_prop_ro("valid", &PythonTexture::valid);
399 module.def("create_uniform_buffer", [](VK_Window &window, size_t size) { return std::make_shared<PythonBuffer>(window.context(), size, VK_BUFFER_USAGE_UNIFORM_BUFFER_BIT); }, nb::arg("window"), nb::arg("size"), nb::keep_alive<0, 1>());
400 module.def("create_storage_buffer", [](VK_Window &window, size_t size) { return std::make_shared<PythonBuffer>(window.context(), size, VK_BUFFER_USAGE_STORAGE_BUFFER_BIT); }, nb::arg("window"), nb::arg("size"), nb::keep_alive<0, 1>());
401 module.def("load_texture", [](VK_Window &window, const std::string &path) { return std::make_shared<PythonTexture>(window.context(), path); }, nb::arg("window"), nb::arg("path"), nb::keep_alive<0, 1>());
402
403 nb::class_<StopWatch<SteadyClockPolicy>>(module, "SteadyStopwatch").def(nb::init<std::string_view>(), nb::arg("name") = "mxvk").def("start", &StopWatch<SteadyClockPolicy>::Start, nb::arg("name")).def("stop", &StopWatch<SteadyClockPolicy>::Stop).def("echo", &StopWatch<SteadyClockPolicy>::Echo, nb::arg("name")).def("time_passed", &StopWatch<SteadyClockPolicy>::TimePassed);
404
405 nb::class_<StopWatch<HighResolutionClockPolicy>>(module, "HighResolutionStopwatch").def(nb::init<std::string_view>(), nb::arg("name") = "mxvk").def("start", &StopWatch<HighResolutionClockPolicy>::Start, nb::arg("name")).def("stop", &StopWatch<HighResolutionClockPolicy>::Stop).def("echo", &StopWatch<HighResolutionClockPolicy>::Echo, nb::arg("name")).def("time_passed", &StopWatch<HighResolutionClockPolicy>::TimePassed);
406
407#if defined(MXVK_WITH_JPEG)
408 module.attr("has_jpeg") = true;
409 module.def(
410 "load_jpeg_rgba",
411 [](const std::string &path) -> nb::object {
412 SDL_Surface *loaded = VK_JPEG::Load(path.c_str());
413 if (loaded == nullptr)
414 return nb::none();
415 SDL_Surface *rgba = SDL_ConvertSurface(loaded, SDL_PIXELFORMAT_RGBA32);
416 SDL_DestroySurface(loaded);
417 if (rgba == nullptr)
418 return nb::none();
419 const size_t byte_count = static_cast<size_t>(rgba->pitch) * static_cast<size_t>(rgba->h);
420 nb::bytes pixels(static_cast<const char *>(rgba->pixels), byte_count);
421 nb::tuple result = nb::make_tuple(pixels, rgba->w, rgba->h, rgba->pitch);
422 SDL_DestroySurface(rgba);
423 return result;
424 },
425 nb::arg("path"));
426 module.def(
427 "save_jpeg_rgba",
428 [](const std::string &path, nb::ndarray<uint8_t, nb::c_contig, nb::device::cpu> pixels, int width, int height, int quality, int pitch) {
429 if (width <= 0 || height <= 0)
430 throw nb::value_error("width and height must be positive");
431 if (pitch == 0)
432 pitch = width * 4;
433 if (pitch < width * 4 || pixels.nbytes() < static_cast<size_t>(pitch) * static_cast<size_t>(height))
434 throw nb::value_error("RGBA buffer is smaller than pitch * height");
435 SDL_Surface *surface = SDL_CreateSurfaceFrom(width, height, SDL_PIXELFORMAT_RGBA32, pixels.data(), pitch);
436 if (surface == nullptr)
437 throw std::runtime_error(SDL_GetError());
438 const bool saved = VK_JPEG::SaveSurface(surface, path.c_str(), quality);
439 SDL_DestroySurface(surface);
440 return saved;
441 },
442 nb::arg("path"),
443 nb::arg("pixels"),
444 nb::arg("width"),
445 nb::arg("height"),
446 nb::arg("quality") = 90,
447 nb::arg("pitch") = 0);
448#endif
449
450#if defined(MXVK_WITH_MIXER)
451 module.attr("has_mixer") = true;
452 nb::class_<VK_Mixer>(module, "Mixer").def(nb::init<>()).def("init", &VK_Mixer::init).def("load_wav", &VK_Mixer::loadWav, nb::arg("path")).def("load_music", &VK_Mixer::loadMusic, nb::arg("path")).def("play_music", &VK_Mixer::playMusic, nb::arg("id"), nb::arg("loops") = 0).def("play_wav", &VK_Mixer::playWav, nb::arg("id"), nb::arg("loops") = 0, nb::arg("channel") = -1).def("is_playing", &VK_Mixer::isPlaying, nb::arg("channel")).def("is_music_playing", &VK_Mixer::isMusicPlaying, nb::arg("id")).def("stop_music", &VK_Mixer::stopMusic).def("cleanup", &VK_Mixer::cleanup);
453#endif
454
455#if defined(MXVK_WITH_CV)
456 module.attr("has_cv") = true;
457 nb::class_<VK_Capture>(module, "Capture")
458 .def(nb::init<>())
459 .def("open", nb::overload_cast<const std::string &>(&VK_Capture::open), nb::arg("path"))
460 .def("open_camera", nb::overload_cast<int, int>(&VK_Capture::open), nb::arg("index"), nb::arg("backend") = 0)
461 .def("close", &VK_Capture::close)
462 .def("is_open", &VK_Capture::is_open)
463 .def("reset_sprite", &VK_Capture::resetSprite)
464 .def("draw", nb::overload_cast<int, int>(&VK_Capture::draw), nb::arg("x"), nb::arg("y"))
465 .def("draw", nb::overload_cast<int, int, int, int>(&VK_Capture::draw), nb::arg("x"), nb::arg("y"), nb::arg("width"), nb::arg("height"))
466 .def("read", nb::overload_cast<>(&VK_Capture::read))
467 .def("grab", &VK_Capture::grab)
468 .def(
469 "read_rgba",
470 [](VK_Capture &capture, bool flip_y) {
471 cv::Mat frame;
472 if (!capture.readRgba(frame, flip_y))
473 return nb::object(nb::none());
474 return opencv_rgba_array(std::move(frame));
475 },
476 nb::arg("flip_y") = false)
477 .def("read_to_sprite", nb::overload_cast<VK_Sprite &>(&VK_Capture::readToSprite), nb::arg("sprite"))
478 .def("read_to_sprite", nb::overload_cast<VK_Sprite &, bool>(&VK_Capture::readToSprite), nb::arg("sprite"), nb::arg("flip_y"))
479 .def("read_to_model_texture", &VK_Capture::readToModelTexture, nb::arg("model"), nb::arg("flip_y") = false)
480 .def("reload", &VK_Capture::reload, nb::arg("width"), nb::arg("height"), nb::arg("vertex_shader_path"), nb::arg("fragment_shader_path"))
481 .def_prop_ro("sprite", &VK_Capture::getSprite, nb::rv_policy::reference_internal)
482 .def("set", &VK_Capture::set, nb::arg("property"), nb::arg("value"))
483 .def("get", &VK_Capture::get, nb::arg("property"))
484#if defined(MXVK_CUDA)
485 .def(
486 "read_gpu_rgba_to_host",
487 [](VK_Capture &capture, bool flip_y) {
488 cv::cuda::GpuMat gpu;
489 if (!capture.readGpuRgba(gpu, flip_y))
490 return nb::object(nb::none());
491 cv::Mat host;
492 gpu.download(host, capture.cudaStream());
493 capture.cudaStream().waitForCompletion();
494 return opencv_rgba_array(std::move(host));
495 },
496 nb::arg("flip_y") = false)
497#endif
498 ;
499#endif
500
501#if defined(MXVK_WITH_FFMPEG_CAPTURE)
502 module.attr("has_ffmpeg_capture") = true;
503 nb::class_<VK_FF_Capture>(module, "FFmpegCapture")
504 .def(nb::init<>())
505 .def("open", nb::overload_cast<const std::string &>(&VK_FF_Capture::open), nb::arg("path"))
506 .def("open", nb::overload_cast<const std::string &, int>(&VK_FF_Capture::open), nb::arg("path"), nb::arg("cuda_device"))
507 .def("seek_start", &VK_FF_Capture::seek_start)
508 .def("skip", &VK_FF_Capture::skip)
509 .def("close", &VK_FF_Capture::close)
510 .def("is_open", &VK_FF_Capture::is_open)
511 .def(
512 "read_rgba",
513 [](VK_FF_Capture &capture, bool flip_y) -> nb::object {
514 std::vector<uint8_t> pixels;
515 int width = 0, height = 0, pitch = 0;
516 if (!capture.readRgba(pixels, width, height, pitch, flip_y))
517 return nb::none();
518 return ffmpeg_rgba8_array(std::move(pixels), width, height, pitch);
519 },
520 nb::arg("flip_y") = false)
521 .def(
522 "read_rgba16",
523 [](VK_FF_Capture &capture, bool flip_y) -> nb::object {
524 std::vector<uint16_t> pixels;
525 int width = 0, height = 0, pitch = 0;
526 if (!capture.readRgba16(pixels, width, height, pitch, flip_y))
527 return nb::none();
528 return ffmpeg_rgba16_array(std::move(pixels), width, height, pitch);
529 },
530 nb::arg("flip_y") = false)
531 .def_prop_ro("width", &VK_FF_Capture::width)
532 .def_prop_ro("height", &VK_FF_Capture::height)
533 .def_prop_ro("fps", &VK_FF_Capture::fps)
534 .def_prop_ro("using_hardware_decode", &VK_FF_Capture::using_hardware_decode)
535 .def_prop_ro("hardware_decode_device", &VK_FF_Capture::hardware_decode_device)
536#if defined(MXVK_CUDA)
537 .def(
538 "read_gpu_rgba_to_host",
539 [](VK_FF_Capture &capture, bool flip_y) {
540 cv::cuda::GpuMat gpu;
541 cv::cuda::Stream stream;
542 if (!capture.readGpuRgba(gpu, stream, flip_y))
543 return nb::object(nb::none());
544 stream.waitForCompletion();
545 cv::Mat host;
546 gpu.download(host);
547 return opencv_rgba_array(std::move(host));
548 },
549 nb::arg("flip_y") = false)
550#endif
551 ;
552#endif
553
554 nb::class_<VK_ConfigItem>(module, "ConfigItem").def(nb::init<>()).def_rw("key", &VK_ConfigItem::key).def_rw("value", &VK_ConfigItem::value);
555
556 nb::class_<VK_Config>(module, "Config").def(nb::init<>()).def(nb::init<const std::string &>(), nb::arg("file_path")).def("item_at_key", &VK_Config::itemAtKey, nb::arg("section"), nb::arg("key"), nb::arg("default_value") = "").def("set_item", &VK_Config::setItem, nb::arg("section"), nb::arg("key"), nb::arg("value")).def("load_file", &VK_Config::loadFile, nb::arg("path")).def("save_file", &VK_Config::saveFile, nb::arg("path")).def("split_by_comma", &VK_Config::splitByComma, nb::arg("value"));
557
558 nb::class_<VK_Joystick>(module, "Joystick").def(nb::init<>()).def("open", &VK_Joystick::open, nb::arg("index")).def("close", &VK_Joystick::close).def("name", &VK_Joystick::name).def("index", &VK_Joystick::joystickIndex).def_static("count", &VK_Joystick::joysticks).def("num_buttons", &VK_Joystick::numButtons).def("num_hats", &VK_Joystick::numHats).def("num_axes", &VK_Joystick::numAxes).def("button", &VK_Joystick::getButton, nb::arg("index")).def("hat", &VK_Joystick::getHat, nb::arg("index")).def("axis", &VK_Joystick::getAxis, nb::arg("index"));
559
560 nb::class_<VK_Controller>(module, "Controller").def(nb::init<>()).def("open", &VK_Controller::open, nb::arg("index")).def("close", &VK_Controller::close).def("name", &VK_Controller::name).def("index", &VK_Controller::controllerIndex).def_static("count", &VK_Controller::joysticks).def("button", [](const VK_Controller &controller, int button) { return controller.getButton(static_cast<SDL_GamepadButton>(button)); }, nb::arg("button")).def("hat", &VK_Controller::getHat, nb::arg("index")).def("axis", [](const VK_Controller &controller, int axis) { return controller.getAxis(static_cast<SDL_GamepadAxis>(axis)); }, nb::arg("axis")).def("active", &VK_Controller::active);
561
562 nb::class_<VKVertex>(module, "Vertex").def(nb::init<>()).def_prop_rw("position", [](const VKVertex &value) { return nb::make_tuple(value.pos[0], value.pos[1], value.pos[2]); }, [](VKVertex &value, const std::array<float, 3> &position) { std::copy(position.begin(), position.end(), value.pos); }).def_prop_rw("tex_coord", [](const VKVertex &value) { return nb::make_tuple(value.texCoord[0], value.texCoord[1]); }, [](VKVertex &value, const std::array<float, 2> &tex_coord) { std::copy(tex_coord.begin(), tex_coord.end(), value.texCoord); }).def_prop_rw("normal", [](const VKVertex &value) { return nb::make_tuple(value.normal[0], value.normal[1], value.normal[2]); }, [](VKVertex &value, const std::array<float, 3> &normal) { std::copy(normal.begin(), normal.end(), value.normal); });
563
564 nb::class_<SubMesh>(module, "SubMesh").def(nb::init<>()).def_rw("first_index", &SubMesh::firstIndex).def_rw("index_count", &SubMesh::indexCount).def_rw("texture_index", &SubMesh::textureIndex).def_rw("material_name", &SubMesh::materialName);
565
566 nb::class_<MXMaterial>(module, "Material").def(nb::init<>()).def_rw("name", &MXMaterial::name).def_prop_rw("ambient", [](const MXMaterial &value) { return nb::make_tuple(value.ka[0], value.ka[1], value.ka[2]); }, [](MXMaterial &value, const std::array<float, 3> &ambient) { std::copy(ambient.begin(), ambient.end(), value.ka); }).def_prop_rw("diffuse", [](const MXMaterial &value) { return nb::make_tuple(value.kd[0], value.kd[1], value.kd[2]); }, [](MXMaterial &value, const std::array<float, 3> &diffuse) { std::copy(diffuse.begin(), diffuse.end(), value.kd); }).def_prop_rw("specular", [](const MXMaterial &value) { return nb::make_tuple(value.ks[0], value.ks[1], value.ks[2]); }, [](MXMaterial &value, const std::array<float, 3> &specular) { std::copy(specular.begin(), specular.end(), value.ks); }).def_rw("shininess", &MXMaterial::ns).def_rw("opacity", &MXMaterial::d).def_rw("illumination_model", &MXMaterial::illum).def_rw("diffuse_map", &MXMaterial::map_kd);
567
568 nb::class_<MXModel>(module, "Model")
569 .def(nb::init<>())
570 .def("load", nb::overload_cast<const std::string &, float>(&MXModel::load), nb::arg("path"), nb::arg("position_scale") = 1.0F)
571 .def("load", nb::overload_cast<const std::string &, const std::string &, const std::string &, float>(&MXModel::load), nb::arg("path"), nb::arg("texture_manifest_path"), nb::arg("texture_base_path"), nb::arg("position_scale") = 1.0F)
572 .def("export_obj", nb::overload_cast<const std::string &, const std::string &>(&MXModel::exportOBJ, nb::const_), nb::arg("obj_path"), nb::arg("mtl_path") = "")
573 .def_static("export_obj_from_file", nb::overload_cast<const std::string &, const std::string &, const std::string &, const std::string &, float>(&MXModel::exportOBJ), nb::arg("model_path"), nb::arg("texture_manifest_path"), nb::arg("texture_base_path"), nb::arg("obj_path"), nb::arg("position_scale") = 1.0F)
574 .def("compress_indices", &MXModel::compressIndices)
575 .def_prop_ro("vertices", &MXModel::vertices, nb::rv_policy::copy)
576 .def_prop_ro("indices", &MXModel::indices, nb::rv_policy::copy)
577 .def_prop_ro("index_count", &MXModel::indexCount)
578 .def_prop_ro("sub_mesh_count", &MXModel::subMeshCount)
579 .def_prop_ro("sub_meshes", &MXModel::subMeshes, nb::rv_policy::copy)
580 .def_prop_ro("materials", &MXModel::materials, nb::rv_policy::copy)
581 .def_prop_ro("material_library_path", &MXModel::mtlLibPath, nb::rv_policy::copy);
582
583 nb::class_<SDL_Color>(module, "Color").def(nb::init<>()).def(nb::init<uint8_t, uint8_t, uint8_t, uint8_t>(), nb::arg("red"), nb::arg("green"), nb::arg("blue"), nb::arg("alpha") = 255).def_rw("red", &SDL_Color::r).def_rw("green", &SDL_Color::g).def_rw("blue", &SDL_Color::b).def_rw("alpha", &SDL_Color::a);
584
585 nb::class_<VK_Console>(module, "Console")
586 .def(nb::init<>())
587 .def("attach", &VK_Console::attach, nb::arg("window"), nb::arg("font_path"), nb::arg("font_size") = 18)
588 .def("set_font", &VK_Console::setFont, nb::arg("font_path"), nb::arg("font_size"))
589 .def("draw", &VK_Console::draw)
590 .def("print_line", &VK_Console::printLine, nb::arg("line"), nb::arg("color") = SDL_Color{255, 255, 255, 255})
591 .def("clear", &VK_Console::clear)
592 .def("set_prompt", &VK_Console::setPrompt, nb::arg("prompt"))
593 .def("set_visible", &VK_Console::setVisible, nb::arg("visible"))
594 .def("show", &VK_Console::show)
595 .def("hide", &VK_Console::hide)
596 .def("toggle", &VK_Console::toggle)
597 .def("visible", &VK_Console::isVisible)
598 .def("set_max_lines", &VK_Console::setMaxLines, nb::arg("count"))
599 .def("set_max_visible_lines", &VK_Console::setMaxVisibleLines, nb::arg("count"))
600 .def("set_sprite_y_origin_top_left", &VK_Console::setSpriteYOriginTopLeft, nb::arg("enabled"))
601 .def_prop_ro("input_buffer", &VK_Console::inputBuffer, nb::rv_policy::copy);
602
603 nb::class_<PointSpriteVertex>(module, "PointSpriteVertex").def(nb::init<>()).def_prop_rw("position", [](const PointSpriteVertex &value) { return nb::make_tuple(value.position[0], value.position[1], value.position[2]); }, [](PointSpriteVertex &value, const std::array<float, 3> &position) { std::copy(position.begin(), position.end(), value.position); }).def_rw("size", &PointSpriteVertex::size).def_prop_rw("color", [](const PointSpriteVertex &value) { return nb::make_tuple(value.color[0], value.color[1], value.color[2], value.color[3]); }, [](PointSpriteVertex &value, const std::array<float, 4> &color) { std::copy(color.begin(), color.end(), value.color); });
604
605 nb::class_<VK_PointSpriteBatch>(module, "PointSpriteBatch")
606 .def(nb::init<>())
607 .def("load", &VK_PointSpriteBatch::load, nb::arg("window"), nb::arg("texture_path"), nb::arg("vertex_shader_path"), nb::arg("fragment_shader_path"), nb::arg("max_vertices"))
608 .def("resize", &VK_PointSpriteBatch::resize, nb::arg("window"))
609 .def("cleanup", &VK_PointSpriteBatch::cleanup)
610 .def(
611 "upload_vertices",
612 [](VK_PointSpriteBatch &batch, const std::vector<PointSpriteVertex> &vertices) { batch.upload_vertices(vertices.data(), vertices.size()); },
613 nb::arg("vertices"))
614 .def("set_additive_blending", &VK_PointSpriteBatch::set_additive_blending, nb::arg("enabled"))
615 .def("set_depth_test_enabled", &VK_PointSpriteBatch::set_depth_test_enabled, nb::arg("enabled"))
616 .def("set_depth_write_enabled", &VK_PointSpriteBatch::set_depth_write_enabled, nb::arg("enabled"))
617 .def_prop_ro("loaded", &VK_PointSpriteBatch::loaded)
618 .def_prop_ro("capacity", &VK_PointSpriteBatch::capacity)
619 .def_prop_ro("vertex_count", &VK_PointSpriteBatch::vertex_count);
620
621 nb::class_<BufferResource>(module, "BufferResource").def(nb::init<>()).def_rw("size", &BufferResource::size).def_prop_ro("buffer", [](const BufferResource &value) { return reinterpret_cast<uintptr_t>(value.buffer); }).def_prop_ro("memory", [](const BufferResource &value) { return reinterpret_cast<uintptr_t>(value.memory); }).def_prop_ro("mapped", [](const BufferResource &value) { return reinterpret_cast<uintptr_t>(value.mapped); });
622
623 nb::class_<TextureResource>(module, "TextureResource").def(nb::init<>()).def_rw("width", &TextureResource::width).def_rw("height", &TextureResource::height).def_prop_ro("image", [](const TextureResource &value) { return reinterpret_cast<uintptr_t>(value.image); }).def_prop_ro("memory", [](const TextureResource &value) { return reinterpret_cast<uintptr_t>(value.memory); }).def_prop_ro("view", [](const TextureResource &value) { return reinterpret_cast<uintptr_t>(value.view); }).def_prop_ro("sampler", [](const TextureResource &value) { return reinterpret_cast<uintptr_t>(value.sampler); });
624
625 nb::class_<VK_Stencil::PushConstants>(module, "StencilPushConstants").def(nb::init<>()).def_rw("time", &VK_Stencil::PushConstants::time).def_rw("aspect", &VK_Stencil::PushConstants::aspect).def_rw("phase", &VK_Stencil::PushConstants::phase).def_rw("scale", &VK_Stencil::PushConstants::scale);
626
627 nb::class_<VK_Stencil>(module, "Stencil")
628 .def(nb::init<>())
629 .def("destroy", &VK_Stencil::destroy)
630 .def(
631 "resize",
632 [](VK_Stencil &value, uint32_t width, uint32_t height) { value.resize(VkExtent2D{width, height}); },
633 nb::arg("width"),
634 nb::arg("height"))
635 .def("valid", &VK_Stencil::valid)
636 .def_prop_ro("format", [](const VK_Stencil &value) { return static_cast<int>(value.stencil_format()); })
637 .def_prop_ro("extent", [](const VK_Stencil &value) {
638 const auto extent = value.extent();
639 return nb::make_tuple(extent.width, extent.height);
640 });
641
642 nb::class_<Font>(module, "Font").def(nb::init<>()).def(nb::init<const std::string &, int>(), nb::arg("path"), nb::arg("size")).def("reset", nb::overload_cast<>(&Font::reset)).def("reset", nb::overload_cast<const std::string &, int>(&Font::reset), nb::arg("path"), nb::arg("size")).def("valid", [](const Font &font) { return static_cast<bool>(font); }).def_prop_ro("path", &Font::path).def_prop_ro("size", &Font::size);
643
644 nb::class_<UniformBufferObject>(module, "ModelUniforms").def(nb::init<>()).def_prop_rw("model", [](const UniformBufferObject &value) { return matrix_to_rows(value.model); }, [](UniformBufferObject &value, const std::array<float, 16> &matrix) { value.model = matrix_from_rows(matrix); }).def_prop_rw("view", [](const UniformBufferObject &value) { return matrix_to_rows(value.view); }, [](UniformBufferObject &value, const std::array<float, 16> &matrix) { value.view = matrix_from_rows(matrix); }).def_prop_rw("projection", [](const UniformBufferObject &value) { return matrix_to_rows(value.proj); }, [](UniformBufferObject &value, const std::array<float, 16> &matrix) { value.proj = matrix_from_rows(matrix); }).def_prop_rw("effects", [](const UniformBufferObject &value) { return vector4_array(value.fx); }, [](UniformBufferObject &value, const std::array<float, 4> &effects) { value.fx = vector4(effects); });
645
646 nb::class_<ModelFragmentPushConstants>(module, "ModelFragmentPushConstants").def(nb::init<>()).def_rw("screen_width", &ModelFragmentPushConstants::screenWidth).def_rw("screen_height", &ModelFragmentPushConstants::screenHeight).def_rw("sprite_x", &ModelFragmentPushConstants::spritePosX).def_rw("sprite_y", &ModelFragmentPushConstants::spritePosY).def_rw("sprite_width", &ModelFragmentPushConstants::spriteSizeW).def_rw("sprite_height", &ModelFragmentPushConstants::spriteSizeH).def_rw("effects_enabled", &ModelFragmentPushConstants::effectsOn).def_prop_rw("params", [](const ModelFragmentPushConstants &value) { return vector4_array(value.params); }, [](ModelFragmentPushConstants &value, const std::array<float, 4> &params) { value.params = vector4(params); });
647
648 nb::class_<ModelFragmentUniforms>(module, "ModelFragmentUniforms")
649 .def(nb::init<>())
650 .def_prop_rw(
651 "mouse",
652 [](const ModelFragmentUniforms &value) { return vector4_array(value.mouse); },
653 [](ModelFragmentUniforms &value, const std::array<float, 4> &data) { value.mouse = vector4(data); })
654 .def_prop_rw(
655 "uniform0",
656 [](const ModelFragmentUniforms &value) { return vector4_array(value.u0); },
657 [](ModelFragmentUniforms &value, const std::array<float, 4> &data) { value.u0 = vector4(data); })
658 .def_prop_rw(
659 "uniform1",
660 [](const ModelFragmentUniforms &value) { return vector4_array(value.u1); },
661 [](ModelFragmentUniforms &value, const std::array<float, 4> &data) { value.u1 = vector4(data); })
662 .def_prop_rw(
663 "uniform2",
664 [](const ModelFragmentUniforms &value) { return vector4_array(value.u2); },
665 [](ModelFragmentUniforms &value, const std::array<float, 4> &data) { value.u2 = vector4(data); })
666 .def_prop_rw(
667 "uniform3",
668 [](const ModelFragmentUniforms &value) { return vector4_array(value.u3); },
669 [](ModelFragmentUniforms &value, const std::array<float, 4> &data) { value.u3 = vector4(data); })
670 .def_prop_rw(
671 "custom_uniforms",
672 [](const ModelFragmentUniforms &value) {
673 std::vector<std::array<float, 4>> result;
674 result.reserve(value.custom_uniforms.size());
675 for (const auto &entry : value.custom_uniforms)
676 result.push_back(vector4_array(entry));
677 return result;
678 },
679 [](ModelFragmentUniforms &value, const std::vector<std::array<float, 4>> &data) {
680 if (data.size() > value.custom_uniforms.size())
681 throw nb::value_error("at most 16 custom uniforms are supported");
682 value.custom_uniforms.fill(glm::vec4{0.0F});
683 for (size_t index = 0; index < data.size(); ++index)
684 value.custom_uniforms[index] = vector4(data[index]);
685 })
686 .def_prop_rw(
687 "audio_bands",
688 [](const ModelFragmentUniforms &value) { return vector4_array(value.audio_bands); },
689 [](ModelFragmentUniforms &value, const std::array<float, 4> &data) { value.audio_bands = vector4(data); })
690 .def_prop_rw("audio_history", [](const ModelFragmentUniforms &value) { return vector4_array(value.audio_history); }, [](ModelFragmentUniforms &value, const std::array<float, 4> &data) { value.audio_history = vector4(data); });
691
692 nb::class_<VKAbstractModel>(module, "AbstractModel")
693 .def(nb::init<>())
694 .def("load", nb::overload_cast<VK_Window *, const std::string &, const std::string &, const std::string &, float>(&VKAbstractModel::load), nb::arg("window"), nb::arg("model_path"), nb::arg("texture_manifest_path") = "", nb::arg("texture_base_path") = "", nb::arg("scale") = 1.0F, nb::keep_alive<1, 2>())
695 .def("set_shaders", &VKAbstractModel::setShaders, nb::arg("window"), nb::arg("vertex_shader_path"), nb::arg("fragment_shader_path"))
696 .def("update_uniforms", &VKAbstractModel::updateUBO, nb::arg("image_index"), nb::arg("uniforms"))
697 .def("enable_extended_fragment_uniforms", &VKAbstractModel::enableExtendedFragmentUniforms)
698 .def("update_fragment_uniforms", &VKAbstractModel::updateFragmentUBO, nb::arg("image_index"), nb::arg("uniforms"))
699 .def("set_fragment_push_constants", &VKAbstractModel::setFragmentPushConstants, nb::arg("constants"))
700 .def(
701 "update_primary_texture",
702 [](VKAbstractModel &model, nb::ndarray<uint8_t, nb::c_contig, nb::device::cpu> pixels, int width, int height, int pitch) {
703 if (width <= 0 || height <= 0)
704 throw nb::value_error("width and height must be positive");
705 if (pitch == 0)
706 pitch = width * 4;
707 if (pitch < width * 4 || pixels.nbytes() < static_cast<size_t>(pitch) * static_cast<size_t>(height))
708 throw nb::value_error("RGBA buffer is smaller than pitch * height");
709 return model.updatePrimaryTexture(pixels.data(), width, height, pitch);
710 },
711 nb::arg("pixels"),
712 nb::arg("width"),
713 nb::arg("height"),
714 nb::arg("pitch") = 0)
715 .def(
716 "render",
717 [](const VKAbstractModel &model, nb::capsule command_buffer, uint32_t image_index, bool wireframe) {
718 auto command = static_cast<VkCommandBuffer>(command_buffer.data());
719 if (command == VK_NULL_HANDLE)
720 throw nb::value_error("command_buffer capsule must contain a VkCommandBuffer pointer");
721 model.render(command, image_index, wireframe);
722 },
723 nb::arg("command_buffer"),
724 nb::arg("image_index"),
725 nb::arg("wireframe") = false)
726 .def(
727 "render_with_push_constants",
728 [](VKAbstractModel &model, nb::capsule command_buffer, uint32_t image_index, size_t texture_index, const UniformBufferObject &uniforms, bool wireframe) {
729 auto command = static_cast<VkCommandBuffer>(command_buffer.data());
730 if (command == VK_NULL_HANDLE)
731 throw nb::value_error("command_buffer capsule must contain a VkCommandBuffer pointer");
732 model.renderWithPushConstants(command, image_index, texture_index, uniforms, wireframe);
733 },
734 nb::arg("command_buffer"),
735 nb::arg("image_index"),
736 nb::arg("texture_index"),
737 nb::arg("uniforms"),
738 nb::arg("wireframe") = false)
739 .def("resize", &VKAbstractModel::resize, nb::arg("window"))
740 .def("cleanup", &VKAbstractModel::cleanup, nb::arg("window"))
741 .def("set_backface_culling", &VKAbstractModel::setBackfaceCulling, nb::arg("enabled"))
742 .def("set_alpha_blending", &VKAbstractModel::setAlphaBlending, nb::arg("enabled"))
743 .def(
744 "set_color_attachment_format",
745 [](VKAbstractModel &model, int format) { model.setColorAttachmentFormat(static_cast<VkFormat>(format)); },
746 nb::arg("format"))
747 .def_prop_ro("model", &VKAbstractModel::model, nb::rv_policy::reference_internal)
748 .def_prop_ro("center_offset",
749 [](const VKAbstractModel &model) {
750 const auto value = model.modelCenterOffset();
751 return nb::make_tuple(value.x, value.y, value.z);
752 })
753 .def_prop_ro("render_scale", &VKAbstractModel::modelRenderScale)
754 .def_prop_ro("axis_extent",
755 [](const VKAbstractModel &model) {
756 const auto value = model.modelAxisExtent();
757 return nb::make_tuple(value.x, value.y, value.z);
758 })
759 .def_prop_ro("loaded", &VKAbstractModel::isLoaded);
760
761 auto sprite = nb::class_<VK_Sprite>(module, "Sprite");
762 sprite.def("load", nb::overload_cast<const std::string &, const std::string &>(&VK_Sprite::loadSprite), nb::arg("path"), nb::arg("fragment_shader_path") = "")
763 .def("create_empty", &VK_Sprite::createEmptySprite, nb::arg("width"), nb::arg("height"), nb::arg("vertex_shader_path") = "", nb::arg("fragment_shader_path") = "")
764 .def("create_empty_rgba16", &VK_Sprite::createEmptySpriteRgba16, nb::arg("width"), nb::arg("height"), nb::arg("vertex_shader_path") = "", nb::arg("fragment_shader_path") = "")
765 .def("draw", nb::overload_cast<int, int>(&VK_Sprite::drawSprite), nb::arg("x"), nb::arg("y"))
766 .def("draw", nb::overload_cast<int, int, float, float>(&VK_Sprite::drawSprite), nb::arg("x"), nb::arg("y"), nb::arg("scale_x"), nb::arg("scale_y"))
767 .def("draw_rotated", nb::overload_cast<int, int, float, float, float>(&VK_Sprite::drawSprite), nb::arg("x"), nb::arg("y"), nb::arg("scale_x"), nb::arg("scale_y"), nb::arg("rotation"))
768 .def("draw_rect", &VK_Sprite::drawSpriteRect, nb::arg("x"), nb::arg("y"), nb::arg("width"), nb::arg("height"))
769 .def(
770 "update_texture",
771 [](VK_Sprite &value, nb::ndarray<uint8_t, nb::c_contig, nb::device::cpu> pixels, int width, int height, int pitch) { value.updateTexture(pixels.data(), width, height, pitch); },
772 nb::arg("pixels"),
773 nb::arg("width"),
774 nb::arg("height"),
775 nb::arg("pitch") = 0)
776 .def(
777 "update_texture_rgba16",
778 [](VK_Sprite &value, nb::ndarray<uint16_t, nb::c_contig, nb::device::cpu> pixels, int width, int height, int pitch) { value.updateTextureRgba16(pixels.data(), width, height, pitch); },
779 nb::arg("pixels"),
780 nb::arg("width"),
781 nb::arg("height"),
782 nb::arg("pitch") = 0)
783 .def("set_shader_params", &VK_Sprite::setShaderParams, nb::arg("p1") = 0.0F, nb::arg("p2") = 0.0F, nb::arg("p3") = 0.0F, nb::arg("p4") = 0.0F)
784 .def("set_effects_enabled", &VK_Sprite::setEffectsEnabled, nb::arg("enabled"))
785 .def("effects_enabled", &VK_Sprite::getEffectsEnabled)
786 .def("clear_queue", &VK_Sprite::clearQueue)
787 .def("set_vertex_shader_path", &VK_Sprite::setVertexShaderPath, nb::arg("path"))
788 .def("set_fragment_shader_path", &VK_Sprite::setFragmentShaderPath, nb::arg("path"))
789 .def("enable_compute_shader", &VK_Sprite::enableComputeShader, nb::arg("path"), nb::arg("local_size_x"), nb::arg("local_size_y"), nb::arg("local_size_z") = 1)
790 .def("enable_instancing", &VK_Sprite::enableInstancing, nb::arg("max_instances"), nb::arg("vertex_shader_path"), nb::arg("fragment_shader_path"))
791 .def("instancing_enabled", &VK_Sprite::isInstancingEnabled)
792 .def("enable_extended_ubo", &VK_Sprite::enableExtendedUBO)
793 .def("extended_ubo_enabled", &VK_Sprite::isExtendedUBOEnabled)
794 .def("set_mouse_state", &VK_Sprite::setMouseState, nb::arg("x"), nb::arg("y"), nb::arg("pressed"), nb::arg("reserved") = 0.0F)
795 .def("set_uniform0", &VK_Sprite::setUniform0)
796 .def("set_uniform1", &VK_Sprite::setUniform1)
797 .def("set_uniform2", &VK_Sprite::setUniform2)
798 .def("set_uniform3", &VK_Sprite::setUniform3)
799 .def("set_audio_bands", &VK_Sprite::setAudioBands, nb::arg("low"), nb::arg("mid"), nb::arg("high"), nb::arg("reserved") = 0.0F)
800 .def("set_custom_uniforms", &VK_Sprite::setCustomUniforms, nb::arg("values"))
801 .def("enable_history_texture", &VK_Sprite::enableHistoryTexture, nb::arg("width"), nb::arg("height"), nb::arg("layers"))
802 .def("enable_history_texture_rgba16", &VK_Sprite::enableHistoryTextureRgba16Float, nb::arg("width"), nb::arg("height"), nb::arg("layers"))
803 .def("share_history_texture", &VK_Sprite::shareHistoryTexture, nb::arg("source"))
804 .def("enable_spectrum_texture", &VK_Sprite::enableSpectrumTexture, nb::arg("bins"))
805 .def("enable_spectrum_history_texture", &VK_Sprite::enableSpectrumHistoryTexture, nb::arg("bins"), nb::arg("layers"))
806 .def_prop_ro("width", &VK_Sprite::getWidth)
807 .def_prop_ro("height", &VK_Sprite::getHeight)
808 .def_prop_ro("has_own_pipeline", &VK_Sprite::hasOwnPipeline);
809
810 auto sprite3d = nb::class_<VK_Sprite3D>(module, "Sprite3D");
811 sprite3d.def(nb::init<>())
812 .def("load", nb::overload_cast<VK_Window *, const std::string &, const std::string &, const std::string &>(&VK_Sprite3D::load), nb::arg("window"), nb::arg("path"), nb::arg("vertex_shader_path") = "", nb::arg("fragment_shader_path") = "")
813 .def(
814 "update_camera",
815 [](VK_Sprite3D &value, uint32_t image_index, const std::array<float, 16> &view, const std::array<float, 16> &projection) { value.updateCamera(image_index, matrix_from_rows(view), matrix_from_rows(projection)); },
816 nb::arg("image_index"),
817 nb::arg("view"),
818 nb::arg("projection"))
819 .def(
820 "draw",
821 [](VK_Sprite3D &value, const std::array<float, 3> &position, const std::array<float, 2> &size, const std::array<float, 4> &color, float rotation) { value.drawSprite(glm::vec3{position[0], position[1], position[2]}, glm::vec2{size[0], size[1]}, vector4(color), rotation); },
822 nb::arg("position"),
823 nb::arg("size"),
824 nb::arg("color") = std::array<float, 4>{1.0F, 1.0F, 1.0F, 1.0F},
825 nb::arg("rotation") = 0.0F)
826 .def(
827 "render",
828 [](VK_Sprite3D &value, nb::capsule command_buffer, uint32_t image_index) {
829 auto command = static_cast<VkCommandBuffer>(command_buffer.data());
830 if (command == VK_NULL_HANDLE)
831 throw nb::value_error("command_buffer capsule must contain a VkCommandBuffer pointer");
832 value.render(command, image_index);
833 },
834 nb::arg("command_buffer"),
835 nb::arg("image_index"))
836 .def("clear_queue", &VK_Sprite3D::clearQueue)
837 .def("set_depth_test_enabled", &VK_Sprite3D::setDepthTestEnabled, nb::arg("enabled"))
838 .def("set_depth_write_enabled", &VK_Sprite3D::setDepthWriteEnabled, nb::arg("enabled"))
839 .def("set_alpha_discard_threshold", &VK_Sprite3D::setAlphaDiscardThreshold, nb::arg("threshold"))
840 .def("resize", &VK_Sprite3D::resize, nb::arg("window"))
841 .def("cleanup", &VK_Sprite3D::cleanup)
842 .def_prop_ro("loaded", &VK_Sprite3D::loaded)
843 .def_prop_ro("width", &VK_Sprite3D::getWidth)
844 .def_prop_ro("height", &VK_Sprite3D::getHeight);
845
846 nb::enum_<ShaderStage>(module, "ShaderStage").value("unknown", ShaderStage::Unknown).value("vertex", ShaderStage::Vertex).value("fragment", ShaderStage::Fragment).value("compute", ShaderStage::Compute);
847
848 nb::enum_<VK_Window::PresentModePreference>(module, "PresentModePreference").value("low_latency", VK_Window::PresentModePreference::LowLatency).value("vsync", VK_Window::PresentModePreference::Vsync);
849
850 nb::enum_<VK_Window::RuntimeMode>(module, "RuntimeMode").value("windowed", VK_Window::RuntimeMode::Windowed).value("headless", VK_Window::RuntimeMode::Headless);
851
852 nb::class_<VulkanContext>(module, "VulkanContext").def_prop_ro("device", [](const VulkanContext &context) { return reinterpret_cast<uintptr_t>(context.device); }).def_prop_ro("physical_device", [](const VulkanContext &context) { return reinterpret_cast<uintptr_t>(context.physical_device); }).def_prop_ro("graphics_queue", [](const VulkanContext &context) { return reinterpret_cast<uintptr_t>(context.graphics_queue); }).def_prop_ro("command_pool", [](const VulkanContext &context) { return reinterpret_cast<uintptr_t>(context.command_pool); });
853
854 nb::class_<VK_Window::PostProcessingEffect>(module, "PostProcessingEffect").def(nb::init<>()).def_rw("fragment_shader_path", &VK_Window::PostProcessingEffect::fragmentShaderPath).def_rw("params", &VK_Window::PostProcessingEffect::params).def_rw("time_enabled", &VK_Window::PostProcessingEffect::timeEnabled).def_rw("spectrum_bin_count", &VK_Window::PostProcessingEffect::spectrumBinCount).def_rw("spectrum_history_layer_count", &VK_Window::PostProcessingEffect::spectrumHistoryLayerCount).def_rw("stage", &VK_Window::PostProcessingEffect::stage).def_rw("history_source", &VK_Window::PostProcessingEffect::historySource);
855
856 nb::class_<VK_Window, PythonWindow>(module, "Window")
857 .def(nb::init<>())
858 .def(nb::init<const std::string &, int, int, bool, bool, VK_Window::PresentModePreference, VK_Window::RuntimeMode>(), nb::arg("title"), nb::arg("width"), nb::arg("height"), nb::arg("fullscreen") = false, nb::arg("validation") = true, nb::arg("present_mode") = VK_Window::PresentModePreference::LowLatency, nb::arg("runtime_mode") = VK_Window::RuntimeMode::Windowed)
859 .def(nb::init<const std::string &, int, int, bool, bool, bool>(), nb::arg("title"), nb::arg("width"), nb::arg("height"), nb::arg("fullscreen"), nb::arg("validation"), nb::arg("enable_vsync"))
860 .def("release", &VK_Window::release)
861 .def("wait_idle", [](VK_Window &window) {
862 const VkDevice device = window.getDevice();
863 if (device == VK_NULL_HANDLE)
864 return;
865
866 const VkResult result = vkDeviceWaitIdle(device);
867 if (result != VK_SUCCESS)
868 throw std::runtime_error("vkDeviceWaitIdle failed");
869 })
870 .def("request_exit", [](VK_Window &window) {
871 auto *python_window = dynamic_cast<PythonWindow *>(&window);
872 if (python_window == nullptr)
873 throw nb::type_error("request_exit requires a Python subclass of Window");
874 python_window->request_exit();
875 })
876 .def("init_vulkan", &VK_Window::initVulkan, nb::arg("validation") = true)
877 .def(
878 "event",
879 [](VK_Window &window, nb::capsule event) {
880 auto *sdl_event = static_cast<SDL_Event *>(event.data());
881 if (sdl_event == nullptr)
882 throw nb::value_error("event capsule must contain an SDL_Event pointer");
883 window.event(*sdl_event);
884 },
885 nb::arg("event"))
886 .def("loop", &VK_Window::loop)
887 .def("render", &VK_Window::render)
888 .def("on_swapchain_recreated", [](VK_Window &) {})
889 .def("on_frame_readback_scheduled", [](VK_Window &) {})
890 .def("on_frame_readback", [](VK_Window &, nb::ndarray<nb::numpy, std::uint8_t>, uint32_t, uint32_t) {}, nb::arg("pixels"), nb::arg("width"), nb::arg("height"))
891 .def(
892 "flush_frame_readbacks",
893 [](VK_Window &window) {
894 auto *python_window = dynamic_cast<PythonWindow *>(&window);
895 if (python_window == nullptr)
896 throw nb::type_error("flush_frame_readbacks requires a Python subclass of Window");
897 python_window->flush_frame_readbacks();
898 })
899 .def("on_record_custom_rendering", [](VK_Window &, nb::capsule, uint32_t) {}, nb::arg("command_buffer"), nb::arg("image_index"))
900 .def(
901 "on_prepare_frame_rendering",
902 [](VK_Window &window, nb::capsule command_buffer, uint32_t image_index) {
903 auto command_buffer_handle = static_cast<VkCommandBuffer>(command_buffer.data());
904 if (command_buffer_handle == VK_NULL_HANDLE)
905 throw nb::value_error("command_buffer capsule must contain a VkCommandBuffer pointer");
906 window.onPrepareFrameRendering(command_buffer_handle, image_index);
907 },
908 nb::arg("command_buffer"),
909 nb::arg("image_index"))
910 .def("proc", &VK_Window::proc)
911 .def("save_snapshot", &VK_Window::saveSnapshot, nb::arg("path"))
912 .def("set_font", &VK_Window::setFont, nb::arg("path"), nb::arg("size") = 24)
913 .def("set_preview_font", &VK_Window::setPreviewFont, nb::arg("path"), nb::arg("size") = 24)
914 .def("print_text", nb::overload_cast<const std::string &, int, int, const SDL_Color &>(&VK_Window::printText), nb::arg("text"), nb::arg("x"), nb::arg("y"), nb::arg("color"))
915 .def("print_text", nb::overload_cast<const std::string &, int, int, const SDL_Color &, const Font &>(&VK_Window::printText), nb::arg("text"), nb::arg("x"), nb::arg("y"), nb::arg("color"), nb::arg("font"))
916 .def("print_preview_text", nb::overload_cast<const std::string &, int, int, const SDL_Color &>(&VK_Window::printPreviewText), nb::arg("text"), nb::arg("x"), nb::arg("y"), nb::arg("color"))
917 .def("print_preview_text", nb::overload_cast<const std::string &, int, int, const SDL_Color &, const Font &>(&VK_Window::printPreviewText), nb::arg("text"), nb::arg("x"), nb::arg("y"), nb::arg("color"), nb::arg("font"))
918 .def("clear_text_queue", &VK_Window::clearTextQueue)
919 .def("set_clear_color", &VK_Window::setClearColor, nb::arg("red"), nb::arg("green"), nb::arg("blue"), nb::arg("alpha") = 1.0F)
920 .def("set_enable_screenshot", &VK_Window::setEnableScreenshot, nb::arg("enabled"))
921 .def("screenshot_enabled", &VK_Window::screenshotEnabled)
922 .def("set_frame_readback_enabled", &VK_Window::setFrameReadbackEnabled, nb::arg("enabled"))
923 .def("set_frame_readback_rgba16_enabled", &VK_Window::setFrameReadbackRgba16Enabled, nb::arg("enabled"))
924 .def("set_hdr_render_intermediates_enabled", &VK_Window::setHdrRenderIntermediatesEnabled, nb::arg("enabled"))
925 .def("set_render_extent", &VK_Window::setRenderExtent, nb::arg("width"), nb::arg("height"))
926 .def("headless", &VK_Window::headless)
927 .def("ensure_render_resources", &VK_Window::ensureRenderResources)
928 .def("trim_memory", &VK_Window::trimMemory)
929 .def(
930 "get_text_dimensions",
931 [](VK_Window &window, const std::string &text) -> nb::object {
932 int width = 0;
933 int height = 0;
934 if (!window.getTextDimensions(text, width, height))
935 return nb::none();
936 return nb::cast(nb::make_tuple(width, height));
937 },
938 nb::arg("text"))
939 .def(
940 "get_text_dimensions",
941 [](VK_Window &window, const std::string &text, const Font &font) -> nb::object {
942 int width = 0;
943 int height = 0;
944 if (!window.getTextDimensions(text, width, height, font))
945 return nb::none();
946 return nb::cast(nb::make_tuple(width, height));
947 },
948 nb::arg("text"),
949 nb::arg("font"))
950 .def(
951 "create_sprite",
952 [](VK_Window &window, const std::string &path, const std::string &vertex_shader_path, const std::string &fragment_shader_path) { return window.createSprite(path, vertex_shader_path, fragment_shader_path); },
953 nb::arg("path"),
954 nb::arg("vertex_shader_path") = "",
955 nb::arg("fragment_shader_path") = "",
956 nb::rv_policy::reference_internal)
957 .def(
958 "create_sprite_from_surface",
959 [](VK_Window &window, nb::capsule surface, const std::string &vertex_shader_path, const std::string &fragment_shader_path) {
960 auto *sdl_surface = static_cast<SDL_Surface *>(surface.data());
961 if (sdl_surface == nullptr)
962 throw nb::value_error("surface capsule must contain an SDL_Surface pointer");
963 return window.createSprite(sdl_surface, vertex_shader_path, fragment_shader_path);
964 },
965 nb::arg("surface"),
966 nb::arg("vertex_shader_path") = "",
967 nb::arg("fragment_shader_path") = "",
968 nb::rv_policy::reference_internal)
969 .def(
970 "create_sprite",
971 [](VK_Window &window, int width, int height, const std::string &vertex_shader_path, const std::string &fragment_shader_path, uint32_t spectrum_bin_count) { return window.createSprite(width, height, vertex_shader_path, fragment_shader_path, spectrum_bin_count); },
972 nb::arg("width"),
973 nb::arg("height"),
974 nb::arg("vertex_shader_path") = "",
975 nb::arg("fragment_shader_path") = "",
976 nb::arg("spectrum_bin_count") = 0,
977 nb::rv_policy::reference_internal)
978 .def(
979 "create_sprite_with_spectrum_history",
980 [](VK_Window &window, int width, int height, const std::string &vertex_shader_path, const std::string &fragment_shader_path, uint32_t spectrum_bin_count, uint32_t spectrum_history_layer_count) { return window.createSprite(width, height, vertex_shader_path, fragment_shader_path, spectrum_bin_count, spectrum_history_layer_count); },
981 nb::arg("width"),
982 nb::arg("height"),
983 nb::arg("vertex_shader_path"),
984 nb::arg("fragment_shader_path"),
985 nb::arg("spectrum_bin_count"),
986 nb::arg("spectrum_history_layer_count"),
987 nb::rv_policy::reference_internal)
988 .def("attach_post_processing_shader", &VK_Window::attachPostProcessingShader, nb::arg("fragment_shader_path"), nb::arg("p1") = 0.0F, nb::arg("p2") = 0.0F, nb::arg("p3") = 0.0F, nb::arg("p4") = 0.0F, nb::rv_policy::reference_internal)
989 .def("attach_post_processing_shaders", &VK_Window::attachPostProcessingShaders, nb::arg("effects"), nb::rv_policy::reference_internal)
990 .def("detach_post_processing_shader", &VK_Window::detachPostProcessingShader)
991 .def("set_post_processing_shader_params", nb::overload_cast<float, float, float, float>(&VK_Window::setPostProcessingShaderParams), nb::arg("p1") = 0.0F, nb::arg("p2") = 0.0F, nb::arg("p3") = 0.0F, nb::arg("p4") = 0.0F)
992 .def("set_post_processing_effect_params", nb::overload_cast<size_t, float, float, float, float>(&VK_Window::setPostProcessingShaderParams), nb::arg("effect_index"), nb::arg("p1") = 0.0F, nb::arg("p2") = 0.0F, nb::arg("p3") = 0.0F, nb::arg("p4") = 0.0F)
993 .def("set_post_processing_shader_time_enabled", nb::overload_cast<bool>(&VK_Window::setPostProcessingShaderTimeEnabled), nb::arg("enabled"))
994 .def("set_post_processing_effect_time_enabled", nb::overload_cast<size_t, bool>(&VK_Window::setPostProcessingShaderTimeEnabled), nb::arg("effect_index"), nb::arg("enabled"))
995 .def("enable_post_processing", &VK_Window::enablePostProcessing, nb::arg("sprite"))
996 .def("set_post_processing_enabled", &VK_Window::setPostProcessingEnabled, nb::arg("enabled"))
997 .def("set_post_processing_texture_consumer_enabled", &VK_Window::setPostProcessingTextureConsumerEnabled, nb::arg("enabled"))
998 .def("set_post_processing_present_fragment_shader", &VK_Window::setPostProcessingPresentFragmentShader, nb::arg("path"))
999 .def(
1000 "create_sprite3d",
1001 [](VK_Window &window, const std::string &path, const std::string &vertex_shader_path, const std::string &fragment_shader_path) { return window.createSprite3D(path, vertex_shader_path, fragment_shader_path); },
1002 nb::arg("path"),
1003 nb::arg("vertex_shader_path") = "",
1004 nb::arg("fragment_shader_path") = "",
1005 nb::rv_policy::reference_internal)
1006 .def(
1007 "create_sprite3d_from_surface",
1008 [](VK_Window &window, nb::capsule surface, const std::string &vertex_shader_path, const std::string &fragment_shader_path) {
1009 auto *sdl_surface = static_cast<SDL_Surface *>(surface.data());
1010 if (sdl_surface == nullptr)
1011 throw nb::value_error("surface capsule must contain an SDL_Surface pointer");
1012 return window.createSprite3D(sdl_surface, vertex_shader_path, fragment_shader_path);
1013 },
1014 nb::arg("surface"),
1015 nb::arg("vertex_shader_path") = "",
1016 nb::arg("fragment_shader_path") = "",
1017 nb::rv_policy::reference_internal)
1018 .def("show_cursor", &VK_Window::showCursor, nb::arg("visible"))
1019 .def("validation_enabled", &VK_Window::validationEnabled)
1020 .def("context", &VK_Window::context)
1021 .def_prop_ro("sdl_window", [](const VK_Window &window) { return reinterpret_cast<uintptr_t>(window.getSDLWindow()); })
1022 .def_prop_ro("device", [](const VK_Window &window) { return reinterpret_cast<uintptr_t>(window.getDevice()); })
1023 .def_prop_ro("physical_device", [](const VK_Window &window) { return reinterpret_cast<uintptr_t>(window.getPhysicalDevice()); })
1024 .def_prop_ro("graphics_queue", [](const VK_Window &window) { return reinterpret_cast<uintptr_t>(window.getGraphicsQueue()); })
1025 .def_prop_ro("command_pool", [](const VK_Window &window) { return reinterpret_cast<uintptr_t>(window.getCommandPool()); })
1026 .def_prop_ro("pipeline_cache", [](const VK_Window &window) { return reinterpret_cast<uintptr_t>(window.getPipelineCache()); })
1027 .def_prop_ro("swapchain_format", [](const VK_Window &window) { return static_cast<int>(window.getSwapchainFormat()); })
1028 .def_prop_ro("scene_color_format", [](const VK_Window &window) { return static_cast<int>(window.getSceneColorFormat()); })
1029 .def_prop_ro("depth_format", [](const VK_Window &window) { return static_cast<int>(window.getDepthFormat()); })
1030 .def_prop_ro("swapchain_extent",
1031 [](const VK_Window &window) {
1032 const VkExtent2D extent = window.getSwapchainExtent();
1033 return nb::make_tuple(extent.width, extent.height);
1034 })
1035 .def_prop_ro("render_extent",
1036 [](const VK_Window &window) {
1037 const VkExtent2D extent = window.getRenderExtent();
1038 return nb::make_tuple(extent.width, extent.height);
1039 })
1040 .def_prop_ro("swapchain_image_count", &VK_Window::getSwapchainImageCount);
1041
1042 nb::class_<PythonEvent>(module, "Event").def_prop_ro("type", &PythonEvent::type).def_prop_ro("timestamp", &PythonEvent::timestamp).def_prop_ro("key", &PythonEvent::key).def_prop_ro("scancode", &PythonEvent::scancode).def_prop_ro("modifiers", &PythonEvent::modifiers).def_prop_ro("key_down", &PythonEvent::key_down).def_prop_ro("key_up", &PythonEvent::key_up).def_prop_ro("down", &PythonEvent::down).def_prop_ro("repeat", &PythonEvent::repeat).def_prop_ro("text", &PythonEvent::text).def_prop_ro("mouse_motion", &PythonEvent::mouse_motion).def_prop_ro("mouse_button_down", &PythonEvent::mouse_button_down).def_prop_ro("mouse_button_up", &PythonEvent::mouse_button_up).def_prop_ro("mouse_wheel", &PythonEvent::mouse_wheel).def_prop_ro("x", &PythonEvent::x).def_prop_ro("y", &PythonEvent::y).def_prop_ro("relative_x", &PythonEvent::relative_x).def_prop_ro("relative_y", &PythonEvent::relative_y).def_prop_ro("button", &PythonEvent::button).def_prop_ro("clicks", &PythonEvent::clicks).def_prop_ro("wheel_x", &PythonEvent::wheel_x).def_prop_ro("wheel_y", &PythonEvent::wheel_y).def_prop_ro("wheel_ticks_x", &PythonEvent::wheel_ticks_x).def_prop_ro("wheel_ticks_y", &PythonEvent::wheel_ticks_y).def_prop_ro("wheel_flipped", &PythonEvent::wheel_flipped);
1043
1044 nb::class_<VK_IOWindow, VK_Window, PythonIOWindow>(module, "IOWindow")
1045 .def(nb::init<const std::string &, const std::string &, int, int, bool, bool>(), nb::arg("path"), nb::arg("title"), nb::arg("width"), nb::arg("height"), nb::arg("fullscreen") = false, nb::arg("enable_vsync") = false)
1046 .def("print", &VK_IOWindow::print, nb::arg("text"), nb::arg("color") = SDL_Color{255, 255, 255, 255})
1047 .def("request_exit", &VK_IOWindow::requestExit)
1048 .def("console_proc", &VK_IOWindow::console_proc)
1049 .def(
1050 "console_event",
1051 [](VK_IOWindow &window, nb::capsule event) {
1052 auto *value = static_cast<SDL_Event *>(event.data());
1053 if (value == nullptr)
1054 throw nb::value_error("event capsule must contain an SDL_Event pointer");
1055 window.console_event(*value);
1056 },
1057 nb::arg("event"))
1058 .def_prop_ro("visible", &VK_IOWindow::visible);
1059
1060 module.attr("VK_Window") = module.attr("Window");
1061 module.attr("VK_IOWindow") = module.attr("IOWindow");
1062 }
void Stop()
Stop the current interval and print its duration.
void Echo(std::string_view name) const
Print the interval recorded by the clock policy.
unsigned long TimePassed() const
Query the time elapsed since the most recent Start().
void Start(std::string_view name)
Start a new timed interval.
void write(nanobind::ndarray< uint8_t, nanobind::c_contig, nanobind::device::cpu > bytes)
Small RAII wrapper for an SDL_ttf font handle.
Definition mxvk_text.hpp:46
int size() const noexcept
Definition mxvk_text.hpp:61
void reset()
Definition mxvk_text.cpp:27
const std::string & path() const noexcept
Definition mxvk_text.hpp:60
const std::vector< MXMaterial > & materials() const
uint32_t indexCount() const
const std::vector< VKVertex > & vertices() const
const std::string & mtlLibPath() const
void compressIndices()
Remove duplicate vertices and remap indices.
size_t subMeshCount() const
const std::vector< uint32_t > & indices() const
void load(const std::string &path, float positionScale=1.0f)
Parse model data from disk into CPU-side arrays.
const std::vector< SubMesh > & subMeshes() const
void exportOBJ(const std::string &objPath, const std::string &mtlPath="") const
Export the loaded model as Wavefront OBJ plus MTL.
Convenience wrapper that owns mesh, textures, descriptors, and pipeline state.
void setAlphaBlending(bool enabled)
Enable or disable alpha blending for this model pipeline.
void setBackfaceCulling(bool enabled)
Enable or disable backface culling for this model pipeline.
void updateFragmentUBO(uint32_t imageIndex, const ModelFragmentUniforms &uniforms)
Update extended fragment uniforms for one swapchain image.
void updateUBO(uint32_t imageIndex, const UniformBufferObject &ubo)
Update one per-frame UBO payload.
float modelRenderScale() const
Access the computed render scale used for normalization.
void load(VK_Window *window, const std::string &modelPath, const std::string &textureManifestPath, const std::string &textureBasePath, float scale=1.0f)
Load mesh/texture resources and build Vulkan state.
void setShaders(VK_Window *window, const std::string &vertSpv, const std::string &fragSpv)
Configure custom shader paths and rebuild pipelines.
bool isLoaded() const
True once the model has been uploaded to GPU buffers.
void cleanup(VK_Window *window)
Destroy all owned Vulkan resources.
const MXModel & model() const
Access the underlying mesh object.
void resize(VK_Window *window)
Rebuild swapchain-dependent resources after resize.
void enableExtendedFragmentUniforms()
Use binding 1 for fragment uniforms and binding 2 for model transforms.
void setFragmentPushConstants(const ModelFragmentPushConstants &constants)
Set sprite-compatible push constants used by custom fragment shaders.
Vulkan OpenCV video capture source.
Definition mxvk_cv.hpp:30
bool readToModelTexture(VKAbstractModel &model, bool flipY=false)
Definition mxvk_cv.cpp:278
bool open(const std::string &filename)
Open a video file.
Definition mxvk_cv.cpp:14
bool reload(size_t width, size_t height, const std::string &vert, const std::string &frag)
Replace the vertex/fragment shaders.
Definition mxvk_cv.cpp:53
void resetSprite()
Destroy the current sprite and its Vulkan resources.
Definition mxvk_cv.cpp:42
bool grab()
Decode and discard the next capture frame without conversion.
Definition mxvk_cv.cpp:66
VK_Sprite * getSprite()
Access the backing sprite.
Definition mxvk_cv.hpp:91
void close()
Close the capture device.
Definition mxvk_cv.cpp:37
double get(unsigned int option)
Query a VideoCapture property.
Definition mxvk_cv.cpp:457
bool readToSprite(VK_Sprite &targetSprite)
Definition mxvk_cv.cpp:346
void draw(int x, int y, int width, int height)
Draw the current frame at a specified position and size.
Definition mxvk_cv.cpp:445
bool is_open() const
Check whether the capture device is open.
Definition mxvk_cv.hpp:71
bool read()
Capture and upload the next video frame.
Definition mxvk_cv.cpp:430
void set(unsigned int option, double value)
Set a VideoCapture property.
Definition mxvk_cv.cpp:455
std::string value
Value associated with the key.
Definition mxvk_cfg.hpp:25
std::string key
Configuration key name.
Definition mxvk_cfg.hpp:24
std::vector< std::string > splitByComma(const std::string &str) const
Split a string on comma delimiters.
Definition mxvk_cfg.cpp:99
void saveFile(const std::string &f2)
Save the current configuration to a file.
Definition mxvk_cfg.cpp:54
void loadFile(const std::string &f)
Load (or reload) a configuration file from disk.
Definition mxvk_cfg.cpp:14
VK_ConfigItem itemAtKey(const std::string &section, const std::string &key, const std::string &default_value="") const
Retrieve a configuration item by section and key.
Definition mxvk_cfg.cpp:85
void setItem(const std::string &section, const std::string &key, const std::string &value)
Insert or update a configuration item.
Definition mxvk_cfg.cpp:97
void setVisible(bool value) noexcept
Show or hide the console.
void draw()
Draw the current console overlay.
void setSpriteYOriginTopLeft(bool enabled) noexcept
Select whether console sprite elements use top-left Y coordinates.
const std::string & inputBuffer() const noexcept
void toggle() noexcept
Toggle console visibility.
bool isVisible() const noexcept
void hide() noexcept
Hide console.
void setFont(const std::string &fontPath, int fontSize)
Change only the console font, leaving the window default font unchanged.
void setPrompt(const std::string &prompt)
Set the input prompt text.
void show() noexcept
Show console.
void clear()
Clear all buffered output lines.
void setMaxVisibleLines(std::size_t maxVisibleLines)
Set max number of lines rendered on screen.
void printLine(const std::string &line, SDL_Color color=SDL_Color{255, 255, 255, 255})
Append one line to console output.
void attach(VK_Window &window, const std::string &fontPath, int fontSize=18)
Attach this console to a window and configure console font rendering.
void setMaxLines(std::size_t maxLines)
Set max number of retained output lines.
RAII wrapper for SDL_Gamepad (standard layout mapping).
int controllerIndex() const
Return the device index this controller was opened with.
void close()
Close the controller handle if open.
bool active() const
Check whether the controller is currently usable.
Uint8 getHat(int hat) const
Read the position of a POV hat.
static int joysticks()
Return the number of connected joysticks/controllers.
bool open(int index)
Open a gamepad by device index.
std::string name() const
Return the controller's name as reported by SDL.
FFmpeg-backed video-file capture source.
void close()
Close the active file and release decoder resources.
bool is_open() const
Check whether a decoder is open.
double fps() const
Source frame rate, falling back to 30 fps when unknown.
int width() const
Source width in pixels.
int height() const
Source height in pixels.
bool open(const std::string &filename)
Open a video file.
int hardware_decode_device() const
Selected CUDA hardware-decode device, or -1 for FFmpeg's default.
bool skip()
Decode and discard the next video frame.
bool using_hardware_decode() const
True when CUDA hardware decode is active.
bool seek_start()
Seek the active video stream back to its beginning.
virtual void console_proc()=0
void print(const std::string &text, SDL_Color col={255, 255, 255, 255})
bool getButton(int button) const
Test whether a button is currently pressed.
bool open(int index)
Open the joystick at the given device index.
Uint8 getHat(int hat) const
Read the position of a POV hat.
static int joysticks()
Return the number of connected joysticks.
int joystickIndex() const
Return the device index this joystick was opened with.
std::string name() const
Return the joystick's name as reported by SDL.
Sint16 getAxis(int axis) const
Read the current axis value.
void close()
Close the joystick handle if open.
int loadWav(const std::string &filename)
Load a WAV file as a sound effect chunk.
int playWav(int id, int value=0, int channel=-1)
Play a previously loaded WAV chunk.
void stopMusic()
Stop background music playback immediately.
void cleanup()
Free all loaded audio chunks and music tracks.
bool isPlaying(int channel) const
Query whether a sound track is currently playing.
void init()
Open the audio device (called automatically by constructor).
int loadMusic(const std::string &filename)
Load an audio file as streaming background music.
bool isMusicPlaying(int id) const
Query whether a loaded music track is currently playing.
int playMusic(int id, int value=0)
Start playing a previously loaded music track.
Reusable Vulkan point-list renderer for textured particle effects.
void load(VK_Window *window, const std::string &texture_path, const std::string &vertex_shader_path, const std::string &fragment_shader_path, size_t max_vertices)
Create texture, vertex buffer, descriptors, and point-list pipeline.
void set_depth_write_enabled(bool enabled)
Enable or disable depth writes in the point-sprite pipeline.
void resize(VK_Window *window)
Recreate swapchain-dependent resources after a window resize.
void cleanup()
Destroy all owned resources and reset the batch to an unloaded state.
void set_additive_blending(bool enabled)
Select additive or alpha-over color blending.
void set_depth_test_enabled(bool enabled)
Enable or disable depth testing in the point-sprite pipeline.
Depth-tested 3D billboard sprite batch.
void cleanup()
Destroy all owned Vulkan resources.
void setDepthWriteEnabled(bool enabled)
Enable or disable depth writes for the 3D sprite pipeline.
void setDepthTestEnabled(bool enabled)
Enable or disable depth testing for the 3D sprite pipeline.
void load(VK_Window *window, const std::string &pngPath, const std::string &vertexShaderPath="", const std::string &fragmentShaderPath="")
Load sprite texture and build the 3D billboard pipeline from a PNG file.
void clearQueue()
Discard all queued sprite draws without rendering them.
void resize(VK_Window *window)
Rebuild swapchain-dependent resources after resize.
void setAlphaDiscardThreshold(float threshold)
Set the alpha threshold used to discard transparent texels.
void createEmptySpriteRgba16(int width, int height, const std::string &vertexShaderPath="", const std::string &fragmentShaderPath="")
Create a blank RGBA16 UNORM sprite texture.
void setUniform3(float x, float y, float z, float w)
Upload user uniform 3 to the extended UBO.
void enableInstancing(uint32_t maxInstances, const std::string &instanceVertShaderPath, const std::string &instanceFragShaderPath)
Enable GPU instancing for this sprite type.
void setVertexShaderPath(const std::string &path)
Override the vertex shader path (used when rebuilding the pipeline).
bool isInstancingEnabled() const
void setUniform1(float x, float y, float z, float w)
Upload user uniform 1 to the extended UBO.
int getHeight() const
bool getEffectsEnabled() const
void setUniform0(float x, float y, float z, float w)
Upload user uniform 0 to the extended UBO.
void shareHistoryTexture(const VK_Sprite &source)
Bind another sprite's history array without taking ownership.
void enableComputeShader(const std::string &path, uint32_t localSizeX, uint32_t localSizeY, uint32_t localSizeZ=1)
Build a compute image-effect pipeline for this sprite.
void setShaderParams(float p1=0.0f, float p2=0.0f, float p3=0.0f, float p4=0.0f)
Set up to four custom shader float parameters.
bool isExtendedUBOEnabled() const
void enableExtendedUBO()
Allocate and initialise the extended uniform buffer object.
bool hasOwnPipeline() const
int getWidth() const
void loadSprite(const std::string &pngPath, const std::string &fragmentShaderPath="")
Load sprite texture from a PNG file.
void createEmptySprite(int width, int height, const std::string &vertexShaderPath="", const std::string &fragmentShaderPath="")
Create a blank (un-initialised) sprite texture.
uint32_t enableSpectrumHistoryTexture(uint32_t bins, uint32_t layers)
Allocate a shader-readable FFT spectrum-history array.
void drawSprite(int x, int y)
Queue a draw at the given pixel position.
void setMouseState(float mx, float my, float pressed, float reserved=0.0f)
Upload mouse state to the extended UBO.
void setEffectsEnabled(bool enabled)
Enable or disable the custom fragment shader effects.
void enableHistoryTexture(uint32_t width, uint32_t height, uint32_t layers)
Allocate a shader-readable RGBA history texture array.
void setCustomUniforms(const std::vector< float > &values)
Upload ordered custom float values to the extended UBO.
void setFragmentShaderPath(const std::string &path)
Replace the fragment shader path and rebuild the custom pipeline.
void clearQueue()
Discard all pending draw commands without rendering.
void enableHistoryTextureRgba16Float(uint32_t width, uint32_t height, uint32_t layers)
Allocate a shader-readable RGBA16F history texture array.
void enableSpectrumTexture(uint32_t bins)
Allocate a shader-readable 1-D floating-point spectrum texture.
void setAudioBands(float low, float mid, float high, float reserved=0.0f)
Upload audio frequency-band energy to the extended UBO.
void setUniform2(float x, float y, float z, float w)
Upload user uniform 2 to the extended UBO.
void drawSpriteRect(int x, int y, int w, int h)
Queue a draw into an explicit destination rectangle.
Owns a stencil attachment and two fullscreen pipelines for stencil-masked rendering.
void destroy()
Release all Vulkan resources and reset the helper to an empty state.
bool valid() const noexcept
Check whether image resources are ready for rendering.
Main Vulkan window wrapper for MXVK.
Definition mxvk.hpp:39
void showCursor(bool on)
Definition mxvk.cpp:4620
bool screenshotEnabled() const noexcept
Check whether F10 screenshot capture is enabled.
Definition mxvk.hpp:176
void loop()
Run the main event/render loop.
Definition mxvk.cpp:651
virtual bool initVulkan(bool validiation)
Initialize Vulkan state.
Definition mxvk.cpp:365
VulkanContext context() const
Definition mxvk.cpp:80
void setFrameReadbackRgba16Enabled(bool enabled) noexcept
Request normalized RGBA16 callbacks from the final HDR intermediate.
Definition mxvk.hpp:189
void enablePostProcessing(VK_Sprite *sprite)
Definition mxvk.cpp:1635
void setRenderExtent(uint32_t width, uint32_t height)
Render post-processing at a fixed extent independent of the preview window.
Definition mxvk.cpp:1406
void clearTextQueue()
Clear all queued text draw calls for the current frame.
Definition mxvk.cpp:3964
size_t getSwapchainImageCount() const noexcept
Get the number of swapchain images currently allocated.
Definition mxvk.hpp:239
void saveSnapshot(const std::string &path)
Save the most recently rendered window contents as a PNG file.
Definition mxvk.cpp:826
void setClearColor(float r, float g, float b, float a=1.0f)
Set the per-frame color attachment clear color.
Definition mxvk.cpp:637
std::vector< VK_Sprite * > attachPostProcessingShaders(const std::vector< PostProcessingEffect > &effects)
Definition mxvk.cpp:1474
void setHdrRenderIntermediatesEnabled(bool enabled)
Select RGBA16F scene/effect intermediates while retaining the presentation format.
Definition mxvk.cpp:1453
void setFrameReadbackEnabled(bool enabled) noexcept
Enable or disable readback of each rendered swapchain frame.
Definition mxvk.hpp:186
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.
Definition mxvk.cpp:1469
void printPreviewText(const std::string &text, int x, int y, const SDL_Color &col)
Queue preview-only text after frame readback.
Definition mxvk.cpp:3974
bool ensureRenderResources()
Ensure deferred render resources are initialized.
Definition mxvk.cpp:1787
void detachPostProcessingShader()
Detach the current post-processing shader and return to direct swapchain rendering.
Definition mxvk.cpp:1566
void setPostProcessingTextureConsumerEnabled(bool enabled) noexcept
Route the completed effect chain to a derived renderer as a sampled texture.
Definition mxvk.hpp:351
void release()
Release Vulkan and SDL resources.
Definition mxvk.cpp:247
void setFont(const std::string &fontPath, int fontSize=24)
Set the active text-render font.
Definition mxvk.cpp:3872
void setPostProcessingEnabled(bool enabled)
Definition mxvk.hpp:348
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:3919
void trimMemory()
Return transient Vulkan command-pool allocations to the driver when supported.
Definition mxvk.cpp:1280
void setPostProcessingShaderParams(float p1=0.0f, float p2=0.0f, float p3=0.0f, float p4=0.0f)
Set the post-processing shader params passed as a vec4 push constant.
Definition mxvk.cpp:1591
virtual void render()
Render one frame.
Definition mxvk.cpp:824
bool headless() const noexcept
Return true when rendering without SDL video, a surface, or presentation.
Definition mxvk.hpp:195
void setPostProcessingPresentFragmentShader(const std::string &path)
Set an optional fragment shader used only when presenting the final post-processing texture to a wind...
Definition mxvk.cpp:1555
virtual void proc()
Execute one processing/update step.
Definition mxvk.cpp:1278
bool validationEnabled() const
Check whether Vulkan validation layers are currently enabled.
Definition mxvk.cpp:3812
void setPostProcessingShaderTimeEnabled(bool enabled)
Keep shader param 1 updated with elapsed render time in seconds.
Definition mxvk.cpp:1612
void setEnableScreenshot(bool enabled) noexcept
Enable or disable F10 screenshot capture.
Definition mxvk.cpp:644
void setPreviewFont(const std::string &fontPath, int fontSize=24)
Set the preview-only HUD font independently of output text.
Definition mxvk.cpp:3902
std::array< float, 16 > matrix_to_rows(const glm::mat4 &matrix)
glm::mat4 matrix_from_rows(const std::array< float, 16 > &values)
std::array< float, 4 > vector4_array(const glm::vec4 &value)
glm::vec4 vector4(const std::array< float, 4 > &values)
void setDefaultShaderDirectory(const std::string &directory)
Owned Vulkan buffer allocation with optional persistent host mapping.
VkDeviceSize size
Requested buffer size in bytes.
Parsed subset of Wavefront MTL material fields.
std::string map_kd
std::string name
Sprite-compatible fragment parameters for UV-based model effects.
Extended shader-viewer uniforms available to fragment shaders at binding 1.
Vertex layout consumed by VK_PointSpriteBatch.
float size
Rasterized point size in pixels, passed to shader location 1.
StorageImageFormat storageImageFormat
uint32_t indexCount
uint32_t textureIndex
std::string materialName
uint32_t firstIndex
Owned sampled 2D texture resources.
uint32_t width
Texture width in pixels.
uint32_t height
Texture height in pixels.
Default transform UBO payload for model shaders.
Vertex payload consumed by MXVK model shaders.
float time
Elapsed animation time in seconds.
float phase
User-defined phase value for animated stencil/content shaders.
float aspect
Render target width divided by height.
float scale
User-defined scale value for stencil/content shaders.
std::array< float, 4 > params
Definition mxvk.hpp:310
Minimal Vulkan handles required by MXVK resource helpers.

◆ build_cos_table()

std::array< float, 361 > mxvk::build_cos_table ( )
inline

Definition at line 83 of file mxvk_math.h.

83 {
84 std::array<float, 361> values{};
85 for (int ang = 0; ang <= 360; ++ang) {
86 values[static_cast<std::size_t>(ang)] = std::cos(static_cast<float>(ang) * PI / 180.0f);
87 }
88 return values;
89 }
constexpr float PI
Mathematical constant pi as a single-precision value.
Definition mxvk_math.h:34

◆ build_sin_table()

std::array< float, 361 > mxvk::build_sin_table ( )
inline

Definition at line 75 of file mxvk_math.h.

75 {
76 std::array<float, 361> values{};
77 for (int ang = 0; ang <= 360; ++ang) {
78 values[static_cast<std::size_t>(ang)] = std::sin(static_cast<float>(ang) * PI / 180.0f);
79 }
80 return values;
81 }

◆ BuildTables()

void mxvk::BuildTables ( )
inline

Rebuild the sine and cosine lookup tables.

Definition at line 98 of file mxvk_math.h.

98 {
99 std::cout << "mxvk_math: building trigonometric lookup tables\n";
100 for (int ang = 0; ang <= 360; ++ang) {
101 const float theta = static_cast<float>(ang) * PI / 180.0f;
102 cos_look[static_cast<std::size_t>(ang)] = std::cos(theta);
103 sin_look[static_cast<std::size_t>(ang)] = std::sin(theta);
104 }
105 std::cout << "mxvk_math: trigonometric lookup tables ready (361 entries)\n";
106 }
std::array< float, 361 > cos_look
Cosine lookup table with one entry per degree from 0 through 360.
Definition mxvk_math.h:95
std::array< float, 361 > sin_look
Sine lookup table with one entry per degree from 0 through 360.
Definition mxvk_math.h:92

◆ color_a()

std::uint8_t mxvk::color_a ( MXCOLOR color)
inlinenodiscardconstexpr

Extract the alpha component from a packed ARGB color.

Definition at line 61 of file mxvk_math.h.

61{ return static_cast<std::uint8_t>((color >> 24u) & 0xFFu); }

◆ color_b()

std::uint8_t mxvk::color_b ( MXCOLOR color)
inlinenodiscardconstexpr

Extract the blue component from a packed ARGB color.

Definition at line 58 of file mxvk_math.h.

58{ return static_cast<std::uint8_t>(color & 0xFFu); }

◆ color_g()

std::uint8_t mxvk::color_g ( MXCOLOR color)
inlinenodiscardconstexpr

Extract the green component from a packed ARGB color.

Definition at line 55 of file mxvk_math.h.

55{ return static_cast<std::uint8_t>((color >> 8u) & 0xFFu); }

◆ color_r()

std::uint8_t mxvk::color_r ( MXCOLOR color)
inlinenodiscardconstexpr

Extract the red component from a packed ARGB color.

Definition at line 52 of file mxvk_math.h.

52{ return static_cast<std::uint8_t>((color >> 16u) & 0xFFu); }

◆ copy_buffer()

void mxvk::copy_buffer ( const VulkanContext & context,
VkBuffer source,
VkBuffer destination,
VkDeviceSize size )

Copy one buffer into another with a one-shot Vulkan 1.3 copy command.

Parameters
contextValid upload context.
sourceSource buffer.
destinationDestination buffer.
sizeBytes to copy.
Exceptions
mxvk::Exceptionif command allocation, submission, or completion fails.

Definition at line 264 of file mxvk_resource.cpp.

264 {
265 if (size == 0) {
266 return;
267 }
268
269 VkCommandBuffer command_buffer = begin_one_time_commands(context);
270
271 VkBufferCopy2 copy_region{};
272 copy_region.sType = VK_STRUCTURE_TYPE_BUFFER_COPY_2;
273 copy_region.size = size;
274
275 VkCopyBufferInfo2 copy_info{};
276 copy_info.sType = VK_STRUCTURE_TYPE_COPY_BUFFER_INFO_2;
277 copy_info.srcBuffer = source;
278 copy_info.dstBuffer = destination;
279 copy_info.regionCount = 1;
280 copy_info.pRegions = &copy_region;
281 vkCmdCopyBuffer2(command_buffer, &copy_info);
282
283 end_one_time_commands(context, command_buffer);
284 }
VkCommandBuffer begin_one_time_commands(const VulkanContext &context)
Allocate and begin a primary one-time command buffer.
void end_one_time_commands(const VulkanContext &context, VkCommandBuffer command_buffer)
End, submit with vkQueueSubmit2, wait idle, and free a one-time command buffer.

◆ copy_buffer_to_image()

void mxvk::copy_buffer_to_image ( VkCommandBuffer command_buffer,
VkBuffer buffer,
VkImage image,
uint32_t width,
uint32_t height )

Record a Vulkan 1.3 vkCmdCopyBufferToImage2 copy for a full 2D image.

Parameters
command_bufferCommand buffer in recording state.
bufferSource buffer containing tightly packed RGBA pixels.
imageDestination image in VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL.
widthCopy width in pixels.
heightCopy height in pixels.

Definition at line 322 of file mxvk_resource.cpp.

322 {
323 VkBufferImageCopy2 region{};
324 region.sType = VK_STRUCTURE_TYPE_BUFFER_IMAGE_COPY_2;
325 region.bufferOffset = 0;
326 region.bufferRowLength = 0;
327 region.bufferImageHeight = 0;
328 region.imageSubresource.aspectMask = VK_IMAGE_ASPECT_COLOR_BIT;
329 region.imageSubresource.mipLevel = 0;
330 region.imageSubresource.baseArrayLayer = 0;
331 region.imageSubresource.layerCount = 1;
332 region.imageOffset = {0, 0, 0};
333 region.imageExtent = {width, height, 1};
334
335 VkCopyBufferToImageInfo2 copy_info{};
336 copy_info.sType = VK_STRUCTURE_TYPE_COPY_BUFFER_TO_IMAGE_INFO_2;
337 copy_info.srcBuffer = buffer;
338 copy_info.dstImage = image;
339 copy_info.dstImageLayout = VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL;
340 copy_info.regionCount = 1;
341 copy_info.pRegions = &region;
342 vkCmdCopyBufferToImage2(command_buffer, &copy_info);
343 }

◆ create_buffer()

void mxvk::create_buffer ( const VulkanContext & context,
VkDeviceSize size,
VkBufferUsageFlags usage,
VkMemoryPropertyFlags properties,
BufferResource & buffer )

Create and bind a Vulkan buffer allocation.

Parameters
contextValid Vulkan device and physical device handles.
sizeBuffer size in bytes.
usageVkBufferUsageFlagBits describing intended buffer use.
propertiesRequired memory properties.
bufferOutput buffer resource; any existing contents are destroyed first.
Exceptions
mxvk::Exceptionon invalid input or Vulkan allocation failure.

Definition at line 64 of file mxvk_resource.cpp.

64 {
65 validate_context(context);
66 if (size == 0) {
67 throw mxvk::Exception("create_buffer requires a non-zero size");
68 }
69 destroy_buffer(context.device, buffer);
70
71 VkBufferCreateInfo buffer_info{};
72 buffer_info.sType = VK_STRUCTURE_TYPE_BUFFER_CREATE_INFO;
73 buffer_info.size = size;
74 buffer_info.usage = usage;
75 buffer_info.sharingMode = VK_SHARING_MODE_EXCLUSIVE;
76 if (vkCreateBuffer(context.device, &buffer_info, nullptr, &buffer.buffer) != VK_SUCCESS) {
77 throw mxvk::Exception("failed to create Vulkan buffer");
78 }
79
80 VkMemoryRequirements requirements{};
81 vkGetBufferMemoryRequirements(context.device, buffer.buffer, &requirements);
82
83 VkMemoryAllocateInfo allocation{};
84 allocation.sType = VK_STRUCTURE_TYPE_MEMORY_ALLOCATE_INFO;
85 allocation.allocationSize = requirements.size;
86 try {
87 allocation.memoryTypeIndex = find_memory_type(context.physical_device, requirements.memoryTypeBits, properties);
88 if (vkAllocateMemory(context.device, &allocation, nullptr, &buffer.memory) != VK_SUCCESS) {
89 throw mxvk::Exception("failed to allocate Vulkan buffer memory");
90 }
91 if (vkBindBufferMemory(context.device, buffer.buffer, buffer.memory, 0) != VK_SUCCESS) {
92 throw mxvk::Exception("failed to bind Vulkan buffer memory");
93 }
94 } catch (...) {
95 destroy_buffer(context.device, buffer);
96 throw;
97 }
98 buffer.size = size;
99 }
uint32_t find_memory_type(VkPhysicalDevice physical_device, uint32_t type_filter, VkMemoryPropertyFlags properties)
Find a memory type satisfying a Vulkan memory type mask and property set.
void destroy_buffer(VkDevice device, BufferResource &buffer)
Unmap and destroy a BufferResource.
VkDeviceMemory memory
Device memory bound to buffer.
VkBuffer buffer
Vulkan buffer handle.
VkPhysicalDevice physical_device
Physical device used for memory type queries.

◆ create_image()

void mxvk::create_image ( const VulkanContext & context,
uint32_t width,
uint32_t height,
VkFormat format,
VkImageTiling tiling,
VkImageUsageFlags usage,
VkMemoryPropertyFlags properties,
VkImage & image,
VkDeviceMemory & memory )

Create a 2D VkImage and allocate/bind its memory.

Parameters
contextValid Vulkan device and physical device handles.
widthImage width in pixels.
heightImage height in pixels.
formatImage format.
tilingImage tiling mode.
usageVkImageUsageFlagBits describing intended image use.
propertiesRequired memory properties.
imageOutput image handle.
memoryOutput memory handle bound to image.
Exceptions
mxvk::Exceptionon invalid dimensions or Vulkan allocation failure.

Definition at line 135 of file mxvk_resource.cpp.

135 {
136 validate_context(context);
137 if (width == 0 || height == 0) {
138 throw mxvk::Exception("create_image requires non-zero dimensions");
139 }
140 if (image != VK_NULL_HANDLE) {
141 vkDestroyImage(context.device, image, nullptr);
142 image = VK_NULL_HANDLE;
143 }
144 if (memory != VK_NULL_HANDLE) {
145 vkFreeMemory(context.device, memory, nullptr);
146 memory = VK_NULL_HANDLE;
147 }
148
149 VkImageCreateInfo image_info{};
150 image_info.sType = VK_STRUCTURE_TYPE_IMAGE_CREATE_INFO;
151 image_info.imageType = VK_IMAGE_TYPE_2D;
152 image_info.extent = {width, height, 1};
153 image_info.mipLevels = 1;
154 image_info.arrayLayers = 1;
155 image_info.format = format;
156 image_info.tiling = tiling;
157 image_info.initialLayout = VK_IMAGE_LAYOUT_UNDEFINED;
158 image_info.usage = usage;
159 image_info.samples = VK_SAMPLE_COUNT_1_BIT;
160 image_info.sharingMode = VK_SHARING_MODE_EXCLUSIVE;
161 if (vkCreateImage(context.device, &image_info, nullptr, &image) != VK_SUCCESS) {
162 throw mxvk::Exception("failed to create Vulkan image");
163 }
164
165 VkMemoryRequirements requirements{};
166 vkGetImageMemoryRequirements(context.device, image, &requirements);
167
168 VkMemoryAllocateInfo allocation{};
169 allocation.sType = VK_STRUCTURE_TYPE_MEMORY_ALLOCATE_INFO;
170 allocation.allocationSize = requirements.size;
171 try {
172 allocation.memoryTypeIndex = find_memory_type(context.physical_device, requirements.memoryTypeBits, properties);
173 if (vkAllocateMemory(context.device, &allocation, nullptr, &memory) != VK_SUCCESS) {
174 throw mxvk::Exception("failed to allocate Vulkan image memory");
175 }
176 if (vkBindImageMemory(context.device, image, memory, 0) != VK_SUCCESS) {
177 throw mxvk::Exception("failed to bind Vulkan image memory");
178 }
179 } catch (...) {
180 if (memory != VK_NULL_HANDLE) {
181 vkFreeMemory(context.device, memory, nullptr);
182 memory = VK_NULL_HANDLE;
183 }
184 if (image != VK_NULL_HANDLE) {
185 vkDestroyImage(context.device, image, nullptr);
186 image = VK_NULL_HANDLE;
187 }
188 throw;
189 }
190 }

◆ create_image_view()

VkImageView mxvk::create_image_view ( VkDevice device,
VkImage image,
VkFormat format,
VkImageAspectFlags aspect )
nodiscard

Create a 2D image view for an image.

Parameters
deviceLogical device.
imageImage to view.
formatView format.
aspectImage aspect mask, usually VK_IMAGE_ASPECT_COLOR_BIT.
Returns
Created image view handle.
Exceptions
mxvk::Exceptionif view creation fails.

Definition at line 192 of file mxvk_resource.cpp.

192 {
193 VkImageViewCreateInfo view_info{};
194 view_info.sType = VK_STRUCTURE_TYPE_IMAGE_VIEW_CREATE_INFO;
195 view_info.image = image;
196 view_info.viewType = VK_IMAGE_VIEW_TYPE_2D;
197 view_info.format = format;
198 view_info.subresourceRange.aspectMask = aspect;
199 view_info.subresourceRange.baseMipLevel = 0;
200 view_info.subresourceRange.levelCount = 1;
201 view_info.subresourceRange.baseArrayLayer = 0;
202 view_info.subresourceRange.layerCount = 1;
203
204 VkImageView image_view = VK_NULL_HANDLE;
205 if (vkCreateImageView(device, &view_info, nullptr, &image_view) != VK_SUCCESS) {
206 throw mxvk::Exception("failed to create Vulkan image view");
207 }
208 return image_view;
209 }

◆ create_shader_module()

VkShaderModule mxvk::create_shader_module ( VkDevice device,
const std::vector< char > & spv_bytes )
nodiscard

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 142 of file mxvk_shader_module.cpp.

142 {
143 if (device == VK_NULL_HANDLE) {
144 throw mxvk::Exception("Cannot create shader module with a null device");
145 }
146 if (spv_bytes.empty() || (spv_bytes.size() % 4U) != 0U) {
147 throw mxvk::Exception("Invalid SPIR-V shader data");
148 }
149
150 VkShaderModuleCreateInfo create_info{};
151 create_info.sType = VK_STRUCTURE_TYPE_SHADER_MODULE_CREATE_INFO;
152 create_info.codeSize = spv_bytes.size();
153 create_info.pCode = reinterpret_cast<const uint32_t *>(spv_bytes.data());
154
155 VkShaderModule module = VK_NULL_HANDLE;
156 if (vkCreateShaderModule(device, &create_info, nullptr, &module) != VK_SUCCESS) {
157 throw mxvk::Exception("Failed to create shader module");
158 }
159
160 return module;
161 }

◆ create_texture_from_png()

void mxvk::create_texture_from_png ( const VulkanContext & context,
const std::string & path,
TextureResource & texture,
VkFormat format = VK_FORMAT_R8G8B8A8_UNORM )

Load a PNG and upload it into a sampled 2D texture.

Parameters
contextValid upload context.
pathPNG path.
textureOutput texture resource; any existing contents are destroyed first.
formatDestination image format. Defaults to VK_FORMAT_R8G8B8A8_UNORM.
Exceptions
mxvk::Exceptionif loading or upload fails.

Definition at line 393 of file mxvk_resource.cpp.

393 {
394 std::unique_ptr<SDL_Surface, decltype(&SDL_DestroySurface)> surface(mxvk::LoadPNG(path.c_str()), SDL_DestroySurface);
395 if (surface == nullptr) {
396 throw mxvk::Exception(std::format("failed to load texture PNG: {}", path));
397 }
398 create_texture_from_surface(context, surface.get(), texture, format);
399 }
void create_texture_from_surface(const VulkanContext &context, SDL_Surface *surface, TextureResource &texture, VkFormat format=VK_FORMAT_R8G8B8A8_UNORM)
Upload an SDL surface into a sampled 2D texture.
SDL_Surface * LoadPNG(const char *file)
Load a PNG file into an SDL_Surface.
Definition mxvk_png.cpp:89

◆ create_texture_from_surface()

void mxvk::create_texture_from_surface ( const VulkanContext & context,
SDL_Surface * surface,
TextureResource & texture,
VkFormat format = VK_FORMAT_R8G8B8A8_UNORM )

Upload an SDL surface into a sampled 2D texture.

Parameters
contextValid upload context.
surfaceSource surface. Pixels are copied immediately.
textureOutput texture resource; any existing contents are destroyed first.
formatDestination image format. Defaults to VK_FORMAT_R8G8B8A8_UNORM.

The source surface is copied row-by-row into a tight staging buffer to tolerate SDL pitch padding. The surface is expected to contain 4 bytes per pixel, as produced by MXVK's PNG loader.

Exceptions
mxvk::Exceptionon invalid input, allocation failure, or upload failure.

Definition at line 345 of file mxvk_resource.cpp.

345 {
346 validate_upload_context(context);
347 if (surface == nullptr || surface->pixels == nullptr || surface->w <= 0 || surface->h <= 0) {
348 throw mxvk::Exception("create_texture_from_surface requires a valid SDL surface");
349 }
350 destroy_texture(context.device, texture);
351
352 const uint32_t width = static_cast<uint32_t>(surface->w);
353 const uint32_t height = static_cast<uint32_t>(surface->h);
354 const VkDeviceSize image_size = static_cast<VkDeviceSize>(width) * static_cast<VkDeviceSize>(height) * 4U;
355 std::vector<std::byte> tight_pixels(static_cast<size_t>(image_size));
356
357 // SDL surfaces may have padded rows; Vulkan buffer-image copies use tight rows here.
358 const auto *src = static_cast<const std::byte *>(surface->pixels);
359 auto *dst = tight_pixels.data();
360 const size_t tight_row_bytes = static_cast<size_t>(width) * 4U;
361 for (uint32_t y = 0; y < height; ++y) {
362 std::memcpy(dst + y * tight_row_bytes, src + static_cast<size_t>(y) * static_cast<size_t>(surface->pitch), tight_row_bytes);
363 }
364
365 BufferResource staging{};
366 try {
367 create_buffer(context, image_size, VK_BUFFER_USAGE_TRANSFER_SRC_BIT, VK_MEMORY_PROPERTY_HOST_VISIBLE_BIT | VK_MEMORY_PROPERTY_HOST_COHERENT_BIT, staging);
368 map_buffer(context.device, staging);
369 std::memcpy(staging.mapped, tight_pixels.data(), tight_pixels.size());
370 unmap_buffer(context.device, staging);
371
372 create_image(context, width, height, format, VK_IMAGE_TILING_OPTIMAL, VK_IMAGE_USAGE_TRANSFER_DST_BIT | VK_IMAGE_USAGE_SAMPLED_BIT, VK_MEMORY_PROPERTY_DEVICE_LOCAL_BIT, texture.image, texture.memory);
373
374 const VkCommandBuffer cmd = begin_one_time_commands(context);
375 transition_image_layout(cmd, texture.image, VK_IMAGE_LAYOUT_UNDEFINED, VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL);
376 copy_buffer_to_image(cmd, staging.buffer, texture.image, width, height);
377 transition_image_layout(cmd, texture.image, VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL, VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL);
378 end_one_time_commands(context, cmd);
379
380 destroy_buffer(context.device, staging);
381
382 texture.view = create_image_view(context.device, texture.image, format, VK_IMAGE_ASPECT_COLOR_BIT);
383 create_sampler(context.device, texture);
384 texture.width = width;
385 texture.height = height;
386 } catch (...) {
387 destroy_buffer(context.device, staging);
388 destroy_texture(context.device, texture);
389 throw;
390 }
391 }
void create_sampler(VkDevice device, TextureResource &texture)
void transition_image_layout(VkCommandBuffer command_buffer, VkImage image, VkImageLayout old_layout, VkImageLayout new_layout, VkImageAspectFlags aspect=VK_IMAGE_ASPECT_COLOR_BIT)
Record a Vulkan 1.3 synchronization2 image layout transition.
void create_image(const VulkanContext &context, uint32_t width, uint32_t height, VkFormat format, VkImageTiling tiling, VkImageUsageFlags usage, VkMemoryPropertyFlags properties, VkImage &image, VkDeviceMemory &memory)
Create a 2D VkImage and allocate/bind its memory.
VkImageView create_image_view(VkDevice device, VkImage image, VkFormat format, VkImageAspectFlags aspect)
Create a 2D image view for an image.
void unmap_buffer(VkDevice device, BufferResource &buffer)
Unmap a BufferResource if it is currently mapped.
void map_buffer(VkDevice device, BufferResource &buffer)
Persistently map a host-visible buffer allocation.
void create_buffer(const VulkanContext &context, VkDeviceSize size, VkBufferUsageFlags usage, VkMemoryPropertyFlags properties, BufferResource &buffer)
Create and bind a Vulkan buffer allocation.
void copy_buffer_to_image(VkCommandBuffer command_buffer, VkBuffer buffer, VkImage image, uint32_t width, uint32_t height)
Record a Vulkan 1.3 vkCmdCopyBufferToImage2 copy for a full 2D image.
void destroy_texture(VkDevice device, TextureResource &texture)
Destroy every Vulkan handle owned by a TextureResource.
VkImage image
Optimal-tiled image containing the texture pixels.
VkImageView view
2D color image view used by descriptors.
VkDeviceMemory memory
Device-local memory bound to image.

◆ defaultEnableScreenshot()

bool mxvk::defaultEnableScreenshot ( )
inlinenodiscard

Definition at line 14 of file mxvk_runtime_options.hpp.

bool & defaultEnableScreenshotStorage()

◆ defaultEnableScreenshotStorage()

bool & mxvk::defaultEnableScreenshotStorage ( )
inlinenodiscard

Definition at line 7 of file mxvk_runtime_options.hpp.

7 {
8 static bool enabled = false;
9 return enabled;
10 }

◆ defaultExecutableName()

const std::string & mxvk::defaultExecutableName ( )
inlinenodiscard

Definition at line 23 of file mxvk_runtime_options.hpp.

std::string & defaultExecutableNameStorage()

◆ defaultExecutableNameStorage()

std::string & mxvk::defaultExecutableNameStorage ( )
inlinenodiscard

Definition at line 16 of file mxvk_runtime_options.hpp.

16 {
17 static std::string name = "mxvk";
18 return name;
19 }

◆ defaultShaderDirectory()

const std::string & mxvk::defaultShaderDirectory ( )
inlinenodiscard

Definition at line 32 of file mxvk_runtime_options.hpp.

std::string & defaultShaderDirectoryStorage()

◆ defaultShaderDirectoryStorage()

std::string & mxvk::defaultShaderDirectoryStorage ( )
inlinenodiscard

Definition at line 25 of file mxvk_runtime_options.hpp.

25 {
26 static std::string directory{};
27 return directory;
28 }

◆ deg2rad()

float mxvk::deg2rad ( float ang)
inlinenodiscard

Convert degrees to radians.

Definition at line 109 of file mxvk_math.h.

109{ return ang * PI / 180.0f; }

◆ destroy_buffer()

void mxvk::destroy_buffer ( VkDevice device,
BufferResource & buffer )

Unmap and destroy a BufferResource.

Parameters
deviceLogical device that owns the resource.
bufferResource to release and reset.

Definition at line 101 of file mxvk_resource.cpp.

101 {
102 if (device == VK_NULL_HANDLE) {
103 buffer = {};
104 return;
105 }
106 unmap_buffer(device, buffer);
107 if (buffer.buffer != VK_NULL_HANDLE) {
108 vkDestroyBuffer(device, buffer.buffer, nullptr);
109 }
110 if (buffer.memory != VK_NULL_HANDLE) {
111 vkFreeMemory(device, buffer.memory, nullptr);
112 }
113 buffer = {};
114 }

◆ destroy_texture()

void mxvk::destroy_texture ( VkDevice device,
TextureResource & texture )

Destroy every Vulkan handle owned by a TextureResource.

Parameters
deviceLogical device that owns the texture.
textureTexture resource to release and reset.

Definition at line 401 of file mxvk_resource.cpp.

401 {
402 if (device == VK_NULL_HANDLE) {
403 texture = {};
404 return;
405 }
406 if (texture.sampler != VK_NULL_HANDLE) {
407 vkDestroySampler(device, texture.sampler, nullptr);
408 }
409 if (texture.view != VK_NULL_HANDLE) {
410 vkDestroyImageView(device, texture.view, nullptr);
411 }
412 if (texture.image != VK_NULL_HANDLE) {
413 vkDestroyImage(device, texture.image, nullptr);
414 }
415 if (texture.memory != VK_NULL_HANDLE) {
416 vkFreeMemory(device, texture.memory, nullptr);
417 }
418 texture = {};
419 }
VkSampler sampler
Sampler configured for linear filtering and clamp-to-edge addressing.

◆ draw_filled_triangle() [1/3]

template<typename PlotPixel>
void mxvk::draw_filled_triangle ( const vec2D & p0,
const vec2D & p1,
const vec2D & p2,
MXCOLOR color,
PlotPixel && plot_pixel )

Rasterize a filled triangle by testing pixels against edge functions.

Template Parameters
PlotPixelCallable accepting (x, y, MXCOLOR).
Parameters
p0First triangle vertex in screen coordinates.
p1Second triangle vertex in screen coordinates.
p2Third triangle vertex in screen coordinates.
colorPacked ARGB color.
plot_pixelPixel plotting callable.

Definition at line 1983 of file mxvk_math.h.

1983 {
1984 const float area = edge_function(p0, p1, p2);
1985 if (std::fabs(area) <= EPSILON) {
1986 return;
1987 }
1988
1989 const int min_x = static_cast<int>(std::floor(std::min({p0.x, p1.x, p2.x})));
1990 const int max_x = static_cast<int>(std::ceil(std::max({p0.x, p1.x, p2.x})));
1991 const int min_y = static_cast<int>(std::floor(std::min({p0.y, p1.y, p2.y})));
1992 const int max_y = static_cast<int>(std::ceil(std::max({p0.y, p1.y, p2.y})));
1993
1994 for (int y = min_y; y <= max_y; ++y) {
1995 for (int x = min_x; x <= max_x; ++x) {
1996 const vec2D p(static_cast<float>(x) + 0.5f, static_cast<float>(y) + 0.5f);
1997 const float w0 = edge_function(p1, p2, p);
1998 const float w1 = edge_function(p2, p0, p);
1999 const float w2 = edge_function(p0, p1, p);
2000 if ((area > 0.0f && w0 >= 0.0f && w1 >= 0.0f && w2 >= 0.0f) || (area < 0.0f && w0 <= 0.0f && w1 <= 0.0f && w2 <= 0.0f)) {
2001 plot_pixel(x, y, color);
2002 }
2003 }
2004 }
2005 }
Two-dimensional float vector with common arithmetic helpers.
Definition mxvk_math.h:158
float x
X coordinate.
Definition mxvk_math.h:161
float y
Y coordinate.
Definition mxvk_math.h:164
float edge_function(const vec2D &a, const vec2D &b, const vec2D &p)
Compute the signed edge function value for point p relative to edge a-b.
Definition mxvk_math.h:1972
constexpr float EPSILON
Default tolerance used for floating-point singularity and zero-length checks.
Definition mxvk_math.h:37

◆ draw_filled_triangle() [2/3]

void mxvk::draw_filled_triangle ( SDL_Renderer * renderer,
const vec2D & p0,
const vec2D & p1,
const vec2D & p2,
MXCOLOR color )
inline

Draw a filled triangle directly to an SDL renderer.

Definition at line 2008 of file mxvk_math.h.

2008{ draw_filled_triangle(p0, p1, p2, color, SDLRendererPixelPlotter{renderer}); }
void draw_filled_triangle(const vec2D &p0, const vec2D &p1, const vec2D &p2, MXCOLOR color, PlotPixel &&plot_pixel)
Rasterize a filled triangle by testing pixels against edge functions.
Definition mxvk_math.h:1983
Pixel plotter adapter that writes packed MXVK colors to an SDL renderer.
Definition mxvk_math.h:1901

◆ draw_filled_triangle() [3/3]

void mxvk::draw_filled_triangle ( VK_Sprite & sprite,
const vec2D & p0,
const vec2D & p1,
const vec2D & p2,
MXCOLOR color,
int pixel_size = 1 )
inline

Draw a filled triangle directly to a VK_Sprite with optional square pixel size.

Definition at line 2011 of file mxvk_math.h.

2011{ draw_filled_triangle(p0, p1, p2, color, VKSpritePixelPlotter{&sprite, pixel_size}); }
Pixel plotter adapter that draws square pixels into a VK_Sprite.
Definition mxvk_math.h:1916

◆ draw_filled_triangle_spans()

template<typename DrawSpan>
void mxvk::draw_filled_triangle_spans ( vec2D p0,
vec2D p1,
vec2D p2,
MXCOLOR color,
DrawSpan && draw_span )

Definition at line 2101 of file mxvk_math.h.

2101{ draw_filled_triangle_spans_clipped(p0, p1, p2, std::numeric_limits<int>::min(), std::numeric_limits<int>::max(), color, std::forward<DrawSpan>(draw_span)); }
void draw_filled_triangle_spans_clipped(vec2D p0, vec2D p1, vec2D p2, int clip_min_y, int clip_max_y, MXCOLOR color, DrawSpan &&draw_span)
Rasterize a filled triangle as horizontal spans.
Definition mxvk_math.h:2024

◆ draw_filled_triangle_spans_clipped()

template<typename DrawSpan>
void mxvk::draw_filled_triangle_spans_clipped ( vec2D p0,
vec2D p1,
vec2D p2,
int clip_min_y,
int clip_max_y,
MXCOLOR color,
DrawSpan && draw_span )

Rasterize a filled triangle as horizontal spans.

Template Parameters
DrawSpanCallable accepting (x0, x1, y, MXCOLOR).
Parameters
p0First triangle vertex in screen coordinates.
p1Second triangle vertex in screen coordinates.
p2Third triangle vertex in screen coordinates.
clip_min_yMinimum inclusive scanline to rasterize.
clip_max_yMaximum inclusive scanline to rasterize.
colorPacked ARGB color.
draw_spanSpan drawing callable.

Definition at line 2024 of file mxvk_math.h.

2024 {
2025 if (std::fabs(edge_function(p0, p1, p2)) <= EPSILON) {
2026 return;
2027 }
2028
2029 struct SpanEdge {
2030 int y0 = 0;
2031 int y1 = -1;
2032 float x0 = 0.0f;
2033 float slope = 0.0f;
2034
2035 [[nodiscard]] float x_at(int y) const { return x0 + static_cast<float>(y - y0) * slope; }
2036 };
2037
2038 const int min_y = std::max(clip_min_y, static_cast<int>(std::floor(std::min({p0.y, p1.y, p2.y}))));
2039 const int max_y = std::min(clip_max_y, static_cast<int>(std::ceil(std::max({p0.y, p1.y, p2.y}))));
2040 if (min_y > max_y) {
2041 return;
2042 }
2043
2044 std::array<SpanEdge, 3> edges{};
2045 int edge_count = 0;
2046 const auto add_edge = [&](vec2D a, vec2D b) {
2047 if (std::fabs(a.y - b.y) <= EPSILON) {
2048 return;
2049 }
2050 if (a.y > b.y) {
2051 std::swap(a, b);
2052 }
2053
2054 const int y0 = std::max(min_y, static_cast<int>(std::ceil(a.y - 0.5f)));
2055 const int y1 = std::min(max_y, static_cast<int>(std::ceil(b.y - 0.5f)) - 1);
2056 if (y0 > y1) {
2057 return;
2058 }
2059
2060 SpanEdge &edge = edges[static_cast<std::size_t>(edge_count++)];
2061 edge.y0 = y0;
2062 edge.y1 = y1;
2063 edge.slope = (b.x - a.x) / (b.y - a.y);
2064 edge.x0 = a.x + ((static_cast<float>(y0) + 0.5f) - a.y) * edge.slope;
2065 };
2066
2067 add_edge(p0, p1);
2068 add_edge(p1, p2);
2069 add_edge(p2, p0);
2070
2071 for (int y = min_y; y <= max_y; ++y) {
2072 std::array<float, 3> intersections{};
2073 int intersection_count = 0;
2074
2075 for (int edge_index = 0; edge_index < edge_count; ++edge_index) {
2076 const SpanEdge &edge = edges[static_cast<std::size_t>(edge_index)];
2077 if (y >= edge.y0 && y <= edge.y1) {
2078 intersections[static_cast<std::size_t>(intersection_count++)] = edge.x_at(y);
2079 }
2080 }
2081
2082 if (intersection_count < 2) {
2083 continue;
2084 }
2085
2086 float min_x = intersections[0];
2087 float max_x = intersections[0];
2088 for (int i = 1; i < intersection_count; ++i) {
2089 min_x = std::min(min_x, intersections[static_cast<std::size_t>(i)]);
2090 max_x = std::max(max_x, intersections[static_cast<std::size_t>(i)]);
2091 }
2092
2093 const int x0 = static_cast<int>(std::ceil(min_x - 0.5f));
2094 const int x1 = static_cast<int>(std::floor(max_x - 0.5f));
2095 if (x0 <= x1) {
2096 draw_span(x0, x1, y, color);
2097 }
2098 }
2099 }

◆ draw_line() [1/3]

template<typename PlotPixel>
void mxvk::draw_line ( int x0,
int y0,
int x1,
int y1,
MXCOLOR color,
PlotPixel && plot_pixel )

Draw a line with Bresenham-style integer stepping.

Template Parameters
PlotPixelCallable accepting (x, y, MXCOLOR).
Parameters
x0Start X coordinate.
y0Start Y coordinate.
x1End X coordinate.
y1End Y coordinate.
colorPacked ARGB color.
plot_pixelPixel plotting callable.

Definition at line 1941 of file mxvk_math.h.

1941 {
1942 const int dx = std::abs(x1 - x0);
1943 const int sx = x0 < x1 ? 1 : -1;
1944 const int dy = -std::abs(y1 - y0);
1945 const int sy = y0 < y1 ? 1 : -1;
1946 int err = dx + dy;
1947
1948 while (1) {
1949 plot_pixel(x0, y0, color);
1950 if (x0 == x1 && y0 == y1) {
1951 break;
1952 }
1953 const int e2 = 2 * err;
1954 if (e2 >= dy) {
1955 err += dy;
1956 x0 += sx;
1957 }
1958 if (e2 <= dx) {
1959 err += dx;
1960 y0 += sy;
1961 }
1962 }
1963 }

◆ draw_line() [2/3]

void mxvk::draw_line ( SDL_Renderer * renderer,
int x0,
int y0,
int x1,
int y1,
MXCOLOR color )
inline

Draw a line directly to an SDL renderer.

Definition at line 1966 of file mxvk_math.h.

1966{ draw_line(x0, y0, x1, y1, color, SDLRendererPixelPlotter{renderer}); }
void draw_line(int x0, int y0, int x1, int y1, MXCOLOR color, PlotPixel &&plot_pixel)
Draw a line with Bresenham-style integer stepping.
Definition mxvk_math.h:1941

◆ draw_line() [3/3]

void mxvk::draw_line ( VK_Sprite & sprite,
int x0,
int y0,
int x1,
int y1,
MXCOLOR color,
int pixel_size = 1 )
inline

Draw a line directly to a VK_Sprite with optional square pixel size.

Definition at line 1969 of file mxvk_math.h.

1969{ draw_line(x0, y0, x1, y1, color, VKSpritePixelPlotter{&sprite, pixel_size}); }

◆ edge_function()

float mxvk::edge_function ( const vec2D & a,
const vec2D & b,
const vec2D & p )
inlinenodiscard

Compute the signed edge function value for point p relative to edge a-b.

Definition at line 1972 of file mxvk_math.h.

1972{ return (p.x - a.x) * (b.y - a.y) - (p.y - a.y) * (b.x - a.x); }

◆ end_one_time_commands()

void mxvk::end_one_time_commands ( const VulkanContext & context,
VkCommandBuffer command_buffer )

End, submit with vkQueueSubmit2, wait idle, and free a one-time command buffer.

Parameters
contextValid upload context with device, queue, and command pool.
command_bufferCommand buffer returned by begin_one_time_commands().
Exceptions
mxvk::Exceptionif end, submit, or queue wait fails.

Definition at line 235 of file mxvk_resource.cpp.

235 {
236 validate_upload_context(context);
237 if (vkEndCommandBuffer(command_buffer) != VK_SUCCESS) {
238 vkFreeCommandBuffers(context.device, context.command_pool, 1, &command_buffer);
239 throw mxvk::Exception("failed to end one-time command buffer");
240 }
241
242 VkCommandBufferSubmitInfo command_buffer_info{};
243 command_buffer_info.sType = VK_STRUCTURE_TYPE_COMMAND_BUFFER_SUBMIT_INFO;
244 command_buffer_info.commandBuffer = command_buffer;
245
246 VkSubmitInfo2 submit_info{};
247 submit_info.sType = VK_STRUCTURE_TYPE_SUBMIT_INFO_2;
248 submit_info.commandBufferInfoCount = 1;
249 submit_info.pCommandBufferInfos = &command_buffer_info;
250
251 if (vkQueueSubmit2(context.graphics_queue, 1, &submit_info, VK_NULL_HANDLE) != VK_SUCCESS) {
252 vkFreeCommandBuffers(context.device, context.command_pool, 1, &command_buffer);
253 throw mxvk::Exception("failed to submit one-time command buffer");
254 }
255 if (vkQueueWaitIdle(context.graphics_queue) != VK_SUCCESS) {
256 vkFreeCommandBuffers(context.device, context.command_pool, 1, &command_buffer);
257 throw mxvk::Exception("failed to wait for one-time command completion");
258 }
259
260 // Upload command buffers are transient; callers only observe completion.
261 vkFreeCommandBuffers(context.device, context.command_pool, 1, &command_buffer);
262 }
VkQueue graphics_queue
Queue used for one-shot upload command submission.

◆ fast_cosf()

float mxvk::fast_cosf ( float theta_degrees)
inlinenodiscard

Approximate cosine using the degree lookup table with linear interpolation.

Parameters
theta_degreesAngle in degrees.
Returns
Approximate cosine of the angle.

Definition at line 119 of file mxvk_math.h.

119 {
120 theta_degrees = std::fmod(theta_degrees, 360.0f);
121 if (theta_degrees < 0.0f) {
122 theta_degrees += 360.0f;
123 }
124 const int theta_int = static_cast<int>(theta_degrees);
125 const float theta_frac = theta_degrees - static_cast<float>(theta_int);
126 return cos_look[static_cast<std::size_t>(theta_int)] + theta_frac * (cos_look[static_cast<std::size_t>(theta_int + 1)] - cos_look[static_cast<std::size_t>(theta_int)]);
127 }

◆ fast_sinf()

float mxvk::fast_sinf ( float theta_degrees)
inlinenodiscard

Approximate sine using the degree lookup table with linear interpolation.

Parameters
theta_degreesAngle in degrees.
Returns
Approximate sine of the angle.

Definition at line 134 of file mxvk_math.h.

134 {
135 theta_degrees = std::fmod(theta_degrees, 360.0f);
136 if (theta_degrees < 0.0f) {
137 theta_degrees += 360.0f;
138 }
139 const int theta_int = static_cast<int>(theta_degrees);
140 const float theta_frac = theta_degrees - static_cast<float>(theta_int);
141 return sin_look[static_cast<std::size_t>(theta_int)] + theta_frac * (sin_look[static_cast<std::size_t>(theta_int + 1)] - sin_look[static_cast<std::size_t>(theta_int)]);
142 }

◆ find_memory_type()

uint32_t mxvk::find_memory_type ( VkPhysicalDevice physical_device,
uint32_t type_filter,
VkMemoryPropertyFlags properties )
nodiscard

Find a memory type satisfying a Vulkan memory type mask and property set.

Parameters
physical_devicePhysical device to query.
type_filterMemory type bitmask from VkMemoryRequirements.
propertiesRequired VkMemoryPropertyFlagBits.
Returns
Matching memory type index.
Exceptions
mxvk::Exceptionif no matching memory type is available.

Definition at line 48 of file mxvk_resource.cpp.

48 {
49 if (physical_device == VK_NULL_HANDLE) {
50 throw mxvk::Exception("find_memory_type requires a valid physical device");
51 }
52
53 VkPhysicalDeviceMemoryProperties memory_properties{};
54 vkGetPhysicalDeviceMemoryProperties(physical_device, &memory_properties);
55 for (uint32_t i = 0; i < memory_properties.memoryTypeCount; ++i) {
56 if ((type_filter & (1U << i)) && (memory_properties.memoryTypes[i].propertyFlags & properties) == properties) {
57 return i;
58 }
59 }
60
61 throw mxvk::Exception("failed to find suitable Vulkan memory type");
62 }

◆ HasPngExtension()

bool mxvk::HasPngExtension ( const char * file)
nodiscard

Definition at line 22 of file mxvk_png.cpp.

22 {
23 if (file == nullptr) {
24 return false;
25 }
26
27 std::string lower(file);
28 std::ranges::transform(lower, lower.begin(), [](const unsigned char ch) { return static_cast<char>(std::tolower(ch)); });
29 return lower.ends_with(".png");
30 }

◆ inspect_spirv()

ShaderModuleInfo mxvk::inspect_spirv ( const std::vector< char > & spv_bytes)
nodiscard

Inspect a SPIR-V entry point and compute local workgroup size.

Definition at line 47 of file mxvk_shader_module.cpp.

47 {
48 if (spv_bytes.size() < 5U * sizeof(uint32_t) || (spv_bytes.size() % sizeof(uint32_t)) != 0U) {
49 throw mxvk::Exception("Invalid SPIR-V shader data");
50 }
51
52 const auto *word_data = reinterpret_cast<const uint32_t *>(spv_bytes.data());
53 const std::span<const uint32_t> words(word_data, spv_bytes.size() / sizeof(uint32_t));
54 if (words.front() != SPIRV_MAGIC) {
55 throw mxvk::Exception("Invalid SPIR-V magic word");
56 }
57
58 ShaderModuleInfo info{};
59 uint32_t entry_point_id = 0;
60 const uint32_t id_bound = words[3];
61 if (id_bound == 0U || id_bound > words.size()) {
62 throw mxvk::Exception("Invalid SPIR-V identifier bound");
63 }
64 std::vector<uint32_t> descriptor_sets(id_bound, UINT32_MAX);
65 std::vector<uint32_t> descriptor_bindings(id_bound, UINT32_MAX);
66 for (std::size_t offset = 5; offset < words.size();) {
67 const uint16_t word_count = static_cast<uint16_t>(words[offset] >> 16U);
68 const uint16_t opcode = static_cast<uint16_t>(words[offset] & 0xFFFFU);
69 if (word_count == 0 || offset + word_count > words.size()) {
70 throw mxvk::Exception("Malformed SPIR-V instruction stream");
71 }
72 if (opcode == OP_ENTRY_POINT && word_count >= 3U && entry_point_id == 0U) {
73 entry_point_id = words[offset + 2U];
74 switch (words[offset + 1U]) {
76 info.stage = ShaderStage::Vertex;
77 break;
79 info.stage = ShaderStage::Fragment;
80 break;
82 info.stage = ShaderStage::Compute;
83 break;
84 default:
85 info.stage = ShaderStage::Unknown;
86 break;
87 }
88 } else if (opcode == OP_DECORATE && word_count >= 4U && words[offset + 1U] < id_bound) {
89 const uint32_t target_id = words[offset + 1U];
90 const uint32_t decoration = words[offset + 2U];
91 if (decoration == DECORATION_BINDING) {
92 descriptor_bindings[target_id] = words[offset + 3U];
93 } else if (decoration == DECORATION_DESCRIPTOR_SET) {
94 descriptor_sets[target_id] = words[offset + 3U];
95 }
96 } else if (opcode == OP_TYPE_IMAGE && word_count >= 9U) {
97 const uint32_t image_format = words[offset + 8U];
98 if (image_format == IMAGE_FORMAT_RGBA8) {
99 info.storageImageFormat = StorageImageFormat::Rgba8;
100 } else if (image_format == IMAGE_FORMAT_RGBA16F) {
101 info.storageImageFormat = StorageImageFormat::Rgba16Float;
102 }
103 }
104 offset += word_count;
105 }
106
107 for (uint32_t id = 0; id < id_bound; ++id) {
108 if (descriptor_sets[id] != 0U) {
109 continue;
110 }
111 switch (descriptor_bindings[id]) {
112 case 2U:
113 info.usesHistoryTexture = true;
114 break;
115 case 3U:
116 info.usesSpectrumTexture = true;
117 break;
118 case 4U:
119 info.usesSpectrumHistoryTexture = true;
120 break;
121 default:
122 break;
123 }
124 }
125
126 if (info.stage == ShaderStage::Compute && entry_point_id != 0U) {
127 for (std::size_t offset = 5; offset < words.size();) {
128 const uint16_t word_count = static_cast<uint16_t>(words[offset] >> 16U);
129 const uint16_t opcode = static_cast<uint16_t>(words[offset] & 0xFFFFU);
130 if (opcode == OP_EXECUTION_MODE && word_count >= 6U && words[offset + 1U] == entry_point_id && words[offset + 2U] == EXECUTION_MODE_LOCAL_SIZE) {
131 info.localSizeX = words[offset + 3U];
132 info.localSizeY = words[offset + 4U];
133 info.localSizeZ = words[offset + 5U];
134 break;
135 }
136 offset += word_count;
137 }
138 }
139 return info;
140 }

◆ load_spv()

std::vector< char > mxvk::load_spv ( const std::string & path)
nodiscard

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 26 of file mxvk_shader_module.cpp.

26 {
27 if (path.empty()) {
28 throw mxvk::Exception("SPIR-V path is empty");
29 }
30
31 std::ifstream file(path, std::ios::binary);
32 if (!file.is_open()) {
33 throw mxvk::Exception("Failed to open SPIR-V file: " + path);
34 }
35
36 const std::vector<char> bytes((std::istreambuf_iterator<char>(file)), std::istreambuf_iterator<char>());
37 if (bytes.empty()) {
38 throw mxvk::Exception("SPIR-V file is empty: " + path);
39 }
40 if ((bytes.size() % 4U) != 0U) {
41 throw mxvk::Exception("SPIR-V file size is not 4-byte aligned: " + path);
42 }
43
44 return bytes;
45 }

◆ LoadPNG()

SDL_Surface * mxvk::LoadPNG ( const char * file)

Load a PNG file into an SDL_Surface.

Parameters
filePath to the PNG file.
Returns
Newly allocated SDL_Surface, or nullptr on failure.

Definition at line 89 of file mxvk_png.cpp.

89 {
90 if (!HasPngExtension(file)) {
91 return nullptr;
92 }
93
94 FILE *fp = std::fopen(file, "rb");
95 if (fp == nullptr) {
96 std::cerr << "Failed to open PNG file: " << file << "\n";
97 return nullptr;
98 }
99
100 png_structp png = png_create_read_struct(PNG_LIBPNG_VER_STRING, nullptr, nullptr, nullptr);
101 if (png == nullptr) {
102 std::fclose(fp);
103 return nullptr;
104 }
105
106 png_infop info = png_create_info_struct(png);
107 if (info == nullptr) {
108 png_destroy_read_struct(&png, nullptr, nullptr);
109 std::fclose(fp);
110 return nullptr;
111 }
112
113 if (setjmp(png_jmpbuf(png))) {
114 png_destroy_read_struct(&png, &info, nullptr);
115 std::fclose(fp);
116 return nullptr;
117 }
118
119 png_init_io(png, fp);
120 png_read_info(png, info);
121
122 const int width = static_cast<int>(png_get_image_width(png, info));
123 const int height = static_cast<int>(png_get_image_height(png, info));
124 const png_byte color_type = png_get_color_type(png, info);
125 const png_byte bit_depth = png_get_bit_depth(png, info);
126
127 if (width <= 0 || height <= 0) {
128 png_destroy_read_struct(&png, &info, nullptr);
129 std::fclose(fp);
130 return nullptr;
131 }
132
133 if (bit_depth == 16) {
134 png_set_strip_16(png);
135 }
136
137 if (color_type == PNG_COLOR_TYPE_PALETTE) {
138 png_set_palette_to_rgb(png);
139 }
140
141 if (png_get_valid(png, info, PNG_INFO_tRNS) != 0) {
142 png_set_tRNS_to_alpha(png);
143 }
144
145 if (color_type == PNG_COLOR_TYPE_GRAY || color_type == PNG_COLOR_TYPE_GRAY_ALPHA) {
146 png_set_gray_to_rgb(png);
147 }
148
149 if (color_type == PNG_COLOR_TYPE_RGB || color_type == PNG_COLOR_TYPE_GRAY || color_type == PNG_COLOR_TYPE_PALETTE) {
150 png_set_filler(png, 0xFF, PNG_FILLER_AFTER);
151 }
152
153 png_read_update_info(png, info);
154
155 SDL_Surface *surface = SDL_CreateSurface(width, height, SDL_PIXELFORMAT_RGBA32);
156 if (surface == nullptr) {
157 png_destroy_read_struct(&png, &info, nullptr);
158 std::fclose(fp);
159 return nullptr;
160 }
161
162 const png_size_t row_bytes = png_get_rowbytes(png, info);
163 if (row_bytes > static_cast<png_size_t>(surface->pitch)) {
164 std::cerr << "mx: Unexpected PNG row stride mismatch for file: " << file << "\n";
165 SDL_DestroySurface(surface);
166 png_destroy_read_struct(&png, &info, nullptr);
167 std::fclose(fp);
168 return nullptr;
169 }
170
171 auto *pixels = static_cast<std::uint8_t *>(surface->pixels);
172 std::vector<png_bytep> row_pointers(static_cast<std::size_t>(height));
173 for (int y = 0; y < height; ++y) {
174 row_pointers[static_cast<std::size_t>(y)] = pixels + (y * surface->pitch);
175 }
176
177 png_read_image(png, row_pointers.data());
178 png_read_end(png, nullptr);
179
180 png_destroy_read_struct(&png, &info, nullptr);
181 std::fclose(fp);
182 return surface;
183 }
bool HasPngExtension(const char *file)
Definition mxvk_png.cpp:22

◆ map_buffer()

void mxvk::map_buffer ( VkDevice device,
BufferResource & buffer )

Persistently map a host-visible buffer allocation.

Parameters
deviceLogical device that owns the buffer memory.
bufferBuffer resource to map.
Exceptions
mxvk::Exceptionif the buffer is invalid or mapping fails.

Definition at line 116 of file mxvk_resource.cpp.

116 {
117 if (buffer.mapped != nullptr) {
118 return;
119 }
120 if (device == VK_NULL_HANDLE || buffer.memory == VK_NULL_HANDLE || buffer.size == 0) {
121 throw mxvk::Exception("map_buffer requires a valid buffer allocation");
122 }
123 if (vkMapMemory(device, buffer.memory, 0, buffer.size, 0, &buffer.mapped) != VK_SUCCESS || buffer.mapped == nullptr) {
124 throw mxvk::Exception("failed to map Vulkan buffer memory");
125 }
126 }
void * mapped
Host pointer returned by vkMapMemory, or nullptr when unmapped.

◆ MXVK_RGB()

MXCOLOR mxvk::MXVK_RGB ( int r,
int g,
int b )
inlinenodiscardconstexpr

Build an opaque ARGB color from red, green, and blue components.

Parameters
rRed component in the low 8 bits.
gGreen component in the low 8 bits.
bBlue component in the low 8 bits.
Returns
Packed color with alpha set to 255.

Definition at line 49 of file mxvk_math.h.

49{ return 0xFF000000u | ((static_cast<MXCOLOR>(r) & 0xFFu) << 16u) | ((static_cast<MXCOLOR>(g) & 0xFFu) << 8u) | (static_cast<MXCOLOR>(b) & 0xFFu); }
std::uint32_t MXCOLOR
Packed 32-bit color in ARGB byte order.
Definition mxvk_math.h:40

◆ operator<<() [1/3]

std::ostream & mxvk::operator<< ( std::ostream & out,
const vec2D & v )
inline

Write a 2D vector to a stream using vec2D::Print().

Definition at line 250 of file mxvk_math.h.

250{ return out << v.Print(); }
std::string Print(const std::string &name="v") const
Format this vector as a named angle-bracket tuple.
Definition mxvk_math.h:242

◆ operator<<() [2/3]

std::ostream & mxvk::operator<< ( std::ostream & out,
const vec3D & v )
inline

Write a 3D vector to a stream using vec3D::Print().

Definition at line 357 of file mxvk_math.h.

357{ return out << v.Print(); }
std::string Print(const std::string &name="v") const
Format this vector as a named angle-bracket tuple.
Definition mxvk_math.h:349

◆ operator<<() [3/3]

std::ostream & mxvk::operator<< ( std::ostream & out,
const vec4D & v )
inline

Write a 4D vector to a stream using vec4D::Print().

Definition at line 487 of file mxvk_math.h.

487{ return out << v.Print(); }
std::string Print(const std::string &name="v") const
Format this vector as a named angle-bracket tuple.
Definition mxvk_math.h:479

◆ operator>>() [1/3]

std::istream & mxvk::operator>> ( std::istream & in,
vec2D & v )
inline

Read a 2D vector from a stream as two scalar coordinates.

Definition at line 253 of file mxvk_math.h.

253{ return in >> v.x >> v.y; }

◆ operator>>() [2/3]

std::istream & mxvk::operator>> ( std::istream & in,
vec3D & v )
inline

Read a 3D vector from a stream as three scalar coordinates.

Definition at line 360 of file mxvk_math.h.

360{ return in >> v.x >> v.y >> v.z; }
float z
Z coordinate.
Definition mxvk_math.h:265
float x
X coordinate.
Definition mxvk_math.h:259
float y
Y coordinate.
Definition mxvk_math.h:262

◆ operator>>() [3/3]

std::istream & mxvk::operator>> ( std::istream & in,
vec4D & v )
inline

Read a 4D vector from a stream as four scalar coordinates.

Definition at line 490 of file mxvk_math.h.

490{ return in >> v.x >> v.y >> v.z >> v.w; }
float y
Y coordinate.
Definition mxvk_math.h:369
float x
X coordinate.
Definition mxvk_math.h:366
float w
Homogeneous W coordinate.
Definition mxvk_math.h:375
float z
Z coordinate.
Definition mxvk_math.h:372

◆ rad2deg()

float mxvk::rad2deg ( float rad)
inlinenodiscard

Convert radians to degrees.

Definition at line 112 of file mxvk_math.h.

112{ return rad * 180.0f / PI; }

◆ rrand()

int mxvk::rrand ( int x,
int y )
inlinenodiscard

Return a pseudo-random integer in the inclusive range between two bounds.

Parameters
xFirst bound.
ySecond bound.
Returns
Random integer in [min(x, y), max(x, y)].

Definition at line 150 of file mxvk_math.h.

150 {
151 if (x > y) {
152 std::swap(x, y);
153 }
154 return x + (std::rand() % (y - x + 1));
155 }

◆ SavePNG()

bool mxvk::SavePNG ( SDL_Texture * texture,
SDL_Renderer * renderer,
const char * filename,
int compression_level = 6 )

Save an SDL_Texture to a PNG file.

Parameters
textureSource texture.
rendererRenderer used to read back the texture pixels.
filenameDestination file path.
compression_levelPNG compression level passed to libpng; 0 favors speed and 9 favors smaller files. Defaults to 6.
Returns
true on success, false on failure.

Definition at line 185 of file mxvk_png.cpp.

185 {
186 if (texture == nullptr || renderer == nullptr || filename == nullptr) {
187 std::cerr << "mx: Invalid SavePNG parameters.\n";
188 return false;
189 }
190
191 float width_f = 0.0F;
192 float height_f = 0.0F;
193 if (!SDL_GetTextureSize(texture, &width_f, &height_f)) {
194 std::cerr << "mx: Failed to query texture size: " << SDL_GetError() << "\n";
195 return false;
196 }
197
198 if (!std::isfinite(width_f) || !std::isfinite(height_f) || width_f <= 0.0F || height_f <= 0.0F) {
199 std::cerr << "mx: Invalid texture size while saving PNG.\n";
200 return false;
201 }
202
203 const int width = static_cast<int>(width_f);
204 const int height = static_cast<int>(height_f);
205 if (width <= 0 || height <= 0) {
206 std::cerr << "mx: Invalid integer texture size while saving PNG.\n";
207 return false;
208 }
209
210 SDL_Texture *previous_target = SDL_GetRenderTarget(renderer);
211 if (!SDL_SetRenderTarget(renderer, texture)) {
212 std::cerr << "mx: Failed to set render target: " << SDL_GetError() << "\n";
213 return false;
214 }
215
216 SDL_Surface *captured = SDL_RenderReadPixels(renderer, nullptr);
217
218 if (!SDL_SetRenderTarget(renderer, previous_target)) {
219 std::cerr << "mx: Failed to restore render target: " << SDL_GetError() << "\n";
220 }
221
222 if (captured == nullptr) {
223 std::cerr << "mx: Failed to read pixels from renderer: " << SDL_GetError() << "\n";
224 return false;
225 }
226
227 std::unique_ptr<SDL_Surface, decltype(&SDL_DestroySurface)> captured_surface(captured, &SDL_DestroySurface);
228
229 SDL_Surface *rgba32_surface_raw = captured_surface.get();
230 std::unique_ptr<SDL_Surface, decltype(&SDL_DestroySurface)> converted_surface(nullptr, &SDL_DestroySurface);
231 if (captured_surface->format != SDL_PIXELFORMAT_RGBA32) {
232 rgba32_surface_raw = SDL_ConvertSurface(captured_surface.get(), SDL_PIXELFORMAT_RGBA32);
233 if (rgba32_surface_raw == nullptr) {
234 std::cerr << "mx: Failed to convert surface to RGBA32: " << SDL_GetError() << "\n";
235 return false;
236 }
237 converted_surface.reset(rgba32_surface_raw);
238 }
239
240 const auto *pixels = static_cast<const std::uint8_t *>(rgba32_surface_raw->pixels);
241 return WriteRgbaPng(filename, pixels, rgba32_surface_raw->w, rgba32_surface_raw->h, rgba32_surface_raw->pitch, 8, false, compression_level);
242 }
bool WriteRgbaPng(const char *filename, const std::uint8_t *pixels, const int width, const int height, const int pitch, const int bit_depth, const bool swap_16bit_endianness, int compression_level)
Definition mxvk_png.cpp:32

◆ SavePNG_RGBA()

bool mxvk::SavePNG_RGBA ( const char * filename,
void * buffer,
int w,
int h,
int compression_level = 6 )

Save a raw RGBA pixel buffer to a PNG file.

Parameters
filenameDestination file path.
bufferPointer to the raw RGBA pixel data.
wImage width in pixels.
hImage height in pixels.
compression_levelPNG compression level passed to libpng; 0 favors speed and 9 favors smaller files. Defaults to 6.
Returns
true on success, false on failure.

Definition at line 279 of file mxvk_png.cpp.

279 {
280 const int pitch = w * 4;
281 return WriteRgbaPng(filename, static_cast<const std::uint8_t *>(buffer), w, h, pitch, 8, false, compression_level);
282 }

◆ SavePNG_RGBA16()

bool mxvk::SavePNG_RGBA16 ( const char * filename,
const void * buffer,
int w,
int h,
int compression_level = 6 )

Save a raw 16-bit RGBA pixel buffer to a PNG file.

Parameters
filenameDestination file path.
bufferPointer to the raw 16-bit RGBA pixel data.
wImage width in pixels.
hImage height in pixels.
compression_levelPNG compression level passed to libpng; 0 favors speed and 9 favors smaller files. Defaults to 6.
Returns
true on success, false on failure.

Definition at line 284 of file mxvk_png.cpp.

284 {
285 const int pitch = w * 8;
286#if SDL_BYTEORDER == SDL_LIL_ENDIAN
287 constexpr bool swap_16bit_endianness = true;
288#else
289 constexpr bool swap_16bit_endianness = false;
290#endif
291 return WriteRgbaPng(filename, static_cast<const std::uint8_t *>(buffer), w, h, pitch, 16, swap_16bit_endianness, compression_level);
292 }

◆ SaveRawBytes()

bool mxvk::SaveRawBytes ( const char * filename,
const void * buffer,
size_t w,
size_t h,
size_t bpp )

Save raw data to a file.

Parameters
filenameOutput file path
bufferPointer to the source data (read-only)
wWidth of the image in pixels
hHeight of the image in pixels
bppBytes per pixel (e.g., 2 for YUYV, 4 for RGBA)
Returns
true on success, false on failure.

Definition at line 244 of file mxvk_png.cpp.

244 {
245 if (filename == nullptr || buffer == nullptr || bpp == 0U) {
246 std::cerr << "mx: Invalid SaveRawBytes parameters.\n";
247 return false;
248 }
249
250 if (w != 0U && h > (std::numeric_limits<size_t>::max() / w)) {
251 std::cerr << "mx: Raw byte size overflow while writing: " << filename << "\n";
252 return false;
253 }
254
255 const size_t pixels = w * h;
256 if (pixels != 0U && bpp > (std::numeric_limits<size_t>::max() / pixels)) {
257 std::cerr << "mx: Raw byte size overflow while writing: " << filename << "\n";
258 return false;
259 }
260
261 const size_t size = pixels * bpp;
262 FILE *fptr = std::fopen(filename, "wb");
263 if (fptr == nullptr) {
264 std::cerr << "mx: Failed to open output file: " << filename << "\n";
265 return false;
266 }
267
268 const size_t written = std::fwrite(buffer, sizeof(unsigned char), size, fptr);
269 if (written != size) {
270 std::cerr << "mx: Error writing frame: " << filename << "\n";
271 std::fclose(fptr);
272 return false;
273 }
274
275 std::fclose(fptr);
276 return true;
277 }

◆ setDefaultEnableScreenshot()

void mxvk::setDefaultEnableScreenshot ( bool enabled)
inline

Definition at line 12 of file mxvk_runtime_options.hpp.

12{ defaultEnableScreenshotStorage() = enabled; }

◆ setDefaultExecutableName()

void mxvk::setDefaultExecutableName ( const std::string & name)
inline

Definition at line 21 of file mxvk_runtime_options.hpp.

21{ defaultExecutableNameStorage() = name.empty() ? "mxvk" : name; }

◆ setDefaultShaderDirectory()

void mxvk::setDefaultShaderDirectory ( const std::string & directory)
inline

Definition at line 30 of file mxvk_runtime_options.hpp.

30{ defaultShaderDirectoryStorage() = directory; }

◆ shade_color()

MXCOLOR mxvk::shade_color ( MXCOLOR color,
float intensity )
inlinenodiscard

Scale the RGB channels of a color while preserving alpha.

Parameters
colorPacked ARGB color to shade.
intensityMultiplier clamped to the range [0, 1].
Returns
Shaded packed ARGB color.

Definition at line 69 of file mxvk_math.h.

69 {
70 intensity = std::clamp(intensity, 0.0f, 1.0f);
71 const auto scale = [intensity](std::uint8_t component) { return static_cast<int>(std::clamp(static_cast<float>(component) * intensity, 0.0f, 255.0f)); };
72 return (static_cast<MXCOLOR>(color_a(color)) << 24u) | ((static_cast<MXCOLOR>(scale(color_r(color))) & 0xFFu) << 16u) | ((static_cast<MXCOLOR>(scale(color_g(color))) & 0xFFu) << 8u) | (static_cast<MXCOLOR>(scale(color_b(color))) & 0xFFu);
73 }
constexpr std::uint8_t color_r(MXCOLOR color)
Extract the red component from a packed ARGB color.
Definition mxvk_math.h:52
constexpr std::uint8_t color_g(MXCOLOR color)
Extract the green component from a packed ARGB color.
Definition mxvk_math.h:55
constexpr std::uint8_t color_a(MXCOLOR color)
Extract the alpha component from a packed ARGB color.
Definition mxvk_math.h:61
constexpr std::uint8_t color_b(MXCOLOR color)
Extract the blue component from a packed ARGB color.
Definition mxvk_math.h:58

◆ transition_image_layout()

void mxvk::transition_image_layout ( VkCommandBuffer command_buffer,
VkImage image,
VkImageLayout old_layout,
VkImageLayout new_layout,
VkImageAspectFlags aspect = VK_IMAGE_ASPECT_COLOR_BIT )

Record a Vulkan 1.3 synchronization2 image layout transition.

Parameters
command_bufferCommand buffer in recording state.
imageImage to transition.
old_layoutCurrent image layout.
new_layoutDesired image layout.
aspectImage aspect mask.

Supported transitions are:

  • VK_IMAGE_LAYOUT_UNDEFINED to VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL
  • VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL to VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL
Exceptions
mxvk::Exceptionfor unsupported transitions.

Definition at line 286 of file mxvk_resource.cpp.

286 {
287 VkImageMemoryBarrier2 barrier{};
288 barrier.sType = VK_STRUCTURE_TYPE_IMAGE_MEMORY_BARRIER_2;
289 barrier.oldLayout = old_layout;
290 barrier.newLayout = new_layout;
291 barrier.srcQueueFamilyIndex = VK_QUEUE_FAMILY_IGNORED;
292 barrier.dstQueueFamilyIndex = VK_QUEUE_FAMILY_IGNORED;
293 barrier.image = image;
294 barrier.subresourceRange.aspectMask = aspect;
295 barrier.subresourceRange.baseMipLevel = 0;
296 barrier.subresourceRange.levelCount = 1;
297 barrier.subresourceRange.baseArrayLayer = 0;
298 barrier.subresourceRange.layerCount = 1;
299
300 // MXVK requires Vulkan 1.3 synchronization2, so uploads use stage/access2 masks.
301 if (old_layout == VK_IMAGE_LAYOUT_UNDEFINED && new_layout == VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL) {
302 barrier.srcStageMask = VK_PIPELINE_STAGE_2_NONE;
303 barrier.srcAccessMask = VK_ACCESS_2_NONE;
304 barrier.dstStageMask = VK_PIPELINE_STAGE_2_COPY_BIT;
305 barrier.dstAccessMask = VK_ACCESS_2_TRANSFER_WRITE_BIT;
306 } else if (old_layout == VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL && new_layout == VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL) {
307 barrier.srcStageMask = VK_PIPELINE_STAGE_2_COPY_BIT;
308 barrier.srcAccessMask = VK_ACCESS_2_TRANSFER_WRITE_BIT;
309 barrier.dstStageMask = VK_PIPELINE_STAGE_2_FRAGMENT_SHADER_BIT;
310 barrier.dstAccessMask = VK_ACCESS_2_SHADER_SAMPLED_READ_BIT;
311 } else {
312 throw mxvk::Exception("unsupported image layout transition");
313 }
314
315 VkDependencyInfo dependency{};
316 dependency.sType = VK_STRUCTURE_TYPE_DEPENDENCY_INFO;
317 dependency.imageMemoryBarrierCount = 1;
318 dependency.pImageMemoryBarriers = &barrier;
319 vkCmdPipelineBarrier2(command_buffer, &dependency);
320 }

◆ unmap_buffer()

void mxvk::unmap_buffer ( VkDevice device,
BufferResource & buffer )

Unmap a BufferResource if it is currently mapped.

Parameters
deviceLogical device that owns the buffer memory.
bufferBuffer resource to unmap.

Definition at line 128 of file mxvk_resource.cpp.

128 {
129 if (device != VK_NULL_HANDLE && buffer.memory != VK_NULL_HANDLE && buffer.mapped != nullptr) {
130 vkUnmapMemory(device, buffer.memory);
131 }
132 buffer.mapped = nullptr;
133 }

◆ WriteRgbaPng()

bool mxvk::WriteRgbaPng ( const char * filename,
const std::uint8_t * pixels,
const int width,
const int height,
const int pitch,
const int bit_depth,
const bool swap_16bit_endianness,
int compression_level )
nodiscard

Definition at line 32 of file mxvk_png.cpp.

32 {
33 if (filename == nullptr || pixels == nullptr || width <= 0 || height <= 0 || pitch <= 0) {
34 std::cerr << "mx: Invalid PNG write parameters.\n";
35 return false;
36 }
37
38 FILE *file = std::fopen(filename, "wb");
39 if (file == nullptr) {
40 std::cerr << "mx: Failed to open file for writing: " << filename << "\n";
41 return false;
42 }
43
44 png_structp png = png_create_write_struct(PNG_LIBPNG_VER_STRING, nullptr, nullptr, nullptr);
45 if (png == nullptr) {
46 std::cerr << "mx: Failed to create PNG write struct.\n";
47 std::fclose(file);
48 return false;
49 }
50
51 png_infop info = png_create_info_struct(png);
52 if (info == nullptr) {
53 std::cerr << "mx: Failed to create PNG info struct.\n";
54 png_destroy_write_struct(&png, nullptr);
55 std::fclose(file);
56 return false;
57 }
58
59 if (setjmp(png_jmpbuf(png))) {
60 std::cerr << "mx: Error during PNG creation.\n";
61 png_destroy_write_struct(&png, &info);
62 std::fclose(file);
63 return false;
64 }
65
66 png_init_io(png, file);
67 png_set_compression_level(png, compression_level);
68 png_set_IHDR(png, info, width, height, bit_depth, PNG_COLOR_TYPE_RGBA, PNG_INTERLACE_NONE, PNG_COMPRESSION_TYPE_DEFAULT, PNG_FILTER_TYPE_DEFAULT);
69
70 if (bit_depth == 16 && swap_16bit_endianness) {
71 png_set_swap(png);
72 }
73
74 png_write_info(png, info);
75
76 std::vector<png_bytep> rows(static_cast<std::size_t>(height));
77 for (int y = 0; y < height; ++y) {
78 rows[static_cast<std::size_t>(y)] = const_cast<png_bytep>(pixels + (y * pitch));
79 }
80
81 png_write_image(png, rows.data());
82 png_write_end(png, nullptr);
83
84 png_destroy_write_struct(&png, &info);
85 std::fclose(file);
86 return true;
87 }

Variable Documentation

◆ cos_look

std::array< float, 361 > mxvk::cos_look = build_cos_table()
inline

Cosine lookup table with one entry per degree from 0 through 360.

Definition at line 95 of file mxvk_math.h.

◆ EPSILON

float mxvk::EPSILON = 1.0e-5f
inlineconstexpr

Default tolerance used for floating-point singularity and zero-length checks.

Definition at line 37 of file mxvk_math.h.

◆ PI

float mxvk::PI = 3.14159265358979323846f
inlineconstexpr

Mathematical constant pi as a single-precision value.

Definition at line 34 of file mxvk_math.h.

◆ sin_look

std::array< float, 361 > mxvk::sin_look = build_sin_table()
inline

Sine lookup table with one entry per degree from 0 through 360.

Definition at line 92 of file mxvk_math.h.