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::VK_Sprite Class Reference

#include <mxvk/include/mxvk/mxvk_sprite.hpp>

Public Member Functions

void clearExternalTextureDescriptors ()
void clearQueue ()
 Discard all pending draw commands without rendering.
void createEmptySprite (int width, int height, const std::string &vertexShaderPath="", const std::string &fragmentShaderPath="")
 Create a blank (un-initialised) sprite texture.
void createEmptySpriteRgba16 (int width, int height, const std::string &vertexShaderPath="", const std::string &fragmentShaderPath="")
 Create a blank RGBA16 UNORM sprite texture.
void dispatchCompute (VkCommandBuffer cmdBuffer, VkImageView inputView, VkImageView outputView, uint32_t width, uint32_t height)
 Dispatch the compute effect between two full-frame images.
void drawSprite (int x, int y)
 Queue a draw at the given pixel position.
void drawSprite (int x, int y, float scaleX, float scaleY)
 Queue a scaled draw.
void drawSprite (int x, int y, float scaleX, float scaleY, float rotation)
 Queue a scaled and rotated draw.
void drawSpriteRect (int x, int y, int w, int h)
 Queue a draw into an explicit destination rectangle.
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 enableExtendedUBO ()
 Allocate and initialise the extended uniform buffer object.
void enableHistoryTexture (uint32_t width, uint32_t height, uint32_t layers)
 Allocate a shader-readable RGBA history texture array.
void enableHistoryTextureRgba16Float (uint32_t width, uint32_t height, uint32_t layers)
 Allocate a shader-readable RGBA16F history texture array.
void enableInstancing (uint32_t maxInstances, const std::string &instanceVertShaderPath, const std::string &instanceFragShaderPath)
 Enable GPU instancing for this sprite type.
uint32_t enableSpectrumHistoryTexture (uint32_t bins, uint32_t layers)
 Allocate a shader-readable FFT spectrum-history array.
void enableSpectrumTexture (uint32_t bins)
 Allocate a shader-readable 1-D floating-point spectrum texture.
bool getEffectsEnabled () const
int getHeight () const
uint32_t getHistoryHead () const
uint32_t getHistoryLayerCount () const
VkPipeline getPipeline () const
VkPipelineLayout getPipelineLayout () const
uint32_t getSpectrumBinCount () const
uint32_t getSpectrumHistoryHead () const
uint32_t getSpectrumHistoryLayerCount () const
int getWidth () const
bool hasComputePipeline () const
bool hasOwnPipeline () const
bool isExtendedUBOEnabled () const
bool isInstancingEnabled () const
void loadSprite (const std::string &pngPath, const std::string &fragmentShaderPath="")
 Load sprite texture from a PNG file.
void loadSprite (SDL_Surface *surface, const std::string &fragmentShaderPath="")
 Load sprite texture from an SDL_Surface.
VK_Sprite & operator= (const VK_Sprite &)=delete
VK_Sprite & operator= (VK_Sprite &&)=delete
void prepareForRendering (VkCommandBuffer cmdBuffer)
 Record texture barriers that must happen before dynamic rendering begins.
void rebuildInstancedPipeline ()
 Destroy and recreate the instanced graphics pipeline.
void rebuildPipeline ()
 Destroy and recreate the custom graphics pipeline.
void releaseUploadResources ()
 Release upload/staging resources tied to the current command pool.
void renderSprites (VkCommandBuffer cmdBuffer, VkPipelineLayout pipelineLayout, uint32_t screenWidth, uint32_t screenHeight)
 Record all queued draw commands into the given command buffer.
void setAudioBands (float low, float mid, float high, float reserved=0.0f)
 Upload audio frequency-band energy to the extended UBO.
void setColorAttachmentFormat (VkFormat format)
 Assign dynamic-rendering color attachment format used to build pipelines.
void setCommandPool (VkCommandPool pool)
 Rebind the command pool used for upload/staging operations.
void setCustomUniforms (const std::vector< float > &values)
 Upload ordered custom float values to the extended UBO.
void setDepthAttachmentFormat (VkFormat format)
 Assign dynamic-rendering depth attachment format used to build pipelines.
void setDescriptorSetLayout (VkDescriptorSetLayout layout)
 Assign an external descriptor-set layout.
void setEffectsEnabled (bool enabled)
 Enable or disable the custom fragment shader effects.
void setExternalTexture (VkImageView image_view, int width, int height)
void setFragmentShaderPath (const std::string &path)
 Replace the fragment shader path and rebuild the custom pipeline.
void setMouseState (float mx, float my, float pressed, float reserved=0.0f)
 Upload mouse state to the extended UBO.
void setPipelineCache (VkPipelineCache cache)
 Use the shared pipeline cache for custom/instanced pipeline creation.
void setRenderPass (VkRenderPass rp)
 Assign the render pass used to build the custom pipeline.
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 setTextureFilter (VkFilter filter)
 Select the hardware filter used when scaling this sprite.
void setUniform0 (float x, float y, float z, float w)
 Upload user uniform 0 to the extended UBO.
void setUniform1 (float x, float y, float z, float w)
 Upload user uniform 1 to the extended UBO.
void setUniform2 (float x, float y, float z, float w)
 Upload user uniform 2 to the extended UBO.
void setUniform3 (float x, float y, float z, float w)
 Upload user uniform 3 to the extended UBO.
void setVertexShaderPath (const std::string &path)
 Override the vertex shader path (used when rebuilding the pipeline).
void shareHistoryTexture (const VK_Sprite &source)
 Bind another sprite's history array without taking ownership.
void updateHistoryTexture (const void *pixels, int width, int height, int pitch=0)
 Upload one RGBA frame into the next history layer.
void updateHistoryTextureRgba16 (const uint16_t *pixels, int width, int height, int pitch=0)
 Upload normalized RGBA16 pixels into an RGBA16F history layer.
void updateSpectrumHistoryTexture (const float *magnitudes, uint32_t bins)
 Upload one FFT spectrum into the next history layer.
void updateSpectrumTexture (const float *magnitudes, uint32_t bins)
 Replace the current floating-point spectrum data.
void updateTexture (const void *pixels, int width, int height, int pitch=0)
 Replace the sprite texture from a raw pixel buffer.
void updateTexture (SDL_Surface *surface)
 Replace the sprite texture from an SDL_Surface.
void updateTextureRgba16 (const uint16_t *pixels, int width, int height, int pitch=0)
 Replace an RGBA16 UNORM sprite from native-endian uint16 data.
 VK_Sprite (const VK_Sprite &)=delete
 VK_Sprite (VK_Sprite &&)=delete
 VK_Sprite (VkDevice device, VkPhysicalDevice physicalDevice, VkQueue graphicsQueue, VkCommandPool commandPool)
 Construct and record Vulkan context handles.
 ~VK_Sprite ()
 Destructor — frees all Vulkan resources.

Public Attributes

VkSampler spriteSampler = VK_NULL_HANDLE
 Texture sampler.

Static Public Attributes

static constexpr std::size_t MAX_CUSTOM_UNIFORMS = 64

Detailed Description

Definition at line 56 of file mxvk_sprite.hpp.

Constructor & Destructor Documentation

◆ VK_Sprite() [1/3]

mxvk::VK_Sprite::VK_Sprite ( VkDevice device,
VkPhysicalDevice physicalDevice,
VkQueue graphicsQueue,
VkCommandPool commandPool )

Construct and record Vulkan context handles.

Parameters
deviceLogical device.
physicalDevicePhysical device.
graphicsQueueGraphics queue.
commandPoolCommand pool for staging operations.

Definition at line 49 of file mxvk_sprite.cpp.

49: device(dev), physicalDevice(physDev), graphicsQueue(gQueue), commandPool(cmdPool) { std::cout << "mxvk: Created Sprite\n"; }

◆ ~VK_Sprite()

mxvk::VK_Sprite::~VK_Sprite ( )

Destructor — frees all Vulkan resources.

Definition at line 83 of file mxvk_sprite.cpp.

83 {
84 vkDeviceWaitIdle(device);
85 drawQueue.clear();
86
87 destroyStagingResources();
88#ifdef MXVK_CUDA
89 destroyCudaInterop();
90#endif
91 if (quadVertexBuffer != VK_NULL_HANDLE) {
92 std::cout << "vk: destroying sprite quad vertex buffer\n";
93 vkDestroyBuffer(device, quadVertexBuffer, nullptr);
94 vkFreeMemory(device, quadVertexBufferMemory, nullptr);
95 }
96 if (quadIndexBuffer != VK_NULL_HANDLE) {
97 std::cout << "vk: destroying sprite quad index buffer\n";
98 vkDestroyBuffer(device, quadIndexBuffer, nullptr);
99 vkFreeMemory(device, quadIndexBufferMemory, nullptr);
100 }
101
102 destroyTextureDescriptorPools();
103
104 if (spriteSampler != VK_NULL_HANDLE) {
105 std::cout << "vk: destroying sprite sampler\n";
106 vkDestroySampler(device, spriteSampler, nullptr);
107 }
108
109 if (!externalTexture && spriteImageView != VK_NULL_HANDLE) {
110 std::cout << "vk: destroying sprite image view\n";
111 vkDestroyImageView(device, spriteImageView, nullptr);
112 }
113
114 if (!externalTexture && spriteImage != VK_NULL_HANDLE) {
115 std::cout << "vk: destroying sprite image\n";
116 vkDestroyImage(device, spriteImage, nullptr);
117 std::cout << "vk: freeing sprite image memory\n";
118 vkFreeMemory(device, spriteImageMemory, nullptr);
119 }
120
121 if (fragmentShaderModule != VK_NULL_HANDLE) {
122 vkDestroyShaderModule(device, fragmentShaderModule, nullptr);
123 }
124
125 if (customPipeline != VK_NULL_HANDLE) {
126 std::cout << "vk: destroying sprite custom pipeline\n";
127 vkDestroyPipeline(device, customPipeline, nullptr);
128 }
129
130 if (customPipelineLayout != VK_NULL_HANDLE) {
131 std::cout << "vk: destroying sprite custom pipeline layout\n";
132 vkDestroyPipelineLayout(device, customPipelineLayout, nullptr);
133 }
134
135 destroyComputePipeline();
136 if (computeShaderModule != VK_NULL_HANDLE) {
137 vkDestroyShaderModule(device, computeShaderModule, nullptr);
138 computeShaderModule = VK_NULL_HANDLE;
139 }
140
141 destroyExtendedUBO();
142 destroyHistoryTexture();
143 destroySpectrumTexture();
144 destroySpectrumHistoryTexture();
145 destroyInstanceResources();
146 }
VkSampler spriteSampler
Texture sampler.

