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_abstract_model.hpp
Go to the documentation of this file.
1/**
2 * @file mxvk_abstract_model.hpp
3 * @brief High-level model wrapper integrated with MXVK dynamic rendering.
4 */
5#pragma once
6
7#include <volk/volk.h>
8
9#include "mxvk.hpp"
10#include "mxvk_model.hpp"
11
12#include <glm/glm.hpp>
13
14#include <array>
15#include <string>
16#include <vector>
17#ifdef MXVK_CUDA
18#include <cuda_runtime_api.h>
19#include <opencv2/core/cuda.hpp>
20#endif
21
22namespace mxvk {
23
24 /**
25 * @struct UniformBufferObject
26 * @brief Default transform UBO payload for model shaders.
27 */
29 glm::mat4 model{1.0f};
30 glm::mat4 view{1.0f};
31 glm::mat4 proj{1.0f};
32 glm::vec4 fx{0.0f, 0.0f, 0.0f, 0.0f};
33 };
34
35 /**
36 * @struct ModelFragmentPushConstants
37 * @brief Sprite-compatible fragment parameters for UV-based model effects.
38 */
40 float screenWidth = 1.0f;
41 float screenHeight = 1.0f;
42 float spritePosX = 0.0f;
43 float spritePosY = 0.0f;
44 float spriteSizeW = 1.0f;
45 float spriteSizeH = 1.0f;
46 float effectsOn = 1.0f;
47 float padding = 0.0f;
48 glm::vec4 params{0.0f};
49 };
50
51 /** @brief Extended shader-viewer uniforms available to fragment shaders at binding 1. */
53 glm::vec4 mouse{0.0f};
54 glm::vec4 u0{0.0f};
55 glm::vec4 u1{0.0f};
56 glm::vec4 u2{0.0f};
57 glm::vec4 u3{0.0f};
58 std::array<glm::vec4, 16> custom_uniforms{};
59 glm::vec4 audio_bands{0.0f};
60 glm::vec4 audio_history{0.0f};
61 };
62
63 /**
64 * @class VKAbstractModel
65 * @brief Convenience wrapper that owns mesh, textures, descriptors, and pipeline state.
66 *
67 * This class is intended to be recorded from inside
68 * `VK_Window::onRecordCustomRendering()` so it participates in the same
69 * dynamic-rendering pass as sprites/text.
70 */
72 public:
73 VKAbstractModel() = default;
74 ~VKAbstractModel() = default;
75
80
81 /**
82 * @brief Load mesh/texture resources and build Vulkan state.
83 * @param window Active MXVK window.
84 * @param modelPath Path to .obj or .mxmod mesh file.
85 * @param textureManifestPath Optional texture manifest path (.tex or .mtl-like text).
86 * @param textureBasePath Optional base path for texture files in the manifest.
87 * @param scale Uniform mesh scale.
88 */
89 void load(VK_Window *window, const std::string &modelPath, const std::string &textureManifestPath, const std::string &textureBasePath, float scale = 1.0f);
90
91 /**
92 * @brief Consume pre-parsed mesh data and build Vulkan state.
93 * @param window Active MXVK window.
94 * @param model Pre-parsed CPU-side model data.
95 * @param textureManifestPath Optional texture manifest path (.tex or .mtl-like text).
96 * @param textureBasePath Optional base path for texture files in the manifest.
97 * @param scale Uniform mesh scale. Kept for API compatibility.
98 */
99 void load(VK_Window *window, MXModel &&model, const std::string &textureManifestPath, const std::string &textureBasePath, [[maybe_unused]] float scale = 1.0f);
100
101 /**
102 * @brief Configure custom shader paths and rebuild pipelines.
103 * @param window Active MXVK window.
104 * @param vertSpv Vertex shader SPIR-V path.
105 * @param fragSpv Fragment shader SPIR-V path.
106 */
107 void setShaders(VK_Window *window, const std::string &vertSpv, const std::string &fragSpv);
108
109 /**
110 * @brief Update one per-frame UBO payload.
111 * @param imageIndex Swapchain image index.
112 * @param ubo New transform values.
113 */
114 void updateUBO(uint32_t imageIndex, const UniformBufferObject &ubo);
115
116 /** @brief Use binding 1 for fragment uniforms and binding 2 for model transforms. Call before load(). */
118
119 /** @brief Update extended fragment uniforms for one swapchain image. */
120 void updateFragmentUBO(uint32_t imageIndex, const ModelFragmentUniforms &uniforms);
121
122 /** @brief Set sprite-compatible push constants used by custom fragment shaders. */
124
125 /**
126 * @brief Upload raw RGBA pixels into the primary model texture.
127 * @param pixels Pointer to RGBA8 pixel data.
128 * @param width Texture width in pixels.
129 * @param height Texture height in pixels.
130 * @param pitch Bytes per input row. When 0, defaults to width * 4.
131 * @return True when the upload succeeds, false for invalid inputs or unavailable resources.
132 */
133 [[nodiscard]] bool updatePrimaryTexture(const void *pixels, int width, int height, int pitch = 0);
134
135#ifdef MXVK_CUDA
136 /**
137 * @brief Upload RGBA8 pixels from CUDA device memory into the primary model texture.
138 *
139 * The Vulkan texture is imported into CUDA as a mipmapped array because
140 * sampled Vulkan images use opaque optimal tiling, not a linear pitched layout.
141 */
142 [[nodiscard]] bool updatePrimaryTextureCuda(const cv::cuda::GpuMat &rgba, cv::cuda::Stream &stream);
143#endif
144
145 /**
146 * @brief Record draw commands for this model.
147 * @param cmd Active command buffer, inside a dynamic rendering scope.
148 * @param imageIndex Current swapchain image index.
149 * @param wireframe Render using the optional wireframe pipeline when available.
150 */
151 void render(VkCommandBuffer cmd, uint32_t imageIndex, bool wireframe = false) const;
152
153 /**
154 * @brief Record one draw using push constants for per-draw transforms and an explicit texture slot.
155 * @param cmd Active command buffer, inside a dynamic rendering scope.
156 * @param imageIndex Current swapchain image index.
157 * @param textureIndex Texture slot to bind for all submeshes in this draw.
158 * @param ubo Transform/effect payload copied into vertex-stage push constants.
159 * @param wireframe Render using the optional wireframe pipeline when available.
160 */
161 void renderWithPushConstants(VkCommandBuffer cmd, uint32_t imageIndex, size_t textureIndex, const UniformBufferObject &ubo, bool wireframe = false);
162
163 /** Render using a non-owning shader-readable image view as texture slot zero. */
164 void renderWithExternalTexture(VkCommandBuffer cmd, uint32_t imageIndex, VkImageView textureView, const UniformBufferObject &ubo, bool wireframe = false);
165
166 /**
167 * @brief Rebuild swapchain-dependent resources after resize.
168 * @param window Active MXVK window.
169 */
170 void resize(VK_Window *window);
171
172 /**
173 * @brief Destroy all owned Vulkan resources.
174 * @param window Active MXVK window.
175 */
176 void cleanup(VK_Window *window);
177
178 /** @brief Access the underlying mesh object. */
179 [[nodiscard]] const MXModel &model() const { return obj; }
180 /** @brief Access the computed center offset used for normalization. */
181 [[nodiscard]] glm::vec3 modelCenterOffset() const { return modelCenterOffsetValue; }
182 /** @brief Access the computed render scale used for normalization. */
183 [[nodiscard]] float modelRenderScale() const { return modelRenderScaleValue; }
184 /** @brief Per-axis extent (max - min) of the source mesh's bounding box. */
185 [[nodiscard]] glm::vec3 modelAxisExtent() const { return modelAxisExtentValue; }
186 /** @brief True once the model has been uploaded to GPU buffers. */
187 [[nodiscard]] bool isLoaded() const { return obj.indexCount() > 0 && vertexBufferReady(); }
188
189 /**
190 * @brief Enable or disable backface culling for this model pipeline.
191 * @param enabled True to cull backfaces, false to disable culling.
192 */
193 void setBackfaceCulling(bool enabled);
194
195 /**
196 * @brief Enable or disable alpha blending for this model pipeline.
197 * @param enabled True to blend fragment alpha and avoid depth writes.
198 */
199 void setAlphaBlending(bool enabled);
200
201 /** @brief Override the dynamic-rendering color format for this model. */
202 void setColorAttachmentFormat(VkFormat format);
203
204 private:
205 struct TextureEntry {
206 VkImage image = VK_NULL_HANDLE;
207 VkDeviceMemory memory = VK_NULL_HANDLE;
208 VkImageView view = VK_NULL_HANDLE;
209 uint32_t width = 0;
210 uint32_t height = 0;
211#ifdef MXVK_CUDA
212 VkDeviceSize cudaExportMemorySize = 0;
213 cudaExternalMemory_t cudaExternalMemory = nullptr;
214 cudaMipmappedArray_t cudaMipmappedArray = nullptr;
215 cudaArray_t cudaArray = nullptr;
216 bool cudaInteropEnabled = false;
217 bool cudaInteropUnavailableLogged = false;
218 bool cudaUploadLogged = false;
219 bool cudaWriteTransitionLogged = false;
220 bool cudaShaderTransitionLogged = false;
221 VkImageLayout cudaImageLayout = VK_IMAGE_LAYOUT_UNDEFINED;
222#endif
223 };
224
225 MXModel obj{};
226 std::vector<TextureEntry> textures{};
227
228 VkDescriptorSetLayout descriptorSetLayout = VK_NULL_HANDLE;
229 VkDescriptorPool descriptorPool = VK_NULL_HANDLE;
230 uint32_t descriptorPoolSetCapacity = 0;
231 std::vector<VkDescriptorSet> descriptorSets{};
232
233 VkPipelineLayout pipelineLayout = VK_NULL_HANDLE;
234 VkPipeline pipelineFill = VK_NULL_HANDLE;
235 VkPipeline pipelineWireframe = VK_NULL_HANDLE;
236 VkSampler textureSampler = VK_NULL_HANDLE;
237
238 std::vector<VkBuffer> uniformBuffers{};
239 std::vector<VkDeviceMemory> uniformBufferMemory{};
240 std::vector<void *> uniformBuffersMapped{};
241 std::vector<VkBuffer> fragmentUniformBuffers{};
242 std::vector<VkDeviceMemory> fragmentUniformBufferMemory{};
243 std::vector<void *> fragmentUniformBuffersMapped{};
244
245 glm::vec3 modelCenterOffsetValue{0.0f, 0.0f, 0.0f};
246 float modelRenderScaleValue = 1.0f;
247 glm::vec3 modelAxisExtentValue{1.0f, 1.0f, 1.0f};
248
249 std::string vertexShaderPath{};
250 std::string fragmentShaderPath{};
251 bool backfaceCullingEnabled = false;
252 bool alphaBlendingEnabled = false;
253 ModelFragmentPushConstants fragmentPushConstants{};
254 bool extendedFragmentUniformsEnabled = false;
255 VkFormat colorAttachmentFormat = VK_FORMAT_UNDEFINED;
256
257 VK_Window *windowPtr = nullptr;
258
259 [[nodiscard]] bool vertexBufferReady() const { return obj.vertexBuffer() != VK_NULL_HANDLE && obj.indexBuffer() != VK_NULL_HANDLE; }
260
261 void computeBoundsAndScale();
262 void loadTextures(const std::string &textureManifestPath, const std::string &textureBasePath);
263 void loadTexturesFromMTL(const std::string &textureBasePath);
264 void createFallbackTexture();
265
266 void createBuffer(VkDeviceSize size, VkBufferUsageFlags usage, VkMemoryPropertyFlags properties, VkBuffer &buffer, VkDeviceMemory &bufferMemory) const;
267 [[nodiscard]] uint32_t findMemoryType(uint32_t typeFilter, VkMemoryPropertyFlags properties) const;
268 [[nodiscard]] VkCommandBuffer beginSingleTimeCommands() const;
269 void endSingleTimeCommands(VkCommandBuffer commandBuffer) const;
270 void createImage(uint32_t width, uint32_t height, VkFormat format, VkImageTiling tiling, VkImageUsageFlags usage, VkMemoryPropertyFlags properties, VkImage &image, VkDeviceMemory &memory) const;
271 void createTextureImage(uint32_t width, uint32_t height, TextureEntry &texture) const;
272#ifdef MXVK_CUDA
273 void createCudaExportableImage(uint32_t width, uint32_t height, TextureEntry &texture) const;
274 void destroyTextureCudaInterop(TextureEntry &texture) const;
275 [[nodiscard]] bool ensureTextureCudaInterop(TextureEntry &texture) const;
276 [[nodiscard]] bool transitionTextureForCudaWrite(TextureEntry &texture) const;
277 [[nodiscard]] bool transitionTextureForShaderRead(TextureEntry &texture) const;
278 [[nodiscard]] bool updatePrimaryTextureCudaHost(TextureEntry &texture, const void *pixels, uint32_t width, uint32_t height, uint32_t pitch) const;
279 void recreatePrimaryTextureForCuda(TextureEntry &texture, uint32_t width, uint32_t height);
280#endif
281 [[nodiscard]] VkImageView createImageView(VkImage image, VkFormat format, VkImageAspectFlags aspectFlags) const;
282 void transitionImageLayout(VkImage image, VkFormat format, VkImageLayout oldLayout, VkImageLayout newLayout) const;
283 void copyBufferToImage(VkBuffer buffer, VkImage image, uint32_t width, uint32_t height) const;
284
285 void createTextureSampler();
286 void createDescriptorSetLayout();
287 void createUniformBuffers();
288 void destroyUniformBuffers();
289 void createDescriptorPool();
290 void createDescriptorSets();
291 void updateTextureDescriptor(VkDescriptorSet descriptorSet, VkImageView imageView) const;
292 void createPipelines();
293
294 void destroyPipelines();
295 void destroyDescriptors();
296 void destroyTextures();
297 };
298
300
301} // namespace mxvk
Loads OBJ/MXMOD meshes and uploads them to Vulkan buffers.
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.
~VKAbstractModel()=default
VKAbstractModel & operator=(const VKAbstractModel &)=delete
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.
VKAbstractModel(VKAbstractModel &&)=delete
void cleanup(VK_Window *window)
Destroy all owned Vulkan resources.
VKAbstractModel & operator=(VKAbstractModel &&)=delete
glm::vec3 modelCenterOffset() const
Access the computed center offset used for normalization.
void setColorAttachmentFormat(VkFormat format)
Override the dynamic-rendering color format for this model.
glm::vec3 modelAxisExtent() const
Per-axis extent (max - min) of the source mesh's bounding box.
const MXModel & model() const
Access the underlying mesh object.
void resize(VK_Window *window)
Rebuild swapchain-dependent resources after resize.
void renderWithPushConstants(VkCommandBuffer cmd, uint32_t imageIndex, size_t textureIndex, const UniformBufferObject &ubo, bool wireframe=false)
Record one draw using push constants for per-draw transforms and an explicit texture slot.
void render(VkCommandBuffer cmd, uint32_t imageIndex, bool wireframe=false) const
Record draw commands for this model.
void renderWithExternalTexture(VkCommandBuffer cmd, uint32_t imageIndex, VkImageView textureView, const UniformBufferObject &ubo, bool wireframe=false)
Render using a non-owning shader-readable image view as texture slot zero.
void enableExtendedFragmentUniforms()
Use binding 1 for fragment uniforms and binding 2 for model transforms.
bool updatePrimaryTexture(const void *pixels, int width, int height, int pitch=0)
Upload raw RGBA pixels into the primary model texture.
VKAbstractModel(const VKAbstractModel &)=delete
void setFragmentPushConstants(const ModelFragmentPushConstants &constants)
Set sprite-compatible push constants used by custom fragment shaders.
Main Vulkan window wrapper for MXVK.
Definition mxvk.hpp:39
Vulkan mesh loader and GPU buffer manager for MXVK.
Definition model.py:1
Utilities for loading and saving PNG images.
Definition mxvk.hpp:31
VKAbstractModel VK_AbstractModel
Sprite-compatible fragment parameters for UV-based model effects.
Extended shader-viewer uniforms available to fragment shaders at binding 1.
std::array< glm::vec4, 16 > custom_uniforms
Default transform UBO payload for model shaders.