libmx2 2.2.1
C++20 multimedia and game-engine library (SDL2 / OpenGL / Vulkan)
Loading...
Searching...
No Matches
vk_text.cpp
Go to the documentation of this file.
1/**
2 * @file vk_text.cpp
3 * @brief Implementation of mx::VKText Vulkan SDL_ttf text renderer.
4 */
5#include"vk_text.hpp"
6
7namespace mx {
8
9 VKText::VKText(VkDevice dev, VkPhysicalDevice physDev, VkQueue gQueue,
10 VkCommandPool cmdPool, const std::string &fontPath, int fontSize)
11 : device(dev), physicalDevice(physDev), graphicsQueue(gQueue), commandPool(cmdPool) {
12
13 if (TTF_Init() == -1) {
14 throw mx::Exception("Failed to initialize SDL_ttf: " + std::string(TTF_GetError()));
15 }
16
17 initFont(fontPath, fontSize);
18 }
19
21 vkDeviceWaitIdle(device);
22 textQuads.clear();
23 clearCache();
24
25 if (descriptorPool != VK_NULL_HANDLE) {
26 vkDestroyDescriptorPool(device, descriptorPool, nullptr);
27 }
28
29 if (fontSampler != VK_NULL_HANDLE) {
30 vkDestroySampler(device, fontSampler, nullptr);
31 }
32
33 if (font) {
34 TTF_CloseFont(font);
35 }
36 TTF_Quit();
37 }
38
40 for (auto &[key, cached] : textureCache) {
41 if (cached.imageView != VK_NULL_HANDLE) {
42 vkDestroyImageView(device, cached.imageView, nullptr);
43 }
44 if (cached.image != VK_NULL_HANDLE) {
45 vkDestroyImage(device, cached.image, nullptr);
46 vkFreeMemory(device, cached.imageMemory, nullptr);
47 }
48 }
49 textureCache.clear();
50 }
51
55
56 void VKText::createDescriptorPool(uint32_t maxSets) {
57 VkDescriptorPoolSize poolSize{};
58 poolSize.type = VK_DESCRIPTOR_TYPE_COMBINED_IMAGE_SAMPLER;
59 poolSize.descriptorCount = maxSets;
60
61 VkDescriptorPoolCreateInfo poolInfo{};
62 poolInfo.sType = VK_STRUCTURE_TYPE_DESCRIPTOR_POOL_CREATE_INFO;
63 poolInfo.poolSizeCount = 1;
64 poolInfo.pPoolSizes = &poolSize;
65 poolInfo.maxSets = maxSets;
66 poolInfo.flags = VK_DESCRIPTOR_POOL_CREATE_FREE_DESCRIPTOR_SET_BIT;
67
68 VK_CHECK_RESULT(vkCreateDescriptorPool(device, &poolInfo, nullptr, &descriptorPool));
69 maxPoolSets = maxSets;
70 }
71
73 vkDeviceWaitIdle(device);
74 textQuads.clear();
75 if (descriptorPool != VK_NULL_HANDLE) {
76 vkDestroyDescriptorPool(device, descriptorPool, nullptr);
77 descriptorPool = VK_NULL_HANDLE;
78 }
79 maxPoolSets *= 2;
81 }
82
83 VkDescriptorSet VKText::createDescriptorSet(VkImageView imageView) {
84 if (descriptorSetLayout == VK_NULL_HANDLE) {
85 throw mx::Exception("VKText::createDescriptorSet called before setDescriptorSetLayout");
86 }
87
88 if (descriptorPool == VK_NULL_HANDLE) {
90 }
91
92 VkDescriptorSetAllocateInfo allocInfo{};
93 allocInfo.sType = VK_STRUCTURE_TYPE_DESCRIPTOR_SET_ALLOCATE_INFO;
94 allocInfo.descriptorPool = descriptorPool;
95 allocInfo.descriptorSetCount = 1;
96 allocInfo.pSetLayouts = &descriptorSetLayout;
97
98 VkDescriptorSet descriptorSet;
99 VkResult res = vkAllocateDescriptorSets(device, &allocInfo, &descriptorSet);
100 if (res == VK_ERROR_OUT_OF_POOL_MEMORY || res == VK_ERROR_FRAGMENTED_POOL) {
102 allocInfo.descriptorPool = descriptorPool;
103 VK_CHECK_RESULT(vkAllocateDescriptorSets(device, &allocInfo, &descriptorSet));
104 } else if (res != VK_SUCCESS) {
105 throw mx::Exception("Fatal : VkResult is \"" + std::to_string(res) + "\" in " + std::string(__FILE__) + " at line " + std::to_string(__LINE__));
106 }
107
108 VkDescriptorImageInfo imageInfo{};
109 imageInfo.imageLayout = VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL;
110 imageInfo.imageView = imageView;
111 imageInfo.sampler = fontSampler;
112
113 VkWriteDescriptorSet descriptorWrite{};
114 descriptorWrite.sType = VK_STRUCTURE_TYPE_WRITE_DESCRIPTOR_SET;
115 descriptorWrite.dstSet = descriptorSet;
116 descriptorWrite.dstBinding = 0;
117 descriptorWrite.dstArrayElement = 0;
118 descriptorWrite.descriptorType = VK_DESCRIPTOR_TYPE_COMBINED_IMAGE_SAMPLER;
119 descriptorWrite.descriptorCount = 1;
120 descriptorWrite.pImageInfo = &imageInfo;
121
122 vkUpdateDescriptorSets(device, 1, &descriptorWrite, 0, nullptr);
123
124 return descriptorSet;
125 }
126
127 void VKText::initFont(const std::string &fontPath, int fontSize) {
128 font = TTF_OpenFont(fontPath.c_str(), fontSize);
129 if (!font) {
130 throw mx::Exception("Failed to load font: " + std::string(TTF_GetError()));
131 }
132
133 VkSamplerCreateInfo samplerInfo{};
134 samplerInfo.sType = VK_STRUCTURE_TYPE_SAMPLER_CREATE_INFO;
135 samplerInfo.magFilter = VK_FILTER_LINEAR;
136 samplerInfo.minFilter = VK_FILTER_LINEAR;
137 samplerInfo.addressModeU = VK_SAMPLER_ADDRESS_MODE_CLAMP_TO_EDGE;
138 samplerInfo.addressModeV = VK_SAMPLER_ADDRESS_MODE_CLAMP_TO_EDGE;
139 samplerInfo.addressModeW = VK_SAMPLER_ADDRESS_MODE_CLAMP_TO_EDGE;
140 samplerInfo.anisotropyEnable = VK_FALSE;
141 samplerInfo.borderColor = VK_BORDER_COLOR_INT_OPAQUE_BLACK;
142 samplerInfo.unnormalizedCoordinates = VK_FALSE;
143 samplerInfo.compareEnable = VK_FALSE;
144 samplerInfo.compareOp = VK_COMPARE_OP_ALWAYS;
145 samplerInfo.mipmapMode = VK_SAMPLER_MIPMAP_MODE_LINEAR;
146
147 VK_CHECK_RESULT(vkCreateSampler(device, &samplerInfo, nullptr, &fontSampler));
148 }
149
150 void VKText::setFont(const std::string &fontPath, int fontSize) {
151 vkDeviceWaitIdle(device);
152 clearQueue();
153 clearCache();
154 if (font) {
155 TTF_CloseFont(font);
156 font = nullptr;
157 }
158 if (fontSampler != VK_NULL_HANDLE) {
159 vkDestroySampler(device, fontSampler, nullptr);
160 fontSampler = VK_NULL_HANDLE;
161 }
162 initFont(fontPath, fontSize);
163 }
164
165 SDL_Surface* VKText::convertToRGBA(SDL_Surface* surface) {
166 SDL_Surface* converted = SDL_CreateRGBSurface(0, surface->w, surface->h, 32,
167 0x000000FF, 0x0000FF00, 0x00FF0000, 0xFF000000);
168 if (!converted) return nullptr;
169 SDL_BlitSurface(surface, nullptr, converted, nullptr);
170 return converted;
171 }
172
173 VkImageView VKText::createImageView(VkImage image, VkFormat format) {
174 VkImageViewCreateInfo viewInfo{};
175 viewInfo.sType = VK_STRUCTURE_TYPE_IMAGE_VIEW_CREATE_INFO;
176 viewInfo.image = image;
177 viewInfo.viewType = VK_IMAGE_VIEW_TYPE_2D;
178 viewInfo.format = format;
179 viewInfo.subresourceRange.aspectMask = VK_IMAGE_ASPECT_COLOR_BIT;
180 viewInfo.subresourceRange.baseMipLevel = 0;
181 viewInfo.subresourceRange.levelCount = 1;
182 viewInfo.subresourceRange.baseArrayLayer = 0;
183 viewInfo.subresourceRange.layerCount = 1;
184
185 VkImageView imageView;
186 VK_CHECK_RESULT(vkCreateImageView(device, &viewInfo, nullptr, &imageView));
187 return imageView;
188 }
189
190 void VKText::printTextG_Solid(const std::string &text, int x, int y, const SDL_Color &col) {
191 if (text.empty() || !font) return;
192
193 TextQuad quad;
194 quad.device = device;
195 quad.text = text;
196 quad.x = x;
197 quad.y = y;
198 quad.color = col;
199 quad.ownsTexture = false; // cache owns all textures
200
201 CacheKey key{text, col.r, col.g, col.b, col.a};
202 auto it = textureCache.find(key);
203
204 if (it != textureCache.end()) {
205 // Cache hit -- reuse the existing GPU texture
206 auto &cached = it->second;
207 quad.textImage = cached.image;
208 quad.textImageMemory = cached.imageMemory;
209 quad.textImageView = cached.imageView;
210 quad.width = cached.width;
211 quad.height = cached.height;
212 } else {
213 // Cache miss -- render with SDL_ttf and upload to GPU
214 SDL_Surface *textSurface = TTF_RenderText_Blended(font, text.c_str(), col);
215 if (!textSurface) return;
216
217 SDL_Surface *rgbaSurface = convertToRGBA(textSurface);
218 SDL_FreeSurface(textSurface);
219 if (!rgbaSurface) return;
220
221 quad.width = rgbaSurface->w;
222 quad.height = rgbaSurface->h;
223
224 createImage(rgbaSurface->w, rgbaSurface->h, VK_FORMAT_R8G8B8A8_UNORM, VK_IMAGE_TILING_OPTIMAL,
225 VK_IMAGE_USAGE_TRANSFER_DST_BIT | VK_IMAGE_USAGE_SAMPLED_BIT,
226 VK_MEMORY_PROPERTY_DEVICE_LOCAL_BIT, quad.textImage, quad.textImageMemory);
227
228 VkBuffer stagingBuffer;
229 VkDeviceMemory stagingMemory;
230 VkDeviceSize imageSize = rgbaSurface->w * rgbaSurface->h * 4;
231
232 createBuffer(imageSize, VK_BUFFER_USAGE_TRANSFER_SRC_BIT,
233 VK_MEMORY_PROPERTY_HOST_VISIBLE_BIT | VK_MEMORY_PROPERTY_HOST_COHERENT_BIT,
234 stagingBuffer, stagingMemory);
235
236 void *data;
237 VK_CHECK_RESULT(vkMapMemory(device, stagingMemory, 0, imageSize, 0, &data));
238 memcpy(data, rgbaSurface->pixels, imageSize);
239 vkUnmapMemory(device, stagingMemory);
240
241 transitionImageLayout(quad.textImage, VK_IMAGE_LAYOUT_UNDEFINED, VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL);
242 copyBufferToImage(stagingBuffer, quad.textImage, rgbaSurface->w, rgbaSurface->h);
243 transitionImageLayout(quad.textImage, VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL, VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL);
244
245 vkDestroyBuffer(device, stagingBuffer, nullptr);
246 vkFreeMemory(device, stagingMemory, nullptr);
247
248 quad.textImageView = createImageView(quad.textImage, VK_FORMAT_R8G8B8A8_UNORM);
249
250 SDL_FreeSurface(rgbaSurface);
251
252 // Store in cache
253 textureCache[key] = {quad.textImage, quad.textImageMemory, quad.textImageView,
254 quad.width, quad.height};
255 }
256
257 // Build the screen-space quad (position-dependent, not cached)
258 float x0 = (float)x;
259 float y0 = (float)y;
260 float x1 = x0 + quad.width;
261 float y1 = y0 + quad.height;
262
263 TextVertex v0 = {{x0, y0}, {0.0f, 0.0f}};
264 TextVertex v1 = {{x1, y0}, {1.0f, 0.0f}};
265 TextVertex v2 = {{x1, y1}, {1.0f, 1.0f}};
266 TextVertex v3 = {{x0, y1}, {0.0f, 1.0f}};
267
268 quad.vertices = {v0, v1, v2, v3};
269 quad.indices = {0, 1, 2, 0, 2, 3};
270 quad.indexCount = 6;
271
272 void *data;
273 VkDeviceSize vertexSize = quad.vertices.size() * sizeof(TextVertex);
274 createBuffer(vertexSize, VK_BUFFER_USAGE_VERTEX_BUFFER_BIT,
275 VK_MEMORY_PROPERTY_HOST_VISIBLE_BIT | VK_MEMORY_PROPERTY_HOST_COHERENT_BIT,
277
278 VK_CHECK_RESULT(vkMapMemory(device, quad.vertexBufferMemory, 0, vertexSize, 0, &data));
279 memcpy(data, quad.vertices.data(), vertexSize);
280 vkUnmapMemory(device, quad.vertexBufferMemory);
281
282 VkDeviceSize indexSize = quad.indices.size() * sizeof(uint16_t);
283 createBuffer(indexSize, VK_BUFFER_USAGE_INDEX_BUFFER_BIT,
284 VK_MEMORY_PROPERTY_HOST_VISIBLE_BIT | VK_MEMORY_PROPERTY_HOST_COHERENT_BIT,
285 quad.indexBuffer, quad.indexBufferMemory);
286
287 VK_CHECK_RESULT(vkMapMemory(device, quad.indexBufferMemory, 0, indexSize, 0, &data));
288 memcpy(data, quad.indices.data(), indexSize);
289 vkUnmapMemory(device, quad.indexBufferMemory);
290
292
293 textQuads.emplace_back(std::move(quad));
294 }
295
296 void VKText::renderText(VkCommandBuffer cmdBuffer, VkPipelineLayout pipelineLayout,
297 uint32_t screenWidth, uint32_t screenHeight) {
298
299 struct TextPushConstants {
300 float screenWidth;
301 float screenHeight;
302 } pc { static_cast<float>(screenWidth), static_cast<float>(screenHeight) };
303
304 vkCmdPushConstants(cmdBuffer, pipelineLayout, VK_SHADER_STAGE_VERTEX_BIT, 0, sizeof(TextPushConstants), &pc);
305
306 for (auto &quad : textQuads) {
307 VkBuffer vertexBuffers[] = {quad.vertexBuffer};
308 VkDeviceSize offsets[] = {0};
309 vkCmdBindVertexBuffers(cmdBuffer, 0, 1, vertexBuffers, offsets);
310 vkCmdBindIndexBuffer(cmdBuffer, quad.indexBuffer, 0, VK_INDEX_TYPE_UINT16);
311 vkCmdBindDescriptorSets(cmdBuffer, VK_PIPELINE_BIND_POINT_GRAPHICS, pipelineLayout, 0, 1, &quad.descriptorSet, 0, nullptr);
312
313 vkCmdDrawIndexed(cmdBuffer, quad.indexCount, 1, 0, 0, 0);
314 }
315 }
316
318 textQuads.clear();
319 if (descriptorPool != VK_NULL_HANDLE) {
320 VK_CHECK_RESULT(vkResetDescriptorPool(device, descriptorPool, 0));
321 }
322 }
323
324 void VKText::createBuffer(VkDeviceSize size, VkBufferUsageFlags usage,
325 VkMemoryPropertyFlags properties, VkBuffer& buffer,
326 VkDeviceMemory& bufferMemory) {
327 VkBufferCreateInfo bufferInfo{};
328 bufferInfo.sType = VK_STRUCTURE_TYPE_BUFFER_CREATE_INFO;
329 bufferInfo.size = size;
330 bufferInfo.usage = usage;
331 bufferInfo.sharingMode = VK_SHARING_MODE_EXCLUSIVE;
332
333 VK_CHECK_RESULT(vkCreateBuffer(device, &bufferInfo, nullptr, &buffer));
334
335 VkMemoryRequirements memRequirements;
336 vkGetBufferMemoryRequirements(device, buffer, &memRequirements);
337
338 VkMemoryAllocateInfo allocInfo{};
339 allocInfo.sType = VK_STRUCTURE_TYPE_MEMORY_ALLOCATE_INFO;
340 allocInfo.allocationSize = memRequirements.size;
341 allocInfo.memoryTypeIndex = findMemoryType(memRequirements.memoryTypeBits, properties);
342
343 VK_CHECK_RESULT(vkAllocateMemory(device, &allocInfo, nullptr, &bufferMemory));
344 VK_CHECK_RESULT(vkBindBufferMemory(device, buffer, bufferMemory, 0));
345 }
346
347 bool VKText::getTextDimensions(const std::string &text, int& width, int& height) {
348 if (text.empty() || !font) {
349 width = 0;
350 height = 0;
351 return false;
352 }
353
354 if (TTF_SizeText(font, text.c_str(), &width, &height) == 0) {
355 return true;
356 }
357 return false;
358 }
359
360 uint32_t VKText::findMemoryType(uint32_t typeFilter, VkMemoryPropertyFlags properties) {
361 VkPhysicalDeviceMemoryProperties memProperties;
362 vkGetPhysicalDeviceMemoryProperties(physicalDevice, &memProperties);
363
364 for (uint32_t i = 0; i < memProperties.memoryTypeCount; i++) {
365 if ((typeFilter & (1 << i)) && (memProperties.memoryTypes[i].propertyFlags & properties) == properties) {
366 return i;
367 }
368 }
369
370 throw mx::Exception("Failed to find suitable memory type!");
371 }
372
373 void VKText::transitionImageLayout(VkImage image, VkImageLayout oldLayout, VkImageLayout newLayout) {
374 VkCommandBuffer commandBuffer = beginSingleTimeCommands();
375
376 VkImageMemoryBarrier barrier{};
377 barrier.sType = VK_STRUCTURE_TYPE_IMAGE_MEMORY_BARRIER;
378 barrier.oldLayout = oldLayout;
379 barrier.newLayout = newLayout;
380 barrier.srcQueueFamilyIndex = VK_QUEUE_FAMILY_IGNORED;
381 barrier.dstQueueFamilyIndex = VK_QUEUE_FAMILY_IGNORED;
382 barrier.image = image;
383 barrier.subresourceRange.aspectMask = VK_IMAGE_ASPECT_COLOR_BIT;
384 barrier.subresourceRange.baseMipLevel = 0;
385 barrier.subresourceRange.levelCount = 1;
386 barrier.subresourceRange.baseArrayLayer = 0;
387 barrier.subresourceRange.layerCount = 1;
388
389 VkPipelineStageFlags sourceStage;
390 VkPipelineStageFlags destinationStage;
391
392 if (oldLayout == VK_IMAGE_LAYOUT_UNDEFINED && newLayout == VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL) {
393 barrier.srcAccessMask = 0;
394 barrier.dstAccessMask = VK_ACCESS_TRANSFER_WRITE_BIT;
395 sourceStage = VK_PIPELINE_STAGE_TOP_OF_PIPE_BIT;
396 destinationStage = VK_PIPELINE_STAGE_TRANSFER_BIT;
397 } else if (oldLayout == VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL && newLayout == VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL) {
398 barrier.srcAccessMask = VK_ACCESS_TRANSFER_WRITE_BIT;
399 barrier.dstAccessMask = VK_ACCESS_SHADER_READ_BIT;
400 sourceStage = VK_PIPELINE_STAGE_TRANSFER_BIT;
401 destinationStage = VK_PIPELINE_STAGE_FRAGMENT_SHADER_BIT;
402 } else {
403 throw std::invalid_argument("unsupported layout transition!");
404 }
405
406 vkCmdPipelineBarrier(commandBuffer, sourceStage, destinationStage, 0, 0, nullptr, 0, nullptr, 1, &barrier);
407 endSingleTimeCommands(commandBuffer);
408 }
409
410 void VKText::copyBufferToImage(VkBuffer buffer, VkImage image, uint32_t width, uint32_t height) {
411 VkCommandBuffer commandBuffer = beginSingleTimeCommands();
412
413 VkBufferImageCopy region{};
414 region.bufferOffset = 0;
415 region.bufferRowLength = 0;
416 region.bufferImageHeight = 0;
417 region.imageSubresource.aspectMask = VK_IMAGE_ASPECT_COLOR_BIT;
418 region.imageSubresource.mipLevel = 0;
419 region.imageSubresource.baseArrayLayer = 0;
420 region.imageSubresource.layerCount = 1;
421 region.imageOffset = {0, 0, 0};
422 region.imageExtent = {width, height, 1};
423
424 vkCmdCopyBufferToImage(commandBuffer, buffer, image, VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL, 1, &region);
425
426 endSingleTimeCommands(commandBuffer);
427 }
428
430 VkCommandBufferAllocateInfo allocInfo{};
431 allocInfo.sType = VK_STRUCTURE_TYPE_COMMAND_BUFFER_ALLOCATE_INFO;
432 allocInfo.level = VK_COMMAND_BUFFER_LEVEL_PRIMARY;
433 allocInfo.commandPool = commandPool;
434 allocInfo.commandBufferCount = 1;
435
436 VkCommandBuffer commandBuffer;
437 VK_CHECK_RESULT(vkAllocateCommandBuffers(device, &allocInfo, &commandBuffer));
438
439 VkCommandBufferBeginInfo beginInfo{};
440 beginInfo.sType = VK_STRUCTURE_TYPE_COMMAND_BUFFER_BEGIN_INFO;
441 beginInfo.flags = VK_COMMAND_BUFFER_USAGE_ONE_TIME_SUBMIT_BIT;
442
443 VK_CHECK_RESULT(vkBeginCommandBuffer(commandBuffer, &beginInfo));
444
445 return commandBuffer;
446 }
447
448 void VKText::endSingleTimeCommands(VkCommandBuffer commandBuffer) {
449 VK_CHECK_RESULT(vkEndCommandBuffer(commandBuffer));
450
451 VkSubmitInfo submitInfo{};
452 submitInfo.sType = VK_STRUCTURE_TYPE_SUBMIT_INFO;
453 submitInfo.commandBufferCount = 1;
454 submitInfo.pCommandBuffers = &commandBuffer;
455
456 VK_CHECK_RESULT(vkQueueSubmit(graphicsQueue, 1, &submitInfo, VK_NULL_HANDLE));
457 VK_CHECK_RESULT(vkQueueWaitIdle(graphicsQueue));
458
459 vkFreeCommandBuffers(device, commandPool, 1, &commandBuffer);
460 }
461
462 void VKText::createImage(uint32_t width, uint32_t height, VkFormat format, VkImageTiling tiling,
463 VkImageUsageFlags usage, VkMemoryPropertyFlags properties,
464 VkImage& image, VkDeviceMemory& imageMemory) {
465 VkImageCreateInfo imageInfo{};
466 imageInfo.sType = VK_STRUCTURE_TYPE_IMAGE_CREATE_INFO;
467 imageInfo.imageType = VK_IMAGE_TYPE_2D;
468 imageInfo.extent.width = width;
469 imageInfo.extent.height = height;
470 imageInfo.extent.depth = 1;
471 imageInfo.mipLevels = 1;
472 imageInfo.arrayLayers = 1;
473 imageInfo.format = format;
474 imageInfo.tiling = tiling;
475 imageInfo.initialLayout = VK_IMAGE_LAYOUT_UNDEFINED;
476 imageInfo.usage = usage;
477 imageInfo.sharingMode = VK_SHARING_MODE_EXCLUSIVE;
478 imageInfo.samples = VK_SAMPLE_COUNT_1_BIT;
479
480 VK_CHECK_RESULT(vkCreateImage(device, &imageInfo, nullptr, &image));
481
482 VkMemoryRequirements memRequirements;
483 vkGetImageMemoryRequirements(device, image, &memRequirements);
484
485 VkMemoryAllocateInfo allocInfo{};
486 allocInfo.sType = VK_STRUCTURE_TYPE_MEMORY_ALLOCATE_INFO;
487 allocInfo.allocationSize = memRequirements.size;
488 allocInfo.memoryTypeIndex = findMemoryType(memRequirements.memoryTypeBits, properties);
489
490 VK_CHECK_RESULT(vkAllocateMemory(device, &allocInfo, nullptr, &imageMemory));
491 VK_CHECK_RESULT(vkBindImageMemory(device, image, imageMemory, 0));
492 }
493
494}
General-purpose exception class for the mx library.
Definition exception.hpp:20
VkPhysicalDevice physicalDevice
Definition vk_text.hpp:242
void printTextG_Solid(const std::string &text, int x, int y, const SDL_Color &col)
Queue a text string for solid (opaque) rendering.
Definition vk_text.cpp:190
VkDescriptorSetLayout descriptorSetLayout
Definition vk_text.hpp:249
VkDescriptorSet createDescriptorSet(VkImageView imageView)
Definition vk_text.cpp:83
TTF_Font * font
Definition vk_text.hpp:246
void initFont(const std::string &fontPath, int fontSize)
Definition vk_text.cpp:127
VkDevice device
Definition vk_text.hpp:241
void growDescriptorPool()
Definition vk_text.cpp:72
VkCommandBuffer beginSingleTimeCommands()
Definition vk_text.cpp:429
uint32_t findMemoryType(uint32_t typeFilter, VkMemoryPropertyFlags properties)
Definition vk_text.cpp:360
VkDescriptorPool descriptorPool
Definition vk_text.hpp:248
uint32_t maxPoolSets
Definition vk_text.hpp:250
VkSampler fontSampler
Sampler used for all text textures.
Definition vk_text.hpp:149
void createBuffer(VkDeviceSize size, VkBufferUsageFlags usage, VkMemoryPropertyFlags properties, VkBuffer &buffer, VkDeviceMemory &bufferMemory)
Definition vk_text.cpp:324
void renderText(VkCommandBuffer cmdBuffer, VkPipelineLayout pipelineLayout, uint32_t screenWidth, uint32_t screenHeight)
Record all queued text quads into a command buffer.
Definition vk_text.cpp:296
void createImage(uint32_t width, uint32_t height, VkFormat format, VkImageTiling tiling, VkImageUsageFlags usage, VkMemoryPropertyFlags properties, VkImage &image, VkDeviceMemory &imageMemory)
Definition vk_text.cpp:462
~VKText()
Destructor – destroys all Vulkan and SDL_ttf resources.
Definition vk_text.cpp:20
void endSingleTimeCommands(VkCommandBuffer commandBuffer)
Definition vk_text.cpp:448
std::unordered_map< CacheKey, CachedTexture, CacheKeyHash > textureCache
Definition vk_text.hpp:251
SDL_Surface * convertToRGBA(SDL_Surface *surface)
Definition vk_text.cpp:165
void createDescriptorPool()
Definition vk_text.cpp:52
void copyBufferToImage(VkBuffer buffer, VkImage image, uint32_t width, uint32_t height)
Definition vk_text.cpp:410
void setFont(const std::string &fontPath, int fontSize)
Change the active font.
Definition vk_text.cpp:150
VkQueue graphicsQueue
Definition vk_text.hpp:243
void clearCache()
Destroy all cached text textures, freeing GPU memory.
Definition vk_text.cpp:39
VkImageView createImageView(VkImage image, VkFormat format)
Definition vk_text.cpp:173
VkCommandPool commandPool
Definition vk_text.hpp:244
void transitionImageLayout(VkImage image, VkImageLayout oldLayout, VkImageLayout newLayout)
Definition vk_text.cpp:373
bool getTextDimensions(const std::string &text, int &width, int &height)
Measure the pixel dimensions of a string with the current font.
Definition vk_text.cpp:347
VKText(VkDevice device, VkPhysicalDevice physicalDevice, VkQueue graphicsQueue, VkCommandPool commandPool, const std::string &fontPath, int fontSize=24)
Construct VKText and load the font.
Definition vk_text.cpp:9
void clearQueue()
Discard all pending text quads without rendering them.
Definition vk_text.cpp:317
std::vector< TextQuad > textQuads
Definition vk_text.hpp:247
Definition cfg.cpp:11
Cache key combining text content and colour.
Definition vk_text.hpp:54
bool ownsTexture
false when texture is owned by the cache.
Definition vk_text.hpp:173
VkDescriptorSet descriptorSet
Definition vk_text.hpp:170
VkDeviceMemory vertexBufferMemory
Definition vk_text.hpp:164
VkDeviceMemory indexBufferMemory
Definition vk_text.hpp:166
VkImageView textImageView
Definition vk_text.hpp:169
VkDeviceMemory textImageMemory
Definition vk_text.hpp:168
std::vector< TextVertex > vertices
Definition vk_text.hpp:161
std::vector< uint16_t > indices
Definition vk_text.hpp:162
#define VK_CHECK_RESULT(f)
Definition vk.hpp:47
Vulkan SDL_ttf text renderer.