Skip to content

Commit 6d8fa1b

Browse files
committed
Embed Vulkan SPIR-V into headers
Add build support to embed Vulkan SPIR-V into generated C++ headers and use them at runtime. Introduces src/imiv/embed_spirv_header.cmake and CMake changes to generate header wrappers, a custom imiv_shaders target, and a compile-time IMIV_HAS_EMBEDDED_VULKAN_SHADERS define. Update Vulkan backend sources to prefer embedded shader word arrays (with fallbacks to .spv files) and add helper helpers for creating shader modules/pipelines from embedded data or files. Update docs to mention embedded SPIR-V and adjust source grouping; also include .clang-format tweaks and add a Windows oiio_exe.aps resource file. Signed-off-by: Vlad (Kuzmin) Erium <libalias@gmail.com> Signed-off-by: Vlad <shaamaan@gmail.com>
1 parent d96011f commit 6d8fa1b

File tree

8 files changed

+318
-26
lines changed

8 files changed

+318
-26
lines changed

.clang-format

Lines changed: 8 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
Language: Cpp
1+
---
22
BasedOnStyle: WebKit
33

44
AccessModifierOffset: -4
@@ -121,11 +121,17 @@ SpacesInContainerLiterals: true
121121
SpacesInCStyleCastParentheses: false
122122
SpacesInParentheses: false
123123
SpacesInSquareBrackets: false
124-
Standard: c++17
125124
StatementMacros:
126125
- Q_UNUSED
127126
- QT_REQUIRE_VERSION
128127
TabWidth: 8
129128
UseTab: Never
130129
#...
131130

131+
---
132+
Language: Cpp
133+
Standard: c++17
134+
135+
---
136+
Language: ObjC
137+
Standard: c++17

src/doc/imiv_dev.rst

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -811,8 +811,10 @@ Backend-specific shader paths
811811

812812
Vulkan
813813
uses build-time SPIR-V for the static upload and preview shaders from
814-
`src/imiv/shaders/`, with optional runtime shader compilation support for
815-
generated OCIO shaders.
814+
`src/imiv/shaders/`, embedded into generated headers at build time so the
815+
final binary does not depend on external ``.spv`` files at runtime. OCIO
816+
preview still uses optional runtime shader compilation support for the
817+
generated OCIO fragment shader.
816818

817819
OpenGL
818820
stays on native GLSL. It uploads supported source formats directly as GL

src/imiv/CMakeLists.txt