◆ VK_Sprite() [2/3]

mxvk::VK_Sprite::VK_Sprite ( const VK_Sprite & )
delete

◆ VK_Sprite() [3/3]

mxvk::VK_Sprite::VK_Sprite ( VK_Sprite && )
delete

Member Function Documentation

◆ clearExternalTextureDescriptors()

void mxvk::VK_Sprite::clearExternalTextureDescriptors ( )

Definition at line 2622 of file mxvk_sprite.cpp.

2622 {
2623 if (!externalTexture && externalDescriptorSets.empty()) {
2624 return;
2625 }
2626 destroyTextureDescriptorPools();
2627 }

◆ clearQueue()

void mxvk::VK_Sprite::clearQueue ( )

Discard all pending draw commands without rendering.

Definition at line 2721 of file mxvk_sprite.cpp.

2721{ drawQueue.clear(); }

◆ createEmptySprite()

void mxvk::VK_Sprite::createEmptySprite ( int width,
int height,
const std::string & vertexShaderPath = "",
const std::string & fragmentShaderPath = "" )

Create a blank (un-initialised) sprite texture.

Parameters
widthPixel width.
heightPixel height.
vertexShaderPathOptional vertex shader.
fragmentShaderPathOptional fragment shader.

Definition at line 1658 of file mxvk_sprite.cpp.

1658{ createEmptySpriteWithFormat(width, height, VK_FORMAT_R8G8B8A8_UNORM, 4, vertexShaderPath, fragmentShaderPath); }

◆ createEmptySpriteRgba16()

void mxvk::VK_Sprite::createEmptySpriteRgba16 ( int width,
int height,
const std::string & vertexShaderPath = "",
const std::string & fragmentShaderPath = "" )

Create a blank RGBA16 UNORM sprite texture.

This preserves transfer-encoded 10/12-bit video input without quantizing it to RGBA8. It does not change render-target precision.

Definition at line 1660 of file mxvk_sprite.cpp.

1660{ createEmptySpriteWithFormat(width, height, VK_FORMAT_R16G16B16A16_UNORM, 8, vertexShaderPath, fragmentShaderPath); }

◆ dispatchCompute()

void mxvk::VK_Sprite::dispatchCompute ( VkCommandBuffer cmdBuffer,
VkImageView inputView,
VkImageView outputView,
uint32_t width,
uint32_t height )

Dispatch the compute effect between two full-frame images.

Definition at line 1558 of file mxvk_sprite.cpp.

1558 {
1559 if (computePipeline == VK_NULL_HANDLE || computePipelineLayout == VK_NULL_HANDLE || inputView == VK_NULL_HANDLE || outputView == VK_NULL_HANDLE || width == 0 || height == 0) {
1560 throw mxvk::Exception("VKSprite::dispatchCompute received an incomplete compute pass");
1561 }
1562
1563 setExternalTexture(inputView, static_cast<int>(width), static_cast<int>(height));
1564 if (computeOutputImageView != outputView) {
1565 if (extendedDescriptorPool != VK_NULL_HANDLE) {
1566 vkDeviceWaitIdle(device);
1567 vkDestroyDescriptorPool(device, extendedDescriptorPool, nullptr);
1568 extendedDescriptorPool = VK_NULL_HANDLE;
1569 extendedDescriptorSet = VK_NULL_HANDLE;
1570 }
1571 computeOutputImageView = outputView;
1572 }
1573 updateExtendedUBO();
1574 if (extendedDescriptorSet == VK_NULL_HANDLE) {
1575 createExtendedDescriptorSet();
1576 }
1577 if (extendedDescriptorSet == VK_NULL_HANDLE) {
1578 throw mxvk::Exception("VKSprite::dispatchCompute could not create its descriptor set");
1579 }
1580
1581 vkCmdBindPipeline(cmdBuffer, VK_PIPELINE_BIND_POINT_COMPUTE, computePipeline);
1582 vkCmdBindDescriptorSets(cmdBuffer, VK_PIPELINE_BIND_POINT_COMPUTE, computePipelineLayout, 0, 1, &extendedDescriptorSet, 0, nullptr);
1583 vkCmdDispatch(cmdBuffer, (width + computeLocalSizeX - 1U) / computeLocalSizeX, (height + computeLocalSizeY - 1U) / computeLocalSizeY, 1U);
1584 }
void setExternalTexture(VkImageView image_view, int width, int height)

◆ drawSprite() [1/3]

void mxvk::VK_Sprite::drawSprite ( int x,
int y )

Queue a draw at the given pixel position.

Parameters
xDestination X.
yDestination Y.

Definition at line 2561 of file mxvk_sprite.cpp.

2561{ drawSpriteRect(x, y, spriteWidth, spriteHeight); }
void drawSpriteRect(int x, int y, int w, int h)
Queue a draw into an explicit destination rectangle.

◆ drawSprite() [2/3]

void mxvk::VK_Sprite::drawSprite ( int x,
int y,
float scaleX,
float scaleY )

Queue a scaled draw.

Parameters
xDestination X.
yDestination Y.
scaleXHorizontal scale factor.
scaleYVertical scale factor.

Definition at line 2563 of file mxvk_sprite.cpp.

2563{ drawSpriteRect(x, y, static_cast<int>(spriteWidth * scaleX), static_cast<int>(spriteHeight * scaleY)); }

◆ drawSprite() [3/3]

void mxvk::VK_Sprite::drawSprite ( int x,
int y,
float scaleX,
float scaleY,
float rotation )

Queue a scaled and rotated draw.

Parameters
xDestination X.
yDestination Y.
scaleXHorizontal scale.
scaleYVertical scale.
rotationClockwise rotation in degrees.

Definition at line 2565 of file mxvk_sprite.cpp.

2565 {
2566 if (!spriteLoaded) {
2567 throw mxvk::Exception("VKSprite::drawSprite called before sprite was loaded");
2568 }
2569
2570 drawQueue.push_back({static_cast<float>(x), static_cast<float>(y), static_cast<float>(static_cast<int>(spriteWidth * scaleX)), static_cast<float>(static_cast<int>(spriteHeight * scaleY)), rotation, shaderParams});
2571 }

◆ drawSpriteRect()

void mxvk::VK_Sprite::drawSpriteRect ( int x,
int y,
int w,
int h )

Queue a draw into an explicit destination rectangle.

Parameters
x,y,w,hDestination rectangle.

Definition at line 2573 of file mxvk_sprite.cpp.

2573 {
2574 if (!spriteLoaded) {
2575 throw mxvk::Exception("VKSprite::drawSpriteRect called before sprite was loaded");
2576 }
2577
2578 drawQueue.push_back({static_cast<float>(x), static_cast<float>(y), static_cast<float>(w), static_cast<float>(h), 0.0f, shaderParams});
2579 }

◆ enableComputeShader()

void mxvk::VK_Sprite::enableComputeShader ( const std::string & path,
uint32_t localSizeX,
uint32_t localSizeY,
uint32_t localSizeZ = 1 )

Build a compute image-effect pipeline for this sprite.

Compute shaders use the extended sprite descriptor ABI: binding 0 is the sampled input image, binding 1 is SpriteExtended, optional history/audio textures remain at bindings 2-4, and binding 5 is a write-only storage image matching the active render precision.

Definition at line 1535 of file mxvk_sprite.cpp.

1535 {
1536 if (path.empty() || localSizeX == 0 || localSizeY == 0 || localSizeZ == 0) {
1537 throw mxvk::Exception("VKSprite::enableComputeShader requires a shader and positive local size");
1538 }
1539
1540 vkDeviceWaitIdle(device);
1541 destroyComputePipeline();
1542 if (computeShaderModule != VK_NULL_HANDLE) {
1543 vkDestroyShaderModule(device, computeShaderModule, nullptr);
1544 computeShaderModule = VK_NULL_HANDLE;
1545 }
1546 computeShaderModule = mxvk::create_shader_module(device, readShaderFile(path));
1547 computeLocalSizeX = localSizeX;
1548 computeLocalSizeY = localSizeY;
1549
1550 if (!extendedUBOEnabled) {
1552 createComputePipeline();
1553 } else {
1554 recreateExtendedDescriptorLayout();
1555 }
1556 }
void enableExtendedUBO()
Allocate and initialise the extended uniform buffer object.
VkShaderModule create_shader_module(VkDevice device, const std::vector< char > &spv_bytes)
Create a shader module from SPIR-V bytecode.

◆ enableExtendedUBO()

void mxvk::VK_Sprite::enableExtendedUBO ( )

Allocate and initialise the extended uniform buffer object.

Definition at line 201 of file mxvk_sprite.cpp.

201 {
202 if (extendedUBOEnabled)
203 return;
204 extendedUBOEnabled = true;
205 createExtendedUBO();
206 createExtendedDescriptorSetLayout();
208 }
void rebuildPipeline()
Destroy and recreate the custom graphics pipeline.

◆ enableHistoryTexture()

void mxvk::VK_Sprite::enableHistoryTexture ( uint32_t width,
uint32_t height,
uint32_t layers )

Allocate a shader-readable RGBA history texture array.

Enables extended descriptors and exposes the array as a combined image sampler at set 0, binding 2. Replaces any previously allocated history texture. The texture is initialized to transparent black.

