MXVK Vulkan Framework 0.24.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 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 enableExtendedUBO ()
 Allocate and initialise the extended uniform buffer object.
void enableInstancing (uint32_t maxInstances, const std::string &instanceVertShaderPath, const std::string &instanceFragShaderPath)
 Enable GPU instancing for this sprite type.
bool getEffectsEnabled () const
int getHeight () const
VkPipeline getPipeline () const
VkPipelineLayout getPipelineLayout () const
int getWidth () 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_Spriteoperator= (const VK_Sprite &)=delete
VK_Spriteoperator= (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 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 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 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.
 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.

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 18 of file mxvk_sprite.cpp.

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

◆ ~VK_Sprite()

mxvk::VK_Sprite::~VK_Sprite ( )

Destructor — frees all Vulkan resources.

Definition at line 58 of file mxvk_sprite.cpp.

58 {
59 vkDeviceWaitIdle(device);
60 drawQueue.clear();
61
62 destroyStagingResources();
63#ifdef MXVK_CUDA
64 destroyCudaInterop();
65#endif
66 if (quadVertexBuffer != VK_NULL_HANDLE) {
67 std::cout << "vk: destroying sprite quad vertex buffer\n";
68 vkDestroyBuffer(device, quadVertexBuffer, nullptr);
69 vkFreeMemory(device, quadVertexBufferMemory, nullptr);
70 }
71 if (quadIndexBuffer != VK_NULL_HANDLE) {
72 std::cout << "vk: destroying sprite quad index buffer\n";
73 vkDestroyBuffer(device, quadIndexBuffer, nullptr);
74 vkFreeMemory(device, quadIndexBufferMemory, nullptr);
75 }
76
77 destroyTextureDescriptorPools();
78
79 if (spriteSampler != VK_NULL_HANDLE) {
80 std::cout << "vk: destroying sprite sampler\n";
81 vkDestroySampler(device, spriteSampler, nullptr);
82 }
83
84 if (!externalTexture && spriteImageView != VK_NULL_HANDLE) {
85 std::cout << "vk: destroying sprite image view\n";
86 vkDestroyImageView(device, spriteImageView, nullptr);
87 }
88
89 if (!externalTexture && spriteImage != VK_NULL_HANDLE) {
90 std::cout << "vk: destroying sprite image\n";
91 vkDestroyImage(device, spriteImage, nullptr);
92 vkFreeMemory(device, spriteImageMemory, nullptr);
93 }
94
95 if (fragmentShaderModule != VK_NULL_HANDLE) {
96 vkDestroyShaderModule(device, fragmentShaderModule, nullptr);
97 }
98
99 if (customPipeline != VK_NULL_HANDLE) {
100 std::cout << "vk: destroying sprite custom pipeline\n";
101 vkDestroyPipeline(device, customPipeline, nullptr);
102 }
103
104 if (customPipelineLayout != VK_NULL_HANDLE) {
105 std::cout << "vk: destroying sprite custom pipeline layout\n";
106 vkDestroyPipelineLayout(device, customPipelineLayout, nullptr);
107 }
108
109 destroyExtendedUBO();
110 destroyInstanceResources();
111 }
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 1712 of file mxvk_sprite.cpp.

1712 {
1713 if (!externalTexture && externalDescriptorSets.empty()) {
1714 return;
1715 }
1716 destroyTextureDescriptorPools();
1717 }

◆ clearQueue()

void mxvk::VK_Sprite::clearQueue ( )

Discard all pending draw commands without rendering.

Definition at line 1818 of file mxvk_sprite.cpp.

1818 {
1819 drawQueue.clear();
1820 }

◆ 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 911 of file mxvk_sprite.cpp.

911 {
912 if (width <= 0 || height <= 0) {
913 throw mxvk::Exception("VKSprite::createEmptySprite invalid dimensions");
914 }
915 if (spriteLoaded || spriteImage != VK_NULL_HANDLE || fragmentShaderModule != VK_NULL_HANDLE) {
916 destroySpriteResources();
917 }
918 spriteWidth = width;
919 spriteHeight = height;
920
921 if (!vertexShaderPath.empty()) {
922 setVertexShaderPath(vertexShaderPath);
923 }
924
925#ifdef MXVK_CUDA
926 try {
927 createCudaExportableImage(width, height, spriteImage, spriteImageMemory);
928 } catch (const std::exception &ex) {
929 std::cout << std::format("mxvk: CUDA exportable sprite image unavailable: {}; using standard Vulkan image\n", ex.what());
930 createImage(width, height, VK_FORMAT_R8G8B8A8_UNORM, VK_IMAGE_TILING_OPTIMAL,
931 VK_IMAGE_USAGE_TRANSFER_DST_BIT | VK_IMAGE_USAGE_SAMPLED_BIT,
932 VK_MEMORY_PROPERTY_DEVICE_LOCAL_BIT, spriteImage, spriteImageMemory);
933 }
934#else
935 createImage(width, height, VK_FORMAT_R8G8B8A8_UNORM, VK_IMAGE_TILING_OPTIMAL,
936 VK_IMAGE_USAGE_TRANSFER_DST_BIT | VK_IMAGE_USAGE_SAMPLED_BIT,
937 VK_MEMORY_PROPERTY_DEVICE_LOCAL_BIT, spriteImage, spriteImageMemory);
938#endif
939
940 VkBuffer stagingBuffer = VK_NULL_HANDLE;
941 VkDeviceMemory stagingMemory = VK_NULL_HANDLE;
942 VkDeviceSize imageSize = static_cast<VkDeviceSize>(width) * height * 4;
943
944 createBuffer(imageSize, VK_BUFFER_USAGE_TRANSFER_SRC_BIT,
945 VK_MEMORY_PROPERTY_HOST_VISIBLE_BIT | VK_MEMORY_PROPERTY_HOST_COHERENT_BIT,
946 stagingBuffer, stagingMemory);
947
948 void *data;
949 VK_CHECK_RESULT(vkMapMemory(device, stagingMemory, 0, imageSize, 0, &data));
950 memset(data, 0, imageSize);
951 vkUnmapMemory(device, stagingMemory);
952
953 transitionImageLayout(spriteImage, VK_IMAGE_LAYOUT_UNDEFINED, VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL);
954 copyBufferToImage(stagingBuffer, spriteImage, width, height);
955 transitionImageLayout(spriteImage, VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL, VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL);
956#ifdef MXVK_CUDA
957 cudaImageLayout = VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL;
958#endif
959
960 vkDestroyBuffer(device, stagingBuffer, nullptr);
961 vkFreeMemory(device, stagingMemory, nullptr);
962
963 spriteImageView = createImageView(spriteImage, VK_FORMAT_R8G8B8A8_UNORM);
964 createSampler();
965 createQuadBuffer();
966 createDescriptorPool();
967
968 createStagingResources(imageSize);
969
970 if (!fragmentShaderPath.empty()) {
971 auto shaderCode = readShaderFile(fragmentShaderPath);
972 fragmentShaderModule = mxvk::create_shader_module(device, shaderCode);
973 hasCustomShader = true;
974 this->fragmentShaderPath = fragmentShaderPath;
975
976 if (colorAttachmentFormat != VK_FORMAT_UNDEFINED && descriptorSetLayout != VK_NULL_HANDLE) {
977 createCustomPipeline();
978 }
979 }
980
981 spriteLoaded = true;
982 std::cout << std::format("mxvk: Created empty sprite: {}x{}\n", spriteWidth, spriteHeight);
983 }
void setVertexShaderPath(const std::string &path)
Override the vertex shader path (used when rebuilding the pipeline).
#define VK_CHECK_RESULT(f)
VkShaderModule create_shader_module(VkDevice device, const std::vector< char > &spv_bytes)
Create a shader module from SPIR-V bytecode.

◆ 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 1641 of file mxvk_sprite.cpp.

1641 {
1642 drawSpriteRect(x, y, spriteWidth, spriteHeight);
1643 }
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 1645 of file mxvk_sprite.cpp.

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

◆ 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 1649 of file mxvk_sprite.cpp.

1649 {
1650 if (!spriteLoaded) {
1651 throw mxvk::Exception("VKSprite::drawSprite called before sprite was loaded");
1652 }
1653
1654 drawQueue.push_back({static_cast<float>(x), static_cast<float>(y),
1655 static_cast<float>(static_cast<int>(spriteWidth * scaleX)),
1656 static_cast<float>(static_cast<int>(spriteHeight * scaleY)),
1657 rotation, shaderParams});
1658 }

◆ 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 1660 of file mxvk_sprite.cpp.

1660 {
1661 if (!spriteLoaded) {
1662 throw mxvk::Exception("VKSprite::drawSpriteRect called before sprite was loaded");
1663 }
1664
1665 drawQueue.push_back({static_cast<float>(x), static_cast<float>(y),
1666 static_cast<float>(w), static_cast<float>(h), 0.0f, shaderParams});
1667 }

◆ enableExtendedUBO()

void mxvk::VK_Sprite::enableExtendedUBO ( )

Allocate and initialise the extended uniform buffer object.

Definition at line 165 of file mxvk_sprite.cpp.

165 {
166 if (extendedUBOEnabled)
167 return;
168 extendedUBOEnabled = true;
169 createExtendedUBO();
170 createExtendedDescriptorSetLayout();
172 }
void rebuildPipeline()
Destroy and recreate the custom graphics pipeline.

◆ 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 453 of file mxvk_sprite.cpp.

455 {
456 if (colorAttachmentFormat == VK_FORMAT_UNDEFINED || descriptorSetLayout == VK_NULL_HANDLE) {
457 throw mxvk::Exception("VKSprite::enableInstancing called before color format/descriptorSetLayout set");
458 }
459 ensureInstanceBuffer(maxInstances);
460 createQuadBuffer();
461 instanceVertPath = instanceVertShaderPath;
462 instanceFragPath = instanceFragShaderPath;
463 createInstancedPipeline(instanceVertShaderPath, instanceFragShaderPath);
464 instancingEnabled = true;
465 std::cout << std::format("mxvk: Instancing enabled (max {} instances)\n", maxInstances);
466 }

◆ getEffectsEnabled()

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

Definition at line 167 of file mxvk_sprite.hpp.

167{ return effectsEnabled; }

◆ getHeight()

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

Definition at line 191 of file mxvk_sprite.hpp.

191{ return spriteHeight; }

◆ getPipeline()

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

Definition at line 229 of file mxvk_sprite.hpp.

229{ return customPipeline; }

◆ getPipelineLayout()

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

Definition at line 231 of file mxvk_sprite.hpp.

231{ return customPipelineLayout; }

◆ getWidth()

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

Definition at line 189 of file mxvk_sprite.hpp.

189{ return spriteWidth; }

◆ hasOwnPipeline()

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

Definition at line 227 of file mxvk_sprite.hpp.

227{ return customPipeline != VK_NULL_HANDLE; }

◆ isExtendedUBOEnabled()

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

Definition at line 257 of file mxvk_sprite.hpp.

257{ return extendedUBOEnabled; }

◆ isInstancingEnabled()

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

Definition at line 251 of file mxvk_sprite.hpp.

251{ 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 870 of file mxvk_sprite.cpp.

870 {
871 SDL_Surface *surface = mxvk::LoadPNG(pngPath.c_str());
872 if (!surface) {
873 throw mxvk::Exception("Failed to load sprite image: " + pngPath);
874 }
875 loadSprite(surface, fragmentShaderPath);
876 SDL_DestroySurface(surface);
877 std::cout << std::format("mxvk: Loaded PNG: {}\n", pngPath);
878 }
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:103

◆ 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 880 of file mxvk_sprite.cpp.

880 {
881 if (!surface) {
882 throw mxvk::Exception("VKSprite::loadSprite called with null surface");
883 }
884 if (spriteLoaded || spriteImage != VK_NULL_HANDLE || fragmentShaderModule != VK_NULL_HANDLE) {
885 destroySpriteResources();
886 }
887 SDL_Surface *rgbaSurface = convertToRGBA(surface);
888 if (!rgbaSurface) {
889 throw mxvk::Exception("Failed to convert sprite surface to RGBA");
890 }
891 spriteWidth = rgbaSurface->w;
892 spriteHeight = rgbaSurface->h;
893 createSpriteTexture(rgbaSurface);
894 SDL_DestroySurface(rgbaSurface);
895 createSampler();
896 createQuadBuffer();
897 if (!fragmentShaderPath.empty()) {
898 auto shaderCode = readShaderFile(fragmentShaderPath);
899 fragmentShaderModule = mxvk::create_shader_module(device, shaderCode);
900 hasCustomShader = true;
901 this->fragmentShaderPath = fragmentShaderPath;
902
903 if (colorAttachmentFormat != VK_FORMAT_UNDEFINED && descriptorSetLayout != VK_NULL_HANDLE) {
904 createCustomPipeline();
905 }
906 }
907 spriteLoaded = true;
908 std::cout << std::format("mxvk: Loaded surface texture: {}x{}\n", spriteWidth, spriteHeight);
909 }

◆ 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 1719 of file mxvk_sprite.cpp.

1719 {
1720#ifdef MXVK_CUDA
1721 recordCudaReadyBarrier(cmdBuffer);
1722#endif
1723 }

◆ rebuildInstancedPipeline()

void mxvk::VK_Sprite::rebuildInstancedPipeline ( )

Destroy and recreate the instanced graphics pipeline.

Definition at line 831 of file mxvk_sprite.cpp.

831 {
832 if (!instancingEnabled || instanceVertPath.empty() || instanceFragPath.empty())
833 return;
834 createInstancedPipeline(instanceVertPath, instanceFragPath);
835 }

◆ rebuildPipeline()

void mxvk::VK_Sprite::rebuildPipeline ( )

Destroy and recreate the custom graphics pipeline.

Definition at line 790 of file mxvk_sprite.cpp.

790 {
791 if (!hasCustomShader || fragmentShaderModule == VK_NULL_HANDLE)
792 return;
793 createCustomPipeline();
794 std::cout << "mxvk: Pipeline rebuilt\n";
795 }

◆ 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 23 of file mxvk_sprite.cpp.

23 {
24 destroyStagingResources();
25 }

◆ 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 1725 of file mxvk_sprite.cpp.

1726 {
1727 if (drawQueue.empty() || !spriteLoaded || !quadBufferCreated) {
1728 return;
1729 }
1730 if (descriptorSet == VK_NULL_HANDLE) {
1731 descriptorSet = createDescriptorSet(spriteImageView);
1732 if (externalTexture) {
1733 externalDescriptorSets[spriteImageView] = descriptorSet;
1734 }
1735 }
1736
1737 if (instancingEnabled && instancedPipeline != VK_NULL_HANDLE && instanceBuffer != VK_NULL_HANDLE) {
1738 uint32_t instanceCount = static_cast<uint32_t>(drawQueue.size());
1739
1740 if (instanceCount > instanceBufferCapacity) {
1741 ensureInstanceBuffer(instanceCount * 2);
1742 }
1743
1744 SpriteInstanceData *dst = static_cast<SpriteInstanceData *>(instanceBufferMapped);
1745 for (uint32_t i = 0; i < instanceCount; ++i) {
1746 const auto &cmd = drawQueue[i];
1747 dst[i].posX = cmd.x;
1748 dst[i].posY = cmd.y;
1749 dst[i].sizeW = cmd.w;
1750 dst[i].sizeH = cmd.h;
1751 dst[i].params[0] = cmd.params.x;
1752 dst[i].params[1] = cmd.params.y;
1753 dst[i].params[2] = cmd.params.z;
1754 dst[i].params[3] = cmd.params.w;
1755 }
1756
1757 vkCmdBindPipeline(cmdBuffer, VK_PIPELINE_BIND_POINT_GRAPHICS, instancedPipeline);
1758 vkCmdBindDescriptorSets(cmdBuffer, VK_PIPELINE_BIND_POINT_GRAPHICS, instancedPipelineLayout,
1759 0, 1, &descriptorSet, 0, nullptr);
1760
1761 VkBuffer buffers[] = {quadVertexBuffer, instanceBuffer};
1762 VkDeviceSize bufOffsets[] = {0, 0};
1763 vkCmdBindVertexBuffers(cmdBuffer, 0, 2, buffers, bufOffsets);
1764 vkCmdBindIndexBuffer(cmdBuffer, quadIndexBuffer, 0, VK_INDEX_TYPE_UINT16);
1765
1766 float screenSize[2] = {static_cast<float>(screenWidth), static_cast<float>(screenHeight)};
1767 vkCmdPushConstants(cmdBuffer, instancedPipelineLayout, VK_SHADER_STAGE_VERTEX_BIT,
1768 0, sizeof(screenSize), screenSize);
1769
1770 vkCmdDrawIndexed(cmdBuffer, 6, instanceCount, 0, 0, 0);
1771 return;
1772 }
1773
1774 VkPipelineLayout layoutToUse = (customPipeline != VK_NULL_HANDLE) ? customPipelineLayout : pipelineLayout;
1775 if (customPipeline != VK_NULL_HANDLE) {
1776 vkCmdBindPipeline(cmdBuffer, VK_PIPELINE_BIND_POINT_GRAPHICS, customPipeline);
1777 }
1778
1779 // When extended UBO is enabled, update UBO and bind extended descriptor set
1780 if (extendedUBOEnabled && customPipeline != VK_NULL_HANDLE) {
1781 updateExtendedUBO();
1782 if (extendedDescriptorSet == VK_NULL_HANDLE) {
1783 createExtendedDescriptorSet();
1784 }
1785 vkCmdBindDescriptorSets(cmdBuffer, VK_PIPELINE_BIND_POINT_GRAPHICS, layoutToUse,
1786 0, 1, &extendedDescriptorSet, 0, nullptr);
1787 } else {
1788 vkCmdBindDescriptorSets(cmdBuffer, VK_PIPELINE_BIND_POINT_GRAPHICS, layoutToUse,
1789 0, 1, &descriptorSet, 0, nullptr);
1790 }
1791
1792 VkBuffer vertexBuffers[] = {quadVertexBuffer};
1793 VkDeviceSize offsets[] = {0};
1794 vkCmdBindVertexBuffers(cmdBuffer, 0, 1, vertexBuffers, offsets);
1795 vkCmdBindIndexBuffer(cmdBuffer, quadIndexBuffer, 0, VK_INDEX_TYPE_UINT16);
1796
1797 for (const auto &cmd : drawQueue) {
1798 struct SpritePushConstants {
1799 float screenWidth;
1800 float screenHeight;
1801 float spritePosX;
1802 float spritePosY;
1803 float spriteSizeW;
1804 float spriteSizeH;
1805 float effectsOn;
1806 float padding2;
1807 float params[4];
1808 } pc{
1809 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}};
1810
1811 vkCmdPushConstants(cmdBuffer, layoutToUse, VK_SHADER_STAGE_VERTEX_BIT | VK_SHADER_STAGE_FRAGMENT_BIT,
1812 0, sizeof(SpritePushConstants), &pc);
1813
1814 vkCmdDrawIndexed(cmdBuffer, 6, 1, 0, 0, 0);
1815 }
1816 }

◆ setColorAttachmentFormat()

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

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

Definition at line 204 of file mxvk_sprite.hpp.

204{ 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 27 of file mxvk_sprite.cpp.

27 {
28 if (pool == commandPool) {
29 return;
30 }
31
32 // uploadCmdBuffer is allocated from commandPool, so it must be released
33 // before switching to another pool.
34 destroyStagingResources();
35 commandPool = pool;
36 }

◆ setDepthAttachmentFormat()

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

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

Definition at line 206 of file mxvk_sprite.hpp.

206{ depthAttachmentFormat = format; }

◆ setDescriptorSetLayout()

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

Assign an external descriptor-set layout.

Definition at line 200 of file mxvk_sprite.hpp.

200{ 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 164 of file mxvk_sprite.hpp.

164{ effectsEnabled = enabled; }

◆ setExternalTexture()

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

Definition at line 1673 of file mxvk_sprite.cpp.

1673 {
1674 if (image_view == VK_NULL_HANDLE || width <= 0 || height <= 0) {
1675 throw mxvk::Exception("VKSprite::setExternalTexture received an invalid image view");
1676 }
1677 if (externalTexture && spriteImageView == image_view) {
1678 spriteWidth = width;
1679 spriteHeight = height;
1680 spriteLoaded = true;
1681 return;
1682 }
1683 auto cached_descriptor = externalDescriptorSets.find(image_view);
1684 descriptorSet = (cached_descriptor != externalDescriptorSets.end()) ? cached_descriptor->second : VK_NULL_HANDLE;
1685 descriptorSetPool = VK_NULL_HANDLE;
1686 if (!externalTexture) {
1687 destroyTextureDescriptorPools();
1688 } else if (extendedDescriptorPool != VK_NULL_HANDLE) {
1689 vkDeviceWaitIdle(device);
1690 vkDestroyDescriptorPool(device, extendedDescriptorPool, nullptr);
1691 extendedDescriptorPool = VK_NULL_HANDLE;
1692 extendedDescriptorSet = VK_NULL_HANDLE;
1693 }
1694 if (!externalTexture && spriteImageView != VK_NULL_HANDLE) {
1695 vkDestroyImageView(device, spriteImageView, nullptr);
1696 }
1697 if (!externalTexture && spriteImage != VK_NULL_HANDLE) {
1698 vkDestroyImage(device, spriteImage, nullptr);
1699 }
1700 if (!externalTexture && spriteImageMemory != VK_NULL_HANDLE) {
1701 vkFreeMemory(device, spriteImageMemory, nullptr);
1702 }
1703 spriteImageView = image_view;
1704 spriteImage = VK_NULL_HANDLE;
1705 spriteImageMemory = VK_NULL_HANDLE;
1706 externalTexture = true;
1707 spriteWidth = width;
1708 spriteHeight = height;
1709 spriteLoaded = true;
1710 }

◆ setFragmentShaderPath()

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

Replace the fragment shader path and rebuild the custom pipeline.

Definition at line 797 of file mxvk_sprite.cpp.

797 {
798 if (path == fragmentShaderPath && fragmentShaderModule != VK_NULL_HANDLE) {
799 return;
800 }
801
802 if (customPipeline != VK_NULL_HANDLE) {
803 vkDestroyPipeline(device, customPipeline, nullptr);
804 customPipeline = VK_NULL_HANDLE;
805 }
806 if (customPipelineLayout != VK_NULL_HANDLE) {
807 vkDestroyPipelineLayout(device, customPipelineLayout, nullptr);
808 customPipelineLayout = VK_NULL_HANDLE;
809 }
810 if (fragmentShaderModule != VK_NULL_HANDLE) {
811 vkDestroyShaderModule(device, fragmentShaderModule, nullptr);
812 fragmentShaderModule = VK_NULL_HANDLE;
813 }
814
815 fragmentShaderPath = path;
816 hasCustomShader = false;
817
818 if (fragmentShaderPath.empty()) {
819 return;
820 }
821
822 const auto shaderCode = readShaderFile(fragmentShaderPath);
823 fragmentShaderModule = mxvk::create_shader_module(device, shaderCode);
824 hasCustomShader = true;
825
826 if (colorAttachmentFormat != VK_FORMAT_UNDEFINED && descriptorSetLayout != VK_NULL_HANDLE) {
827 createCustomPipeline();
828 }
829 }

◆ 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 174 of file mxvk_sprite.cpp.

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

◆ setPipelineCache()

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

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

Definition at line 214 of file mxvk_sprite.hpp.

214{ pipelineCache = cache; }

◆ setRenderPass()

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

Assign the render pass used to build the custom pipeline.

Definition at line 202 of file mxvk_sprite.hpp.

202{ 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 1669 of file mxvk_sprite.cpp.

1669 {
1670 shaderParams = glm::vec4(p1, p2, p3, p4);
1671 }

◆ 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 38 of file mxvk_sprite.cpp.

38 {
39 if (filter != VK_FILTER_NEAREST && filter != VK_FILTER_LINEAR) {
40 throw mxvk::Exception("VKSprite::setTextureFilter supports only nearest or linear filtering");
41 }
42 if (textureFilter == filter) {
43 return;
44 }
45
46 textureFilter = filter;
47 if (spriteSampler != VK_NULL_HANDLE) {
48 destroyTextureDescriptorPools();
49 vkDestroySampler(device, spriteSampler, nullptr);
50 spriteSampler = VK_NULL_HANDLE;
51 createSampler();
52 }
53
54 std::cout << "mxvk: Sprite texture filter set to "
55 << (textureFilter == VK_FILTER_NEAREST ? "nearest\n" : "linear\n");
56 }

◆ 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 178 of file mxvk_sprite.cpp.

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

◆ 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 182 of file mxvk_sprite.cpp.

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

◆ 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 186 of file mxvk_sprite.cpp.

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

◆ 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 190 of file mxvk_sprite.cpp.

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

◆ setVertexShaderPath()

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

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

Definition at line 222 of file mxvk_sprite.hpp.

222{ vertexShaderPath = path; }

◆ 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 1033 of file mxvk_sprite.cpp.

1033 {
1034 if (!pixels) {
1035 throw mxvk::Exception("VKSprite::updateTexture called with null pixel data");
1036 }
1037 if (!spriteLoaded) {
1038 throw mxvk::Exception("VKSprite::updateTexture called before sprite was loaded");
1039 }
1040 if (width <= 0 || height <= 0) {
1041 throw mxvk::Exception("VKSprite::updateTexture invalid dimensions");
1042 }
1043 int srcPitch = (pitch > 0) ? pitch : width * 4;
1044 if (width == spriteWidth && height == spriteHeight && srcPitch == width * 4) {
1045#ifdef MXVK_CUDA
1046 if (updateTextureCudaHost(pixels, static_cast<uint32_t>(width), static_cast<uint32_t>(height), static_cast<uint32_t>(srcPitch))) {
1047 return;
1048 }
1049#endif
1050 updateSpriteTexture(pixels, width, height);
1051 } else if (width == spriteWidth && height == spriteHeight) {
1052#ifdef MXVK_CUDA
1053 if (updateTextureCudaHost(pixels, static_cast<uint32_t>(width), static_cast<uint32_t>(height), static_cast<uint32_t>(srcPitch))) {
1054 return;
1055 }
1056#endif
1057 std::vector<uint8_t> packed(width * height * 4);
1058 const uint8_t *src = static_cast<const uint8_t *>(pixels);
1059 for (int row = 0; row < height; ++row) {
1060 memcpy(packed.data() + row * width * 4, src + row * srcPitch, width * 4);
1061 }
1062 updateSpriteTexture(packed.data(), width, height);
1063 } else {
1064 if (stagingResourcesCreated && uploadFence != VK_NULL_HANDLE) {
1065 vkWaitForFences(device, 1, &uploadFence, VK_TRUE, UINT64_MAX);
1066 }
1067#ifdef MXVK_CUDA
1068 destroyCudaInterop();
1069#endif
1070 destroyTextureDescriptorPools();
1071 if (spriteImageView != VK_NULL_HANDLE) {
1072 vkDestroyImageView(device, spriteImageView, nullptr);
1073 spriteImageView = VK_NULL_HANDLE;
1074 }
1075 if (spriteImage != VK_NULL_HANDLE) {
1076 vkDestroyImage(device, spriteImage, nullptr);
1077 spriteImage = VK_NULL_HANDLE;
1078 }
1079 if (spriteImageMemory != VK_NULL_HANDLE) {
1080 vkFreeMemory(device, spriteImageMemory, nullptr);
1081 spriteImageMemory = VK_NULL_HANDLE;
1082 }
1083 spriteWidth = width;
1084 spriteHeight = height;
1085 std::vector<uint8_t> packed;
1086 const void *texData = pixels;
1087 if (srcPitch != width * 4) {
1088 packed.resize(width * height * 4);
1089 const uint8_t *src = static_cast<const uint8_t *>(pixels);
1090 for (int row = 0; row < height; ++row) {
1091 memcpy(packed.data() + row * width * 4, src + row * srcPitch, width * 4);
1092 }
1093 texData = packed.data();
1094 }
1095 // Resize path: wrap raw pixels in a temporary SDL3 surface (no copy)
1096 SDL_Surface *tmpSurface = SDL_CreateSurfaceFrom(
1097 width, height, SDL_PIXELFORMAT_RGBA32,
1098 const_cast<void *>(texData), width * 4);
1099 if (!tmpSurface) {
1100 throw mxvk::Exception("VKSprite::updateTexture failed to create temp surface");
1101 }
1102 createSpriteTexture(tmpSurface);
1103 SDL_DestroySurface(tmpSurface);
1104 createDescriptorPool();
1105 }
1106 }

◆ 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 985 of file mxvk_sprite.cpp.

985 {
986 if (!surface) {
987 throw mxvk::Exception("VKSprite::updateTexture called with null surface");
988 }
989 if (!spriteLoaded) {
990 throw mxvk::Exception("VKSprite::updateTexture called before sprite was loaded");
991 }
992 SDL_Surface *rgbaSurface = convertToRGBA(surface);
993 if (!rgbaSurface) {
994 throw mxvk::Exception("Failed to convert surface to RGBA in updateTexture");
995 }
996 if (rgbaSurface->w == spriteWidth && rgbaSurface->h == spriteHeight) {
997#ifdef MXVK_CUDA
998 if (updateTextureCudaHost(rgbaSurface->pixels, static_cast<uint32_t>(rgbaSurface->w), static_cast<uint32_t>(rgbaSurface->h),
999 static_cast<uint32_t>(rgbaSurface->pitch))) {
1000 SDL_DestroySurface(rgbaSurface);
1001 return;
1002 }
1003#endif
1004 updateSpriteTexture(rgbaSurface->pixels, rgbaSurface->w, rgbaSurface->h);
1005 } else {
1006 if (stagingResourcesCreated && uploadFence != VK_NULL_HANDLE) {
1007 vkWaitForFences(device, 1, &uploadFence, VK_TRUE, UINT64_MAX);
1008 }
1009#ifdef MXVK_CUDA
1010 destroyCudaInterop();
1011#endif
1012 destroyTextureDescriptorPools();
1013 if (spriteImageView != VK_NULL_HANDLE) {
1014 vkDestroyImageView(device, spriteImageView, nullptr);
1015 spriteImageView = VK_NULL_HANDLE;
1016 }
1017 if (spriteImage != VK_NULL_HANDLE) {
1018 vkDestroyImage(device, spriteImage, nullptr);
1019 spriteImage = VK_NULL_HANDLE;
1020 }
1021 if (spriteImageMemory != VK_NULL_HANDLE) {
1022 vkFreeMemory(device, spriteImageMemory, nullptr);
1023 spriteImageMemory = VK_NULL_HANDLE;
1024 }
1025 spriteWidth = rgbaSurface->w;
1026 spriteHeight = rgbaSurface->h;
1027 createSpriteTexture(rgbaSurface);
1028 createDescriptorPool();
1029 }
1030 SDL_DestroySurface(rgbaSurface);
1031 }

Member Data Documentation

◆ spriteSampler

VkSampler mxvk::VK_Sprite::spriteSampler = VK_NULL_HANDLE

Texture sampler.

Definition at line 238 of file mxvk_sprite.hpp.


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