232 lines
7.4 KiB
Plaintext
232 lines
7.4 KiB
Plaintext
////
|
|
- Copyright (c) 2023-2025, Sascha Willems
|
|
-
|
|
- SPDX-License-Identifier: Apache-2.0
|
|
-
|
|
- Licensed under the Apache License, Version 2.0 the "License";
|
|
- you may not use this file except in compliance with the License.
|
|
- You may obtain a copy of the License at
|
|
-
|
|
- http://www.apache.org/licenses/LICENSE-2.0
|
|
-
|
|
- Unless required by applicable law or agreed to in writing, software
|
|
- distributed under the License is distributed on an "AS IS" BASIS,
|
|
- WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
- See the License for the specific language governing permissions and
|
|
- limitations under the License.
|
|
-
|
|
////
|
|
= Framework
|
|
// omit in toc
|
|
:pp: {plus}{plus}
|
|
|
|
This folder contains the base framework used by the samples.
|
|
It offers sample base classes, encapsulates common functionality for e.g.
|
|
loading assets (images, models, shaders), wraps common Vulkan objects and implements frequently used concepts like a cache and a scene graph.
|
|
The framework also implements platform support for Windows, Linux, MacOS and Android.
|
|
|
|
It can be used as a guideline for writing advanced Vulkan applications.
|
|
|
|
Before trying to implement common functions, consider checking if the framework doesn't already provides what you are looking for.
|
|
|
|
== Sample base classes
|
|
|
|
The framework provides two different sample base classes.
|
|
When xref:../scripts/README.adoc[creating new samples], you can choose between one of them:
|
|
|
|
=== High level base sample class
|
|
|
|
This base class abstracts away most of the Vulkan API calls and as such makes heavy use of the Vulkan object wrapper classes of the framework.
|
|
Writing samples with the base class is less verbose.
|
|
|
|
See link:./vulkan_sample.h[vulkan_sample.h] and link:./vulkan_sample.cpp[vulkan_sample.cpp].
|
|
|
|
=== API sample base class
|
|
|
|
This base class uses less abstraction, letting you work more explicitly with the api.
|
|
|
|
See link:./api_vulkan_sample.h[api_vulkan_sample.h] and link:./api_vulkan_sample.cpp[api_vulkan_sample.cpp].
|
|
|
|
=== Support for Vulkan-Hpp
|
|
|
|
While the framework itself primarily uses the C-Interface for Vulkan, both the high level and the API sample base class also come with https://github.com/KhronosGroup/Vulkan-Hpp[Vulkan-Hpp] variants, letting you write samples using the C{pp} Vulkan language bindings instead.
|
|
|
|
See link:./hpp_vulkan_sample.h[hpp_vulkan_sample.h] / link:./hpp_vulkan_sample.cpp[hpp_vulkan_sample.cpp] and link:./hpp_api_vulkan_sample.h[hpp_api_vulkan_sample.h] / link:./hpp_api_vulkan_sample.cpp[hpp_api_vulkan_sample.cpp].
|
|
|
|
== Commonly used framework concepts
|
|
|
|
=== Enabling extensions
|
|
|
|
Vulkan is an extensible api.
|
|
New features are usually exposed through either instance or device extensions.
|
|
Extensions can be enabled in the constructor of both the high level and api base sample class:
|
|
|
|
[,cpp]
|
|
----
|
|
MySample::MySample()
|
|
{
|
|
add_instance_extension(VK_SOME_INSTANCE_EXTENSION_NAME);
|
|
add_device_extension(VK_SOME_DEVICE_EXTENSION_NAME);
|
|
}
|
|
----
|
|
|
|
NOTE: The framework uses the https://github.com/zeux/volk[Volk] meta-loader, which will automatically load extension function pointers for all enabled extensions.
|
|
There is no need to manually get extension pointer functions.
|
|
|
|
=== Changing the Vulkan api version
|
|
|
|
By default all samples create a Vulkan 1.1 instance.
|
|
Higher versions can be requested in the constructor of a sample:
|
|
|
|
[,cpp]
|
|
----
|
|
MySample::MySample()
|
|
{
|
|
set_api_version(VK_API_VERSION_1_2);
|
|
}
|
|
----
|
|
|
|
=== Requesting GPU features
|
|
|
|
Most extensions also require enabling corresponding feature flags.
|
|
This can be done by overriding the `request_gpu_features` function of the base class:
|
|
|
|
[,cpp]
|
|
----
|
|
void MySample::request_gpu_features(vkb::PhysicalDevice &gpu)
|
|
{
|
|
// Get a reference to the feature structure required for an extension
|
|
auto &requested_extension_feature = gpu
|
|
request_extension_features<VkPhysicalDeviceSomeExtensionFeaturesKHR>(VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_SOME_EXTENSION_FEATURES_KHR);
|
|
|
|
// Enable a selected feature
|
|
requested_extension_feature.featureName = VK_TRUE;
|
|
}
|
|
----
|
|
|
|
=== Extending the graphical user interface
|
|
|
|
The framework includes a graphical user interface based on https://github.com/ocornut/imgui[Dear ImGui].
|
|
This can be used by samples to display values and add controls like buttons, dropdowns, etc.
|
|
|
|
To add additional elements to the UI of a sample, you override the respective function from the base class:
|
|
|
|
Samples based on the high level base class need to override the `draw_gui` function:
|
|
|
|
[,cpp]
|
|
----
|
|
void MySample::draw_gui()
|
|
{
|
|
if (ImGui::Checkbox("Enable Option", &option_enabled))
|
|
{
|
|
...
|
|
}
|
|
}
|
|
----
|
|
|
|
Samples based on the api base class need to override the `on_update_ui_overlay` function:
|
|
|
|
[,cpp]
|
|
----
|
|
void MyApiSample::on_update_ui_overlay(vkb::Drawer &drawer)
|
|
{
|
|
if (drawer.checkbox("Enable option", &option_enabled))
|
|
{
|
|
...
|
|
}
|
|
}
|
|
----
|
|
|
|
=== Loading models
|
|
|
|
The framework supports https://www.khronos.org/gltf/[glTF models] and includes a loader for this format.
|
|
|
|
The high level base class works with a single glTF scene loaded at startup.
|
|
The scene is part of the base class, and there is no need to explicitly draw it:
|
|
|
|
[,cpp]
|
|
----
|
|
bool MySample::prepare(const vkb::ApplicationOptions &options)
|
|
{
|
|
scene = load_scene("filename.gltf");
|
|
}
|
|
----
|
|
|
|
With the api base class are explicitly declared, loaded and rendered:
|
|
|
|
[,cpp]
|
|
----
|
|
// my_sample.h
|
|
class MyApiSample : public ApiVulkanSample
|
|
{
|
|
std::unique_ptr<vkb::sg::SubMesh> modelA;
|
|
std::unique_ptr<vkb::sg::SubMesh> modelB;
|
|
...
|
|
}
|
|
|
|
// my_sample.cpp
|
|
bool MyApiSample::prepare(const vkb::ApplicationOptions &options)
|
|
{
|
|
modelA = load_model("filenameA.gltf");
|
|
modelB = load_model("filenameB.gltf");
|
|
}
|
|
|
|
void MyApiSample::build_command_buffers()
|
|
{
|
|
vkBeginCommandBuffer(...);
|
|
...
|
|
draw_model(modelA, draw_cmd_buffers[i]);
|
|
...
|
|
draw_model(modelB, draw_cmd_buffers[i]);
|
|
...
|
|
vkEndCommandBufer(...);
|
|
}
|
|
----
|
|
|
|
=== Loading images
|
|
|
|
The framework supports the https://www.khronos.org/ktx/[KTX] GPU container format and includes a loader for this format.
|
|
As a container format, KTX supports different image formats ranging from basic RGBA images to compressed formats.
|
|
|
|
[,cpp]
|
|
----
|
|
texture = load_texture("rgba_texture.ktx", vkb::sg::Image::Color);
|
|
----
|
|
|
|
Images (textures) loaded like this can then be used as descriptors later on:
|
|
|
|
[,cpp]
|
|
----
|
|
VkDescriptorImageInfo image_descriptor = create_descriptor(texture);
|
|
----
|
|
|
|
=== Loading shaders
|
|
|
|
The framework supports loading textual GLSL shaders.
|
|
These shaders are then compiled to https://registry.khronos.org/SPIR-V/specs/unified1/SPIRV.html[SPIR-V] at runtime so Vulkan can ingest them.
|
|
|
|
With the high level base class, shaders are attached to the scene graphics' render pipeline:
|
|
|
|
[,cpp]
|
|
----
|
|
vkb::ShaderSource vert_shader("vs.vert");
|
|
vkb::ShaderSource frag_shader("fs.frag");
|
|
auto scene_subpass = std::make_unique<vkb::ForwardSubpass>(get_render_context(), std::move(vert_shader), std::move(frag_shader), *scene, *camera);
|
|
|
|
auto render_pipeline = vkb::RenderPipeline();
|
|
render_pipeline.add_subpass(std::move(scene_subpass));
|
|
|
|
set_render_pipeline(std::move(render_pipeline));
|
|
----
|
|
|
|
While in the api base class, this is again more explicitly by creating shader modules used at pipeline creation time:
|
|
|
|
[,cpp]
|
|
----
|
|
std::array<VkPipelineShaderStageCreateInfo, 2> shader_stages;
|
|
...
|
|
shader_stages[0] = load_shader("vs.vert", VK_SHADER_STAGE_VERTEX_BIT);
|
|
shader_stages[1] = load_shader("fs.frag", VK_SHADER_STAGE_FRAGMENT_BIT);
|
|
VK_CHECK(vkCreateGraphicsPipelines(get_device().get_handle(), pipeline_cache, 1, &pipeline_create_info, nullptr, &pipeline));
|
|
----
|