Parameters
widthWidth of every history layer in pixels.
heightHeight of every history layer in pixels.
layersNumber of layers in the circular history buffer.
Exceptions
mxvk::Exceptionwhen any dimension is zero.

Definition at line 233 of file mxvk_sprite.cpp.

233{ enableHistoryTextureWithFormat(width, height, layers, VK_FORMAT_R8G8B8A8_UNORM); }

◆ enableHistoryTextureRgba16Float()

void mxvk::VK_Sprite::enableHistoryTextureRgba16Float ( uint32_t width,
uint32_t height,
uint32_t layers )

Allocate a shader-readable RGBA16F history texture array.

Definition at line 235 of file mxvk_sprite.cpp.

235{ enableHistoryTextureWithFormat(width, height, layers, VK_FORMAT_R16G16B16A16_SFLOAT); }

◆ enableInstancing()

void mxvk::VK_Sprite::enableInstancing ( uint32_t maxInstances,
const std::string & instanceVertShaderPath,
const std::string & instanceFragShaderPath )

Enable GPU instancing for this sprite type.

Parameters
maxInstancesMaximum simultaneous instances.
instanceVertShaderPathVertex shader supporting instancing.
instanceFragShaderPathFragment shader.

Definition at line 1122 of file mxvk_sprite.cpp.

1122 {
1123 if (colorAttachmentFormat == VK_FORMAT_UNDEFINED || descriptorSetLayout == VK_NULL_HANDLE) {
1124 throw mxvk::Exception("VKSprite::enableInstancing called before color format/descriptorSetLayout set");
1125 }
1126 ensureInstanceBuffer(maxInstances);
1127 createQuadBuffer();
1128 instanceVertPath = instanceVertShaderPath;
1129 instanceFragPath = instanceFragShaderPath;
1130 createInstancedPipeline(instanceVertShaderPath, instanceFragShaderPath);
1131 instancingEnabled = true;
1132 std::cout << std::format("mxvk: Instancing enabled (max {} instances)\n", maxInstances);
1133 }

◆ enableSpectrumHistoryTexture()

uint32_t mxvk::VK_Sprite::enableSpectrumHistoryTexture ( uint32_t bins,
uint32_t layers )

Allocate a shader-readable FFT spectrum-history array.

Enables extended descriptors and exposes the history as a combined image sampler at set 0, binding 4. The R32_SFLOAT 1-D array is initialized to zero and uses a circular write head.

Parameters
binsNumber of frequency bins in every history layer.
layersRequested number of history layers. The active GPU's maximum image-array-layer limit is applied automatically.
Returns
Number of history layers actually allocated.
Exceptions
mxvk::Exceptionwhen either requested dimension is zero.

Definition at line 563 of file mxvk_sprite.cpp.

563 {
564 if (bins == 0 || layers == 0) {
565 throw mxvk::Exception("VKSprite::enableSpectrumHistoryTexture requires positive bin and layer counts");
566 }
567 VkPhysicalDeviceProperties properties{};
568 vkGetPhysicalDeviceProperties(physicalDevice, &properties);
569 const uint32_t allocatedLayers = std::min(layers, properties.limits.maxImageArrayLayers);
570 if (allocatedLayers == 0) {
571 throw mxvk::Exception("VKSprite::enableSpectrumHistoryTexture is unavailable on this device");
572 }
573 if (spectrumHistoryTextureEnabled && spectrumHistoryBins == bins && spectrumHistoryLayers == allocatedLayers) {
574 return spectrumHistoryLayers;
575 }
576 if (!extendedUBOEnabled) {
578 }
579
580 vkDeviceWaitIdle(device);
581 destroySpectrumHistoryTexture();
582
583 if (allocatedLayers != layers) {
584 std::cerr << "vk: spectrum history clamped to device array-layer limit " << allocatedLayers << " (was " << layers << ")\n";
585 }
586 spectrumHistoryBins = bins;
587 spectrumHistoryLayers = allocatedLayers;
588 spectrumHistoryHead = 0;
589 spectrumHistoryWriteIndex = 0;
590 extendedUBOData.audio_history = glm::vec4(0.0f, static_cast<float>(allocatedLayers), static_cast<float>(bins), 0.0f);
591
592 createImage(bins, 1, VK_FORMAT_R32_SFLOAT, VK_IMAGE_TILING_OPTIMAL, VK_IMAGE_USAGE_TRANSFER_DST_BIT | VK_IMAGE_USAGE_SAMPLED_BIT, VK_MEMORY_PROPERTY_DEVICE_LOCAL_BIT, spectrumHistoryImage, spectrumHistoryImageMemory, allocatedLayers, VK_IMAGE_TYPE_1D);
593
594 const VkDeviceSize imageSize = static_cast<VkDeviceSize>(bins) * static_cast<VkDeviceSize>(allocatedLayers) * sizeof(float);
595 VkBuffer stagingBuffer = VK_NULL_HANDLE;
596 VkDeviceMemory stagingMemory = VK_NULL_HANDLE;
597 createBuffer(imageSize, VK_BUFFER_USAGE_TRANSFER_SRC_BIT, VK_MEMORY_PROPERTY_HOST_VISIBLE_BIT | VK_MEMORY_PROPERTY_HOST_COHERENT_BIT, stagingBuffer, stagingMemory);
598
599 void *data = nullptr;
600 VK_CHECK_RESULT(vkMapMemory(device, stagingMemory, 0, imageSize, 0, &data));
601 memset(data, 0, static_cast<std::size_t>(imageSize));
602 vkUnmapMemory(device, stagingMemory);
603
604 transitionImageLayout(spectrumHistoryImage, VK_IMAGE_LAYOUT_UNDEFINED, VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL, 0, allocatedLayers);
605 copyBufferToImage(stagingBuffer, spectrumHistoryImage, bins, 1, 0, allocatedLayers);
606 transitionImageLayout(spectrumHistoryImage, VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL, VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL, 0, allocatedLayers);
607
608 vkDestroyBuffer(device, stagingBuffer, nullptr);
609 vkFreeMemory(device, stagingMemory, nullptr);
610
611 spectrumHistoryImageView = createImageView(spectrumHistoryImage, VK_FORMAT_R32_SFLOAT, VK_IMAGE_VIEW_TYPE_1D_ARRAY, allocatedLayers);
612 spectrumHistoryTextureEnabled = true;
613 recreateExtendedDescriptorLayout();
614 return allocatedLayers;
615 }
#define VK_CHECK_RESULT(f)

◆ enableSpectrumTexture()

void mxvk::VK_Sprite::enableSpectrumTexture ( uint32_t bins)

Allocate a shader-readable 1-D floating-point spectrum texture.

Enables extended descriptors and exposes the texture as a combined image sampler at set 0, binding 3. The texture is initialized to zero.

Parameters
binsNumber of R32_SFLOAT frequency bins.
Exceptions
mxvk::Exceptionwhen bins is zero.

Definition at line 462 of file mxvk_sprite.cpp.

462 {
463 if (bins == 0) {
464 throw mxvk::Exception("VKSprite::enableSpectrumTexture requires a positive bin count");
465 }
466 if (spectrumTextureEnabled && spectrumBins == bins) {
467 return;
468 }
469 if (!extendedUBOEnabled) {
471 }
472
473 vkDeviceWaitIdle(device);
474 destroySpectrumTexture();
475
476 spectrumBins = bins;
477 createImage(bins, 1, VK_FORMAT_R32_SFLOAT, VK_IMAGE_TILING_OPTIMAL, VK_IMAGE_USAGE_TRANSFER_DST_BIT | VK_IMAGE_USAGE_SAMPLED_BIT, VK_MEMORY_PROPERTY_DEVICE_LOCAL_BIT, spectrumImage, spectrumImageMemory, 1, VK_IMAGE_TYPE_1D);
478
479 const VkDeviceSize imageSize = static_cast<VkDeviceSize>(bins) * sizeof(float);
480 VkBuffer stagingBuffer = VK_NULL_HANDLE;
481 VkDeviceMemory stagingMemory = VK_NULL_HANDLE;
482 createBuffer(imageSize, VK_BUFFER_USAGE_TRANSFER_SRC_BIT, VK_MEMORY_PROPERTY_HOST_VISIBLE_BIT | VK_MEMORY_PROPERTY_HOST_COHERENT_BIT, stagingBuffer, stagingMemory);
483
484 void *data = nullptr;
485 VK_CHECK_RESULT(vkMapMemory(device, stagingMemory, 0, imageSize, 0, &data));
486 memset(data, 0, static_cast<std::size_t>(imageSize));
487 vkUnmapMemory(device, stagingMemory);
488
489 transitionImageLayout(spectrumImage, VK_IMAGE_LAYOUT_UNDEFINED, VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL);
490 copyBufferToImage(stagingBuffer, spectrumImage, bins, 1);
491 transitionImageLayout(spectrumImage, VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL, VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL);
492
493 vkDestroyBuffer(device, stagingBuffer, nullptr);
494 vkFreeMemory(device, stagingMemory, nullptr);
495
496 spectrumImageView = createImageView(spectrumImage, VK_FORMAT_R32_SFLOAT, VK_IMAGE_VIEW_TYPE_1D);
497 spectrumTextureEnabled = true;
498 recreateExtendedDescriptorLayout();
499 }

◆ getEffectsEnabled()

bool mxvk::VK_Sprite::getEffectsEnabled ( ) const
inline
Returns
true if shader effects are enabled.

Definition at line 185 of file mxvk_sprite.hpp.

185{ return effectsEnabled; }

◆ getHeight()

int mxvk::VK_Sprite::getHeight ( ) const
inline
Returns
Sprite texture height in pixels.

Definition at line 208 of file mxvk_sprite.hpp.

208{ return spriteHeight; }

◆ getHistoryHead()

uint32_t mxvk::VK_Sprite::getHistoryHead ( ) const
inlinenodiscard
Returns
The logical oldest-layer index for a circular history sampler.

Definition at line 398 of file mxvk_sprite.hpp.

398{ return historyHead; }

