MXVK Vulkan Framework 0.33.1
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,
90 const std::string &modelPath,
91 const std::string &textureManifestPath,
92 const std::string &textureBasePath,
93 float scale = 1.0f);
94
95 /**
96 * @brief Consume pre-parsed mesh data and build Vulkan state.
97 * @param window Active MXVK window.
98 * @param model Pre-parsed CPU-side model data.
99 * @param textureManifestPath Optional texture manifest path (.tex or .mtl-like text).
100 * @param textureBasePath Optional base path for texture files in the manifest.
101 * @param scale Uniform mesh scale. Kept for API compatibility.
102 */
103 void load(VK_Window *window,
104 MXModel &&model,
105 const std::string &textureManifestPath,
106 const std::string &textureBasePath,
107 [[maybe_unused]] float scale = 1.0f);
108
109 /**
110 * @brief Configure custom shader paths and rebuild pipelines.
111 * @param window Active MXVK window.
112 * @param vertSpv Vertex shader SPIR-V path.
113 * @param fragSpv Fragment shader SPIR-V path.
114 */
115 void setShaders(VK_Window *window, const std::string &vertSpv, const std::string &fragSpv);
116
117 /**
118 * @brief Update one per-frame UBO payload.
119 * @param imageIndex Swapchain image index.
120 * @param ubo New transform values.
121 */
122 void updateUBO(uint32_t imageIndex, const UniformBufferObject &ubo);
123
124 /** @brief Use binding 1 for fragment uniforms and binding 2 for model transforms. Call before load(). */
126
127 /** @brief Update extended fragment uniforms for one swapchain image. */
128 void updateFragmentUBO(uint32_t imageIndex, const ModelFragmentUniforms &uniforms);
129
130 /** @brief Set sprite-compatible push constants used by custom fragment shaders. */
132
133 /**
134 * @brief Upload raw RGBA pixels into the primary model texture.
135 * @param pixels Pointer to RGBA8 pixel data.
136 * @param width Texture width in pixels.
137 * @param height Texture height in pixels.
138 * @param pitch Bytes per input row. When 0, defaults to width * 4.
139 * @return True when the upload succeeds, false for invalid inputs or unavailable resources.
140 */
141 [[nodiscard]] bool updatePrimaryTexture(const void *pixels, int width, int height, int pitch = 0);
142
143#ifdef MXVK_CUDA
144 /**
145 * @brief Upload RGBA8 pixels from CUDA device memory into the primary model texture.
146 *
147 * The Vulkan texture is imported into CUDA as a mipmapped array because
148 * sampled Vulkan images use opaque optimal tiling, not a linear pitched layout.
149 */
150 [[nodiscard]] bool updatePrimaryTextureCuda(const cv::cuda::GpuMat &rgba, cv::cuda::Stream &stream);
151#endif
152
153 /**
154 * @brief Record draw commands for this model.
155 * @param cmd Active command buffer, inside a dynamic rendering scope.
156 * @param imageIndex Current swapchain image index.
157 * @param wireframe Render using the optional wireframe pipeline when available.
158 */
159 void render(VkCommandBuffer cmd, uint32_t imageIndex, bool wireframe = false) const;
160
161 /**
162 * @brief Record one draw using push constants for per-draw transforms and an explicit texture slot.
163 * @param cmd Active command buffer, inside a dynamic rendering scope.
164 * @param imageIndex Current swapchain image index.
165 * @param textureIndex Texture slot to bind for all submeshes in this draw.
166 * @param ubo Transform/effect payload copied into vertex-stage push constants.
167 * @param wireframe Render using the optional wireframe pipeline when available.
168 */
169 void renderWithPushConstants(VkCommandBuffer cmd,
170 uint32_t imageIndex,
171 size_t textureIndex,
172 const UniformBufferObject &ubo,
173 bool wireframe = false);
174
175 /** Render using a non-owning shader-readable image view as texture slot zero. */
176 void renderWithExternalTexture(VkCommandBuffer cmd,
177 uint32_t imageIndex,
178 VkImageView textureView,
179 const UniformBufferObject &ubo,
180 bool wireframe = false);
181
182 /**
183 * @brief Rebuild swapchain-dependent resources after resize.
184 * @param window Active MXVK window.
185 */
186 void resize(VK_Window *window);
187
188 /**
189 * @brief Destroy all owned Vulkan resources.
190 * @param window Active MXVK window.
191 */
192 void cleanup(VK_Window *window);
193
194 /** @brief Access the underlying mesh object. */
195 [[nodiscard]] const MXModel &model() const { return obj; }
196 /** @brief Access the computed center offset used for normalization. */
197 [[nodiscard]] glm::vec3 modelCenterOffset() const { return modelCenterOffsetValue; }
198 /** @brief Access the computed render scale used for normalization. */
199 [[nodiscard]] float modelRenderScale() const { return modelRenderScaleValue; }
200 /** @brief Per-axis extent (max - min) of the source mesh's bounding box. */
201 [[nodiscard]] glm::vec3 modelAxisExtent() const { return modelAxisExtentValue; }
202 /** @brief True once the model has been uploaded to GPU buffers. */
203 [[nodiscard]] bool isLoaded() const { return obj.indexCount() > 0 && vertexBufferReady(); }
204
205 /**
206 * @brief Enable or disable backface culling for this model pipeline.
207 * @param enabled True to cull backfaces, false to disable culling.
208 */
209 void setBackfaceCulling(bool enabled);
210
211 /**
212 * @brief Enable or disable alpha blending for this model pipeline.
213 * @param enabled True to blend fragment alpha and avoid depth writes.
214 */
215 void setAlphaBlending(bool enabled);
216
217 private:
218 struct TextureEntry {
219 VkImage image = VK_NULL_HANDLE;
220 VkDeviceMemory memory = VK_NULL_HANDLE;
221 VkImageView view = VK_NULL_HANDLE;
222 uint32_t width = 0;
223 uint32_t height = 0;
224#ifdef MXVK_CUDA
225 VkDeviceSize cudaExportMemorySize = 0;
226 cudaExternalMemory_t cudaExternalMemory = nullptr;
227 cudaMipmappedArray_t cudaMipmappedArray = nullptr;
228 cudaArray_t cudaArray = nullptr;
229 bool cudaInteropEnabled = false;
230 bool cudaInteropUnavailableLogged = false;
231 bool cudaUploadLogged = false;
232 bool cudaWriteTransitionLogged = false;
233 bool cudaShaderTransitionLogged = false;
234 VkImageLayout cudaImageLayout = VK_IMAGE_LAYOUT_UNDEFINED;
235#endif
236 };
237
238 MXModel obj{};
239 std::vector<TextureEntry> textures{};
240
241 VkDescriptorSetLayout descriptorSetLayout = VK_NULL_HANDLE;
242 VkDescriptorPool descriptorPool = VK_NULL_HANDLE;
243 uint32_t descriptorPoolSetCapacity = 0;
244 std::vector<VkDescriptorSet> descriptorSets{};
245
246 VkPipelineLayout pipelineLayout = VK_NULL_HANDLE;
247 VkPipeline pipelineFill = VK_NULL_HANDLE;
248 VkPipeline pipelineWireframe = VK_NULL_HANDLE;
249 VkSampler textureSampler = VK_NULL_HANDLE;
250
251 std::vector<VkBuffer> uniformBuffers{};
252 std::vector<VkDeviceMemory> uniformBufferMemory{};
253 std::vector<void *> uniformBuffersMapped{};
254 std::vector<VkBuffer> fragmentUniformBuffers{};
255 std::vector<VkDeviceMemory> fragmentUniformBufferMemory{};
256 std::vector<void *> fragmentUniformBuffersMapped{};
257
258 glm::vec3 modelCenterOffsetValue{0.0f, 0.0f, 0.0f};
259 float modelRenderScaleValue = 1.0f;
260 glm::vec3 modelAxisExtentValue{1.0f, 1.0f, 1.0f};
261
262 std::string vertexShaderPath{};
263 std::string fragmentShaderPath{};
264 bool backfaceCullingEnabled = false;
265 bool alphaBlendingEnabled = false;
266 ModelFragmentPushConstants fragmentPushConstants{};
267 bool extendedFragmentUniformsEnabled = false;
268
269 VK_Window *windowPtr = nullptr;
270
271 [[nodiscard]] bool vertexBufferReady() const { return obj.vertexBuffer() != VK_NULL_HANDLE && obj.indexBuffer() != VK_NULL_HANDLE; }
272
273 void computeBoundsAndScale();
274 void loadTextures(const std::string &textureManifestPath, const std::string &textureBasePath);
275 void loadTexturesFromMTL(const std::string &textureBasePath);
276 void createFallbackTexture();
277
278 void createBuffer(VkDeviceSize size, VkBufferUsageFlags usage,
279 VkMemoryPropertyFlags properties, VkBuffer &buffer,
280 VkDeviceMemory &bufferMemory) const;
281 [[nodiscard]] uint32_t findMemoryType(uint32_t typeFilter, VkMemoryPropertyFlags properties) const;
282 [[nodiscard]] VkCommandBuffer beginSingleTimeCommands() const;
283 void endSingleTimeCommands(VkCommandBuffer commandBuffer) const;
284 void createImage(uint32_t width, uint32_t height, VkFormat format,
285 VkImageTiling tiling, VkImageUsageFlags usage,
286 VkMemoryPropertyFlags properties, VkImage &image,
287 VkDeviceMemory &memory) const;
288 void createTextureImage(uint32_t width, uint32_t height, TextureEntry &texture) const;
289#ifdef MXVK_CUDA
290 void createCudaExportableImage(uint32_t width, uint32_t height, TextureEntry &texture) const;
291 void destroyTextureCudaInterop(TextureEntry &texture) const;
292 [[nodiscard]] bool ensureTextureCudaInterop(TextureEntry &texture) const;
293 [[nodiscard]] bool transitionTextureForCudaWrite(TextureEntry &texture) const;
294 [[nodiscard]] bool transitionTextureForShaderRead(TextureEntry &texture) const;
295 [[nodiscard]] bool updatePrimaryTextureCudaHost(TextureEntry &texture, const void *pixels,
296 uint32_t width, uint32_t height, uint32_t pitch) const;
297 void recreatePrimaryTextureForCuda(TextureEntry &texture, uint32_t width, uint32_t height);
298#endif
299 [[nodiscard]] VkImageView createImageView(VkImage image, VkFormat format, VkImageAspectFlags aspectFlags) const;
300 void transitionImageLayout(VkImage image, VkFormat format, VkImageLayout oldLayout, VkImageLayout newLayout) const;
301 void copyBufferToImage(VkBuffer buffer, VkImage image, uint32_t width, uint32_t height) const;
302
303 void createTextureSampler();
304 void createDescriptorSetLayout();
305 void createUniformBuffers();
306 void destroyUniformBuffers();
307 void createDescriptorPool();
308 void createDescriptorSets();
309 void updateTextureDescriptor(VkDescriptorSet descriptorSet,
310 VkImageView imageView) const;
311 void createPipelines();
312
313 void destroyPipelines();
314 void destroyDescriptors();
315 void destroyTextures();
316 };
317
319
320} // 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.
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:38
Vulkan mesh loader and GPU buffer manager for MXVK.
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.