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_sprite.hpp
Go to the documentation of this file.
1/**
2 * @file mxvk_sprite.hpp
3 * @brief Vulkan 2-D sprite renderer with optional custom shaders and instancing.
4 *
5 * VKSprite manages a Vulkan texture, a screen-space quad, and an optional
6 * custom graphics pipeline. It supports:
7 * - Loading images from PNG files or SDL_Surface objects.
8 * - Drawing at arbitrary positions, scales, and rotations.
9 * - GPU instancing for large batches of identical sprites.
10 * - Extended UBO with mouse state and four custom vec4 uniforms.
11 */
12#pragma once
13
14#include <volk/volk.h>
15
16#include <SDL3/SDL.h>
17
18#include "mxvk_exception.hpp"
19#include <array>
20#include <cstdlib>
21#include <cstring>
22#include <format>
23#include <fstream>
24#include <glm/glm.hpp>
25#include <iostream>
26#include <stdexcept>
27#include <string>
28#include <unordered_map>
29#include <vector>
30#ifdef MXVK_CUDA
31#include <cuda_runtime_api.h>
32#include <opencv2/core/cuda.hpp>
33#endif
34
35#ifndef VK_CHECK_RESULT
36#define VK_CHECK_RESULT(f) \
37 { \
38 VkResult res = (f); \
39 if (res != VK_SUCCESS) { \
40 throw mxvk::Exception(std::format("Fatal : VkResult is \"{}\" in {} at line {}", static_cast<int>(res), __FILE__, __LINE__)); \
41 } \
42 }
43#endif
44
45namespace mxvk {
46
47 /**
48 * @class VKSprite
49 * @brief Vulkan 2-D sprite with texture, custom shader, and instancing support.
50 *
51 * Allocates and manages all Vulkan resources required to render one sprite
52 * type: image, sampler, descriptor set, vertex/index buffers, and an
53 * optional custom pipeline. Multiple draw commands are batched into a
54 * queue and submitted together in renderSprites().
55 */
56 class VK_Sprite {
57 public:
58 static constexpr std::size_t MAX_CUSTOM_UNIFORMS = 64;
59
60 /**
61 * @brief Construct and record Vulkan context handles.
62 * @param device Logical device.
63 * @param physicalDevice Physical device.
64 * @param graphicsQueue Graphics queue.
65 * @param commandPool Command pool for staging operations.
66 */
67 VK_Sprite(VkDevice device, VkPhysicalDevice physicalDevice, VkQueue graphicsQueue,
68 VkCommandPool commandPool);
69
70 /** @brief Destructor — frees all Vulkan resources. */
71 ~VK_Sprite();
72
73 VK_Sprite(const VK_Sprite &) = delete;
74 VK_Sprite &operator=(const VK_Sprite &) = delete;
75 VK_Sprite(VK_Sprite &&) = delete;
77
78 /**
79 * @brief Load sprite texture from a PNG file.
80 * @param pngPath Path to the PNG file.
81 * @param fragmentShaderPath Optional custom fragment shader (SPIR-V .spv).
82 */
83 void loadSprite(const std::string &pngPath, const std::string &fragmentShaderPath = "");
84
85 /**
86 * @brief Load sprite texture from an SDL_Surface.
87 * @param surface Source surface (not consumed).
88 * @param fragmentShaderPath Optional custom fragment shader.
89 */
90 void loadSprite(SDL_Surface *surface, const std::string &fragmentShaderPath = "");
91
92 /**
93 * @brief Create a blank (un-initialised) sprite texture.
94 * @param width Pixel width.
95 * @param height Pixel height.
96 * @param vertexShaderPath Optional vertex shader.
97 * @param fragmentShaderPath Optional fragment shader.
98 */
99 void createEmptySprite(int width, int height, const std::string &vertexShaderPath = "", const std::string &fragmentShaderPath = "");
100
101 /**
102 * @brief Queue a draw at the given pixel position.
103 * @param x Destination X.
104 * @param y Destination Y.
105 */
106 void drawSprite(int x, int y);
107
108 /**
109 * @brief Queue a scaled draw.
110 * @param x Destination X.
111 * @param y Destination Y.
112 * @param scaleX Horizontal scale factor.
113 * @param scaleY Vertical scale factor.
114 */
115 void drawSprite(int x, int y, float scaleX, float scaleY);
116
117 /**
118 * @brief Queue a scaled and rotated draw.
119 * @param x Destination X.
120 * @param y Destination Y.
121 * @param scaleX Horizontal scale.
122 * @param scaleY Vertical scale.
123 * @param rotation Clockwise rotation in degrees.
124 */
125 void drawSprite(int x, int y, float scaleX, float scaleY, float rotation);
126
127 /**
128 * @brief Queue a draw into an explicit destination rectangle.
129 * @param x,y,w,h Destination rectangle.
130 */
131 void drawSpriteRect(int x, int y, int w, int h);
132
133 /**
134 * @brief Replace the sprite texture from an SDL_Surface.
135 * @param surface New surface (not consumed).
136 */
137 void updateTexture(SDL_Surface *surface);
138
139 /**
140 * @brief Replace the sprite texture from a raw pixel buffer.
141 * @param pixels Pointer to RGBA data.
142 * @param width Buffer width.
143 * @param height Buffer height.
144 * @param pitch Row stride in bytes (0 = auto).
145 */
146 void updateTexture(const void *pixels, int width, int height, int pitch = 0);
147
148 void setExternalTexture(VkImageView image_view, int width, int height);
150
151#ifdef MXVK_CUDA
152 /** @brief Replace the sprite texture directly from CUDA device memory. */
153 bool updateTextureCuda(const cv::cuda::GpuMat &rgba, cv::cuda::Stream &stream);
154#endif
155
156 /**
157 * @brief Set up to four custom shader float parameters.
158 * @param p1,p2,p3,p4 Parameter values packed into a vec4.
159 */
160 void setShaderParams(float p1 = 0.0f, float p2 = 0.0f, float p3 = 0.0f, float p4 = 0.0f);
161
162 /**
163 * @brief Enable or disable the custom fragment shader effects.
164 * @param enabled @c true to enable effects.
165 */
166 void setEffectsEnabled(bool enabled) { effectsEnabled = enabled; }
167
168 /** @return @c true if shader effects are enabled. */
169 bool getEffectsEnabled() const { return effectsEnabled; }
170
171 /**
172 * @brief Record all queued draw commands into the given command buffer.
173 * @param cmdBuffer Active command buffer.
174 * @param pipelineLayout Pipeline layout for push constants/descriptors.
175 * @param screenWidth Current viewport width.
176 * @param screenHeight Current viewport height.
177 */
178 void renderSprites(VkCommandBuffer cmdBuffer, VkPipelineLayout pipelineLayout,
179 uint32_t screenWidth, uint32_t screenHeight);
180
181 /**
182 * @brief Record texture barriers that must happen before dynamic rendering begins.
183 * @param cmdBuffer Command buffer currently being recorded outside a rendering instance.
184 */
185 void prepareForRendering(VkCommandBuffer cmdBuffer);
186
187 /** @brief Discard all pending draw commands without rendering. */
188 void clearQueue();
189
190 /** @return Sprite texture width in pixels. */
191 int getWidth() const { return spriteWidth; }
192 /** @return Sprite texture height in pixels. */
193 int getHeight() const { return spriteHeight; }
194
195 /**
196 * @brief Select the hardware filter used when scaling this sprite.
197 * @param filter VK_FILTER_NEAREST for sharp pixels or VK_FILTER_LINEAR for smoothing.
198 */
199 void setTextureFilter(VkFilter filter);
200
201 /** @brief Assign an external descriptor-set layout. */
202 void setDescriptorSetLayout(VkDescriptorSetLayout layout) { descriptorSetLayout = layout; }
203 /** @brief Assign the render pass used to build the custom pipeline. */
204 void setRenderPass(VkRenderPass rp) { renderPass = rp; }
205 /** @brief Assign dynamic-rendering color attachment format used to build pipelines. */
206 void setColorAttachmentFormat(VkFormat format) { colorAttachmentFormat = format; }
207 /** @brief Assign dynamic-rendering depth attachment format used to build pipelines. */
208 void setDepthAttachmentFormat(VkFormat format) { depthAttachmentFormat = format; }
209 /**
210 * @brief Rebind the command pool used for upload/staging operations.
211 *
212 * Any in-flight upload resources tied to the previous pool are released first.
213 */
214 void setCommandPool(VkCommandPool pool);
215 /** @brief Use the shared pipeline cache for custom/instanced pipeline creation. */
216 void setPipelineCache(VkPipelineCache cache) { pipelineCache = cache; }
217 /**
218 * @brief Release upload/staging resources tied to the current command pool.
219 *
220 * Call this before destroying or recreating the command pool.
221 */
223 /** @brief Override the vertex shader path (used when rebuilding the pipeline). */
224 void setVertexShaderPath(const std::string &path) { vertexShaderPath = path; }
225 /** @brief Replace the fragment shader path and rebuild the custom pipeline. */
226 void setFragmentShaderPath(const std::string &path);
227
228 /**
229 * @brief Build a compute image-effect pipeline for this sprite.
230 *
231 * Compute shaders use the extended sprite descriptor ABI: binding 0 is
232 * the sampled input image, binding 1 is SpriteExtended, optional
233 * history/audio textures remain at bindings 2-4, and binding 5 is a
234 * write-only RGBA8 storage image.
235 */
236 void enableComputeShader(const std::string &path, uint32_t localSizeX,
237 uint32_t localSizeY, uint32_t localSizeZ = 1);
238
239 /** @return Whether this sprite owns an executable compute pipeline. */
240 [[nodiscard]] bool hasComputePipeline() const {
241 return computePipeline != VK_NULL_HANDLE;
242 }
243
244 /** @brief Dispatch the compute effect between two full-frame images. */
245 void dispatchCompute(VkCommandBuffer cmdBuffer, VkImageView inputView,
246 VkImageView outputView, uint32_t width,
247 uint32_t height);
248
249 /** @return @c true if a custom pipeline has been built. */
250 bool hasOwnPipeline() const { return customPipeline != VK_NULL_HANDLE; }
251 /** @return The custom VkPipeline handle (may be VK_NULL_HANDLE). */
252 VkPipeline getPipeline() const { return customPipeline; }
253 /** @return The custom pipeline layout handle. */
254 VkPipelineLayout getPipelineLayout() const { return customPipelineLayout; }
255
256 /** @brief Destroy and recreate the custom graphics pipeline. */
257 void rebuildPipeline();
258 /** @brief Destroy and recreate the instanced graphics pipeline. */
260
261 VkSampler spriteSampler = VK_NULL_HANDLE; ///< Texture sampler.
262
263 /**
264 * @brief Enable GPU instancing for this sprite type.
265 * @param maxInstances Maximum simultaneous instances.
266 * @param instanceVertShaderPath Vertex shader supporting instancing.
267 * @param instanceFragShaderPath Fragment shader.
268 */
269 void enableInstancing(uint32_t maxInstances,
270 const std::string &instanceVertShaderPath,
271 const std::string &instanceFragShaderPath);
272
273 /** @return @c true if GPU instancing is active. */
274 bool isInstancingEnabled() const { return instancingEnabled; }
275
276 /** @brief Allocate and initialise the extended uniform buffer object. */
277 void enableExtendedUBO();
278
279 /** @return @c true if the extended UBO is active. */
280 bool isExtendedUBOEnabled() const { return extendedUBOEnabled; }
281
282 /**
283 * @brief Upload mouse state to the extended UBO.
284 * @param mx Mouse X (normalised or pixels).
285 * @param my Mouse Y.
286 * @param pressed Mouse button state.
287 * @param reserved Reserved channel.
288 */
289 void setMouseState(float mx, float my, float pressed, float reserved = 0.0f);
290
291 /** @brief Upload user uniform 0 to the extended UBO. @param x,y,z,w Components. */
292 void setUniform0(float x, float y, float z, float w);
293 /** @brief Upload user uniform 1 to the extended UBO. @param x,y,z,w Components. */
294 void setUniform1(float x, float y, float z, float w);
295 /** @brief Upload user uniform 2 to the extended UBO. @param x,y,z,w Components. */
296 void setUniform2(float x, float y, float z, float w);
297 /** @brief Upload user uniform 3 to the extended UBO. @param x,y,z,w Components. */
298 void setUniform3(float x, float y, float z, float w);
299
300 /**
301 * @brief Upload audio frequency-band energy to the extended UBO.
302 *
303 * The values are appended after the custom-uniform array to preserve
304 * the existing SpriteExtended prefix and custom-uniform offsets.
305 *
306 * @param low Energy below 300 Hz.
307 * @param mid Energy from 300 through 3000 Hz.
308 * @param high Energy above 3000 Hz.
309 * @param reserved Reserved channel.
310 */
311 void setAudioBands(float low, float mid, float high, float reserved = 0.0f);
312
313 /**
314 * @brief Upload ordered custom float values to the extended UBO.
315 *
316 * Custom shaders can append @c vec4 custom_uniforms[16] after @c u3 in
317 * their binding-1 SpriteExtended block. Value N is available at
318 * @c custom_uniforms[N/4][N%4]. Existing shaders that use only the
319 * original SpriteExtended prefix remain compatible.
320 *
321 * @param values Up to MAX_CUSTOM_UNIFORMS values. Unused slots are zeroed.
322 * @throws mxvk::Exception when too many values are supplied.
323 */
324 void setCustomUniforms(const std::vector<float> &values);
325
326 /**
327 * @brief Allocate a shader-readable RGBA history texture array.
328 *
329 * Enables extended descriptors and exposes the array as a combined image
330 * sampler at set 0, binding 2. Replaces any previously allocated history
331 * texture. The texture is initialized to transparent black.
332 *
333 * @param width Width of every history layer in pixels.
334 * @param height Height of every history layer in pixels.
335 * @param layers Number of layers in the circular history buffer.
336 * @throws mxvk::Exception when any dimension is zero.
337 */
338 void enableHistoryTexture(uint32_t width, uint32_t height, uint32_t layers);
339
340 /**
341 * @brief Bind another sprite's history array without taking ownership.
342 *
343 * The source sprite must outlive this sprite. This is intended for
344 * post-processing passes which read one shared input history ring.
345 * No image, memory, or image view is allocated or freed by this sprite.
346 *
347 * @param source Sprite which owns an enabled history texture.
348 * @throws mxvk::Exception when the source has no history texture or
349 * belongs to a different Vulkan device.
350 */
351 void shareHistoryTexture(const VK_Sprite &source);
352
353 /**
354 * @brief Upload one RGBA frame into the next history layer.
355 *
356 * The write head advances after a successful upload. Input dimensions
357 * must match those passed to enableHistoryTexture().
358 *
359 * @param pixels RGBA8 source pixels.
360 * @param width Source width in pixels.
361 * @param height Source height in pixels.
362 * @param pitch Source row stride in bytes, or zero for tightly packed data.
363 * @throws mxvk::Exception for null data, invalid dimensions, or an inactive cache.
364 */
365 void updateHistoryTexture(const void *pixels, int width, int height, int pitch = 0);
366
367#ifdef MXVK_CUDA
368 /**
369 * @brief Upload a CUDA RGBA frame into the next history layer.
370 *
371 * Copies device-to-device into the Vulkan history array and advances
372 * the write head after a successful upload. Dimensions and format must
373 * match the active RGBA8 history texture.
374 *
375 * @param rgba CUDA RGBA8 source image.
376 * @param stream CUDA stream used for the asynchronous copy.
377 * @return @c true on success, or @c false for invalid input or when
378 * direct CUDA/Vulkan history interop is unavailable.
379 */
380 [[nodiscard]] bool updateHistoryTextureCuda(const cv::cuda::GpuMat &rgba,
381 cv::cuda::Stream &stream);
382#endif
383
384 /** @return The logical oldest-layer index for a circular history sampler. */
385 [[nodiscard]] uint32_t getHistoryHead() const { return historyHead; }
386
387 /** @return Number of allocated texture-history layers. */
388 [[nodiscard]] uint32_t getHistoryLayerCount() const { return historyLayers; }
389
390 /**
391 * @brief Allocate a shader-readable 1-D floating-point spectrum texture.
392 *
393 * Enables extended descriptors and exposes the texture as a combined
394 * image sampler at set 0, binding 3. The texture is initialized to zero.
395 *
396 * @param bins Number of R32_SFLOAT frequency bins.
397 * @throws mxvk::Exception when @p bins is zero.
398 */
399 void enableSpectrumTexture(uint32_t bins);
400
401 /**
402 * @brief Replace the current floating-point spectrum data.
403 * @param magnitudes Pointer to @p bins frequency magnitudes.
404 * @param bins Number of values; must match enableSpectrumTexture().
405 */
406 void updateSpectrumTexture(const float *magnitudes, uint32_t bins);
407
408 /** @return Number of allocated spectrum bins. */
409 [[nodiscard]] uint32_t getSpectrumBinCount() const { return spectrumBins; }
410
411 /**
412 * @brief Allocate a shader-readable FFT spectrum-history array.
413 *
414 * Enables extended descriptors and exposes the history as a combined
415 * image sampler at set 0, binding 4. The R32_SFLOAT 1-D array is
416 * initialized to zero and uses a circular write head.
417 *
418 * @param bins Number of frequency bins in every history layer.
419 * @param layers Requested number of history layers. The active GPU's
420 * maximum image-array-layer limit is applied automatically.
421 * @return Number of history layers actually allocated.
422 * @throws mxvk::Exception when either requested dimension is zero.
423 */
424 uint32_t enableSpectrumHistoryTexture(uint32_t bins, uint32_t layers);
425
426 /**
427 * @brief Upload one FFT spectrum into the next history layer.
428 *
429 * @param magnitudes Pointer to @p bins frequency magnitudes.
430 * @param bins Number of values; must match the configured history.
431 */
432 void updateSpectrumHistoryTexture(const float *magnitudes, uint32_t bins);
433
434 /** @return Physical array layer containing the newest FFT spectrum. */
435 [[nodiscard]] uint32_t getSpectrumHistoryHead() const { return spectrumHistoryHead; }
436
437 /** @return Number of allocated FFT spectrum-history layers. */
438 [[nodiscard]] uint32_t getSpectrumHistoryLayerCount() const { return spectrumHistoryLayers; }
439
440 private:
441 struct SpriteVertex {
442 float pos[2];
443 float texCoord[2];
444 };
445
446 struct SpriteDrawCmd {
447 float x, y, w, h;
448 float rotation;
449 glm::vec4 params;
450 };
451
452 VkDevice device = VK_NULL_HANDLE;
453 VkPhysicalDevice physicalDevice = VK_NULL_HANDLE;
454 VkQueue graphicsQueue = VK_NULL_HANDLE;
455 VkCommandPool commandPool = VK_NULL_HANDLE;
456 VkPipelineCache pipelineCache = VK_NULL_HANDLE;
457 VkImage spriteImage = VK_NULL_HANDLE;
458 VkDeviceMemory spriteImageMemory = VK_NULL_HANDLE;
459 VkImageView spriteImageView = VK_NULL_HANDLE;
460 int spriteWidth = 0;
461 int spriteHeight = 0;
462 bool spriteLoaded = false;
463 bool externalTexture = false;
464 VkShaderModule fragmentShaderModule = VK_NULL_HANDLE;
465 VkShaderModule computeShaderModule = VK_NULL_HANDLE;
466 VkPipeline computePipeline = VK_NULL_HANDLE;
467 VkPipelineLayout computePipelineLayout = VK_NULL_HANDLE;
468 VkImageView computeOutputImageView = VK_NULL_HANDLE;
469 uint32_t computeLocalSizeX = 1;
470 uint32_t computeLocalSizeY = 1;
471 void createComputePipeline();
472 void destroyComputePipeline();
473 bool hasCustomShader = false;
474 glm::vec4 shaderParams = glm::vec4(0.0f);
475 bool effectsEnabled = true;
476 std::vector<SpriteDrawCmd> drawQueue;
477
478 VkPipeline customPipeline = VK_NULL_HANDLE;
479 VkPipelineLayout customPipelineLayout = VK_NULL_HANDLE;
480 VkRenderPass renderPass = VK_NULL_HANDLE;
481 VkFormat colorAttachmentFormat = VK_FORMAT_UNDEFINED;
482 VkFormat depthAttachmentFormat = VK_FORMAT_UNDEFINED;
483 std::string vertexShaderPath;
484 std::string fragmentShaderPath;
485 void createCustomPipeline();
486
487 VkBuffer quadVertexBuffer = VK_NULL_HANDLE;
488 VkDeviceMemory quadVertexBufferMemory = VK_NULL_HANDLE;
489 VkBuffer quadIndexBuffer = VK_NULL_HANDLE;
490 VkDeviceMemory quadIndexBufferMemory = VK_NULL_HANDLE;
491 bool quadBufferCreated = false;
492 void createQuadBuffer();
493
494 VkDescriptorPool descriptorPool = VK_NULL_HANDLE;
495 std::vector<VkDescriptorPool> descriptorPools{};
496 VkDescriptorPool descriptorSetPool = VK_NULL_HANDLE;
497 uint32_t nextDescriptorPoolSets = 16;
498 VkDescriptorSetLayout descriptorSetLayout = VK_NULL_HANDLE;
499 VkDescriptorSet descriptorSet = VK_NULL_HANDLE;
500 std::unordered_map<VkImageView, VkDescriptorSet> externalDescriptorSets{};
501 VkFilter textureFilter = VK_FILTER_LINEAR;
502 void createDescriptorPool();
503 void destroyDescriptorPools();
504 void destroyTextureDescriptorPools();
505 VkDescriptorSet createDescriptorSet(VkImageView imageView);
506 void destroySpriteResources();
507
508 VkBuffer persistentStagingBuffer = VK_NULL_HANDLE;
509 VkDeviceMemory persistentStagingMemory = VK_NULL_HANDLE;
510 void *persistentStagingMapped = nullptr;
511 VkDeviceSize persistentStagingSize = 0;
512 VkFence uploadFence = VK_NULL_HANDLE;
513 VkCommandBuffer uploadCmdBuffer = VK_NULL_HANDLE;
514 bool stagingResourcesCreated = false;
515 [[nodiscard]] VkDeviceSize stagingAllocationSize(VkDeviceSize requiredSize) const;
516 void createStagingResources(VkDeviceSize size);
517 void destroyStagingResources();
518 void createBuffer(VkDeviceSize size, VkBufferUsageFlags usage,
519 VkMemoryPropertyFlags properties, VkBuffer &buffer,
520 VkDeviceMemory &bufferMemory);
521 uint32_t findMemoryType(uint32_t typeFilter, VkMemoryPropertyFlags properties);
522 void copyBufferToImage(VkBuffer buffer, VkImage image, uint32_t width, uint32_t height,
523 uint32_t baseArrayLayer = 0, uint32_t layerCount = 1);
524 VkCommandBuffer beginSingleTimeCommands();
525 void endSingleTimeCommands(VkCommandBuffer commandBuffer);
526 void transitionImageLayout(VkImage image, VkImageLayout oldLayout, VkImageLayout newLayout,
527 uint32_t baseArrayLayer = 0, uint32_t layerCount = 1);
528 void createImage(uint32_t width, uint32_t height, VkFormat format, VkImageTiling tiling,
529 VkImageUsageFlags usage, VkMemoryPropertyFlags properties,
530 VkImage &image, VkDeviceMemory &imageMemory,
531 uint32_t arrayLayers = 1,
532 VkImageType imageType = VK_IMAGE_TYPE_2D);
533 VkImageView createImageView(VkImage image, VkFormat format,
534 VkImageViewType viewType = VK_IMAGE_VIEW_TYPE_2D,
535 uint32_t layerCount = 1);
536 void createSampler();
537 SDL_Surface *convertToRGBA(SDL_Surface *surface);
538 void createSpriteTexture(SDL_Surface *surface);
539 void updateSpriteTexture(const void *pixels, uint32_t width, uint32_t height);
540#ifdef MXVK_CUDA
541 void destroyCudaInterop();
542 void destroyCudaHistoryInterop();
543 bool ensureCudaInterop();
544 bool ensureCudaHistoryInterop();
545 bool transitionCudaImageForWrite();
546 bool transitionCudaImageForShaderRead();
547 void transitionCudaHistoryLayer(VkImageLayout oldLayout,
548 VkImageLayout newLayout,
549 VkAccessFlags sourceAccess,
550 VkAccessFlags destinationAccess,
551 VkPipelineStageFlags sourceStage,
552 VkPipelineStageFlags destinationStage);
553 bool updateTextureCudaHost(const void *pixels, uint32_t width, uint32_t height, uint32_t pitch);
554 void recordCudaReadyBarrier(VkCommandBuffer cmdBuffer);
555 void createCudaExportableImage(uint32_t width, uint32_t height,
556 uint32_t arrayLayers, VkImage &image,
557 VkDeviceMemory &imageMemory,
558 VkDeviceSize &exportMemorySize);
559 VkDeviceSize cudaExportMemorySize = 0;
560 cudaExternalMemory_t cudaExternalMemory = nullptr;
561 cudaMipmappedArray_t cudaMipmappedArray = nullptr;
562 cudaArray_t cudaArray = nullptr;
563 bool cudaInteropEnabled = false;
564 bool cudaInteropUnavailableLogged = false;
565 bool cudaUploadLogged = false;
566 bool cudaWriteTransitionLogged = false;
567 bool cudaSampleBarrierLogged = false;
568 bool cudaImageNeedsShaderBarrier = false;
569 VkImageLayout cudaImageLayout = VK_IMAGE_LAYOUT_UNDEFINED;
570 VkDeviceSize cudaHistoryExportMemorySize = 0;
571 cudaExternalMemory_t cudaHistoryExternalMemory = nullptr;
572 cudaMipmappedArray_t cudaHistoryMipmappedArray = nullptr;
573 cudaArray_t cudaHistoryArray = nullptr;
574 bool cudaHistoryInteropEnabled = false;
575 bool cudaHistoryInteropUnavailableLogged = false;
576 bool cudaHistoryUploadLogged = false;
577#endif
578 std::vector<char> readShaderFile(const std::string &filename);
579
580 struct SpriteExtendedUBO {
581 glm::vec4 mouse;
582 glm::vec4 u0;
583 glm::vec4 u1;
584 glm::vec4 u2;
585 glm::vec4 u3;
586 std::array<glm::vec4, MAX_CUSTOM_UNIFORMS / 4> custom_uniforms;
587 glm::vec4 audio_bands;
588 glm::vec4 audio_history;
589 };
590 bool extendedUBOEnabled = false;
591 SpriteExtendedUBO extendedUBOData{};
592 VkBuffer extendedUBOBuffer = VK_NULL_HANDLE;
593 VkDeviceMemory extendedUBOMemory = VK_NULL_HANDLE;
594 void *extendedUBOMapped = nullptr;
595 VkDescriptorSetLayout extendedDescriptorSetLayout = VK_NULL_HANDLE;
596 VkDescriptorPool extendedDescriptorPool = VK_NULL_HANDLE;
597 VkDescriptorSet extendedDescriptorSet = VK_NULL_HANDLE;
598 bool ownExtendedDescriptorSetLayout = false;
599 void createExtendedUBO();
600 void updateExtendedUBO();
601 void createExtendedDescriptorSetLayout();
602 void createExtendedDescriptorSet();
603 void destroyExtendedUBO();
604
605 bool historyTextureEnabled = false;
606 bool historyTextureShared = false;
607 VkImage historyImage = VK_NULL_HANDLE;
608 VkDeviceMemory historyImageMemory = VK_NULL_HANDLE;
609 VkImageView historyImageView = VK_NULL_HANDLE;
610 uint32_t historyWidth = 0;
611 uint32_t historyHeight = 0;
612 uint32_t historyLayers = 0;
613 uint32_t historyHead = 0;
614 void destroyHistoryTexture();
615
616 bool spectrumTextureEnabled = false;
617 VkImage spectrumImage = VK_NULL_HANDLE;
618 VkDeviceMemory spectrumImageMemory = VK_NULL_HANDLE;
619 VkImageView spectrumImageView = VK_NULL_HANDLE;
620 uint32_t spectrumBins = 0;
621 void destroySpectrumTexture();
622
623 bool spectrumHistoryTextureEnabled = false;
624 VkImage spectrumHistoryImage = VK_NULL_HANDLE;
625 VkDeviceMemory spectrumHistoryImageMemory = VK_NULL_HANDLE;
626 VkImageView spectrumHistoryImageView = VK_NULL_HANDLE;
627 uint32_t spectrumHistoryBins = 0;
628 uint32_t spectrumHistoryLayers = 0;
629 uint32_t spectrumHistoryHead = 0;
630 uint32_t spectrumHistoryWriteIndex = 0;
631 void destroySpectrumHistoryTexture();
632 void recreateExtendedDescriptorLayout();
633
634 struct SpriteInstanceData {
635 float posX, posY, sizeW, sizeH;
636 float params[4];
637 };
638 VkBuffer instanceBuffer = VK_NULL_HANDLE;
639 VkDeviceMemory instanceBufferMemory = VK_NULL_HANDLE;
640 void *instanceBufferMapped = nullptr;
641 uint32_t instanceBufferCapacity = 0;
642 bool instancingEnabled = false;
643 VkPipeline instancedPipeline = VK_NULL_HANDLE;
644 VkPipelineLayout instancedPipelineLayout = VK_NULL_HANDLE;
645 std::string instanceVertPath;
646 std::string instanceFragPath;
647 void createInstancedPipeline(const std::string &vertPath, const std::string &fragPath);
648 void ensureInstanceBuffer(uint32_t count);
649 void destroyInstanceResources();
650 };
651
652} // namespace mxvk
void setUniform3(float x, float y, float z, float w)
Upload user uniform 3 to the extended UBO.
VkPipelineLayout getPipelineLayout() const
void enableInstancing(uint32_t maxInstances, const std::string &instanceVertShaderPath, const std::string &instanceFragShaderPath)
Enable GPU instancing for this sprite type.
void renderSprites(VkCommandBuffer cmdBuffer, VkPipelineLayout pipelineLayout, uint32_t screenWidth, uint32_t screenHeight)
Record all queued draw commands into the given command buffer.
void setVertexShaderPath(const std::string &path)
Override the vertex shader path (used when rebuilding the pipeline).
uint32_t getSpectrumHistoryLayerCount() const
static constexpr std::size_t MAX_CUSTOM_UNIFORMS
bool isInstancingEnabled() const
void releaseUploadResources()
Release upload/staging resources tied to the current command pool.
uint32_t getSpectrumBinCount() 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.
uint32_t getHistoryHead() const
void setDepthAttachmentFormat(VkFormat format)
Assign dynamic-rendering depth attachment format used to build pipelines.
VK_Sprite(VK_Sprite &&)=delete
void updateSpectrumHistoryTexture(const float *magnitudes, uint32_t bins)
Upload one FFT spectrum into the next history layer.
void setCommandPool(VkCommandPool pool)
Rebind the command pool used for upload/staging operations.
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.
VK_Sprite & operator=(VK_Sprite &&)=delete
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.
void setDescriptorSetLayout(VkDescriptorSetLayout layout)
Assign an external descriptor-set layout.
bool isExtendedUBOEnabled() const
void enableExtendedUBO()
Allocate and initialise the extended uniform buffer object.
void prepareForRendering(VkCommandBuffer cmdBuffer)
Record texture barriers that must happen before dynamic rendering begins.
void updateTexture(SDL_Surface *surface)
Replace the sprite texture from an SDL_Surface.
bool hasOwnPipeline() const
void rebuildInstancedPipeline()
Destroy and recreate the instanced graphics pipeline.
int getWidth() const
uint32_t getHistoryLayerCount() const
void setColorAttachmentFormat(VkFormat format)
Assign dynamic-rendering color attachment format used to build pipelines.
void setRenderPass(VkRenderPass rp)
Assign the render pass used to build the custom pipeline.
void loadSprite(const std::string &pngPath, const std::string &fragmentShaderPath="")
Load sprite texture from a PNG file.
void updateSpectrumTexture(const float *magnitudes, uint32_t bins)
Replace the current floating-point spectrum data.
void clearExternalTextureDescriptors()
void createEmptySprite(int width, int height, const std::string &vertexShaderPath="", const std::string &fragmentShaderPath="")
Create a blank (un-initialised) sprite texture.
VkPipeline getPipeline() const
uint32_t enableSpectrumHistoryTexture(uint32_t bins, uint32_t layers)
Allocate a shader-readable FFT spectrum-history array.
uint32_t getSpectrumHistoryHead() const
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 setExternalTexture(VkImageView image_view, int width, int height)
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 dispatchCompute(VkCommandBuffer cmdBuffer, VkImageView inputView, VkImageView outputView, uint32_t width, uint32_t height)
Dispatch the compute effect between two full-frame images.
bool hasComputePipeline() const
void updateHistoryTexture(const void *pixels, int width, int height, int pitch=0)
Upload one RGBA frame into the next history layer.
void setPipelineCache(VkPipelineCache cache)
Use the shared pipeline cache for custom/instanced pipeline creation.
void setTextureFilter(VkFilter filter)
Select the hardware filter used when scaling this sprite.
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 enableSpectrumTexture(uint32_t bins)
Allocate a shader-readable 1-D floating-point spectrum texture.
void rebuildPipeline()
Destroy and recreate the custom graphics pipeline.
VK_Sprite & operator=(const VK_Sprite &)=delete
VK_Sprite(const VK_Sprite &)=delete
void setAudioBands(float low, float mid, float high, float reserved=0.0f)
Upload audio frequency-band energy to the extended UBO.
VK_Sprite(VkDevice device, VkPhysicalDevice physicalDevice, VkQueue graphicsQueue, VkCommandPool commandPool)
Construct and record Vulkan context handles.
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.
VkSampler spriteSampler
Texture sampler.
Utilities for loading and saving PNG images.
Definition mxvk.hpp:31