16 VkFormat color_format,
17 VkFormat depth_format,
18 VkPipelineCache pipeline_cache,
19 const std::string &mask_vertex_shader,
20 const std::string &mask_fragment_shader,
21 const std::string &content_vertex_shader,
22 const std::string &content_fragment_shader) {
26 render_color_format = color_format;
27 render_depth_format = depth_format;
28 cache = pipeline_cache;
29 mask_vertex_path = mask_vertex_shader;
30 mask_fragment_path = mask_fragment_shader;
31 content_vertex_path = content_vertex_shader;
32 content_fragment_path = content_fragment_shader;
33 stencil_image_format = choose_stencil_format();
43 render_color_format = VK_FORMAT_UNDEFINED;
44 render_depth_format = VK_FORMAT_UNDEFINED;
45 stencil_image_format = VK_FORMAT_UNDEFINED;
46 cache = VK_NULL_HANDLE;
47 mask_vertex_path.clear();
48 mask_fragment_path.clear();
49 content_vertex_path.clear();
50 content_fragment_path.clear();
54 if (
extent.width == stencil_extent.width &&
extent.height == stencil_extent.height) {
67 VkImageMemoryBarrier2 barrier{};
68 barrier.sType = VK_STRUCTURE_TYPE_IMAGE_MEMORY_BARRIER_2;
69 barrier.srcStageMask = image_initialized ? VK_PIPELINE_STAGE_2_LATE_FRAGMENT_TESTS_BIT : VK_PIPELINE_STAGE_2_NONE;
70 barrier.srcAccessMask = image_initialized ? VK_ACCESS_2_DEPTH_STENCIL_ATTACHMENT_WRITE_BIT : VK_ACCESS_2_NONE;
71 barrier.dstStageMask = VK_PIPELINE_STAGE_2_EARLY_FRAGMENT_TESTS_BIT | VK_PIPELINE_STAGE_2_LATE_FRAGMENT_TESTS_BIT;
72 barrier.dstAccessMask = VK_ACCESS_2_DEPTH_STENCIL_ATTACHMENT_READ_BIT | VK_ACCESS_2_DEPTH_STENCIL_ATTACHMENT_WRITE_BIT;
73 barrier.oldLayout = image_initialized ? VK_IMAGE_LAYOUT_STENCIL_ATTACHMENT_OPTIMAL : VK_IMAGE_LAYOUT_UNDEFINED;
74 barrier.newLayout = VK_IMAGE_LAYOUT_STENCIL_ATTACHMENT_OPTIMAL;
75 barrier.srcQueueFamilyIndex = VK_QUEUE_FAMILY_IGNORED;
76 barrier.dstQueueFamilyIndex = VK_QUEUE_FAMILY_IGNORED;
77 barrier.image = image;
78 barrier.subresourceRange.aspectMask = VK_IMAGE_ASPECT_STENCIL_BIT;
79 barrier.subresourceRange.levelCount = 1;
80 barrier.subresourceRange.layerCount = 1;
82 VkDependencyInfo dependency{};
83 dependency.sType = VK_STRUCTURE_TYPE_DEPENDENCY_INFO;
84 dependency.imageMemoryBarrierCount = 1;
85 dependency.pImageMemoryBarriers = &barrier;
86 vkCmdPipelineBarrier2(cmd, &dependency);
87 image_initialized =
true;
94 attachment.imageView = view;
95 attachment.imageLayout = VK_IMAGE_LAYOUT_STENCIL_ATTACHMENT_OPTIMAL;
96 attachment.loadOp = VK_ATTACHMENT_LOAD_OP_CLEAR;
97 attachment.storeOp = VK_ATTACHMENT_STORE_OP_DONT_CARE;
98 attachment.clearValue.depthStencil = {1.0f, 0U};
102 if (mask_pipeline == VK_NULL_HANDLE || mask_layout == VK_NULL_HANDLE) {
105 vkCmdBindPipeline(cmd, VK_PIPELINE_BIND_POINT_GRAPHICS, mask_pipeline);
106 vkCmdPushConstants(cmd, mask_layout, VK_SHADER_STAGE_FRAGMENT_BIT, 0,
sizeof(
PushConstants), &push_constants);
107 vkCmdDraw(cmd, 3, 1, 0, 0);
111 if (content_pipeline == VK_NULL_HANDLE || content_layout == VK_NULL_HANDLE) {
114 vkCmdBindPipeline(cmd, VK_PIPELINE_BIND_POINT_GRAPHICS, content_pipeline);
115 vkCmdPushConstants(cmd, content_layout, VK_SHADER_STAGE_FRAGMENT_BIT, 0,
sizeof(
PushConstants), &push_constants);
116 vkCmdDraw(cmd, 3, 1, 0, 0);
120 return vk_context.device != VK_NULL_HANDLE && image != VK_NULL_HANDLE && view != VK_NULL_HANDLE &&
121 stencil_extent.width > 0U && stencil_extent.height > 0U;
124 void VK_Stencil::create_resources() {
125 if (vk_context.
device == VK_NULL_HANDLE || stencil_extent.width == 0U || stencil_extent.height == 0U) {
129 stencil_extent.width,
130 stencil_extent.height,
131 stencil_image_format,
132 VK_IMAGE_TILING_OPTIMAL,
133 VK_IMAGE_USAGE_DEPTH_STENCIL_ATTACHMENT_BIT,
134 VK_MEMORY_PROPERTY_DEVICE_LOCAL_BIT,
138 image_initialized =
false;
141 void VK_Stencil::create_pipelines() {
142 VkPushConstantRange push_constant{};
143 push_constant.stageFlags = VK_SHADER_STAGE_FRAGMENT_BIT;
144 push_constant.offset = 0;
147 VkPipelineLayoutCreateInfo layout_info{};
148 layout_info.sType = VK_STRUCTURE_TYPE_PIPELINE_LAYOUT_CREATE_INFO;
149 layout_info.pushConstantRangeCount = 1;
150 layout_info.pPushConstantRanges = &push_constant;
151 if (vkCreatePipelineLayout(vk_context.device, &layout_info,
nullptr, &mask_layout) != VK_SUCCESS ||
152 vkCreatePipelineLayout(vk_context.device, &layout_info,
nullptr, &content_layout) != VK_SUCCESS) {
153 throw mxvk::Exception(
"failed to create stencil pipeline layout");
156 mask_pipeline = create_pipeline(mask_vertex_path, mask_fragment_path, mask_layout,
true);
157 content_pipeline = create_pipeline(content_vertex_path, content_fragment_path, content_layout,
false);
160 void VK_Stencil::destroy_pipelines() {
161 if (vk_context.device == VK_NULL_HANDLE) {
162 mask_layout = VK_NULL_HANDLE;
163 content_layout = VK_NULL_HANDLE;
164 mask_pipeline = VK_NULL_HANDLE;
165 content_pipeline = VK_NULL_HANDLE;
168 if (mask_pipeline != VK_NULL_HANDLE) {
169 vkDestroyPipeline(vk_context.device, mask_pipeline,
nullptr);
171 if (content_pipeline != VK_NULL_HANDLE) {
172 vkDestroyPipeline(vk_context.device, content_pipeline,
nullptr);
174 if (mask_layout != VK_NULL_HANDLE) {
175 vkDestroyPipelineLayout(vk_context.device, mask_layout,
nullptr);
177 if (content_layout != VK_NULL_HANDLE) {
178 vkDestroyPipelineLayout(vk_context.device, content_layout,
nullptr);
180 mask_layout = VK_NULL_HANDLE;
181 content_layout = VK_NULL_HANDLE;
182 mask_pipeline = VK_NULL_HANDLE;
183 content_pipeline = VK_NULL_HANDLE;
186 void VK_Stencil::destroy_image() {
187 if (vk_context.device == VK_NULL_HANDLE) {
188 image = VK_NULL_HANDLE;
189 memory = VK_NULL_HANDLE;
190 view = VK_NULL_HANDLE;
191 image_initialized =
false;
194 if (view != VK_NULL_HANDLE) {
195 vkDestroyImageView(vk_context.device, view,
nullptr);
197 if (image != VK_NULL_HANDLE) {
198 vkDestroyImage(vk_context.device, image,
nullptr);
200 if (memory != VK_NULL_HANDLE) {
201 vkFreeMemory(vk_context.device, memory,
nullptr);
203 image = VK_NULL_HANDLE;
204 memory = VK_NULL_HANDLE;
205 view = VK_NULL_HANDLE;
206 image_initialized =
false;
209 VkFormat VK_Stencil::choose_stencil_format()
const {
210 const std::array<VkFormat, 3> candidates{
212 VK_FORMAT_D24_UNORM_S8_UINT,
213 VK_FORMAT_D32_SFLOAT_S8_UINT,
216 for (
const VkFormat format : candidates) {
217 VkFormatProperties properties{};
218 vkGetPhysicalDeviceFormatProperties(vk_context.physical_device, format, &properties);
219 if ((properties.optimalTilingFeatures & VK_FORMAT_FEATURE_DEPTH_STENCIL_ATTACHMENT_BIT) != 0U) {
223 throw mxvk::Exception(
"failed to find a supported Vulkan stencil format");
226 VkPipeline VK_Stencil::create_pipeline(
const std::string &vertex_shader,
227 const std::string &fragment_shader,
228 VkPipelineLayout layout,
229 bool writes_stencil)
const {
230 const std::vector<char> vert_bytes =
load_spv(vertex_shader);
231 const std::vector<char> frag_bytes =
load_spv(fragment_shader);
233 VkShaderModule frag_module = VK_NULL_HANDLE;
234 VkPipeline pipeline = VK_NULL_HANDLE;
238 std::array<VkPipelineShaderStageCreateInfo, 2> stages{};
239 stages[0].sType = VK_STRUCTURE_TYPE_PIPELINE_SHADER_STAGE_CREATE_INFO;
240 stages[0].stage = VK_SHADER_STAGE_VERTEX_BIT;
241 stages[0].module = vert_module;
242 stages[0].pName =
"main";
243 stages[1].sType = VK_STRUCTURE_TYPE_PIPELINE_SHADER_STAGE_CREATE_INFO;
244 stages[1].stage = VK_SHADER_STAGE_FRAGMENT_BIT;
245 stages[1].module = frag_module;
246 stages[1].pName =
"main";
248 VkPipelineVertexInputStateCreateInfo vertex_input{};
249 vertex_input.sType = VK_STRUCTURE_TYPE_PIPELINE_VERTEX_INPUT_STATE_CREATE_INFO;
251 VkPipelineInputAssemblyStateCreateInfo input_assembly{};
252 input_assembly.sType = VK_STRUCTURE_TYPE_PIPELINE_INPUT_ASSEMBLY_STATE_CREATE_INFO;
253 input_assembly.topology = VK_PRIMITIVE_TOPOLOGY_TRIANGLE_LIST;
255 VkPipelineViewportStateCreateInfo viewport_state{};
256 viewport_state.sType = VK_STRUCTURE_TYPE_PIPELINE_VIEWPORT_STATE_CREATE_INFO;
257 viewport_state.viewportCount = 1;
258 viewport_state.scissorCount = 1;
260 const VkDynamicState dynamic_states[] = {VK_DYNAMIC_STATE_VIEWPORT, VK_DYNAMIC_STATE_SCISSOR};
261 VkPipelineDynamicStateCreateInfo dynamic_state{};
262 dynamic_state.sType = VK_STRUCTURE_TYPE_PIPELINE_DYNAMIC_STATE_CREATE_INFO;
263 dynamic_state.dynamicStateCount = 2;
264 dynamic_state.pDynamicStates = dynamic_states;
266 VkPipelineRasterizationStateCreateInfo rasterizer{};
267 rasterizer.sType = VK_STRUCTURE_TYPE_PIPELINE_RASTERIZATION_STATE_CREATE_INFO;
268 rasterizer.polygonMode = VK_POLYGON_MODE_FILL;
269 rasterizer.cullMode = VK_CULL_MODE_NONE;
270 rasterizer.frontFace = VK_FRONT_FACE_COUNTER_CLOCKWISE;
271 rasterizer.lineWidth = 1.0f;
273 VkPipelineMultisampleStateCreateInfo multisample{};
274 multisample.sType = VK_STRUCTURE_TYPE_PIPELINE_MULTISAMPLE_STATE_CREATE_INFO;
275 multisample.rasterizationSamples = VK_SAMPLE_COUNT_1_BIT;
277 VkStencilOpState stencil_op{};
278 stencil_op.failOp = VK_STENCIL_OP_KEEP;
279 stencil_op.passOp = writes_stencil ? VK_STENCIL_OP_REPLACE : VK_STENCIL_OP_KEEP;
280 stencil_op.depthFailOp = VK_STENCIL_OP_KEEP;
281 stencil_op.compareOp = writes_stencil ? VK_COMPARE_OP_ALWAYS : VK_COMPARE_OP_EQUAL;
282 stencil_op.compareMask = 0xffU;
283 stencil_op.writeMask = writes_stencil ? 0xffU : 0U;
284 stencil_op.reference = 1U;
286 VkPipelineDepthStencilStateCreateInfo depth_stencil{};
287 depth_stencil.sType = VK_STRUCTURE_TYPE_PIPELINE_DEPTH_STENCIL_STATE_CREATE_INFO;
288 depth_stencil.depthTestEnable = VK_FALSE;
289 depth_stencil.depthWriteEnable = VK_FALSE;
290 depth_stencil.stencilTestEnable = VK_TRUE;
291 depth_stencil.front = stencil_op;
292 depth_stencil.back = stencil_op;
294 VkPipelineColorBlendAttachmentState blend_attachment{};
295 blend_attachment.colorWriteMask = writes_stencil ? 0U : (VK_COLOR_COMPONENT_R_BIT | VK_COLOR_COMPONENT_G_BIT | VK_COLOR_COMPONENT_B_BIT | VK_COLOR_COMPONENT_A_BIT);
297 VkPipelineColorBlendStateCreateInfo color_blend{};
298 color_blend.sType = VK_STRUCTURE_TYPE_PIPELINE_COLOR_BLEND_STATE_CREATE_INFO;
299 color_blend.attachmentCount = 1;
300 color_blend.pAttachments = &blend_attachment;
302 VkPipelineRenderingCreateInfo rendering{};
303 rendering.sType = VK_STRUCTURE_TYPE_PIPELINE_RENDERING_CREATE_INFO;
304 rendering.colorAttachmentCount = 1;
305 rendering.pColorAttachmentFormats = &render_color_format;
306 rendering.depthAttachmentFormat = render_depth_format;
307 rendering.stencilAttachmentFormat = stencil_image_format;
309 VkGraphicsPipelineCreateInfo pipeline_info{};
310 pipeline_info.sType = VK_STRUCTURE_TYPE_GRAPHICS_PIPELINE_CREATE_INFO;
311 pipeline_info.pNext = &rendering;
312 pipeline_info.stageCount =
static_cast<uint32_t
>(stages.size());
313 pipeline_info.pStages = stages.data();
314 pipeline_info.pVertexInputState = &vertex_input;
315 pipeline_info.pInputAssemblyState = &input_assembly;
316 pipeline_info.pViewportState = &viewport_state;
317 pipeline_info.pRasterizationState = &rasterizer;
318 pipeline_info.pMultisampleState = &multisample;
319 pipeline_info.pDepthStencilState = &depth_stencil;
320 pipeline_info.pColorBlendState = &color_blend;
321 pipeline_info.pDynamicState = &dynamic_state;
322 pipeline_info.layout = layout;
324 if (vkCreateGraphicsPipelines(vk_context.device, cache, 1, &pipeline_info,
nullptr, &pipeline) != VK_SUCCESS) {
325 throw mxvk::Exception(
"failed to create stencil graphics pipeline");
328 if (pipeline != VK_NULL_HANDLE) {
329 vkDestroyPipeline(vk_context.device, pipeline,
nullptr);
331 if (frag_module != VK_NULL_HANDLE) {
332 vkDestroyShaderModule(vk_context.device, frag_module,
nullptr);
334 vkDestroyShaderModule(vk_context.device, vert_module,
nullptr);
338 vkDestroyShaderModule(vk_context.device, frag_module,
nullptr);
339 vkDestroyShaderModule(vk_context.device, vert_module,
nullptr);
void initialize(const VulkanContext &context, VkExtent2D extent, VkFormat color_format, VkFormat depth_format, VkPipelineCache pipeline_cache, const std::string &mask_vertex_shader, const std::string &mask_fragment_shader, const std::string &content_vertex_shader, const std::string &content_fragment_shader)
Create the stencil attachment and fullscreen mask/content pipelines.
~VK_Stencil()
Destroy any live Vulkan resources owned by the helper.
void draw_mask(VkCommandBuffer cmd, const PushConstants &push_constants) const
Draw the fullscreen stencil-writing mask pass.
VkExtent2D extent() const noexcept
Return the current stencil attachment extent.
void draw_content(VkCommandBuffer cmd, const PushConstants &push_constants) const
Draw the fullscreen content pass clipped by stencil reference value 1.
void prepare_for_rendering(VkCommandBuffer cmd)
Transition the stencil image for use as a dynamic-rendering stencil attachment.
void destroy()
Release all Vulkan resources and reset the helper to an empty state.
void resize(VkExtent2D extent)
Recreate the stencil image for a new render extent.
void configure_attachment(VkRenderingAttachmentInfo &attachment) const
Fill a VkRenderingAttachmentInfo for VK_Window dynamic rendering.
bool valid() const noexcept
Check whether image resources are ready for rendering.
Reusable dynamic-rendering stencil image and fullscreen stencil pipelines.
Utilities for loading and saving PNG images.
VkShaderModule create_shader_module(VkDevice device, const std::vector< char > &spv_bytes)
Create a shader module from SPIR-V bytecode.
void create_image(const VulkanContext &context, uint32_t width, uint32_t height, VkFormat format, VkImageTiling tiling, VkImageUsageFlags usage, VkMemoryPropertyFlags properties, VkImage &image, VkDeviceMemory &memory)
Create a 2D VkImage and allocate/bind its memory.
VkImageView create_image_view(VkDevice device, VkImage image, VkFormat format, VkImageAspectFlags aspect)
Create a 2D image view for an image.
std::vector< char > load_spv(const std::string &path)
Load a SPIR-V file from disk.
Push-constant payload shared by the mask and content shaders.
Minimal Vulkan handles required by MXVK resource helpers.
VkDevice device
Logical device used to create and destroy resources.