◆ getHistoryLayerCount()

uint32_t mxvk::VK_Sprite::getHistoryLayerCount ( ) const
inlinenodiscard
Returns
Number of allocated texture-history layers.

Definition at line 401 of file mxvk_sprite.hpp.

401{ return historyLayers; }

◆ getPipeline()

VkPipeline mxvk::VK_Sprite::getPipeline ( ) const
inline
Returns
The custom VkPipeline handle (may be VK_NULL_HANDLE).

Definition at line 262 of file mxvk_sprite.hpp.

262{ return customPipeline; }

◆ getPipelineLayout()

VkPipelineLayout mxvk::VK_Sprite::getPipelineLayout ( ) const
inline
Returns
The custom pipeline layout handle.

Definition at line 264 of file mxvk_sprite.hpp.

264{ return customPipelineLayout; }

◆ getSpectrumBinCount()

uint32_t mxvk::VK_Sprite::getSpectrumBinCount ( ) const
inlinenodiscard
Returns
Number of allocated spectrum bins.

Definition at line 422 of file mxvk_sprite.hpp.

422{ return spectrumBins; }

◆ getSpectrumHistoryHead()

uint32_t mxvk::VK_Sprite::getSpectrumHistoryHead ( ) const
inlinenodiscard
Returns
Physical array layer containing the newest FFT spectrum.

Definition at line 448 of file mxvk_sprite.hpp.

448{ return spectrumHistoryHead; }

◆ getSpectrumHistoryLayerCount()

uint32_t mxvk::VK_Sprite::getSpectrumHistoryLayerCount ( ) const
inlinenodiscard
Returns
Number of allocated FFT spectrum-history layers.

Definition at line 451 of file mxvk_sprite.hpp.

451{ return spectrumHistoryLayers; }

◆ getWidth()

int mxvk::VK_Sprite::getWidth ( ) const
inline
Returns
Sprite texture width in pixels.

Definition at line 206 of file mxvk_sprite.hpp.

206{ return spriteWidth; }

◆ hasComputePipeline()

bool mxvk::VK_Sprite::hasComputePipeline ( ) const
inlinenodiscard
Returns
Whether this sprite owns an executable compute pipeline.

Definition at line 254 of file mxvk_sprite.hpp.

254{ return computePipeline != VK_NULL_HANDLE; }

◆ hasOwnPipeline()

bool mxvk::VK_Sprite::hasOwnPipeline ( ) const
inline
Returns
true if a custom pipeline has been built.

Definition at line 260 of file mxvk_sprite.hpp.

260{ return customPipeline != VK_NULL_HANDLE; }

◆ isExtendedUBOEnabled()

bool mxvk::VK_Sprite::isExtendedUBOEnabled ( ) const
inline
Returns
true if the extended UBO is active.

Definition at line 288 of file mxvk_sprite.hpp.

288{ return extendedUBOEnabled; }

◆ isInstancingEnabled()

bool mxvk::VK_Sprite::isInstancingEnabled ( ) const
inline
Returns
true if GPU instancing is active.

Definition at line 282 of file mxvk_sprite.hpp.

282{ return instancingEnabled; }

◆ loadSprite() [1/2]

void mxvk::VK_Sprite::loadSprite ( const std::string & pngPath,
const std::string & fragmentShaderPath = "" )

Load sprite texture from a PNG file.

Parameters
pngPathPath to the PNG file.
fragmentShaderPathOptional custom fragment shader (SPIR-V .spv).

Definition at line 1617 of file mxvk_sprite.cpp.

1617 {
1618 SDL_Surface *surface = mxvk::LoadPNG(pngPath.c_str());
1619 if (!surface) {
1620 throw mxvk::Exception("Failed to load sprite image: " + pngPath);
1621 }
1622 loadSprite(surface, fragmentShaderPath);
1623 SDL_DestroySurface(surface);
1624 std::cout << std::format("mxvk: Loaded PNG: {}\n", pngPath);
1625 }
void loadSprite(const std::string &pngPath, const std::string &fragmentShaderPath="")
Load sprite texture from a PNG file.
SDL_Surface * LoadPNG(const char *file)
Load a PNG file into an SDL_Surface.
Definition mxvk_png.cpp:89

◆ loadSprite() [2/2]

void mxvk::VK_Sprite::loadSprite ( SDL_Surface * surface,
const std::string & fragmentShaderPath = "" )

Load sprite texture from an SDL_Surface.

Parameters
surfaceSource surface (not consumed).
fragmentShaderPathOptional custom fragment shader.

Definition at line 1627 of file mxvk_sprite.cpp.

1627 {
1628 if (!surface) {
1629 throw mxvk::Exception("VKSprite::loadSprite called with null surface");
1630 }
1631 if (spriteLoaded || spriteImage != VK_NULL_HANDLE || fragmentShaderModule != VK_NULL_HANDLE) {
1632 destroySpriteResources();
1633 }
1634 SDL_Surface *rgbaSurface = convertToRGBA(surface);
1635 if (!rgbaSurface) {
1636 throw mxvk::Exception("Failed to convert sprite surface to RGBA");
1637 }
1638 spriteWidth = rgbaSurface->w;
1639 spriteHeight = rgbaSurface->h;
1640 createSpriteTexture(rgbaSurface);
1641 SDL_DestroySurface(rgbaSurface);
1642 createSampler();
1643 createQuadBuffer();
1644 if (!fragmentShaderPath.empty()) {
1645 auto shaderCode = readShaderFile(fragmentShaderPath);
1646 fragmentShaderModule = mxvk::create_shader_module(device, shaderCode);
1647 hasCustomShader = true;
1648 this->fragmentShaderPath = fragmentShaderPath;
1649
1650 if (colorAttachmentFormat != VK_FORMAT_UNDEFINED && descriptorSetLayout != VK_NULL_HANDLE) {
1651 createCustomPipeline();
1652 }
1653 }
1654 spriteLoaded = true;
1655 std::cout << std::format("mxvk: Loaded surface texture: {}x{}\n", spriteWidth, spriteHeight);
1656 }

◆ operator=() [1/2]

VK_Sprite & mxvk::VK_Sprite::operator= ( const VK_Sprite & )
delete

◆ operator=() [2/2]

VK_Sprite & mxvk::VK_Sprite::operator= ( VK_Sprite && )
delete

◆ prepareForRendering()

void mxvk::VK_Sprite::prepareForRendering ( VkCommandBuffer cmdBuffer)

Record texture barriers that must happen before dynamic rendering begins.

Parameters
cmdBufferCommand buffer currently being recorded outside a rendering instance.

Definition at line 2629 of file mxvk_sprite.cpp.

2629 {
2630#ifdef MXVK_CUDA
2631 recordCudaReadyBarrier(cmdBuffer);
2632#endif
2633 }

◆ rebuildInstancedPipeline()

void mxvk::VK_Sprite::rebuildInstancedPipeline ( )

Destroy and recreate the instanced graphics pipeline.

Definition at line 1586 of file mxvk_sprite.cpp.

1586 {
1587 if (!instancingEnabled || instanceVertPath.empty() || instanceFragPath.empty())
1588 return;
1589 createInstancedPipeline(instanceVertPath, instanceFragPath);
1590 }

◆ rebuildPipeline()

void mxvk::VK_Sprite::rebuildPipeline ( )

Destroy and recreate the custom graphics pipeline.

Definition at line 1455 of file mxvk_sprite.cpp.

1455 {
1456 if (!hasCustomShader || fragmentShaderModule == VK_NULL_HANDLE)
1457 return;
1458 createCustomPipeline();
1459 std::cout << "mxvk: Pipeline rebuilt\n";
1460 }

◆ releaseUploadResources()

void mxvk::VK_Sprite::releaseUploadResources ( )

Release upload/staging resources tied to the current command pool.

Call this before destroying or recreating the command pool.

Definition at line 51 of file mxvk_sprite.cpp.

51{ destroyStagingResources(); }

◆ renderSprites()

void mxvk::VK_Sprite::renderSprites ( VkCommandBuffer cmdBuffer,
VkPipelineLayout pipelineLayout,
uint32_t screenWidth,
uint32_t screenHeight )

Record all queued draw commands into the given command buffer.

Parameters
cmdBufferActive command buffer.
pipelineLayoutPipeline layout for push constants/descriptors.
screenWidthCurrent viewport width.
screenHeightCurrent viewport height.

Definition at line 2635 of file mxvk_sprite.cpp.

