Idiomatic C++23 cross-platform composable abstraction framework for Vulkan and low-level graphics.
Fubuki is designed to be both idiomatic (modern) C++ and idiomatic low-level graphics code.
- Tutorials: see /tutorials.
- Doxygen: run
doxygen
on theDoxyfile
available at the repository root. - Style guide, design notes, contribution guidelines: see /doc.
- Minimally-invasive abstractions thanks to the pass-by-view paradigm.
- Fubuki's API is designed to limit the changes required in existing code to benefit from its features.
- Reports error with
std::expected
unless otherwise specified.- Constructors that can
throw
have anoexcept
equivalent factory function.
- Constructors that can
- Compile-time validated
pNext
chain. - Compile-time validated loading system for Vulkan function pointers (
PFN
).fubuki::invoke<"vkFunctionName">
/fubuki::fuyu::invoke<"...">
invoke the corresponding Vulkan function with zero runtime overhead.
- Platform-agnostic API to create native window surfaces.
- Platform-agnostic API to interact with the keyboard, the mouse and display monitor (under the restrictions of the platform).
- Accessible internals for custom platform-specific code.
fubuki::small_vector
(vector
with Small Buffer Optimisation - SBO)- Much, much more. For a slightly more in-depth overview of the features Fubuki provides, see the corresponding
README.md
in each library:
Compiler | Platform | Manually tested | CI-tested |
---|---|---|---|
clang-19 |
Linux | âś… | âś… |
gcc-14 |
Linux | âś… | âś… |
MSVC-2022 |
Windows | âś… | âś… |
MinGW/gcc-13 |
Windows | ✅ | ❌ |
MinGW/LLVM-17 |
Windows | ✅ | ❌ |
- Vulkan SDK.
- Fubuki code is provided for
1.4.309.0
. - Version-dependent code can be regenerated for any other SDK version which uses a compatible format for the Vulkan registry. See the maintenance documentation.
- Fubuki code is provided for
- CMake 3.23 or later.
- A compiler supporting C++23.
- Python 3.9 or above (see https://www.python.org/ for installation details).
doxygen
(if you want to build the Doxygen documentation)
Important
Only tested on Ubuntu 24.04. Please do not hesitate to open an issue if your platform requires, for example, different packages.
sudo apt-get install pkg-config libwayland-dev wayland-protocols wayland-scanner++ libwayland-client++1 libx11-dev libxrandr-dev libxkbcommon-dev
Note
If you are using WSL, consider taking a look at this thread, which suggests solutions when the connection to a display fails. In particular, see this answer.
Developers will need the following:
clang-format
. The executable must be exported so that it can be found from$ENV{PATH}
(in the CMake meaning of the expression).- Python
termcolor
.
Simply run
git clone --recurse-submodules --remote-submodules https://github.com/Erellu/fubuki.git
Or if you have an old version of Git (2.12 or before)
git clone --recursive https://github.com/Erellu/fubuki.git
git submodule init
git submodule update
- Clone the repository and pull its submodules.
- Download the Vulkan SDK and the other dependencies.
- Run
cmake
(orcmake-gui
if you prefer the graphical interface) and select the build system (ninja
, etc.).- Set
CMAKE_BUILD_TYPE
to one ofDebug
,Release
,RelWithDebInfo
,MinSizeRel
. - Set the other Fubuki options (see below).
- Set
- Go to your build directory and build (
ninja
or whatever you selected).
Name | Description | Default |
---|---|---|
FUBUKI_BUILD_TESTS |
Build Fubuki unit tests | OFF |
FUBUKI_BUILD_TUTORIALS |
Build Fubuki tutorials. | ON |
FUBUKI_INSTALL |
Install Fubuki to directory set in CMAKE_INSTALL_PREFIX . |
ON |
FUBUKI_NO_IO |
Do not build fubuki::io (use this when building Fubuki for a platform fubuki::io doesn't support, such as OSX). This also disables tutorials and tests that depend on this target. |
OFF |
FUBUKI_SKIP_GENERATION |
(Dev) Skip code generations processes. Put it on OFF when changing the Vulkan SDK version. |
ON |
FUBUKI_VERBOSE_BUILD |
(Dev) Display detailed messages when configuring Fubuki. | OFF |
At the current state of the project, I cannot provide prebuilt binaries, so you will have to build it yourself:
- Follow the building steps above.
- Set
FUBUKI_INSTALL
toON
. - Set
CMAKE_INSTALL_PREFIX
to the desired install location. - Build.
Example C++ code
// The name is automagically validated at compile time
auto* const pfn = fubuki::pfn<"vkCreateDevice">(instance);
fubuki_assert(pfn, "Could not retrieve the function pointer");
// Compile error
// auto* const pfn2 = fubuki::pfn<"vkThisFunctionDoesNotExist">(instance);
Example C++ code
auto device_functions = fubuki::load(instance, vk_version, device);
// The name and the arguments are also validated at compile time
auto result = fubuki::invoke<"vkQueueSubmit">(device_functions, ...);
// Compile-time error: vkQueueSubmit cannot be invoke with such arguments
// auto result = fubuki::invoke<"vkQueueSubmit">(device_functions, an_int);
Example C++ code
VkDeviceCreateInfo vk_info {...};
VkPhysicalDeviceVulkan11Features vk11 {...};
VkPhysicalDeviceVulkan12Features vk12 {...};
VkPhysicalDeviceVulkan13Features vk13 {...};
const fubuki::pnext_chain chain{
vk11,
vk12,
vk13,
};
// Contents are validated at compile time
fubuki::extend(vk_info, chain);
See:
- Tutorial 00: Instance and device: basic usage and general semantics
- Tutorial 02: Vulkan context: how to build a declarative API to setup a complete execution context.
- Tutorial 04: Triangle: The usual triangle, using Vulkan 1.3's dynamic rendering,
fubuki::fuyu
andfuyu::command::pipe
.
Similar to fubuki::invoke
, fubuki::fuyu::invoke
provides an interface to call Vulkan functions by name. It is a simple compatibility layer for fuyu::views
:
Example C++ code
[[nodiscard]]
VkExtent2D render_area_granularity(const fuyu::render_pass_view pass) noexcept
{
VkExtent2D result = {};
// Similar to pfn<"name">, the function name is validated at compile time
fuyu::invoke<"vkGetRenderAreaGranularity">(pass.device, pass.handle.vk, std::addressof(result));
return result;
}
Note
This function already exists in fubuki
, under fubuki::fuyu::render_area_granularity
.
fubuki::fuyu
introduces command pipes
that can be used to compose an execution flow, similar to std::ranges
:
Example C++ code
// Typical succession of commands to draw a shape
auto flow
= cmd::pipe::record(command_buffer, VK_COMMAND_BUFFER_USAGE_ONE_TIME_SUBMIT_BIT)
| cmd::pipe::barrier(
cmd::image_memory_barrier
{
.source = {.access_mask = {}, .layout = VK_IMAGE_LAYOUT_UNDEFINED},
.destination = {.access_mask = VK_ACCESS_COLOR_ATTACHMENT_READ_BIT | VK_ACCESS_COLOR_ATTACHMENT_WRITE_BIT, .layout = VK_IMAGE_LAYOUT_GENERAL},
.target =
{
.handle = render_target.image,
.subresource_range =
{
.aspectMask = VK_IMAGE_ASPECT_COLOR_BIT,
.baseMipLevel = 0,
.levelCount = 1,
.baseArrayLayer = 0,
.layerCount = 1
}
}
},
cmd::barrier_stage
{
.source = VK_PIPELINE_STAGE_TOP_OF_PIPE_BIT,
.destination = VK_PIPELINE_STAGE_COLOR_ATTACHMENT_OUTPUT_BIT,
})
| cmd::pipe::render_scope(
cmd::dynamic_render_target
{
.flags = {},
.render_area = {.offset = {}, .extent = app::swapchain().info().image.extent},
.layer_count = 1,
.view_mask = {},
.attachments =
{
.colour =
{
{
.sType = VK_STRUCTURE_TYPE_RENDERING_ATTACHMENT_INFO,
.pNext = {},
.imageView = render_target.view.handle().vk,
.imageLayout = VK_IMAGE_LAYOUT_GENERAL,
.resolveMode = VK_RESOLVE_MODE_AVERAGE_BIT,
.resolveImageView = app::swapchain_resources()[next_image].view.handle.vk,
.resolveImageLayout = VK_IMAGE_LAYOUT_GENERAL,
.loadOp = VK_ATTACHMENT_LOAD_OP_CLEAR,
.storeOp = VK_ATTACHMENT_STORE_OP_STORE,
.clearValue = {VkClearColorValue{{0.0f, 0.0f, 0.2f, 1.0f}}}
}
}
}
},
[&](cmd::pipe::flow f) noexcept -> cmd::pipe::flow
{
const VkViewport vp
{
.x = 0.f,
.y = 0.f,
.width = static_cast<float>(app::swapchain().info().image.extent.width),
.height = static_cast<float>(app::swapchain().info().image.extent.height),
.minDepth = 0.0f,
.maxDepth = 1.0f
};
const VkRect2D scissor = {.offset = {}, .extent = app::swapchain().info().image.extent};
return std::move(f)
| cmd::pipe::set::viewport(0, {vp})
| cmd::pipe::set::scissor(0, {scissor})
| cmd::pipe::bind::pipeline(pipeline.handle())
| cmd::pipe::bind::vertex_buffer(0, vertex.buffer, 0)
| cmd::pipe::draw::call(6, 1, 0, 0);
})
| cmd::pipe::barrier(
cmd::image_memory_barrier
{
.source = {.access_mask = VK_ACCESS_COLOR_ATTACHMENT_WRITE_BIT, .layout = VK_IMAGE_LAYOUT_UNDEFINED},
.destination = {.access_mask = {}, .layout = VK_IMAGE_LAYOUT_PRESENT_SRC_KHR},
.target =
{
.handle = app::swapchain_resources()[frame_index].image,
.subresource_range =
{
.aspectMask = VK_IMAGE_ASPECT_COLOR_BIT,
.baseMipLevel = 0,
.levelCount = 1,
.baseArrayLayer = 0,
.layerCount = 1
}
}
},
cmd::barrier_stage
{
.source = VK_PIPELINE_STAGE_COLOR_ATTACHMENT_OUTPUT_BIT,
.destination = VK_PIPELINE_STAGE_BOTTOM_OF_PIPE_BIT,
});
Example C++ code
// Enable all available core features for Vulkan core 1.1 to 1.3
using device = fubuki::fuyu::device;
auto physical_device_info = fuyu::hardware::physical_device_information(context::instance(), physical_device);
fubuki_assert(physical_device_info.has_value(), "Failed to retrieve physical devices properties. API error:" << physical_device_info.error());
const fubuki::pnext_chain chain{
physical_device_info->features.vk11,
physical_device_info->features.vk12,
physical_device_info->features.vk13,
};
const auto dev = device::make(instance, physical_device, {/*info*/}, {/*extensions, queues, surfaces*/}, chain);
// Or with the throwing constructor:
auto dev2 = device{instance, physical_device, {/*info*/}, {/*extensions, queues, surfaces*/}), chain};
Example C++ code
// Create an array of descriptor sets with a pnext chain created on-the-fly
using ds_array = fubuki::fuyu::descriptor_set_array;
const auto sets =
ds_array::make(pool,
{/*info*/},
fubuki::owning_pnext_chain
{
VkDescriptorSetVariableDescriptorCountAllocateInfo
{
.sType = {},
.pNext = {},
.descriptorSetCount = fubuki::vk_size(variable_sets),
.pDescriptorCounts = variable_sets.data(),
},
}.chain());
// Or with the throwing constructor:
const auto sets2 =
ds_array
{
pool,
{/*info*/},
fubuki::owning_pnext_chain
{
VkDescriptorSetVariableDescriptorCountAllocateInfo
{
.sType = {},
.pNext = {},
.descriptorSetCount = fubuki::vk_size(variable_sets),
.pDescriptorCounts = variable_sets.data(),
},
}.chain()
};
See:
Example C++ code
const auto compiled_shader = hyoka::shader::compile // std::expected
({
.shader = {hyoka::shader::lang::..., source_code},
.stage = hyoka::shader::stage::...
});
if(not compiled_shader)
{
handle(compiled_shader.error());
}
Fubuki is opened to all contributions, but reserves the right to decline pull requests and to not handle all issues.
For more information, see:
- The style guide: See the associated Markdown file.
- The contribution guidelines: See the associated Markdown file.
- The design notes: See the associated Markdown file.
Fubuki
is available under the BSD-2-Clause license below:
/*
* BSD 2-Clause License
*
* Copyright (c) 2025, Erwan DUHAMEL
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are met:
*
* 1. Redistributions of source code must retain the above copyright notice, this
* list of conditions and the following disclaimer.
*
* 2. Redistributions in binary form must reproduce the above copyright notice,
* this list of conditions and the following disclaimer in the documentation
* and/or other materials provided with the distribution.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
* AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
* DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
* FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
* SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
* CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
* OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
* OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
fubuki::move_only_function
is based onzhihaoy/nontype_functional
, available under the BSD-2 license.fubuki::future::inplace_vector
is based on the reference implementation, available under the MIT License.- Some code comes from StackOverflow answers licensed under the CC BY-SA (3.0 or 4.0) license. The provenance for each snippet is cited in-code.
Fubuki also integrates code from the following repositories:
-
glm
, available under the license here, employed in Fubuki according to the conditions of the MIT License. -
Doxygen Theme - Flat Design
is available under the MIT License.