MXVK Vulkan Framework 0.35.0
C++20 Vulkan rendering framework for practical 2D and 3D application development with SDL3.
Loading...
Searching...
No Matches
mxvk_shader_module.cpp
Go to the documentation of this file.
2
4
5#include <fstream>
6#include <iterator>
7#include <span>
8
9namespace mxvk {
10 namespace {
11 constexpr uint32_t SPIRV_MAGIC = 0x07230203U;
12 constexpr uint16_t OP_ENTRY_POINT = 15U;
13 constexpr uint16_t OP_EXECUTION_MODE = 16U;
14 constexpr uint16_t OP_TYPE_IMAGE = 25U;
15 constexpr uint16_t OP_DECORATE = 71U;
16 constexpr uint32_t EXECUTION_MODEL_VERTEX = 0U;
17 constexpr uint32_t EXECUTION_MODEL_FRAGMENT = 4U;
18 constexpr uint32_t EXECUTION_MODEL_COMPUTE = 5U;
19 constexpr uint32_t EXECUTION_MODE_LOCAL_SIZE = 17U;
20 constexpr uint32_t DECORATION_BINDING = 33U;
21 constexpr uint32_t DECORATION_DESCRIPTOR_SET = 34U;
22 constexpr uint32_t IMAGE_FORMAT_RGBA16F = 2U;
23 constexpr uint32_t IMAGE_FORMAT_RGBA8 = 4U;
24 } // namespace
25
26 std::vector<char> load_spv(const std::string &path) {
27 if (path.empty()) {
28 throw mxvk::Exception("SPIR-V path is empty");
29 }
30
31 std::ifstream file(path, std::ios::binary);
32 if (!file.is_open()) {
33 throw mxvk::Exception("Failed to open SPIR-V file: " + path);
34 }
35
36 const std::vector<char> bytes((std::istreambuf_iterator<char>(file)), std::istreambuf_iterator<char>());
37 if (bytes.empty()) {
38 throw mxvk::Exception("SPIR-V file is empty: " + path);
39 }
40 if ((bytes.size() % 4U) != 0U) {
41 throw mxvk::Exception("SPIR-V file size is not 4-byte aligned: " + path);
42 }
43
44 return bytes;
45 }
46
47 ShaderModuleInfo inspect_spirv(const std::vector<char> &spv_bytes) {
48 if (spv_bytes.size() < 5U * sizeof(uint32_t) || (spv_bytes.size() % sizeof(uint32_t)) != 0U) {
49 throw mxvk::Exception("Invalid SPIR-V shader data");
50 }
51
52 const auto *word_data = reinterpret_cast<const uint32_t *>(spv_bytes.data());
53 const std::span<const uint32_t> words(word_data, spv_bytes.size() / sizeof(uint32_t));
54 if (words.front() != SPIRV_MAGIC) {
55 throw mxvk::Exception("Invalid SPIR-V magic word");
56 }
57
58 ShaderModuleInfo info{};
59 uint32_t entry_point_id = 0;
60 const uint32_t id_bound = words[3];
61 if (id_bound == 0U || id_bound > words.size()) {
62 throw mxvk::Exception("Invalid SPIR-V identifier bound");
63 }
64 std::vector<uint32_t> descriptor_sets(id_bound, UINT32_MAX);
65 std::vector<uint32_t> descriptor_bindings(id_bound, UINT32_MAX);
66 for (std::size_t offset = 5; offset < words.size();) {
67 const uint16_t word_count = static_cast<uint16_t>(words[offset] >> 16U);
68 const uint16_t opcode = static_cast<uint16_t>(words[offset] & 0xFFFFU);
69 if (word_count == 0 || offset + word_count > words.size()) {
70 throw mxvk::Exception("Malformed SPIR-V instruction stream");
71 }
72 if (opcode == OP_ENTRY_POINT && word_count >= 3U && entry_point_id == 0U) {
73 entry_point_id = words[offset + 2U];
74 switch (words[offset + 1U]) {
75 case EXECUTION_MODEL_VERTEX:
77 break;
78 case EXECUTION_MODEL_FRAGMENT:
80 break;
81 case EXECUTION_MODEL_COMPUTE:
83 break;
84 default:
86 break;
87 }
88 } else if (opcode == OP_DECORATE && word_count >= 4U && words[offset + 1U] < id_bound) {
89 const uint32_t target_id = words[offset + 1U];
90 const uint32_t decoration = words[offset + 2U];
91 if (decoration == DECORATION_BINDING) {
92 descriptor_bindings[target_id] = words[offset + 3U];
93 } else if (decoration == DECORATION_DESCRIPTOR_SET) {
94 descriptor_sets[target_id] = words[offset + 3U];
95 }
96 } else if (opcode == OP_TYPE_IMAGE && word_count >= 9U) {
97 const uint32_t image_format = words[offset + 8U];
98 if (image_format == IMAGE_FORMAT_RGBA8) {
100 } else if (image_format == IMAGE_FORMAT_RGBA16F) {
102 }
103 }
104 offset += word_count;
105 }
106
107 for (uint32_t id = 0; id < id_bound; ++id) {
108 if (descriptor_sets[id] != 0U) {
109 continue;
110 }
111 switch (descriptor_bindings[id]) {
112 case 2U:
113 info.usesHistoryTexture = true;
114 break;
115 case 3U:
116 info.usesSpectrumTexture = true;
117 break;
118 case 4U:
119 info.usesSpectrumHistoryTexture = true;
120 break;
121 default:
122 break;
123 }
124 }
125
126 if (info.stage == ShaderStage::Compute && entry_point_id != 0U) {
127 for (std::size_t offset = 5; offset < words.size();) {
128 const uint16_t word_count = static_cast<uint16_t>(words[offset] >> 16U);
129 const uint16_t opcode = static_cast<uint16_t>(words[offset] & 0xFFFFU);
130 if (opcode == OP_EXECUTION_MODE && word_count >= 6U && words[offset + 1U] == entry_point_id && words[offset + 2U] == EXECUTION_MODE_LOCAL_SIZE) {
131 info.localSizeX = words[offset + 3U];
132 info.localSizeY = words[offset + 4U];
133 info.localSizeZ = words[offset + 5U];
134 break;
135 }
136 offset += word_count;
137 }
138 }
139 return info;
140 }
141
142 VkShaderModule create_shader_module(VkDevice device, const std::vector<char> &spv_bytes) {
143 if (device == VK_NULL_HANDLE) {
144 throw mxvk::Exception("Cannot create shader module with a null device");
145 }
146 if (spv_bytes.empty() || (spv_bytes.size() % 4U) != 0U) {
147 throw mxvk::Exception("Invalid SPIR-V shader data");
148 }
149
150 VkShaderModuleCreateInfo create_info{};
151 create_info.sType = VK_STRUCTURE_TYPE_SHADER_MODULE_CREATE_INFO;
152 create_info.codeSize = spv_bytes.size();
153 create_info.pCode = reinterpret_cast<const uint32_t *>(spv_bytes.data());
154
155 VkShaderModule module = VK_NULL_HANDLE;
156 if (vkCreateShaderModule(device, &create_info, nullptr, &module) != VK_SUCCESS) {
157 throw mxvk::Exception("Failed to create shader module");
158 }
159
160 return module;
161 }
162} // namespace mxvk
Utilities for loading and saving PNG images.
Definition mxvk.hpp:31
VkShaderModule create_shader_module(VkDevice device, const std::vector< char > &spv_bytes)
Create a shader module from SPIR-V bytecode.
ShaderModuleInfo inspect_spirv(const std::vector< char > &spv_bytes)
Inspect a SPIR-V entry point and compute local workgroup size.
std::vector< char > load_spv(const std::string &path)
Load a SPIR-V file from disk.
StorageImageFormat storageImageFormat