forked from KhronosGroup/Vulkan-Samples
-
Notifications
You must be signed in to change notification settings - Fork 0
/
Copy pathconstant_data.h
277 lines (228 loc) · 10.6 KB
/
constant_data.h
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
/* Copyright (c) 2019-2024, Arm Limited and Contributors
*
* SPDX-License-Identifier: MIT
*
* Permission is hereby granted, free of charge,
* to any person obtaining a copy of this software and associated documentation files (the "Software"),
* to deal in the Software without restriction, including without limitation the rights to
* use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software,
* and to permit persons to whom the Software is furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED,
* INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
* WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
*/
#pragma once
#include "rendering/render_pipeline.h"
#include "rendering/subpasses/forward_subpass.h"
#include "scene_graph/components/camera.h"
#include "scene_graph/components/perspective_camera.h"
#include "vulkan_sample.h"
/**
* @brief This structure will be pushed in its entirety if 256 bytes of push constants
* are supported by the physical device, otherwise it will be trimmed to 128 bytes
* (i.e. only "model" and "camera_view_proj" will be pushed)
*
* The shaders will be compiled with a define to handle this difference.
*/
struct alignas(16) MVPUniform
{
glm::mat4 model;
glm::mat4 camera_view_proj;
glm::mat4 scale;
// This value is ignored by the shader and is just to increase bandwidth
glm::mat4 padding;
};
/**
* @brief Constant Data sample
*
* This sample is designed to show the different ways in which Vulkan can push constant data to the
* shaders.
*
* The current ways that are supported are:
* - Push Constants
* - Descriptor Sets
* - Dynamic Descriptor Sets
* - Update-after-bind Descriptor Sets
* - Pre-allocated buffer array
*
* The sample also shows the performance implications that these different methods would have on your
* application or game. These performance deltas may differ between platforms and vendors.
*
* The data structure used will be pushed in its entirety if 256 bytes of push constants
* are supported by the physical device, otherwise it will be trimmed to 128 bytes
* (i.e. only "model" and "camera_view_proj" will be pushed)
*
* The shaders will be compiled with a definition to handle this difference.
*/
class ConstantData : public vkb::VulkanSampleC
{
public:
/**
* @brief The sample supported methods of using constant data in shaders
*/
enum Method
{
PushConstants,
DescriptorSets,
DynamicDescriptorSets,
UpdateAfterBindDescriptorSets, // May be disabled if the device doesn't support
BufferArray,
Undefined
};
/**
* @brief Describes the properties of a method
*/
struct MethodProperties
{
const char *description;
bool supported{true};
};
ConstantData();
virtual ~ConstantData() = default;
virtual bool prepare(const vkb::ApplicationOptions &options) override;
/**
* @brief The base subpass to help prepare the shader variants and store the push constant limit
*/
class ConstantDataSubpass : public vkb::ForwardSubpass
{
public:
ConstantDataSubpass(vkb::RenderContext &render_context, vkb::ShaderSource &&vertex_shader, vkb::ShaderSource &&fragment_shader, vkb::sg::Scene &scene, vkb::sg::Camera &camera) :
vkb::ForwardSubpass(render_context, std::move(vertex_shader), std::move(fragment_shader), scene, camera)
{}
virtual void prepare() override;
uint32_t struct_size{128};
};
/**
* @brief A custom forward subpass to isolate just the use of push constants
*
* This subpass is intentionally set up with custom shaders that possess just a single push constant structure
*/
class PushConstantSubpass : public ConstantDataSubpass
{
public:
PushConstantSubpass(vkb::RenderContext &render_context, vkb::ShaderSource &&vertex_shader, vkb::ShaderSource &&fragment_shader, vkb::sg::Scene &scene, vkb::sg::Camera &camera) :
ConstantDataSubpass(render_context, std::move(vertex_shader), std::move(fragment_shader), scene, camera)
{}
/**
* @brief Updates the MVP uniform member variable to then be pushed into the shader
*/
virtual void update_uniform(vkb::CommandBuffer &command_buffer, vkb::sg::Node &node, size_t thread_index) override;
/**
* @brief Overridden to intentionally disable any dynamic shader module updates
*/
virtual vkb::PipelineLayout &prepare_pipeline_layout(vkb::CommandBuffer &command_buffer, const std::vector<vkb::ShaderModule *> &shader_modules) override;
/**
* @brief Overridden to push a custom data structure to the shader
*/
virtual void prepare_push_constants(vkb::CommandBuffer &command_buffer, vkb::sg::SubMesh &sub_mesh) override;
// The MVP uniform data structure
MVPUniform mvp_uniform;
};
/**
* @brief A custom forward subpass to isolate just the use of uniform buffer objects
*
* This subpass is intentionally set up with custom shaders that possess just a single UBO binding
* The subpass will use the right UBO method (Static, Dynamic or Update-after-bind) based on its setting as set by the sample
*/
class DescriptorSetSubpass : public ConstantDataSubpass
{
public:
DescriptorSetSubpass(vkb::RenderContext &render_context, vkb::ShaderSource &&vertex_shader, vkb::ShaderSource &&fragment_shader, vkb::sg::Scene &scene, vkb::sg::Camera &camera) :
ConstantDataSubpass(render_context, std::move(vertex_shader), std::move(fragment_shader), scene, camera)
{}
/**
* @brief Creates a buffer filled with the mvp data and binds it
*/
virtual void update_uniform(vkb::CommandBuffer &command_buffer, vkb::sg::Node &node, size_t thread_index) override;
/**
* @brief Dynamically retrieves the correct pipeline layout depending on the method of UBO
*/
virtual vkb::PipelineLayout &prepare_pipeline_layout(vkb::CommandBuffer &command_buffer, const std::vector<vkb::ShaderModule *> &shader_modules) override;
/**
* @brief Overridden to intentionally disable any push constants
*/
virtual void prepare_push_constants(vkb::CommandBuffer &command_buffer, vkb::sg::SubMesh &sub_mesh) override;
// The method by which the UBO subpass will operate
Method method;
};
/**
* @brief A custom forward subpass to isolate the use of a shader storage buffer object
*
* This subpass is intentionally set up with custom shaders that own just a buffer binding holding an array of mvp data
* The subpass will use instancing to index into the UBO array
*/
class BufferArraySubpass : public ConstantDataSubpass
{
public:
BufferArraySubpass(vkb::RenderContext &render_context, vkb::ShaderSource &&vertex_shader, vkb::ShaderSource &&fragment_shader, vkb::sg::Scene &scene, vkb::sg::Camera &camera) :
ConstantDataSubpass(render_context, std::move(vertex_shader), std::move(fragment_shader), scene, camera)
{}
virtual void draw(vkb::CommandBuffer &command_buffer) override;
/**
* @brief No-op, uniform data is sent upfront before the draw call
*/
virtual void update_uniform(vkb::CommandBuffer &command_buffer, vkb::sg::Node &node, size_t thread_index) override;
/**
* @brief Returns a default pipeline layout
*/
virtual vkb::PipelineLayout &prepare_pipeline_layout(vkb::CommandBuffer &command_buffer, const std::vector<vkb::ShaderModule *> &shader_modules) override;
/**
* @brief Overridden to intentionally disable any push constants
*/
virtual void prepare_push_constants(vkb::CommandBuffer &command_buffer, vkb::sg::SubMesh &sub_mesh) override;
/**
* @brief Overridden to send an index
*/
virtual void draw_submesh_command(vkb::CommandBuffer &command_buffer, vkb::sg::SubMesh &sub_mesh) override;
uint32_t instance_index{0};
};
template <typename T>
std::unique_ptr<vkb::RenderPipeline> create_render_pipeline(const std::string &vertex_shader, const std::string &fragment_shader)
{
static_assert(std::is_base_of<ConstantDataSubpass, T>::value, "T is an invalid type. Must be a derived class from ConstantDataSubpass");
vkb::ShaderSource vert_shader(vertex_shader);
vkb::ShaderSource frag_shader(fragment_shader);
auto subpass = std::make_unique<T>(get_render_context(), std::move(vert_shader), std::move(frag_shader), get_scene(), *camera);
// We want to check if the push constants limit can support the full 256 bytes
auto push_constant_limit = get_device().get_gpu().get_properties().limits.maxPushConstantsSize;
if (push_constant_limit >= 256)
{
subpass->struct_size = 256;
}
std::vector<std::unique_ptr<vkb::rendering::SubpassC>> subpasses{};
subpasses.push_back(std::move(subpass));
return std::make_unique<vkb::RenderPipeline>(std::move(subpasses));
}
private:
virtual void draw_gui() override;
virtual void draw_renderpass(vkb::CommandBuffer &command_buffer, vkb::RenderTarget &render_target) override;
virtual void request_gpu_features(vkb::PhysicalDevice &gpu) override;
/**
* @brief Helper function to determine the constant data method that is selected and supported by the sample
* @returns The method that is selected, otherwise push constants
*/
inline Method get_active_method();
vkb::sg::PerspectiveCamera *camera{};
// The render pipeline designed for using push constants
std::unique_ptr<vkb::RenderPipeline> push_constant_render_pipeline{nullptr};
// The render pipeline designed for using Descriptor Sets, Dynamic Descriptor Sets and Update-after-bind Descriptor Sets
std::unique_ptr<vkb::RenderPipeline> descriptor_set_render_pipeline{nullptr};
// The render pipeline designed for using a large shader storage buffer object that is instanced into to get the relevant MVP data
std::unique_ptr<vkb::RenderPipeline> buffer_array_render_pipeline{nullptr};
// The samples constant data methods and their properties
std::unordered_map<Method, MethodProperties> methods = {
{Method::PushConstants, {"Push Constants"}},
{Method::DescriptorSets, {"Descriptor Sets"}},
{Method::DynamicDescriptorSets, {"Dynamic Descriptor Sets"}},
{Method::UpdateAfterBindDescriptorSets, {"Update-after-bind Descriptor Sets", false}},
{Method::BufferArray, {"Single Pre-allocated Buffer Array"}}};
int gui_method_value{static_cast<int>(Method::PushConstants)};
int last_gui_method_value{static_cast<int>(Method::PushConstants)};
};
std::unique_ptr<vkb::VulkanSampleC> create_constant_data();