diff --git a/src/Graphics/Mesh.hpp b/src/Graphics/Mesh.hpp index e3daa95..be63d88 100644 --- a/src/Graphics/Mesh.hpp +++ b/src/Graphics/Mesh.hpp @@ -28,7 +28,7 @@ namespace Dynamo::Graphics { * */ struct MeshDescriptor { - using AttributeBuffer = std::vector; + using AttributeBuffer = std::vector; std::vector attributes; unsigned vertex_count; diff --git a/src/Graphics/Renderer.cpp b/src/Graphics/Renderer.cpp index 1c002da..be0f434 100644 --- a/src/Graphics/Renderer.cpp +++ b/src/Graphics/Renderer.cpp @@ -9,7 +9,7 @@ namespace Dynamo::Graphics { _surface = _display.create_vulkan_surface(_instance); // Create the logical device - _physical = PhysicalDevice::select(_instance, _surface); + _physical = PhysicalDevice::select_best(_instance, _surface); _device = VkDevice_create(_physical); // Build the swapchain and its views @@ -24,6 +24,7 @@ namespace Dynamo::Graphics { _shaders = ShaderRegistry(_device); _materials = MaterialRegistry(_device, root_asset_directory + "/vulkan_cache.bin"); _uniforms = UniformRegistry(_device, _physical, _transfer_pool); + _textures = TextureRegistry(_device, _physical, _transfer_pool); _framebuffers = FramebufferCache(_device); // Frame contexts @@ -46,6 +47,7 @@ namespace Dynamo::Graphics { // High-level objects _frame_contexts.destroy(); _framebuffers.destroy(); + _textures.destroy(); _uniforms.destroy(); _materials.destroy(); _shaders.destroy(); @@ -87,6 +89,10 @@ namespace Dynamo::Graphics { void Renderer::destroy_shader(Shader shader) { _shaders.destroy(shader); } + Texture Renderer::build_texture(const TextureDescriptor &descriptor) { return _textures.build(descriptor); } + + void Renderer::destroy_texture(Texture texture) { _textures.destroy(texture); } + Material Renderer::build_material(const MaterialDescriptor &descriptor) { return _materials.build(descriptor, _swapchain, _shaders, _uniforms); } @@ -104,7 +110,14 @@ namespace Dynamo::Graphics { return {}; } - void Renderer::write_uniform(Uniform uniform, void *data) { _uniforms.write(uniform, data); } + void Renderer::write_uniform(Uniform uniform, void *data, unsigned index, unsigned count) { + _uniforms.write(uniform, data, index, count); + } + + void Renderer::bind_texture(Uniform uniform, Texture texture, unsigned index) { + const TextureInstance &instance = _textures.get(texture); + _uniforms.bind(uniform, instance, index); + } void Renderer::draw(const Model &model) { _models.push_back(model); } @@ -123,18 +136,18 @@ namespace Dynamo::Graphics { rebuild_swapchain(); return; } else if (acquire_result != VK_SUCCESS && acquire_result != VK_SUBOPTIMAL_KHR) { - VkResult_log("Acquire Image", acquire_result); + VkResult_check("Acquire Image", acquire_result); } - VkResult_log("Reset Fence", vkResetFences(_device, 1, &frame.sync_fence)); - VkResult_log("Reset Command Buffer", vkResetCommandBuffer(frame.command_buffer, 0)); + VkResult_check("Reset Fence", vkResetFences(_device, 1, &frame.sync_fence)); + VkResult_check("Reset Command Buffer", vkResetCommandBuffer(frame.command_buffer, 0)); VkCommandBufferBeginInfo begin_info = {}; begin_info.sType = VK_STRUCTURE_TYPE_COMMAND_BUFFER_BEGIN_INFO; begin_info.flags = 0; begin_info.pInheritanceInfo = nullptr; - VkResult_log("Begin Command Recording", vkBeginCommandBuffer(frame.command_buffer, &begin_info)); + VkResult_check("Begin Command Recording", vkBeginCommandBuffer(frame.command_buffer, &begin_info)); // Group models by material and geometry std::sort(_models.begin(), _models.end(), [](const Model &a, const Model &b) { @@ -248,7 +261,7 @@ namespace Dynamo::Graphics { if (prev_renderpass != VK_NULL_HANDLE) { vkCmdEndRenderPass(frame.command_buffer); } - VkResult_log("End Command Buffer", vkEndCommandBuffer(frame.command_buffer)); + VkResult_check("End Command Buffer", vkEndCommandBuffer(frame.command_buffer)); // Submit commands VkQueue queue; @@ -265,7 +278,7 @@ namespace Dynamo::Graphics { submit_info.pSignalSemaphores = &frame.sync_render_done; submit_info.pWaitDstStageMask = &wait_stage_mask; - VkResult_log("Graphics Submit", vkQueueSubmit(queue, 1, &submit_info, frame.sync_fence)); + VkResult_check("Graphics Submit", vkQueueSubmit(queue, 1, &submit_info, frame.sync_fence)); // Present the render VkPresentInfoKHR present_info = {}; @@ -282,7 +295,7 @@ namespace Dynamo::Graphics { if (present_result == VK_ERROR_OUT_OF_DATE_KHR || present_result == VK_SUBOPTIMAL_KHR) { rebuild_swapchain(); } else if (present_result != VK_SUCCESS) { - VkResult_log("Present Render", present_result); + VkResult_check("Present Render", present_result); } } } // namespace Dynamo::Graphics \ No newline at end of file diff --git a/src/Graphics/Renderer.hpp b/src/Graphics/Renderer.hpp index e8b7944..2ae39ef 100644 --- a/src/Graphics/Renderer.hpp +++ b/src/Graphics/Renderer.hpp @@ -5,6 +5,7 @@ #include #include #include +#include #include #include #include @@ -13,6 +14,7 @@ #include #include #include +#include #include #include @@ -42,6 +44,7 @@ namespace Dynamo::Graphics { ShaderRegistry _shaders; MaterialRegistry _materials; UniformRegistry _uniforms; + TextureRegistry _textures; FramebufferCache _framebuffers; FrameContextList _frame_contexts; @@ -50,12 +53,16 @@ namespace Dynamo::Graphics { VkClearValue _clear; // TODO - Fixes: + // * Buffer has a lot of problems... Resizing will invalidate handles used by other resources + // - Instead of Buffer, build a MemoryPool that tracks allocated VkBuffers / VkImages + // - A VkBuffer should persist throughout program lifetime to prevent messy rebuilding of allocated resources + // - If a VkBuffer is full, create a new one. Need a sane minimum size to prevent overallocation (limit 4096) + // - Allow passing in fallback memory types when allocating memory // * Pre-defined render pass ---- Define a default render pass to handle the no-draw case - // * Let Buffer take in fallback memory types, only throw when all options exhausted // TODO - Features: + // * Live update texture? --- Support non-shader-optimal image layouts // * Depth-stencil buffer ---- Update jukebox to showcase 3d perspective (to visualize depth buffering) - // * Texture system ---- Similar to shaders / meshes, generate a handle and return // * Draw-to-texture ---- overload render(), render(Texture texture) /** @@ -116,6 +123,21 @@ namespace Dynamo::Graphics { */ void destroy_shader(Shader shader); + /** + * @brief Build a texture. + * + * @param descriptor + * @return Texture + */ + Texture build_texture(const TextureDescriptor &descriptor); + + /** + * @brief Free texture resources. + * + * @param texture + */ + void destroy_texture(Texture texture); + /** * @brief Build a material. * @@ -143,12 +165,25 @@ namespace Dynamo::Graphics { /** * @brief Write to a uniform. * - * Data must match the size of the uniform variable. + * If the uniform is an array, an index offset and count can be provided. * * @param uniform * @param data + * @param index + * @param count + */ + void write_uniform(Uniform uniform, void *data, unsigned index = 0, unsigned count = 1); + + /** + * @brief Bind a texture to a uniform variable. + * + * If the uniform is an array, an index offset can be provided. + * + * @param uniform + * @param texture + * @param index */ - void write_uniform(Uniform uniform, void *data); + void bind_texture(Uniform uniform, Texture texture, unsigned index = 0); /** * @brief Draw a model in the current frame. diff --git a/src/Graphics/Texture.hpp b/src/Graphics/Texture.hpp new file mode 100644 index 0000000..0251fa7 --- /dev/null +++ b/src/Graphics/Texture.hpp @@ -0,0 +1,104 @@ +#pragma once + +#include + +#include + +namespace Dynamo::Graphics { + /** + * @brief Texture resource handle. + * + */ + DYN_DEFINE_ID_TYPE(Texture); + + /** + * @brief Texture formats. + * + */ + enum class TextureFormat { + F32_R_Norm, + U8_RGB_Norm, + U8_RGBA_Norm, + }; + + /** + * @brief Texture filter modes. + * + */ + enum class TextureFilter { + Nearest, + Linear, + }; + + /** + * @brief Texture addressing modes. + * + */ + enum class TextureAddressMode { + Repeat, + RepeatMirror, + Clamp, + ClampMirror, + ClampBorder, + }; + + /** + * @brief Texture descriptor. + * + */ + struct TextureDescriptor { + /** + * @brief Texture unit byte buffer. + * + */ + std::vector texels; + + /** + * @brief Width of the texture in texture units. + * + */ + unsigned width = 0; + + /** + * @brief Height of the texture in texture units. + * + */ + unsigned height = 0; + + /** + * @brief Format of the texture determines how the byte buffer is interpreted. + * + */ + TextureFormat format = TextureFormat::U8_RGBA_Norm; + + /** + * @brief Minification filter. + * + */ + TextureFilter min_filter = TextureFilter::Nearest; + + /** + * @brief Magnification filter. + * + */ + TextureFilter mag_filter = TextureFilter::Nearest; + + /** + * @brief How U coordinates are addressed outside [0, 1). + * + */ + TextureAddressMode u_address_mode = TextureAddressMode::Repeat; + + /** + * @brief How V coordinates are addressed outside [0, 1). + * + */ + TextureAddressMode v_address_mode = TextureAddressMode::Repeat; + + /** + * @brief How W coordinates are addressed outside [0, 1). + * + */ + TextureAddressMode w_address_mode = TextureAddressMode::Repeat; + }; +} // namespace Dynamo::Graphics \ No newline at end of file diff --git a/src/Graphics/Vulkan/Buffer.cpp b/src/Graphics/Vulkan/Buffer.cpp index 7696396..07f06f7 100644 --- a/src/Graphics/Vulkan/Buffer.cpp +++ b/src/Graphics/Vulkan/Buffer.cpp @@ -8,7 +8,7 @@ namespace Dynamo::Graphics::Vulkan { VkBufferUsageFlagBits usage, VkMemoryPropertyFlags properties) : _device(device), - _physical_settings(physical.memory), _command_buffer(command_buffer), + _physical(&physical), _command_buffer(command_buffer), _usage(usage | VK_BUFFER_USAGE_TRANSFER_DST_BIT | VK_BUFFER_USAGE_TRANSFER_SRC_BIT), _properties(properties), _allocator(MIN_ALLOCATION_SIZE) { _handle = VkBuffer_create(_device, _usage, _allocator.capacity(), nullptr, 0); @@ -21,20 +21,20 @@ namespace Dynamo::Graphics::Vulkan { vkGetBufferMemoryRequirements(_device, buffer, &_requirements); unsigned type_index; - for (type_index = 0; type_index < _physical_settings.memoryTypeCount; type_index++) { - VkMemoryType type = _physical_settings.memoryTypes[type_index]; + for (type_index = 0; type_index < _physical->memory.memoryTypeCount; type_index++) { + VkMemoryType type = _physical->memory.memoryTypes[type_index]; if ((_requirements.memoryTypeBits & (1 << type_index)) && ((_properties & type.propertyFlags) == _properties)) { break; } } - if (type_index == _physical_settings.memoryTypeCount) { + if (type_index == _physical->memory.memoryTypeCount) { Log::error("Vulkan could not find suitable memory type for buffer."); } VkDeviceMemory memory = VkDeviceMemory_allocate(_device, type_index, _requirements.size); if (_properties & VK_MEMORY_PROPERTY_HOST_VISIBLE_BIT) { - VkResult_log( + VkResult_check( "Map Device Memory", vkMapMemory(_device, memory, 0, _allocator.capacity(), 0, reinterpret_cast(&_mapped))); DYN_ASSERT(_mapped != nullptr); @@ -106,6 +106,11 @@ namespace Dynamo::Graphics::Vulkan { vkQueueWaitIdle(_transfer_queue); } + void Buffer::copy_to(VkImage &dst, unsigned offset, VkBufferImageCopy *regions, unsigned region_count) { + VkBuffer_immediate_image_copy(_handle, dst, _transfer_queue, _command_buffer, regions, region_count); + vkQueueWaitIdle(_transfer_queue); + } + void Buffer::destroy() { vkDestroyBuffer(_device, _handle, nullptr); vkFreeMemory(_device, _memory, nullptr); diff --git a/src/Graphics/Vulkan/Buffer.hpp b/src/Graphics/Vulkan/Buffer.hpp index c55aad1..3e6b099 100644 --- a/src/Graphics/Vulkan/Buffer.hpp +++ b/src/Graphics/Vulkan/Buffer.hpp @@ -12,13 +12,9 @@ namespace Dynamo::Graphics::Vulkan { */ constexpr unsigned MIN_ALLOCATION_SIZE = 256 * (1 << 20); - /** - * @brief Dynamic Vulkan buffer. - * - */ class Buffer { VkDevice _device; - VkPhysicalDeviceMemoryProperties _physical_settings; + const PhysicalDevice *_physical; VkCommandBuffer _command_buffer; VkQueue _transfer_queue; @@ -33,12 +29,6 @@ namespace Dynamo::Graphics::Vulkan { char *_mapped; - /** - * @brief Allocate memory for a buffer. - * - * @param buffer - * @return VkDeviceMemory - */ VkDeviceMemory allocate(VkBuffer buffer); public: @@ -49,73 +39,24 @@ namespace Dynamo::Graphics::Vulkan { VkMemoryPropertyFlags properties); Buffer() = default; - /** - * @brief Get the buffer handle. - * - * @return VkBuffer - */ VkBuffer handle() const; - /** - * @brief Get the capacity of the buffer. - * - * @return unsigned - */ unsigned capacity() const; - /** - * @brief Reserve a block of memory. - * - * @param size - * @param alignment - * @return unsigned - */ unsigned reserve(unsigned size); - /** - * @brief Free an allocated block. - * - * @param block_offset - * @return unsigned - */ void free(unsigned block_offset); - /** - * @brief Get the size of an allocated block. - * - * @param offset - * @return unsigned - */ unsigned size(unsigned block_offset) const; - /** - * @brief Resize the buffer. - * - * @param size - */ void resize(unsigned size); - /** - * @brief Get a pointer to mapped memory. - * - * @param block_offset - * @return void* - */ void *get_mapped(unsigned block_offset); - /** - * @brief Copy contents to another buffer. - * - * @param dst - * @param regions - * @param region_count - */ void copy_to(Buffer &dst, VkBufferCopy *regions, unsigned region_count); - /** - * @brief Destroy the buffer and free underlying memory. - * - */ + void copy_to(VkImage &dst, unsigned offset, VkBufferImageCopy *regions, unsigned region_count); + void destroy(); }; } // namespace Dynamo::Graphics::Vulkan \ No newline at end of file diff --git a/src/Graphics/Vulkan/FrameContext.hpp b/src/Graphics/Vulkan/FrameContext.hpp index 3e1e77b..cb141d0 100644 --- a/src/Graphics/Vulkan/FrameContext.hpp +++ b/src/Graphics/Vulkan/FrameContext.hpp @@ -8,13 +8,11 @@ namespace Dynamo::Graphics::Vulkan { /** * @brief Maximum number of frames in flight. * + * Can't be too high or we'll experience latency. + * */ constexpr unsigned MAX_FRAMES_IN_FLIGHT = 3; - /** - * @brief Render frame context. - * - */ struct FrameContext { VkFence sync_fence; VkSemaphore sync_render_start; @@ -22,21 +20,12 @@ namespace Dynamo::Graphics::Vulkan { VkCommandBuffer command_buffer; }; - /** - * @brief Frame context list. - * - */ class FrameContextList { VkDevice _device; std::array _contexts; unsigned _index; public: - /** - * @brief Initialize the context objects. - * - * @param command_pool - */ FrameContextList(VkDevice device, VkCommandPool command_pool) : _device(device), _index(0) { std::array buffers; VkCommandBuffer_allocate(device, @@ -53,21 +42,12 @@ namespace Dynamo::Graphics::Vulkan { } FrameContextList() = default; - /** - * @brief Grab the next context object. - * - * @return const FrameContext& - */ const FrameContext &next() { FrameContext &context = _contexts[_index]; _index = (_index + 1) % MAX_FRAMES_IN_FLIGHT; return context; } - /** - * @brief Destroy the context objects. - * - */ void destroy() { for (const FrameContext &context : _contexts) { vkDestroyFence(_device, context.sync_fence, nullptr); diff --git a/src/Graphics/Vulkan/FramebufferCache.hpp b/src/Graphics/Vulkan/FramebufferCache.hpp index 13938bb..8968299 100644 --- a/src/Graphics/Vulkan/FramebufferCache.hpp +++ b/src/Graphics/Vulkan/FramebufferCache.hpp @@ -5,10 +5,6 @@ #include namespace Dynamo::Graphics::Vulkan { - /** - * @brief Framebuffer configuration settings. - * - */ struct FramebufferSettings { VkImageView view; VkExtent2D extent; @@ -31,10 +27,6 @@ namespace Dynamo::Graphics::Vulkan { }; }; - /** - * @brief Framebuffer cache. - * - */ class FramebufferCache { VkDevice _device; std::unordered_map _cache; @@ -43,18 +35,8 @@ namespace Dynamo::Graphics::Vulkan { FramebufferCache(VkDevice device); FramebufferCache() = default; - /** - * @brief Build a framebuffer. - * - * @param settings - * @return VkFramebuffer - */ VkFramebuffer get(const FramebufferSettings &settings); - /** - * @brief Destroy all framebuffers, invalidating existing handles. - * - */ void destroy(); }; } // namespace Dynamo::Graphics::Vulkan \ No newline at end of file diff --git a/src/Graphics/Vulkan/MaterialRegistry.cpp b/src/Graphics/Vulkan/MaterialRegistry.cpp index babcb8f..801c011 100644 --- a/src/Graphics/Vulkan/MaterialRegistry.cpp +++ b/src/Graphics/Vulkan/MaterialRegistry.cpp @@ -17,7 +17,7 @@ namespace Dynamo::Graphics::Vulkan { cache_info.initialDataSize = size; cache_info.pInitialData = buffer.data(); - VkResult_log("Create Pipeline Cache", vkCreatePipelineCache(_device, &cache_info, nullptr, &_pipeline_cache)); + VkResult_check("Create Pipeline Cache", vkCreatePipelineCache(_device, &cache_info, nullptr, &_pipeline_cache)); _ofstream.open(filename, std::ios::trunc | std::ios::binary); } @@ -64,7 +64,7 @@ namespace Dynamo::Graphics::Vulkan { VkRenderPass renderpass; VkResult result = vkCreateRenderPass(_device, &renderpass_info, nullptr, &renderpass); - VkResult_log("Create Render Pass", result); + VkResult_check("Create Render Pass", result); return renderpass; } @@ -189,7 +189,7 @@ namespace Dynamo::Graphics::Vulkan { // Build and cache VkPipeline pipeline; VkResult result = vkCreateGraphicsPipelines(_device, _pipeline_cache, 1, &pipeline_info, nullptr, &pipeline); - VkResult_log("Create Graphics Pipeline", result); + VkResult_check("Create Graphics Pipeline", result); return pipeline; } @@ -213,7 +213,7 @@ namespace Dynamo::Graphics::Vulkan { layout_settings.descriptor_layouts.push_back(set.layout); // TODO: What if we have duplicate set layouts? Can we reuse? DescriptorAllocation allocation = uniforms.allocate(set); - instance.descriptor_sets.push_back(allocation.set); + instance.descriptor_sets.push_back(allocation.descriptor_set); for (Uniform uniform : allocation.uniforms) { instance.uniforms.push_back(uniform); } diff --git a/src/Graphics/Vulkan/MaterialRegistry.hpp b/src/Graphics/Vulkan/MaterialRegistry.hpp index 8637378..c7af5f4 100644 --- a/src/Graphics/Vulkan/MaterialRegistry.hpp +++ b/src/Graphics/Vulkan/MaterialRegistry.hpp @@ -15,10 +15,6 @@ #include namespace Dynamo::Graphics::Vulkan { - /** - * @brief Render pass configuration settings. - * - */ struct RenderPassSettings { VkFormat color_format; VkFormat depth_format; @@ -49,10 +45,6 @@ namespace Dynamo::Graphics::Vulkan { }; }; - /** - * @brief Pipeline layout settings. - * - */ struct PipelineLayoutSettings { std::vector descriptor_layouts; std::vector push_constant_ranges; @@ -98,10 +90,6 @@ namespace Dynamo::Graphics::Vulkan { }; }; - /** - * @brief Graphics pipeline configuration settings. - * - */ struct GraphicsPipelineSettings { VkPrimitiveTopology topology = VK_PRIMITIVE_TOPOLOGY_TRIANGLE_LIST; VkPolygonMode polygon_mode = VK_POLYGON_MODE_FILL; @@ -134,10 +122,6 @@ namespace Dynamo::Graphics::Vulkan { }; }; - /** - * @brief Material instance with references to allocated Vulkan resources. - * - */ struct MaterialInstance { VkRenderPass renderpass; VkPipelineLayout layout; @@ -148,10 +132,6 @@ namespace Dynamo::Graphics::Vulkan { std::vector push_constant_offsets; }; - /** - * @brief Material registry caches Vulkan objects associated with a material. - * - */ class MaterialRegistry { VkDevice _device; std::ofstream _ofstream; @@ -163,68 +143,25 @@ namespace Dynamo::Graphics::Vulkan { SparseArray _instances; - /** - * @brief Create a Vulkan render pass. - * - * @param settings - * @return VkRenderPass - */ VkRenderPass build_renderpass(const RenderPassSettings &settings) const; - /** - * @brief Create a Vulkan pipeline. - * - * @param settings - * @return VkPipeline - */ VkPipeline build_pipeline(const GraphicsPipelineSettings &settings) const; public: MaterialRegistry(VkDevice device, const std::string &filename); MaterialRegistry() = default; - /** - * @brief Build a material and its resources. - * - * @param descriptor - * @param swapchain - * @param shaders - * @param uniforms - * @return MaterialInstance - */ Material build(const MaterialDescriptor &descriptor, const Swapchain &swapchain, const ShaderRegistry &shaders, UniformRegistry &uniforms); - /** - * @brief Get a material instance. - * - * @param material - * @return MaterialInstance& - */ MaterialInstance &get(Material material); - /** - * @brief Destroy a material instance. - * - * Pipeline, layout, and render pass are preserved, only uniforms are freed. - * - * @param material - * @param uniforms - */ void destroy(Material material, UniformRegistry &uniforms); - /** - * @brief Destroy all Vulkan resources. - * - */ void destroy(); - /** - * @brief Write the pipeline cache to disk. - * - */ void write_to_disk(); }; } // namespace Dynamo::Graphics::Vulkan \ No newline at end of file diff --git a/src/Graphics/Vulkan/MeshRegistry.hpp b/src/Graphics/Vulkan/MeshRegistry.hpp index 9ead453..b208c92 100644 --- a/src/Graphics/Vulkan/MeshRegistry.hpp +++ b/src/Graphics/Vulkan/MeshRegistry.hpp @@ -9,10 +9,6 @@ #include namespace Dynamo::Graphics::Vulkan { - /** - * @brief Mesh GPU allocation instance. - * - */ struct MeshAllocation { std::vector attribute_offsets; std::vector buffers; @@ -24,10 +20,6 @@ namespace Dynamo::Graphics::Vulkan { VkIndexType index_type; }; - /** - * @brief Mesh registry. - * - */ class MeshRegistry { VkDevice _device; Buffer _vertex; @@ -40,33 +32,12 @@ namespace Dynamo::Graphics::Vulkan { MeshRegistry(VkDevice device, const PhysicalDevice &physical, VkCommandPool transfer_pool); MeshRegistry() = default; - /** - * @brief Get a mesh allocation. - * - * @param mesh - * @return MeshAllocation& - */ MeshAllocation &get(Mesh mesh); - /** - * @brief Upload a mesh descriptor to VRAM. - * - * @param descriptor - * @return Mesh - */ Mesh build(const MeshDescriptor &descriptor); - /** - * @brief Free all allocated buffers for a mesh. - * - * @param mesh - */ void destroy(Mesh mesh); - /** - * @brief Destroy mesh allocation buffers. - * - */ void destroy(); }; } // namespace Dynamo::Graphics::Vulkan \ No newline at end of file diff --git a/src/Graphics/Vulkan/PhysicalDevice.cpp b/src/Graphics/Vulkan/PhysicalDevice.cpp index 47a09e8..aa2f03e 100644 --- a/src/Graphics/Vulkan/PhysicalDevice.cpp +++ b/src/Graphics/Vulkan/PhysicalDevice.cpp @@ -62,7 +62,7 @@ namespace Dynamo::Graphics::Vulkan { } } - PhysicalDevice PhysicalDevice::select(VkInstance instance, VkSurfaceKHR surface) { + PhysicalDevice PhysicalDevice::select_best(VkInstance instance, VkSurfaceKHR surface) { unsigned count = 0; vkEnumeratePhysicalDevices(instance, &count, nullptr); std::vector handles(count); diff --git a/src/Graphics/Vulkan/PhysicalDevice.hpp b/src/Graphics/Vulkan/PhysicalDevice.hpp index 3193570..245b784 100644 --- a/src/Graphics/Vulkan/PhysicalDevice.hpp +++ b/src/Graphics/Vulkan/PhysicalDevice.hpp @@ -5,10 +5,7 @@ #include namespace Dynamo::Graphics::Vulkan { - /** - * @brief Vulkan Queue Family. - * - */ + struct QueueFamily { unsigned index = 0; unsigned count = 0; @@ -16,20 +13,12 @@ namespace Dynamo::Graphics::Vulkan { }; using QueueFamilyRef = std::reference_wrapper; - /** - * @brief Available swapchain options. - * - */ struct SwapchainOptions { VkSurfaceCapabilitiesKHR capabilities; std::vector formats; std::vector present_modes; }; - /** - * @brief Wrapper object for a Vulkan physical device and its properties. - * - */ struct PhysicalDevice { VkPhysicalDevice handle; VkSurfaceKHR surface; @@ -44,50 +33,17 @@ namespace Dynamo::Graphics::Vulkan { QueueFamily compute_queues; QueueFamily transfer_queues; - /** - * @brief Create PhysicalDevice object. - * - * @param handle - * @param surface - */ PhysicalDevice(VkPhysicalDevice handle, VkSurfaceKHR surface); PhysicalDevice() = default; - /** - * @brief Select the best available physical device. - * - * @param instance - * @param surface - * @return PhysicalDevice - */ - static PhysicalDevice select(VkInstance instance, VkSurfaceKHR surface); + static PhysicalDevice select_best(VkInstance instance, VkSurfaceKHR surface); - /** - * @brief Get the available swapchain configuration options. - * - * @return SwapchainOptions - */ SwapchainOptions get_swapchain_options() const; - /** - * @brief Get the unique set of queue families - * - * @return std::vector - */ std::vector unique_queue_families() const; - /** - * @brief Get the set of required extensions. - * - * @return std::vector - */ std::vector required_extensions() const; - /** - * @brief Compute the desirability "score". - * - * @return unsigned - */ unsigned score() const; }; } // namespace Dynamo::Graphics::Vulkan \ No newline at end of file diff --git a/src/Graphics/Vulkan/ShaderRegistry.cpp b/src/Graphics/Vulkan/ShaderRegistry.cpp index f549138..b65c742 100644 --- a/src/Graphics/Vulkan/ShaderRegistry.cpp +++ b/src/Graphics/Vulkan/ShaderRegistry.cpp @@ -136,20 +136,20 @@ namespace Dynamo::Graphics::Vulkan { // Add descriptor metadata to reflection DescriptorBinding descriptor_binding; descriptor_binding.name = refl_binding.name; + descriptor_binding.type = layout_binding.descriptorType; descriptor_binding.shared = shared_it != shared_uniforms.end(); - descriptor_binding.set = refl_binding.set; descriptor_binding.binding = refl_binding.binding; - descriptor_binding.descriptor_count = layout_binding.descriptorCount; + descriptor_binding.count = layout_binding.descriptorCount; descriptor_binding.size = refl_binding.block.size; descriptor_set.bindings.push_back(descriptor_binding); Log::info( "* Descriptor (name: {}, set: {}, binding: {}, size: {}, dim: {}, shared: {}, type: {}, stage: {})", descriptor_binding.name, - descriptor_binding.set, + refl_binding.set, descriptor_binding.binding, descriptor_binding.size, - descriptor_binding.descriptor_count, + descriptor_binding.count, descriptor_binding.shared, VkDescriptorType_string(layout_binding.descriptorType), VkShaderStageFlagBits_string(static_cast(layout_binding.stageFlags))); diff --git a/src/Graphics/Vulkan/ShaderRegistry.hpp b/src/Graphics/Vulkan/ShaderRegistry.hpp index c58558b..3d56636 100644 --- a/src/Graphics/Vulkan/ShaderRegistry.hpp +++ b/src/Graphics/Vulkan/ShaderRegistry.hpp @@ -11,10 +11,6 @@ #include namespace Dynamo::Graphics::Vulkan { - /** - * @brief Descriptor set layout key. - * - */ struct DescriptorLayoutKey { std::vector bindings; @@ -54,42 +50,26 @@ namespace Dynamo::Graphics::Vulkan { }; }; - /** - * @brief Reflected descriptor binding. - * - */ struct DescriptorBinding { std::string name; - unsigned set; + VkDescriptorType type; unsigned binding; - unsigned descriptor_count; + unsigned count; unsigned size; bool shared; }; - /** - * @brief Descriptor set. - * - */ struct DescriptorSet { VkDescriptorSetLayout layout; std::vector bindings; }; - /** - * @brief Push constant. - * - */ struct PushConstant { std::string name; VkPushConstantRange range; bool shared; }; - /** - * @brief Shader module instance. - * - */ struct ShaderModule { VkShaderModule handle; std::vector bindings; @@ -98,53 +78,20 @@ namespace Dynamo::Graphics::Vulkan { std::vector push_constants; }; - /** - * @brief Shader registry. - * - */ class ShaderRegistry { VkDevice _device; SparseArray _modules; std::unordered_map _descriptor_layouts; - /** - * @brief Compile a shader source. - * - * @param name - * @param code - * @param stage - * @param optimized - * @return std::vector - */ std::vector compile(const std::string &name, const std::string &code, VkShaderStageFlagBits stage, bool optimized); - /** - * @brief Extract vertex inputs from the shader source. - * - * @param module - * @param reflection - */ void reflect_vertex_input(ShaderModule &module, SpvReflectShaderModule reflection); - /** - * @brief Extract descriptor sets from the shader source. - * - * @param module - * @param reflection - * @param shared_uniforms - */ void reflect_descriptor_sets(ShaderModule &module, SpvReflectShaderModule reflection, const std::vector &shared_uniforms); - /** - * @brief Extract push constants from the shader source. - * - * @param module - * @param reflection - * @param shared_uniforms - */ void reflect_push_constants(ShaderModule &module, SpvReflectShaderModule reflection, const std::vector &shared_uniforms); @@ -153,33 +100,12 @@ namespace Dynamo::Graphics::Vulkan { ShaderRegistry(VkDevice device); ShaderRegistry() = default; - /** - * @brief Get a shader module. - * - * @param shader - * @return ShaderModule& - */ const ShaderModule &get(Shader shader) const; - /** - * @brief Build a shader module from a descriptor. - * - * @param descriptor - * @return Shader - */ Shader build(const ShaderDescriptor &descriptor); - /** - * @brief Destroy a shader module. - * - * @param shader - */ void destroy(Shader shader); - /** - * @brief Destroy all existing shader modules. - * - */ void destroy(); }; } // namespace Dynamo::Graphics::Vulkan diff --git a/src/Graphics/Vulkan/Swapchain.cpp b/src/Graphics/Vulkan/Swapchain.cpp index 01ba99c..e483573 100644 --- a/src/Graphics/Vulkan/Swapchain.cpp +++ b/src/Graphics/Vulkan/Swapchain.cpp @@ -77,7 +77,7 @@ namespace Dynamo::Graphics::Vulkan { swapchain_info.oldSwapchain = previous.value().handle; } - VkResult_log("Create Swapchain", vkCreateSwapchainKHR(device, &swapchain_info, nullptr, &handle)); + VkResult_check("Create Swapchain", vkCreateSwapchainKHR(device, &swapchain_info, nullptr, &handle)); // Destroy the old swapchain if (previous.has_value()) { diff --git a/src/Graphics/Vulkan/Swapchain.hpp b/src/Graphics/Vulkan/Swapchain.hpp index 966c8ee..2ce159e 100644 --- a/src/Graphics/Vulkan/Swapchain.hpp +++ b/src/Graphics/Vulkan/Swapchain.hpp @@ -8,10 +8,6 @@ #include namespace Dynamo::Graphics::Vulkan { - /** - * @brief Wrapper object for a Vulkan swapchain and its properties. - * - */ struct Swapchain { VkDevice device; VkSwapchainKHR handle; @@ -23,25 +19,12 @@ namespace Dynamo::Graphics::Vulkan { std::vector images; std::vector views; - /** - * @brief Create a Vulkan swapchain. - * - * @param device - * @param physical - * @param display - * @param previous - * @return Swapchain - */ Swapchain(VkDevice device, const PhysicalDevice &physical, const Display &display, std::optional previous = {}); Swapchain() = default; - /** - * @brief Destroy the swapchain and its resources. - * - */ void destroy(); }; } // namespace Dynamo::Graphics::Vulkan \ No newline at end of file diff --git a/src/Graphics/Vulkan/TextureRegistry.cpp b/src/Graphics/Vulkan/TextureRegistry.cpp new file mode 100644 index 0000000..bfc676f --- /dev/null +++ b/src/Graphics/Vulkan/TextureRegistry.cpp @@ -0,0 +1,162 @@ +#include +#include +#include + +namespace Dynamo::Graphics::Vulkan { + TextureRegistry::TextureRegistry(VkDevice device, const PhysicalDevice &physical, VkCommandPool transfer_pool) : + _device(device), _physical(&physical) { + std::array transfer_commands; + VkCommandBuffer_allocate(_device, + transfer_pool, + VK_COMMAND_BUFFER_LEVEL_PRIMARY, + transfer_commands.data(), + transfer_commands.size()); + _command_buffer = transfer_commands[0]; + vkGetDeviceQueue(_device, physical.transfer_queues.index, 0, &_queue); + _staging = Buffer(device, + physical, + transfer_commands[1], + VK_BUFFER_USAGE_TRANSFER_SRC_BIT, + VK_MEMORY_PROPERTY_HOST_VISIBLE_BIT | VK_MEMORY_PROPERTY_HOST_COHERENT_BIT); + } + + Texture TextureRegistry::build(const TextureDescriptor &descriptor) { + TextureInstance instance; + + // Build sampler + SamplerSettings sampler_settings; + sampler_settings.u_address_mode = convert_texture_address_mode(descriptor.u_address_mode); + sampler_settings.v_address_mode = convert_texture_address_mode(descriptor.v_address_mode); + sampler_settings.w_address_mode = convert_texture_address_mode(descriptor.w_address_mode); + sampler_settings.mag_filter = convert_texture_filter(descriptor.mag_filter); + sampler_settings.min_filter = convert_texture_filter(descriptor.min_filter); + sampler_settings.border_color = VK_BORDER_COLOR_FLOAT_TRANSPARENT_BLACK; + + auto sampler_it = _samplers.find(sampler_settings); + if (sampler_it != _samplers.end()) { + instance.sampler = sampler_it->second; + } else { + instance.sampler = VkSampler_create(_device, + sampler_settings.u_address_mode, + sampler_settings.v_address_mode, + sampler_settings.w_address_mode, + sampler_settings.mag_filter, + sampler_settings.min_filter, + sampler_settings.border_color, + _physical->properties.limits.maxSamplerAnisotropy); + _samplers.emplace(sampler_settings, instance.sampler); + } + + // TODO: What a mess. + // Build image + VkExtent3D extent; + extent.width = descriptor.width; + extent.height = descriptor.height; + extent.depth = 1; + + VkFormat format = convert_texture_format(descriptor.format); + VkImageUsageFlags usage = VK_IMAGE_USAGE_TRANSFER_DST_BIT | VK_IMAGE_USAGE_SAMPLED_BIT; + + instance.image = VkImage_create(_device, + extent, + format, + VK_IMAGE_LAYOUT_UNDEFINED, + VK_IMAGE_TYPE_2D, + VK_IMAGE_TILING_OPTIMAL, + usage, + VK_SAMPLE_COUNT_1_BIT, + 1, + 1, + nullptr, + 0); + + // TODO: This needs to be abstracted away + VkMemoryRequirements requirements; + vkGetImageMemoryRequirements(_device, instance.image, &requirements); + + unsigned type_index; + for (type_index = 0; type_index < _physical->memory.memoryTypeCount; type_index++) { + VkMemoryType type = _physical->memory.memoryTypes[type_index]; + if ((requirements.memoryTypeBits & (1 << type_index)) && + (VK_MEMORY_PROPERTY_DEVICE_LOCAL_BIT & type.propertyFlags)) { + break; + } + } + if (type_index == _physical->memory.memoryTypeCount) { + Log::error("Vulkan could not find suitable memory type for image."); + } + + VkDeviceMemory memory = VkDeviceMemory_allocate(_device, type_index, requirements.size); + vkBindImageMemory(_device, instance.image, memory, 0); + + VkImageSubresourceRange subresources; + subresources.aspectMask = VK_IMAGE_ASPECT_COLOR_BIT; + subresources.baseMipLevel = 0; + subresources.levelCount = 1; + subresources.baseArrayLayer = 0; + subresources.layerCount = 1; + + // Transition to optimal layout for buffer copying + VkImage_transition_layout(instance.image, + _queue, + _command_buffer, + VK_FORMAT_R8G8B8A8_SRGB, + VK_IMAGE_LAYOUT_UNDEFINED, + VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL, + subresources); + + // Copy image to staging buffer + unsigned offset = _staging.reserve(descriptor.texels.size()); + std::memcpy(_staging.get_mapped(offset), descriptor.texels.data(), descriptor.texels.size()); + + // Copy buffer to image + VkBufferImageCopy region = {}; + region.bufferOffset = 0; + region.bufferRowLength = 0; + region.bufferImageHeight = 0; + + region.imageSubresource.aspectMask = VK_IMAGE_ASPECT_COLOR_BIT; + region.imageSubresource.mipLevel = 0; + region.imageSubresource.baseArrayLayer = 0; + region.imageSubresource.layerCount = 1; + + region.imageOffset = {0, 0, 0}; + region.imageExtent = {descriptor.width, descriptor.height, 1}; + + _staging.copy_to(instance.image, offset, ®ion, 1); + + // Transition back to shader read optimal layout + VkImage_transition_layout(instance.image, + _queue, + _command_buffer, + VK_FORMAT_R8G8B8A8_SRGB, + VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL, + VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL, + subresources); + + // Build image view + instance.view = VkImageView_create(_device, instance.image, format, VK_IMAGE_VIEW_TYPE_2D, subresources); + + return _instances.insert(instance); + } + + const TextureInstance &TextureRegistry::get(Texture texture) const { return _instances.get(texture); } + + void TextureRegistry::destroy(Texture texture) { + const TextureInstance &instance = _instances.get(texture); + vkDestroyImageView(_device, instance.view, nullptr); + vkDestroyImage(_device, instance.image, nullptr); + _instances.remove(texture); + } + + void TextureRegistry::destroy() { + _instances.foreach ([&](TextureInstance &instance) { + vkDestroyImageView(_device, instance.view, nullptr); + vkDestroyImage(_device, instance.image, nullptr); + }); + for (const auto &[key, sampler] : _samplers) { + vkDestroySampler(_device, sampler, nullptr); + } + _samplers.clear(); + } +} // namespace Dynamo::Graphics::Vulkan \ No newline at end of file diff --git a/src/Graphics/Vulkan/TextureRegistry.hpp b/src/Graphics/Vulkan/TextureRegistry.hpp new file mode 100644 index 0000000..7fd10ad --- /dev/null +++ b/src/Graphics/Vulkan/TextureRegistry.hpp @@ -0,0 +1,70 @@ +#pragma once + +#include + +#include + +#include +#include +#include +#include + +namespace Dynamo::Graphics::Vulkan { + struct SamplerSettings { + VkSamplerAddressMode u_address_mode; + VkSamplerAddressMode v_address_mode; + VkSamplerAddressMode w_address_mode; + VkFilter min_filter; + VkFilter mag_filter; + VkBorderColor border_color; + + bool operator==(const SamplerSettings &other) const { + return u_address_mode == other.u_address_mode && v_address_mode == other.v_address_mode && + w_address_mode == other.w_address_mode && min_filter == other.min_filter && + mag_filter == other.mag_filter && border_color == other.border_color; + } + + struct Hash { + inline size_t operator()(const SamplerSettings &settings) const { + size_t hash0 = std::hash{}(settings.u_address_mode); + size_t hash1 = std::hash{}(settings.v_address_mode); + size_t hash2 = std::hash{}(settings.w_address_mode); + size_t hash3 = std::hash{}(settings.min_filter); + size_t hash4 = std::hash{}(settings.mag_filter); + size_t hash5 = std::hash{}(settings.border_color); + + return hash0 ^ (hash1 << 1) ^ (hash2 << 2) ^ (hash3 << 3) ^ (hash4 << 4) ^ (hash5 << 5); + } + }; + }; + + struct TextureInstance { + VkImage image; + VkImageView view; + VkSampler sampler; + }; + + class TextureRegistry { + VkDevice _device; + const PhysicalDevice *_physical; + Buffer _staging; + + VkQueue _queue; + VkCommandBuffer _command_buffer; + + std::unordered_map _samplers; + SparseArray _instances; + + public: + TextureRegistry(VkDevice device, const PhysicalDevice &physical, VkCommandPool transfer_pool); + TextureRegistry() = default; + + Texture build(const TextureDescriptor &descriptor); + + const TextureInstance &get(Texture texture) const; + + void destroy(Texture texture); + + void destroy(); + }; +} // namespace Dynamo::Graphics::Vulkan \ No newline at end of file diff --git a/src/Graphics/Vulkan/UniformRegistry.cpp b/src/Graphics/Vulkan/UniformRegistry.cpp index 98b0cda..2b149b0 100644 --- a/src/Graphics/Vulkan/UniformRegistry.cpp +++ b/src/Graphics/Vulkan/UniformRegistry.cpp @@ -4,10 +4,10 @@ namespace Dynamo::Graphics::Vulkan { UniformRegistry::UniformRegistry(VkDevice device, const PhysicalDevice &physical, VkCommandPool transfer_pool) : _device(device) { - std::array sizes; - sizes[0].type = VK_DESCRIPTOR_TYPE_UNIFORM_BUFFER; - sizes[0].descriptorCount = 1024; - + std::array sizes = { + VkDescriptorPoolSize{VK_DESCRIPTOR_TYPE_UNIFORM_BUFFER, 1024}, + VkDescriptorPoolSize{VK_DESCRIPTOR_TYPE_COMBINED_IMAGE_SAMPLER, 1024}, + }; _pool = VkDescriptorPool_create(device, sizes.data(), sizes.size(), 1024); VkCommandBuffer transfer_buffer; @@ -18,8 +18,45 @@ namespace Dynamo::Graphics::Vulkan { VK_BUFFER_USAGE_UNIFORM_BUFFER_BIT, VK_MEMORY_PROPERTY_HOST_VISIBLE_BIT | VK_MEMORY_PROPERTY_HOST_COHERENT_BIT); - // Minimum of 256 bytes for push constants - _push_constant_buffer = VirtualMemory(256); + // Limit of 128 bytes for push constants + _push_constant_buffer = VirtualMemory(128); + } + + unsigned UniformRegistry::allocate_uniform_buffer(VkDescriptorSet descriptor_set, DescriptorBinding &binding) { + // Allocate shared uniform binding once only + unsigned alloc_size = binding.size * binding.count; + unsigned block_offset = 0; + if (binding.shared) { + auto shared_it = _shared_offsets.find(binding.name); + if (shared_it != _shared_offsets.end()) { + block_offset = shared_it->second; + } else { + block_offset = _uniform_buffer.reserve(alloc_size); + _shared_offsets.emplace(binding.name, block_offset); + } + } else { + block_offset = _uniform_buffer.reserve(alloc_size); + } + + // Write each binding array element + for (unsigned i = 0; i < binding.count; i++) { + VkDescriptorBufferInfo buffer_info; + buffer_info.buffer = _uniform_buffer.handle(); + buffer_info.offset = block_offset + i * binding.size; + buffer_info.range = binding.size; + + VkWriteDescriptorSet write = {}; + write.sType = VK_STRUCTURE_TYPE_WRITE_DESCRIPTOR_SET; + write.descriptorType = binding.type; + write.dstSet = descriptor_set; + write.dstBinding = binding.binding; + write.dstArrayElement = i; + write.descriptorCount = 1; + write.pBufferInfo = &buffer_info; + vkUpdateDescriptorSets(_device, 1, &write, 0, nullptr); + } + + return block_offset; } DescriptorAllocation UniformRegistry::allocate(const DescriptorSet &set) { @@ -27,49 +64,26 @@ namespace Dynamo::Graphics::Vulkan { // TODO: Recycle descriptor sets that are not used // TODO: Need to allocate a new descriptor pool if this fails - VkDescriptorSet_allocate(_device, _pool, &set.layout, &allocation.set, 1); + VkDescriptorSet_allocate(_device, _pool, &set.layout, &allocation.descriptor_set, 1); - // Map descriptors to buffer + // Process uniform bindings for (DescriptorBinding binding : set.bindings) { - unsigned block_size = binding.size * binding.descriptor_count; - - // Allocate shared uniform once only - unsigned block_offset; - if (binding.shared) { - auto shared_it = _shared_offsets.find(binding.name); - if (shared_it != _shared_offsets.end()) { - block_offset = shared_it->second; - } else { - block_offset = _uniform_buffer.reserve(block_size); - _shared_offsets.emplace(binding.name, block_offset); - } - } else { - block_offset = _uniform_buffer.reserve(block_size); - } - - // Write each binding array element - for (unsigned i = 0; i < binding.descriptor_count; i++) { - VkDescriptorBufferInfo buffer_info; - buffer_info.buffer = _uniform_buffer.handle(); - buffer_info.offset = block_offset + i * binding.size; - buffer_info.range = binding.size; - - VkWriteDescriptorSet write = {}; - write.sType = VK_STRUCTURE_TYPE_WRITE_DESCRIPTOR_SET; - write.descriptorType = VK_DESCRIPTOR_TYPE_UNIFORM_BUFFER; - write.dstSet = allocation.set; - write.dstBinding = binding.binding; - write.dstArrayElement = i; - write.descriptorCount = 1; - write.pBufferInfo = &buffer_info; - vkUpdateDescriptorSets(_device, 1, &write, 0, nullptr); - } - UniformVariable var; var.name = binding.name; var.type = UniformVariableType::Descriptor; - var.block_offset = block_offset; - var.block_size = block_size; + var.descriptor_type = binding.type; + var.descriptor_set = allocation.descriptor_set; + var.binding = binding.binding; + var.array_count = binding.count; + var.block_size = binding.size; + + // Handle each descriptor type + switch (binding.type) { + case VK_DESCRIPTOR_TYPE_UNIFORM_BUFFER: + var.block_offset = allocate_uniform_buffer(allocation.descriptor_set, binding); + default: + break; + } allocation.uniforms.push_back(_variables.insert(var)); } @@ -80,6 +94,13 @@ namespace Dynamo::Graphics::Vulkan { UniformVariable var; var.name = push_constant.name; var.type = UniformVariableType::PushConstant; + + // Unused, only applicable to descriptors + var.descriptor_type = VK_DESCRIPTOR_TYPE_UNIFORM_BUFFER; + var.descriptor_set = VK_NULL_HANDLE; + var.binding = 0; + var.array_count = 0; + var.block_size = push_constant.range.size; // Allocate shared uniform once only @@ -109,18 +130,38 @@ namespace Dynamo::Graphics::Vulkan { return _push_constant_buffer.get_mapped(block_offset); } - void UniformRegistry::write(Uniform uniform, void *data) { + void UniformRegistry::write(Uniform uniform, void *data, unsigned index, unsigned count) { const UniformVariable &var = _variables.get(uniform); - void *ptr = nullptr; + char *ptr = nullptr; switch (var.type) { case UniformVariableType::Descriptor: - ptr = _uniform_buffer.get_mapped(var.block_offset); + ptr = static_cast(_uniform_buffer.get_mapped(var.block_offset)); break; case UniformVariableType::PushConstant: - ptr = _push_constant_buffer.get_mapped(var.block_offset); + ptr = static_cast(_push_constant_buffer.get_mapped(var.block_offset)); break; } - std::memcpy(ptr, data, var.block_size); + std::memcpy(ptr + index * var.block_size, data, var.block_size * count); + } + + void UniformRegistry::bind(Uniform uniform, const TextureInstance &texture, unsigned index) { + UniformVariable &var = _variables.get(uniform); + + VkDescriptorImageInfo image_info; + image_info.imageView = texture.view; + image_info.imageLayout = VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL; + image_info.sampler = texture.sampler; + + VkWriteDescriptorSet write = {}; + write.sType = VK_STRUCTURE_TYPE_WRITE_DESCRIPTOR_SET; + write.descriptorType = VK_DESCRIPTOR_TYPE_COMBINED_IMAGE_SAMPLER; + write.dstSet = var.descriptor_set; + write.dstBinding = var.binding; + write.dstArrayElement = index; + write.descriptorCount = 1; + write.pImageInfo = &image_info; + + vkUpdateDescriptorSets(_device, 1, &write, 0, nullptr); } void UniformRegistry::free(Uniform uniform) { diff --git a/src/Graphics/Vulkan/UniformRegistry.hpp b/src/Graphics/Vulkan/UniformRegistry.hpp index b83c758..e69c1b8 100644 --- a/src/Graphics/Vulkan/UniformRegistry.hpp +++ b/src/Graphics/Vulkan/UniformRegistry.hpp @@ -4,15 +4,17 @@ #include +#include #include #include #include +#include #include #include namespace Dynamo::Graphics::Vulkan { /** - * @brief In Vulkan, shader variables can be from a descriptor or push constant. + * @brief In Vulkan, uniform variables can be from a descriptor or push constant. * * Renderer API should be able to access both types with the same API. * @@ -22,40 +24,28 @@ namespace Dynamo::Graphics::Vulkan { PushConstant, }; - /** - * @brief Uniform variable block data. - * - */ struct UniformVariable { std::string name; UniformVariableType type; - unsigned block_offset; + VkDescriptorType descriptor_type; + VkDescriptorSet descriptor_set; + unsigned binding; + unsigned array_count; unsigned block_size; + unsigned block_offset; }; - /** - * @brief Descriptor allocation result. - * - */ struct DescriptorAllocation { - VkDescriptorSet set; + VkDescriptorSet descriptor_set; std::vector uniforms; }; - /** - * @brief Push constant allocation result. - * - */ struct PushConstantAllocation { Uniform uniform; VkPushConstantRange range; unsigned block_offset; }; - /** - * @brief Uniform shader variable registry. - * - */ class UniformRegistry { VkDevice _device; VkDescriptorPool _pool; @@ -65,61 +55,26 @@ namespace Dynamo::Graphics::Vulkan { std::unordered_map _shared_offsets; SparseArray _variables; + unsigned allocate_uniform_buffer(VkDescriptorSet descriptor_set, DescriptorBinding &binding); + public: UniformRegistry(VkDevice device, const PhysicalDevice &physical, VkCommandPool transfer_pool); UniformRegistry() = default; - /** - * @brief Reserve memory for uniforms from a descriptor set. - * - * @param set - * @return DescriptorAllocation - */ DescriptorAllocation allocate(const DescriptorSet &set); - /** - * @brief Reserve memory for uniforms from a push constant. - * - * @param push_constant - * @return PushConstantAllocation - */ PushConstantAllocation allocate(const PushConstant &push_constant); - /** - * @brief Get a uniform variable. - * - * @param uniform - * @return const UniformVariable& - */ const UniformVariable &get(Uniform uniform); - /** - * @brief Get the data pointer to a push constant variable - * - * @param block_offset - * @return void* - */ void *get_push_constant_data(unsigned block_offset); - /** - * @brief Write a value to a uniform variable. - * - * @param uniform - * @param data - */ - void write(Uniform uniform, void *data); - - /** - * @brief Free a uniform variable. - * - * @param uniform - */ + void write(Uniform uniform, void *data, unsigned index, unsigned count); + + void bind(Uniform uniform, const TextureInstance &texture, unsigned index); + void free(Uniform uniform); - /** - * @brief Destroy all uniform allocations. - * - */ void destroy(); }; } // namespace Dynamo::Graphics::Vulkan \ No newline at end of file diff --git a/src/Graphics/Vulkan/Utils.cpp b/src/Graphics/Vulkan/Utils.cpp index 20f1297..9e6459f 100644 --- a/src/Graphics/Vulkan/Utils.cpp +++ b/src/Graphics/Vulkan/Utils.cpp @@ -477,7 +477,56 @@ namespace Dynamo::Graphics::Vulkan { } } - void VkResult_log(const std::string &op_message, VkResult result) { + const char *VkImageLayout_string(VkImageLayout layout) { + switch (layout) { + case VK_IMAGE_LAYOUT_UNDEFINED: + return "VK_IMAGE_LAYOUT_UNDEFINED"; + case VK_IMAGE_LAYOUT_GENERAL: + return "VK_IMAGE_LAYOUT_GENERAL"; + case VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL: + return "VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL"; + case VK_IMAGE_LAYOUT_DEPTH_STENCIL_ATTACHMENT_OPTIMAL: + return "VK_IMAGE_LAYOUT_DEPTH_STENCIL_ATTACHMENT_OPTIMAL"; + case VK_IMAGE_LAYOUT_DEPTH_STENCIL_READ_ONLY_OPTIMAL: + return "VK_IMAGE_LAYOUT_DEPTH_STENCIL_READ_ONLY_OPTIMAL"; + case VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL: + return "VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL"; + case VK_IMAGE_LAYOUT_TRANSFER_SRC_OPTIMAL: + return "VK_IMAGE_LAYOUT_TRANSFER_SRC_OPTIMAL"; + case VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL: + return "VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL"; + case VK_IMAGE_LAYOUT_PREINITIALIZED: + return "VK_IMAGE_LAYOUT_PREINITIALIZED"; + case VK_IMAGE_LAYOUT_DEPTH_READ_ONLY_STENCIL_ATTACHMENT_OPTIMAL: + return "VK_IMAGE_LAYOUT_DEPTH_READ_ONLY_STENCIL_ATTACHMENT_OPTIMAL"; + case VK_IMAGE_LAYOUT_DEPTH_ATTACHMENT_STENCIL_READ_ONLY_OPTIMAL: + return "VK_IMAGE_LAYOUT_DEPTH_ATTACHMENT_STENCIL_READ_ONLY_OPTIMAL"; + case VK_IMAGE_LAYOUT_DEPTH_ATTACHMENT_OPTIMAL: + return "VK_IMAGE_LAYOUT_DEPTH_ATTACHMENT_OPTIMAL"; + case VK_IMAGE_LAYOUT_DEPTH_READ_ONLY_OPTIMAL: + return "VK_IMAGE_LAYOUT_DEPTH_READ_ONLY_OPTIMAL"; + case VK_IMAGE_LAYOUT_STENCIL_ATTACHMENT_OPTIMAL: + return "VK_IMAGE_LAYOUT_STENCIL_ATTACHMENT_OPTIMAL"; + case VK_IMAGE_LAYOUT_STENCIL_READ_ONLY_OPTIMAL: + return "VK_IMAGE_LAYOUT_STENCIL_READ_ONLY_OPTIMAL"; + case VK_IMAGE_LAYOUT_PRESENT_SRC_KHR: + return "VK_IMAGE_LAYOUT_PRESENT_SRC_KHR"; + case VK_IMAGE_LAYOUT_SHARED_PRESENT_KHR: + return "VK_IMAGE_LAYOUT_SHARED_PRESENT_KHR"; + case VK_IMAGE_LAYOUT_FRAGMENT_DENSITY_MAP_OPTIMAL_EXT: + return "VK_IMAGE_LAYOUT_FRAGMENT_DENSITY_MAP_OPTIMAL_EXT"; + case VK_IMAGE_LAYOUT_FRAGMENT_SHADING_RATE_ATTACHMENT_OPTIMAL_KHR: + return "VK_IMAGE_LAYOUT_FRAGMENT_SHADING_RATE_ATTACHMENT_OPTIMAL_KHR"; + case VK_IMAGE_LAYOUT_READ_ONLY_OPTIMAL_KHR: + return "VK_IMAGE_LAYOUT_READ_ONLY_OPTIMAL_KHR"; + case VK_IMAGE_LAYOUT_ATTACHMENT_OPTIMAL_KHR: + return "VK_IMAGE_LAYOUT_ATTACHMENT_OPTIMAL_KHR"; + case VK_IMAGE_LAYOUT_MAX_ENUM: + return "VK_IMAGE_LAYOUT_MAX_ENUM"; + } + } + + void VkResult_check(const std::string &op_message, VkResult result) { if (result != VK_SUCCESS) { Log::error("Graphics::Vulkan {}: {}", op_message, VkResult_string(result)); } @@ -516,6 +565,41 @@ namespace Dynamo::Graphics::Vulkan { } } + VkFormat convert_texture_format(TextureFormat format) { + switch (format) { + case TextureFormat::F32_R_Norm: + return VK_FORMAT_R32_SFLOAT; + case TextureFormat::U8_RGB_Norm: + return VK_FORMAT_R8G8B8_UNORM; + case TextureFormat::U8_RGBA_Norm: + return VK_FORMAT_R8G8B8A8_UNORM; + } + } + + VkFilter convert_texture_filter(TextureFilter filter) { + switch (filter) { + case TextureFilter::Nearest: + return VK_FILTER_NEAREST; + case TextureFilter::Linear: + return VK_FILTER_LINEAR; + } + } + + VkSamplerAddressMode convert_texture_address_mode(TextureAddressMode address_mode) { + switch (address_mode) { + case TextureAddressMode::Repeat: + return VK_SAMPLER_ADDRESS_MODE_REPEAT; + case TextureAddressMode::RepeatMirror: + return VK_SAMPLER_ADDRESS_MODE_MIRRORED_REPEAT; + case TextureAddressMode::Clamp: + return VK_SAMPLER_ADDRESS_MODE_CLAMP_TO_EDGE; + case TextureAddressMode::ClampMirror: + return VK_SAMPLER_ADDRESS_MODE_MIRROR_CLAMP_TO_EDGE; + case TextureAddressMode::ClampBorder: + return VK_SAMPLER_ADDRESS_MODE_CLAMP_TO_BORDER; + } + } + VkInstance VkInstance_create(const Display &display) { std::vector extensions = display.get_vulkan_extensions(); @@ -552,7 +636,7 @@ namespace Dynamo::Graphics::Vulkan { Log::info(""); VkInstance instance; - VkResult_log("Create Instance", vkCreateInstance(&instance_info, nullptr, &instance)); + VkResult_check("Create Instance", vkCreateInstance(&instance_info, nullptr, &instance)); return instance; } @@ -573,7 +657,7 @@ namespace Dynamo::Graphics::Vulkan { debugger_info.pfnUserCallback = &VkDebugUtilsMessengerEXT_message_callback; VkDebugUtilsMessengerEXT debugger; - VkResult_log("Create Debugger", vkCreateDebugUtilsMessengerEXT(instance, &debugger_info, nullptr, &debugger)); + VkResult_check("Create Debugger", vkCreateDebugUtilsMessengerEXT(instance, &debugger_info, nullptr, &debugger)); return debugger; } @@ -615,7 +699,7 @@ namespace Dynamo::Graphics::Vulkan { device_info.pNext = &descriptor_indexing; VkDevice device; - VkResult_log("Create Device", vkCreateDevice(physical.handle, &device_info, nullptr, &device)); + VkResult_check("Create Device", vkCreateDevice(physical.handle, &device_info, nullptr, &device)); return device; } @@ -626,7 +710,7 @@ namespace Dynamo::Graphics::Vulkan { alloc_info.allocationSize = size; VkDeviceMemory memory; - VkResult_log("Allocate Memory", vkAllocateMemory(device, &alloc_info, nullptr, &memory)); + VkResult_check("Allocate Memory", vkAllocateMemory(device, &alloc_info, nullptr, &memory)); return memory; } @@ -654,7 +738,7 @@ namespace Dynamo::Graphics::Vulkan { } VkBuffer buffer; - VkResult_log("Create Buffer", vkCreateBuffer(device, &buffer_info, nullptr, &buffer)); + VkResult_check("Create Buffer", vkCreateBuffer(device, &buffer_info, nullptr, &buffer)); return buffer; } @@ -682,6 +766,125 @@ namespace Dynamo::Graphics::Vulkan { vkQueueSubmit(queue, 1, &submit_info, VK_NULL_HANDLE); } + void VkBuffer_immediate_image_copy(VkBuffer src, + VkImage dst, + VkQueue queue, + VkCommandBuffer command_buffer, + VkBufferImageCopy *regions, + unsigned region_count) { + // Copy command + VkCommandBufferBeginInfo begin_info = {}; + begin_info.sType = VK_STRUCTURE_TYPE_COMMAND_BUFFER_BEGIN_INFO; + begin_info.flags = VK_COMMAND_BUFFER_USAGE_ONE_TIME_SUBMIT_BIT; + + vkBeginCommandBuffer(command_buffer, &begin_info); + vkCmdCopyBufferToImage(command_buffer, src, dst, VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL, region_count, regions); + vkEndCommandBuffer(command_buffer); + + // Submit the command to the transfer queue + VkSubmitInfo submit_info = {}; + submit_info.sType = VK_STRUCTURE_TYPE_SUBMIT_INFO; + submit_info.commandBufferCount = 1; + submit_info.pCommandBuffers = &command_buffer; + + vkQueueSubmit(queue, 1, &submit_info, VK_NULL_HANDLE); + } + + VkImage VkImage_create(VkDevice device, + const VkExtent3D &extent, + VkFormat format, + VkImageLayout layout, + VkImageType type, + VkImageTiling tiling, + VkImageUsageFlags usage, + VkSampleCountFlagBits samples, + unsigned mip_levels, + unsigned array_layers, + const QueueFamily *queue_families, + unsigned queue_family_count) { + std::vector family_indices; + for (unsigned i = 0; i < queue_family_count; i++) { + family_indices.push_back(queue_families[i].index); + } + + VkImageCreateInfo image_info = {}; + image_info.sType = VK_STRUCTURE_TYPE_IMAGE_CREATE_INFO; + image_info.extent = extent; + image_info.format = format; + image_info.imageType = type; + image_info.usage = usage; + image_info.initialLayout = layout; + image_info.samples = samples; + image_info.mipLevels = mip_levels; + image_info.arrayLayers = array_layers; + image_info.queueFamilyIndexCount = family_indices.size(); + image_info.pQueueFamilyIndices = family_indices.data(); + + if (family_indices.size() > 1) { + image_info.sharingMode = VK_SHARING_MODE_CONCURRENT; + } else { + image_info.sharingMode = VK_SHARING_MODE_EXCLUSIVE; + } + + VkImage image; + VkResult_check("Create Image", vkCreateImage(device, &image_info, nullptr, &image)); + return image; + } + + void VkImage_transition_layout(VkImage image, + VkQueue queue, + VkCommandBuffer command_buffer, + VkFormat format, + VkImageLayout prev, + VkImageLayout next, + const VkImageSubresourceRange &subresources) { + VkCommandBufferBeginInfo begin_info = {}; + begin_info.sType = VK_STRUCTURE_TYPE_COMMAND_BUFFER_BEGIN_INFO; + begin_info.flags = VK_COMMAND_BUFFER_USAGE_ONE_TIME_SUBMIT_BIT; + + VkImageMemoryBarrier barrier = {}; + barrier.sType = VK_STRUCTURE_TYPE_IMAGE_MEMORY_BARRIER; + barrier.image = image; + barrier.oldLayout = prev; + barrier.newLayout = next; + barrier.subresourceRange = subresources; + barrier.srcQueueFamilyIndex = VK_QUEUE_FAMILY_IGNORED; + barrier.dstQueueFamilyIndex = VK_QUEUE_FAMILY_IGNORED; + + VkPipelineStageFlags src_stage = 0; + VkPipelineStageFlags dst_stage = 0; + + if (prev == VK_IMAGE_LAYOUT_UNDEFINED && next == VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL) { + barrier.srcAccessMask = 0; + barrier.dstAccessMask = VK_ACCESS_TRANSFER_WRITE_BIT; + + src_stage = VK_PIPELINE_STAGE_TOP_OF_PIPE_BIT; + dst_stage = VK_PIPELINE_STAGE_TRANSFER_BIT; + } else if (prev == VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL && next == VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL) { + barrier.srcAccessMask = VK_ACCESS_TRANSFER_WRITE_BIT; + barrier.dstAccessMask = VK_ACCESS_SHADER_READ_BIT; + + src_stage = VK_PIPELINE_STAGE_TRANSFER_BIT; + dst_stage = VK_PIPELINE_STAGE_FRAGMENT_SHADER_BIT; + } else { + Log::error("Graphics::Vulkan unsupported image layout transition: {} -> {}", + VkImageLayout_string(prev), + VkImageLayout_string(next)); + } + + vkBeginCommandBuffer(command_buffer, &begin_info); + vkCmdPipelineBarrier(command_buffer, src_stage, dst_stage, 0, 0, nullptr, 0, nullptr, 1, &barrier); + vkEndCommandBuffer(command_buffer); + + // Submit the command to the transfer queue + VkSubmitInfo submit_info = {}; + submit_info.sType = VK_STRUCTURE_TYPE_SUBMIT_INFO; + submit_info.commandBufferCount = 1; + submit_info.pCommandBuffers = &command_buffer; + + vkQueueSubmit(queue, 1, &submit_info, VK_NULL_HANDLE); + } + VkImageView VkImageView_create(VkDevice device, VkImage image, VkFormat format, @@ -697,10 +900,44 @@ namespace Dynamo::Graphics::Vulkan { view_info.subresourceRange = subresources; VkImageView view; - VkResult_log("Create ImageView", vkCreateImageView(device, &view_info, nullptr, &view)); + VkResult_check("Create ImageView", vkCreateImageView(device, &view_info, nullptr, &view)); return view; } + VkSampler VkSampler_create(VkDevice device, + VkSamplerAddressMode address_mode_u, + VkSamplerAddressMode address_mode_v, + VkSamplerAddressMode address_mode_w, + VkFilter mag_filter, + VkFilter min_filter, + VkBorderColor border_color, + float max_anisotropy) { + VkSamplerCreateInfo sampler_info = {}; + sampler_info.sType = VK_STRUCTURE_TYPE_SAMPLER_CREATE_INFO; + sampler_info.addressModeU = address_mode_u; + sampler_info.addressModeV = address_mode_v; + sampler_info.addressModeW = address_mode_w; + sampler_info.magFilter = mag_filter; + sampler_info.minFilter = min_filter; + sampler_info.borderColor = border_color; + sampler_info.maxAnisotropy = max_anisotropy; + sampler_info.anisotropyEnable = VK_TRUE; + sampler_info.unnormalizedCoordinates = VK_FALSE; + + // TODO + sampler_info.compareEnable = VK_FALSE; + sampler_info.compareOp = VK_COMPARE_OP_ALWAYS; + + sampler_info.mipmapMode = VK_SAMPLER_MIPMAP_MODE_LINEAR; + sampler_info.mipLodBias = 0.0f; + sampler_info.minLod = 0.0f; + sampler_info.maxLod = 0.0f; + + VkSampler sampler; + VkResult_check("Create Sampler", vkCreateSampler(device, &sampler_info, nullptr, &sampler)); + return sampler; + } + VkDescriptorSetLayout VkDescriptorSetLayout_create(VkDevice device, const VkDescriptorSetLayoutBinding *bindings, unsigned binding_count) { @@ -710,8 +947,8 @@ namespace Dynamo::Graphics::Vulkan { layout_info.pBindings = bindings; VkDescriptorSetLayout vk_layout; - VkResult_log("Create Descriptor Set Layout", - vkCreateDescriptorSetLayout(device, &layout_info, nullptr, &vk_layout)); + VkResult_check("Create Descriptor Set Layout", + vkCreateDescriptorSetLayout(device, &layout_info, nullptr, &vk_layout)); return vk_layout; } @@ -728,7 +965,7 @@ namespace Dynamo::Graphics::Vulkan { layout_info.pPushConstantRanges = push_constant_ranges; VkPipelineLayout layout; - VkResult_log("Create Pipeline Layout", vkCreatePipelineLayout(device, &layout_info, nullptr, &layout)); + VkResult_check("Create Pipeline Layout", vkCreatePipelineLayout(device, &layout_info, nullptr, &layout)); return layout; } @@ -739,7 +976,7 @@ namespace Dynamo::Graphics::Vulkan { shader_info.codeSize = bytecode.size() * sizeof(uint32_t); VkShaderModule shader; - VkResult_log("Create Shader Module", vkCreateShaderModule(device, &shader_info, nullptr, &shader)); + VkResult_check("Create Shader Module", vkCreateShaderModule(device, &shader_info, nullptr, &shader)); return shader; } @@ -759,7 +996,7 @@ namespace Dynamo::Graphics::Vulkan { framebuffer_info.layers = layer_count; VkFramebuffer framebuffer; - VkResult_log("Create Framebuffer", vkCreateFramebuffer(device, &framebuffer_info, nullptr, &framebuffer)); + VkResult_check("Create Framebuffer", vkCreateFramebuffer(device, &framebuffer_info, nullptr, &framebuffer)); return framebuffer; } @@ -770,7 +1007,7 @@ namespace Dynamo::Graphics::Vulkan { pool_info.queueFamilyIndex = family.index; VkCommandPool pool; - VkResult_log("Create Command Pool", vkCreateCommandPool(device, &pool_info, nullptr, &pool)); + VkResult_check("Create Command Pool", vkCreateCommandPool(device, &pool_info, nullptr, &pool)); return pool; } @@ -785,7 +1022,7 @@ namespace Dynamo::Graphics::Vulkan { alloc_info.level = level; alloc_info.commandBufferCount = count; - VkResult_log("Allocate Command Buffers", vkAllocateCommandBuffers(device, &alloc_info, dst)); + VkResult_check("Allocate Command Buffers", vkAllocateCommandBuffers(device, &alloc_info, dst)); } VkDescriptorPool @@ -798,7 +1035,7 @@ namespace Dynamo::Graphics::Vulkan { pool_info.flags = VK_DESCRIPTOR_POOL_CREATE_UPDATE_AFTER_BIND_BIT; VkDescriptorPool pool; - VkResult_log("Create Descriptor Pool", vkCreateDescriptorPool(device, &pool_info, nullptr, &pool)); + VkResult_check("Create Descriptor Pool", vkCreateDescriptorPool(device, &pool_info, nullptr, &pool)); return pool; } @@ -813,7 +1050,7 @@ namespace Dynamo::Graphics::Vulkan { alloc_info.descriptorSetCount = count; alloc_info.pSetLayouts = layouts; - VkResult_log("Allocate Descriptor Sets", vkAllocateDescriptorSets(device, &alloc_info, dst)); + VkResult_check("Allocate Descriptor Sets", vkAllocateDescriptorSets(device, &alloc_info, dst)); } VkFence VkFence_create(VkDevice device) { @@ -822,7 +1059,7 @@ namespace Dynamo::Graphics::Vulkan { fence_info.flags = VK_FENCE_CREATE_SIGNALED_BIT; VkFence fence; - VkResult_log("Create Fence", vkCreateFence(device, &fence_info, nullptr, &fence)); + VkResult_check("Create Fence", vkCreateFence(device, &fence_info, nullptr, &fence)); return fence; } @@ -831,7 +1068,7 @@ namespace Dynamo::Graphics::Vulkan { semaphore_info.sType = VK_STRUCTURE_TYPE_SEMAPHORE_CREATE_INFO; VkSemaphore semaphore; - VkResult_log("Create Semaphore", vkCreateSemaphore(device, &semaphore_info, nullptr, &semaphore)); + VkResult_check("Create Semaphore", vkCreateSemaphore(device, &semaphore_info, nullptr, &semaphore)); return semaphore; } } // namespace Dynamo::Graphics::Vulkan \ No newline at end of file diff --git a/src/Graphics/Vulkan/Utils.hpp b/src/Graphics/Vulkan/Utils.hpp index dd67b55..9682f39 100644 --- a/src/Graphics/Vulkan/Utils.hpp +++ b/src/Graphics/Vulkan/Utils.hpp @@ -6,142 +6,51 @@ #include #include +#include #include #include namespace Dynamo::Graphics::Vulkan { - /** - * @brief Compute the size of VkFormat - * - * @param format - * @return unsigned - */ unsigned VkFormat_size(VkFormat format); - /** - * @brief Convert VkPhysicalDeviceType to string. - * - * @param type - * @return const char* - */ const char *VkPhysicalDeviceType_string(VkPhysicalDeviceType type); - /** - * @brief Convert VkShaderStageFlagBits to string. - * - * @param stage - * @return const char* - */ const char *VkShaderStageFlagBits_string(VkShaderStageFlagBits stage); - /** - * @brief Convert VkDescriptorType to string. - * - * @param type - * @return const char* - */ const char *VkDescriptorType_string(VkDescriptorType type); - /** - * @brief Convert VkResult to string. - * - * @param result - * @return const char* - */ const char *VkResult_string(VkResult result); - /** - * @brief Log the result of a Vulkan operation. - * - * @param op_message - * @param result - */ - void VkResult_log(const std::string &op_message, VkResult result); - - /** - * @brief Convert Fill to VkPolygonMode. - * - * @param fill - * @return VkPolygonMode - */ + const char *VkImageLayout_string(VkImageLayout layout); + + void VkResult_check(const std::string &op_message, VkResult result); + VkPolygonMode convert_fill(Fill fill); - /** - * @brief Convert Cull to VkCullModeFlags - * - * @param cull - * @return VkCullModeFlags - */ VkCullModeFlags convert_cull(Cull cull); - /** - * @brief Convert Topology to VkPrimitiveTopology - * - * @param topology - * @return VkPrimitiveTopology - */ VkPrimitiveTopology convert_topology(Topology topology); - /** - * @brief Create a Vulkan instance. - * - * @param display - * @return VkInstance - */ + VkFormat convert_texture_format(TextureFormat format); + + VkFilter convert_texture_filter(TextureFilter filter); + + VkSamplerAddressMode convert_texture_address_mode(TextureAddressMode address_mode); + VkInstance VkInstance_create(const Display &display); - /** - * @brief Create a Vulkan debug messenger. - * - * @param instance - * @return VkDebugUtilsMessengerEXT - */ VkDebugUtilsMessengerEXT VkDebugUtilsMessengerEXT_create(VkInstance instance); - /** - * @brief Create the Vulkan logical device. - * - * @param physical - * @return VkDevice - */ VkDevice VkDevice_create(const PhysicalDevice &physical); - /** - * @brief Allocate Vulkan device memory. - * - * @param device - * @param type_index - * @param size - * @return VkDeviceMemory - */ VkDeviceMemory VkDeviceMemory_allocate(VkDevice device, unsigned type_index, unsigned size); - /** - * @brief Create a Vulkan buffer. - * - * @param device - * @param usage - * @param size - * @param queue_families - * @param queue_family_count - * @return VkBuffer - */ VkBuffer VkBuffer_create(VkDevice device, VkBufferUsageFlags usage, unsigned size, const QueueFamily *queue_families, unsigned queue_family_count); - /** - * @brief Submit a command to copy the contents of a Vulkan buffer to another buffer. - * - * @param src - * @param dst - * @param queue - * @param command_buffer - * @param regions - * @param region_count - */ void VkBuffer_immediate_copy(VkBuffer src, VkBuffer dst, VkQueue queue, @@ -149,17 +58,34 @@ namespace Dynamo::Graphics::Vulkan { VkBufferCopy *regions, unsigned region_count); - /** - * @brief Create a Vulkan image view. - * - * @param device - * @param image - * @param format - * @param type - * @param subresources - * @param swizzle - * @return VkImageView - */ + void VkBuffer_immediate_image_copy(VkBuffer src, + VkImage dst, + VkQueue queue, + VkCommandBuffer command_buffer, + VkBufferImageCopy *regions, + unsigned region_count); + + VkImage VkImage_create(VkDevice device, + const VkExtent3D &extent, + VkFormat format, + VkImageLayout layout, + VkImageType type, + VkImageTiling tiling, + VkImageUsageFlags usage, + VkSampleCountFlagBits samples, + unsigned mip_levels, + unsigned array_layers, + const QueueFamily *queue_families, + unsigned queue_family_count); + + void VkImage_transition_layout(VkImage image, + VkQueue queue, + VkCommandBuffer command_buffer, + VkFormat format, + VkImageLayout prev, + VkImageLayout next, + const VkImageSubresourceRange &subresources); + VkImageView VkImageView_create(VkDevice device, VkImage image, VkFormat format, @@ -167,53 +93,26 @@ namespace Dynamo::Graphics::Vulkan { const VkImageSubresourceRange &subresources, const VkComponentMapping &swizzle = {}); - /** - * @brief Create a Vulkan descriptor set layout. - * - * @param device - * @param bindings - * @param binding_count - * @return VkDescriptorSetLayout - */ + VkSampler VkSampler_create(VkDevice device, + VkSamplerAddressMode address_mode_u, + VkSamplerAddressMode address_mode_v, + VkSamplerAddressMode address_mode_w, + VkFilter mag_filter, + VkFilter min_filter, + VkBorderColor border_color, + float max_anisotropy); + VkDescriptorSetLayout VkDescriptorSetLayout_create(VkDevice device, const VkDescriptorSetLayoutBinding *bindings, unsigned binding_count); - /** - * @brief Create a Vulkan pipeline layout. - * - * @param device - * @param descriptor_layouts - * @param descriptor_layout_count - * @param push_constant_ranges - * @param push_constant_range_count - * @return VkPipelineLayout - */ VkPipelineLayout VkPipelineLayout_create(VkDevice device, VkDescriptorSetLayout *descriptor_layouts, unsigned descriptor_layout_count, VkPushConstantRange *push_constant_ranges, unsigned push_constant_range_count); - /** - * @brief Create a Vulkan shader module. - * - * @param device - * @param bytecode - * @return VkShaderModule - */ VkShaderModule VkShaderModule_create(VkDevice device, const std::vector &bytecode); - /** - * @brief Create a Vulkan framebuffer. - * - * @param device - * @param renderpass - * @param extent - * @param views - * @param view_count - * @param layer_count - * @return VkFramebuffer - */ VkFramebuffer VkFramebuffer_create(VkDevice device, VkRenderPass renderpass, const VkExtent2D &extent, @@ -221,70 +120,24 @@ namespace Dynamo::Graphics::Vulkan { unsigned view_count, unsigned layer_count); - /** - * @brief Create a Vulkan command pool. - * - * @param device - * @param family - * @return VkCommandPool - */ VkCommandPool VkCommandPool_create(VkDevice device, QueueFamily family); - /** - * @brief Allocate Vulkan command buffers from a pool. - * - * @param device - * @param pool - * @param level - * @param dst - * @param count - */ void VkCommandBuffer_allocate(VkDevice device, VkCommandPool pool, VkCommandBufferLevel level, VkCommandBuffer *dst, unsigned count); - /** - * @brief Create a Vulkan descriptor pool. - * - * @param device - * @param sizes - * @param size_count - * @param max_sets - * @return VkDescriptorPool - */ VkDescriptorPool VkDescriptorPool_create(VkDevice device, VkDescriptorPoolSize *sizes, unsigned size_count, unsigned max_sets); - /** - * @brief Allocate Vulkan descriptor sets from a pool. - * - * @param device - * @param pool - * @param layouts - * @param dst - * @param count - */ void VkDescriptorSet_allocate(VkDevice device, VkDescriptorPool pool, const VkDescriptorSetLayout *layouts, VkDescriptorSet *dst, unsigned count); - /** - * @brief Create a Vulkan fence. - * - * @param device - * @return VkFence - */ VkFence VkFence_create(VkDevice device); - /** - * @brief Create a Vulkan semaphore. - * - * @param device - * @return VkSemaphore - */ VkSemaphore VkSemaphore_create(VkDevice device); } // namespace Dynamo::Graphics::Vulkan diff --git a/src/Utils/Allocator.cpp b/src/Utils/Allocator.cpp index 82b87b2..82a2e3c 100644 --- a/src/Utils/Allocator.cpp +++ b/src/Utils/Allocator.cpp @@ -45,6 +45,7 @@ namespace Dynamo { } std::optional Allocator::reserve(unsigned size, unsigned alignment) { + DYN_ASSERT(size > 0); for (auto it = _free.begin(); it != _free.end(); it++) { Block &block = *it; @@ -74,7 +75,7 @@ namespace Dynamo { DYN_ASSERT(_used.count(offset) == 0); // Return the offset to the allocation and track it - _used[offset] = size; + _used.emplace(offset, size); return offset; } }