Repo created
This commit is contained in:
parent
4af19165ec
commit
68073add76
12458 changed files with 12350765 additions and 2 deletions
279
libs/shaders/vulkan_shaders_preprocessor.py
Normal file
279
libs/shaders/vulkan_shaders_preprocessor.py
Normal file
|
|
@ -0,0 +1,279 @@
|
|||
import os
|
||||
import re
|
||||
import sys
|
||||
from subprocess import Popen, PIPE
|
||||
import json
|
||||
|
||||
VERTEX_SHADER_EXT = '.vsh.glsl'
|
||||
FRAG_SHADER_EXT = '.fsh.glsl'
|
||||
VERTEX_SHADER_EXT_OUT = '.vert'
|
||||
FRAG_SHADER_EXT_OUT = '.frag'
|
||||
|
||||
SHADERS_LIB_COMMON_PATTERN = '// Common'
|
||||
SHADERS_LIB_VS_PATTERN = '// VS'
|
||||
SHADERS_LIB_FS_PATTERN = '// FS'
|
||||
SHADERS_LIB_COMMON_INDEX = 0
|
||||
SHADERS_LIB_VS_INDEX = 1
|
||||
SHADERS_LIB_FS_INDEX = 2
|
||||
|
||||
UNIFORMS = 'uniforms'
|
||||
SAMPLERS = 'samplers'
|
||||
|
||||
debug_output = False
|
||||
|
||||
|
||||
# Read index file which contains program to shaders bindings.
|
||||
def read_index_file(file_path):
|
||||
gpu_programs = dict()
|
||||
with open(file_path, 'r') as f:
|
||||
index = 0
|
||||
for line in f:
|
||||
line_parts = line.strip().split()
|
||||
if len(line_parts) != 3:
|
||||
print('Incorrect GPU program definition : ' + line)
|
||||
exit(1)
|
||||
|
||||
vertex_shader = next(f for f in line_parts if f.endswith(VERTEX_SHADER_EXT))
|
||||
fragment_shader = next(f for f in line_parts if f.endswith(FRAG_SHADER_EXT))
|
||||
|
||||
if not vertex_shader:
|
||||
print('Vertex shader not found in GPU program definition : ' + line)
|
||||
exit(1)
|
||||
|
||||
if not fragment_shader:
|
||||
print('Fragment shader not found in GPU program definition : ' + line)
|
||||
exit(1)
|
||||
|
||||
if line_parts[0] in gpu_programs.keys():
|
||||
print('More than one definition of %s gpu program' % line_parts[0])
|
||||
exit(1)
|
||||
|
||||
gpu_programs[index] = (vertex_shader, fragment_shader, line_parts[0])
|
||||
index += 1
|
||||
|
||||
gpu_programs_cache = dict()
|
||||
for (k, v) in gpu_programs.items():
|
||||
gpu_programs_cache[v[2]] = (v[0], v[1], k)
|
||||
|
||||
return gpu_programs_cache
|
||||
|
||||
|
||||
# Read GLSL-file with common shader functions.
|
||||
def read_shaders_lib_file(file_path):
|
||||
shaders_library = ['', '', '']
|
||||
with open(file_path, 'r') as f:
|
||||
shaders_lib_content = f.read()
|
||||
if len(shaders_lib_content) == 0:
|
||||
return shaders_library
|
||||
|
||||
common_index = shaders_lib_content.find(SHADERS_LIB_COMMON_PATTERN)
|
||||
if common_index < 0:
|
||||
print('Common functions block is not found in ' + file_path)
|
||||
exit(1)
|
||||
vs_index = shaders_lib_content.find(SHADERS_LIB_VS_PATTERN)
|
||||
if vs_index < 0:
|
||||
print('Vertex shaders functions block is not found in ' + file_path)
|
||||
exit(1)
|
||||
fs_index = shaders_lib_content.find(SHADERS_LIB_FS_PATTERN)
|
||||
if fs_index < 0:
|
||||
print('Vertex shaders functions block is not found in ' + file_path)
|
||||
exit(1)
|
||||
if not (common_index < vs_index < fs_index):
|
||||
print('Order of functions block is incorrect in ' + file_path)
|
||||
exit(1)
|
||||
|
||||
shaders_library[SHADERS_LIB_COMMON_INDEX] = shaders_lib_content[common_index:vs_index - 1]
|
||||
shaders_library[SHADERS_LIB_VS_INDEX] = shaders_lib_content[vs_index:fs_index - 1]
|
||||
shaders_library[SHADERS_LIB_FS_INDEX] = shaders_lib_content[fs_index:]
|
||||
|
||||
return shaders_library
|
||||
|
||||
|
||||
def get_shaders_lib_content(shader_file, shaders_library):
|
||||
lib_content = shaders_library[SHADERS_LIB_COMMON_INDEX]
|
||||
if shader_file.find(VERTEX_SHADER_EXT) >= 0:
|
||||
lib_content += shaders_library[SHADERS_LIB_VS_INDEX]
|
||||
elif shader_file.find(FRAG_SHADER_EXT) >= 0:
|
||||
lib_content += shaders_library[SHADERS_LIB_FS_INDEX]
|
||||
return lib_content
|
||||
|
||||
|
||||
def get_shader_line(line, layout_counters):
|
||||
if line.lstrip().startswith('//') or line == '\n' or len(line) == 0:
|
||||
return None
|
||||
|
||||
output_line = line.rstrip()
|
||||
|
||||
if output_line.find('layout (binding') >= 0:
|
||||
if output_line.find('sampler') >= 0:
|
||||
match = re.search(r"binding\s*=\s*(\d+)", output_line)
|
||||
sampler_match = re.search(r"sampler2D\s+(\w+)", output_line)
|
||||
if match and sampler_match:
|
||||
binding_index = int(match.group(1))
|
||||
sampler_name = sampler_match.group(1)
|
||||
if binding_index == 0:
|
||||
print('Binding index must not be 0 for sampler in the line: ' + line)
|
||||
exit(1)
|
||||
layout_counters[SAMPLERS][sampler_name] = binding_index
|
||||
else:
|
||||
print('Sampler name or binding index is not found in the line: ' + line)
|
||||
exit(1)
|
||||
else:
|
||||
match = re.search(r"binding\s*=\s*(\d+)", output_line)
|
||||
if match:
|
||||
binding_index = int(match.group(1))
|
||||
if binding_index != 0:
|
||||
print('Binding index must be 0 in the line: ' + line)
|
||||
exit(1)
|
||||
else:
|
||||
print('Binding index is not found in the line: ' + line)
|
||||
exit(1)
|
||||
layout_counters[UNIFORMS] += 1
|
||||
|
||||
return output_line
|
||||
|
||||
|
||||
def generate_spirv_compatible_glsl_shader(output_file, shader_file, shader_dir, shaders_library,
|
||||
layout_counters, reflection_dict):
|
||||
output_file.write('#version 310 es\n')
|
||||
output_file.write('precision highp float;\n')
|
||||
output_file.write('#define LOW_P lowp\n')
|
||||
output_file.write('#define MEDIUM_P mediump\n')
|
||||
output_file.write('#define HIGH_P highp\n')
|
||||
output_file.write('#define VULKAN_MODE\n')
|
||||
is_fragment_shader = shader_file.find(FRAG_SHADER_EXT) >= 0
|
||||
lib_content = get_shaders_lib_content(shader_file, shaders_library)
|
||||
conditional_started = False
|
||||
conditional_skip = False
|
||||
for line in open(os.path.join(shader_dir, shader_file)):
|
||||
# Remove some useless conditional compilation.
|
||||
if conditional_started and line.lstrip().startswith('#else'):
|
||||
conditional_skip = True
|
||||
continue
|
||||
if conditional_started and line.lstrip().startswith('#endif'):
|
||||
conditional_skip = False
|
||||
conditional_started = False
|
||||
continue
|
||||
if conditional_skip:
|
||||
continue
|
||||
if line.lstrip().startswith('#ifdef ENABLE_VTF'):
|
||||
conditional_started = True
|
||||
continue
|
||||
|
||||
if line.lstrip().startswith('void main'):
|
||||
# Write reflection for uniforms block.
|
||||
uniforms_index = 'vs_uni';
|
||||
if is_fragment_shader:
|
||||
uniforms_index = 'fs_uni'
|
||||
if layout_counters[UNIFORMS] > 0:
|
||||
reflection_dict[uniforms_index] = 0
|
||||
else:
|
||||
reflection_dict[uniforms_index] = -1
|
||||
|
||||
# Write reflection for samplers.
|
||||
sample_index = 'tex'
|
||||
if not sample_index in reflection_dict:
|
||||
reflection_dict[sample_index] = []
|
||||
for (s, idx) in layout_counters[SAMPLERS].items():
|
||||
sampler = {'name': s, 'idx': idx, 'frag':int(is_fragment_shader)}
|
||||
reflection_dict[sample_index].append(sampler)
|
||||
|
||||
# Write shaders library.
|
||||
for lib_line in lib_content.splitlines():
|
||||
shader_line = get_shader_line(lib_line, layout_counters)
|
||||
if shader_line:
|
||||
output_file.write('%s\n' % shader_line)
|
||||
|
||||
shader_line = get_shader_line(line, layout_counters)
|
||||
if shader_line:
|
||||
output_file.write('%s\n' % shader_line)
|
||||
|
||||
layout_counters[UNIFORMS] = 0
|
||||
layout_counters[SAMPLERS] = dict()
|
||||
|
||||
|
||||
# Execute external program.
|
||||
def execute_external(options):
|
||||
p = Popen(options, stdin=PIPE, stdout=PIPE, stderr=PIPE)
|
||||
output, err = p.communicate()
|
||||
rc = p.returncode
|
||||
if rc != 0:
|
||||
for line in err.split(b'\n'):
|
||||
print(line.decode('utf-8'))
|
||||
|
||||
|
||||
# Generate SPIR-V shader from GLSL source.
|
||||
def generate_shader(shader, shader_dir, generation_dir, shaders_library, program_name,
|
||||
layout_counters, output_name, reflection_dict, glslc_path):
|
||||
output_path = os.path.join(generation_dir, output_name)
|
||||
with open(output_path, 'w') as file:
|
||||
generate_spirv_compatible_glsl_shader(file, shader, shader_dir, shaders_library,
|
||||
layout_counters, reflection_dict)
|
||||
spv_path = output_path + '.spv'
|
||||
try:
|
||||
execute_external([glslc_path, '-c', output_path, '-o', spv_path, '-std=310es', '--target-env=vulkan'])
|
||||
if debug_output:
|
||||
debug_dir = os.path.join(generation_dir, 'debug', program_name)
|
||||
debug_path = os.path.join(debug_dir, output_name)
|
||||
if not os.path.exists(debug_dir):
|
||||
os.makedirs(debug_dir)
|
||||
execute_external([glslc_path, '-S', output_path, '-o', debug_path + '.spv.txt', '-std=310es','--target-env=vulkan'])
|
||||
os.rename(output_path, debug_path)
|
||||
else:
|
||||
os.remove(output_path)
|
||||
except:
|
||||
print('Could not generate SPIR-V for the shader {0}. Most likely glslc from Android NDK is not found.'.format(shader))
|
||||
os.remove(output_path)
|
||||
exit(1)
|
||||
return spv_path
|
||||
|
||||
|
||||
def write_shader_to_pack(pack_file, shader_file_name):
|
||||
offset = pack_file.tell()
|
||||
with open(shader_file_name, 'rb') as shader_file:
|
||||
pack_file.write(shader_file.read())
|
||||
os.remove(shader_file_name)
|
||||
return offset, pack_file.tell() - offset
|
||||
|
||||
|
||||
if __name__ == '__main__':
|
||||
if len(sys.argv) < 7:
|
||||
print('Usage : ' + sys.argv[0] + ' <shader_dir> <index_file> <shaders_lib> <generation_dir> <glslc_path> [--debug]')
|
||||
exit(1)
|
||||
|
||||
shader_dir = sys.argv[1]
|
||||
index_file_name = sys.argv[2]
|
||||
shaders_lib_file = sys.argv[3]
|
||||
generation_dir = sys.argv[4]
|
||||
glslc_path = sys.argv[5]
|
||||
|
||||
if len(sys.argv) >= 7:
|
||||
debug_output = (sys.argv[6] == '--debug')
|
||||
|
||||
shaders = [file for file in os.listdir(shader_dir) if
|
||||
os.path.isfile(os.path.join(shader_dir, file)) and (
|
||||
file.endswith(VERTEX_SHADER_EXT) or file.endswith(FRAG_SHADER_EXT))]
|
||||
|
||||
gpu_programs_cache = read_index_file(os.path.join(shader_dir, index_file_name))
|
||||
shaders_library = read_shaders_lib_file(os.path.join(shader_dir, shaders_lib_file))
|
||||
reflection = []
|
||||
current_offset = 0
|
||||
with open(os.path.join(generation_dir, 'shaders_pack.spv'), 'wb') as pack_file:
|
||||
for (k, v) in gpu_programs_cache.items():
|
||||
layout_counters = {UNIFORMS: 0, SAMPLERS: dict()}
|
||||
reflection_dict = {'prg': v[2], 'info': dict()}
|
||||
vs_offset = write_shader_to_pack(pack_file, generate_shader(v[0], shader_dir, generation_dir,
|
||||
shaders_library, k,
|
||||
layout_counters, k + VERTEX_SHADER_EXT_OUT,
|
||||
reflection_dict['info'], glslc_path))
|
||||
reflection_dict['vs_off'] = vs_offset[0]
|
||||
reflection_dict['vs_size'] = vs_offset[1]
|
||||
fs_offset = write_shader_to_pack(pack_file, generate_shader(v[1], shader_dir, generation_dir,
|
||||
shaders_library, k,
|
||||
layout_counters, k + FRAG_SHADER_EXT_OUT,
|
||||
reflection_dict['info'], glslc_path))
|
||||
reflection_dict['fs_off'] = fs_offset[0]
|
||||
reflection_dict['fs_size'] = fs_offset[1]
|
||||
reflection.append(reflection_dict)
|
||||
with open(os.path.join(generation_dir, 'reflection.json'), 'w') as reflection_file:
|
||||
reflection_file.write(json.dumps(reflection, separators=(',',':')))
|
||||
Loading…
Add table
Add a link
Reference in a new issue