2635 {
2636 if (drawQueue.empty() || !spriteLoaded || !quadBufferCreated) {
2637 return;
2638 }
2639 if (descriptorSet == VK_NULL_HANDLE) {
2640 descriptorSet = createDescriptorSet(spriteImageView);
2641 if (externalTexture) {
2642 externalDescriptorSets[spriteImageView] = descriptorSet;
2643 }
2644 }
2645
2646 if (instancingEnabled && instancedPipeline != VK_NULL_HANDLE && instanceBuffer != VK_NULL_HANDLE) {
2647 uint32_t instanceCount = static_cast<uint32_t>(drawQueue.size());
2648
2649 if (instanceCount > instanceBufferCapacity) {
2650 ensureInstanceBuffer(instanceCount * 2);
2651 }
2652
2653 SpriteInstanceData *dst = static_cast<SpriteInstanceData *>(instanceBufferMapped);
2654 for (uint32_t i = 0; i < instanceCount; ++i) {
2655 const auto &cmd = drawQueue[i];
2656 dst[i].posX = cmd.x;
2657 dst[i].posY = cmd.y;
2658 dst[i].sizeW = cmd.w;
2659 dst[i].sizeH = cmd.h;
2660 dst[i].params[0] = cmd.params.x;
2661 dst[i].params[1] = cmd.params.y;
2662 dst[i].params[2] = cmd.params.z;
2663 dst[i].params[3] = cmd.params.w;
2664 }
2665
2666 vkCmdBindPipeline(cmdBuffer, VK_PIPELINE_BIND_POINT_GRAPHICS, instancedPipeline);
2667 vkCmdBindDescriptorSets(cmdBuffer, VK_PIPELINE_BIND_POINT_GRAPHICS, instancedPipelineLayout, 0, 1, &descriptorSet, 0, nullptr);
2668
2669 VkBuffer buffers[] = {quadVertexBuffer, instanceBuffer};
2670 VkDeviceSize bufOffsets[] = {0, 0};
2671 vkCmdBindVertexBuffers(cmdBuffer, 0, 2, buffers, bufOffsets);
2672 vkCmdBindIndexBuffer(cmdBuffer, quadIndexBuffer, 0, VK_INDEX_TYPE_UINT16);
2673
2674 float screenSize[2] = {static_cast<float>(screenWidth), static_cast<float>(screenHeight)};
2675 vkCmdPushConstants(cmdBuffer, instancedPipelineLayout, VK_SHADER_STAGE_VERTEX_BIT, 0, sizeof(screenSize), screenSize);
2676
2677 vkCmdDrawIndexed(cmdBuffer, 6, instanceCount, 0, 0, 0);
2678 return;
2679 }
2680
2681 VkPipelineLayout layoutToUse = (customPipeline != VK_NULL_HANDLE) ? customPipelineLayout : pipelineLayout;
2682 if (customPipeline != VK_NULL_HANDLE) {
2683 vkCmdBindPipeline(cmdBuffer, VK_PIPELINE_BIND_POINT_GRAPHICS, customPipeline);
2684 }
2685
2686 // When extended UBO is enabled, update UBO and bind extended descriptor set
2687 if (extendedUBOEnabled && customPipeline != VK_NULL_HANDLE) {
2688 updateExtendedUBO();
2689 if (extendedDescriptorSet == VK_NULL_HANDLE) {
2690 createExtendedDescriptorSet();
2691 }
2692 vkCmdBindDescriptorSets(cmdBuffer, VK_PIPELINE_BIND_POINT_GRAPHICS, layoutToUse, 0, 1, &extendedDescriptorSet, 0, nullptr);
2693 } else {
2694 vkCmdBindDescriptorSets(cmdBuffer, VK_PIPELINE_BIND_POINT_GRAPHICS, layoutToUse, 0, 1, &descriptorSet, 0, nullptr);
2695 }
2696
2697 VkBuffer vertexBuffers[] = {quadVertexBuffer};
2698 VkDeviceSize offsets[] = {0};
2699 vkCmdBindVertexBuffers(cmdBuffer, 0, 1, vertexBuffers, offsets);
2700 vkCmdBindIndexBuffer(cmdBuffer, quadIndexBuffer, 0, VK_INDEX_TYPE_UINT16);
2701
2702 for (const auto &cmd : drawQueue) {
2703 struct SpritePushConstants {
2704 float screenWidth;
2705 float screenHeight;
2706 float spritePosX;
2707 float spritePosY;
2708 float spriteSizeW;
2709 float spriteSizeH;
2710 float effectsOn;
2711 float padding2;
2712 float params[4];
2713 } pc{static_cast<float>(screenWidth), static_cast<float>(screenHeight), cmd.x, cmd.y, cmd.w, cmd.h, effectsEnabled ? 1.0f : 0.0f, cmd.rotation, {cmd.params.x, cmd.params.y, cmd.params.z, cmd.params.w}};
2714
2715 vkCmdPushConstants(cmdBuffer, layoutToUse, VK_SHADER_STAGE_VERTEX_BIT | VK_SHADER_STAGE_FRAGMENT_BIT, 0, sizeof(SpritePushConstants), &pc);
2716
2717 vkCmdDrawIndexed(cmdBuffer, 6, 1, 0, 0, 0);
2718 }
2719 }

◆ setAudioBands()

void mxvk::VK_Sprite::setAudioBands ( float low,
float mid,
float high,
float reserved = 0.0f )

Upload audio frequency-band energy to the extended UBO.

The values are appended after the custom-uniform array to preserve the existing SpriteExtended prefix and custom-uniform offsets.

Parameters
lowEnergy below 300 Hz.
midEnergy from 300 through 3000 Hz.
highEnergy above 3000 Hz.
reservedReserved channel.

Definition at line 220 of file mxvk_sprite.cpp.

220{ extendedUBOData.audio_bands = glm::vec4(low, mid, high, reserved); }

◆ setColorAttachmentFormat()

void mxvk::VK_Sprite::setColorAttachmentFormat ( VkFormat format)
inline

Assign dynamic-rendering color attachment format used to build pipelines.

Definition at line 221 of file mxvk_sprite.hpp.

221{ colorAttachmentFormat = format; }

◆ setCommandPool()

void mxvk::VK_Sprite::setCommandPool ( VkCommandPool pool)

Rebind the command pool used for upload/staging operations.

Any in-flight upload resources tied to the previous pool are released first.

Definition at line 53 of file mxvk_sprite.cpp.

53 {
54 if (pool == commandPool) {
55 return;
56 }
57
58 // uploadCmdBuffer is allocated from commandPool, so it must be released
59 // before switching to another pool.
60 destroyStagingResources();
61 commandPool = pool;
62 }

◆ setCustomUniforms()

void mxvk::VK_Sprite::setCustomUniforms ( const std::vector< float > & values)

Upload ordered custom float values to the extended UBO.

Custom shaders can append vec4 custom_uniforms[16] after u3 in their binding-1 SpriteExtended block. Value N is available at custom_uniforms[N/4][N%4]. Existing shaders that use only the original SpriteExtended prefix remain compatible.

Parameters
valuesUp to MAX_CUSTOM_UNIFORMS values. Unused slots are zeroed.
Exceptions
mxvk::Exceptionwhen too many values are supplied.

Definition at line 222 of file mxvk_sprite.cpp.

222 {
223 if (values.size() > MAX_CUSTOM_UNIFORMS) {
224 throw mxvk::Exception(std::format("VKSprite::setCustomUniforms supports at most {} values", MAX_CUSTOM_UNIFORMS));
225 }
226
227 extendedUBOData.custom_uniforms.fill(glm::vec4(0.0f));
228 for (std::size_t index = 0; index < values.size(); ++index) {
229 extendedUBOData.custom_uniforms[index / 4][index % 4] = values[index];
230 }
231 }
static constexpr std::size_t MAX_CUSTOM_UNIFORMS

◆ setDepthAttachmentFormat()

void mxvk::VK_Sprite::setDepthAttachmentFormat ( VkFormat format)
inline

Assign dynamic-rendering depth attachment format used to build pipelines.

Definition at line 223 of file mxvk_sprite.hpp.

223{ depthAttachmentFormat = format; }

◆ setDescriptorSetLayout()

void mxvk::VK_Sprite::setDescriptorSetLayout ( VkDescriptorSetLayout layout)
inline

Assign an external descriptor-set layout.

Definition at line 217 of file mxvk_sprite.hpp.

217{ descriptorSetLayout = layout; }

◆ setEffectsEnabled()

void mxvk::VK_Sprite::setEffectsEnabled ( bool enabled)
inline

Enable or disable the custom fragment shader effects.

Parameters
enabledtrue to enable effects.

Definition at line 182 of file mxvk_sprite.hpp.

182{ effectsEnabled = enabled; }

◆ setExternalTexture()

void mxvk::VK_Sprite::setExternalTexture ( VkImageView image_view,
int width,
int height )

Definition at line 2583 of file mxvk_sprite.cpp.

2583 {
2584 if (image_view == VK_NULL_HANDLE || width <= 0 || height <= 0) {
2585 throw mxvk::Exception("VKSprite::setExternalTexture received an invalid image view");
2586 }
2587 if (externalTexture && spriteImageView == image_view) {
2588 spriteWidth = width;
2589 spriteHeight = height;
2590 spriteLoaded = true;
2591 return;
2592 }
2593 auto cached_descriptor = externalDescriptorSets.find(image_view);
2594 descriptorSet = (cached_descriptor != externalDescriptorSets.end()) ? cached_descriptor->second : VK_NULL_HANDLE;
2595 descriptorSetPool = VK_NULL_HANDLE;
2596 if (!externalTexture) {
2597 destroyTextureDescriptorPools();
2598 } else if (extendedDescriptorPool != VK_NULL_HANDLE) {
2599 vkDeviceWaitIdle(device);
2600 vkDestroyDescriptorPool(device, extendedDescriptorPool, nullptr);
2601 extendedDescriptorPool = VK_NULL_HANDLE;
2602 extendedDescriptorSet = VK_NULL_HANDLE;
2603 }
2604 if (!externalTexture && spriteImageView != VK_NULL_HANDLE) {
2605 vkDestroyImageView(device, spriteImageView, nullptr);
2606 }
2607 if (!externalTexture && spriteImage != VK_NULL_HANDLE) {
2608 vkDestroyImage(device, spriteImage, nullptr);
2609 }
2610 if (!externalTexture && spriteImageMemory != VK_NULL_HANDLE) {
2611 vkFreeMemory(device, spriteImageMemory, nullptr);
2612 }
2613 spriteImageView = image_view;
2614 spriteImage = VK_NULL_HANDLE;
2615 spriteImageMemory = VK_NULL_HANDLE;
2616 externalTexture = true;
2617 spriteWidth = width;
2618 spriteHeight = height;
2619 spriteLoaded = true;
2620 }

◆ setFragmentShaderPath()

void mxvk::VK_Sprite::setFragmentShaderPath ( const std::string & path)

Replace the fragment shader path and rebuild the custom pipeline.

Definition at line 1462 of file mxvk_sprite.cpp.

