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_point_sprite_batch.cpp
Go to the documentation of this file.
2
3#include "mxvk/mxvk.hpp"
6
7#include <array>
8#include <cstddef>
9#include <cstring>
10#include <format>
11
12namespace mxvk {
13
15
16 void VK_PointSpriteBatch::load(VK_Window *window, const std::string &texture_path_value, const std::string &vertex_shader_path_value, const std::string &fragment_shader_path_value, size_t max_vertex_count) {
17 if (window == nullptr) {
18 throw mxvk::Exception("VK_PointSpriteBatch::load called with null window");
19 }
20 if (max_vertex_count == 0) {
21 throw mxvk::Exception("VK_PointSpriteBatch::load requires non-zero vertex capacity");
22 }
23
24 cleanup();
25 context = {
26 .device = window->getDevice(),
27 .physical_device = window->getPhysicalDevice(),
28 .graphics_queue = window->getGraphicsQueue(),
29 .command_pool = window->getCommandPool(),
30 };
31 pipeline_cache = window->getPipelineCache();
32 color_attachment_format = window->getSwapchainFormat();
33 depth_attachment_format = window->getDepthFormat();
34 image_count = window->getSwapchainImageCount();
35 texture_path = texture_path_value;
36 vertex_shader_path = vertex_shader_path_value;
37 fragment_shader_path = fragment_shader_path_value;
38 max_vertices = max_vertex_count;
39
40 if (context.device == VK_NULL_HANDLE || context.physical_device == VK_NULL_HANDLE || context.graphics_queue == VK_NULL_HANDLE || context.command_pool == VK_NULL_HANDLE) {
41 throw mxvk::Exception("Cannot create point-sprite batch before Vulkan render resources are available");
42 }
43 if (color_attachment_format == VK_FORMAT_UNDEFINED || image_count == 0) {
44 throw mxvk::Exception("Cannot create point-sprite batch before swapchain resources are available");
45 }
46
47 create_texture_from_png(context, texture_path, texture);
48 create_vertex_buffer();
49 create_swapchain_resources();
50 batch_loaded = true;
51 }
52
54 if (!batch_loaded || window == nullptr) {
55 return;
56 }
57
58 context.device = window->getDevice();
59 context.physical_device = window->getPhysicalDevice();
60 context.graphics_queue = window->getGraphicsQueue();
61 context.command_pool = window->getCommandPool();
62 pipeline_cache = window->getPipelineCache();
63 color_attachment_format = window->getSwapchainFormat();
64 depth_attachment_format = window->getDepthFormat();
65 image_count = window->getSwapchainImageCount();
66
67 cleanup_swapchain_resources();
68 create_swapchain_resources();
69 }
70
72 cleanup_swapchain_resources();
73 destroy_vertex_buffer();
74 destroy_texture(context.device, texture);
75 context = {};
76 pipeline_cache = VK_NULL_HANDLE;
77 color_attachment_format = VK_FORMAT_UNDEFINED;
78 depth_attachment_format = VK_FORMAT_UNDEFINED;
79 image_count = 0;
80 texture_path.clear();
81 vertex_shader_path.clear();
82 fragment_shader_path.clear();
83 max_vertices = 0;
84 active_vertices = 0;
85 batch_loaded = false;
86 }
87
88 void VK_PointSpriteBatch::upload_vertices(const PointSpriteVertex *vertices, size_t count) {
89 if (!batch_loaded || vertex_buffer.mapped == nullptr || vertices == nullptr || count == 0) {
90 active_vertices = 0;
91 return;
92 }
93 if (count > max_vertices) {
94 throw mxvk::Exception("VK_PointSpriteBatch::upload_vertices exceeds batch capacity");
95 }
96
97 std::memcpy(vertex_buffer.mapped, vertices, count * sizeof(PointSpriteVertex));
98 active_vertices = count;
99 }
100
101 void VK_PointSpriteBatch::update_mvp(uint32_t image_index, const glm::mat4 &mvp) {
102 if (image_index >= uniform_buffers.size() || uniform_buffers[image_index].mapped == nullptr) {
103 return;
104 }
105
106 UniformBufferObject ubo{};
107 ubo.mvp = mvp;
108 std::memcpy(uniform_buffers[image_index].mapped, &ubo, sizeof(ubo));
109 }
110
111 void VK_PointSpriteBatch::render(VkCommandBuffer cmd, uint32_t image_index) {
112 if (!batch_loaded || active_vertices == 0 || pipeline == VK_NULL_HANDLE || image_index >= descriptor_sets.size()) {
113 return;
114 }
115
116 vkCmdBindPipeline(cmd, VK_PIPELINE_BIND_POINT_GRAPHICS, pipeline);
117 VkBuffer buffers[] = {vertex_buffer.buffer};
118 VkDeviceSize offsets[] = {0};
119 vkCmdBindVertexBuffers(cmd, 0, 1, buffers, offsets);
120 vkCmdBindDescriptorSets(cmd, VK_PIPELINE_BIND_POINT_GRAPHICS, pipeline_layout, 0, 1, &descriptor_sets[image_index], 0, nullptr);
121 vkCmdDraw(cmd, static_cast<uint32_t>(active_vertices), 1, 0, 0);
122 }
123
125 if (additive_blending == enabled) {
126 return;
127 }
128 additive_blending = enabled;
129 if (batch_loaded) {
130 destroy_pipeline();
131 create_pipeline();
132 }
133 }
134
136 if (depth_test_enabled == enabled) {
137 return;
138 }
139 depth_test_enabled = enabled;
140 if (batch_loaded) {
141 destroy_pipeline();
142 create_pipeline();
143 }
144 }
145
147 if (depth_write_enabled == enabled) {
148 return;
149 }
150 depth_write_enabled = enabled;
151 if (batch_loaded) {
152 destroy_pipeline();
153 create_pipeline();
154 }
155 }
156
157 void VK_PointSpriteBatch::create_vertex_buffer() {
158 create_buffer(context, sizeof(PointSpriteVertex) * static_cast<VkDeviceSize>(max_vertices), VK_BUFFER_USAGE_VERTEX_BUFFER_BIT, VK_MEMORY_PROPERTY_HOST_VISIBLE_BIT | VK_MEMORY_PROPERTY_HOST_COHERENT_BIT, vertex_buffer);
159 map_buffer(context.device, vertex_buffer);
160 }
161
162 void VK_PointSpriteBatch::destroy_vertex_buffer() { destroy_buffer(context.device, vertex_buffer); }
163
164 void VK_PointSpriteBatch::create_swapchain_resources() {
165 create_descriptor_set_layout();
166 create_uniform_buffers();
167 create_descriptor_pool();
168 create_descriptor_sets();
169 create_pipeline();
170 }
171
172 void VK_PointSpriteBatch::cleanup_swapchain_resources() {
173 if (context.device == VK_NULL_HANDLE) {
174 descriptor_sets.clear();
175 uniform_buffers.clear();
176 return;
177 }
178 destroy_pipeline();
179 if (descriptor_pool != VK_NULL_HANDLE) {
180 vkDestroyDescriptorPool(context.device, descriptor_pool, nullptr);
181 descriptor_pool = VK_NULL_HANDLE;
182 }
183 if (descriptor_set_layout != VK_NULL_HANDLE) {
184 vkDestroyDescriptorSetLayout(context.device, descriptor_set_layout, nullptr);
185 descriptor_set_layout = VK_NULL_HANDLE;
186 }
187 destroy_uniform_buffers();
188 descriptor_sets.clear();
189 }
190
191 void VK_PointSpriteBatch::create_descriptor_set_layout() {
192 VkDescriptorSetLayoutBinding sampler_binding{};
193 sampler_binding.binding = 0;
194 sampler_binding.descriptorCount = 1;
195 sampler_binding.descriptorType = VK_DESCRIPTOR_TYPE_COMBINED_IMAGE_SAMPLER;
196 sampler_binding.stageFlags = VK_SHADER_STAGE_FRAGMENT_BIT;
197
198 VkDescriptorSetLayoutBinding ubo_binding{};
199 ubo_binding.binding = 1;
200 ubo_binding.descriptorCount = 1;
201 ubo_binding.descriptorType = VK_DESCRIPTOR_TYPE_UNIFORM_BUFFER;
202 ubo_binding.stageFlags = VK_SHADER_STAGE_VERTEX_BIT;
203
204 std::array<VkDescriptorSetLayoutBinding, 2> bindings{sampler_binding, ubo_binding};
205 VkDescriptorSetLayoutCreateInfo layout_info{};
206 layout_info.sType = VK_STRUCTURE_TYPE_DESCRIPTOR_SET_LAYOUT_CREATE_INFO;
207 layout_info.bindingCount = static_cast<uint32_t>(bindings.size());
208 layout_info.pBindings = bindings.data();
209 if (vkCreateDescriptorSetLayout(context.device, &layout_info, nullptr, &descriptor_set_layout) != VK_SUCCESS) {
210 throw mxvk::Exception("failed to create point-sprite descriptor set layout");
211 }
212 }
213
214 void VK_PointSpriteBatch::create_uniform_buffers() {
215 uniform_buffers.resize(image_count);
216 for (auto &buffer : uniform_buffers) {
217 create_buffer(context, sizeof(UniformBufferObject), VK_BUFFER_USAGE_UNIFORM_BUFFER_BIT, VK_MEMORY_PROPERTY_HOST_VISIBLE_BIT | VK_MEMORY_PROPERTY_HOST_COHERENT_BIT, buffer);
218 map_buffer(context.device, buffer);
219 }
220 }
221
222 void VK_PointSpriteBatch::destroy_uniform_buffers() {
223 for (auto &buffer : uniform_buffers) {
224 destroy_buffer(context.device, buffer);
225 }
226 uniform_buffers.clear();
227 }
228
229 void VK_PointSpriteBatch::create_descriptor_pool() {
230 const uint32_t count = static_cast<uint32_t>(image_count);
231 std::array<VkDescriptorPoolSize, 2> pool_sizes{};
232 pool_sizes[0].type = VK_DESCRIPTOR_TYPE_COMBINED_IMAGE_SAMPLER;
233 pool_sizes[0].descriptorCount = count;
234 pool_sizes[1].type = VK_DESCRIPTOR_TYPE_UNIFORM_BUFFER;
235 pool_sizes[1].descriptorCount = count;
236
237 VkDescriptorPoolCreateInfo pool_info{};
238 pool_info.sType = VK_STRUCTURE_TYPE_DESCRIPTOR_POOL_CREATE_INFO;
239 pool_info.poolSizeCount = static_cast<uint32_t>(pool_sizes.size());
240 pool_info.pPoolSizes = pool_sizes.data();
241 pool_info.maxSets = count;
242 if (vkCreateDescriptorPool(context.device, &pool_info, nullptr, &descriptor_pool) != VK_SUCCESS) {
243 throw mxvk::Exception("failed to create point-sprite descriptor pool");
244 }
245 }
246
247 void VK_PointSpriteBatch::create_descriptor_sets() {
248 std::vector<VkDescriptorSetLayout> layouts(image_count, descriptor_set_layout);
249 VkDescriptorSetAllocateInfo alloc_info{};
250 alloc_info.sType = VK_STRUCTURE_TYPE_DESCRIPTOR_SET_ALLOCATE_INFO;
251 alloc_info.descriptorPool = descriptor_pool;
252 alloc_info.descriptorSetCount = static_cast<uint32_t>(image_count);
253 alloc_info.pSetLayouts = layouts.data();
254
255 descriptor_sets.resize(image_count, VK_NULL_HANDLE);
256 if (vkAllocateDescriptorSets(context.device, &alloc_info, descriptor_sets.data()) != VK_SUCCESS) {
257 throw mxvk::Exception("failed to allocate point-sprite descriptor sets");
258 }
259
260 for (size_t i = 0; i < image_count; ++i) {
261 VkDescriptorImageInfo image_info{};
262 image_info.imageLayout = VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL;
263 image_info.imageView = texture.view;
264 image_info.sampler = texture.sampler;
265
266 VkDescriptorBufferInfo buffer_info{};
267 buffer_info.buffer = uniform_buffers[i].buffer;
268 buffer_info.offset = 0;
269 buffer_info.range = sizeof(UniformBufferObject);
270
271 std::array<VkWriteDescriptorSet, 2> writes{};
272 writes[0].sType = VK_STRUCTURE_TYPE_WRITE_DESCRIPTOR_SET;
273 writes[0].dstSet = descriptor_sets[i];
274 writes[0].dstBinding = 0;
275 writes[0].descriptorType = VK_DESCRIPTOR_TYPE_COMBINED_IMAGE_SAMPLER;
276 writes[0].descriptorCount = 1;
277 writes[0].pImageInfo = &image_info;
278 writes[1].sType = VK_STRUCTURE_TYPE_WRITE_DESCRIPTOR_SET;
279 writes[1].dstSet = descriptor_sets[i];
280 writes[1].dstBinding = 1;
281 writes[1].descriptorType = VK_DESCRIPTOR_TYPE_UNIFORM_BUFFER;
282 writes[1].descriptorCount = 1;
283 writes[1].pBufferInfo = &buffer_info;
284
285 vkUpdateDescriptorSets(context.device, static_cast<uint32_t>(writes.size()), writes.data(), 0, nullptr);
286 }
287 }
288
289 void VK_PointSpriteBatch::create_pipeline() {
290 destroy_pipeline();
291
292 const std::vector<char> vert_code = read_shader_file(vertex_shader_path);
293 const std::vector<char> frag_code = read_shader_file(fragment_shader_path);
294 const VkShaderModule vert_module = mxvk::create_shader_module(context.device, vert_code);
295 VkShaderModule frag_module = VK_NULL_HANDLE;
296
297 try {
298 frag_module = mxvk::create_shader_module(context.device, frag_code);
299
300 VkPipelineShaderStageCreateInfo vert_stage{};
301 vert_stage.sType = VK_STRUCTURE_TYPE_PIPELINE_SHADER_STAGE_CREATE_INFO;
302 vert_stage.stage = VK_SHADER_STAGE_VERTEX_BIT;
303 vert_stage.module = vert_module;
304 vert_stage.pName = "main";
305
306 VkPipelineShaderStageCreateInfo frag_stage{};
307 frag_stage.sType = VK_STRUCTURE_TYPE_PIPELINE_SHADER_STAGE_CREATE_INFO;
308 frag_stage.stage = VK_SHADER_STAGE_FRAGMENT_BIT;
309 frag_stage.module = frag_module;
310 frag_stage.pName = "main";
311 std::array<VkPipelineShaderStageCreateInfo, 2> shader_stages = {vert_stage, frag_stage};
312
313 VkVertexInputBindingDescription binding{};
314 binding.binding = 0;
315 binding.stride = sizeof(PointSpriteVertex);
316 binding.inputRate = VK_VERTEX_INPUT_RATE_VERTEX;
317
318 std::array<VkVertexInputAttributeDescription, 3> attributes{};
319 attributes[0].binding = 0;
320 attributes[0].location = 0;
321 attributes[0].format = VK_FORMAT_R32G32B32_SFLOAT;
322 attributes[0].offset = offsetof(PointSpriteVertex, position);
323 attributes[1].binding = 0;
324 attributes[1].location = 1;
325 attributes[1].format = VK_FORMAT_R32_SFLOAT;
326 attributes[1].offset = offsetof(PointSpriteVertex, size);
327 attributes[2].binding = 0;
328 attributes[2].location = 2;
329 attributes[2].format = VK_FORMAT_R32G32B32A32_SFLOAT;
330 attributes[2].offset = offsetof(PointSpriteVertex, color);
331
332 VkPipelineVertexInputStateCreateInfo vertex_input{};
333 vertex_input.sType = VK_STRUCTURE_TYPE_PIPELINE_VERTEX_INPUT_STATE_CREATE_INFO;
334 vertex_input.vertexBindingDescriptionCount = 1;
335 vertex_input.pVertexBindingDescriptions = &binding;
336 vertex_input.vertexAttributeDescriptionCount = static_cast<uint32_t>(attributes.size());
337 vertex_input.pVertexAttributeDescriptions = attributes.data();
338
339 VkPipelineInputAssemblyStateCreateInfo input_assembly{};
340 input_assembly.sType = VK_STRUCTURE_TYPE_PIPELINE_INPUT_ASSEMBLY_STATE_CREATE_INFO;
341 input_assembly.topology = VK_PRIMITIVE_TOPOLOGY_POINT_LIST;
342 input_assembly.primitiveRestartEnable = VK_FALSE;
343
344 std::array<VkDynamicState, 2> dynamic_states = {VK_DYNAMIC_STATE_VIEWPORT, VK_DYNAMIC_STATE_SCISSOR};
345 VkPipelineDynamicStateCreateInfo dynamic_state{};
346 dynamic_state.sType = VK_STRUCTURE_TYPE_PIPELINE_DYNAMIC_STATE_CREATE_INFO;
347 dynamic_state.dynamicStateCount = static_cast<uint32_t>(dynamic_states.size());
348 dynamic_state.pDynamicStates = dynamic_states.data();
349
350 VkPipelineViewportStateCreateInfo viewport_state{};
351 viewport_state.sType = VK_STRUCTURE_TYPE_PIPELINE_VIEWPORT_STATE_CREATE_INFO;
352 viewport_state.viewportCount = 1;
353 viewport_state.scissorCount = 1;
354
355 VkPipelineRasterizationStateCreateInfo rasterizer{};
356 rasterizer.sType = VK_STRUCTURE_TYPE_PIPELINE_RASTERIZATION_STATE_CREATE_INFO;
357 rasterizer.polygonMode = VK_POLYGON_MODE_FILL;
358 rasterizer.cullMode = VK_CULL_MODE_NONE;
359 rasterizer.frontFace = VK_FRONT_FACE_COUNTER_CLOCKWISE;
360 rasterizer.lineWidth = 1.0f;
361
362 VkPipelineMultisampleStateCreateInfo multisampling{};
363 multisampling.sType = VK_STRUCTURE_TYPE_PIPELINE_MULTISAMPLE_STATE_CREATE_INFO;
364 multisampling.rasterizationSamples = VK_SAMPLE_COUNT_1_BIT;
365
366 VkPipelineDepthStencilStateCreateInfo depth_stencil{};
367 depth_stencil.sType = VK_STRUCTURE_TYPE_PIPELINE_DEPTH_STENCIL_STATE_CREATE_INFO;
368 depth_stencil.depthTestEnable = depth_test_enabled ? VK_TRUE : VK_FALSE;
369 depth_stencil.depthWriteEnable = depth_write_enabled ? VK_TRUE : VK_FALSE;
370 depth_stencil.depthCompareOp = VK_COMPARE_OP_LESS_OR_EQUAL;
371
372 VkPipelineColorBlendAttachmentState blend{};
373 blend.colorWriteMask = VK_COLOR_COMPONENT_R_BIT | VK_COLOR_COMPONENT_G_BIT | VK_COLOR_COMPONENT_B_BIT | VK_COLOR_COMPONENT_A_BIT;
374 blend.blendEnable = VK_TRUE;
375 blend.srcColorBlendFactor = VK_BLEND_FACTOR_SRC_ALPHA;
376 blend.dstColorBlendFactor = additive_blending ? VK_BLEND_FACTOR_ONE : VK_BLEND_FACTOR_ONE_MINUS_SRC_ALPHA;
377 blend.colorBlendOp = VK_BLEND_OP_ADD;
378 blend.srcAlphaBlendFactor = VK_BLEND_FACTOR_ONE;
379 blend.dstAlphaBlendFactor = VK_BLEND_FACTOR_ZERO;
380 blend.alphaBlendOp = VK_BLEND_OP_ADD;
381
382 VkPipelineColorBlendStateCreateInfo color_blending{};
383 color_blending.sType = VK_STRUCTURE_TYPE_PIPELINE_COLOR_BLEND_STATE_CREATE_INFO;
384 color_blending.attachmentCount = 1;
385 color_blending.pAttachments = &blend;
386
387 VkPipelineLayoutCreateInfo layout_info{};
388 layout_info.sType = VK_STRUCTURE_TYPE_PIPELINE_LAYOUT_CREATE_INFO;
389 layout_info.setLayoutCount = 1;
390 layout_info.pSetLayouts = &descriptor_set_layout;
391 if (vkCreatePipelineLayout(context.device, &layout_info, nullptr, &pipeline_layout) != VK_SUCCESS) {
392 throw mxvk::Exception("failed to create point-sprite pipeline layout");
393 }
394
395 VkPipelineRenderingCreateInfo rendering_info{};
396 rendering_info.sType = VK_STRUCTURE_TYPE_PIPELINE_RENDERING_CREATE_INFO;
397 rendering_info.colorAttachmentCount = 1;
398 rendering_info.pColorAttachmentFormats = &color_attachment_format;
399 rendering_info.depthAttachmentFormat = depth_attachment_format;
400 rendering_info.stencilAttachmentFormat = VK_FORMAT_UNDEFINED;
401
402 VkGraphicsPipelineCreateInfo pipeline_info{};
403 pipeline_info.sType = VK_STRUCTURE_TYPE_GRAPHICS_PIPELINE_CREATE_INFO;
404 pipeline_info.pNext = &rendering_info;
405 pipeline_info.stageCount = static_cast<uint32_t>(shader_stages.size());
406 pipeline_info.pStages = shader_stages.data();
407 pipeline_info.pVertexInputState = &vertex_input;
408 pipeline_info.pInputAssemblyState = &input_assembly;
409 pipeline_info.pViewportState = &viewport_state;
410 pipeline_info.pRasterizationState = &rasterizer;
411 pipeline_info.pMultisampleState = &multisampling;
412 pipeline_info.pDepthStencilState = &depth_stencil;
413 pipeline_info.pColorBlendState = &color_blending;
414 pipeline_info.pDynamicState = &dynamic_state;
415 pipeline_info.layout = pipeline_layout;
416 pipeline_info.renderPass = VK_NULL_HANDLE;
417 if (vkCreateGraphicsPipelines(context.device, pipeline_cache, 1, &pipeline_info, nullptr, &pipeline) != VK_SUCCESS) {
418 throw mxvk::Exception("failed to create point-sprite graphics pipeline");
419 }
420 } catch (...) {
421 if (frag_module != VK_NULL_HANDLE) {
422 vkDestroyShaderModule(context.device, frag_module, nullptr);
423 }
424 vkDestroyShaderModule(context.device, vert_module, nullptr);
425 throw;
426 }
427
428 vkDestroyShaderModule(context.device, frag_module, nullptr);
429 vkDestroyShaderModule(context.device, vert_module, nullptr);
430 }
431
432 void VK_PointSpriteBatch::destroy_pipeline() {
433 if (context.device == VK_NULL_HANDLE) {
434 pipeline = VK_NULL_HANDLE;
435 pipeline_layout = VK_NULL_HANDLE;
436 return;
437 }
438 if (pipeline != VK_NULL_HANDLE) {
439 vkDestroyPipeline(context.device, pipeline, nullptr);
440 pipeline = VK_NULL_HANDLE;
441 }
442 if (pipeline_layout != VK_NULL_HANDLE) {
443 vkDestroyPipelineLayout(context.device, pipeline_layout, nullptr);
444 pipeline_layout = VK_NULL_HANDLE;
445 }
446 }
447
448 std::vector<char> VK_PointSpriteBatch::read_shader_file(const std::string &path) const { return mxvk::load_spv(path); }
449
450} // namespace mxvk
~VK_PointSpriteBatch()
Destroy all owned Vulkan resources.
void load(VK_Window *window, const std::string &texture_path, const std::string &vertex_shader_path, const std::string &fragment_shader_path, size_t max_vertices)
Create texture, vertex buffer, descriptors, and point-list pipeline.
void set_depth_write_enabled(bool enabled)
Enable or disable depth writes in the point-sprite pipeline.
void render(VkCommandBuffer cmd, uint32_t image_index)
Record point-sprite draw commands into an active rendering scope.
void update_mvp(uint32_t image_index, const glm::mat4 &mvp)
Update the MVP uniform for one swapchain image.
void resize(VK_Window *window)
Recreate swapchain-dependent resources after a window resize.
void cleanup()
Destroy all owned resources and reset the batch to an unloaded state.
void set_additive_blending(bool enabled)
Select additive or alpha-over color blending.
void upload_vertices(const PointSpriteVertex *vertices, size_t count)
Copy point vertices into the persistent mapped vertex buffer.
void set_depth_test_enabled(bool enabled)
Enable or disable depth testing in the point-sprite pipeline.
Main Vulkan window wrapper for MXVK.
Definition mxvk.hpp:39
VkDevice getDevice() const noexcept
Get the Vulkan logical device handle.
Definition mxvk.hpp:198
VkQueue getGraphicsQueue() const noexcept
Get the graphics queue handle.
Definition mxvk.hpp:204
VkCommandPool getCommandPool() const noexcept
Get the command pool used for graphics/upload work.
Definition mxvk.hpp:207
size_t getSwapchainImageCount() const noexcept
Get the number of swapchain images currently allocated.
Definition mxvk.hpp:239
VkFormat getDepthFormat() const noexcept
Get the depth format used for dynamic rendering attachments.
Definition mxvk.hpp:236
VkPipelineCache getPipelineCache() const noexcept
Get the persistent Vulkan pipeline cache used by framework pipelines.
Definition mxvk.hpp:210
VkPhysicalDevice getPhysicalDevice() const noexcept
Get the Vulkan physical device handle.
Definition mxvk.hpp:201
VkFormat getSwapchainFormat() const noexcept
Get the swapchain color format.
Definition mxvk.hpp:213
Reusable point-sprite renderer for particle and starfield effects.
Utilities for loading and saving PNG images.
Definition mxvk.hpp:31
VkShaderModule create_shader_module(VkDevice device, const std::vector< char > &spv_bytes)
Create a shader module from SPIR-V bytecode.
void create_texture_from_png(const VulkanContext &context, const std::string &path, TextureResource &texture, VkFormat format=VK_FORMAT_R8G8B8A8_UNORM)
Load a PNG and upload it into a sampled 2D texture.
void map_buffer(VkDevice device, BufferResource &buffer)
Persistently map a host-visible buffer allocation.
void create_buffer(const VulkanContext &context, VkDeviceSize size, VkBufferUsageFlags usage, VkMemoryPropertyFlags properties, BufferResource &buffer)
Create and bind a Vulkan buffer allocation.
void destroy_buffer(VkDevice device, BufferResource &buffer)
Unmap and destroy a BufferResource.
std::vector< char > load_spv(const std::string &path)
Load a SPIR-V file from disk.
void destroy_texture(VkDevice device, TextureResource &texture)
Destroy every Vulkan handle owned by a TextureResource.
Vertex layout consumed by VK_PointSpriteBatch.
VkDevice device
Logical device used to create and destroy resources.