Lines changed: 65 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -218,7 +218,28 @@ set (_imiv_shader_spv_16f_fp64 "${CMAKE_CURRENT_BINARY_DIR}/imiv_upload_to_rgba1
218218
set (_imiv_shader_spv_32f_fp64 "${CMAKE_CURRENT_BINARY_DIR}/imiv_upload_to_rgba32f_fp64.comp.spv")
219219
set (_imiv_preview_vert_spv "${CMAKE_CURRENT_BINARY_DIR}/imiv_preview.vert.spv")
220220
set (_imiv_preview_frag_spv "${CMAKE_CURRENT_BINARY_DIR}/imiv_preview.frag.spv")
221+
set (_imiv_shader_hdr_16f "${CMAKE_CURRENT_BINARY_DIR}/imiv_upload_to_rgba16f_spv.h")
222+
set (_imiv_shader_hdr_32f "${CMAKE_CURRENT_BINARY_DIR}/imiv_upload_to_rgba32f_spv.h")
223+
set (_imiv_shader_hdr_16f_fp64 "${CMAKE_CURRENT_BINARY_DIR}/imiv_upload_to_rgba16f_fp64_spv.h")
224+
set (_imiv_shader_hdr_32f_fp64 "${CMAKE_CURRENT_BINARY_DIR}/imiv_upload_to_rgba32f_fp64_spv.h")
225+
set (_imiv_preview_vert_hdr "${CMAKE_CURRENT_BINARY_DIR}/imiv_preview_vert_spv.h")
226+
set (_imiv_preview_frag_hdr "${CMAKE_CURRENT_BINARY_DIR}/imiv_preview_frag_spv.h")
221227
set (_imiv_shader_outputs)
228+
set (_imiv_embedded_shader_headers)
229+
230+
function (_imiv_add_embedded_spirv_header input_spv output_hdr symbol_name)
231+
add_custom_command (
232+
OUTPUT "${output_hdr}"
233+
COMMAND ${CMAKE_COMMAND}
234+
-DINPUT="${input_spv}"
235+
-DOUTPUT="${output_hdr}"
236+
-DSYMBOL_NAME="${symbol_name}"
237+
-P "${CMAKE_CURRENT_SOURCE_DIR}/embed_spirv_header.cmake"
238+
DEPENDS
239+
"${input_spv}"
240+
"${CMAKE_CURRENT_SOURCE_DIR}/embed_spirv_header.cmake"
241+
COMMENT "imiv: embedding Vulkan shader ${symbol_name}")
242+
endfunction ()
222243

223244
if (_imiv_want_vulkan)
224245
find_program (OIIO_IMIV_GLSLC_EXECUTABLE
@@ -294,14 +315,47 @@ if (_imiv_want_vulkan)
294315
-o "${_imiv_preview_frag_spv}"
295316
DEPENDS "${_imiv_preview_frag_src}"
296317
COMMENT "imiv: compiling Vulkan preview fragment shader")
318+
_imiv_add_embedded_spirv_header (
319+
"${_imiv_shader_spv_16f}"
320+
"${_imiv_shader_hdr_16f}"
321+
"g_imiv_upload_to_rgba16f_spv")
322+
_imiv_add_embedded_spirv_header (
323+
"${_imiv_shader_spv_32f}"
324+
"${_imiv_shader_hdr_32f}"
325+
"g_imiv_upload_to_rgba32f_spv")
326+
_imiv_add_embedded_spirv_header (
327+
"${_imiv_shader_spv_16f_fp64}"
328+
"${_imiv_shader_hdr_16f_fp64}"
329+
"g_imiv_upload_to_rgba16f_fp64_spv")
330+
_imiv_add_embedded_spirv_header (
331+
"${_imiv_shader_spv_32f_fp64}"
332+
"${_imiv_shader_hdr_32f_fp64}"
333+
"g_imiv_upload_to_rgba32f_fp64_spv")
334+
_imiv_add_embedded_spirv_header (
335+
"${_imiv_preview_vert_spv}"
336+
"${_imiv_preview_vert_hdr}"
337+
"g_imiv_preview_vert_spv")
338+
_imiv_add_embedded_spirv_header (
339+
"${_imiv_preview_frag_spv}"
340+
"${_imiv_preview_frag_hdr}"
341+
"g_imiv_preview_frag_spv")
297342
set (_imiv_shader_outputs
298343
"${_imiv_shader_spv_16f}"
299344
"${_imiv_shader_spv_32f}"
300345
"${_imiv_shader_spv_16f_fp64}"
301346
"${_imiv_shader_spv_32f_fp64}"
302347
"${_imiv_preview_vert_spv}"
303348
"${_imiv_preview_frag_spv}")
304-
add_custom_target (imiv_shaders DEPENDS ${_imiv_shader_outputs})
349+
set (_imiv_embedded_shader_headers
350+
"${_imiv_shader_hdr_16f}"
351+
"${_imiv_shader_hdr_32f}"
352+
"${_imiv_shader_hdr_16f_fp64}"
353+
"${_imiv_shader_hdr_32f_fp64}"
354+
"${_imiv_preview_vert_hdr}"
355+
"${_imiv_preview_frag_hdr}")
356+
add_custom_target (imiv_shaders DEPENDS
357+
${_imiv_shader_outputs}
358+
${_imiv_embedded_shader_headers})
305359
else ()
306360
message (STATUS
307361
"imiv: glslc not found; Vulkan compute upload shader generation disabled")
@@ -857,14 +911,14 @@ source_group ("imgui_te" FILES
857911
${_imiv_test_engine_sources}
858912
${_imiv_test_engine_integration_sources}
859913
"${CMAKE_CURRENT_SOURCE_DIR}/imiv_test_engine.h")
860-
source_group ("imiv\\shared" FILES ${_imiv_shared_sources})
861-
source_group ("imiv\\platform\\glfw" FILES ${_imiv_platform_glfw_sources})
862-
source_group ("imiv\\renderer\\vulkan" FILES ${_imiv_renderer_vulkan_sources})
914+
source_group ("Source Files" FILES ${_imiv_shared_sources})
915+
source_group ("Source Files\\platform\\glfw" FILES ${_imiv_platform_glfw_sources})
916+
source_group ("Source Files\\renderer\\vulkan" FILES ${_imiv_renderer_vulkan_sources})
863917
if (_imiv_renderer_metal_sources)
864-
source_group ("imiv\\renderer\\metal" FILES ${_imiv_renderer_metal_sources})
918+
source_group ("Source Files\\renderer\\metal" FILES ${_imiv_renderer_metal_sources})
865919
endif ()
866920
if (_imiv_renderer_opengl_sources)
867-
source_group ("imiv\\renderer\\opengl" FILES ${_imiv_renderer_opengl_sources})
921+
source_group ("Source Files\\renderer\\opengl" FILES ${_imiv_renderer_opengl_sources})
868922
endif ()
869923

870924
if (TARGET imiv)
@@ -876,6 +930,11 @@ if (TARGET imiv)
876930
else ()
877931
target_compile_definitions (imiv PRIVATE IMIV_HAS_COMPUTE_UPLOAD_SHADERS=0)
878932
endif ()
933+
if (_imiv_embedded_shader_headers)
934+
target_compile_definitions (imiv PRIVATE IMIV_HAS_EMBEDDED_VULKAN_SHADERS=1)
935+
else ()
936+
target_compile_definitions (imiv PRIVATE IMIV_HAS_EMBEDDED_VULKAN_SHADERS=0)
937+
endif ()
879938
if (_imiv_renderer_is_vulkan)
880939
target_compile_definitions (imiv PRIVATE IMIV_BACKEND_VULKAN_GLFW=1
881940
$<$<CONFIG:Debug>:IMIV_VULKAN_VALIDATION=1>)

src/imiv/embed_spirv_header.cmake

Lines changed: 60 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,60 @@
1+
if (NOT DEFINED INPUT OR NOT DEFINED OUTPUT OR NOT DEFINED SYMBOL_NAME)
2+
message (FATAL_ERROR
3+
"embed_spirv_header.cmake requires INPUT, OUTPUT, and SYMBOL_NAME")
4+
endif ()
5+
6+
get_filename_component (_embed_output_dir "${OUTPUT}" DIRECTORY)
7+
file (MAKE_DIRECTORY "${_embed_output_dir}")
8+
file (READ "${INPUT}" _embed_hex HEX)
9+
string (LENGTH "${_embed_hex}" _embed_hex_length)
10+
if (_embed_hex_length EQUAL 0)
11+
message (FATAL_ERROR "SPIR-V input '${INPUT}' is empty")
12+
endif ()
13+
14+
math (EXPR _embed_remainder "${_embed_hex_length} % 8")
15+
if (NOT _embed_remainder EQUAL 0)
16+
message (FATAL_ERROR
17+
"SPIR-V input '${INPUT}' has invalid size ${_embed_hex_length}")
18+
endif ()
19+
20+
math (EXPR _embed_word_count "${_embed_hex_length} / 8")
21+
math (EXPR _embed_last_index "${_embed_word_count} - 1")
22+
23+
file (WRITE "${OUTPUT}" "// Generated by embed_spirv_header.cmake. Do not edit.\n")
24+
file (APPEND "${OUTPUT}" "#pragma once\n\n")
25+
file (APPEND "${OUTPUT}" "#include <cstddef>\n")
26+
file (APPEND "${OUTPUT}" "#include <cstdint>\n\n")
27+
file (APPEND "${OUTPUT}" "namespace Imiv {\n\n")
28+
file (APPEND "${OUTPUT}" "static const uint32_t ${SYMBOL_NAME}[] = {\n")
29+
30+
set (_embed_line " ")
31+
set (_embed_items_on_line 0)
32+
foreach (_embed_word_index RANGE 0 ${_embed_last_index})
33+
math (EXPR _embed_offset "${_embed_word_index} * 8")
34+
math (EXPR _embed_offset_1 "${_embed_offset} + 2")
35+
math (EXPR _embed_offset_2 "${_embed_offset} + 4")
36+
math (EXPR _embed_offset_3 "${_embed_offset} + 6")
37+
string (SUBSTRING "${_embed_hex}" ${_embed_offset} 2 _embed_b0)
38+
string (SUBSTRING "${_embed_hex}" ${_embed_offset_1} 2 _embed_b1)
39+
string (SUBSTRING "${_embed_hex}" ${_embed_offset_2} 2 _embed_b2)
40+
string (SUBSTRING "${_embed_hex}" ${_embed_offset_3} 2 _embed_b3)
41+
42+
string (APPEND _embed_line
43+
"0x${_embed_b3}${_embed_b2}${_embed_b1}${_embed_b0}u")
44+
if (NOT _embed_word_index EQUAL _embed_last_index)
45+
string (APPEND _embed_line ", ")
46+
endif ()
47+
48+
math (EXPR _embed_items_on_line "${_embed_items_on_line} + 1")
49+
if (_embed_items_on_line EQUAL 4 OR _embed_word_index EQUAL _embed_last_index)
50+
string (APPEND _embed_line "\n")
51+
file (APPEND "${OUTPUT}" "${_embed_line}")
52+
set (_embed_line " ")
53+
set (_embed_items_on_line 0)
54+
endif ()
55+
endforeach ()
56+
57+
file (APPEND "${OUTPUT}" "};\n")
58+
file (APPEND "${OUTPUT}"
59+
"static constexpr std::size_t ${SYMBOL_NAME}_word_count = sizeof(${SYMBOL_NAME}) / sizeof(${SYMBOL_NAME}[0]);\n\n")
60+
file (APPEND "${OUTPUT}" "} // namespace Imiv\n")

src/imiv/imiv_backends.md

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -109,7 +109,8 @@ Implementation:
109109
Constraints:
110110

111111
- uses runtime shader compilation for OCIO
112-
- uses Vulkan SPIR-V pipelines
112+
- uses Vulkan SPIR-V pipelines embedded into the binary at build time for the
113+
static upload/preview stages
113114
- remains the canonical backend for feature parity and regressions
114115

115116
Notes:

src/imiv/imiv_vulkan_ocio.cpp

Lines changed: 34 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,12 @@
1515
#include <string>
1616
#include <vector>
1717

18+
#if defined(IMIV_WITH_VULKAN) \
19+
&& defined(IMIV_HAS_EMBEDDED_VULKAN_SHADERS) \
20+
&& IMIV_HAS_EMBEDDED_VULKAN_SHADERS
21+
# include "imiv_preview_vert_spv.h"
22+
#endif
23+
1824
namespace Imiv {
1925

2026
#if defined(IMIV_WITH_VULKAN)
@@ -98,6 +104,21 @@ namespace {
98104
error_message, path.c_str());
99105
}
100106

107+
bool create_shader_module_from_embedded_or_file(
108+
VkDevice device, VkAllocationCallbacks* allocator,
109+
const uint32_t* words, size_t word_count, const std::string& path,
110+
const char* debug_name, VkShaderModule& shader_module,
111+
std::string& error_message)
112+
{
113+
if (words != nullptr && word_count != 0) {
114+
return create_shader_module_from_words(
115+
device, allocator, words, word_count, shader_module,
116+
error_message, debug_name);
117+
}
118+
return create_shader_module_from_file(device, allocator, path,
119+
shader_module, error_message);
120+
}
121+
101122
void destroy_ocio_texture(VulkanState& vk_state, OcioVulkanTexture& texture)
102123
{
103124
if (texture.sampler != VK_NULL_HANDLE) {
@@ -626,9 +647,19 @@ namespace {
626647

627648
const std::string shader_vert = std::string(IMIV_SHADER_DIR)
628649
+ "/imiv_preview.vert.spv";
629-
if (!create_shader_module_from_file(vk_state.device, vk_state.allocator,
630-
shader_vert, vert_module,
631-
error_message)) {
650+
# if defined(IMIV_HAS_EMBEDDED_VULKAN_SHADERS) \
651+
&& IMIV_HAS_EMBEDDED_VULKAN_SHADERS
652+
const uint32_t* shader_vert_words = g_imiv_preview_vert_spv;
653+
const size_t shader_vert_word_count
654+
= g_imiv_preview_vert_spv_word_count;
655+
# else
656+
const uint32_t* shader_vert_words = nullptr;
657+
const size_t shader_vert_word_count = 0;
658+
# endif
659+
if (!create_shader_module_from_embedded_or_file(
660+
vk_state.device, vk_state.allocator, shader_vert_words,
661+
shader_vert_word_count, shader_vert, "imiv.preview.vert",
662+
vert_module, error_message)) {
632663
return false;
633664
}
634665
if (!create_shader_module_from_words(

0 commit comments

Comments
 (0)