1462 {
1463 if (path == fragmentShaderPath && fragmentShaderModule != VK_NULL_HANDLE) {
1464 return;
1465 }
1466
1467 if (customPipeline != VK_NULL_HANDLE) {
1468 vkDestroyPipeline(device, customPipeline, nullptr);
1469 customPipeline = VK_NULL_HANDLE;
1470 }
1471 if (customPipelineLayout != VK_NULL_HANDLE) {
1472 vkDestroyPipelineLayout(device, customPipelineLayout, nullptr);
1473 customPipelineLayout = VK_NULL_HANDLE;
1474 }
1475 if (fragmentShaderModule != VK_NULL_HANDLE) {
1476 vkDestroyShaderModule(device, fragmentShaderModule, nullptr);
1477 fragmentShaderModule = VK_NULL_HANDLE;
1478 }
1479
1480 fragmentShaderPath = path;
1481 hasCustomShader = false;
1482
1483 if (fragmentShaderPath.empty()) {
1484 return;
1485 }
1486
1487 const auto shaderCode = readShaderFile(fragmentShaderPath);
1488 fragmentShaderModule = mxvk::create_shader_module(device, shaderCode);
1489 hasCustomShader = true;
1490
1491 if (colorAttachmentFormat != VK_FORMAT_UNDEFINED && descriptorSetLayout != VK_NULL_HANDLE) {
1492 createCustomPipeline();
1493 }
1494 }

◆ setMouseState()

void mxvk::VK_Sprite::setMouseState ( float mx,
float my,
float pressed,
float reserved = 0.0f )

Upload mouse state to the extended UBO.

Parameters
mxMouse X (normalised or pixels).
myMouse Y.
pressedMouse button state.
reservedReserved channel.

Definition at line 210 of file mxvk_sprite.cpp.

210{ extendedUBOData.mouse = glm::vec4(mx, my, pressed, reserved); }

◆ setPipelineCache()

void mxvk::VK_Sprite::setPipelineCache ( VkPipelineCache cache)
inline

Use the shared pipeline cache for custom/instanced pipeline creation.

Definition at line 231 of file mxvk_sprite.hpp.

231{ pipelineCache = cache; }

◆ setRenderPass()

void mxvk::VK_Sprite::setRenderPass ( VkRenderPass rp)
inline

Assign the render pass used to build the custom pipeline.

Definition at line 219 of file mxvk_sprite.hpp.

219{ renderPass = rp; }

◆ setShaderParams()

void mxvk::VK_Sprite::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.

Parameters
p1,p2,p3,p4Parameter values packed into a vec4.

Definition at line 2581 of file mxvk_sprite.cpp.

2581{ shaderParams = glm::vec4(p1, p2, p3, p4); }

◆ setTextureFilter()

void mxvk::VK_Sprite::setTextureFilter ( VkFilter filter)

Select the hardware filter used when scaling this sprite.

Parameters
filterVK_FILTER_NEAREST for sharp pixels or VK_FILTER_LINEAR for smoothing.

Definition at line 64 of file mxvk_sprite.cpp.

64 {
65 if (filter != VK_FILTER_NEAREST && filter != VK_FILTER_LINEAR) {
66 throw mxvk::Exception("VKSprite::setTextureFilter supports only nearest or linear filtering");
67 }
68 if (textureFilter == filter) {
69 return;
70 }
71
72 textureFilter = filter;
73 if (spriteSampler != VK_NULL_HANDLE) {
74 destroyTextureDescriptorPools();
75 vkDestroySampler(device, spriteSampler, nullptr);
76 spriteSampler = VK_NULL_HANDLE;
77 createSampler();
78 }
79
80 std::cout << "mxvk: Sprite texture filter set to " << (textureFilter == VK_FILTER_NEAREST ? "nearest\n" : "linear\n");
81 }

◆ setUniform0()

void mxvk::VK_Sprite::setUniform0 ( float x,
float y,
float z,
float w )

Upload user uniform 0 to the extended UBO.

Parameters
x,y,z,wComponents.

Definition at line 212 of file mxvk_sprite.cpp.

212{ extendedUBOData.u0 = glm::vec4(x, y, z, w); }

◆ setUniform1()

void mxvk::VK_Sprite::setUniform1 ( float x,
float y,
float z,
float w )

Upload user uniform 1 to the extended UBO.

Parameters
x,y,z,wComponents.

Definition at line 214 of file mxvk_sprite.cpp.

214{ extendedUBOData.u1 = glm::vec4(x, y, z, w); }

◆ setUniform2()

void mxvk::VK_Sprite::setUniform2 ( float x,
float y,
float z,
float w )

Upload user uniform 2 to the extended UBO.

Parameters
x,y,z,wComponents.

Definition at line 216 of file mxvk_sprite.cpp.

216{ extendedUBOData.u2 = glm::vec4(x, y, z, w); }

◆ setUniform3()

void mxvk::VK_Sprite::setUniform3 ( float x,
float y,
float z,
float w )

Upload user uniform 3 to the extended UBO.

Parameters
x,y,z,wComponents.

Definition at line 218 of file mxvk_sprite.cpp.

218{ extendedUBOData.u3 = glm::vec4(x, y, z, w); }

◆ setVertexShaderPath()

void mxvk::VK_Sprite::setVertexShaderPath ( const std::string & path)
inline

Override the vertex shader path (used when rebuilding the pipeline).

Definition at line 239 of file mxvk_sprite.hpp.

239{ vertexShaderPath = path; }

◆ shareHistoryTexture()

void mxvk::VK_Sprite::shareHistoryTexture ( const VK_Sprite & source)

Bind another sprite's history array without taking ownership.

The source sprite must outlive this sprite. This is intended for post-processing passes which read one shared input history ring. No image, memory, or image view is allocated or freed by this sprite.

Parameters
sourceSprite which owns an enabled history texture.
Exceptions
mxvk::Exceptionwhen the source has no history texture or belongs to a different Vulkan device.

Definition at line 314 of file mxvk_sprite.cpp.

314 {
315 if (source.device != device) {
316 throw mxvk::Exception("VKSprite::shareHistoryTexture requires sprites on the same Vulkan device");
317 }
318 if (!source.historyTextureEnabled || source.historyImageView == VK_NULL_HANDLE || source.historyLayers == 0) {
319 throw mxvk::Exception("VKSprite::shareHistoryTexture source has no enabled history texture");
320 }
321 if (&source == this) {
322 throw mxvk::Exception("VKSprite::shareHistoryTexture cannot share a sprite with itself");
323 }
324
325 if (!extendedUBOEnabled) {
327 }
328
329 vkDeviceWaitIdle(device);
330 destroyHistoryTexture();
331 historyImageView = source.historyImageView;
332 historyImageFormat = source.historyImageFormat;
333 historyWidth = source.historyWidth;
334 historyHeight = source.historyHeight;
335 historyLayers = source.historyLayers;
336 historyHead = source.historyHead;
337 historyTextureEnabled = true;
338 historyTextureShared = true;
339 recreateExtendedDescriptorLayout();
340 }

◆ updateHistoryTexture()

void mxvk::VK_Sprite::updateHistoryTexture ( const void * pixels,
int width,
int height,
int pitch = 0 )

Upload one RGBA frame into the next history layer.

The write head advances after a successful upload. Input dimensions must match those passed to enableHistoryTexture().

Parameters
pixelsRGBA8 source pixels.
widthSource width in pixels.
heightSource height in pixels.
pitchSource row stride in bytes, or zero for tightly packed data.
Exceptions
mxvk::Exceptionfor null data, invalid dimensions, or an inactive cache.

Definition at line 342 of file mxvk_sprite.cpp.

342 {
343 if (historyImageFormat == VK_FORMAT_R16G16B16A16_SFLOAT) {
344 const int source_pitch = pitch > 0 ? pitch : width * 4;
345 if (pixels == nullptr || width <= 0 || height <= 0 || source_pitch < width * 4) {
346 throw mxvk::Exception("VKSprite::updateHistoryTexture received invalid RGBA8 pixels");
347 }
348 std::vector<uint16_t> converted(static_cast<std::size_t>(width) * height * 4U);
349 const auto *source = static_cast<const uint8_t *>(pixels);
350 for (int row = 0; row < height; ++row) {
351 const uint8_t *source_row = source + static_cast<std::size_t>(row) * source_pitch;
352 uint16_t *destination_row = converted.data() + static_cast<std::size_t>(row) * width * 4U;
353 for (int component = 0; component < width * 4; ++component) {
354 destination_row[component] = float_to_half(source_row[component] / 255.0F);
355 }
356 }
357 uploadHistoryTextureBytes(converted.data(), width, height, width * 8, 8);
358 return;
359 }
360 uploadHistoryTextureBytes(pixels, width, height, pitch, 4);
361 }
uint16_t float_to_half(float value) noexcept

◆ updateHistoryTextureRgba16()

void mxvk::VK_Sprite::updateHistoryTextureRgba16 ( const uint16_t * pixels,
int width,
int height,
int pitch = 0 )

Upload normalized RGBA16 pixels into an RGBA16F history layer.

Definition at line 363 of file mxvk_sprite.cpp.

363 {
364 if (historyImageFormat != VK_FORMAT_R16G16B16A16_SFLOAT) {
365 throw mxvk::Exception("VKSprite::updateHistoryTextureRgba16 requires RGBA16F history");
366 }
367 const int source_pitch = pitch > 0 ? pitch : width * 8;
368 if (pixels == nullptr || width <= 0 || height <= 0 || source_pitch < width * 8) {
369 throw mxvk::Exception("VKSprite::updateHistoryTextureRgba16 received invalid pixels");
370 }
371 std::vector<uint16_t> converted(static_cast<std::size_t>(width) * height * 4U);
372 const auto *source = reinterpret_cast<const uint8_t *>(pixels);
373 for (int row = 0; row < height; ++row) {
374 const auto *source_row = reinterpret_cast<const uint16_t *>(source + static_cast<std::size_t>(row) * source_pitch);
375 uint16_t *destination_row = converted.data() + static_cast<std::size_t>(row) * width * 4U;
376 for (int component = 0; component < width * 4; ++component) {
377 destination_row[component] = float_to_half(source_row[component] / 65535.0F);
378 }
379 }
380 uploadHistoryTextureBytes(converted.data(), width, height, width * 8, 8);
381 }

◆ updateSpectrumHistoryTexture()

