blob: be8229efd68ac2d200c729b00f62d0debb52d5e9 [file] [log] [blame]
import itertools
import sys
from enum import Enum
# Organizes all combinations of valid features for draw.glsl into their own custom-named namespace.
# Generates MSL code to declare each namespace and #include draw.glsl with corresponding #defines.
class Feature():
def __init__(self, name, index):
self.name = name
self.index = index
# Each feature has a specific index. These must stay in sync with render_context_metal_impl.mm.
ENABLE_CLIPPING = Feature('ENABLE_CLIPPING', 0)
ENABLE_CLIP_RECT = Feature('ENABLE_CLIP_RECT', 1)
ENABLE_ADVANCED_BLEND = Feature('ENABLE_ADVANCED_BLEND', 2)
ENABLE_FEATHER = Feature('ENABLE_FEATHER', 3)
ENABLE_EVEN_ODD = Feature('ENABLE_EVEN_ODD', 4)
ENABLE_NESTED_CLIPPING = Feature('ENABLE_NESTED_CLIPPING', 5)
ENABLE_HSL_BLEND_MODES = Feature('ENABLE_HSL_BLEND_MODES', 6)
DRAW_INTERIOR_TRIANGLES = Feature('DRAW_INTERIOR_TRIANGLES', 7)
ATLAS_BLIT = Feature('ATLAS_BLIT', 8)
whole_program_features = {ENABLE_CLIPPING,
ENABLE_CLIP_RECT,
ENABLE_ADVANCED_BLEND,
ENABLE_FEATHER}
fragment_only_features = {ENABLE_EVEN_ODD,
ENABLE_NESTED_CLIPPING,
ENABLE_HSL_BLEND_MODES}
all_features = whole_program_features.union(fragment_only_features)
# Returns whether a valid program exists for the given feature set.
def is_valid_feature_set(feature_set):
if ENABLE_NESTED_CLIPPING in feature_set and ENABLE_CLIPPING not in feature_set:
return False
if ENABLE_HSL_BLEND_MODES in feature_set and ENABLE_ADVANCED_BLEND not in feature_set:
return False
return True
# Returns whether the given feature set is the *simplest* set that defines a unique vertex shader.
# (Many feature sets produce identical vertex shaders.)
def is_unique_vertex_feature_set(feature_set):
# Fragment-only features have no effect on the vertex shader.
if fragment_only_features.intersection(feature_set):
return False
return True
non_atlas_coverage_features = {ENABLE_FEATHER,
ENABLE_EVEN_ODD,
ENABLE_NESTED_CLIPPING}
non_image_mesh_features = {ENABLE_FEATHER,
ENABLE_EVEN_ODD,
ENABLE_NESTED_CLIPPING,
DRAW_INTERIOR_TRIANGLES,
ATLAS_BLIT}
# Returns whether the given feature set is compatible with an image mesh shader.
def is_image_mesh_feature_set(feature_set):
return not non_image_mesh_features.intersection(feature_set)
ShaderType = Enum('ShaderType', ['VERTEX', 'FRAGMENT'])
DrawType = Enum('DrawType', ['PATH', 'IMAGE_MESH'])
FillType = Enum('FillType', ['CLOCKWISE', 'LEGACY'])
def emit_shader(out, shader_type, draw_type, fill_type, feature_set):
assert(is_valid_feature_set(feature_set))
if shader_type == ShaderType.VERTEX:
assert(is_unique_vertex_feature_set(feature_set))
out.write('#define VERTEX\n')
else:
out.write('#define FRAGMENT\n')
if draw_type == DrawType.IMAGE_MESH:
assert(is_image_mesh_feature_set(feature_set))
namespace_id = ['0', '0', '0', '0', '0', '0', '0', '0', '0']
for feature in feature_set:
namespace_id[feature.index] = '1'
for feature in feature_set:
out.write('#define %s 1\n' % feature.name)
if fill_type == FillType.CLOCKWISE:
out.write('#define CLOCKWISE_FILL 1\n')
if draw_type == DrawType.PATH:
out.write('#define DRAW_PATH 1\n')
out.write('namespace %s%s\n' %
('c' if fill_type == FillType.CLOCKWISE else 'p',
''.join(namespace_id)))
out.write('{\n')
out.write('#include "draw_path.minified.vert"\n')
if ATLAS_BLIT in feature_set:
out.write('#include "draw_mesh.minified.frag"\n')
else:
out.write('#include "draw_raster_order_path.minified.frag"\n')
out.write('}\n')
out.write('#undef DRAW_PATH\n')
else:
out.write('#define DRAW_IMAGE 1\n')
out.write('#define DRAW_IMAGE_MESH 1\n')
out.write('namespace m%s\n' % ''.join(namespace_id))
out.write('{\n')
out.write('#include "draw_image_mesh.minified.vert"\n')
out.write('#include "draw_mesh.minified.frag"\n')
out.write('}\n')
out.write('#undef DRAW_IMAGE_MESH\n')
out.write('#undef DRAW_IMAGE\n')
for feature in feature_set:
out.write('#undef %s\n' % feature.name)
if shader_type == ShaderType.VERTEX:
out.write('#undef VERTEX\n')
else:
out.write('#undef FRAGMENT\n')
if fill_type == FillType.CLOCKWISE:
out.write('#undef CLOCKWISE_FILL\n')
out.write('\n')
# Organize all combinations of valid features into their own namespace.
out = open(sys.argv[1], 'w', newline='\n')
# Precompile the bare minimum set of shaders required to draw everything. We can compile more
# specialized shaders in the background at runtime, and use the fully-featured (slower) shaders
# while waiting for the compilations to complete.
# Path tessellation shaders.
emit_shader(out, ShaderType.VERTEX, DrawType.PATH, FillType.LEGACY,
whole_program_features)
emit_shader(out, ShaderType.FRAGMENT, DrawType.PATH, FillType.LEGACY, all_features)
emit_shader(out, ShaderType.FRAGMENT, DrawType.PATH, FillType.CLOCKWISE, all_features)
# Interior triangulation shaders.
emit_shader(out, ShaderType.VERTEX, DrawType.PATH, FillType.LEGACY,
whole_program_features.union({DRAW_INTERIOR_TRIANGLES}))
emit_shader(out, ShaderType.FRAGMENT, DrawType.PATH, FillType.LEGACY,
all_features.union({DRAW_INTERIOR_TRIANGLES}))
emit_shader(out, ShaderType.FRAGMENT, DrawType.PATH, FillType.CLOCKWISE,
all_features.union({DRAW_INTERIOR_TRIANGLES}))
# Atlas blit shaders.
emit_shader(out, ShaderType.VERTEX, DrawType.PATH, FillType.LEGACY,
whole_program_features\
.union({DRAW_INTERIOR_TRIANGLES, ATLAS_BLIT})\
.difference(non_atlas_coverage_features))
emit_shader(out, ShaderType.FRAGMENT, DrawType.PATH, FillType.LEGACY,
all_features\
.union({DRAW_INTERIOR_TRIANGLES, ATLAS_BLIT})\
.difference(non_atlas_coverage_features))
# Image mesh shaders.
emit_shader(out, ShaderType.VERTEX, DrawType.IMAGE_MESH, FillType.LEGACY,
whole_program_features.difference(non_image_mesh_features))
emit_shader(out, ShaderType.FRAGMENT, DrawType.IMAGE_MESH, FillType.LEGACY,
all_features.difference(non_image_mesh_features))
# If we wanted to emit all combos...
# for n in range(0, len(all_features) + 1):
# for feature_set in itertools.combinations(all_features, n):
# if not is_valid_feature_set(feature_set):
# continue