diff --git a/CMakeLists.txt b/CMakeLists.txt index 39cba4aac..1073fa38f 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -38,6 +38,9 @@ set(CMAKE_ARCHIVE_OUTPUT_DIRECTORY "${PROJECT_SOURCE_DIR}/build/obj/${XE_PLATFOR # Create scratch/ if needed file(MAKE_DIRECTORY "${PROJECT_SOURCE_DIR}/scratch") +# Python for shader compilation scripts +find_package(Python3 REQUIRED COMPONENTS Interpreter) + # Include helpers include(cmake/XeniaHelpers.cmake) diff --git a/cmake/XeniaHelpers.cmake b/cmake/XeniaHelpers.cmake index 4390ee68b..ec16de618 100644 --- a/cmake/XeniaHelpers.cmake +++ b/cmake/XeniaHelpers.cmake @@ -100,6 +100,96 @@ function(xe_target_defaults target) endif() endfunction() +# xe_shader_rules_spirv(target shader_dir) +# +# Ensures SPIR-V shaders are compiled before the target builds. +# Uses a stamp file; rebuilds when any shader source changes. +function(xe_shader_rules_spirv target shader_dir) + get_filename_component(shader_dir "${shader_dir}" ABSOLUTE) + file(GLOB _sources + "${shader_dir}/*.xesl" "${shader_dir}/*.glsl" + "${shader_dir}/*.xesli" "${shader_dir}/*.glsli") + set(_stamp "${CMAKE_CURRENT_BINARY_DIR}/${target}_spirv.stamp") + set(_script "${PROJECT_SOURCE_DIR}/tools/build/compile_shader_spirv.py") + set(_valid_stages vs hs ds gs ps cs) + set(_commands) + set(_bytecode_dir "${shader_dir}/bytecode/vulkan_spirv") + list(APPEND _commands COMMAND ${CMAKE_COMMAND} -E make_directory "${_bytecode_dir}") + foreach(src ${_sources}) + get_filename_component(_name ${src} NAME) + string(REGEX REPLACE "\\.[^.]+$" "" _basename "${_name}") + string(REPLACE "." "_" _id "${_basename}") + string(LENGTH "${_id}" _len) + if(_len LESS 3) + continue() + endif() + math(EXPR _s "${_len} - 2") + string(SUBSTRING "${_id}" ${_s} 2 _stage) + if(NOT _stage IN_LIST _valid_stages) + continue() + endif() + list(APPEND _commands COMMAND ${Python3_EXECUTABLE} "${_script}" "${src}" "${_bytecode_dir}/${_id}.h") + endforeach() + add_custom_command( + OUTPUT "${_stamp}" + ${_commands} + COMMAND ${CMAKE_COMMAND} -E touch "${_stamp}" + DEPENDS ${_sources} "${_script}" + COMMENT "Compiling SPIR-V shaders for ${target}..." + VERBATIM + ) + add_custom_target(${target}-spirv-shaders DEPENDS "${_stamp}") + add_dependencies(${target} ${target}-spirv-shaders) + # Add sources for IDE visibility but prevent VS from compiling them itself + set_source_files_properties(${_sources} PROPERTIES HEADER_FILE_ONLY TRUE) + target_sources(${target} PRIVATE ${_sources}) +endfunction() + +# xe_shader_rules_dxbc(target shader_dir) +# +# Ensures DXBC shaders are compiled before the target builds. +# Uses a stamp file; rebuilds when any shader source changes. +function(xe_shader_rules_dxbc target shader_dir) + get_filename_component(shader_dir "${shader_dir}" ABSOLUTE) + file(GLOB _sources + "${shader_dir}/*.xesl" "${shader_dir}/*.hlsl" + "${shader_dir}/*.xesli" "${shader_dir}/*.hlsli") + set(_stamp "${CMAKE_CURRENT_BINARY_DIR}/${target}_dxbc.stamp") + set(_script "${PROJECT_SOURCE_DIR}/tools/build/compile_shader_dxbc.py") + set(_valid_stages vs hs ds gs ps cs) + set(_commands) + set(_bytecode_dir "${shader_dir}/bytecode/d3d12_5_1") + list(APPEND _commands COMMAND ${CMAKE_COMMAND} -E make_directory "${_bytecode_dir}") + foreach(src ${_sources}) + get_filename_component(_name ${src} NAME) + string(REGEX REPLACE "\\.[^.]+$" "" _basename "${_name}") + string(REPLACE "." "_" _id "${_basename}") + string(LENGTH "${_id}" _len) + if(_len LESS 3) + continue() + endif() + math(EXPR _s "${_len} - 2") + string(SUBSTRING "${_id}" ${_s} 2 _stage) + if(NOT _stage IN_LIST _valid_stages) + continue() + endif() + list(APPEND _commands COMMAND ${Python3_EXECUTABLE} "${_script}" "${src}" "${_bytecode_dir}/${_id}.h") + endforeach() + add_custom_command( + OUTPUT "${_stamp}" + ${_commands} + COMMAND ${CMAKE_COMMAND} -E touch "${_stamp}" + DEPENDS ${_sources} "${_script}" + COMMENT "Compiling DXBC shaders for ${target}..." + VERBATIM + ) + add_custom_target(${target}-dxbc-shaders DEPENDS "${_stamp}") + add_dependencies(${target} ${target}-dxbc-shaders) + # Add sources for IDE visibility but prevent VS from compiling them itself + set_source_files_properties(${_sources} PROPERTIES HEADER_FILE_ONLY TRUE) + target_sources(${target} PRIVATE ${_sources}) +endfunction() + # xe_force_c(files...) # # Forces the given source files to compile as C. diff --git a/src/xenia/gpu/d3d12/CMakeLists.txt b/src/xenia/gpu/d3d12/CMakeLists.txt index cbe9fdfba..47630aebf 100644 --- a/src/xenia/gpu/d3d12/CMakeLists.txt +++ b/src/xenia/gpu/d3d12/CMakeLists.txt @@ -1,9 +1,6 @@ add_library(xenia-gpu-d3d12 STATIC) xe_platform_sources(xenia-gpu-d3d12 ${CMAKE_CURRENT_SOURCE_DIR}) -# Include shader bytecode headers -file(GLOB _d3d12_shader_headers "${CMAKE_CURRENT_SOURCE_DIR}/../shaders/bytecode/d3d12_5_1/*.h" -) -target_sources(xenia-gpu-d3d12 PRIVATE ${_d3d12_shader_headers}) +xe_shader_rules_dxbc(xenia-gpu-d3d12 ${CMAKE_CURRENT_SOURCE_DIR}/../shaders) target_link_libraries(xenia-gpu-d3d12 PUBLIC fmt xxhash xenia-base xenia-gpu xenia-ui xenia-ui-d3d12 ) diff --git a/src/xenia/gpu/vulkan/CMakeLists.txt b/src/xenia/gpu/vulkan/CMakeLists.txt index 22c8b57f6..e2876d3b7 100644 --- a/src/xenia/gpu/vulkan/CMakeLists.txt +++ b/src/xenia/gpu/vulkan/CMakeLists.txt @@ -1,9 +1,6 @@ add_library(xenia-gpu-vulkan STATIC) xe_platform_sources(xenia-gpu-vulkan ${CMAKE_CURRENT_SOURCE_DIR}) -# Include shader bytecode headers -file(GLOB _vk_shader_headers "${CMAKE_CURRENT_SOURCE_DIR}/../shaders/bytecode/vulkan_spirv/*.h" -) -target_sources(xenia-gpu-vulkan PRIVATE ${_vk_shader_headers}) +xe_shader_rules_spirv(xenia-gpu-vulkan ${CMAKE_CURRENT_SOURCE_DIR}/../shaders) target_include_directories(xenia-gpu-vulkan PRIVATE ${PROJECT_SOURCE_DIR}/third_party/Vulkan-Headers/include ${PROJECT_SOURCE_DIR}/third_party/glslang diff --git a/src/xenia/ui/d3d12/CMakeLists.txt b/src/xenia/ui/d3d12/CMakeLists.txt index d214c403a..74f478562 100644 --- a/src/xenia/ui/d3d12/CMakeLists.txt +++ b/src/xenia/ui/d3d12/CMakeLists.txt @@ -1,9 +1,6 @@ add_library(xenia-ui-d3d12 STATIC) xe_platform_sources(xenia-ui-d3d12 ${CMAKE_CURRENT_SOURCE_DIR}) -# Include shader bytecode headers -file(GLOB _d3d12_ui_shader_headers "${CMAKE_CURRENT_SOURCE_DIR}/../shaders/bytecode/d3d12_5_1/*.h" -) -target_sources(xenia-ui-d3d12 PRIVATE ${_d3d12_ui_shader_headers}) +xe_shader_rules_dxbc(xenia-ui-d3d12 ${CMAKE_CURRENT_SOURCE_DIR}/../shaders) target_link_libraries(xenia-ui-d3d12 PUBLIC xenia-base xenia-ui) xe_target_defaults(xenia-ui-d3d12) diff --git a/src/xenia/ui/vulkan/CMakeLists.txt b/src/xenia/ui/vulkan/CMakeLists.txt index b6af414bb..8836d4d45 100644 --- a/src/xenia/ui/vulkan/CMakeLists.txt +++ b/src/xenia/ui/vulkan/CMakeLists.txt @@ -1,10 +1,7 @@ add_library(xenia-ui-vulkan STATIC) xe_platform_sources(xenia-ui-vulkan ${CMAKE_CURRENT_SOURCE_DIR}) xe_platform_sources(xenia-ui-vulkan ${CMAKE_CURRENT_SOURCE_DIR}/functions) -# Include shader bytecode headers -file(GLOB _vk_ui_shader_headers "${CMAKE_CURRENT_SOURCE_DIR}/../shaders/bytecode/vulkan_spirv/*.h" -) -target_sources(xenia-ui-vulkan PRIVATE ${_vk_ui_shader_headers}) +xe_shader_rules_spirv(xenia-ui-vulkan ${CMAKE_CURRENT_SOURCE_DIR}/../shaders) target_include_directories(xenia-ui-vulkan PRIVATE ${PROJECT_SOURCE_DIR}/third_party/Vulkan-Headers/include ${PROJECT_SOURCE_DIR}/third_party/glslang diff --git a/tools/build/compile_shader_dxbc.py b/tools/build/compile_shader_dxbc.py new file mode 100644 index 000000000..d191a2125 --- /dev/null +++ b/tools/build/compile_shader_dxbc.py @@ -0,0 +1,121 @@ +#!/usr/bin/env python3 + +"""Compiles a single shader to DXBC and generates a C header via FXC. + +Usage: compile_shader_dxbc.py + +FXC directly produces the .h file — no post-processing needed. +""" + +import os +import subprocess +import sys +from glob import glob + + +DXBC_STAGES = ["vs", "hs", "ds", "gs", "ps", "cs"] + + +def find_fxc(): + """Find FXC via FXC_PATH env or Windows SDK search.""" + fxc = os.environ.get("FXC_PATH") + if fxc: + return fxc + + # Search Windows Kits. + program_files_x86 = os.environ.get("ProgramFiles(x86)", "") + candidates = glob(os.path.join( + program_files_x86, "Windows Kits", "10", "bin", "*", "x64", "fxc.exe")) + if candidates: + return candidates[-1] # Highest version is last + + return None + + +def parse_stage(filename): + """Extract the 2-char shader stage from filename like 'foo.cs.xesl'.""" + basename = os.path.splitext(filename)[0] # 'foo.cs' + identifier = basename.replace(".", "_") # 'foo_cs' + stage_key = identifier[-2:] + if stage_key not in DXBC_STAGES: + return None + return stage_key + + +def main(): + if len(sys.argv) != 3: + print(f"Usage: {sys.argv[0]} ", file=sys.stderr) + return 1 + + input_path = sys.argv[1] + output_path = sys.argv[2] + + src_name = os.path.basename(input_path) + src_dir = os.path.dirname(input_path) + + stage = parse_stage(src_name) + if stage is None: + print(f"ERROR: cannot determine shader stage from: {src_name}", file=sys.stderr) + return 1 + + identifier = os.path.splitext(src_name)[0].replace(".", "_") + + fxc = find_fxc() + if not fxc: + print("ERROR: could not find fxc! Set FXC_PATH environment variable " + "or install Windows SDK.", file=sys.stderr) + return 1 + + # Create output directory if needed. + os.makedirs(os.path.dirname(output_path), exist_ok=True) + + is_dxc = "dxc" in os.path.basename(fxc).lower() + + # Start with base command — use wine on non-Windows platforms. + if sys.platform != "win32": + compiler_args = ["wine", fxc] + else: + compiler_args = [fxc] + + if is_dxc: + # DXC only supports SM 6.0+. + compiler_args.extend([ + "-T", f"{stage}_6_0", + "-HV", "2017", + "-D", "SHADING_LANGUAGE_HLSL_XE=1", + "-I", src_dir, + "-Fh", output_path, + "-Vn", identifier, + "-nologo", + input_path, + ]) + else: + # FXC uses traditional syntax. + compiler_args.extend([ + "/D", "SHADING_LANGUAGE_HLSL_XE=1", + "/I", src_dir, + "/Fh", output_path, + "/T", f"{stage}_5_1", + "/Vn", identifier, + "/O3", + "/Qstrip_reflect", + "/Qstrip_debug", + "/Qstrip_priv", + "/Gfp", + "/nologo", + input_path, + ]) + + result = subprocess.run(compiler_args, stdout=subprocess.DEVNULL, + stderr=subprocess.PIPE) + if result.returncode != 0: + print(f"ERROR: failed to compile DXBC shader: {src_name}", file=sys.stderr) + if result.stderr: + sys.stderr.buffer.write(result.stderr) + return 1 + + return 0 + + +if __name__ == "__main__": + sys.exit(main()) diff --git a/tools/build/compile_shader_spirv.py b/tools/build/compile_shader_spirv.py new file mode 100644 index 000000000..2f7eb262c --- /dev/null +++ b/tools/build/compile_shader_spirv.py @@ -0,0 +1,171 @@ +#!/usr/bin/env python3 + +"""Compiles a single shader to SPIR-V and generates a C header. + +Usage: compile_shader_spirv.py + +Pipeline: + 1. glslangValidator -> unoptimized .spv + 2. spirv-opt -> optimized .spv + 3. spirv-dis -> disassembly .txt + 4. Generate .h with disassembly comment + uint32_t array +""" + +import os +import struct +import subprocess +import sys + + +SPIRV_STAGES = { + "vs": "vert", "hs": "tesc", "ds": "tese", + "gs": "geom", "ps": "frag", "cs": "comp", +} + +XESL_WRAPPER = ( + "#version 460\n" + "#extension GL_EXT_control_flow_attributes : require\n" + "#extension GL_EXT_samplerless_texture_functions : require\n" + "#extension GL_GOOGLE_include_directive : require\n" + "#include \"%s\"\n" +) + + +def find_vulkan_tools(): + """Find Vulkan SDK tools via VULKAN_SDK env or PATH.""" + vulkan_sdk = os.environ.get("VULKAN_SDK") + if vulkan_sdk: + bin_dir = os.path.join(vulkan_sdk, "bin") + if os.path.isdir(bin_dir): + return ( + os.path.join(bin_dir, "glslangValidator"), + os.path.join(bin_dir, "spirv-opt"), + os.path.join(bin_dir, "spirv-dis"), + ) + + # Fall back to PATH + return ("glslangValidator", "spirv-opt", "spirv-dis") + + +def parse_stage(filename): + """Extract the 2-char shader stage from filename like 'foo.cs.xesl'.""" + basename = os.path.splitext(filename)[0] # 'foo.cs' + identifier = basename.replace(".", "_") # 'foo_cs' + stage_key = identifier[-2:] + if stage_key not in SPIRV_STAGES: + return None, None + return stage_key, SPIRV_STAGES[stage_key] + + +def main(): + if len(sys.argv) != 3: + print(f"Usage: {sys.argv[0]} ", file=sys.stderr) + return 1 + + input_path = sys.argv[1] + output_path = sys.argv[2] + + src_name = os.path.basename(input_path) + src_dir = os.path.dirname(input_path) + src_is_xesl = src_name.endswith(".xesl") + + stage_key, spirv_stage = parse_stage(src_name) + if spirv_stage is None: + print(f"ERROR: cannot determine shader stage from: {src_name}", file=sys.stderr) + return 1 + + # Compute identifier (matches what Lua does: basename with dots -> underscores) + identifier = os.path.splitext(src_name)[0].replace(".", "_") + + glslang, spirv_opt, spirv_dis = find_vulkan_tools() + + # Create output directory if needed. + os.makedirs(os.path.dirname(output_path), exist_ok=True) + + # Temp file paths next to output. + base = os.path.splitext(output_path)[0] + glslang_spv = base + ".glslang.spv" + opt_spv = base + ".spv" + dis_txt = base + ".txt" + + try: + # Step 1: glslangValidator + glslang_args = [ + glslang, + "--stdin" if src_is_xesl else input_path, + "-DSHADING_LANGUAGE_GLSL_XE=1", + "-S", spirv_stage, + "-o", glslang_spv, + "-V", + ] + if src_is_xesl: + glslang_args.append(f"-I{src_dir}") + + stdin_data = (XESL_WRAPPER % src_name) if src_is_xesl else None + result = subprocess.run(glslang_args, input=stdin_data, text=True, + stdout=subprocess.DEVNULL, stderr=subprocess.PIPE) + if result.returncode != 0: + print(f"ERROR: glslangValidator failed for {src_name}", file=sys.stderr) + if result.stderr: + sys.stderr.write(result.stderr) + return 1 + + # Step 2: spirv-opt + result = subprocess.run([ + spirv_opt, "-O", "-O", "--canonicalize-ids", + glslang_spv, "-o", opt_spv, + ], stdout=subprocess.DEVNULL, stderr=subprocess.PIPE) + if result.returncode != 0: + print(f"ERROR: spirv-opt failed for {src_name}", file=sys.stderr) + if result.stderr: + sys.stderr.write(result.stderr) + return 1 + + # Step 3: spirv-dis + result = subprocess.run([spirv_dis, "-o", dis_txt, opt_spv], + stdout=subprocess.DEVNULL, stderr=subprocess.PIPE) + if result.returncode != 0: + print(f"ERROR: spirv-dis failed for {src_name}", file=sys.stderr) + if result.stderr: + sys.stderr.write(result.stderr) + return 1 + + # Step 4: Generate header + with open(output_path, "w") as out: + out.write("// Generated with `xb buildshaders`.\n#if 0\n") + with open(dis_txt, "r") as dis_file: + dis_data = dis_file.read() + if dis_data: + out.write(dis_data) + if dis_data[-1] != "\n": + out.write("\n") + out.write("#endif\n\nconst uint32_t %s[] = {" % identifier) + with open(opt_spv, "rb") as spv_file: + index = 0 + while True: + word = spv_file.read(4) + if len(word) == 0: + break + if len(word) != 4: + print("ERROR: SPIR-V binary is misaligned", file=sys.stderr) + return 1 + if index % 6 == 0: + out.write("\n ") + else: + out.write(" ") + index += 1 + value = struct.unpack(" d3d12_5_1") - dxbc_dir_path = os.path.join(os.path.dirname(src_path), - "bytecode/d3d12_5_1") - os.makedirs(dxbc_dir_path, exist_ok=True) - dxbc_file_path_base = os.path.join(dxbc_dir_path, dxbc_identifier) - # Not enabling treating warnings as errors (/WX) because it - # overrides #pragma warning, and the FXAA shader triggers a - # bug in FXC causing an uninitialized variable warning if - # early exit from a function is done. - # FXC writes errors and warnings to stderr, not stdout, but - # stdout receives generic status messages that only add - # clutter in this case. - # Check if using DXC or FXC based on executable name - is_dxc = "dxc" in fxc.lower() - - # Start with base command - use wine on non-Windows platforms - if sys.platform != "win32": - compiler_args = ["wine", fxc] - else: - compiler_args = [fxc] - src_dir = os.path.dirname(src_path) - if is_dxc: - # DXC only supports SM 6.0+, cannot compile SM 5.1 - print("WARNING: DXC doesn't support SM 5.1, using SM 6.0") - compiler_args.extend([ - "-T", f"{dxbc_stage}_6_0", - "-HV", "2017", - "-D", "SHADING_LANGUAGE_HLSL_XE=1", - "-I", src_dir, - "-Fh", f"{dxbc_file_path_base}.h", - "-Vn", dxbc_identifier, - "-nologo", - src_path, - ]) - else: - # FXC uses traditional syntax. - # Do NOT use /all_resources_bound — it alters the generated - # DXBC bytecode (changes dcl_globalFlags, register allocation - # and instruction scheduling) which causes rendering artifacts - # such as blue lightning/flash glitches at scaled resolutions. - compiler_args.extend([ - "/D", "SHADING_LANGUAGE_HLSL_XE=1", - "/I", src_dir, - "/Fh", f"{dxbc_file_path_base}.h", - "/T", f"{dxbc_stage}_5_1", - "/Vn", dxbc_identifier, - "/O3", - "/Qstrip_reflect", - "/Qstrip_debug", - "/Qstrip_priv", - "/Gfp", - "/nologo", - src_path, - ]) - if subprocess.call(compiler_args, stdout=subprocess.DEVNULL) != 0: - print(f"ERROR: failed to compile DXBC shader: {src_path}") - return 1 + output = os.path.join(src_dir, "bytecode", "d3d12_5_1", f"{identifier}.h") + print(f"- {src_path} > d3d12_5_1") + result = subprocess.call([sys.executable, compile_dxbc, src_path, output]) + if result != 0: + return result # Vulkan SPIR-V. if all_targets or "spirv" in targets: print("Building Vulkan SPIR-V shaders...") - - # Get the SPIR-V tool paths. - vulkan_sdk_path = os.environ.get("VULKAN_SDK") - if not vulkan_sdk_path: - print("ERROR: VULKAN_SDK environment variable is not set") - if sys.platform == "win32": - print("Please install Vulkan SDK from:") - print("https://sdk.lunarg.com/sdk/download/latest/windows/vulkan-sdk.exe") - else: - print("Please install Vulkan SDK and set VULKAN_SDK environment variable") - return 1 - if not os.path.exists(vulkan_sdk_path): - print(f"ERROR: could not find the Vulkan SDK at {vulkan_sdk_path}") - return 1 - vulkan_bin_path = os.path.join(vulkan_sdk_path, "bin") - if not os.path.exists(vulkan_bin_path): - print("ERROR: could not find the Vulkan SDK binaries") - return 1 - glslang = os.path.join(vulkan_bin_path, "glslangValidator") - if not has_bin(glslang): - print("ERROR: could not find glslangValidator") - return 1 - spirv_opt = os.path.join(vulkan_bin_path, "spirv-opt") - if not has_bin(spirv_opt): - print("ERROR: could not find spirv-opt") - return 1 - spirv_dis = os.path.join(vulkan_bin_path, "spirv-dis") - if not has_bin(spirv_dis): - print("ERROR: could not find spirv-dis") - return 1 - - # Build SPIR-V. - spirv_stages = { - "vs": "vert", "hs": "tesc", "ds": "tese", - "gs": "geom", "ps": "frag", "cs": "comp", - } - spirv_xesl_wrapper = ( - "#version 460\n" - "#extension GL_EXT_control_flow_attributes : require\n" - "#extension GL_EXT_samplerless_texture_functions : require\n" - "#extension GL_GOOGLE_include_directive : require\n" - "#include \"%s\"\n" - ) for src_path in src_paths: src_name = os.path.basename(src_path) - src_is_xesl = src_name.endswith(".xesl") - if ((not src_is_xesl and not src_name.endswith(".glsl")) or + if ((not src_name.endswith(".glsl") and + not src_name.endswith(".xesl")) or len(src_name) <= 8 or src_name[-8] != "."): continue - spirv_identifier = src_name[:-5].replace(".", "_") - spirv_stage = spirv_stages.get(spirv_identifier[-2:], None) - if spirv_stage is None: + identifier = src_name[:-5].replace(".", "_") + if identifier[-2:] not in valid_stages: continue - print(f"- {src_path} > vulkan_spirv") src_dir = os.path.dirname(src_path) - spirv_dir_path = os.path.join(src_dir, "bytecode/vulkan_spirv") - os.makedirs(spirv_dir_path, exist_ok=True) - spirv_file_path_base = os.path.join(spirv_dir_path, spirv_identifier) - spirv_glslang_file_path = f"{spirv_file_path_base}.glslang.spv" - - glslang_arguments = [glslang, - "--stdin" if src_is_xesl else src_path, - "-DSHADING_LANGUAGE_GLSL_XE=1", - "-S", spirv_stage, - "-o", spirv_glslang_file_path, - "-V"] - if src_is_xesl: - glslang_arguments.append(f"-I{src_dir}") - if subprocess.run( - glslang_arguments, - input=(spirv_xesl_wrapper % src_name) if src_is_xesl else None, - text=True).returncode != 0: - print("ERROR: failed to build a SPIR-V shader") - return 1 - - spirv_file_path = f"{spirv_file_path_base}.spv" - if subprocess.call([spirv_opt, "-O", "-O", "--canonicalize-ids", - spirv_glslang_file_path, "-o", spirv_file_path]) != 0: - print("ERROR: failed to optimize a SPIR-V shader") - return 1 - os.remove(spirv_glslang_file_path) - - spirv_dis_file_path = f"{spirv_file_path_base}.txt" - if subprocess.call([spirv_dis, "-o", spirv_dis_file_path, - spirv_file_path]) != 0: - print("ERROR: failed to disassemble a SPIR-V shader") - return 1 - - # Generate the header from the disassembly and the binary. - with open(f"{spirv_file_path_base}.h", "w") as out_file: - out_file.write("// Generated with `xb buildshaders`.\n#if 0\n") - with open(spirv_dis_file_path, "r") as spirv_dis_file: - spirv_dis_data = spirv_dis_file.read() - if len(spirv_dis_data) > 0: - out_file.write(spirv_dis_data) - if spirv_dis_data[-1] != "\n": - out_file.write("\n") - out_file.write("#endif\n\nconst uint32_t %s[] = {" % spirv_identifier) - with open(spirv_file_path, "rb") as spirv_file: - index = 0 - c = spirv_file.read(4) - while len(c) != 0: - if len(c) != 4: - print("ERROR: a SPIR-V shader is misaligned") - return 1 - if index % 6 == 0: - out_file.write("\n ") - else: - out_file.write(" ") - index += 1 - out_file.write("0x%08X," % int.from_bytes(c, sys.byteorder)) - c = spirv_file.read(4) - out_file.write("\n};\n") - os.remove(spirv_dis_file_path) - os.remove(spirv_file_path) + output = os.path.join(src_dir, "bytecode", "vulkan_spirv", f"{identifier}.h") + print(f"- {src_path} > vulkan_spirv") + result = subprocess.call([sys.executable, compile_spirv, src_path, output]) + if result != 0: + return result return 0 @@ -1976,12 +1792,6 @@ class DevenvCommand(Command): else: print("IDE not detected. CMakeLists.txt is in the project root.") - print("\n- generating shaders...") - shader_result = build_shaders() - if shader_result != 0: - print_error("Shader generation failed") - return shader_result - print("\n- running cmake configure...") run_cmake_configure()