void mxvk::VK_Sprite::updateSpectrumHistoryTexture ( const float * magnitudes,
uint32_t bins )

Upload one FFT spectrum into the next history layer.

Parameters
magnitudesPointer to bins frequency magnitudes.
binsNumber of values; must match the configured history.

Definition at line 617 of file mxvk_sprite.cpp.

617 {
618 if (!spectrumHistoryTextureEnabled || spectrumHistoryImage == VK_NULL_HANDLE) {
619 throw mxvk::Exception("VKSprite::updateSpectrumHistoryTexture called before enableSpectrumHistoryTexture");
620 }
621 if (magnitudes == nullptr) {
622 throw mxvk::Exception("VKSprite::updateSpectrumHistoryTexture called with null data");
623 }
624 if (bins != spectrumHistoryBins) {
625 throw mxvk::Exception("VKSprite::updateSpectrumHistoryTexture bin count does not match the history texture");
626 }
627
628 const VkDeviceSize imageSize = static_cast<VkDeviceSize>(bins) * sizeof(float);
629 createStagingResources(imageSize);
630 VK_CHECK_RESULT(vkWaitForFences(device, 1, &uploadFence, VK_TRUE, UINT64_MAX));
631 VK_CHECK_RESULT(vkResetFences(device, 1, &uploadFence));
632 memcpy(persistentStagingMapped, magnitudes, static_cast<std::size_t>(imageSize));
633
634 VK_CHECK_RESULT(vkResetCommandBuffer(uploadCmdBuffer, 0));
635 VkCommandBufferBeginInfo beginInfo{};
636 beginInfo.sType = VK_STRUCTURE_TYPE_COMMAND_BUFFER_BEGIN_INFO;
637 beginInfo.flags = VK_COMMAND_BUFFER_USAGE_ONE_TIME_SUBMIT_BIT;
638 VK_CHECK_RESULT(vkBeginCommandBuffer(uploadCmdBuffer, &beginInfo));
639
640 VkImageMemoryBarrier barrier{};
641 barrier.sType = VK_STRUCTURE_TYPE_IMAGE_MEMORY_BARRIER;
642 barrier.oldLayout = VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL;
643 barrier.newLayout = VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL;
644 barrier.srcQueueFamilyIndex = VK_QUEUE_FAMILY_IGNORED;
645 barrier.dstQueueFamilyIndex = VK_QUEUE_FAMILY_IGNORED;
646 barrier.image = spectrumHistoryImage;
647 barrier.subresourceRange.aspectMask = VK_IMAGE_ASPECT_COLOR_BIT;
648 barrier.subresourceRange.baseMipLevel = 0;
649 barrier.subresourceRange.levelCount = 1;
650 barrier.subresourceRange.baseArrayLayer = spectrumHistoryWriteIndex;
651 barrier.subresourceRange.layerCount = 1;
652 barrier.srcAccessMask = VK_ACCESS_SHADER_READ_BIT;
653 barrier.dstAccessMask = VK_ACCESS_TRANSFER_WRITE_BIT;
654 vkCmdPipelineBarrier(uploadCmdBuffer, VK_PIPELINE_STAGE_FRAGMENT_SHADER_BIT, VK_PIPELINE_STAGE_TRANSFER_BIT, 0, 0, nullptr, 0, nullptr, 1, &barrier);
655
656 VkBufferImageCopy region{};
657 region.imageSubresource.aspectMask = VK_IMAGE_ASPECT_COLOR_BIT;
658 region.imageSubresource.mipLevel = 0;
659 region.imageSubresource.baseArrayLayer = spectrumHistoryWriteIndex;
660 region.imageSubresource.layerCount = 1;
661 region.imageExtent = {bins, 1, 1};
662 vkCmdCopyBufferToImage(uploadCmdBuffer, persistentStagingBuffer, spectrumHistoryImage, VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL, 1, &region);
663
664 barrier.oldLayout = VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL;
665 barrier.newLayout = VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL;
666 barrier.srcAccessMask = VK_ACCESS_TRANSFER_WRITE_BIT;
667 barrier.dstAccessMask = VK_ACCESS_SHADER_READ_BIT;
668 vkCmdPipelineBarrier(uploadCmdBuffer, VK_PIPELINE_STAGE_TRANSFER_BIT, VK_PIPELINE_STAGE_FRAGMENT_SHADER_BIT, 0, 0, nullptr, 0, nullptr, 1, &barrier);
669
670 VK_CHECK_RESULT(vkEndCommandBuffer(uploadCmdBuffer));
671 VkSubmitInfo submitInfo{};
672 submitInfo.sType = VK_STRUCTURE_TYPE_SUBMIT_INFO;
673 submitInfo.commandBufferCount = 1;
674 submitInfo.pCommandBuffers = &uploadCmdBuffer;
675 VK_CHECK_RESULT(vkQueueSubmit(graphicsQueue, 1, &submitInfo, uploadFence));
676 VK_CHECK_RESULT(vkWaitForFences(device, 1, &uploadFence, VK_TRUE, UINT64_MAX));
677
678 spectrumHistoryHead = spectrumHistoryWriteIndex;
679 spectrumHistoryWriteIndex = (spectrumHistoryWriteIndex + 1) % spectrumHistoryLayers;
680 extendedUBOData.audio_history = glm::vec4(static_cast<float>(spectrumHistoryHead), static_cast<float>(spectrumHistoryLayers), static_cast<float>(spectrumHistoryBins), 0.0f);
681 }

◆ updateSpectrumTexture()

void mxvk::VK_Sprite::updateSpectrumTexture ( const float * magnitudes,
uint32_t bins )

Replace the current floating-point spectrum data.

Parameters
magnitudesPointer to bins frequency magnitudes.
binsNumber of values; must match enableSpectrumTexture().

Definition at line 501 of file mxvk_sprite.cpp.

501 {
502 if (!spectrumTextureEnabled || spectrumImage == VK_NULL_HANDLE) {
503 throw mxvk::Exception("VKSprite::updateSpectrumTexture called before enableSpectrumTexture");
504 }
505 if (magnitudes == nullptr) {
506 throw mxvk::Exception("VKSprite::updateSpectrumTexture called with null data");
507 }
508 if (bins != spectrumBins) {
509 throw mxvk::Exception("VKSprite::updateSpectrumTexture bin count does not match the spectrum texture");
510 }
511
512 const VkDeviceSize imageSize = static_cast<VkDeviceSize>(bins) * sizeof(float);
513 createStagingResources(imageSize);
514 VK_CHECK_RESULT(vkWaitForFences(device, 1, &uploadFence, VK_TRUE, UINT64_MAX));
515 VK_CHECK_RESULT(vkResetFences(device, 1, &uploadFence));
516 memcpy(persistentStagingMapped, magnitudes, static_cast<std::size_t>(imageSize));
517
518 VK_CHECK_RESULT(vkResetCommandBuffer(uploadCmdBuffer, 0));
519 VkCommandBufferBeginInfo beginInfo{};
520 beginInfo.sType = VK_STRUCTURE_TYPE_COMMAND_BUFFER_BEGIN_INFO;
521 beginInfo.flags = VK_COMMAND_BUFFER_USAGE_ONE_TIME_SUBMIT_BIT;
522 VK_CHECK_RESULT(vkBeginCommandBuffer(uploadCmdBuffer, &beginInfo));
523
524 VkImageMemoryBarrier barrier{};
525 barrier.sType = VK_STRUCTURE_TYPE_IMAGE_MEMORY_BARRIER;
526 barrier.oldLayout = VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL;
527 barrier.newLayout = VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL;
528 barrier.srcQueueFamilyIndex = VK_QUEUE_FAMILY_IGNORED;
529 barrier.dstQueueFamilyIndex = VK_QUEUE_FAMILY_IGNORED;
530 barrier.image = spectrumImage;
531 barrier.subresourceRange.aspectMask = VK_IMAGE_ASPECT_COLOR_BIT;
532 barrier.subresourceRange.baseMipLevel = 0;
533 barrier.subresourceRange.levelCount = 1;
534 barrier.subresourceRange.baseArrayLayer = 0;
535 barrier.subresourceRange.layerCount = 1;
536 barrier.srcAccessMask = VK_ACCESS_SHADER_READ_BIT;
537 barrier.dstAccessMask = VK_ACCESS_TRANSFER_WRITE_BIT;
538 vkCmdPipelineBarrier(uploadCmdBuffer, VK_PIPELINE_STAGE_FRAGMENT_SHADER_BIT, VK_PIPELINE_STAGE_TRANSFER_BIT, 0, 0, nullptr, 0, nullptr, 1, &barrier);
539
540 VkBufferImageCopy region{};
541 region.imageSubresource.aspectMask = VK_IMAGE_ASPECT_COLOR_BIT;
542 region.imageSubresource.mipLevel = 0;
543 region.imageSubresource.baseArrayLayer = 0;
544 region.imageSubresource.layerCount = 1;
545 region.imageExtent = {bins, 1, 1};
546 vkCmdCopyBufferToImage(uploadCmdBuffer, persistentStagingBuffer, spectrumImage, VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL, 1, &region);
547
548 barrier.oldLayout = VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL;
549 barrier.newLayout = VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL;
550 barrier.srcAccessMask = VK_ACCESS_TRANSFER_WRITE_BIT;
551 barrier.dstAccessMask = VK_ACCESS_SHADER_READ_BIT;
552 vkCmdPipelineBarrier(uploadCmdBuffer, VK_PIPELINE_STAGE_TRANSFER_BIT, VK_PIPELINE_STAGE_FRAGMENT_SHADER_BIT, 0, 0, nullptr, 0, nullptr, 1, &barrier);
553
554 VK_CHECK_RESULT(vkEndCommandBuffer(uploadCmdBuffer));
555 VkSubmitInfo submitInfo{};
556 submitInfo.sType = VK_STRUCTURE_TYPE_SUBMIT_INFO;
557 submitInfo.commandBufferCount = 1;
558 submitInfo.pCommandBuffers = &uploadCmdBuffer;
559 VK_CHECK_RESULT(vkQueueSubmit(graphicsQueue, 1, &submitInfo, uploadFence));
560 VK_CHECK_RESULT(vkWaitForFences(device, 1, &uploadFence, VK_TRUE, UINT64_MAX));
561 }

