GCC Code Coverage Report


ernie/
Coverage:
low: ≥ 0%
medium: ≥ 75.0%
high: ≥ 90.0%
Lines:
55 / 55
100.0%
Branches:
42 / 70
60.0%
Functions:
4 / 5
80.0%

device/kernel.cpp
Line Branch Exec Source
1 // SPDX-License-Identifier: BSD-3-Clause
2 /// @file kernel.cpp
3 /// @brief Creating compute pipelines and recording dispatches.
4 #include "ernie/device/kernel.h"
5
6 #include "ernie/util/on_failure.h"
7
8 #include <stdexcept>
9
10 namespace ernie {
11
12 100 ComputeKernel::ComputeKernel(DeviceContext& context, std::span<const std::uint32_t> spirv,
13 std::string_view name, std::uint32_t parameterBytes,
14 100 const BindlessHeap* heap, std::string_view entryPoint)
15 100 : device_(context.Device()), name_(name), parameterBytes_(parameterBytes),
16
2/2
✓ Branch 0 taken 12 times.
✓ Branch 1 taken 88 times.
100 heap_(heap != nullptr ? heap->Set() : VK_NULL_HANDLE) {
17
1/2
✓ Branch 1 taken 100 times.
✗ Branch 2 not taken.
100 ERNIE_ZONE("Create compute pipeline");
18 100 ERNIE_ZONE_TEXT(name_);
19
2/2
✓ Branch 0 taken 1 time.
✓ Branch 1 taken 99 times.
100 if (parameterBytes > context.Limits().maxPushConstantsSize) {
20 1 throw DeviceError(DeviceErrorKind::Unsupported,
21
1/2
✓ Branch 2 taken 1 time.
✗ Branch 3 not taken.
2 name_ + " passes more bytes per dispatch than the device allows");
22 }
23
2/2
✓ Branch 0 taken 1 time.
✓ Branch 1 taken 98 times.
99 if (parameterBytes % 4 != 0) {
24
2/4
✓ Branch 2 taken 1 time.
✗ Branch 3 not taken.
✓ Branch 5 taken 1 time.
✗ Branch 6 not taken.
2 throw std::invalid_argument(name_ + " passes bytes per dispatch in 4-byte words");
25 }
26 98 const VkPushConstantRange range{.stageFlags = VK_SHADER_STAGE_COMPUTE_BIT,
27 98 .size = parameterBytes};
28
2/2
✓ Branch 0 taken 12 times.
✓ Branch 1 taken 86 times.
98 const VkDescriptorSetLayout heapLayout = heap != nullptr ? heap->Layout() : VK_NULL_HANDLE;
29 98 const VkPipelineLayoutCreateInfo layout{.sType = VK_STRUCTURE_TYPE_PIPELINE_LAYOUT_CREATE_INFO,
30
2/2
✓ Branch 0 taken 86 times.
✓ Branch 1 taken 12 times.
98 .setLayoutCount = heap != nullptr ? 1u : 0u,
31 .pSetLayouts = &heapLayout,
32 98 .pushConstantRangeCount = parameterBytes > 0 ? 1u : 0u,
33
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 98 times.
98 .pPushConstantRanges = &range};
34
2/4
✓ Branch 1 taken 98 times.
✗ Branch 2 not taken.
✓ Branch 4 taken 98 times.
✗ Branch 5 not taken.
98 CheckVk(vkCreatePipelineLayout(device_, &layout, nullptr, &layout_),
35
2/4
✓ Branch 1 taken 98 times.
✗ Branch 2 not taken.
✓ Branch 4 taken 98 times.
✗ Branch 5 not taken.
294 "Creating " + name_ + "'s layout");
36
1/2
✓ Branch 1 taken 98 times.
✗ Branch 2 not taken.
98 const OnFailure freeLayout([&] { vkDestroyPipelineLayout(device_, layout_, nullptr); });
37
38 // maintenance5 would take the code inline; a module is portable to every Vulkan 1.3 driver.
39
1/2
✓ Branch 1 taken 98 times.
✗ Branch 2 not taken.
98 const VkShaderModuleCreateInfo code{.sType = VK_STRUCTURE_TYPE_SHADER_MODULE_CREATE_INFO,
40 98 .codeSize = spirv.size_bytes(),
41
1/2
✓ Branch 1 taken 98 times.
✗ Branch 2 not taken.
98 .pCode = spirv.data()};
42 98 VkShaderModule module = VK_NULL_HANDLE;
43
3/6
✓ Branch 1 taken 98 times.
✗ Branch 2 not taken.
✓ Branch 4 taken 98 times.
✗ Branch 5 not taken.
✓ Branch 7 taken 98 times.
✗ Branch 8 not taken.
98 CheckVk(vkCreateShaderModule(device_, &code, nullptr, &module), "Loading " + name_);
44
1/2
✓ Branch 1 taken 98 times.
✗ Branch 2 not taken.
98 const std::string entry(entryPoint);
45 98 const VkComputePipelineCreateInfo pipeline{
46 .sType = VK_STRUCTURE_TYPE_COMPUTE_PIPELINE_CREATE_INFO,
47 .stage = {.sType = VK_STRUCTURE_TYPE_PIPELINE_SHADER_STAGE_CREATE_INFO,
48 .stage = VK_SHADER_STAGE_COMPUTE_BIT,
49 .module = module,
50 98 .pName = entry.c_str()},
51
1/2
✓ Branch 1 taken 98 times.
✗ Branch 2 not taken.
98 .layout = layout_};
52
1/2
✓ Branch 1 taken 98 times.
✗ Branch 2 not taken.
98 const VkResult result = vkCreateComputePipelines(device_, context.PipelineCache(), 1, &pipeline,
53 nullptr, &pipeline_);
54
1/2
✓ Branch 1 taken 98 times.
✗ Branch 2 not taken.
98 vkDestroyShaderModule(device_, module, nullptr);
55
2/4
✓ Branch 1 taken 98 times.
✗ Branch 2 not taken.
✓ Branch 4 taken 98 times.
✗ Branch 5 not taken.
98 CheckVk(result, "Creating " + name_);
56
1/2
✓ Branch 1 taken 98 times.
✗ Branch 2 not taken.
98 context.Name(VK_OBJECT_TYPE_PIPELINE, reinterpret_cast<std::uint64_t>(pipeline_), name_);
57 102 }
58
59 98 ComputeKernel::~ComputeKernel() {
60 98 vkDestroyPipeline(device_, pipeline_, nullptr);
61 98 vkDestroyPipelineLayout(device_, layout_, nullptr);
62 98 }
63
64 172 void ComputeKernel::Dispatch(VkCommandBuffer commands, glm::uvec3 groups,
65 std::span<const std::byte> parameters) const {
66
2/2
✓ Branch 0 taken 1 time.
✓ Branch 1 taken 171 times.
172 if (parameters.size() != parameterBytes_) {
67
2/4
✓ Branch 2 taken 1 time.
✗ Branch 3 not taken.
✓ Branch 5 taken 1 time.
✗ Branch 6 not taken.
2 throw std::invalid_argument(name_ + " takes " + std::to_string(parameterBytes_) +
68
1/2
✓ Branch 1 taken 1 time.
✗ Branch 2 not taken.
2 " bytes per dispatch, not " +
69
2/4
✓ Branch 2 taken 1 time.
✗ Branch 3 not taken.
✓ Branch 5 taken 1 time.
✗ Branch 6 not taken.
3 std::to_string(parameters.size()));
70 }
71 171 vkCmdBindPipeline(commands, VK_PIPELINE_BIND_POINT_COMPUTE, pipeline_);
72
2/2
✓ Branch 0 taken 18 times.
✓ Branch 1 taken 153 times.
171 if (heap_ != VK_NULL_HANDLE) {
73 18 vkCmdBindDescriptorSets(commands, VK_PIPELINE_BIND_POINT_COMPUTE, layout_, 0, 1, &heap_, 0,
74 nullptr);
75 }
76
1/2
✓ Branch 0 taken 171 times.
✗ Branch 1 not taken.
171 if (parameterBytes_ > 0) {
77 171 vkCmdPushConstants(commands, layout_, VK_SHADER_STAGE_COMPUTE_BIT, 0, parameterBytes_,
78 171 parameters.data());
79 }
80 171 vkCmdDispatch(commands, groups.x, groups.y, groups.z);
81 171 }
82
83 73 void ComputeBarrier(VkCommandBuffer commands) {
84 73 Barrier(commands, VK_PIPELINE_STAGE_2_COMPUTE_SHADER_BIT, VK_ACCESS_2_SHADER_WRITE_BIT,
85 VK_PIPELINE_STAGE_2_COMPUTE_SHADER_BIT,
86 VK_ACCESS_2_SHADER_READ_BIT | VK_ACCESS_2_SHADER_WRITE_BIT);
87 73 }
88
89 } // namespace ernie
90