@@ -59,92 +59,140 @@ class PipelineBuilder {
5959};
6060```
6161
62- Implement ` build() ` :
62+ Before implementing ` build() ` , add some helper functions/constants, starting with the viewport and dynamic states :
6363
6464``` cpp
65- auto PipelineBuilder::build (vk::PipelineLayout const layout,
66- PipelineState const& state) const
67- -> vk::UniquePipeline {
65+ // single viewport and scissor.
66+ constexpr auto viewport_state_v =
67+ vk::PipelineViewportStateCreateInfo ({}, 1, {}, 1);
68+
69+ // these dynamic states are guaranteed to be available.
70+ constexpr auto dynamic_states_v = std::array{
71+ vk::DynamicState::eViewport,
72+ vk::DynamicState::eScissor,
73+ vk::DynamicState::eLineWidth,
74+ };
75+ ```
76+
77+ The shader stages:
78+
79+ ```cpp
80+ [[nodiscard]] constexpr auto
81+ create_shader_stages(vk::ShaderModule const vertex,
82+ vk::ShaderModule const fragment) {
6883 // set vertex (0) and fragment (1) shader stages.
69- auto shader_stages = std::array<vk::PipelineShaderStageCreateInfo, 2>{};
70- shader_stages [ 0]
84+ auto ret = std::array<vk::PipelineShaderStageCreateInfo, 2>{};
85+ ret [0]
7186 .setStage(vk::ShaderStageFlagBits::eVertex)
7287 .setPName("main")
73- .setModule(state.vertex_shader );
74- shader_stages [ 1]
88+ .setModule(vertex );
89+ ret [1]
7590 .setStage(vk::ShaderStageFlagBits::eFragment)
7691 .setPName("main")
77- .setModule(state.fragment_shader);
78-
79- auto pvisci = vk::PipelineVertexInputStateCreateInfo{};
80- pvisci.setVertexAttributeDescriptions(state.vertex_attributes)
81- .setVertexBindingDescriptions(state.vertex_bindings);
92+ .setModule(fragment);
93+ return ret;
94+ }
95+ ```
8296
83- auto prsci = vk::PipelineRasterizationStateCreateInfo{};
84- prsci.setPolygonMode(state.polygon_mode).setCullMode(state.cull_mode);
97+ The depth/stencil state:
8598
86- auto pdssci = vk::PipelineDepthStencilStateCreateInfo{};
99+ ``` cpp
100+ [[nodiscard]] constexpr auto
101+ create_depth_stencil_state (std::uint8_t flags,
102+ vk::CompareOp const depth_compare) {
103+ auto ret = vk::PipelineDepthStencilStateCreateInfo{};
87104 auto const depth_test =
88- (state.flags & PipelineFlag::DepthTest) == PipelineFlag::DepthTest;
89- pdssci.setDepthTestEnable(depth_test ? vk::True : vk::False)
90- .setDepthCompareOp(state.depth_compare);
105+ (flags & PipelineFlag::DepthTest) == PipelineFlag::DepthTest;
106+ ret.setDepthTestEnable(depth_test ? vk::True : vk::False)
107+ .setDepthCompareOp(depth_compare);
108+ return ret;
109+ }
110+ ```
91111
92- auto const piasci =
93- vk::PipelineInputAssemblyStateCreateInfo{{}, state.topology};
112+ And a color blend attachment:
94113
95- auto pcbas = vk::PipelineColorBlendAttachmentState{};
114+ ```cpp
115+ [[nodiscard]] constexpr auto
116+ create_color_blend_attachment(std::uint8_t const flags) {
117+ auto ret = vk::PipelineColorBlendAttachmentState{};
96118 auto const alpha_blend =
97- (state. flags & PipelineFlag::AlphaBlend) == PipelineFlag::AlphaBlend;
119+ (flags & PipelineFlag::AlphaBlend) == PipelineFlag::AlphaBlend;
98120 using CCF = vk::ColorComponentFlagBits;
99- pcbas .setColorWriteMask(CCF::eR | CCF::eG | CCF::eB | CCF::eA)
121+ ret .setColorWriteMask(CCF::eR | CCF::eG | CCF::eB | CCF::eA)
100122 .setBlendEnable(alpha_blend ? vk::True : vk::False)
123+ // standard alpha blending:
124+ // (alpha * src) + (1 - alpha) * dst
101125 .setSrcColorBlendFactor(vk::BlendFactor::eSrcAlpha)
102126 .setDstColorBlendFactor(vk::BlendFactor::eOneMinusSrcAlpha)
103127 .setColorBlendOp(vk::BlendOp::eAdd)
104128 .setSrcAlphaBlendFactor(vk::BlendFactor::eOne)
105129 .setDstAlphaBlendFactor(vk::BlendFactor::eZero)
106130 .setAlphaBlendOp(vk::BlendOp::eAdd);
107- auto pcbsci = vk::PipelineColorBlendStateCreateInfo{};
108- pcbsci.setAttachments(pcbas);
109-
110- // these dynamic states are guaranteed to be available.
111- auto const pdscis = std::array{
112- vk::DynamicState::eViewport,
113- vk::DynamicState::eScissor,
114- vk::DynamicState::eLineWidth,
115- };
116- auto pdsci = vk::PipelineDynamicStateCreateInfo{};
117- pdsci.setDynamicStates(pdscis);
131+ return ret;
132+ }
133+ ```
134+
135+ Now we can implement ` build() ` :
136+
137+ ``` cpp
138+ auto PipelineBuilder::build (vk::PipelineLayout const layout,
139+ PipelineState const& state) const
140+ -> vk::UniquePipeline {
141+ auto const shader_stage_ci =
142+ create_shader_stages(state.vertex_shader, state.fragment_shader);
118143
119- // single viewport and scissor.
120- auto const pvsci = vk::PipelineViewportStateCreateInfo({}, 1, {}, 1);
144+ auto vertex_input_ci = vk::PipelineVertexInputStateCreateInfo{};
145+ vertex_input_ci.setVertexAttributeDescriptions(state.vertex_attributes)
146+ .setVertexBindingDescriptions(state.vertex_bindings);
121147
122- auto pmsci = vk::PipelineMultisampleStateCreateInfo{};
123- pmsci .setRasterizationSamples(m_info.samples)
148+ auto multisample_state_ci = vk::PipelineMultisampleStateCreateInfo{};
149+ multisample_state_ci .setRasterizationSamples(m_info.samples)
124150 .setSampleShadingEnable(vk::False);
125151
126- auto prci = vk::PipelineRenderingCreateInfo{};
127- // could be a depth-only pass.
152+ auto const input_assembly_ci =
153+ vk::PipelineInputAssemblyStateCreateInfo{{}, state.topology};
154+
155+ auto rasterization_state_ci = vk::PipelineRasterizationStateCreateInfo{};
156+ rasterization_state_ci.setPolygonMode(state.polygon_mode)
157+ .setCullMode(state.cull_mode);
158+
159+ auto const depth_stencil_state_ci =
160+ create_depth_stencil_state(state.flags, state.depth_compare);
161+
162+ auto const color_blend_attachment =
163+ create_color_blend_attachment(state.flags);
164+ auto color_blend_state_ci = vk::PipelineColorBlendStateCreateInfo{};
165+ color_blend_state_ci.setAttachments(color_blend_attachment);
166+
167+ auto dynamic_state_ci = vk::PipelineDynamicStateCreateInfo{};
168+ dynamic_state_ci.setDynamicStates(dynamic_states_v);
169+
170+ // Dynamic Rendering requires passing this in the pNext chain.
171+ auto rendering_ci = vk::PipelineRenderingCreateInfo{};
172+ // could be a depth-only pass, argument is span-like (notice the plural
173+ // ` Formats() ` ), only set if not Undefined.
128174 if (m_info.color_format != vk::Format::eUndefined) {
129- prci .setColorAttachmentFormats(m_info.color_format);
175+ rendering_ci .setColorAttachmentFormats(m_info.color_format);
130176 }
131- prci.setDepthAttachmentFormat(m_info.depth_format);
132-
133- auto gpci = vk::GraphicsPipelineCreateInfo{};
134- gpci.setStages(shader_stages)
135- .setPRasterizationState(&prsci)
136- .setPDepthStencilState(&pdssci)
137- .setPInputAssemblyState(&piasci)
138- .setPColorBlendState(&pcbsci)
139- .setPDynamicState(&pdsci)
140- .setPViewportState(&pvsci)
141- .setPMultisampleState(&pmsci)
142- .setLayout(layout)
143- .setPNext(&prci);
177+ // single depth attachment format, ok to set to Undefined.
178+ rendering_ci.setDepthAttachmentFormat(m_info.depth_format);
179+
180+ auto pipeline_ci = vk::GraphicsPipelineCreateInfo{};
181+ pipeline_ci.setLayout(layout)
182+ .setStages(shader_stage_ci)
183+ .setPVertexInputState(&vertex_input_ci)
184+ .setPViewportState(&viewport_state_v)
185+ .setPMultisampleState(&multisample_state_ci)
186+ .setPInputAssemblyState(&input_assembly_ci)
187+ .setPRasterizationState(&rasterization_state_ci)
188+ .setPDepthStencilState(&depth_stencil_state_ci)
189+ .setPColorBlendState(&color_blend_state_ci)
190+ .setPDynamicState(&dynamic_state_ci)
191+ .setPNext(&rendering_ci);
144192
145193 auto ret = vk::Pipeline{};
146194 // use non-throwing API.
147- if (m_info.device.createGraphicsPipelines({}, 1, &gpci , {}, &ret) !=
195+ if (m_info.device.createGraphicsPipelines({}, 1, &pipeline_ci , {}, &ret) !=
148196 vk::Result::eSuccess) {
149197 return {};
150198 }
@@ -195,15 +243,17 @@ if (!m_pipeline) {
195243}
196244```
197245
198- We can now bind it and use it to draw the triangle in the shader. Since we used dynamic viewport and scissor during pipeline creation, we need to set those after binding the pipeline.
246+ We can now bind it and use it to draw the triangle in the shader:
199247
200248```cpp
201249command_buffer.beginRendering(rendering_info);
202250ImGui::ShowDemoWindow();
203251
204252command_buffer.bindPipeline(vk::PipelineBindPoint::eGraphics, *m_pipeline);
253+ // we are creating pipelines with dynamic viewport and scissor states.
254+ // they must be set here after binding (before drawing).
205255auto viewport = vk::Viewport{};
206- // flip the viewport across the X-axis (negative height):
256+ // flip the viewport about the X-axis (negative height):
207257// https://www.saschawillems.de/blog/2019/03/29/flipping-the-vulkan-viewport/
208258viewport.setX(0.0f)
209259 .setY(static_cast<float>(m_render_target->extent.height))
@@ -242,6 +292,8 @@ layout (location = 0) in vec3 in_color;
242292out_color = vec4(in_color, 1.0);
243293```
244294
295+ > Make sure to recompile both the SPIR-V shaders in assets/.
296+
245297And a black clear color:
246298
247299``` cpp
0 commit comments