◆ updateTexture() [1/2]

void mxvk::VK_Sprite::updateTexture ( const void * pixels,
int width,
int height,
int pitch = 0 )

Replace the sprite texture from a raw pixel buffer.

Parameters
pixelsPointer to RGBA data.
widthBuffer width.
heightBuffer height.
pitchRow stride in bytes (0 = auto).

Definition at line 1787 of file mxvk_sprite.cpp.

1787 {
1788 if (!pixels) {
1789 throw mxvk::Exception("VKSprite::updateTexture called with null pixel data");
1790 }
1791 if (!spriteLoaded) {
1792 throw mxvk::Exception("VKSprite::updateTexture called before sprite was loaded");
1793 }
1794 if (width <= 0 || height <= 0) {
1795 throw mxvk::Exception("VKSprite::updateTexture invalid dimensions");
1796 }
1797 if (spriteImageFormat != VK_FORMAT_R8G8B8A8_UNORM || spriteBytesPerPixel != 4) {
1798 throw mxvk::Exception("VKSprite::updateTexture cannot update an RGBA16 sprite; use "
1799 "updateTextureRgba16");
1800 }
1801 int srcPitch = (pitch > 0) ? pitch : width * 4;
1802 if (width == spriteWidth && height == spriteHeight && srcPitch == width * 4) {
1803#ifdef MXVK_CUDA
1804 if (updateTextureCudaHost(pixels, static_cast<uint32_t>(width), static_cast<uint32_t>(height), static_cast<uint32_t>(srcPitch))) {
1805 return;
1806 }
1807#endif
1808 updateSpriteTexture(pixels, width, height);
1809 } else if (width == spriteWidth && height == spriteHeight) {
1810#ifdef MXVK_CUDA
1811 if (updateTextureCudaHost(pixels, static_cast<uint32_t>(width), static_cast<uint32_t>(height), static_cast<uint32_t>(srcPitch))) {
1812 return;
1813 }
1814#endif
1815 std::vector<uint8_t> packed(width * height * 4);
1816 const uint8_t *src = static_cast<const uint8_t *>(pixels);
1817 for (int row = 0; row < height; ++row) {
1818 memcpy(packed.data() + row * width * 4, src + row * srcPitch, width * 4);
1819 }
1820 updateSpriteTexture(packed.data(), width, height);
1821 } else {
1822 if (stagingResourcesCreated && uploadFence != VK_NULL_HANDLE) {
1823 vkWaitForFences(device, 1, &uploadFence, VK_TRUE, UINT64_MAX);
1824 }
1825#ifdef MXVK_CUDA
1826 destroyCudaInterop();
1827#endif
1828 destroyTextureDescriptorPools();
1829 if (spriteImageView != VK_NULL_HANDLE) {
1830 vkDestroyImageView(device, spriteImageView, nullptr);
1831 spriteImageView = VK_NULL_HANDLE;
1832 }
1833 if (spriteImage != VK_NULL_HANDLE) {
1834 vkDestroyImage(device, spriteImage, nullptr);
1835 spriteImage = VK_NULL_HANDLE;
1836 }
1837 if (spriteImageMemory != VK_NULL_HANDLE) {
1838 vkFreeMemory(device, spriteImageMemory, nullptr);
1839 spriteImageMemory = VK_NULL_HANDLE;
1840 }
1841 spriteWidth = width;
1842 spriteHeight = height;
1843 std::vector<uint8_t> packed;
1844 const void *texData = pixels;
1845 if (srcPitch != width * 4) {
1846 packed.resize(width * height * 4);
1847 const uint8_t *src = static_cast<const uint8_t *>(pixels);
1848 for (int row = 0; row < height; ++row) {
1849 memcpy(packed.data() + row * width * 4, src + row * srcPitch, width * 4);
1850 }
1851 texData = packed.data();
1852 }
1853 // Resize path: wrap raw pixels in a temporary SDL3 surface (no copy)
1854 SDL_Surface *tmpSurface = SDL_CreateSurfaceFrom(width, height, SDL_PIXELFORMAT_RGBA32, const_cast<void *>(texData), width * 4);
1855 if (!tmpSurface) {
1856 throw mxvk::Exception("VKSprite::updateTexture failed to create temp surface");
1857 }
1858 createSpriteTexture(tmpSurface);
1859 SDL_DestroySurface(tmpSurface);
1860 createDescriptorPool();
1861 }
1862 }

◆ updateTexture() [2/2]

void mxvk::VK_Sprite::updateTexture ( SDL_Surface * surface)

Replace the sprite texture from an SDL_Surface.

Parameters
surfaceNew surface (not consumed).

Definition at line 1740 of file mxvk_sprite.cpp.

1740 {
1741 if (!surface) {
1742 throw mxvk::Exception("VKSprite::updateTexture called with null surface");
1743 }
1744 if (!spriteLoaded) {
1745 throw mxvk::Exception("VKSprite::updateTexture called before sprite was loaded");
1746 }
1747 SDL_Surface *rgbaSurface = convertToRGBA(surface);
1748 if (!rgbaSurface) {
1749 throw mxvk::Exception("Failed to convert surface to RGBA in updateTexture");
1750 }
1751 if (rgbaSurface->w == spriteWidth && rgbaSurface->h == spriteHeight) {
1752#ifdef MXVK_CUDA
1753 if (updateTextureCudaHost(rgbaSurface->pixels, static_cast<uint32_t>(rgbaSurface->w), static_cast<uint32_t>(rgbaSurface->h), static_cast<uint32_t>(rgbaSurface->pitch))) {
1754 SDL_DestroySurface(rgbaSurface);
1755 return;
1756 }
1757#endif
1758 updateSpriteTexture(rgbaSurface->pixels, rgbaSurface->w, rgbaSurface->h);
1759 } else {
1760 if (stagingResourcesCreated && uploadFence != VK_NULL_HANDLE) {
1761 vkWaitForFences(device, 1, &uploadFence, VK_TRUE, UINT64_MAX);
1762 }
1763#ifdef MXVK_CUDA
1764 destroyCudaInterop();
1765#endif
1766 destroyTextureDescriptorPools();
1767 if (spriteImageView != VK_NULL_HANDLE) {
1768 vkDestroyImageView(device, spriteImageView, nullptr);
1769 spriteImageView = VK_NULL_HANDLE;
1770 }
1771 if (spriteImage != VK_NULL_HANDLE) {
1772 vkDestroyImage(device, spriteImage, nullptr);
1773 spriteImage = VK_NULL_HANDLE;
1774 }
1775 if (spriteImageMemory != VK_NULL_HANDLE) {
1776 vkFreeMemory(device, spriteImageMemory, nullptr);
1777 spriteImageMemory = VK_NULL_HANDLE;
1778 }
1779 spriteWidth = rgbaSurface->w;
1780 spriteHeight = rgbaSurface->h;
1781 createSpriteTexture(rgbaSurface);
1782 createDescriptorPool();
1783 }
1784 SDL_DestroySurface(rgbaSurface);
1785 }

◆ updateTextureRgba16()

void mxvk::VK_Sprite::updateTextureRgba16 ( const uint16_t * pixels,
int width,
int height,
int pitch = 0 )

Replace an RGBA16 UNORM sprite from native-endian uint16 data.

Parameters
pixelsPointer to four uint16 components per pixel.
widthBuffer width.
heightBuffer height.
pitchRow stride in bytes (0 = tightly packed).

Definition at line 1864 of file mxvk_sprite.cpp.

1864 {
1865 if (pixels == nullptr) {
1866 throw mxvk::Exception("VKSprite::updateTextureRgba16 called with null pixel data");
1867 }
1868 if (!spriteLoaded || spriteImageFormat != VK_FORMAT_R16G16B16A16_UNORM || spriteBytesPerPixel != 8) {
1869 throw mxvk::Exception("VKSprite::updateTextureRgba16 requires an RGBA16 sprite");
1870 }
1871 if (width != spriteWidth || height != spriteHeight || width <= 0 || height <= 0) {
1872 throw mxvk::Exception("VKSprite::updateTextureRgba16 dimensions do not match");
1873 }
1874
1875 const int rowBytes = width * 8;
1876 const int sourcePitch = pitch > 0 ? pitch : rowBytes;
1877 if (sourcePitch < rowBytes) {
1878 throw mxvk::Exception("VKSprite::updateTextureRgba16 pitch is too small");
1879 }
1880 if (sourcePitch == rowBytes) {
1881 updateSpriteTexture(pixels, static_cast<uint32_t>(width), static_cast<uint32_t>(height));
1882 return;
1883 }
1884
1885 std::vector<uint16_t> packed(static_cast<size_t>(width) * static_cast<size_t>(height) * 4U);
1886 const auto *source = reinterpret_cast<const uint8_t *>(pixels);
1887 auto *destination = reinterpret_cast<uint8_t *>(packed.data());
1888 for (int row = 0; row < height; ++row) {
1889 std::memcpy(destination + static_cast<size_t>(row) * rowBytes, source + static_cast<size_t>(row) * sourcePitch, static_cast<size_t>(rowBytes));
1890 }
1891 updateSpriteTexture(packed.data(), static_cast<uint32_t>(width), static_cast<uint32_t>(height));
1892 }

Member Data Documentation

◆ MAX_CUSTOM_UNIFORMS

std::size_t mxvk::VK_Sprite::MAX_CUSTOM_UNIFORMS = 64
staticconstexpr

Definition at line 58 of file mxvk_sprite.hpp.

◆ spriteSampler

VkSampler mxvk::VK_Sprite::spriteSampler = VK_NULL_HANDLE

Texture sampler.

Definition at line 271 of file mxvk_sprite.hpp.


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